图像注释保存与重现功能测试方案.md 31 KB

图像注释保存与重现功能测试方案

文档信息


1. 测试概述

1.1 测试目标

验证图像注释的创建、保存、加载、重现、编辑和删除功能,确保所有类型的注释工具都能正常工作,并且注释数据能够正确持久化和恢复。

1.2 测试范围

  • 所有注释工具的创建功能
  • 注释自动保存机制
  • 注释数据的加载和重现
  • 注释编辑功能
  • 注释删除功能
  • 注释数据格式验证
  • API调用验证
  • Redux状态管理验证

1.3 测试环境

  • 浏览器: Chrome 90+, Edge 90+, Firefox 88+
  • 测试框架: Cypress E2E
  • Mock工具: Cypress Intercept
  • 前端框架: React + Redux Toolkit
  • 图像工具库: Cornerstone Tools

2. 注释工具类型清单

基于实际代码 src/pages/view/components/MeasurementPanel.tsx,系统支持以下测量工具:

2.1 基础测量工具

工具名称 工具标识(action) 图标名称 描述 优先级
线段测量 线段测量 btn_LineMeasurement 测量两点之间的直线距离 P0
角度测量 角度测量 btn_AngleMeasurement 测量三点形成的角度 P0
清除测量 清除测量 btn_ClearMeasurement 清除所有测量标记 P0
测量校正 测量校正 MeasurementCalibration 校正测量比例 P1

2.2 专业测量工具

工具名称 工具标识(action) 图标名称 描述 优先级
Cobb角 Cobb角 btn_Cobbangle 脊柱侧弯角度测量 P1

2.3 宠物专用测量工具

工具名称 工具标识(action) 图标名称 描述 优先级
髋臼水平角 髋臼水平角 btn_DAR 髋关节发育评估 P1
胫骨平台夹角 胫骨平台夹角 btn_TPA 膝关节角度测量 P1
髋关节牵引指数 髋关节牵引指数 btn_HDI 髋关节松弛度评估 P1
髋关节水平角 髋关节水平角 btn_NHA 髋关节角度测量 P1
心锥比 心锥比 btn_VHS 心脏大小评估 P1
胫骨平台骨切开术 胫骨平台骨切开术 btn_TPLO TPLO手术规划 P2
胫骨结节前移术 胫骨结节前移术 btn_TTA TTA手术规划 P2
胫骨结节前移术5点测量法 胫骨结节前移术5点测量法 btn_TTA2 TTA手术规划(5点法) P2
水平截骨术 水平截骨术 btn_CBLO CBLO手术规划 P2
股骨头覆盖率 股骨头覆盖率 btn_DLS 髋关节覆盖评估 P2
髋臼背覆盖 髋臼背覆盖 btn_DAC 髋关节覆盖评估 P2
拆线长度测量 拆线长度测量 btn_FoldLine 折线长度测量 P1
多边形长度测量 多边形长度测量 btn_Polygon 多边形周长测量 P1
找圆心 找圆心 btn_CenterCircle 定位圆形中心点 P1
找中线 找中线 btn_CenterLine 计算中线位置 P1
找中点 找中点 btn_Midpoint 计算线段中点 P1
直线垂直倾斜度 直线垂直倾斜度 btn_VerticalInclination 垂直倾斜角度 P1
直线水平倾斜度 直线水平倾斜度 btn_HorizontalInclination 水平倾斜角度 P1
矩形区域灰度 矩形区域灰度 btn_RectangularGrayscale 矩形区域灰度分析 P1
直线灰度 直线灰度 btn_LineGrayscale 直线灰度分布分析 P1

工具总数: 24个 (4个基础工具 + 1个专业工具 + 19个宠物专用工具)

注意: 这些测量工具主要用于宠物医学影像分析,包括骨科手术规划、心脏评估、髋关节评估等专业应用场景。


3. 测试套件设计

测试套件 1: 注释创建功能测试

文件路径: cypress/e2e/process/annotation-creation.cy.ts

TC-ANN-CREATE-01: 长度测量工具创建

测试目标: 验证LengthTool创建长度测量注释

前置条件:

  • 已登录系统
  • 已打开图像处理页面
  • 已加载测试图像

测试步骤:

  1. 选择长度测量工具
  2. 在图像上点击两个点创建测量线
  3. 验证测量值实时显示
  4. 验证注释保存API被调用

