|
|
@@ -0,0 +1,1347 @@
|
|
|
+# 图像注释保存与重现功能测试方案
|
|
|
+
|
|
|
+## 文档信息
|
|
|
+
|
|
|
+- **测试方案版本**: 1.0
|
|
|
+- **创建日期**: 2025-12-16
|
|
|
+- **最后更新**: 2025-12-16
|
|
|
+- **相关需求文档**: [图像注释保存与重现功能需求](../需求/图像注释保存与重现功能需求.md)
|
|
|
+- **相关实现文档**: [保存注释功能实现文档](../实现/保存注释功能实现文档.md)
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 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配置**:
|
|
|
+```typescript
|
|
|
+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配置**:
|
|
|
+```typescript
|
|
|
+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数据示例**:
|
|
|
+```json
|
|
|
+{
|
|
|
+ "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配置**:
|
|
|
+```typescript
|
|
|
+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注释数据模板
|
|
|
+
|
|
|
+#### 长度测量注释
|
|
|
+```json
|
|
|
+{
|
|
|
+ "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"
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+#### 角度测量注释
|
|
|
+```json
|
|
|
+{
|
|
|
+ "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"
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+#### 文本标注
|
|
|
+```json
|
|
|
+{
|
|
|
+ "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`
|
|
|
+
|
|
|
+```typescript
|
|
|
+/**
|
|
|
+ * 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`
|
|
|
+
|
|
|
+```typescript
|
|
|
+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天
|
|
|
+
|
|
|
+14. ✅ TC-ANN-CREATE-04: 面积测量工具
|
|
|
+15. ✅ TC-ANN-CREATE-05: 髋关节角度测量
|
|
|
+16. ✅ TC-ANN-SAVE-03: 批量保存优化
|
|
|
+17. ✅ TC-ANN-LOAD-03: 空注释数据处理
|
|
|
+18. ✅ TC-ANN-LOAD-04: 加载失败处理
|
|
|
+19. ✅ TC-ANN-LOAD-05: 数据验证
|
|
|
+20. ✅ TC-ANN-DELETE-02: 批量删除
|
|
|
+21. ✅ TC-ANN-DELETE-03: 删除确认
|
|
|
+22. ✅ TC-ANN-API-03: 错误码处理
|
|
|
+23. ✅ TC-ANN-PERF-01: 大量注释性能
|
|
|
+24. ✅ TC-ANN-PERF-03: 并发操作性能
|
|
|
+
|
|
|
+### 第三阶段: 边界和优化测试 (优先级: P2)
|
|
|
+
|
|
|
+**时间**: 1-2天
|
|
|
+
|
|
|
+25. ✅ TC-ANN-EDIT-03: 并发编辑冲突
|
|
|
+26. ✅ TC-ANN-PERF-02: 数据大小限制
|
|
|
+27. 浏览器兼容性测试
|
|
|
+28. 性能基准测试
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 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 环境配置
|
|
|
+
|
|
|
+```typescript
|
|
|
+// cypress.config.ts
|
|
|
+export default {
|
|
|
+ e2e: {
|
|
|
+ baseUrl: 'http://localhost:3000',
|
|
|
+ viewportWidth: 1920,
|
|
|
+ viewportHeight: 1080,
|
|
|
+ video: false,
|
|
|
+ defaultCommandTimeout: 10000
|
|
|
+ }
|
|
|
+};
|
|
|
+```
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 11. 测试执行指南
|
|
|
+
|
|
|
+### 11.1 运行所有注释测试
|
|
|
+
|
|
|
+```bash
|
|
|
+npx cypress run --spec "cypress/e2e/process/annotation-*.cy.ts"
|
|
|
+```
|
|
|
+
|
|
|
+### 11.2 运行特定测试套件
|
|
|
+
|
|
|
+```bash
|
|
|
+# 创建功能测试
|
|
|
+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中运行
|
|
|
+
|
|
|
+```bash
|
|
|
+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. 注释数据结构参考
|
|
|
+
|
|
|
+```typescript
|
|
|
+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
|
|
|
+```
|
|
|
+
|
|
|
+**响应**:
|
|
|
+```json
|
|
|
+{
|
|
|
+ "code": "0x000000",
|
|
|
+ "description": "Success",
|
|
|
+ "data": [AnnotationData]
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+#### 保存注释
|
|
|
+
|
|
|
+**请求**:
|
|
|
+```
|
|
|
+POST /dr/api/v1/auth/image/{sopInstanceUid}/annotation
|
|
|
+Content-Type: application/json
|
|
|
+
|
|
|
+AnnotationData
|
|
|
+```
|
|
|
+
|
|
|
+**响应**:
|
|
|
+```json
|
|
|
+{
|
|
|
+ "code": "0x000000",
|
|
|
+ "description": "Success",
|
|
|
+ "data": {}
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+#### 删除注释
|
|
|
+
|
|
|
+**请求**:
|
|
|
+```
|
|
|
+DELETE /dr/api/v1/auth/image/{sopInstanceUid}/annotation/{annotationId}
|
|
|
+```
|
|
|
+
|
|
|
+**响应**:
|
|
|
+```json
|
|
|
+{
|
|
|
+ "code": "0x000000",
|
|
|
+ "description": "Success",
|
|
|
+ "data": {}
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 文档版本
|
|
|
+
|
|
|
+- **版本**: 1.0.0
|
|
|
+- **创建日期**: 2025-12-16
|
|
|
+- **最后更新**: 2025-12-16
|
|
|
+- **作者**: Cline AI Assistant
|
|
|
+- **审核状态**: ⏳ 待审核
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+**测试方案状态**: ✅ 已完成
|