{"_id":"s-auto-e2e-kit","_rev":"4-338fd141fbf17708953f5ec36e0de5e0","name":"s-auto-e2e-kit","dist-tags":{"latest":"0.1.2"},"versions":{"0.0.1":{"name":"s-auto-e2e-kit","version":"0.0.1","keywords":["playwright","e2e","end-to-end","test","testing","reporter","html-report","quantified-report","auto-e2e","auto-e2e-toolkit","factory","scene-agnostic","wait-strategies","network-mock"],"author":{"name":"lisiyuan0828"},"license":"MIT","_id":"s-auto-e2e-kit@0.0.1","maintainers":[{"name":"lisiyuan0828","email":"lisiyuan0828@gmail.com"}],"homepage":"https://github.com/lisiyuan0828/S-AutoE2eKit#readme","bugs":{"url":"https://github.com/lisiyuan0828/S-AutoE2eKit/issues"},"dist":{"shasum":"825f14540c60482a6268c3bbb5bf7a6255792d20","tarball":"https://registry.npmjs.org/s-auto-e2e-kit/-/s-auto-e2e-kit-0.0.1.tgz","fileCount":24,"integrity":"sha512-wX/kUezTTqMfZxn9jr1DTp03LX35JuO5GwjkEbidfVM47ky6Kwyqsvu9W1QDpYeBf2mSkY45nihBtfvg3a9ktw==","signatures":[{"sig":"MEQCIF72/tvjdu3mbHzMcofogbFgIrE0lhOOR0G291a/BDgSAiAuh8ciyXiyHFZjjksW4YjC894EHm4wd78AfTYTKC31uQ==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":225202},"main":"lib/index.js","engines":{"node":">=14"},"exports":{".":"./lib/index.js","./utils":"./lib/utils/index.js","./utils/*":"./lib/utils/*.js","./reporters":"./lib/reporters/index.js","./reporters/*":"./lib/reporters/*.js","./package.json":"./package.json"},"gitHead":"d99d720e5f820f1ed723b46f401ad36fc67fc297","scripts":{"sync-vendor":"node scripts/sync-vendor.js"},"_npmUser":{"name":"lisiyuan0828","email":"lisiyuan0828@gmail.com"},"repository":{"url":"git+https://github.com/lisiyuan0828/S-AutoE2eKit.git","type":"git"},"_npmVersion":"10.8.2","description":"Drop-in Playwright E2E factory toolkit: scene-agnostic reporters (quantified Markdown+JSON, multi-scene index, enhanced HTML, tabbed self-contained summary) and cross-repo utils (wait strategies, persistence, network mocks, event capture, visible text, ru","directories":{},"_nodeVersion":"20.18.0","dependencies":{"marked":"^18.0.0"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"_hasShrinkwrap":false,"peerDependencies":{"@playwright/test":">=1.55.0 <2.0.0"},"_npmOperationalInternal":{"tmp":"tmp/s-auto-e2e-kit_0.0.1_1779347277378_0.38782862216261793","host":"s3://npm-registry-packages-npm-production"}},"0.1.0":{"name":"s-auto-e2e-kit","version":"0.1.0","keywords":["playwright","e2e","end-to-end","test","testing","reporter","html-report","quantified-report","auto-e2e","auto-e2e-toolkit","factory","scene-agnostic","wait-strategies","network-mock","cli","init","scaffold","bootstrap"],"author":{"name":"lisiyuan0828"},"license":"MIT","_id":"s-auto-e2e-kit@0.1.0","maintainers":[{"name":"lisiyuan0828","email":"lisiyuan0828@gmail.com"}],"homepage":"https://github.com/lisiyuan0828/S-AutoE2eKit#readme","bugs":{"url":"https://github.com/lisiyuan0828/S-AutoE2eKit/issues"},"bin":{"auto-e2e":"bin/auto-e2e.mjs","auto-e2e-kit":"bin/auto-e2e.mjs"},"dist":{"shasum":"0daad64899d68afe82e99cae704e457a1db5bc41","tarball":"https://registry.npmjs.org/s-auto-e2e-kit/-/s-auto-e2e-kit-0.1.0.tgz","fileCount":47,"integrity":"sha512-MIVoFWwl4PhLvyWR1ATmqJRlHKYrBrrsrMM3orCL6St/Iz48LBM2sCXSkf8NNHaGOyK7mHp+QUy1OKlVjqTzKQ==","signatures":[{"sig":"MEYCIQC8duHwGkq3FDQReLNp33Di1iMM45TMwPdQIoqMkztkDQIhAMiJDqi4Z402gRb1Ac211wyPB805yaTmy7BIOxxTyIxb","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":287814},"main":"lib/index.js","engines":{"node":">=14"},"exports":{".":"./lib/index.js","./utils":"./lib/utils/index.js","./utils/*":"./lib/utils/*.js","./reporters":"./lib/reporters/index.js","./reporters/*":"./lib/reporters/*.js","./package.json":"./package.json"},"gitHead":"fde4531241c97fe0de8ff33edb1cd1ff722fd829","scripts":{"cli":"node bin/auto-e2e.mjs","cli:help":"node bin/auto-e2e.mjs --help","cli:init":"node bin/auto-e2e.mjs init","cli:doctor":"node bin/auto-e2e.mjs doctor","sync-vendor":"node scripts/sync-vendor.js"},"_npmUser":{"name":"lisiyuan0828","email":"lisiyuan0828@gmail.com"},"repository":{"url":"git+https://github.com/lisiyuan0828/S-AutoE2eKit.git","type":"git"},"_npmVersion":"10.8.2","description":"Drop-in Playwright E2E factory toolkit: scene-agnostic reporters (quantified Markdown+JSON, multi-scene index, enhanced HTML, tabbed self-contained summary) and cross-repo utils (wait strategies, persistence, network mocks, event capture, visible text, ru","directories":{},"_nodeVersion":"20.18.0","dependencies":{"marked":"^18.0.0"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"_hasShrinkwrap":false,"peerDependencies":{"@playwright/test":">=1.55.0 <2.0.0"},"_npmOperationalInternal":{"tmp":"tmp/s-auto-e2e-kit_0.1.0_1779352400315_0.06164208390566128","host":"s3://npm-registry-packages-npm-production"}},"0.1.1":{"name":"s-auto-e2e-kit","version":"0.1.1","keywords":["playwright","e2e","end-to-end","test","testing","reporter","html-report","quantified-report","auto-e2e","auto-e2e-toolkit","factory","scene-agnostic","wait-strategies","network-mock","cli","init","scaffold","bootstrap"],"author":{"name":"lisiyuan0828"},"license":"MIT","_id":"s-auto-e2e-kit@0.1.1","maintainers":[{"name":"lisiyuan0828","email":"lisiyuan0828@gmail.com"}],"homepage":"https://github.com/lisiyuan0828/S-AutoE2eKit#readme","bugs":{"url":"https://github.com/lisiyuan0828/S-AutoE2eKit/issues"},"bin":{"auto-e2e":"bin/auto-e2e.mjs","auto-e2e-kit":"bin/auto-e2e.mjs"},"dist":{"shasum":"e7121749989d0cf05ce2835724d102f2a0df3d6e","tarball":"https://registry.npmjs.org/s-auto-e2e-kit/-/s-auto-e2e-kit-0.1.1.tgz","fileCount":46,"integrity":"sha512-A2mpbzpSq4OE9A2OLOx2oLyNmoxL7GUtv38pWzuKCourLbOoQ8YXrQuhiU2UQKvse0n0Y6e5cB9NmuZiYo3h1Q==","signatures":[{"sig":"MEUCIHkQPSjHhKx6bbcgcBtAWnPvZAOGGX5nxx5Y7Wh3ITA0AiEAvKgJ2v4KoH6oKjCpbjEri6FTvRZ0fAhcpqTPgXHjYVY=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":286762},"main":"lib/index.js","engines":{"node":">=14"},"exports":{".":"./lib/index.js","./utils":"./lib/utils/index.js","./utils/*":"./lib/utils/*.js","./reporters":"./lib/reporters/index.js","./reporters/*":"./lib/reporters/*.js","./package.json":"./package.json"},"gitHead":"958c9c8662a9c18b0f119576e71f06ce80d1b001","scripts":{"cli":"node bin/auto-e2e.mjs","cli:help":"node bin/auto-e2e.mjs --help","cli:init":"node bin/auto-e2e.mjs init","cli:doctor":"node bin/auto-e2e.mjs doctor","sync-vendor":"node scripts/sync-vendor.js"},"_npmUser":{"name":"lisiyuan0828","email":"lisiyuan0828@gmail.com"},"repository":{"url":"git+https://github.com/lisiyuan0828/S-AutoE2eKit.git","type":"git"},"_npmVersion":"10.8.2","description":"Drop-in Playwright E2E factory toolkit: scene-agnostic reporters (quantified Markdown+JSON, multi-scene index, enhanced HTML, tabbed self-contained summary) and cross-repo utils (wait strategies, persistence, network mocks, event capture, visible text, ru","directories":{},"_nodeVersion":"20.18.0","dependencies":{"marked":"^18.0.0"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"_hasShrinkwrap":false,"peerDependencies":{"@playwright/test":">=1.55.0 <2.0.0"},"_npmOperationalInternal":{"tmp":"tmp/s-auto-e2e-kit_0.1.1_1779354771999_0.38705474088582403","host":"s3://npm-registry-packages-npm-production"}},"0.1.2":{"name":"s-auto-e2e-kit","version":"0.1.2","description":"Drop-in Playwright E2E factory toolkit: scene-agnostic reporters (quantified Markdown+JSON, multi-scene index, enhanced HTML, tabbed self-contained summary) and cross-repo utils (wait strategies, persistence, network mocks, event capture, visible text, ru","license":"MIT","author":{"name":"lisiyuan0828"},"homepage":"https://github.com/lisiyuan0828/S-AutoE2eKit#readme","repository":{"type":"git","url":"git+https://github.com/lisiyuan0828/S-AutoE2eKit.git"},"bugs":{"url":"https://github.com/lisiyuan0828/S-AutoE2eKit/issues"},"main":"lib/index.js","bin":{"auto-e2e":"bin/auto-e2e.mjs","auto-e2e-kit":"bin/auto-e2e.mjs"},"exports":{".":"./lib/index.js","./reporters":"./lib/reporters/index.js","./reporters/*":"./lib/reporters/*.js","./utils":"./lib/utils/index.js","./utils/*":"./lib/utils/*.js","./package.json":"./package.json"},"scripts":{"sync-vendor":"node scripts/sync-vendor.js","cli":"node bin/auto-e2e.mjs","cli:init":"node bin/auto-e2e.mjs init","cli:doctor":"node bin/auto-e2e.mjs doctor","cli:help":"node bin/auto-e2e.mjs --help"},"peerDependencies":{"@playwright/test":">=1.55.0 <2.0.0"},"dependencies":{"marked":"^18.0.0"},"engines":{"node":">=14"},"keywords":["playwright","e2e","end-to-end","test","testing","reporter","html-report","quantified-report","auto-e2e","auto-e2e-toolkit","factory","scene-agnostic","wait-strategies","network-mock","cli","init","scaffold","bootstrap"],"publishConfig":{"registry":"https://registry.npmjs.org/","access":"public"},"_id":"s-auto-e2e-kit@0.1.2","gitHead":"36d13ad97650491c18815c114149d82bbed925a6","_nodeVersion":"20.18.0","_npmVersion":"10.8.2","dist":{"integrity":"sha512-XbdKoLF/DyiKXQLDWZoxO/UQOjGW5WiQaXli39iEoiVtPHn7zSLOERAPTS/bdcrYz3vMiqKfXwf/POzq/d/R+Q==","shasum":"f7528ff7211063136ada21db4133d0600693bb48","tarball":"https://registry.npmjs.org/s-auto-e2e-kit/-/s-auto-e2e-kit-0.1.2.tgz","fileCount":46,"unpackedSize":293854,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQCUiAKJHwkC+G4gyXsYwWGXyhiRbs3kG/x/CLhETl1OjAIhALtXKsfEhhYsmCout2bgi7E1nt9e+hCk8LaPoXlNjV76"}]},"_npmUser":{"name":"lisiyuan0828","email":"lisiyuan0828@gmail.com"},"directories":{},"maintainers":[{"name":"lisiyuan0828","email":"lisiyuan0828@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/s-auto-e2e-kit_0.1.2_1779355203472_0.8716233722888029"},"_hasShrinkwrap":false}},"time":{"created":"2026-05-21T07:07:57.269Z","modified":"2026-05-21T09:20:03.776Z","0.0.1":"2026-05-21T07:07:57.526Z","0.1.0":"2026-05-21T08:33:20.482Z","0.1.1":"2026-05-21T09:12:52.202Z","0.1.2":"2026-05-21T09:20:03.640Z"},"bugs":{"url":"https://github.com/lisiyuan0828/S-AutoE2eKit/issues"},"author":{"name":"lisiyuan0828"},"license":"MIT","homepage":"https://github.com/lisiyuan0828/S-AutoE2eKit#readme","keywords":["playwright","e2e","end-to-end","test","testing","reporter","html-report","quantified-report","auto-e2e","auto-e2e-toolkit","factory","scene-agnostic","wait-strategies","network-mock","cli","init","scaffold","bootstrap"],"repository":{"type":"git","url":"git+https://github.com/lisiyuan0828/S-AutoE2eKit.git"},"description":"Drop-in Playwright E2E factory toolkit: scene-agnostic reporters (quantified Markdown+JSON, multi-scene index, enhanced HTML, tabbed self-contained summary) and cross-repo utils (wait strategies, persistence, network mocks, event capture, visible text, ru","maintainers":[{"name":"lisiyuan0828","email":"lisiyuan0828@gmail.com"}],"readme":"# s-auto-e2e-kit\n\n> 即装即用的 **Playwright E2E 测试工厂套件**：场景无关的 reporters、跨仓库通用 utils、量化版 HTML 报告 —— 装一次，所有用 Playwright 的内部仓库都能复用。\n\n[English](./README.md) · [简体中文](./README.zh.md)\n\n[![npm](https://img.shields.io/npm/v/s-auto-e2e-kit.svg)](https://www.npmjs.com/package/s-auto-e2e-kit)\n[![license](https://img.shields.io/badge/license-MIT-green.svg)](#许可证)\n[![Node](https://img.shields.io/badge/node-%E2%89%A514-brightgreen.svg)](#环境要求)\n[![Playwright](https://img.shields.io/badge/playwright-%E2%89%A51.55%20%3C2-blueviolet.svg)](#环境要求)\n\n---\n\n## 目录\n\n- [为什么需要 s-auto-e2e-kit](#为什么需要-s-auto-e2e-kit)\n- [能力矩阵](#能力矩阵)\n- [快速开始](#快速开始)\n- [API 参考](#api-参考)\n  - [Reporters](#reporters)\n  - [Utils](#utils)\n- [架构](#架构)\n- [仓库结构](#仓库结构)\n- [从源码构建](#从源码构建)\n- [设计原则 —— 5 条铁律](#设计原则--5-条铁律)\n- [常见问题](#常见问题)\n- [版本策略](#版本策略)\n- [许可证](#许可证)\n\n---\n\n## 为什么需要 s-auto-e2e-kit\n\n每个跑 Playwright 端到端测试的团队都在重复造同一套底层管线：拼一份量化版 Markdown/JSON 报告、再写一遍等待策略（visible / text / count / response / nav / event）、再造一遍持久化助手（cookie / localStorage / 事件缓冲）、再封一层网络 mock 原语，最后还要在 Playwright 自带 HTML 报告之上拧一份业务定制的 HTML。**s-auto-e2e-kit** 把这些工作压成一个 npm 包，开箱即用：\n\n1. **4 个 reporter** —— `quantified`（Markdown + JSON）、`sceneIndex`（多场景汇总）、`html`（继承 Playwright HtmlReporter 并自动挂载业务 JSON）、`quantifiedSummary`（自完备的 5 tab 详情 HTML）。\n2. **6 个 util** —— `wait`、`persistence`、`mocks`、`eventCapture`、`visibleText`、`runContext`。\n3. **同一份契约** —— 所有 reporter / util 都是**场景无关**的：包内不内置任何项目专属域名、Cookie 键、API 路径、step 蓝图。业务相关参数全部由调用方注入。\n\n**设计理念**\n\n- **物理隔离**。不依赖 webpack alias、不依赖 `@/` 路径、不假设宿主仓库的目录结构。纯 CommonJS，Node ≥ 14 即可。\n- **零项目硬编码**。项目名、输出路径、域名、Cookie 键、API mock 规则 —— 任何业务相关信息一律通过 reporter `options` / util 参数传入。\n- **包内自洽**。依赖只声明在本包 `package.json`，绝不\"借用\"宿主仓库的 `node_modules`。\n- **派生产物不入库**。原 Phase 4 Plan A 计划 vendor Playwright HTML reporter 产物，最终采用的 Plan F 改为通过 `playwright/lib/runner` 公共 exports 直接继承 `HtmlReporter` —— 无需 vendor，无脆弱路径 hack。\n- **接入路径 = 发包后路径**。无论本地 `file:` 协议链接还是从 npm 安装，调用方都写 `require('s-auto-e2e-kit')`。从本地切到发布版 = `package.json` 改一行，业务侧 0 改动。\n\n## 能力矩阵\n\n| 能力 | 入口 | 场景无关 |\n|---|---|---|\n| 量化 Markdown + JSON 报告（每个用例一份） | `reporters.quantified` | ✅ —— step 蓝图字典通过 `opts.blueprintLookup` 注入 |\n| 多场景汇总入口 | `reporters.sceneIndex` | ✅ |\n| 增强版 Playwright HTML 报告（自动挂载业务 JSON） | `reporters.html` | ✅ —— 通过 `playwright/lib/runner` 继承官方 `HtmlReporter` |\n| 自完备的量化总览 HTML（5 个 tab） | `reporters.quantifiedSummary` | ✅ |\n| 6 种等待策略（VIS / TXT / CNT / RES / NAV / EVT） | `utils.wait` | ✅ |\n| 持久化读写（localStorage / sessionStorage / cookie / window / 事件缓冲） | `utils.persistence` | ✅ —— channel 列表由调用方传入 |\n| 网络 mock 三原语（`mockJson` / `applyMockRules` / `silenceStaticResources`） | `utils.mocks` | ✅ —— 规则由调用方传入 |\n| 事件捕获（基于 localStorage，reload 安全） | `utils.eventCapture` | ✅ —— 事件名 / storageKey 通过构造函数传 |\n| 提取页面可见文本 + 翻译完整度判定 | `utils.visibleText` | ✅ |\n| 运行上下文（`YYYYMMDD_<branch>` 戳 + 项目根解析） | `utils.runContext` | ✅ |\n\n## 快速开始\n\n### 安装\n\n```bash\nnpm i -D s-auto-e2e-kit\n# 或：pnpm add -D s-auto-e2e-kit\n# 或：yarn add -D s-auto-e2e-kit\n```\n\nPeer 依赖：`@playwright/test >=1.55.0 <2.0.0`。Node ≥ 14（增强版 HTML reporter 用到 `playwright/lib/runner`，建议 Node ≥ 18）。\n\n### 接入 reporter\n\n```js\n// playwright.config.js\nconst { reporters } = require('s-auto-e2e-kit');\n\nmodule.exports = {\n  reporter: [\n    ['list'],\n\n    // 增强版 HTML 报告 —— 继承 Playwright 自带 HtmlReporter，\n    // 自动把每个 test 的 quantified.json 挂为可下载 attachment。\n    reporters.html.resolveHtmlReporter({\n      outputFolder: 'playwright-report-enhanced',\n      open: 'never',\n      title: '我的项目 E2E',\n      attachQuantifiedJson: true,\n      resultsRoot: 'test-results',\n    }),\n\n    // 自完备的 tab 化详情 HTML（5 个 tab：详情 / 截图 / 错误 / 信号 / Trace）。\n    reporters.quantifiedSummary.resolve({\n      outputFile: 'playwright-report-enhanced/<runStamp>/quantified-summary.html',\n      testResultsDir: 'test-results/<runStamp>',\n      title: '我的项目 E2E · 量化报告',\n    }),\n  ],\n};\n```\n\n### 在 spec 中驱动量化 reporter\n\n```js\nconst { test, expect } = require('@playwright/test');\nconst { reporters, utils } = require('s-auto-e2e-kit');\n\ntest('TC-LOGIN-P0-01 · 登录主流程', async ({ page }) => {\n  const reporter = new reporters.quantified.QuantifiedReporter({\n    sceneName: 'login',\n    caseId: 'TC-LOGIN-P0-01',\n    title: '登录主流程',\n    priority: 'P0',\n    projectRoot: process.cwd(),\n    // blueprintLookup 是「可选」 —— 不传走「无蓝图降级模式」。\n    // 想要 phase 分组、中文语义步骤标题、失败手册等增强能力，\n    // 由调用方注入业务专属字典 —— 业务相关数据始终由调用方持有。\n  });\n\n  await reporter.runStep('open-page', async () => {\n    await page.goto('https://example.com/login');\n    await utils.wait.waitVisible(page, '#login-form');\n  });\n\n  // ... 更多 step ...\n\n  await reporter.finalize();\n});\n```\n\n### 单独使用 utils\n\n```js\nconst { utils } = require('s-auto-e2e-kit');\n\n// 6 种等待策略\nawait utils.wait.waitVisible(page, '#hero');\nawait utils.wait.waitText(page, '#switcher', '中文');\n\n// 网络 mock（规则由调用方决定）\nawait utils.mocks.applyMockRules(page, [\n  { url: /\\/api\\/user$/, body: { id: 1, name: 'alice' } },\n]);\nawait utils.mocks.silenceStaticResources(page, ['cdn.example.com']);\n\n// 持久化（channel 由调用方声明）\nconst snapshot = await utils.persistence.readPersistence(page, {\n  localStorage: ['app:locale', 'app:theme'],\n  cookie: ['session_id'],\n});\n\n// 运行戳：YYYYMMDD_<gitBranch>，用于测试产物分目录\nconst { stamp } = utils.runContext.getRunStamp({ projectRoot: process.cwd() });\n```\n\n## API 参考\n\n### Reporters\n\n#### `reporters.quantified`\n\n```ts\nnew QuantifiedReporter(opts: {\n  sceneName: string;          // 例 'login' —— 决定输出目录\n  caseId: string;             // 例 'TC-LOGIN-P0-01'\n  title: string;\n  priority: 'P0' | 'P1' | 'P2';\n  dimensions?: string[];      // 覆盖维度，如 ['F2', 'P1-P5']\n  entryUrl?: string;\n  projectRoot: string;\n  blueprintLookup?: BlueprintLookup;  // 可选 —— 调用方注入的业务字典\n});\n```\n\n产物：\n\n- `docs/qa-reports/<runStamp>/<scene>/<caseId>.md` —— 给人看的 Markdown\n- `test-results/<runStamp>/<scene>/<caseId>.json` —— 给机器解析的 JSON（含 `shotIndex`、`shotsByStep`、持久化快照、console / pageError / 网络失败时间线）\n\n不传 `blueprintLookup` 时进入\"无蓝图降级\"模式：数据采集和基础渲染照常工作，只是没有 phase 分组、语义步骤标题、失败手册。**业务专属蓝图永远留在消费方仓库**，绝不进包。\n\n#### `reporters.sceneIndex`\n\n```ts\nbuildSceneIndex(opts: { projectRoot: string; runStamp?: string }): Promise<void>\n```\n\n遍历 `docs/qa-reports/<runStamp>/*`，在顶层写一份 `INDEX.md` 把所有场景报告链起来。\n\n#### `reporters.html`\n\n```ts\nreporters.html.resolveHtmlReporter(opts: {\n  outputFolder?: string;          // 默认 'playwright-report-enhanced'\n  open?: 'always' | 'never' | 'on-failure';\n  title?: string;\n  attachQuantifiedJson?: boolean; // 默认 true\n  resultsRoot?: string;           // 默认 'test-results'\n}): [bridgePath: string, opts: object]\n```\n\n返回 Playwright reporter 数组项。内部通过 `require('playwright/lib/runner').html` 拿到官方 `HtmlReporter`（Playwright ≥ 1.60），再 `extends` 出来注入业务 attachment。环境不可用时降级为占位 reporter，只 warning 一次，不 crash 测试。\n\n进阶/单测用法还可拿到：`bridgePath`、`createEnhancedHtmlReporter(BaseHtmlReporter)`、`tryLoadPlaywrightHtml()`。\n\n#### `reporters.quantifiedSummary`\n\n```ts\nreporters.quantifiedSummary.resolve(opts: {\n  outputFile: string;       // 支持 '<runStamp>' 占位符\n  testResultsDir: string;   // 支持 '<runStamp>' 占位符\n  title?: string;\n}): [reporterPath: string, opts: object]\n```\n\n在 `outputFile` 处生成一份完全自完备的 HTML：内联自己的 JS / CSS，把所有引用到的截图、Playwright 产物（trace.zip / video.webm / test-failed-N.png）拷贝到 `<outputDir>/_assets/<caseId>/{shots,pw}/`，并把所有引用改写成相对路径 —— 整个 `playwright-report-enhanced/<runStamp>/` 目录可以**整包打包发同事 / 上传 CI artifact**，在任何机器上打开都还能正常加载。\n\n右侧详情区分 5 个 tab：\n\n| Tab | 内容 |\n|---|---|\n| 📝 详情 | 步骤的 Markdown 详情（已渲染） |\n| 📷 截图 | reporter 自拍截图（success / error）+ Playwright 自动 `test-failed-N.png`，缩略图 + 点开大图，懒加载 |\n| ⚠️ 错误 | fail step 的 actual + 时间窗口内的 pageError（带堆栈折叠） |\n| 📋 信号 | 时间窗口内的 console / requestfailed（按 step.startTs ± 200ms 切片） |\n| 📦 Trace | 一键复制 `npx playwright show-trace <abs-path>` + 跳同目录 PW 原生报告 + 下载本地 trace.zip 副本 |\n\n进阶/单测用法还可拿到：`QuantifiedSummaryReporter`、`scanQuantifiedJsons`、`renderSummaryHtml`。\n\n### Utils\n\n#### `utils.wait` —— 6 种策略\n\n```ts\nwaitVisible(page, selector, opts?)              // VIS —— 元素可见 + 稳定\nwaitText(page, selector, expectedText, opts?)   // TXT —— 文本匹配\nwaitCount(page, selector, expectedCount, opts?) // CNT —— 元素数量\nwaitResponse(page, urlMatcher, opts?)           // RES —— 网络响应\nwaitNavigation(page, urlMatcher?, opts?)        // NAV —— URL 变化\nwaitEvent(page, eventName, predicate?, opts?)   // EVT —— 页面事件\n```\n\n#### `utils.persistence` —— 通用原语\n\n```ts\nreadPersistence(page, channels: { localStorage?: string[]; sessionStorage?: string[]; cookie?: string[]; window?: string[] }): Promise<Snapshot>\nclearAllPersistence(page): Promise<void>\npresetLocalStorage(page, kv: Record<string, string>): Promise<void>\npresetCookies(context, cookies: Cookie[]): Promise<void>\ninjectNavigatorLanguage(context, lang: string): Promise<void>\ninstallEventBuffer(context, opts: { storageKey: string }): Promise<void>\n```\n\n> 项目专属的 `readI18nPersistence` / `installLocaleEventCapture` 之类**不在**本包内 —— 它们以适配层形式留在消费方仓库。\n\n#### `utils.mocks` —— 三原语\n\n```ts\nmockJson(page, urlMatcher, body, opts?): Promise<void>\napplyMockRules(page, rules: MockRule[]): Promise<void>\nsilenceStaticResources(page, domains: string[]): Promise<void>\n```\n\n> 项目专属的聚合（如某产品线的 `mockHeaderApis`）应留在消费方仓库的适配层。\n\n#### `utils.eventCapture` —— reload 安全的事件缓冲\n\n```ts\nnew EventCapture({ eventName: string; storageKey?: string })\n  .install(context): Promise<void>\n  .read(page): Promise<Event[]>\n  .clear(page): Promise<void>\n```\n\n旧版 `STORAGE_KEY` 别名向后兼容导出。\n\n#### `utils.visibleText`\n\n```ts\ncollectVisibleTexts(page, opts?): Promise<string[]>\nclassifyTranslation(source: string, target: string): '✅ 已翻译' | '⚠️ 部分翻译' | '❌ 未翻译' | '⏭️ 不需翻译'\n```\n\n#### `utils.runContext`\n\n```ts\ngetRunStamp(opts: { projectRoot: string }): { stamp: string; branch: string; date: string }\nsanitizeBranch(branch: string): string\ntodayYYYYMMDD(): string\n```\n\n## 架构\n\n```\n┌──────────────────────────────────────────────────────────┐\n│                  消费方（宿主仓库）                        │\n│  playwright.config.js     spec.js     reporter 适配层      │\n│         │                    │                │            │\n│         └────── require('s-auto-e2e-kit') ────┘            │\n└──────────────────────────────────────────────────────────┘\n                              │\n                              ▼\n┌──────────────────────────────────────────────────────────┐\n│                    s-auto-e2e-kit                         │\n│                                                           │\n│  reporters/                                               │\n│   ├─ quantified-reporter.js          (Markdown + JSON)   │\n│   ├─ scene-index-reporter.js         (多场景汇总)          │\n│   ├─ html-reporter.js  ─►  html-reporter-bridge.js       │\n│   │                       (通过 playwright/lib/runner     │\n│   │                        继承 HtmlReporter)             │\n│   ├─ quantified-summary-reporter.js  (5 tab 自完备 HTML，  │\n│   │                                    资产复制)           │\n│   └─ _summary-template/{html,css,js}（渲染模板）          │\n│                                                           │\n│  utils/                                                   │\n│   ├─ wait-strategies.js     (6 种策略)                    │\n│   ├─ persistence-helpers.js (通用原语)                    │\n│   ├─ network-mocks.js       (3 原语)                      │\n│   ├─ event-capture.js       (reload-safe buffer)         │\n│   ├─ visible-text.js                                     │\n│   └─ run-context.js         (YYYYMMDD_<branch> 戳)        │\n└──────────────────────────────────────────────────────────┘\n                              │\n                              ▼\n            @playwright/test  (peer dep, ≥1.55 <2)\n```\n\n## 仓库结构\n\n```\ns-auto-e2e-kit/\n├── lib/\n│   ├── index.js                        # 顶层入口 —— { reporters, utils, version }\n│   ├── reporters/\n│   │   ├── index.js                    # reporter 子入口\n│   │   ├── quantified-reporter.js\n│   │   ├── scene-index-reporter.js\n│   │   ├── html-reporter.js\n│   │   ├── html-reporter-bridge.js\n│   │   ├── quantified-summary-reporter.js\n│   │   ├── quantified-summary-reporter-bridge.js\n│   │   └── _summary-template/{summary.html, summary.css, summary.js}\n│   └── utils/\n│       ├── index.js                    # utils 子入口\n│       ├── wait-strategies.js\n│       ├── persistence-helpers.js\n│       ├── network-mocks.js\n│       ├── event-capture.js\n│       ├── visible-text.js\n│       └── run-context.js\n├── scripts/\n│   └── sync-vendor.js                  # Phase 4 Plan A 历史占位；Plan F 已不再需要\n├── package.json\n├── README.md / README.zh.md\n├── CHANGELOG.md\n└── LICENSE\n```\n\n## 从源码构建\n\n### 环境要求\n\n- Node.js **≥ 14**（建议 ≥ 18 —— `reporters.html` 用到 `playwright/lib/runner`，对 Node 版本有要求）\n- 宿主仓库需有 `@playwright/test` `>=1.55.0 <2.0.0`\n- 唯一运行时依赖：[`marked`](https://www.npmjs.com/package/marked) `^18.0.0`（`quantifiedSummary` 用来渲染 step detail Markdown）\n\n### 在宿主仓库本地开发\n\n```bash\n# 1. 宿主仓库 package.json 通过 file: 协议链接\n#    \"devDependencies\": {\n#      \"s-auto-e2e-kit\": \"file:../path/to/s-auto-e2e-kit\"\n#    }\nnpm install\n\n# 2. 改包内代码，重跑 e2e\nnpx playwright test\n```\n\n> 因为 `lib/` 是纯 CommonJS，**无构建步骤**。包内改完代码下次 `require()` 立即生效。\n\n### 本地打包验证\n\n```bash\nnpm pack\n# 产出 s-auto-e2e-kit-<version>.tgz\n# 在沙箱里安装验证：\nmkdir /tmp/saek-test && cd /tmp/saek-test && npm init -y\nnpm install /path/to/s-auto-e2e-kit-*.tgz\nnode -e \"console.log(require('s-auto-e2e-kit').version)\"\n```\n\n### 发布到 npm\n\n```bash\n# 升 package.json 版本号\nnpm version patch     # 或 minor / major\n# 发布 —— package.json 的 publishConfig 强制走 registry.npmjs.org 并以 public 访问\nnpm publish\n```\n\n`.npmignore` 把发包内容收紧：只有 `lib/`、`scripts/`、`README*.md`、`CHANGELOG.md`、`LICENSE` 进 tarball。\n\n## 设计原则 —— 5 条铁律\n\n1. **物理隔离**。包内代码绝不依赖宿主仓库 webpack alias、`@/` 路径或目录约定。\n2. **零项目硬编码**。所有项目相关数据（项目名、输出路径、域名、mock 规则、Cookie 键、step 蓝图）一律通过 reporter `options` / util 参数注入。包内源码 grep 任何产品线关键字都应 0 命中。\n3. **包内自洽**。依赖只声明在本包 `package.json`，绝不\"借用\"宿主仓库的 `node_modules`。\n4. **派生产物不入库**。原本要 vendor 的需求（如 Phase 4 Plan A：拷贝 Playwright HTML reporter 产物）改为通过 `playwright/lib/runner` 公开 exports 继承官方实现（Plan F），无需 vendor，无路径 hack。\n5. **接入路径 = 发包后路径**。调用方一律 `require('s-auto-e2e-kit')`。从 `file:` 链接切到 npm 发布版只需改 `package.json` 一行，业务侧 0 改动。\n\n## 常见问题\n\n**Q：4 个 reporter 必须一起用吗？**\n不需要。它们可自由组合。最小配置就是 `['list', reporters.html.resolveHtmlReporter({...})]`。需要 tab 化自完备 HTML 时再加 `quantifiedSummary`；需要每个用例的 Markdown 报告时再在 spec 内驱动 `quantified`。\n\n**Q：业务专属的 step 蓝图 / mock 规则 / Cookie 键放哪里？**\n留在宿主仓库的**适配层**（如 `tests/e2e/_<product>/`），基于本包的通用原语组装出业务专属字典。包本身保持产品线 grep-clean。\n\n**Q：宿主环境拿不到 `playwright/lib/runner` 怎么办？**\n`reporters.html` 会优雅降级：bridge 打印一次 warning，给一个 noop reporter —— 测试**不会**因此 crash。其他 3 个 reporter（`quantified` / `sceneIndex` / `quantifiedSummary`）不依赖该路径，在任何 Node ≥ 14 的环境都能用。\n\n**Q：既然 Plan F 不用 vendor，为什么 `.gitignore` 里还有 `vendor/`、`scripts/sync-vendor.js` 还在？**\n历史原因：Phase 4 Plan A 计划 vendor Playwright HTML report 产物。Plan F（直接继承 `HtmlReporter`）落地后该路径作废。脚本和忽略规则保留作为决策记录，但当前实现里没有任何代码会触发它们。\n\n**Q：在 monorepo / 嵌套 workspace 里能用吗？**\n可以 —— 所有路径以调用方传入的 `projectRoot`（缺省为 `process.cwd()`）为锚，绝不基于 `__dirname`。\n\n**Q：有构建步骤吗？**\n没有。`lib/` 直接发 CommonJS，`npm publish` 原样打包。\n\n## 版本策略\n\ns-auto-e2e-kit 遵循语义化版本，从 `0.0.x` 起步。`1.0.0` 之前 minor 版本之间可能引入 break change，CHANGELOG 会显式标注（见 [CHANGELOG.md](./CHANGELOG.md)）。`1.0.0` 之后会锁定公开 API。\n\n## 许可证\n\n[MIT](./LICENSE) © s-auto-e2e-kit contributors。\n","readmeFilename":"README.zh.md"}