验证点:

  • ✅ 工具被正确激活
  • ✅ 可以在图像上创建测量线
  • ✅ 测量值正确显示(格式: "XX.X mm")
  • ✅ API调用: POST /dr/api/v1/auth/image/{id}/annotation
  • ✅ 请求体包含正确的toolName: "LengthTool"
  • ✅ 包含handles.points坐标数据
  • ✅ 包含cachedStats.length测量结果

Mock配置:

cy.intercept('POST', '/dr/api/v1/auth/image/*/annotation', {
  statusCode: 200,
  body: {
    code: '0x000000',
    description: 'Success',
    data: {}
  }
}).as('saveAnnotation');

优先级: P0 (高)


TC-ANN-CREATE-02: 角度测量工具创建

测试目标: 验证AngleTool创建角度测量注释

前置条件: 同TC-ANN-CREATE-01

测试步骤:

  1. 选择角度测量工具
  2. 在图像上点击三个点创建角度测量
  3. 验证角度值实时显示
  4. 验证注释保存API被调用

验证点:

  • ✅ 工具被正确激活
  • ✅ 可以在图像上创建角度测量
  • ✅ 角度值正确显示(格式: "XX.X°")
  • ✅ toolName: "AngleTool"
  • ✅ handles.points包含3个坐标点
  • ✅ cachedStats.angle包含角度值

优先级: P0 (高)


TC-ANN-CREATE-03: 文本标签工具创建

测试目标: 验证LabelTool创建文本标注

前置条件: 同TC-ANN-CREATE-01

测试步骤:

  1. 选择文本标签工具
  2. 在图像上点击位置
  3. 输入文本内容: "测试标注"
  4. 确认输入
  5. 验证注释保存API被调用

验证点:

  • ✅ 工具被正确激活
  • ✅ 可以在图像上放置文本标签
  • ✅ 文本内容正确显示
  • ✅ toolName: "LabelTool"
  • ✅ label字段包含文本内容
  • ✅ handles.textBox包含文本框位置

优先级: P0 (高)


TC-ANN-CREATE-04: Cobb角测量工具创建

测试目标: 验证Cobb角测量工具创建专业测量注释

前置条件: 同TC-ANN-CREATE-01

测试步骤:

  1. 选择Cobb角测量工具
  2. 按照工具要求标记脊柱关键点
  3. 验证Cobb角度值自动计算并显示
  4. 验证注释保存API被调用

验证点:

  • ✅ 工具被正确激活
  • ✅ 可以标记所有必需的解剖点
  • ✅ Cobb角度值正确计算并显示
  • ✅ action: "Cobb角"
  • ✅ handles.points包含脊柱关键点
  • ✅ cachedStats包含角度值
  • ✅ 包含元数据: viewPlaneNormal, viewUp等

优先级: P1 (中)


TC-ANN-CREATE-05: 宠物专用测量工具创建(髋关节水平角)

测试目标: 验证宠物专用测量工具(以髋关节水平角为例)创建专业测量

前置条件: 同TC-ANN-CREATE-01

测试步骤:

  1. 选择髋关节水平角测量工具
  2. 按照工具要求标记关键解剖点
  3. 验证角度值自动计算并显示
  4. 验证注释保存API被调用

验证点:

  • ✅ 工具被正确激活
  • ✅ 可以标记所有必需的解剖点
  • ✅ 角度值正确计算并显示
  • ✅ action: "髋关节水平角"
  • ✅ handles.points包含关键解剖点
  • ✅ cachedStats包含测量结果
  • ✅ 包含元数据: viewPlaneNormal, viewUp等

优先级: P1 (中)

: 其他宠物专用测量工具(如胫骨平台夹角、心锥比、矩形区域灰度等)的测试方法类似,主要验证:

  • 工具正确激活
  • 关键点标记
  • 测量结果计算
  • 注释保存

测试套件 2: 注释保存功能测试

文件路径: cypress/e2e/process/annotation-save.cy.ts

TC-ANN-SAVE-01: 自动保存机制验证

测试目标: 验证注释创建后自动保存

前置条件: 同TC-ANN-CREATE-01

测试步骤:

  1. 创建一个长度测量注释
  2. 等待保存API调用
  3. 验证API请求体数据格式

验证点:

  • ✅ 创建注释后1秒内触发保存API
  • ✅ API请求方法: POST
  • ✅ API路径: /dr/api/v1/auth/image/{sopInstanceUid}/annotation
  • ✅ 请求体为JSON格式
  • ✅ 包含必需字段: id, toolName, sopInstanceUid, handles, metadata
  • ✅ 包含时间戳: createdAt, updatedAt

