主题
端到端测试
本文引用的源码文件
本文引用的文件
目录
引言
本文件面向 AI Helper Chrome 扩展(Manifest V3,版本 2.3.1)的端到端测试体系。文档聚焦 Playwright e2e 测试框架的配置与环境搭建、浏览器自动化配置、测试环境准备、测试数据管理、E2E 场景设计、测试执行策略、具体测试脚本示例、视觉回归与 UI 一致性验证,以及测试维护指南。
本项目采用双进程架构:Chrome 扩展端与独立本地 Agent 端。Agent 端源码修改后必须重启进程,新接口才生效;但当前仓库中的 Playwright e2e 主要覆盖扩展侧内容脚本工具、侧边栏交互、选项页保存按钮栏等前端行为,不直接启动或连接 Agent HTTP/WebSocket 服务。因此,本节 E2E 文档以扩展端真实 Chromium 页面和 content script 工具为主。
项目结构
Playwright e2e 测试位于 test/e2e,由根级 playwright.config.js 驱动,并通过 package.json 中脚本统一入口。
graph TB Root["仓库根目录"] --> PWConfig["playwright.config.js"] Root --> Package["package.json"] Root --> TestE2E["test/e2e"] TestE2E --> Helpers["helpers/load-module.js"] TestE2E --> SpecA["content-tools.e2e.spec.js"] TestE2E --> SpecB["demo-product-form.e2e.spec.js"] TestE2E --> SpecC["message-jump-interaction.e2e.spec.js"] TestE2E --> SpecD["message-jump.e2e.spec.js"] TestE2E --> SpecE["options-save-bar.e2e.spec.js"] TestE2E --> SpecF["iframe-ref.e2e.spec.js"]
图表来源
章节来源
核心组件
Playwright e2e 的核心由三部分构成:
| 组件 | 职责 | 关键实现位置 |
|---|---|---|
| Playwright 配置 | 定义测试目录、并行策略、重试、headless、动作超时、Chromium 设备 | playwright.config.js |
| 模块加载器 | 使用 esbuild 将 content script 相关模块打包为 IIFE,注入到被测页面并暴露全局调用能力 | test/e2e/helpers/load-module.js |
| 测试用例集 | 覆盖内容工具链路、商品录入 Demo、消息跳转交互、消息跳转布局、选项页保存按钮栏吸附、iframe 跨帧 ref | 各 .e2e.spec.js 文件 |
Playwright 配置要点
- 测试目录:
./test/e2e - 并行执行:开启
fullyParallel - CI 模式:
forbidOnly在 CI 下启用,避免误提交test.only - 失败重试:CI 下重试 1 次,本地默认 0 次
- 浏览器运行:headless 模式,动作超时 10 秒
- 项目:仅 Chromium,使用桌面 Chrome 设备描述
章节来源
模块加载器
load-module.js 通过 esbuild 把以下 content script 模块聚合为 IIFE:
src/content/page-utils.jssrc/content/shadow-dom-utils.jssrc/content/page-interaction.jssrc/content/page-extract.jssrc/content/interaction-tools.jssrc/shared/page-snapshot-renderer.js
打包结果挂载到 window.__tools,并提供:
getContentBundle():缓存 bundle,避免重复构建callTool(page, fnName, ...args):在 page 上下文调用工具函数callToolInFrame(page, frameName, fnName, ...args):在指定 iframe 上下文中调用工具
该设计使 e2e 测试能在真实 Chromium 中直接验证 content script 的工具链,而不必依赖扩展安装流程。
章节来源
架构总览
Playwright e2e 的执行路径可概括为:
sequenceDiagram participant Dev as "开发者" participant NPM as "npm test : e2e" participant PW as "Playwright" participant Browser as "Chromium" participant Page as "被测页面" participant Tools as "window.__tools" Dev->>NPM : 运行 npm run test : e2e NPM->>PW : playwright test PW->>Browser : 启动 headless Chromium Browser->>Page : 导航到 file : // 或内置 HTML Page->>Tools : addInitScript 注入 IIFE Page->>Tools : 调用 query / interact / extract 等工具 Tools-->>Page : 返回 success / content / availableOptions 等 Page-->>PW : expect 断言 PW-->>Dev : 测试结果
图表来源
详细组件分析
内容工具端到端测试
content-tools.e2e.spec.js 是内容脚本工具链的代表性 e2e 用例,覆盖:
queryInteractiveElements→interactByRef完整链路fillForm多类型表单填充selectDropdown原生 select 选择clickByText文本点击- Shadow DOM 穿透查询与点击
scrollAndCollect无限滚动收集extractLinks、pageToJson链接与表格提取searchInPage正则搜索getIframeContent同源 iframe 内容获取queryInteractiveElements分页、叠加层提升、去重、ref 稳定性
该用例通过 addInitScript 注入 window.__tools,再使用 callTool 调用工具方法,最后用 Playwright 断言 DOM 状态、返回值字段和交互结果。
flowchart TD
Start(["进入用例"]) --> LoadBundle["加载 content 工具 bundle"]
LoadBundle --> Inject["addInitScript 注入 window.__tools"]
Inject --> Navigate["导航到 fixtures 页面"]
Navigate --> Query["调用 queryInteractiveElements"]
Query --> ValidateQuery{"查询成功?"}
ValidateQuery --> |否| Fail["断言失败"]
ValidateQuery --> |是| Interact["调用 interactByRef 或 fillForm/select/click"]
Interact --> AssertDOM["断言 DOM 值、属性、可见性"]
AssertDOM --> End(["用例结束"])
图表来源
章节来源
商品录入 Demo 页面冒烟测试
demo-product-form.e2e.spec.js 针对演示页面进行冒烟测试,覆盖:
- 四步向导流转
- 模态框打开、条款勾选、确认提交
- 提交后列表追加、回到 Step1
- 必填校验阻止下一步并显示错误
- 双语切换保持选中值与持久化
- 清空列表恢复空态
- 叠加层提升到
[打开层],ref 可操作
该用例具备“素材缺失自动跳过”机制:若 .resources/demo/form-autofill/product-form.html 不存在,则整组用例 skip,避免克隆仓库时出现无关失败。
flowchart TD
CheckDemo["检查 demo 页面是否存在"] --> HasDemo{"存在?"}
HasDemo --> |否| Skip["跳过整组用例"]
HasDemo --> |是| RunSuite["运行商品录入用例"]
RunSuite --> StepFlow["四步向导填写"]
StepFlow --> Modal["打开确认模态框"]
Modal --> Terms["勾选条款并提交"]
Terms --> ListAppend["列表追加记录"]
ListAppend --> Reset["重置或清空列表"]
Reset --> OverlayRef["叠加层 ref 操作"]
OverlayRef --> End(["用例结束"])
图表来源
章节来源
消息跳转交互测试
message-jump-interaction.e2e.spec.js 用真实鼠标、键盘事件驱动侧边栏消息跳转把手,覆盖:
- 悬停把手展开列表
- 鼠标移出自动收起,钉住后移出保持展开
- Tab 键盘可达,↑/↓ 移动焦点
- 搜索过滤命中子集,编号仍为全局组序
- 中文输入法组合期间不销毁输入框,上屏后才过滤
- Esc 优先清空搜索词,再按一次退钉住/收起
- 边界反馈:最后一组继续向下跳时按钮进入 blocked 态
- 钉住后手动滚列表去点远处条目,不会被“跟随当前项”拽回顶部
- 点击条目后移开鼠标再滚对话,当前项会滚进列表可视区
该用例通过 esbuild 动态构建 NavKit,并在内存 HTML 中注入真实样式与 DOM,从而验证真实交互而非纯逻辑。
sequenceDiagram participant PW as "Playwright" participant Page as "内存 HTML 页面" participant NavKit as "NavKit.initMessageJumpNav" participant User as "用户交互" PW->>Page : setContent(navPageHtml) Page->>NavKit : initMessageJumpNav() User->>Page : hover 把手 Page-->>PW : 面板 expanded User->>Page : click 钉住 User->>Page : type 搜索词 User->>Page : press Escape Page-->>PW : 清词、恢复全部、退钉住、收起
图表来源
- test/e2e/message-jump-interaction.e2e.spec.js#L22-L41
- test/e2e/message-jump-interaction.e2e.spec.js#L47-L82
- test/e2e/message-jump-interaction.e2e.spec.js#L84-L274
章节来源
消息跳转布局与渲染测试
message-jump.e2e.spec.js 侧重真实布局与滚动下的消息跳转分组定位,覆盖:
- 问答组覆盖每条提问与其后的回答,编号连续
- 按编号跳转后反查当前组得到同一编号
- 连按上一条/下一条在组间移动并停在边界
- 面板为实色表面,条目不折行成单行截断
- 长提问恰好两行且由 CSS 画省略号
- 折行的第二行与第一行文字左对齐
- 当前组条目高亮态样式
- 暗色主题下列表同样是实色底
- 列表垂直滚动时 Header 固定可见
- 面板无显式高度时列表区不得塌成 0
- 预览文案折叠空白并按上限截断
该用例读取真实 theme.css 与 side_panel/styles.css,并使用真实 renderQuestionListHtml 生成面板内容,避免手写副本漂移。
章节来源
选项页保存按钮栏吸附测试
options-save-bar.e2e.spec.js 验证真实浏览器下保存按钮栏的吸附行为:
- 内容超出一屏:按钮栏吸附在视口底部保持可见
- 滚到页面底部后释放回文档流
- 内容一屏放得下:按钮栏保持在文档流正常位置
该用例通过 esbuild 打包 src/options/save-bar.js,并注入真实 src/options/styles.css,构造与真实 options 页面一致的层级结构。
章节来源
iframe 跨帧 ref 测试
iframe-ref.e2e.spec.js 验证真实 iframe 环境下跨帧 ref 采集与整体渲染组装,覆盖:
- auto 采集:frame 节点属性与 display:none 剪枝
- none 采集旧入口薄包装
- 子帧内采集:frameInfo 来自 srcdoc 文档、loadId 存在
- 可见性判定:可见帧 true;display:none 与零尺寸 false
- 组装渲染:占位行编号 + 子帧区块缩进 + 隐藏帧移除
- 子帧内本地 ref 交互:注册表解析 + type 生效
该用例复用 load-module.js 提供的 callTool 与 callToolInFrame,确保跨帧工具调用路径正确。
章节来源
依赖关系分析
e2e 测试对仓库其他部分的依赖如下:
graph LR PWConfig["playwright.config.js"] --> Specs["所有 .e2e.spec.js"] Specs --> LoadModule["helpers/load-module.js"] Specs --> ContentModules["src/content/*"] Specs --> SharedModules["src/shared/*"] Specs --> SidePanelCSS["src/side_panel/styles.css"] Specs --> ThemeCSS["src/shared/theme.css"] Specs --> OptionsCSS["src/options/styles.css"] Specs --> Esbuild["esbuild"]
图表来源
- playwright.config.js#L4-L18
- test/e2e/helpers/load-module.js#L12-L32
- test/e2e/message-jump-interaction.e2e.spec.js#L22-L41
- test/e2e/message-jump.e2e.spec.js#L21-L39
- test/e2e/options-save-bar.e2e.spec.js#L18-L29
章节来源
性能与执行策略
并行测试
Playwright 配置开启 fullyParallel: true,意味着每个 spec 文件内的用例可在不同 worker 中并行执行。这能显著缩短 CI 时间,但也要求用例之间保持隔离,避免共享全局状态。
重试机制
- 本地环境:
retries: 0 - CI 环境:
retries: 1
建议在新增不稳定用例时先定位根因,而不是盲目增加重试次数。对于网络或外部资源依赖的用例,应优先改为本地 fixture 或 mock。
动作超时
actionTimeout: 10000 表示每次 action 最多等待 10 秒。若某个用例频繁超时,应检查:
- 是否缺少
waitForSelector或waitForFunction - 是否动画未结束就断言
- 是否 fixture 页面加载过慢
- 是否使用了错误的定位器
调试模式
可通过 npm run test:e2e:headed 启动 headed 模式,便于观察浏览器行为。该命令对应 package.json 中的脚本定义。
章节来源
视觉回归与 UI 一致性验证
当前仓库的 Playwright e2e 并未使用截图对比类视觉回归库,而是通过以下方式保证 UI 一致性:
| 验证方式 | 说明 | 对应用例 |
|---|---|---|
| 真实样式加载 | 测试直接读取 theme.css 与 side_panel/styles.css,避免样式漂移 | test/e2e/message-jump.e2e.spec.js |
| 计算样式断言 | 断言背景透明度、textOverflow、lineClamp、visibleLines 等 | test/e2e/message-jump.e2e.spec.js |
| 主题断言 | 断言暗色主题下面板背景与文字颜色 | test/e2e/message-jump.e2e.spec.js |
| 吸附行为断言 | 断言 .floating 类名与视口位置 | test/e2e/options-save-bar.e2e.spec.js |
| 面板结构断言 | 断言面板内部 header、list、item 的滚动行为 | test/e2e/message-jump.e2e.spec.js |
如果需要引入截图对比,建议:
- 使用 Playwright 内置
toMatchSnapshot - 固定 viewport 尺寸
- 固定字体与缩放
- 区分 light/dark 主题基线
- 将基线图片纳入版本控制
- 在 CI 中比较差异并生成报告
章节来源
故障排查指南
常见问题与处理
| 问题 | 可能原因 | 处理建议 |
|---|---|---|
用例报 test.only | 本地误提交 test.only | CI 下 forbidOnly 会直接失败;删除 only |
| 用例不稳定 | 动画未结束、DOM 未就绪、fixture 加载慢 | 增加 waitForSelector、waitForFunction,或使用 expect.poll |
| content 工具调用失败 | bundle 未注入或全局名不对 | 检查 addInitScript 与 window.__tools |
| iframe 工具调用失败 | 未找到目标 frame | 使用 callToolInFrame 并确保 frame name 匹配 |
| 样式断言失败 | 主题变量或 CSS 变更 | 同步更新测试断言,或修复样式 |
| 中文输入法导致列表重建异常 | composition 期间重建输入框 | 参考 message-jump-interaction.e2e.spec.js 的组合事件断言 |
| 吸附行为异常 | 视口高度或内容高度变化 | 检查 setupPage 中 contentHeight 与 viewportSize |
调试技巧
- 使用
npm run test:e2e:headed观察真实浏览器 - 在
beforeEach中打印page.url()与关键 DOM 结构 - 对复杂交互使用
page.screenshot()辅助定位 - 对异步动画使用
waitForTimeout或更精确的状态等待 - 对 fixture 页面问题,先用浏览器手动复现
章节来源
- playwright.config.js#L7-L11
- test/e2e/message-jump-interaction.e2e.spec.js#L152-L180
- test/e2e/options-save-bar.e2e.spec.js#L32-L42
结论
该项目的 Playwright e2e 测试体系围绕真实 Chromium、真实样式与真实 DOM 展开,重点验证内容脚本工具链、侧边栏交互、选项页吸附行为与 iframe 跨帧能力。测试通过 esbuild 动态打包模块、通过 addInitScript 注入工具、通过 fixture 页面模拟业务场景,既避免了安装扩展的复杂度,又保留了足够的真实性。
未来可扩展的方向包括:
- 引入截图对比进行视觉回归
- 增加更多跨页面流程验证
- 补充 Agent 集成测试(需启动本地 Agent 进程)
- 细化性能基准用例
- 完善测试数据管理与版本化
附录:测试脚本示例索引
| 测试目标 | 文件路径 | 关键能力 |
|---|---|---|
| 内容工具链路 | test/e2e/content-tools.e2e.spec.js | query、interact、fillForm、select、clickByText、Shadow DOM、scroll_collect、extract、search、iframe_content |
| 商品录入 Demo | test/e2e/demo-product-form.e2e.spec.js | 向导流程、模态框、双语、列表、叠加层 ref |
| 消息跳转交互 | test/e2e/message-jump-interaction.e2e.spec.js | hover、click、keyboard、composition、Esc、blocked、pinned |
| 消息跳转布局 | test/e2e/message-jump.e2e.spec.js | 分组定位、两行截断、省略号、高亮、暗色主题、滚动 |
| 保存按钮栏吸附 | test/e2e/options-save-bar.e2e.spec.js | floating、视口底部、文档流释放 |
| iframe 跨帧 ref | test/e2e/iframe-ref.e2e.spec.js | auto/none 采集、frameInfo、可见性、渲染组装 |
| 模块加载器 | test/e2e/helpers/load-module.js | getContentBundle、callTool、callToolInFrame |