Explorar el Código

feat: 增强Cypress测试配置和Mock数据支持

- 在 cypress.config.ts 中添加 Chrome 浏览器默认配置
- 在 cypress/e2e/login.cy.ts 中集成软件信息和国际化Mock调用
- 在 cypress/support/e2e.ts 中临时禁用浏览器控制台日志拦截功能
- 在 cypress/support/mock/handlers/i18n.ts 中大幅扩展中文翻译数据,支持宠物管理、注册表单、检查功能等多语言键值
- 在 cypress/support/mock/handlers/system.ts 中更新Mock数据:产品名称改为VETDROS,设备改为Physics模式,添加语言配置
- 在 cypress/support/pageObjects/LoginPage.ts 中扩展页面对象,支持版本更新弹窗和下载进度弹窗的测试

改动文件:
- cypress.config.ts
- cypress/e2e/login.cy.ts
- cypress/support/e2e.ts
- cypress/support/mock/handlers/i18n.ts
- cypress/support/mock/handlers/system.ts
- cypress/support/pageObjects/LoginPage.ts
dengdx hace 3 semanas
padre
commit
20180eb00f

+ 1 - 0
cypress.config.ts

@@ -11,6 +11,7 @@ export default defineConfig({
   viewportWidth: 1920,
   viewportHeight: 1080,
   e2e: {
+    defaultBrowser: 'chrome',
     baseUrl: null,//'http://localhost:10086/#/pages/index/index', // Adjust this to match your application's base 
     // specPattern: '__e2e_test__/**/*.spec.ts',
     setupNodeEvents(on, config) {

+ 6 - 4
cypress/e2e/login.cy.ts

@@ -1,5 +1,7 @@
 // import { describe, it } from 'node:test';
 import { mockLoginFail, mockLoginSuccess } from '../support/mock/handlers/user';
+import { mockGetSoftwareInfoSuccess } from '../support/mock/handlers/system';
+import { mockI18nSuccess } from '../support/mock/handlers/i18n';
 import LoginPage from '../support/pageObjects/LoginPage';
 // import "cypress";
 
@@ -7,10 +9,10 @@ describe('Login Page', () => {
   const loginPage = new LoginPage();
 
   beforeEach(() => {
-    // loginPage.visit();
-    // loginPage.getUsernameInput().should('be.visible');
-    // loginPage.getPasswordInput().should('be.visible');
-    // loginPage.getSubmitButton().should('be.visible');
+    // Mock software_info API to prevent 404 errors
+    mockGetSoftwareInfoSuccess();
+    // Mock i18n translation file
+    mockI18nSuccess('zh_CN');
   });
 
   it('should successfully log in with correct credentials', () => {

+ 50 - 50
cypress/support/e2e.ts

@@ -12,59 +12,59 @@ import './pageObjects/WorklistPage';
 // require('./pageObjects/LoginPage')
 // 拦截浏览器 console 日志并输出到终端
 // 使用 CDP (Chrome DevTools Protocol) 来监听 console 事件
-Cypress.on('window:before:load', (win) => {
-  // 保存原始的 console 方法
-  const originalLog = win.console.log
-  const originalError = win.console.error
-  const originalWarn = win.console.warn
+// Cypress.on('window:before:load', (win) => {
+//   // 保存原始的 console 方法
+//   const originalLog = win.console.log
+//   const originalError = win.console.error
+//   const originalWarn = win.console.warn
   
-  // 在 window 对象上创建日志缓冲区
-  if (!win.__consoleLogs__) {
-    win.__consoleLogs__ = []
-  }
+//   // 在 window 对象上创建日志缓冲区
+//   if (!win.__consoleLogs__) {
+//     win.__consoleLogs__ = []
+//   }
   
-  // 重写 console.log - 存储到缓冲区
-  win.console.log = function(...args: any[]) {
-    originalLog.apply(win.console, args)
-    const message = args.map(arg => 
-      typeof arg === 'object' ? JSON.stringify(arg, null, 2) : String(arg)
-    ).join(' ')
-    win.__consoleLogs__.push({ type: 'log', message, timestamp: new Date().toISOString() })
-  }
+//   // 重写 console.log - 存储到缓冲区
+//   win.console.log = function(...args: any[]) {
+//     originalLog.apply(win.console, args)
+//     const message = args.map(arg => 
+//       typeof arg === 'object' ? JSON.stringify(arg, null, 2) : String(arg)
+//     ).join(' ')
+//     win.__consoleLogs__.push({ type: 'log', message, timestamp: new Date().toISOString() })
+//   }
   
-  // 重写 console.error
-  win.console.error = function(...args: any[]) {
-    originalError.apply(win.console, args)
-    const message = args.map(arg => 
-      typeof arg === 'object' ? JSON.stringify(arg, null, 2) : String(arg)
-    ).join(' ')
-    win.__consoleLogs__.push({ type: 'error', message, timestamp: new Date().toISOString() })
-  }
+//   // 重写 console.error
+//   win.console.error = function(...args: any[]) {
+//     originalError.apply(win.console, args)
+//     const message = args.map(arg => 
+//       typeof arg === 'object' ? JSON.stringify(arg, null, 2) : String(arg)
+//     ).join(' ')
+//     win.__consoleLogs__.push({ type: 'error', message, timestamp: new Date().toISOString() })
+//   }
   
-  // 重写 console.warn
-  win.console.warn = function(...args: any[]) {
-    originalWarn.apply(win.console, args)
-    const message = args.map(arg => 
-      typeof arg === 'object' ? JSON.stringify(arg, null, 2) : String(arg)
-    ).join(' ')
-    win.__consoleLogs__.push({ type: 'warn', message, timestamp: new Date().toISOString() })
-  }
-})
+//   // 重写 console.warn
+//   win.console.warn = function(...args: any[]) {
+//     originalWarn.apply(win.console, args)
+//     const message = args.map(arg => 
+//       typeof arg === 'object' ? JSON.stringify(arg, null, 2) : String(arg)
+//     ).join(' ')
+//     win.__consoleLogs__.push({ type: 'warn', message, timestamp: new Date().toISOString() })
+//   }
+// })
 
-// 添加自定义命令来刷新日志到终端
-Cypress.Commands.add('flushConsoleLogs', () => {
-  cy.window({ log: false }).then((win: any) => {
-    if (win.__consoleLogs__ && win.__consoleLogs__.length > 0) {
-      const logs = win.__consoleLogs__.splice(0) // 清空缓冲区
-      logs.forEach((log: any) => {
-        const prefix = log.type === 'error' ? '[ERROR] ' : log.type === 'warn' ? '[WARN] ' : ''
-        cy.task('browserLog', `${prefix}${log.message}`, { log: false })
-      })
-    }
-  })
-})
+// // 添加自定义命令来刷新日志到终端
+// Cypress.Commands.add('flushConsoleLogs', () => {
+//   cy.window({ log: false }).then((win: any) => {
+//     if (win.__consoleLogs__ && win.__consoleLogs__.length > 0) {
+//       const logs = win.__consoleLogs__.splice(0) // 清空缓冲区
+//       logs.forEach((log: any) => {
+//         const prefix = log.type === 'error' ? '[ERROR] ' : log.type === 'warn' ? '[WARN] ' : ''
+//         cy.task('browserLog', `${prefix}${log.message}`, { log: false })
+//       })
+//     }
+//   })
+// })
 
-// 自动在每个测试后刷新日志
-afterEach(() => {
-  cy.flushConsoleLogs()
-})
+// // 自动在每个测试后刷新日志
+// afterEach(() => {
+//   cy.flushConsoleLogs()
+// })

+ 255 - 42
cypress/support/mock/handlers/i18n.ts

@@ -90,48 +90,261 @@ export function mockGetLanguageListEmpty() {
  */
 export function mockI18nSuccess(locale: 'zh_CN' | 'en_US') {
   const mockData = locale === 'zh_CN' ? {
-    greeting: '你好,世界!',
-    name: '张三',
-    patient: '患者管理',
-    register: '注册',
-    tasklist: '任务清单',
-    historylist: '历史清单',
-    archivelist: '归档清单',
-    bin: '回收站',
-    outputlist: '传输清单',
-    exam: '检查',
-    examlist: '检查清单',
-    process: '处理',
-    print: '打印',
-    printlist: '打印清单',
-    worklist: '任务清单',
-    'worklist.operationPanel': '操作面板',
-    'register.basicInfoPanel': '基本信息表单区域',
-    'register.protocolListPanel': '待选择协议列表区域',
-    'register.selectedProtocolListPanel': '已选择协议列表区域',
-    'worklistTable.patientId': '患者编号',
-    'worklistTable.name': '患者姓名',
-    'register.patientId': '患者编号',
-    'register.patientName': '患者姓名',
-    'register.gender': '性别',
-    'register.gender.male': '男',
-    'register.gender.female': '女',
-    // Login page
-    'login.username': '用户名',
-    'login.username.placeholder': '请输入用户名',
-    'login.username.required': '请输入用户名',
-    'login.password': '密码',
-    'login.password.placeholder': '请输入密码',
-    'login.password.required': '请输入密码',
-    'login.submit': '登录',
-    'login.emergency': '急诊',
-    'login.success': '登录成功',
-    'login.failed': '登录失败',
-    'login.failedDetail': ',详情:',
-    'login.networkError': ',网络错误:',
-    'login.unknownError': '未知错误',
-    'login.emergency.success': '急诊模式启动成功',
-    'login.emergency.failed': '急诊操作失败:'
+    "greeting": "你好,世界!",
+    "name": "张三",
+    "patient": "患者管理",
+    "animal.patient": "宠物管理",
+    "register": "注册",
+    "tasklist": "任务清单",
+    "historylist": "历史清单",
+    "archivelist": "归档清单",
+    "bin": "回收站",
+    "outputlist": "传输清单",
+    "exam": "检查",
+    "examlist": "检查清单",
+    "process": "处理",
+    "print": "打印",
+    "printlist": "打印清单",
+    "worklist": "任务清单",
+    "worklist.operationPanel": "操作面板",
+    "register.basicInfoPanel": "基本信息表单区域",
+    "register.protocolListPanel": "待选择协议列表区域",
+    "register.selectedProtocolListPanel": "已选择协议列表区域",
+    "worklistTable.patientId": "患者编号22",
+    "worklistTable.name": "患者姓名",
+    "worklistTable.alias": "曾用名",
+    "worklistTable.englishName": "英文名",
+    "worklistTable.registrationId": "登记号",
+    "worklistTable.birthDate": "出生日期",
+    "worklistTable.age": "年龄",
+    "worklistTable.gender": "性别",
+    "worklistTable.bodyType": "病人体型",
+    "worklistTable.weight": "体重",
+    "worklistTable.height": "身高",
+    "worklistTable.pregnancyStatus": "怀孕状态",
+    "worklistTable.referringDoctor": "转诊医师",
+    "searchPanel.name": "按姓名查询",
+    "searchPanel.patientId": "按患者编号查询",
+    "searchPanel.registrationId": "按登记号查询",
+    "searchPanel.startDate": "开始日期",
+    "searchPanel.endDate": "结束日期",
+    "searchPanel.search": "查询",
+    "register.patientId": "患者编号",
+    "animal.register.patientId": "宠物编号",
+    "register.patientId.placeholder": "请输入患者编号",
+    "animal.register.patientId.placeholder": "请输入宠物编号",
+    "register.patientName": "患者姓名",
+    "animal.register.patientName": "宠物昵称",
+    "register.patientName.placeholder": "请输入患者姓名",
+    "animal.register.patientName.placeholder": "请输入宠物昵称",
+    "register.previousName": "曾用名",
+    "register.previousName.placeholder": "请输入曾用名",
+    "register.englishName": "英文名",
+    "register.englishName.placeholder": "请输入英文名",
+    "register.registrationNo": "登记号",
+    "register.registrationNo.placeholder": "请输入登记号",
+    "register.dateOfBirth": "出生日期",
+    "register.age": "年龄",
+    "register.gender": "性别",
+    "register.gender.male": "男",
+    "register.gender.female": "女",
+    "register.gender.other": "其他",
+    "register.gender.unknown": "未知",
+    "register.gender.male_pet": "公",
+    "register.gender.female_pet": "母",
+    "register.gender.other_pet": "其他",
+    "register.gender.unknown_pet": "未知",
+    "register.gender.castration": "阉割",
+    "register.gender.sterilization": "绝育",
+    "register.owner_name": "宠主姓名",
+    "register.owner_name.placeholder": "请输入宠主姓名",
+    "register.bodyType": "病人体型",
+    "register.bodyType.slim": "瘦",
+    "register.bodyType.average": "平均",
+    "register.bodyType.fat": "重",
+    "register.weight": "体重",
+    "register.height": "身高",
+    "register.pregnancyStatus": "怀孕状态",
+    "register.pregnancyStatus.yes": "是",
+    "register.pregnancyStatus.no": "否",
+    "register.pregnancyStatus.na": "不适用",
+    "register.referringPhysician": "转诊医师",
+    "register.referringPhysician.placeholder": "请输入转诊医师姓名",
+    "register.bodyPart": "身体部位",
+    "register.bodyPart.head": "头部",
+    "register.bodyPart.chest": "胸部",
+    "register.accessionNumber": "登记号",
+    "register.accessionNumber.placeholder": "请输入登记号",
+    "register.protocol.A": "协议A",
+    "register.protocol.B": "协议B",
+    "register.protocol.C": "协议C",
+    "register.protocol.D": "协议D",
+    "register.protocol.E": "协议E",
+    "register.protocol.F": "协议F",
+    "register.selectedProtocol.A": "已选协议A",
+    "register.selectedProtocol.B": "已选协议B",
+    "register.selectedProtocol.C": "已选协议C",
+    "register.selectedProtocol.remove": "移除",
+    "outputTable.name": "病人姓名",
+    "outputTable.id": "病人ID",
+    "outputTable.priority": "优先级",
+    "outputTable.status": "状态",
+    "outputTable.retryCount": "重试次数",
+    "outputTable.target": "目标",
+    "outputAction.retry": "重试",
+    "outputAction.delete": "删除",
+    "bin.diskCapacity": "磁盘容量:",
+    "bin.freeSpace": "剩余空间:",
+    "bin.binCapacity": "回收站容量:",
+    "bin.delete": "删除",
+    "bin.restore": "恢复",
+    "bin.empty": "清空",
+    "dicomNodeDetail.title": "DICOM节点详情",
+    "dicomNodeDetail.nodeList": "DICOM节点列表",
+    "dicomNodeDetail.selectNode": "请选择DICOM节点",
+    "dicomNodeDetail.host": "主机名称",
+    "dicomNodeDetail.ip": "主机IP",
+    "dicomNodeDetail.port": "主机端口",
+    "dicomNodeDetail.calledAET": "被叫实体名",
+    "dicomNodeDetail.callingAET": "主叫实体名",
+    "dicomNodeDetail.testConnection": "测试连接状态",
+    "dicomNodeDetail.archive": "归档",
+    "actionPanel.deleteTask": "删除检查任务",
+    "actionPanel.editPatient": "编辑患者信息",
+    "actionPanel.lockTask": "锁定任务",
+    "actionPanel.risSync": "RIS同步",
+    "actionPanel.reRegister": "再登记",
+    "actionPanel.saveLocal": "保存本地",
+    "actionPanel.importXLS": "从XLS导入",
+    "actionPanel.sortList": "列表排序",
+    "actionPanel.cloudShare": "云分享",
+    "actionPanel.imageExchange": "图像交换",
+    "actionPanel.qrPrint": "二维码打印",
+    "actionPanel.send": "发送",
+    "actionPanel.burn": "刻录",
+    "actionPanel.export": "导出",
+    "actionPanel.import": "导入",
+    "actionPanel.showReport": "显示报告",
+    "Small": "小",
+    "Medium": "中",
+    "Large": "大",
+    "workstation.free": "自由位",
+    "workstation.direct": "传统位",
+    "workstation.table": "卧位",
+    "workstation.wall": "立位",
+    "register.patientSize": "患者体型",
+    "register.patientSize.placeholder": "请输入患者体型",
+    "animal.register.patientSize": "宠物体型",
+    "animal.register.patientSize.placeholder": "请输入宠物体型",
+    "register.sexNeutered": "绝育情况",
+    "register.sexNeutered.placeholder": "请输入绝育情况",
+    "register.sexNeutered.altered": "绝育",
+    "register.sexNeutered.unaltered": "未绝育",
+    "register.chipNumber": "芯片编号",
+    "register.chipNumber.placeholder": "请输入芯片编号",
+    "register.variety": "品种",
+    "register.variety.placeholder": "请输入品种",
+    "register.patientType": "患者类型",
+    "register.patientType.placeholder": "请输入患者类型",
+    "register.operatorId": "操作员ID",
+    "register.operatorId.placeholder": "请输入操作员ID",
+    "register.modality": "物理疗法",
+    "register.modality.placeholder": "请输入物理疗法",
+    "register.thickness": "厚度",
+    "register.studyType": "检查类型",
+    "register.studyType.placeholder": "请输入检查类型",
+    "register.comment": "备注",
+    "register.comment.placeholder": "请输入备注",
+    "register.cat": "猫",
+    "register.dog": "狗",
+    "register.equine": "马",
+    "register.lizard": "蜥蜴",
+    "register.birds": "鸟类",
+    "register.rabbit": "兔",
+    "register.snake": "蛇",
+    "register.turtle": "龟",
+    "register.gnawer": "啮齿类",
+    "worklistTable.StudyInstanceUID": "检查实例UID",
+    "worklistTable.StudyID": "检查ID",
+    "worklistTable.SpecificCharacterSet": "特定字符集",
+    "worklistTable.AccessionNumber": "登记号",
+    "worklistTable.PatientID": "患者ID",
+    "worklistTable.PatientName": "患者姓名",
+    "worklistTable.DisplayPatientName": "显示患者姓名",
+    "worklistTable.PatientSize": "患者体型",
+    "worklistTable.PatientAge": "患者年龄",
+    "worklistTable.PatientSex": "患者性别",
+    "animal.worklistTable.PatientID": "宠物ID",
+    "animal.worklistTable.PatientName": "宠物昵称",
+    "animal.worklistTable.PatientAge": "宠物年龄",
+    "animal.worklistTable.PatientSex": "宠物性别",
+    "worklistTable.AdmittingTime": "入院时间",
+    "worklistTable.RegSource": "登记来源",
+    "worklistTable.StudyStatus": "检查状态",
+    "worklistTable.RequestedProcedureID": "请求的程序ID",
+    "worklistTable.PerformedProtocolCodeValue": "执行的协议代码值",
+    "worklistTable.PerformedProtocolCodeMeaning": "执行的协议代码含义",
+    "worklistTable.PerformedProcedureStepID": "执行的程序步骤ID",
+    "worklistTable.StudyDescription": "检查描述",
+    "worklistTable.StudyStartDatetime": "检查开始日期时间",
+    "worklistTable.ScheduledProcedureStepStartDate": "计划的程序步骤开始日期",
+    "worklistTable.StudyLock": "检查锁定",
+    "worklistTable.OperatorID": "操作员ID",
+    "worklistTable.Modality": "模态",
+    "worklistTable.Views": "视图",
+    "worklistTable.Thickness": "厚度",
+    "worklistTable.PatientType": "患者类型",
+    "worklistTable.StudyType": "检查类型",
+    "worklistTable.QRCode": "二维码",
+    "worklistTable.IsExported": "是否已导出",
+    "worklistTable.IsEdited": "是否已编辑",
+    "worklistTable.WorkRef": "工作参考",
+    "worklistTable.IsAppended": "是否已附加",
+    "worklistTable.CreationTime": "创建时间",
+    "worklistTable.MappedStatus": "映射状态",
+    "worklistTable.IsDelete": "是否已删除",
+    "login.username": "用户名",
+    "login.username.placeholder": "请输入用户名",
+    "login.username.required": "请输入用户名",
+    "login.password": "密码",
+    "login.password.placeholder": "请输入密码",
+    "login.password.required": "请输入密码",
+    "login.submit": "登录",
+    "login.emergency": "急诊",
+    "login.success": "登录成功",
+    "login.failed": "登录失败",
+    "login.failedDetail": ",详情:",
+    "login.networkError": ",网络错误:",
+    "login.unknownError": "未知错误",
+    "login.emergency.success": "急诊模式启动成功",
+    "login.emergency.failed": "急诊操作失败:",
+    "nav.config": "配置",
+    "nav.logout": "退出",
+    "register.filter.protocol": "协议",
+    "register.filter.view": "体位",
+    "register.register": "注册",
+    "register.exam": "检查",
+    "register.no.views": "暂无可选择体位",
+    "state.quota": "配额",
+    "exam.close.aec": "关闭 AEC",
+    "exam.open.aec": "打开 AEC",
+    "exam.thinkness": "厚度",
+    "exam.exposure.mode": "曝光模式",
+    "exam.bodysize.placeholder": "选择体型",
+    "exam.thickness.label": "厚度 (cm)",
+    "exam.thickness.placeholder": "厚度",
+    "exam.exposureMode.label": "曝光模式",
+    "exam.exposureMode.placeholder": "选择曝光模式",
+    "exam.aec.enabled": "开启AEC",
+    "exam.aec.disabled": "关闭AEC",
+    "exam.action.resetParams": "重置参数",
+    "exam.action.deletePosition": "删除选择的体位",
+    "exam.action.copyPosition": "复制选择的体位",
+    "exam.action.saveParams": "保存参数",
+    "exam.action.toggleCamera": "打开/关闭摄像头",
+    "exam.action.reject": "拒绝",
+    "exam.action.restore": "恢复",
+    "exam.action.addMorePositions": "添加更多体位"
   } : {
     greeting: 'Hello, world!',
     name: 'John Doe',

+ 7 - 5
cypress/support/mock/handlers/system.ts

@@ -32,11 +32,13 @@ export function mockGetSoftwareInfoSuccess() {
     body: {
       code: "0x000000",
       data: {
-        FPD: "Simulator",
-        GEN: "Simulator",
-        guest: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3NTY3MjAxMTUsImlkIjoyLCJuYW1lIjoiZ3Vlc3QifQ.cDkkxM2mkiCQf7T87WsCMewITk13c7jSDoniT7gDHXQ",
+        FPD: "Physics",
+        GEN: "Physics",
+        current_locale: "zh_CN",
+        default_locale: "zh_CN",
+        guest: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3NjA0OTc2NDksImlkIjoyLCJuYW1lIjoiZ3Vlc3QifQ.hUV5_GOzlWKDeFioJCsHMG2IXa0oJPfNNlPsr69ypqc",
         language: ["en", "zh"],
-        product: "DROS",
+        product: "VETDROS",
         server: {
           auth: {
             build: "2025-08-25 17:45:18",
@@ -87,7 +89,7 @@ export function mockGetSoftwareInfoSuccess() {
             version: "0.3.0-20-ge9ec04a"
           }
         },
-        sn: "2edbc382-044adc78-95bed11b-51c9328a"
+        sn: "19d5d2eb-8b720370-7d617b19-670dd1ae"
       },
       description: "Success",
       solution: ""

+ 30 - 0
cypress/support/pageObjects/LoginPage.ts

@@ -86,6 +86,36 @@ class LoginPage {
       this.getPasswordInput().should('have.attr', 'placeholder', 'Enter password');
     }
   }
+
+  // 版本更新弹窗相关方法
+  getVersionUpdateModal() {
+    return cy.get('[data-testid="version-update-modal"]');
+  }
+
+  getUpdateNowButton() {
+    return cy.get('[data-testid="update-now-btn"]');
+  }
+
+  getRemindLaterButton() {
+    return cy.get('[data-testid="remind-later-btn"]');
+  }
+
+  getSkipVersionButton() {
+    return cy.get('[data-testid="skip-version-btn"]');
+  }
+
+  // 下载进度弹窗相关方法
+  getDownloadProgressModal() {
+    return cy.get('[data-testid="download-progress-modal"]');
+  }
+
+  getDownloadProgressBar() {
+    return cy.get('[data-testid="download-progress-bar"]');
+  }
+
+  getDownloadCancelButton() {
+    return cy.get('[data-testid="download-cancel-btn"]');
+  }
 }
 
 export default LoginPage;