优先级: P0 (高)


TC-ANN-SAVE-02: 防抖保存机制

测试目标: 验证编辑注释时的防抖保存

前置条件: 已创建一个注释

测试步骤:

  1. 拖拽注释手柄修改位置
  2. 连续快速拖拽多次
  3. 验证保存API调用次数

验证点:

  • ✅ 连续修改不会立即保存
  • ✅ 停止修改后1秒触发保存
  • ✅ 只发送一次保存请求(防抖)
  • ✅ 请求体包含最新的修改数据

优先级: P0 (高)


TC-ANN-SAVE-03: 批量保存优化

测试目标: 验证多个注释的批量保存

前置条件: 同TC-ANN-CREATE-01

测试步骤:

  1. 快速创建3个不同类型的注释
  2. 验证保存API调用

验证点:

  • ✅ 可以同时创建多个注释
  • ✅ 保存请求合理优化(批量或防抖)
  • ✅ 所有注释数据都被保存
  • ✅ 无重复保存请求

优先级: P1 (中)


TC-ANN-SAVE-04: 保存失败处理

测试目标: 验证保存失败时的错误处理

前置条件: Mock保存API返回错误

测试步骤:

  1. 创建一个注释
  2. Mock API返回500错误
  3. 验证错误处理机制

验证点:

  • ✅ 捕获API错误
  • ✅ 显示错误提示(可选)
  • ✅ 注释数据保留在本地
  • ✅ 用户可以重试保存
  • ✅ Redux状态标记为保存失败

Mock配置:

cy.intercept('POST', '/dr/api/v1/auth/image/*/annotation', {
  statusCode: 500,
  body: {
    code: '0x000001',
    description: 'Internal Server Error'
  }
}).as('saveAnnotationFail');

优先级: P0 (高)


测试套件 3: 注释加载和重现功能测试

文件路径: cypress/e2e/process/annotation-load.cy.ts

TC-ANN-LOAD-01: 自动加载注释

测试目标: 验证打开图像时自动加载注释

前置条件:

  • 已登录系统
  • Mock注释数据

测试步骤:

  1. 打开图像处理页面
  2. 验证加载注释API被调用
  3. 验证注释显示在图像上

验证点:

  • ✅ 图像加载完成后立即调用获取注释API
  • ✅ API路径: GET /dr/api/v1/auth/image/{sopInstanceUid}/annotation
  • ✅ 返回的注释数据被正确解析
  • ✅ 所有注释显示在图像上
  • ✅ 注释位置准确
  • ✅ 测量值正确显示

Mock数据示例:

{
  "code": "0x000000",
  "description": "Success",
  "data": [
    {
      "id": "ann-001",
      "toolName": "LengthTool",
      "sopInstanceUid": "1.2.3.4.5",
      "handles": {
        "points": [
          {"x": 100, "y": 100, "z": 0},
          {"x": 200, "y": 200, "z": 0}
        ]
      },
      "metadata": {
        "viewPlaneNormal": [0, 0, 1],
        "viewUp": [0, -1, 0],
        "FrameOfReferenceUID": "1.2.3",
        "referencedImageId": "image-1"
      },
      "cachedStats": {
        "length": 141.42
      },
      "createdAt": "2025-12-16T10:00:00Z",
      "updatedAt": "2025-12-16T10:00:00Z"
    }
  ]
}

优先级: P0 (高)


TC-ANN-LOAD-02: 加载多个注释

测试目标: 验证同时加载多个不同类型的注释

前置条件: Mock包含3个不同类型注释的数据

测试步骤:

  1. 打开图像
  2. 验证所有注释都被加载和显示

验证点:

  • ✅ 所有类型的注释都能正确加载
  • ✅ LengthTool注释正确显示
  • ✅ AngleTool注释正确显示
  • ✅ LabelTool注释正确显示
  • ✅ 注释顺序与数据顺序一致
  • ✅ 不同注释互不干扰

优先级: P0 (高)


TC-ANN-LOAD-03: 空注释数据处理

测试目标: 验证图像无注释时的处理

前置条件: Mock返回空注释列表

测试步骤:

  1. 打开图像
  2. 验证处理空数据的逻辑

验证点:

  • ✅ API返回空数组不报错
  • ✅ 图像正常显示
  • ✅ 无注释显示在图像上
  • ✅ 用户可以创建新注释

