# 重置高压发生器功能 E2E 测试方案 ## 功能概述 重置高压发生器功能允许用户通过界面上的RESET按钮重置设备参数,将高压发生器恢复到初始状态。 ## 功能实现分析 ### 数据流 ``` UI Button (RESET) ↓ handleResetParameters() ↓ dispatch(resetDevices()) [Redux Thunk] ↓ resetAllDevices() [API] ↓ POST /auth/device/action ↓ 设备重置完成 ``` ### 技术架构 - **UI层**: ContentAreaLarge.tsx 组件 - **状态管理**: Redux Toolkit (deviceSlice) - **异步处理**: createAsyncThunk - **API调用**: axios + interceptor ### Redux状态管理 ```typescript interface DeviceState { status: 'idle' | 'loading' | 'succeeded' | 'failed'; error: string | null; deviceError: string | null; } ``` ### API请求格式 ```json POST /auth/device/action { "deviceUri": "DIOS/DEVICE/Generator", "reqName": "RESET", "reqParam": "", "reqTransaction": "", "reqClientID": "" } ``` ### API响应格式 **成功响应**: ```json { "code": "0x000000", "description": "Success", "data": {} } ``` **失败响应**: ```json { "code": "0x010001", "description": "设备通信失败", "solution": "检查设备连接" } ``` --- ## 测试套件设计 ### 测试套件:重置高压发生器功能 **文件路径**: `cypress/e2e/exam/reset-generator.cy.ts` --- #### TC-RESET-01: 成功重置设备 **测试目标**: 验证正常重置流程 **前置条件**: - 已登录系统 - 已进入检查页面 - Mock所有依赖API(quota、i18n等) **测试步骤**: 1. Mock设备重置API成功响应 2. 定位RESET按钮 3. 验证按钮初始状态为可用 4. 点击RESET按钮 5. 验证按钮变为禁用状态(loading) 6. 等待API调用完成 7. 验证按钮恢复为可用状态 8. 验证Redux状态更新为'succeeded' **验证点**: - ✅ 按钮可点击 - ✅ 点击后按钮禁用(disabled=true) - ✅ API调用成功(code='0x000000') - ✅ 按钮恢复可用状态 - ✅ Redux状态: status='succeeded' - ✅ 无错误提示 **Mock配置**: ```typescript cy.intercept('POST', '/auth/device/action', (req) => { if (req.body.reqName === 'RESET') { req.reply({ statusCode: 200, body: { code: '0x000000', description: 'Success', data: {} } }); } }).as('resetDevice'); ``` --- #### TC-RESET-02: API失败处理 **测试目标**: 验证重置失败时的错误处理 **前置条件**: - 已登录系统 - 已进入检查页面 **测试步骤**: 1. Mock设备重置API失败响应 2. 点击RESET按钮 3. 等待API调用完成 4. 验证Redux状态更新为'failed' 5. 验证错误信息存储到state.error 6. 验证按钮恢复可用 7. 验证控制台输出错误日志 **验证点**: - ✅ API返回非'0x000000'错误码 - ✅ Redux状态: status='failed' - ✅ 错误信息存储到state.error - ✅ 按钮恢复可用 - ✅ 控制台输出错误日志 **Mock配置**: ```typescript cy.intercept('POST', '/auth/device/action', { statusCode: 200, body: { code: '0x010001', description: '设备通信失败', solution: '检查设备连接' } }).as('resetDeviceFail'); ``` --- #### TC-RESET-03: 按钮禁用状态管理 **测试目标**: 验证loading期间按钮状态管理 **前置条件**: - 已登录系统 - 已进入检查页面 **测试步骤**: 1. Mock设备重置API延迟响应(2秒) 2. 点击RESET按钮 3. 验证按钮立即禁用 4. 验证disabled属性为true 5. 等待1秒,验证按钮仍然禁用 6. 等待API响应完成 7. 验证按钮恢复可用 **验证点**: - ✅ 点击后按钮立即禁用 - ✅ disabled属性为true - ✅ 响应前按钮保持禁用 - ✅ 响应后按钮恢复可用 - ✅ Redux状态正确转换: idle → loading → succeeded **Mock配置**: ```typescript cy.intercept('POST', '/auth/device/action', (req) => { req.reply({ statusCode: 200, body: { code: '0x000000', description: 'Success', data: {} }, delay: 2000 }); }).as('resetDeviceDelay'); ``` --- #### TC-RESET-04: 网络错误处理 **测试目标**: 验证网络异常场景的错误处理 **前置条件**: - 已登录系统 - 已进入检查页面 **测试步骤**: 1. Mock网络错误 2. 点击RESET按钮 3. 等待错误发生 4. 验证Redux状态更新为'failed' 5. 验证错误信息被捕获 6. 验证按钮恢复可用 7. 验证用户可以重试 **验证点**: - ✅ 捕获网络错误 - ✅ Redux状态: status='failed' - ✅ 错误信息存储 - ✅ 状态恢复正常 - ✅ 用户可重试操作 **Mock配置**: ```typescript cy.intercept('POST', '/auth/device/action', { forceNetworkError: true }).as('resetDeviceNetworkError'); ``` --- #### TC-RESET-05: 防止重复点击 **测试目标**: 验证loading状态下防止重复操作 **前置条件**: - 已登录系统 - 已进入检查页面 **测试步骤**: 1. Mock设备重置API延迟响应(3秒) 2. 点击RESET按钮 3. 立即再次尝试点击按钮 4. 验证第二次点击无效 5. 验证只发送了一次API请求 6. 等待第一次请求完成 7. 验证可以再次点击 **验证点**: - ✅ loading时按钮禁用 - ✅ 再次点击无效 - ✅ 只发送一次API请求 - ✅ 完成后才能再次点击 - ✅ 防抖机制有效 **Mock配置**: ```typescript let requestCount = 0; cy.intercept('POST', '/auth/device/action', (req) => { requestCount++; req.reply({ statusCode: 200, body: { code: '0x000000', description: 'Success', data: {} }, delay: 3000 }); }).as('resetDeviceMultiple'); // 验证requestCount === 1 ``` --- ## Page Object Model 设计 ### ExamPage 扩展 **文件**: `cypress/support/pageObjects/ExamPage.ts` ```typescript class ExamPage { // ... 现有方法 /** * 获取重置高压发生器按钮 */ getResetGeneratorButton() { return cy.get('[data-testid="reset-generator-btn"]'); } /** * 点击重置按钮 */ clickResetGenerator() { this.getResetGeneratorButton().click(); } /** * 验证重置按钮为启用状态 */ verifyResetButtonEnabled() { this.getResetGeneratorButton() .should('be.visible') .should('not.be.disabled'); } /** * 验证重置按钮为禁用状态 */ verifyResetButtonDisabled() { this.getResetGeneratorButton() .should('be.visible') .should('be.disabled'); } /** * 等待重置操作完成 */ waitForResetComplete() { cy.wait('@resetDevice'); this.verifyResetButtonEnabled(); } /** * 验证Redux设备状态 * @param expectedStatus - 期望的状态: 'idle' | 'loading' | 'succeeded' | 'failed' */ verifyDeviceStatus(expectedStatus: string) { cy.window().its('store').invoke('getState') .its('device').its('status') .should('eq', expectedStatus); } /** * 验证设备错误信息 * @param expectedError - 期望的错误信息,null表示无错误 */ verifyDeviceError(expectedError: string | null) { cy.window().its('store').invoke('getState') .its('device').its('error') .should('eq', expectedError); } } ``` --- ## Mock Handlers 设计 ### 文件结构 **文件**: `cypress/support/mock/handlers/deviceActions.ts` ```typescript /** * Mock设备重置成功响应 */ export const mockResetDeviceSuccess = () => { cy.intercept('POST', '/auth/device/action', (req) => { if (req.body.reqName === 'RESET') { req.reply({ statusCode: 200, body: { code: '0x000000', description: 'Success', data: {} } }); } }).as('resetDevice'); }; /** * Mock设备重置失败响应 * @param errorCode - 错误码,默认为'0x010001' * @param description - 错误描述 */ export const mockResetDeviceFail = ( errorCode: string = '0x010001', description: string = '设备通信失败' ) => { cy.intercept('POST', '/auth/device/action', { statusCode: 200, body: { code: errorCode, description: description, solution: '检查设备连接' } }).as('resetDeviceFail'); }; /** * Mock网络错误 */ export const mockResetDeviceNetworkError = () => { cy.intercept('POST', '/auth/device/action', (req) => { if (req.body.reqName === 'RESET') { req.reply({ forceNetworkError: true }); } }).as('resetDeviceNetworkError'); }; /** * Mock设备重置延迟响应 * @param delayMs - 延迟毫秒数 */ export const mockResetDeviceDelay = (delayMs: number) => { cy.intercept('POST', '/auth/device/action', (req) => { if (req.body.reqName === 'RESET') { req.reply({ statusCode: 200, body: { code: '0x000000', description: 'Success', data: {} }, delay: delayMs }); } }).as('resetDeviceDelay'); }; /** * Mock所有设备操作(用于其他测试场景) */ export const mockAllDeviceActions = () => { cy.intercept('POST', '/auth/device/action', (req) => { req.reply({ statusCode: 200, body: { code: '0x000000', description: 'Success', data: {} } }); }).as('deviceAction'); }; ``` --- ## UI组件修改 ### ContentAreaLarge.tsx 修改点 在RESET按钮添加测试标识: ```tsx