# DOM 渲染功能测试指南 ## 🎯 测试目标 验证从 Konva Canvas 迁移到 DOM 渲染后,所有功能正常工作,特别是在微信小程序环境中。 ## 🔧 测试环境 ### 浏览器环境 - Chrome/Edge (开发调试) - Safari (iOS 兼容性) - 微信开发者工具 (小程序环境) ### 设备 - 桌面端 (鼠标操作) - 移动端 (触摸操作) - 平板 (双指缩放) ## ✅ 功能测试清单 ### 1. 基础绘制功能 #### 1.1 矩形工具 **测试步骤**: 1. 选择矩形工具 2. 在画布上按下鼠标/手指 3. 拖动到目标位置 4. 释放鼠标/手指 **预期结果**: - ✅ 矩形正确绘制 - ✅ 矩形位置、大小准确 - ✅ 矩形颜色、边框宽度符合设置 - ✅ 可以绘制负方向矩形(从右下往左上拖) **测试数据**: ```typescript // 预期数据结构 { id: "shape_xxx", type: "rect", x: 100, y: 100, width: 200, height: 150, stroke: "#ff0000", strokeWidth: 2 } ``` #### 1.2 笔工具(线条) **测试步骤**: 1. 选择笔工具 2. 在画布上绘制任意路径 3. 释放鼠标/手指 **预期结果**: - ✅ 线条流畅,无断点 - ✅ 线条颜色、粗细符合设置 - ✅ 线条端点圆滑(lineCap: round) - ✅ 快速绘制时不丢失点 **测试数据**: ```typescript { id: "shape_xxx", type: "line", x: 0, y: 0, points: [100, 100, 105, 102, 110, 105, ...], stroke: "#ff0000", strokeWidth: 2 } ``` #### 1.3 文本注释 **测试步骤**: 1. 选择文本工具 2. 点击画布位置 3. 输入文本内容 4. 确认 **预期结果**: - ✅ 弹出输入框 - ✅ 文本正确显示在点击位置 - ✅ 文本大小、颜色符合设置 - ✅ 支持多行文本 - ✅ 支持特殊字符 **测试用例**: - 普通文本: "扣1分" - 数字: "123" - 特殊字符: "+5", "-2" - 多行文本: "第一行\n第二行" #### 1.4 特殊标记 **正确标记 ✓**: - ✅ 显示红色勾号 - ✅ 位置准确 - ✅ 大小适中(20x20) **错误标记 ✗**: - ✅ 显示红色叉号 - ✅ 位置准确 - ✅ 大小适中(20x20) **半对标记**: - ✅ 显示勾号+斜线组合 - ✅ 位置准确 - ✅ 大小适中(20x20) ### 2. 擦除功能 #### 2.1 点击擦除 **测试步骤**: 1. 绘制多个不同类型的标记 2. 选择擦除工具 3. 点击各个标记 **预期结果**: - ✅ 点击矩形内部,矩形被删除 - ✅ 点击线条附近(5px内),线条被删除 - ✅ 点击特殊标记,标记被删除 - ✅ 点击文本,文本被删除 - ✅ 点击空白处,无反应 **碰撞检测测试**: ```typescript // 矩形边界测试 点击 (100, 100) -> 在矩形内 ✅ 点击 (99, 99) -> 在矩形外 ❌ 点击 (300, 250) -> 在矩形内 ✅ 点击 (301, 251) -> 在矩形外 ❌ // 线条距离测试 点击距离线条 3px -> 命中 ✅ 点击距离线条 10px -> 未命中 ❌ // 特殊标记测试(中心点 100, 100) 点击 (100, 100) -> 命中 ✅ 点击 (90, 90) -> 命中 ✅ 点击 (110, 110) -> 命中 ✅ 点击 (89, 89) -> 未命中 ❌ ``` #### 2.2 拖动擦除 **测试步骤**: 1. 绘制多个标记 2. 选择擦除工具 3. 按住鼠标/手指拖动经过标记 **预期结果**: - ✅ 经过的标记被连续删除 - ✅ 擦除流畅,无卡顿 ### 3. 撤销/重做功能 #### 3.1 撤销操作 **测试步骤**: 1. 依次绘制:矩形 -> 线条 -> 文本 2. 点击撤销按钮 3 次 **预期结果**: - ✅ 第1次撤销:文本消失 - ✅ 第2次撤销:线条消失 - ✅ 第3次撤销:矩形消失 - ✅ 第4次撤销:按钮禁用,无反应 **命令栈验证**: ```typescript // 初始状态 commandStack: [] currentIndex: -1 canUndo: false // 绘制矩形后 commandStack: [AddShapeCommand(rect)] currentIndex: 0 canUndo: true // 绘制线条后 commandStack: [AddShapeCommand(rect), AddShapeCommand(line)] currentIndex: 1 canUndo: true // 撤销一次后 commandStack: [AddShapeCommand(rect), AddShapeCommand(line)] currentIndex: 0 canUndo: true canRedo: true ``` #### 3.2 重做操作 **测试步骤**: 1. 绘制标记 2. 撤销 3. 点击重做 **预期结果**: - ✅ 标记重新出现 - ✅ 位置、样式与原来一致 #### 3.3 撤销后新建 **测试步骤**: 1. 绘制:A -> B -> C 2. 撤销 2 次(剩下 A) 3. 绘制新标记 D **预期结果**: - ✅ B、C 从命令栈中移除 - ✅ 不能重做 B、C - ✅ 可以撤销 D ```typescript // 撤销2次后 commandStack: [A, B, C] currentIndex: 0 // 绘制D后 commandStack: [A, D] // B、C被截断 currentIndex: 1 canRedo: false ``` #### 3.4 清空所有标记 **测试步骤**: 1. 绘制多个标记 2. 点击"清空"按钮 3. 点击撤销 **预期结果**: - ✅ 所有标记消失 - ✅ 撤销后所有标记恢复 ### 4. 缩放功能 #### 4.1 手动缩放 **测试步骤**: 1. 设置缩放比例为 0.5 2. 绘制标记 3. 设置缩放比例为 2.0 **预期结果**: - ✅ 图片大小正确变化 - ✅ 标记跟随图片缩放 - ✅ 标记位置相对图片不变 - ✅ 新绘制的标记坐标正确 **坐标转换验证**: ```typescript // 缩放 0.5 时点击屏幕 (100, 100) 实际坐标 = (100 / 0.5, 100 / 0.5) = (200, 200) // 缩放 2.0 时点击屏幕 (100, 100) 实际坐标 = (100 / 2.0, 100 / 2.0) = (50, 50) ``` #### 4.2 自适应宽度模式 **测试步骤**: 1. 启用自适应宽度 2. 调整浏览器窗口宽度 **预期结果**: - ✅ 图片宽度自动适应容器 - ✅ 高度按比例缩放 - ✅ 标记位置正确 #### 4.3 双指缩放(触摸设备) **测试步骤**: 1. 在触摸设备上打开 2. 双指捏合/展开 **预期结果**: - ✅ 图片跟随手势缩放 - ✅ 显示缩放百分比提示 - ✅ 标记跟随缩放 ### 5. 快捷打分功能 #### 5.1 加分模式 **测试步骤**: 1. 设置快捷打分:加分模式,分值 2 2. 启用快捷打分点击模式 3. 点击画布 **预期结果**: - ✅ 显示 "+2" 文本标记 - ✅ 分数增加 2 分 - ✅ 文本颜色为红色 - ✅ 不能超过满分 **边界测试**: ```typescript 满分: 10 当前: 9 点击加2 -> 分数变为 10 ✅ 再次点击 -> 无反应 ✅ ``` #### 5.2 减分模式 **测试步骤**: 1. 设置快捷打分:减分模式,分值 1 2. 启用快捷打分点击模式 3. 点击画布 **预期结果**: - ✅ 显示 "-1" 文本标记 - ✅ 分数减少 1 分 - ✅ 文本颜色为蓝色 - ✅ 不能低于 0 分 ### 6. 多图片场景 #### 6.1 多图片渲染 **测试步骤**: 1. 加载包含 3 张图片的题目 2. 在每张图片上绘制不同标记 **预期结果**: - ✅ 每张图片独立渲染 - ✅ 标记数据互不干扰 - ✅ 撤销操作针对最后操作的图片 #### 6.2 横向/纵向布局 **测试步骤**: 1. 切换图片布局模式 **预期结果**: - ✅ 横向布局:图片水平排列 - ✅ 纵向布局:图片垂直排列 - ✅ 标记位置不受影响 ### 7. 只读模式 #### 7.1 历史记录查看 **测试步骤**: 1. 打开已批改的试卷 2. 查看标记 **预期结果**: - ✅ 所有标记正确显示 - ✅ 不响应点击事件 - ✅ 不显示工具栏 ### 8. 数据持久化 #### 8.1 数据导出 **测试步骤**: 1. 绘制多种标记 2. 导出数据 **预期结果**: ```json { "version": "1.0", "shapes": [ { "id": "shape_xxx", "type": "rect", "x": 100, "y": 100, "width": 200, "height": 150, "stroke": "#ff0000", "strokeWidth": 2 } ], "specialMarks": [ { "id": "mark_xxx", "type": "correct", "x": 300, "y": 200 } ], "annotations": [ { "id": "text_xxx", "x": 400, "y": 300, "text": "扣1分", "fontSize": 14, "color": "#000000" } ] } ``` #### 8.2 数据导入 **测试步骤**: 1. 导入上述 JSON 数据 2. 查看渲染结果 **预期结果**: - ✅ 所有标记正确恢复 - ✅ 位置、样式一致 ### 9. 性能测试 #### 9.1 大量标记渲染 **测试步骤**: 1. 导入包含 100+ 标记的数据 2. 观察渲染性能 **预期结果**: - ✅ 初始渲染 < 1s - ✅ 滚动流畅,无卡顿 - ✅ 内存占用合理 #### 9.2 快速连续绘制 **测试步骤**: 1. 快速绘制多条线条 **预期结果**: - ✅ 线条流畅,无延迟 - ✅ 不丢失点 - ✅ 不出现重复标记 ### 10. 兼容性测试 #### 10.1 微信小程序 **测试步骤**: 1. 在微信开发者工具中运行 2. 测试所有功能 **预期结果**: - ✅ 所有功能正常 - ✅ 触摸事件响应正常 - ✅ 无控制台错误 #### 10.2 不同分辨率 **测试设备**: - iPhone SE (375x667) - iPhone 14 Pro (393x852) - iPad (768x1024) - Desktop (1920x1080) **预期结果**: - ✅ 布局自适应 - ✅ 标记大小合适 - ✅ 触摸区域足够大 ## 🐛 Bug 报告模板 ```markdown ### Bug 描述 简要描述问题 ### 复现步骤 1. 步骤1 2. 步骤2 3. 步骤3 ### 预期行为 应该发生什么 ### 实际行为 实际发生了什么 ### 环境信息 - 设备: iPhone 14 - 系统: iOS 16.0 - 浏览器: 微信开发者工具 - 版本: 1.0.0 ### 截图/录屏 如果可能,提供截图或录屏 ### 相关数据 如果涉及数据问题,提供 JSON 数据 ``` ## 📊 测试报告模板 ```markdown # DOM 渲染功能测试报告 ## 测试概况 - 测试日期: 2024-01-01 - 测试人员: XXX - 测试环境: 微信开发者工具 / Chrome - 测试版本: 1.0.0 ## 测试结果汇总 | 功能模块 | 测试用例数 | 通过 | 失败 | 通过率 | |---------|-----------|------|------|--------| | 基础绘制 | 10 | 10 | 0 | 100% | | 擦除功能 | 5 | 5 | 0 | 100% | | 撤销重做 | 8 | 8 | 0 | 100% | | 缩放功能 | 6 | 6 | 0 | 100% | | 快捷打分 | 4 | 4 | 0 | 100% | | 多图片 | 3 | 3 | 0 | 100% | | 只读模式 | 2 | 2 | 0 | 100% | | 数据持久化 | 4 | 4 | 0 | 100% | | 性能测试 | 3 | 3 | 0 | 100% | | 兼容性 | 5 | 5 | 0 | 100% | | **总计** | **50** | **50** | **0** | **100%** | ## 详细测试结果 [详细记录每个测试用例的结果] ## 发现的问题 [列出发现的所有问题] ## 建议 [提出改进建议] ## 结论 ✅ 通过 / ❌ 不通过 ``` ## 🎯 自动化测试(可选) ### 单元测试示例 ```typescript import { describe, it, expect } from 'vitest' import { useSimpleDomLayer } from './useMarkingDom' describe('useMarkingDom', () => { it('should add shape correctly', () => { const layer = useSimpleDomLayer({...}) // 模拟绘制矩形 layer.handleMouseDown(mockEvent, mockRect) layer.handleMouseMove(mockEvent, mockRect) layer.handleMouseUp() expect(layer.markingData.value.shapes).toHaveLength(1) expect(layer.markingData.value.shapes[0].type).toBe('rect') }) it('should undo correctly', () => { const layer = useSimpleDomLayer({...}) // 添加标记 layer.handleMouseDown(mockEvent, mockRect) layer.handleMouseUp() expect(layer.markingData.value.shapes).toHaveLength(1) // 撤销 layer.undo() expect(layer.markingData.value.shapes).toHaveLength(0) }) }) ``` ## 📝 测试记录 ### 测试日期: ___________ ### 测试人员: ___________ | 测试项 | 状态 | 备注 | |--------|------|------| | 绘制矩形 | ⬜ | | | 绘制线条 | ⬜ | | | 文本注释 | ⬜ | | | 特殊标记 | ⬜ | | | 擦除功能 | ⬜ | | | 撤销操作 | ⬜ | | | 重做操作 | ⬜ | | | 清空标记 | ⬜ | | | 手动缩放 | ⬜ | | | 自适应宽度 | ⬜ | | | 快捷打分 | ⬜ | | | 多图片 | ⬜ | | | 只读模式 | ⬜ | | | 数据导出 | ⬜ | | | 数据导入 | ⬜ | | | 性能测试 | ⬜ | | | 小程序兼容 | ⬜ | | **签名**: ___________