Mock配置:

cy.intercept('GET', '/dr/api/v1/auth/image/*/annotation', {
  statusCode: 200,
  body: {
    code: '0x000000',
    description: 'Success',
    data: []
  }
}).as('getEmptyAnnotations');

优先级: P1 (中)


TC-ANN-LOAD-04: 加载失败处理

测试目标: 验证加载注释失败时的处理

前置条件: Mock API返回错误

测试步骤:

  1. 打开图像
  2. Mock API返回500错误
  3. 验证错误处理

验证点:

  • ✅ 捕获API错误
  • ✅ 图像仍然正常显示
  • ✅ 显示错误提示(可选)
  • ✅ 用户可以重试加载
  • ✅ 不影响其他功能

优先级: P1 (中)


TC-ANN-LOAD-05: 注释数据验证

测试目标: 验证加载的注释数据格式验证

前置条件: Mock包含格式错误的注释数据

测试步骤:

  1. Mock返回缺少必需字段的注释
  2. 验证数据验证机制

验证点:

  • ✅ 识别数据格式错误
  • ✅ 跳过无效注释
  • ✅ 继续加载有效注释
  • ✅ 记录错误日志
  • ✅ 不崩溃或阻塞

优先级: P1 (中)


测试套件 4: 注释编辑功能测试

文件路径: cypress/e2e/process/annotation-edit.cy.ts

TC-ANN-EDIT-01: 拖拽修改注释位置

测试目标: 验证通过拖拽手柄修改注释

前置条件: 已加载包含注释的图像

测试步骤:

  1. 选择一个长度测量注释
  2. 拖拽手柄修改端点位置
  3. 验证测量值更新
  4. 验证保存API被调用

验证点:

  • ✅ 可以选中注释
  • ✅ 手柄可拖拽
  • ✅ 测量值实时更新
  • ✅ 触发保存API(防抖)
  • ✅ 新位置数据正确保存

优先级: P0 (高)


TC-ANN-EDIT-02: 修改文本标签内容

测试目标: 验证修改文本标注的内容

前置条件: 已加载包含文本标注的图像

测试步骤:

  1. 双击文本标注
  2. 修改文本内容
  3. 确认修改
  4. 验证保存API被调用

验证点:

  • ✅ 可以编辑文本内容
  • ✅ 文本立即更新显示
  • ✅ 触发保存API
  • ✅ 新文本内容正确保存

优先级: P0 (高)


TC-ANN-EDIT-03: 并发编辑冲突处理

测试目标: 验证多用户编辑冲突的处理

前置条件: Mock服务器返回版本冲突

测试步骤:

  1. 修改一个注释
  2. Mock服务器返回版本冲突错误
  3. 验证冲突处理机制

验证点:

  • ✅ 检测到版本冲突
  • ✅ 提示用户冲突情况
  • ✅ 提供冲突解决选项(可选)
  • ✅ 防止数据丢失

优先级: P2 (低)


测试套件 5: 注释删除功能测试

文件路径: cypress/e2e/process/annotation-delete.cy.ts

TC-ANN-DELETE-01: 单个注释删除

测试目标: 验证删除单个注释

前置条件: 已加载包含注释的图像

测试步骤:

  1. 选中一个注释
  2. 按Delete键或点击删除按钮
  3. 确认删除(如有提示)
  4. 验证注释被删除

验证点:

  • ✅ 可以选中注释
  • ✅ 删除操作生效
  • ✅ 注释从图像上消失
  • ✅ 调用删除API(如果是已保存的注释)
  • ✅ Redux状态更新

优先级: P0 (高)


TC-ANN-DELETE-02: 批量删除注释

测试目标: 验证批量删除多个注释

前置条件: 已加载包含多个注释的图像

测试步骤:

  1. 选择多个注释(Ctrl+点击)
  2. 执行批量删除
  3. 验证所有选中的注释被删除

验证点:

  • ✅ 可以多选注释
  • ✅ 批量删除生效
  • ✅ 所有选中注释被删除
  • ✅ 未选中注释保留
  • ✅ 批量调用删除API或单次批量删除

优先级: P1 (中)


TC-ANN-DELETE-03: 删除确认提示

测试目标: 验证删除确认对话框

前置条件: 配置启用删除确认

测试步骤:

  1. 尝试删除注释
  2. 验证弹出确认对话框
  3. 点击取消
  4. 验证注释未被删除
  5. 再次删除并确认
  6. 验证注释被删除

