# 端到端与黑盒验收测试方案 > 文档状态:当前发布验收基线。本文定义要验证什么、什么结果算通过;尚未表示对应自动化脚本已经全部实现。 > > 日期:2026-07-10。 > > 适用范围:五个公开包、`apps/demo` 六个路由、Worker/WASM、真实发布压缩包、真实与异常办公文件。 ## 1. 文档职责 本文负责回答四个问题: 1. 用户从公开入口使用项目时,哪些行为必须真实可用; 2. 如何验证正式构建、Worker、WASM、下载、导出和发布包,而不是只检查源码; 3. 失败时必须保存哪些证据,另一名 Agent 如何重复执行; 4. 满足哪些条件后才允许解除发布暂停。 配套文档: | 文档 | 用途 | |---|---| | [项目架构审查与目标设计](architecture-review-and-target-design.md) | 说明为什么要改,以及目标边界 | | [稳定化整改路线图](plan/stabilization-roadmap.md) | 说明先做什么、依赖关系和任务状态 | | [Agent 黑盒验收执行手册](testing/agent-execution-runbook.md) | 给出启动浏览器、收集证据、分派与清理的操作步骤 | | [视觉验收交接](visual-acceptance-handoff.md) | 上游对比、视觉密度和逐页人工确认的补充标准 | | [组件浏览器验证计划](component-browser-verification-plan.md) | 历史组件和真实材料清单,作为补充参考 | 旧视觉文档可以补充比较方法,但不能替代本文的正式构建、真实 Worker/WASM、发布压缩包、安全输入和恢复路径检查。 ## 2. 黑盒边界 黑盒测试只能通过以下入口观察系统: - 五个包在发布压缩包中的公开导出; - 正式构建后的 demo 页面和用户可见控件; - 浏览器公开事件、DOM、Canvas、iframe、下载和文件选择行为; - 浏览器控制台、页面异常、网络请求和响应头; - 导出文件能否被独立工具重新打开; - 进程、Worker、对象 URL、缓存和内存能否在操作后释放。 以下方式不能作为黑盒通过证据: - 直接调用内部函数并断言返回值; - 只确认某个按钮、方法名或 DOM 节点存在; - 只运行 `tsc`、构建或结构测试; - Worker 失败后静默退回主线程,但测试仍记为 Worker 通过; - 页面写着 `READY`、`PASSED` 或 `implemented`; - 用工作区源码别名代替真实发布包消费; - 失败后删除断言、跳过用例或放宽为“未抛异常”。 核心单元测试、Vue 组件测试仍然需要建设,但它们与本文的黑盒检查相互补充,不能互相替代。 ## 3. 当前可运行入口与目标入口 ### 3.1 当前基础命令 ```bash pnpm install --frozen-lockfile pnpm typecheck pnpm build node packages/vue-docx/tests/verify-integration.mjs node packages/vue-xlsx/test/structure.mjs pnpm audit --prod --audit-level moderate pnpm --filter demo preview --host 127.0.0.1 --port 4173 ``` 这些基础命令有已知局限: - `pnpm typecheck` 已覆盖 Vue 单文件组件,但仍不等于运行时验证; - XLSX 结构测试读取构建后的 `dist`,只检查空控制器的公共方法; - DOCX 集成测试允许 Worker 回退主线程; - `pnpm build` 成功不代表浏览器资源地址和发布压缩包可用。 因此,执行者必须如实记录这些结果,但不能据此宣布黑盒通过。 ### 3.2 P1 统一命令 P1-CI-01 已建立以下根级真实脚本: ```text pnpm check pnpm test:unit pnpm test:component pnpm test:blackbox pnpm test:consumer pnpm test:matrix pnpm test:docs pnpm test:release ``` 每个脚本必须失败时返回非零退出码,并把证据写到固定目录。 当前 P1 套件内容如下: - `test:unit` 运行 DOCX/XLSX 核心行为、既有 DOCX 集成、XLSX 结构和材料清单检查; - `test:component` 运行三个 Vue 包的状态、事件、卸载、对象 URL 和 Worker 生命周期检查; - `test:blackbox` 只使用正式构建,依次运行路由直达/刷新、DOCX/XLSX 业务流程、上游界面与交互一致性和六项竞态回归; - `test:consumer` 重新打五个真实 tgz,在工作区外安装,执行类型、正式构建和浏览器 Worker/WASM 检查; - `test:matrix` 从同一批真实 tgz 在工作区外验证 Vue 下限与当前版本,并执行 Chromium、Firefox 和 WebKit 核心流程; - `test:docs` 检查本文、执行手册、根命令和实际脚本是否一致; - `test:release` 显式运行类型、正式构建、单元、组件、正式黑盒、压力、性能、两次隔离构建、同批 tgz 外部消费、三浏览器矩阵、文档、发布就绪和候选清单检查。它不再是 `pnpm check` 的别名,但实现者本会话运行的结果仍不等于全新会话独立复核。 路线图兼容别名 `test:e2e` 与 `test:pack` 分别指向 `test:blackbox` 与 `test:consumer`,不得形成两套不同实现。 ### 3.3 自动化测试入口约定 P0/P1 整改必须同时提供一个只使用公开接口的测试消费页。它不是新的产品 API,而是正式构建测试入口,至少提供以下稳定元素: | 入口 | 约定 | |---|---| | 页面状态 | `[data-testid="page-status"]`,`data-state` 只能是 `idle/loading/ready/error` | | 当前材料 | `[data-testid="loaded-file"]`,显示当前文件名 | | DOCX 来源 | `[data-testid="docx-import-source"]`,显示 `worker` 或 `main-thread` | | DOCX 页面 | `[data-testid="docx-page"]`,带从 1 开始的页码 | | XLSX 工作表 | `[data-testid="xlsx-sheet-tab"]`,选中项带 `aria-selected` | | 错误 | `[data-testid="load-error"]`,包含稳定错误码和用户可读信息 | | 诊断 | 页面通过公开 `onDiagnostic` 事件或等价公开回调报告 requestId、Worker 生命周期、WASM URL、取消和资源释放;不得读取模块私有变量 | 测试消费页还必须允许通过公开配置传入 `wasmUrl`、`workerUrl`、资源上限、是否允许主线程回退和 URL 规则。`DOCX-002`、`DOCX-003`、资源低阈值用例都通过该入口执行。 这些入口由路线图 `P0-DOCX-01`、`P1-E2E-01` 和 `P1-CONSOLE-01` 落地。在它们尚未存在时,相应用例应明确记为 `BLOCKED`,不能由 Agent 自行读取内部状态或把人工猜测记为通过。 ## 4. 测试环境和材料 ### 4.1 浏览器矩阵 | 执行时机 | Chromium | Firefox | WebKit | 说明 | |---|---:|---:|---:|---| | 每次相关提交 | 必跑 | 可选 | 可选 | 运行受影响套件和 P0 路由冒烟 | | 每日或阶段收口 | 必跑 | 必跑 | 必跑 | 完整功能、响应式和恢复用例 | | 候选版本 | 必跑 | 必跑 | 必跑 | 使用候选压缩包和相同提交 | 浏览器版本、操作系统、时区、语言、设备像素比和视口必须写入结果。默认时区使用 `Asia/Shanghai`,默认语言为 `zh-CN`,并补一次 `en-US` 文案和布局检查。 ### 4.2 视口矩阵 | 名称 | 尺寸 | 重点 | |---|---|---| | Desktop | `1440×900` | 完整工具栏、页面和网格 | | Laptop | `1280×720` | 低高度、内部滚动和固定工具栏 | | Tablet | `768×1024` | 控件换行、触控目标和内部滚动 | | Mobile | `390×844` | 无页面级横向溢出、关键操作仍可完成 | ### 4.3 正式路由 | 页面 | 路由 | 最低要求 | |---|---|---| | Home | `/#/` | 导航、状态说明和路由跳转 | | DOCX Viewer | `/#/docx-viewer` | 真实解析、分页、表格、图片和错误恢复 | | DOCX Editor | `/#/docx-editor` | 启动、编辑、撤销、只读和导出再导入 | | XLSX Viewer | `/#/xlsx-viewer` | 工作簿、工作表、选择、编辑、图表和导出 | | PDF Viewer | `/#/pdf-viewer` | 真正 PDF 渲染、翻页、搜索、缩放和隔离 | | Components | `/#/components` | 通用组件真实交互、键盘和错误状态 | ### 4.4 当前真实材料 材料以 `test-data/manifest.json` 为权威清单,文件位于 `apps/demo/public/samples/`,至少覆盖: | 类型 | 材料 | |---|---| | DOCX | `demo.docx`、`legal-contract.docx`、`invoice-table.docx`、`report-with-image.docx`、`chinese-mixed.docx`、`corrupted.docx` | | XLSX | `financial-model.xlsx`、`sales-table.xlsx`、`charts-images.xlsx`、`large-grid.xlsx`、`corrupted.xlsx` | | PDF | `sample.pdf`、`scanned-invoice.pdf`、`rotated-pages.pdf`、`large-contract.pdf`、`corrupted.pdf` | | 图像与结构数据 | `invoice.png`、`contract-page.png`、`ocr-layout.json`、`field-citations.json` | 每次候选版本执行前保存材料文件名、字节数和 SHA-256。若材料发生变化,必须重新建立视觉和性能基线,不能沿用旧截图。 `P1-FIXTURE-01` 已生成机器可读的 `test-data/manifest.json`。每项记录文件名、类型、分类、合成来源、预期行为、字节数和 SHA-256,例如: ```json { "file": "invoice-table.docx", "sha256": "<实际值>", "type": "docx", "category": "normal", "source": "synthetic-v1", "expected": "preserve invoice text and table structure through roundtrip", "bytes": 36004 } ``` 所有材料由 `scripts/generate_test_materials.py` 以固定种子、固定时间戳和稳定 ZIP/PDF 元数据生成。`node scripts/ci/verify-fixtures.mjs` 检查完整文件集合、类型签名、分类覆盖、别名和校验值;损坏材料只记录稳定错误类别,不记录随浏览器变化的完整堆栈。 ## 5. 结果、证据和严重级别 ### 5.1 统一套件编号 路线图只引用以下九个稳定套件编号: | 套件 | 目的 | |---|---| | `BB-P0-ROUTES` | 六个正式路由、公共资源和最小用户路径 | | `BB-DOCX-WORKER` | DOCX 主线程/Worker/WASM、查看和编辑闭环 | | `BB-SEC-URL` | URL 协议、PDF 隔离、下载、CSV 和外部链接安全 | | `BB-RACE` | 快速切换、取消、卸载和旧结果覆盖 | | `BB-PACK-CONSUMER` | 真实 npm 压缩包、CSS、类型、Worker/WASM 消费 | | `BB-PERF-XLSX` | XLSX 包体、首屏、大表滚动和按需加载 | | `BB-STRESS` | 安全限制、异常材料、重复操作和资源释放 | | `BB-UX-PARITY` | DOCX/XLSX 与上游 Extend UI React 的界面和核心交互一致性 | | `BB-RELEASE` | 跨浏览器、视觉、无障碍和候选版本总验收 | 用例编号一旦进入持续集成或缺陷记录,不得重用。废弃用例保留编号并标记 `retired`。 ### 5.2 严重级别 | 级别 | 定义 | 发布处理 | |---|---|---| | P0 | 页面崩溃、公开包不可安装、脚本执行、数据严重错误、核心 Worker/WASM 完全不可用 | 立即暂停,必须修复 | | P1 | 核心查看、编辑、导出、恢复或安全限制不可用 | 必须修复 | | P2 | 明显视觉、键盘、响应式、性能或兼容问题 | 修复;确需保留时必须由负责人书面接受 | | P3 | 不影响完成任务的文案或细节问题 | 可进入后续计划,但要有负责人 | 安全问题不得以“仅 demo”降级。公开页面、示例和组件库都可能处理不可信内容。 ### 5.3 证据目录 建议结构: ```text output/acceptance//// ├── summary.json ├── environment.json ├── commands.log ├── console.json ├── page-errors.json ├── network.json ├── downloads/ ├── screenshots/ ├── traces/ ├── performance/ └── notes.md ``` `summary.json` 至少包含提交号、工作区是否有未提交修改、套件和用例 ID、开始结束时间、执行者、浏览器版本、材料校验值、结果、重试次数、缺陷链接和证据相对路径。 ## 6. `BB-P0-ROUTES`:正式路由与资源冒烟 | 用例 ID | 优先级 | 前置条件 | 操作 | 可观察断言 | 必留证据 | |---|---:|---|---|---|---| | ROUTE-001 | P0 | 干净安装并完成正式构建 | 启动 `vite preview`,依次打开六个 hash 路由 | HTTP 入口成功;每页标题和主区域出现;无空白页 | 六页首屏截图、console、pageerror、network | | ROUTE-002 | P0 | ROUTE-001 | 每页等待网络空闲,再等待明确业务就绪状态 | 无未捕获异常、Vue warning、关键资源 404/5xx;页面不永久停在 loading | 就绪条件和等待耗时 | | ROUTE-003 | P0 | DOCX/XLSX/PDF 页面 | 检查所有 Worker、WASM、样本请求 | Worker JS 为 JavaScript;WASM 为 `application/wasm` 且前 4 字节为 `00 61 73 6d` | URL、状态、响应头、字节签名 | | ROUTE-004 | P1 | 六页可打开 | 用导航链接进入每页,刷新,再用浏览器前进后退 | 路由状态和导航高亮正确,无重复初始化错误 | 路由顺序、截图、console | | ROUTE-005 | P1 | 四种视口 | 对六页分别切换视口并检查滚动 | 页面级无意外横向滚动;宽内容只在内部滚动;控件不遮挡 | 每页四视口截图和溢出尺寸 | | ROUTE-006 | P1 | 可键盘操作 | 只用 Tab、Shift+Tab、Enter、Space、Escape、方向键完成主要导航 | 焦点可见且顺序合理;无键盘陷阱;禁用控件不可触发 | 焦点路径和失败控件 | | ROUTE-007 | P1 | 每种文件页面 | 先加载有效材料,再加载损坏材料,再回到有效材料 | 错误清楚;旧内容不残留;无需刷新即可恢复 | 三阶段截图、状态、console | | ROUTE-008 | P2 | Home | 检查状态说明和 API 表格 | 不把未通过能力标为完成;链接到当前文档 | 页面截图和文案清单 | 通过条件:ROUTE-001 至 ROUTE-007 全部通过,P0/P1 为零;ROUTE-008 的任何不实状态按 P1 处理。 ## 7. `BB-DOCX-WORKER`:DOCX 全链路 ### 7.1 Worker、WASM 和查看器 | 用例 ID | 优先级 | 操作 | 可观察断言 | 证据 | |---|---:|---|---|---| | DOCX-000 | P0 | 在同一提交分别启动 Vite 开发模式和正式 preview,加载同一材料 | 两种模式都使用真实 Worker 和同一显式 WASM 配置;开发模式只能作为兼容检查,不能替代正式 preview 结论 | 两种模式的请求、来源和截图 | | DOCX-001 | P0 | 正式构建加载 `demo.docx` | 解析成功且诊断来源明确为 Worker;不得静默回退 | Worker 请求/回包、来源字段、页面截图 | | DOCX-002 | P0 | 在测试消费页通过公开 runtime 配置传入无效 `workerUrl` | 页面显示可操作错误;默认不在主线程继续重解析 | console、公开诊断事件、错误结构、主线程长任务 | | DOCX-003 | P0 | 在测试消费页通过公开 runtime 配置传入自定义 `wasmUrl` | 主线程和 Worker 使用同一配置;没有未配置的默认 WASM 请求 | 完整网络记录和公开诊断事件 | | DOCX-004 | P1 | 逐个切换五个有效 DOCX | 每份文档标题、正文和页数属于当前材料;无旧状态 | 每份材料截图、页面数、材料名 | | DOCX-005 | P1 | 加载合同、表格、图片、中文材料 | 分页、表格正文、图片、中文、页眉页脚按承诺显示;未支持项明确标注 | 关键区域截图和已知差异 | | DOCX-006 | P1 | 加载 `corrupted.docx` 后恢复到 `demo.docx` | 损坏文件有稳定错误;恢复后内容和工具栏可用 | 错误与恢复证据 | ### 7.2 编辑器 | 用例 ID | 优先级 | 操作 | 可观察断言 | 证据 | |---|---:|---|---|---| | DOCX-101 | P0 | 打开 Editor 路由 | 不出现 `metadata.warnings` 等运行异常;默认文档可见 | 页面、console、pageerror | | DOCX-102 | P1 | 在段落中间插入中英文,使用输入法组合输入 | 只修改目标位置;不复制、重排或重置整段样式 | 编辑前后文本和选区 | | DOCX-103 | P1 | 选择部分文本并切换粗体、斜体、下划线、颜色 | 只影响选择范围;折叠光标行为符合文档 | 选区和样式截图 | | DOCX-104 | P1 | 连续编辑、撤销、重做 | 内容、样式和当前选择按顺序恢复 | 每一步模型可观察结果 | | DOCX-105 | P1 | 插入表格,编辑不同单元格,增删行列 | 单元格正文可见,操作只影响目标表格,可撤销 | 表格截图和导出结果 | | DOCX-106 | P1 | 插入图片,拖动、缩放并切换环绕方式 | 预览和提交尺寸/位置一致;导出后保留 | 操作轨迹、导出再导入截图 | | DOCX-107 | P1 | 切换只读、修订、批注、表单域 | 只读禁止修改;任何可见入口都有真实行为或明确禁用说明 | 控件状态和结果 | | DOCX-108 | P1 | 导出 DOCX,再在新上下文重新导入 | 文本、表格、图片和关键格式保留;文件可由 `python-docx` 打开 | 下载文件、校验值、重导截图 | | DOCX-109 | P2 | 快捷键、右键菜单、主题、缩放和缩略图 | 可见功能与说明一致,不影响文档状态和撤销历史 | 交互录像或连续截图 | 通过条件:真实 Worker 必须被证明;Viewer 和 Editor 都使用正式构建;所有可见且宣称支持的 P1 能力必须通过,不能用隐藏错误或主线程回退代替。 ## 8. `BB-SEC-URL`:URL、下载与内容隔离 所有安全用例只能在隔离的 localhost 环境执行。测试脚本只设置本地内存标记,不访问外部攻击地址,不执行系统命令。 | 用例 ID | 优先级 | 输入/操作 | 可观察断言 | 证据 | |---|---:|---|---|---| | SEC-001 | P0 | 分别传入 `http`、`https`、`blob`、`javascript`、`data`、`file` 和自定义协议 | 只有明确允许的协议通过;拒绝结果为结构化错误 | 协议矩阵和错误码 | | SEC-002 | P0 | XLSX `src` 使用只修改 `window.__blackboxMarker` 的 `javascript:` 地址并点下载 | 标记不变化,下载不触发,页面给出不安全 URL 错误 | 标记前后值、console | | SEC-003 | P0 | localhost 返回以 `%PDF-` 开头但 MIME 为 HTML、后续带本地脚本标记的内容 | 内容不进入可执行同源 iframe;父页面标记不变化 | 响应头、DOM、标记值 | | SEC-004 | P0 | PDF 首次检查返回 PDF、iframe 第二次请求改为 HTML | 只渲染第一次已验证字节,或安全拒绝;不能二次加载原地址执行 | 请求次数、响应差异、标记值 | | SEC-005 | P1 | DOCX 外部超链接使用不允许协议 | 链接不可导航;内部书签仍可用;http/https 按配置工作 | href、点击结果、URL规则日志 | | SEC-006 | P1 | 单元格值以 `= + - @` 和前导空白开头后导出 CSV | 默认安全导出不会形成可执行公式;原样导出必须是显式危险选项 | CSV 原始字节和配置 | | SEC-007 | P1 | URL 重定向到不同协议、不同来源或过大内容 | 浏览器直连按架构规则拒绝任何重定向。产品未提供服务端代理时,到此即可通过,代理分支记为 `N/A`;只有产品明确启用受控同源代理时,才允许逐跳跟随,并在每一跳重新检查协议、来源和大小 | 必留直连拒绝结果;启用代理时再留代理逐跳记录和读取字节数 | | SEC-008 | P1 | 网络失败或跨域失败后点下载/导出 | 失败状态不保留可用下载按钮,不会回退到原始不安全 URL | 按钮状态和网络记录 | 通过条件:SEC-001 至 SEC-004 全部通过;任何可执行标记变化均为 P0。URL 规则必须在加载、iframe、图片、超链接和下载入口共用,不能每个组件各写一套。 ## 9. `BB-RACE`:取消、快速切换和旧结果隔离 测试服务器应支持人为延迟不同请求,确保旧请求比新请求晚返回。 | 用例 ID | 优先级 | 操作 | 可观察断言 | 证据 | |---|---:|---|---|---| | RACE-001 | P0 | DOCX A 延迟 2 秒,立即切到 B | 最终只显示 B;A 完成后不能覆盖 model、错误、文件名或页数 | 时间线、请求编号、最终截图 | | RACE-002 | P0 | XLSX A 延迟,切到 B 并编辑 B | A 不能覆盖 B 的工作表、选择、只读或导出状态 | 时间线和编辑结果 | | RACE-003 | P0 | PDF A 延迟,切到 B | 最终 iframe/渲染字节、页数、搜索都属于 B | 请求和页面状态 | | RACE-004 | P1 | 加载期间离开路由并关闭上下文 | 请求被取消,Worker 终止,卸载后无状态写入和异常 | 取消事件、Worker 列表、console | | RACE-005 | P1 | 连续切换有效→损坏→有效材料 20 次 | 最后材料正确;loading/error 不挂起;无重复监听错误 | 操作序列、最终状态 | | RACE-006 | P1 | 加载期间点击导出、下载、撤销和新建 | 不导出旧/半成品模型;控件禁用或返回明确状态 | 控件状态、下载目录 | | RACE-007 | P1 | Worker 在取消后返回成功或错误消息 | 过期回包被丢弃并释放,不能写 UI | Worker 消息时间线 | | RACE-008 | P2 | 同一文件快速重复加载 | 可去重但结果必须确定;缓存不能改变错误和取消语义 | 请求次数、缓存诊断 | 通过条件:最终页面状态必须只来自最新请求。用“等待所有请求都完成”掩盖旧结果覆盖不算通过。 ## 10. `BB-PACK-CONSUMER`:真实发布包消费 该套件必须在工作区之外的临时目录安装真实 `.tgz`,不能用 `workspace:*`、源码别名或本地 `dist` 直接引用。 当前人工执行的打包顺序: ```bash pnpm install --frozen-lockfile pnpm build PACK_DIR="$(mktemp -d)/packs" mkdir -p "$PACK_DIR" for pkg in packages/docx-core packages/xlsx-core packages/vue-docx packages/vue-xlsx packages/vue-pdf packages/vue-ui; do (cd "$pkg" && npm pack --json --pack-destination "$PACK_DIR") done find "$PACK_DIR" -maxdepth 1 -name '*.tgz' -print ``` 固定消费模板位于 `tests/consumer/template/`。`pnpm test:consumer` 把模板复制到系统临时目录,只用五个 tgz 的绝对路径安装,执行 `tsc`、`vue-tsc`、Vite 生产构建和正式 preview,再删除临时目录。模板公共目录只复制测试文档,不复制 Worker/WASM;五个包必须是临时 `node_modules` 内的普通目录,不能是工作区链接。 | 用例 ID | 优先级 | 操作 | 可观察断言 | 证据 | |---|---:|---|---|---| | PACK-000 | P0 | 在临时消费项目分别启动 Vite 开发模式和正式 preview | 两种模式都只使用 tgz 公开入口并能加载 Worker/WASM;开发模式兼容检查不能替代正式 preview | 两种模式的命令、网络和截图 | | PACK-001 | P0 | 从干净提交构建五个包并执行 `npm pack --json` | 每包包含声明的 JS、类型、CSS和必要资源;不包含测试材料和源码垃圾 | tarball 文件清单 | | PACK-002 | P0 | 创建最小 Vite+Vue 消费项目并安装五个 tgz | 安装、类型检查和生产构建成功 | lockfile、命令日志 | | PACK-003 | P0 | 消费项目只从公开入口加载 DOCX/XLSX/PDF | 页面可用,Worker/WASM 无 404,不能依赖仓库 public 目录 | 浏览器网络和截图 | | PACK-004 | P1 | 导入 `@arcships/vue-docx/style.css`、`@arcships/vue-xlsx/style.css`、`@arcships/vue-pdf/style.css` | 所有样式入口受 `exports` 支持,组件有预期样式 | resolve 结果和截图 | | PACK-005 | P1 | 在 TypeScript 消费项目使用主要公开类型 | 类型来自 tgz,未泄漏仓库内部路径 | `vue-tsc`/`tsc` 日志 | | PACK-006 | P1 | 检查 `exports` 允许和禁止子路径 | 文档中的入口可导入;未公开内部文件给出确定错误 | 导入矩阵 | | PACK-007 | P1 | 对 tgz 解包后检查 Worker/WASM 数量和校验值 | 每种资源只有一种交付策略,无重复二进制 | 清单、SHA-256、总字节 | | PACK-008 | P1 | 在不含历史 `dist` 的干净检出执行 README 开发流程 | 文档命令可直接启动,测试的是当前源码 | 临时检出日志 | 通过条件:PACK-001 至 PACK-003 为发布硬门槛。被测试 tgz 的 SHA-256 必须与候选版本实际发布文件一致。 ## 11. `BB-PERF-XLSX`:包体和大表性能 `P3-PERF-BASELINE-01` 本身先在固定环境连续运行三轮,只负责采集并提交第一份待批准基线;这一轮没有旧基线可比较,不能据此给候选版本性能 PASS。基线获批并存入机器可读配置后,后续提交先执行“相对已批准基线不得回退超过 10%”,再逐步收紧到明确硬预算。依赖基线的优化任务在基线达到 `ready_for_gate` 前不得开始。不得在本文中用未经测量的数字伪装最终预算。 | 用例 ID | 优先级 | 操作 | 指标/断言 | 证据 | |---|---:|---|---|---| | PERF-001 | P1 | 构建 vue-xlsx 和 demo | 记录原始/gzip JS、CSS、WASM、地图和总传输;无重复 WASM | 构建清单和压缩统计 | | PERF-002 | P1 | 只打开 Home,再进入 XLSX | 地图、地区图和重型图表代码不应在 Home 首屏加载;首次需要时才请求 | 两阶段网络瀑布 | | PERF-003 | P1 | 加载 `large-grid.xlsx` | 记录加载到首个可操作单元格的时间和主线程长任务 | trace 和 performance entry | | PERF-004 | P1 | 大表连续垂直/水平滚动 10 秒 | 可见区正确;95 分位帧耗时不超过已批准预算;无每帧 Canvas 尺寸重置 | trace、帧耗时和截图 | | PERF-005 | P1 | 滚动后选择、编辑、Tab 移动和撤销 | 坐标与当前滚动位置一致,交互延迟在预算内 | 输入时间线和选区截图 | | PERF-006 | P2 | 打开图表/地区图,再切换工作表 | 首次按需加载有明确 loading;切换后缓存复用且不重复下载 | 网络和状态截图 | | PERF-007 | P1 | 连续加载五个工作簿并回到 Home | Worker、对象 URL、图片和大数组释放;内存回落达到批准预算 | heap/进程内存采样 | | PERF-008 | P2 | 低端 CPU/网络模拟 | loading、取消和错误仍可操作,无长时间冻结 | 限速参数和 trace | 性能失败不能通过提高构建警告阈值、隐藏日志或减少测试材料解决。 ## 12. `BB-STRESS`:安全限制和资源释放 ### 12.1 安全生成原则 不得为测试制造会真实耗尽开发机内存或磁盘的文件。PDF 只验证整份文件体积上限,不测试页数、单页像素、总内存、并发页或人为构造的复杂度上限。优先使用以下方法: - 在测试配置中把 DOCX/XLSX 的输入、解压、XML、图片像素和页数上限降到很小;PDF 则通过公开 `maxFileSize` 把整份文件上限调到略低于现有有效样本; - 使用几 KB 到几 MB 的可控 ZIP,声明或实际内容刚好越过测试上限; - 使用尺寸元数据超限但内容很小的图片头; - Worker 超时使用人为延迟,不使用无限循环; - 所有恶意 URL 指向 localhost,只修改内存标记; - 测试结束必须删除临时文件和停止服务器。 ### 12.2 用例 | 用例 ID | 优先级 | 操作 | 可观察断言 | 证据 | |---|---:|---|---|---| | STRESS-001 | P0 | 将输入上限设为 1 KiB,加载略大的有效文件 | 读取/解析前拒绝,错误给出实际值和上限 | 读取字节数、错误结构 | | STRESS-002 | P0 | 小输入 ZIP 的总解压量或单项超过低测试上限 | 在分配大量内存前拒绝;主线程不冻结 | 归档清单、内存和时间 | | STRESS-003 | P0 | ZIP 条目数或压缩比超过测试上限 | 确定拒绝;不得先 `unzipSync` 全部内容 | 调用顺序诊断 | | STRESS-004 | P0 | 图片宽高/总像素超过低测试上限 | 解码前拒绝;不创建大 Canvas/像素缓冲 | 解码诊断、内存 | | STRESS-005 | P1 | Worker 延迟超过超时,然后加载正常文件 | 超时终止 Worker;下一次加载正常恢复 | Worker 生命周期和恢复截图 | | STRESS-006 | P1 | 轮流加载全部有效和损坏材料 3 轮 | 无持续增长的 Worker、监听器、对象 URL、缓存和历史 | 三轮资源计数 | | STRESS-007 | P1 | 断网、500、截断响应和错误 MIME | 错误可见且可恢复,不保留旧内容和危险下载 | 网络故障矩阵 | | STRESS-008 | P1 | 历史记录写入大单元格/文档直到字节预算 | 达到预算后按规则淘汰;撤销语义仍确定 | 历史条数、字节和结果 | | STRESS-009 | P2 | 同页重复挂载/卸载 viewer 50 次 | 无重复全局监听和未结束计时器 | 监听器/计时器计数 | | STRESS-010 | P1 | 关闭页面时仍有解析和缩略图任务 | 所有任务取消,关闭后无异常回写 | 关闭后的 console 和进程状态 | | STRESS-011 | P0 | ZIP 条目名使用 `../`、绝对路径、反斜杠穿越和重复规范化名称 | 归档预检在写入/解析前拒绝,错误不泄露本机绝对路径 | 条目清单、错误码、无文件落盘证明 | | STRESS-012 | P0 | 文档 XML 使用未闭合标签、深层嵌套、实体/超长属性和错误编码 | 在 XML 预算内确定失败,页面可恢复,不执行外部实体 | 输入摘要、耗时、错误和恢复结果 | | STRESS-013 | P1 | 对每个预算分别测试 `limit-1`、`limit`、`limit+1` | 前两者按契约成功,略超限确定失败;错误报告准确实际值和上限 | 三个边界结果和配置 | | STRESS-014 | P0 | 多个单项都低于上限,但总解压量、总像素或总 XML 组合超限 | 总预算生效,不能只检查单项;失败后资源释放 | 各单项值、合计、内存和错误 | | STRESS-015 | P0 | 连续提交 `javascript:`、`data:`、`file:`、未知协议和带前导空白的危险地址,然后加载正常 localhost 材料 | 每个危险地址都在请求前拒绝;拒绝不会污染后续正常加载、下载和缓存状态 | 协议序列、请求计数、错误码和恢复结果 | | STRESS-016 | P1 | 使用本地故障服务器连续返回同源、跨来源、协议变化、循环和超长重定向链 | 浏览器直连一律拒绝重定向且无无限请求;产品启用受控代理时按 SEC-007 逐跳检查并受跳数/字节预算限制 | 重定向类型、请求次数、最终状态和资源释放 | ## 13. XLSX、PDF 与通用组件完整功能用例 ### 13.1 XLSX Viewer | 用例 ID | 优先级 | 材料/操作 | 可观察断言 | |---|---:|---|---| | XLSX-001 | P1 | `financial-model.xlsx`:加载并逐个切换工作表 | 工作表名称、数量、关键单元格和格式符合预期文件;切换清除旧选择 | | XLSX-002 | P1 | `financial-model.xlsx`:编辑数字和公式、重新计算、撤销和重做 | 显示值、公式栏、依赖单元格和历史顺序一致 | | XLSX-003 | P1 | `sales-table.xlsx`:滚动、长文本、冻结区域和表格排序 | 表头/冻结区域按承诺工作;长文本、排序和选择坐标正确 | | XLSX-004 | P1 | `charts-images.xlsx`:检查图表、图片和工作表切换 | 图表/图片属于正确工作表,位置和数据正确;按需代码只在需要时加载 | | XLSX-005 | P1 | `large-grid.xlsx`:滚动、范围选择、编辑和 Tab 移动 | 大表可操作,选择与编辑不漂移;同时执行 `PERF-003` 至 `PERF-005` | | XLSX-006 | P1 | `corrupted.xlsx` 后恢复到 `financial-model.xlsx` | 错误可见,无旧网格;恢复后工作表和工具栏可用 | | XLSX-007 | P1 | 单击、Shift/Cmd 扩展和拖动选择范围 | 活动单元格、选择范围、名称框和复制内容一致 | | XLSX-008 | P1 | 双击编辑,Enter 提交,Escape 取消,Tab 提交并移动 | 四种路径各自符合契约,只读模式全部阻止写入 | | XLSX-009 | P1 | 格式、合并/取消合并、复制粘贴、填充、行列尺寸和表格排序 | 操作真实修改工作簿,可撤销,重新渲染与导出一致 | | XLSX-010 | P1 | 图表/图片选择、移动、缩放后切换工作表 | 位置、大小和选中状态正确,不污染其他工作表 | | XLSX-011 | P0 | 导出 XLSX,用固定版本 `openpyxl` 独立打开,再在新 context 导入 | 文件是可打开的真实 XLSX;工作表、关键值、公式、合并、图表/图片承诺能力保持 | | XLSX-012 | P1 | 导出当前工作表 CSV,并执行 SEC-006 | 行列和转义正确;危险前缀按安全策略处理 | | XLSX-013 | P1 | 从允许的远程 URL 加载,随后下载原始源和导出编辑版 | 原始下载与源校验值一致;编辑导出包含修改;失败 URL 不保留下载能力 | | XLSX-014 | P1 | 连续切换五个材料和只读/Worker选项 | 最终状态只属于最新材料;旧 Worker、图片 URL 和历史释放 | 材料的来源、类别、关键行为和校验值必须来自 `test-data/manifest.json`;更细的工作表、公式、合并、图表和图片断言由独立核心测试明确写出,不能由测试读取同一渲染结果后自我验证。上述用例分别纳入 `BB-P0-ROUTES`、`BB-RACE`、`BB-PERF-XLSX`、`BB-SEC-URL` 和 `BB-RELEASE`。 ### 13.2 PDF Viewer | 用例 ID | 优先级 | 操作 | 可观察断言 | |---|---:|---|---| | PDF-001 | P1 | 加载 `sample.pdf` | 使用 PDF 引擎从已验证字节渲染;页数正确 | | PDF-002 | P1 | 缩略图、前后页和页码导航 | 显示页、页码和选中缩略图同步 | | PDF-003 | P1 | 缩放、旋转、搜索真实文本 | 页面真实变化;搜索定位到结果,不只改本地数字 | | PDF-004 | P1 | 加载扫描、旋转和大合同材料 | 图像页、混合方向和长文档可操作 | | PDF-005 | P1 | 下载后检查 MIME、文件名和 SHA | 下载的是验证过的 PDF 字节,不是二次原始 URL | | PDF-006 | P1 | 损坏材料后恢复 | 不留旧页数、缩略图、搜索和 iframe 内容 | | PDF-007 | P2 | 四种视口和纯键盘 | 工具栏、缩略图和页面不遮挡,焦点可见 | | PDF-008 | P1 | 检查默认上限,再通过公开 `maxFileSize` 调整为略低于 `sample.pdf` | 默认值为 `50 MiB`;超过当前值返回 `PDF_TOO_LARGE`,不创建引擎、页面或缩略图;恢复配置后可正常打开。该配置可由宿主调整,不叠加第二层隐藏硬上限 | PDF-008 是 PDF 唯一的资源拒绝边界。PDF-001~007 不以页数、单页像素、总内存、并发页或其他复杂度阈值为前置,也不能因为这些未设置的阈值而把真实查看功能标为阻断。 ### 13.3 Components 页面 | 用例 ID | 优先级 | 操作 | 可观察断言 | |---|---:|---|---| | COMP-001 | P1 | FileUpload 点击、拖放、错误类型、超限、多文件 | accept、大小、多文件和错误事件符合公开接口 | | COMP-002 | P2 | FileThumbnail 覆盖 PDF/DOCX/XLSX/图片/未知类型 | 图标、名称、错误和释放行为正确 | | COMP-003 | P1 | SignaturePad 鼠标和触控画线、清空、导出 | `isEmpty`、图像和尺寸状态真实变化 | | COMP-004 | P2 | Tooltip 用 hover、focus、Escape 和边界位置 | 键盘可用,不被容器裁剪,不遮挡触发器 | | COMP-005 | P2 | BoundingBoxCitations 点击多页、多置信度框 | 坐标、页码、颜色和事件对应材料 | | COMP-006 | P2 | LayoutBlocks 加载 OCR JSON 并点击各类型 | 图片缩放后框仍对齐,列表和点击事件正确 | | COMP-007 | P2 | Spinner 在不同容器、尺寸和主题显示 | 动画、颜色、可访问名称和布局稳定 | | COMP-008 | P1 | 连续上传/移除文件并离开页面 | 对象 URL、FileReader 和监听器释放 | 这些用例分别并入 `BB-P0-ROUTES`、`BB-SEC-URL`、`BB-STRESS` 和 `BB-RELEASE` 的执行结果,不另设路线图套件编号。 ### 13.4 `BB-UX-PARITY`:上游界面与交互一致性 该套件把上游 Extend UI React 当作产品基准,而不是只确认本地控件“能显示”。对比基准固定为官方 `extend-hq/ui@f2ff2f90954acfed1f60b7fd070a9491d4113047`;切换基准提交必须在路线图中说明原因并重新建立截图。Vue 可以采用不同内部实现,但用户可见的信息层级、核心操作、状态反馈和编辑结果不得缺失。 | 用例 ID | 优先级 | 操作 | 通过标准 | |---|---:|---|---| | UX-DOCX-001 | P1 | 打开多页 DOCX,使用页码、前后页、缩放、缩略图和下载 | 查看器首先呈现文档产品界面;工具栏无裁切;页码、页面、缩略图同步;缩放真实改变页面尺寸;下载为原 DOCX 字节 | | UX-DOCX-002 | P1 | 在编辑器中真实拖选一段文字,应用格式,再撤销/重做 | DOM 选区与模型范围一致;只修改选中文字;格式变化可见;撤销和重做恢复内容与选区 | | UX-DOCX-003 | P1 | 在段中按 Enter、段首按 Backspace,并切换只读后重复命令 | Enter 拆成两个段落且不丢文字;段落合并结果正确;只读状态下键盘和工具栏都不能修改模型 | | UX-DOCX-004 | P1 | 检查表格、图片、缩放和窄屏工具栏 | 表格与图片不重复、不丢失;缩放真实生效;桌面和笔记本宽度下核心控件不被横向藏起 | | UX-XLSX-001 | P1 | 打开财务模型,检查公式栏、名称框、样式、合并单元格和空白工作区 | 已用区域与空白网格连续;单元格值、公式和格式对应源文件;初始工作区至少覆盖 200 行、50 列 | | UX-XLSX-002 | P1 | 单选、范围选择、行列选择、编辑、取消、提交和撤销 | 活动单元格、选区、表头高亮、名称框和公式栏同步;编辑和历史真实修改工作簿 | | UX-XLSX-003 | P1 | 滚动冻结工作表、调整行列尺寸并查看图表/图片 | 冻结区域保持;调整后网格重排;图表和图片随所属区域正确移动,不漂移到其他工作表 | | UX-XLSX-004 | P1 | 检查只读、搜索、右键菜单和图表工作表 | 只读阻止所有写入;搜索给出可见结果;右键命令作用于点击位置;图表工作表不是空白成功状态 | 自动化入口是 `tests/blackbox/ux_parity.py`,已经纳入 `pnpm test:blackbox`。每个用例使用新浏览器 context,导航前监听控制台、页面异常、失败请求、响应和下载;必须保存操作前后截图。仅组件测试通过、工具栏元素存在或页面状态为 `ready`,都不能代替本套件通过。 以下情况直接记为失败并暂停候选发布: - 演示页的运行配置、状态表或 JSON 快照占据首屏,而实际文档/工作簿被推到首屏下方; - 上游已有的核心操作在 Vue 版本中缺失、无反馈或只是改变本地数字; - DOCX 工具栏在 `1440×900` 或 `1280×720` 中仍有不可到达的隐藏控件; - XLSX 只画出有值的少量单元格,或公式栏、选区、冻结、样式与工作簿状态不同步; - 首次失败后重试通过却标记为稳定通过。 ## 14. `BB-RELEASE`:候选版本总验收 候选版本从一个完整入口生成: ```bash CI_RUN_ID= pnpm test:release ``` 该命令先对同一源码快照执行两次隔离安装、正式构建和五包打包;随后把第一轮得到的唯一五个 tgz 交给外部消费和三浏览器矩阵。最终候选目录为: ```text output/acceptance//BB-RELEASE//candidate/ ``` `candidate-manifest.json` 必须列出提交、统一版本、五个 tgz 的 SHA-256 与 SHA-512 完整性值、套件摘要和校验脚本。消费、矩阵和待发布文件的 tgz SHA 必须逐项相同;任何一步不得重新打包替换。发布任务只下载这个目录并重新校验,不检出源码、不构建,也不重新执行 `npm pack`。 本会话可以用该入口做开发后自测,但 `RELEASE-008` 和 `RELEASE-010` 仍必须由没有参与实现的全新会话完成,才能把 P4-RC-01 标为完成。 | 用例 ID | 优先级 | 操作 | 通过标准 | |---|---:|---|---| | RELEASE-001 | P0 | 从候选提交和冻结锁文件建立干净环境 | 安装、类型、单元、组件、构建全部通过 | | RELEASE-002 | P0 | 运行前八个 BB 套件 | P0/P1 为零;P2 均已修复或有明确批准 | | RELEASE-003 | P0 | 安装候选 tgz 到独立消费项目 | 被测试 tgz 与待发布 tgz 校验值相同且可用 | | RELEASE-004 | P1 | Chromium、Firefox、WebKit 全套核心用例 | 浏览器差异有明确处理,不存在单浏览器核心失败 | | RELEASE-005 | P1 | 六页四视口截图对比和人工复核 | 无未接受视觉回归、页面级溢出和控件遮挡 | | RELEASE-006 | P1 | 键盘与语义检查 | 主要任务可键盘完成;焦点、名称、角色和状态正确 | | RELEASE-007 | P1 | 检查控制台、网络、下载、Worker 和内存证据 | 无未知 warning/error、关键 404、泄漏和静默回退 | | RELEASE-008 | P1 | 新 Agent 只读当前四份核心文档重复执行 | 能找到命令、用例和证据;结论一致 | | RELEASE-009 | P1 | 核对 README、包文档和 demo 状态 | 文档只描述已验证能力;限制和已知差异可发现 | | RELEASE-010 | P0 | 生成候选版本摘要并由独立验证者复核 | 摘要列出提交、tgz 校验值、全部套件和剩余风险 | ## 15. 视觉、无障碍和跨浏览器规则 ### 15.1 视觉回归 - UI 外壳、工具栏、表格头、错误态和空态使用稳定截图比较; - 文档、Canvas、光标和动画区域单独截图或遮罩动态部分; - 首次建立基线必须人工确认,不能拿当前错误页面自动设为基线; - 截图差异超过批准阈值时保存差异图并人工判断; - 修复逻辑问题后必须重建相关截图,不能只更新快照。 - DOCX/XLSX 截图必须同时对照固定上游提交的同类状态;不能把当前本地错误页面直接批准为新基线。 ### 15.2 无障碍 至少检查:语义名称、角色、禁用/选中/展开状态、Tab 顺序、焦点可见、Escape 关闭、颜色对比、缩放 200%、输入错误提示和 Canvas 的可替代说明。自动扫描只能发现部分问题,键盘路径必须真实操作。 ### 15.3 跨浏览器 若某能力依赖浏览器原生 PDF、剪贴板、文件系统或下载差异,必须在接口文档中说明,并为每个浏览器写出可观察断言。不能因为 Chromium 通过就默认其他浏览器通过。 ## 16. 失败、重试与已知差异 1. 单个失败最多自动重试一次,首次失败证据必须保留; 2. 重试后通过仍标记 `flaky`,不能直接记为稳定通过; 3. 同一用例在干净环境稳定失败两次,即建立缺陷,不继续无限重试; 4. 产品缺陷与测试环境缺陷分开记录;没有证据时按产品失败处理; 5. 已知差异必须包含影响、负责人、接受人、到期时间和对应用例; 6. P0、安全 P1、数据错误、公开包不可用和静默主线程回退不得列为已知可接受差异; 7. 黑盒执行 Agent 发现产品问题后只记录,不在同一验收运行中修改产品代码。 ## 17. 自动化建设顺序 当前文档先保证任何 Agent 可以按手册人工驱动;随后按以下顺序落地自动脚本: 1. 六路由、控制台、网络和正式资源冒烟; 2. DOCX Worker/WASM 与 Editor 启动; 3. npm 压缩包独立消费; 4. URL/PDF/CSV 安全用例; 5. 快速切换和取消; 6. DOCX/XLSX 编辑与导出再导入; 7. PDF 和 Components 完整交互; 8. DOCX/XLSX 上游界面与交互一致性; 9. 性能、压力、视觉和跨浏览器。 自动化实现应放在明确的测试目录,并提供根脚本。实现前,路径和命令只属于计划,不得写成“已存在”。 ## 18. 最终通过条件 候选版本只有同时满足以下条件才可发布: - `BB-P0-ROUTES`、`BB-DOCX-WORKER`、`BB-SEC-URL`、`BB-RACE`、`BB-PACK-CONSUMER`、`BB-PERF-XLSX`、`BB-STRESS`、`BB-UX-PARITY`、`BB-RELEASE` 全部有完整证据; - 所有 P0/P1 已关闭,未接受的 P2 为零; - 六个正式路由在三种浏览器和四个视口完成核心路径; - 真实 Worker/WASM、真实发布压缩包和导出再导入得到证明; - 控制台、页面异常和关键网络失败清零; - 安全限制在拒绝后能恢复正常文件; - 候选 tgz 校验值与被测试文件一致; - 新 Agent 只读当前核心文档可以重复执行并得到一致结论; - 项目负责人确认所有明确保留的 P2 差异。 任何一项仅有源码说明、demo 文案或手工口头确认而没有证据时,视为未通过。