验证点:

  • ✅ 删除前显示确认对话框
  • ✅ 取消操作保留注释
  • ✅ 确认操作删除注释
  • ✅ 对话框文本清晰明确

优先级: P1 (中)


测试套件 6: 数据格式和API测试

文件路径: cypress/e2e/process/annotation-api.cy.ts

TC-ANN-API-01: 保存API请求格式验证

测试目标: 验证保存注释的API请求格式

前置条件: 已创建注释

测试步骤:

  1. 拦截保存API请求
  2. 验证请求格式

验证点:

  • ✅ 请求方法: POST
  • ✅ Content-Type: application/json
  • ✅ 请求体包含所有必需字段
  • ✅ sopInstanceUid正确
  • ✅ toolName正确
  • ✅ handles数据格式正确
  • ✅ metadata数据格式正确
  • ✅ 时间戳格式正确(ISO 8601)

优先级: P0 (高)


TC-ANN-API-02: 获取API响应格式验证

测试目标: 验证获取注释的API响应处理

前置条件: Mock正确格式的响应

测试步骤:

  1. 打开图像触发获取API
  2. 验证响应数据处理

验证点:

  • ✅ 正确解析响应JSON
  • ✅ 正确处理data数组
  • ✅ 正确反序列化注释对象
  • ✅ 坐标数据正确转换
  • ✅ 元数据正确恢复

优先级: P0 (高)


TC-ANN-API-03: API错误码处理

测试目标: 验证各种API错误码的处理

前置条件: Mock不同错误响应

测试步骤:

  1. Mock不同错误码(401, 403, 404, 500等)
  2. 验证错误处理逻辑

验证点:

  • ✅ 401: 提示重新登录
  • ✅ 403: 提示权限不足
  • ✅ 404: 提示资源不存在
  • ✅ 500: 提示服务器错误
  • ✅ 网络错误: 提示网络问题
  • ✅ 所有错误都有用户友好提示

优先级: P1 (中)


测试套件 7: 性能和边界测试

文件路径: cypress/e2e/process/annotation-performance.cy.ts

TC-ANN-PERF-01: 大量注释加载性能

测试目标: 验证加载大量注释时的性能

前置条件: Mock包含100个注释的数据

测试步骤:

  1. 打开包含100个注释的图像
  2. 测量加载和渲染时间

验证点:

  • ✅ 加载时间 < 3秒
  • ✅ 所有注释正确显示
  • ✅ UI不卡顿
  • ✅ 内存使用合理

优先级: P1 (中)


TC-ANN-PERF-02: 注释数据大小限制

测试目标: 验证注释数据大小限制

前置条件: 尝试创建超大注释

测试步骤:

  1. 创建包含大量坐标点的注释
  2. 验证大小限制机制

验证点:

  • ✅ 单个注释 < 100KB
  • ✅ 超过限制时提示用户
  • ✅ 防止创建过大的注释

优先级: P2 (低)


TC-ANN-PERF-03: 并发操作性能

测试目标: 验证快速连续操作的性能

前置条件: 已加载图像

测试步骤:

  1. 快速连续创建多个注释
  2. 快速连续修改注释
  3. 验证系统响应

验证点:

  • ✅ 操作响应及时
  • ✅ 防抖机制有效
  • ✅ 不会重复保存
  • ✅ UI保持流畅

优先级: P1 (中)


4. 测试数据准备

4.1 Mock注释数据模板

长度测量注释

{
  "id": "length-001",
  "toolName": "LengthTool",
  "sopInstanceUid": "1.2.840.10008.5.1.4.1.1.2.1",
  "handles": {
    "points": [
      {"x": 100, "y": 100, "z": 0},
      {"x": 200, "y": 200, "z": 0}
    ]
  },
  "metadata": {
    "viewPlaneNormal": [0, 0, 1],
    "viewUp": [0, -1, 0],
    "FrameOfReferenceUID": "1.2.3.4.5",
    "referencedImageId": "wadouri:http://example.com/image.dcm"
  },
  "cachedStats": {
    "length": 141.42
  },
  "label": "Length Measurement",
  "createdAt": "2025-12-16T10:00:00.000Z",
  "updatedAt": "2025-12-16T10:00:00.000Z",
  "userId": "user-001"
}

角度测量注释

{
  "id": "angle-001",
  "toolName": "AngleTool",
  "sopInstanceUid": "1.2.840.10008.5.1.4.1.1.2.1",
  "handles": {
    "points": [
      {"x": 100, "y": 100, "z": 0},
      {"x": 150, "y": 150, "z": 0},
      {"x": 200, "y": 100, "z": 0}
    ]
  },
  "metadata": {
    "viewPlaneNormal": [0, 0, 1],
    "viewUp": [0, -1, 0],
    "FrameOfReferenceUID": "1.2.3.4.5",
    "referencedImageId": "wadouri:http://example.com/image.dcm"
  },
  "cachedStats": {
    "angle": 90.0
  },
  "label": "Angle Measurement",
  "createdAt": "2025-12-16T10:01:00.000Z",
  "updatedAt": "2025-12-16T10:01:00.000Z",
  "userId": "user-001"
}

文本标注

{
  "id": "label-001",
  "toolName": "LabelTool",
  "sopInstanceUid": "1.2.840.10008.5.1.4.1.1.2.1",
  "handles": {
    "points": [
      {"x": 150, "y": 150, "z": 0}
    ],
    "textBox": {"x": 160, "y": 150}
  },
  "metadata": {
    "viewPlaneNormal": [0, 0, 1],
    "viewUp": [0, -1, 0],
    "FrameOfReferenceUID": "1.2.3.4.5",
    "referencedImageId": "wadouri:http://example.com/image.dcm"
  },
  "label": "疑似结节",
  "createdAt": "2025-12-16T10:02:00.000Z",
  "updatedAt": "2025-12-16T10:02:00.000Z",
  "userId": "user-001"
}

4.2 Mock Handlers

文件: cypress/support/mock/handlers/annotation.ts

/**
 * Mock获取注释成功
 */
export const mockGetAnnotationsSuccess = (annotations: any[]) => {
  cy.intercept('GET', '/dr/api/v1/auth/image/*/annotation', {
    statusCode: 200,
    body: {
      code: '0x000000',
      description: 'Success',
      data: annotations
    }
  }).as('getAnnotations');
};

/**
 * Mock保存注释成功
 */
export const mockSaveAnnotationSuccess = () => {
  cy.intercept('POST', '/dr/api/v1/auth/image/*/annotation', {
    statusCode: 200,
    body: {
      code: '0x000000',
      description: 'Success',
      data: {}
    }
  }).as('saveAnnotation');
};

/**
 * Mock删除注释成功
 */
export const mockDeleteAnnotationSuccess = () => {
  cy.intercept('DELETE', '/dr/api/v1/auth/image/*/annotation/*', {
    statusCode: 200,
    body: {
      code: '0x000000',
      description: 'Success',
      data: {}
    }
  }).as('deleteAnnotation');
};

/**
 * Mock API失败
 */
export const mockAnnotationAPIFail = (statusCode: number = 500) => {
  cy.intercept('**/annotation**', {
    statusCode: statusCode,
    body: {
      code: '0x000001',
      description: 'Internal Server Error'
    }
  }).as('annotationAPIFail');
};

5. Page Object Model

ProcessPage扩展

文件: cypress/support/pageObjects/ProcessPage.ts

class ProcessPage {
  // 工具选择
  selectLengthTool() {
    cy.get('[data-testid="length-tool-btn"]').click();
  }

  selectAngleTool() {
    cy.get('[data-testid="angle-tool-btn"]').click();
  }

  selectLabelTool() {
    cy.get('[data-testid="label-tool-btn"]').click();
  }

  selectAreaTool() {
    cy.get('[data-testid="area-tool-btn"]').click();
  }

  // 注释操作
  createLengthAnnotation(point1: {x: number, y: number}, point2: {x: number, y: number}) {
    this.selectLengthTool();
    cy.get('.viewport-canvas').click(point1.x, point1.y);
    cy.get('.viewport-canvas').click(point2.x, point2.y);
  }

  createAngleAnnotation(
    point1: {x: number, y: number},
    point2: {x: number, y: number},
    point3: {x: number, y: number}
  ) {
    this.selectAngleTool();
    cy.get('.viewport-canvas').click(point1.x, point1.y);
    cy.get('.viewport-canvas').click(point2.x, point2.y);
    cy.get('.viewport-canvas').click(point3.x, point3.y);
  }

  createLabelAnnotation(position: {x: number, y: number}, text: string) {
    this.selectLabelTool();
    cy.get('.viewport-canvas').click(position.x, position.y);
    cy.get('input[type="text"]').type(text);
    cy.get('input[type="text"]').type('{enter}');
  }

  // 验证注释显示
  verifyAnnotationVisible(toolName: string) {
    cy.get(`.annotation-${toolName.toLowerCase()}`).should('be.visible');
  }

  verifyMeasurementValue(value: string) {
    cy.get('.measurement-value').should('contain', value);
  }

  // 注释编辑
  selectAnnotation(annotationId: string) {
    cy.get(`[data-annotation-id="${annotationId}"]`).click();
  }

  deleteSelectedAnnotation() {
    cy.get('body').type('{del}');
  }

  // Redux状态验证
  verifyAnnotationInStore(annotationId: string) {
    cy.window().its('store').invoke('getState')
      .its('annotations').its('list')
      .should('contain', annotationId);
  }

  // API调用验证
  waitForSaveAnnotation() {
    cy.wait('@saveAnnotation');
  }

  waitForLoadAnnotations() {
    cy.wait('@getAnnotations');
  }

  verifyAnnotationCount(count: number) {
    cy.get('.annotation-item').should('have.length', count);
  }
}

export default ProcessPage;

6. 测试执行计划

第一阶段: 核心功能测试 (优先级: P0)

时间: 3-4天

  1. ✅ TC-ANN-CREATE-01: 长度测量工具创建
  2. ✅ TC-ANN-CREATE-02: 角度测量工具创建
  3. ✅ TC-ANN-CREATE-03: 文本标签工具创建
  4. ✅ TC-ANN-SAVE-01: 自动保存机制
  5. ✅ TC-ANN-SAVE-02: 防抖保存机制
  6. ✅ TC-ANN-SAVE-04: 保存失败处理
  7. ✅ TC-ANN-LOAD-01: 自动加载注释
  8. ✅ TC-ANN-LOAD-02: 加载多个注释
  9. ✅ TC-ANN-EDIT-01: 拖拽修改注释
  10. ✅ TC-ANN-EDIT-02: 修改文本内容
  11. ✅ TC-ANN-DELETE-01: 单个注释删除
  12. ✅ TC-ANN-API-01: 保存API格式验证
  13. ✅ TC-ANN-API-02: 获取API响应验证

第二阶段: 扩展功能测试 (优先级: P1)

时间: 2-3天

  1. ✅ TC-ANN-CREATE-04: 面积测量工具
  2. ✅ TC-ANN-CREATE-05: 髋关节角度测量
  3. ✅ TC-ANN-SAVE-03: 批量保存优化
  4. ✅ TC-ANN-LOAD-03: 空注释数据处理
  5. ✅ TC-ANN-LOAD-04: 加载失败处理
  6. ✅ TC-ANN-LOAD-05: 数据验证
  7. ✅ TC-ANN-DELETE-02: 批量删除
  8. ✅ TC-ANN-DELETE-03: 删除确认
  9. ✅ TC-ANN-API-03: 错误码处理
  10. ✅ TC-ANN-PERF-01: 大量注释性能
  11. ✅ TC-ANN-PERF-03: 并发操作性能

第三阶段: 边界和优化测试 (优先级: P2)

时间: 1-2天

  1. ✅ TC-ANN-EDIT-03: 并发编辑冲突
  2. ✅ TC-ANN-PERF-02: 数据大小限制
  3. 浏览器兼容性测试
  4. 性能基准测试

7. 测试覆盖率目标

  • 功能覆盖: 100% - 所有注释类型和操作
  • 场景覆盖:
    • 正常场景: 100%
    • 边界场景: 90%+
    • 异常场景: 85%+
  • API覆盖: 100% - 所有注释相关API
  • 工具覆盖: 100% - 所有24种测量工具

8. 验收标准

8.1 功能验收

  • 所有P0测试用例通过 (100%)
  • 至少90%的P1测试用例通过
  • 所有注释工具可正常创建注释
  • 注释保存和加载功能稳定
  • 注释编辑和删除功能正常
  • API调用格式正确

8.2 性能验收

  • 单个注释创建响应时间 < 100ms
  • 注释保存时间 < 1s
  • 加载50个注释 < 1s
  • 加载200个注释 < 5s
  • UI操作流畅,无明显卡顿

8.3 稳定性验收

  • 所有测试用例可重复执行
  • 无随机失败的测试
  • 错误处理机制完善
  • 无内存泄漏
  • 长时间运行稳定

8.4 用户体验验收

  • 工具选择直观
  • 注释创建流畅
  • 测量值准确显示
  • 错误提示清晰
  • 操作可撤销(如适用)

9. 风险评估

9.1 技术风险

风险项 影响 概率 缓解措施
Cornerstone Tools集成问题 深入学习Cornerstone API,准备降级方案
坐标系转换错误 详细测试各种图像类型,验证坐标精度
大数据量性能问题 实现虚拟化渲染,分批加载
浏览器兼容性问题 多浏览器测试,使用polyfill

9.2 业务风险

风险项 影响 概率 缓解措施
需求变更 模块化设计,易于扩展
用户接受度低 用户测试,收集反馈
数据丢失 实现本地缓存,离线支持

10. 测试环境搭建

10.1 依赖的Mock Handlers

  1. 用户认证: mockLoginSuccess()
  2. 国际化: mockI18nSuccess()
  3. 配额: mockQuotaCheck()
  4. 图像加载: mockImageLoad()
  5. 注释API: mockGetAnnotationsSuccess(), mockSaveAnnotationSuccess()

10.2 测试数据要求

  • 至少3种不同类型的注释数据
  • 包含正常、边界、异常情况的数据
  • 足够的数据量进行性能测试

10.3 环境配置

// cypress.config.ts
export default {
  e2e: {
    baseUrl: 'http://localhost:3000',
    viewportWidth: 1920,
    viewportHeight: 1080,
    video: false,
    defaultCommandTimeout: 10000
  }
};

11. 测试执行指南

11.1 运行所有注释测试

npx cypress run --spec "cypress/e2e/process/annotation-*.cy.ts"

11.2 运行特定测试套件

# 创建功能测试
npx cypress run --spec "cypress/e2e/process/annotation-creation.cy.ts"

# 保存功能测试
npx cypress run --spec "cypress/e2e/process/annotation-save.cy.ts"

# 加载功能测试
npx cypress run --spec "cypress/e2e/process/annotation-load.cy.ts"

11.3 在Cypress UI中运行

npx cypress open

然后选择相应的测试文件。


12. 测试报告

12.1 测试结果记录表

测试套件 用例总数 通过 失败 跳过 通过率 执行时间
注释创建 5 - - - - -
注释保存 4 - - - - -
注释加载 5 - - - - -
注释编辑 3 - - - - -
注释删除 3 - - - - -
API测试 3 - - - - -
性能测试 3 - - - - -
总计 26 - - - - -

12.2 缺陷记录模板

缺陷ID: BUG-ANN-XXX
测试用例: TC-ANN-XXX
严重程度: Critical / Major / Minor
优先级: P0 / P1 / P2

复现步骤:
1. ...
2. ...
3. ...

实际结果: ...
预期结果: ...
附件: (截图/视频)

13. 附录

A. 注释数据结构参考

interface AnnotationData {
  // 基础信息
  id: string;
  toolName: string;
  sopInstanceUid: string;

  // 几何数据
  handles: {
    points: Point3[];
    activeHandleIndex?: number;
    textBox?: Point2;
  };

  // 元数据
  metadata: {
    viewPlaneNormal: Point3;
    viewUp: Point3;
    FrameOfReferenceUID: string;
    referencedImageId: string;
  };

  // 计算结果
  cachedStats?: {
    length?: number;
    angle?: number;
    area?: number;
  };

  // 显示属性
  label?: string;
  highlighted?: boolean;
  isSelected?: boolean;

  // 时间戳
  createdAt: string;
  updatedAt: string;
  userId?: string;
}

B. API接口文档

获取注释

请求:

GET /dr/api/v1/auth/image/{sopInstanceUid}/annotation

响应:

{
  "code": "0x000000",
  "description": "Success",
  "data": [AnnotationData]
}

保存注释

请求:

POST /dr/api/v1/auth/image/{sopInstanceUid}/annotation
Content-Type: application/json

AnnotationData

响应:

{
  "code": "0x000000",
  "description": "Success",
  "data": {}
}

删除注释

请求:

DELETE /dr/api/v1/auth/image/{sopInstanceUid}/annotation/{annotationId}

响应:

{
  "code": "0x000000",
  "description": "Success",
  "data": {}
}

文档版本

  • 版本: 1.0.0
  • 创建日期: 2025-12-16
  • 最后更新: 2025-12-16
  • 作者: Cline AI Assistant
  • 审核状态: ⏳ 待审核

测试方案状态: ✅ 已完成