openspec-playwright 0.1.80 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -49,12 +49,14 @@ Both modes update `app-knowledge.md` and `app-exploration.md`. All `.spec.ts` fi
49
49
  - Edge cases requiring pre-condition data that UI cannot set up
50
50
  - Cases where Step 4 exploration confirmed no UI element exists
51
51
 
52
- **Decision rule**:
52
+ **Setup vs Assertion**: API is acceptable for **setup/precondition** (preparing test data). Every **final assertion** about visible UI state must use UI selectors — never use `page.request` to assert something the user can see on screen.
53
+
54
+ **Decision rule (per assertion)**:
53
55
 
54
56
  ```
55
- Can this be tested through the UI?
56
- → Yes → page.getByRole/ByLabel/ByText + click/fill/type + assert UI
57
- → No → record reason → use page.request
57
+ Can the user SEE this on screen?
58
+ → Yes → MUST use: page.getByRole/ByLabel/ByText + expect()
59
+ → No → Record reason → page.request acceptable
58
60
  ```
59
61
 
60
62
  **Never use API calls to replace routine UI flows.** If a test completes in < 200ms, it is almost certainly using `page.request` instead of real UI interactions.
@@ -192,8 +194,9 @@ await browser_navigate(`${BASE_URL}/<route>`);
192
194
 
193
195
  Wait for page stability:
194
196
 
195
- - Prefer `browser_wait_for` with text or selector
196
- - Avoid `networkidle` / `load` — too slow or unreliable
197
+ - **React 19 / Next.js App Router**: use `page.waitForLoadState('networkidle')` React 19 concurrent mode batches events asynchronously; 200-500ms timeouts are unreliable under resource contention
198
+ - **Vue 2/3 / Angular / React 18 / Plain JS / jQuery**: `waitForSelector(targetElement)` is sufficient and faster DOM updates are synchronous; Playwright's actionability checks auto-wait correctly
199
+ - Prefer specific element waits (`waitForSelector`) over generic load states
197
200
  - Ready signal: heading, spinner disappears, or URL change
198
201
 
199
202
  #### 4.3. Parse the snapshot
@@ -472,6 +475,16 @@ For each discovered route:
472
475
  - Read: test-plan.md, app-exploration.md, app-knowledge.md, seed.spec.ts
473
476
  - For each test case: verify selectors in real browser, then write Playwright code
474
477
 
478
+ **Per-assertion UI check** (before writing each assertion):
479
+ ```
480
+ Is this assertion about a visible UI result?
481
+ → Yes → MUST use: expect(locator) with page selector
482
+ → No → Is this a precondition or unreachable HTTP error?
483
+ → Yes → page.request is acceptable (record reason)
484
+ → No → This is a bug — rewrite with UI selector
485
+ ```
486
+ **Never use page.request for assertions the user can see on screen.** If you wrote page.request.get() for a visible result → rewrite with expect(locator) from the browser snapshot.
487
+
475
488
  **Selector verification (change mode)**:
476
489
 
477
490
  1. Navigate to route with correct auth state
@@ -798,39 +811,116 @@ If tests fail → use Playwright MCP tools to inspect UI, fix selectors, re-run.
798
811
  | `browser_take_screenshot` | Visually compare before/after fixes |
799
812
  | `browser_run_code` | Execute custom fix logic (optional) |
800
813
 
801
- **Healer Decision Table failure type → action:**
814
+ **Healer — Phase 1: Triage**
802
815
 
803
- | Failure type | Signal | Action |
804
- | — | — | — |
805
- | **Network/backend** | `fetch failed`, `net::ERR`, 4xx/5xx in console | `browser_network_requests` → verify backend → `test.skip()` + report |
806
- | **Selector changed** | Element not found | `browser_snapshot` → fix selector → re-run (attempt 1/3) |
807
- | **Assertion mismatch** | Wrong content/value | `browser_snapshot` compare fix assertion re-run (attempt 1/3) |
808
- | **Timing issue** | waitFor/evaluate timeout | Switch to `request` API or add `waitFor` re-run (attempt 1/3) |
809
- | **page.evaluate with fetch** | CORS errors in browser context | Switch to `page.request` API → re-run (attempt 1/3) |
810
- | **Auth expired** | Redirected to login mid-test | Re-run auth.setupre-run test |
811
- | **3 heals failed** | After 3 attempts, still failing | **STOP**. `test.skip()` if app bug; report recommendation if test bug |
816
+ When a test fails, classify before attempting repair:
817
+
818
+ | Failure Type | Signal | Classification | Action |
819
+ | | | | |
820
+ | **Network/Backend** | `net::ERR`, 4xx/5xx in console/network | **App Bug** | `test.skip()` + report as app bug |
821
+ | **JS Runtime Error** | Console error (non-network) | **App Bug** | `test.skip()` + report as app bug |
822
+ | **Auth Expired** | Redirected to login mid-test | **Flaky** | Re-run auth.setup → re-run |
823
+ | **Selector Not Found** | Element not found | **Test Bug** | Phase 2 Healer |
824
+ | **Assertion Mismatch** | Wrong content/value | **Ambiguous** | Phase 2 Healer |
825
+ | **Timeout** | waitFor/evaluate timeout | **Flaky** | Retry isolated (1×, not counted in heal attempts). If it passes isolated but fails in suite → **RAFT**. If it consistently times out → check framework: React 19 / Next.js App Router: add `page.waitForLoadState('networkidle')`. Vue/Angular/React 18 / Plain JS / jQuery: use `waitForSelector(targetElement)` instead of timeout tuning. |
826
+ | **Same test fails in suite, passes isolated** | — | **RAFT** | `test.skip()` in suite, note RAFT in report |
827
+
828
+ - **App Bug** → skip immediately (no healing needed)
829
+ - **Flaky** → retry once isolated
830
+ - **Test Bug / Ambiguous** → Phase 2
831
+
832
+ > **Type ≠ Blame**: "Test Bug" means the assertion or selector is wrong — it does NOT mean "blame the test author." The test was generated from the spec. Root cause may be spec ambiguity, spec→test generation error, or app→spec deviation. Only a human can determine blame.
833
+
834
+ **Healer — Phase 2: Repair**
835
+
836
+ After Triage classifies failure as "Test Bug" or "Ambiguous":
837
+
838
+ 1. Navigate to the failing page
839
+ 2. Get page snapshot: `browser_snapshot`
840
+ 3. **EXPLICIT COMPARISON** — output before fixing:
841
+ ```
842
+ ASSERTION: "<what the test expects>"
843
+ ACTUAL: "<what the snapshot shows>"
844
+ MATCH: <yes/no>
845
+ ```
846
+ 4. If MATCH=no:
847
+ - Is `ACTUAL` reasonable per the test's intended spec behavior?
848
+ - If yes → fix the assertion to match ACTUAL (app behavior is correct)
849
+ - If uncertain → **Phase 3**
850
+ 5. If selector issue → find equivalent stable selector from snapshot
851
+ 6. Apply fix → re-run **only that test** (attempt 1/3)
852
+ 7. If healed → append to `app-knowledge.md` → **Selector Fixes** table (route, old → new selector, reason)
853
+
854
+ **Healer — Phase 3: Escalate**
855
+
856
+ When Phase 2 tried ≥3 heals without success, OR ASSERTION vs ACTUAL comparison is ambiguous:
812
857
 
813
- **Healer protocol:**
814
- 1. Read failing test → identify failure type
815
- 2. Match to decision table → take action
816
- 3. After fix: re-run only that test
817
- 4. If healed: append to `app-knowledge.md` → **Selector Fixes** table (route, old selector → new selector, reason)
818
- 5. **If 3 attempts exhausted without heal → STOP**. Do not retry further.
858
+ **STOP** and output:
819
859
 
820
- ### 10. False Pass Detection
860
+ ```
861
+ E2E Test Failed — Human Decision Required
862
+
863
+ Test: <test-name>
864
+ Failure: <type>
865
+ Assertion: "<what test expects>"
866
+ Actual: "<what app shows>"
867
+
868
+ This failure could be:
869
+ 1. App does not match the spec → **app bug**
870
+ 2. Test was generated from ambiguous/incorrect spec → **spec issue**
871
+ 3. Spec itself is outdated (app was updated) → **spec drift**
872
+
873
+ Please decide:
874
+ (a) Fix the app to match the spec
875
+ (b) Update the spec to match the app
876
+ (c) Update the test assertion
877
+ (d) Skip this test with test.skip() until resolved
878
+ ```
879
+
880
+ Wait for user input before proceeding.
881
+
882
+ **Decision tree — follow the path based on user's choice:**
821
883
 
822
- Run after test suite completes (even if all pass). Common patterns:
884
+ | Choice | What to do | After fix, do this |
885
+ |--------|-----------|-------------------|
886
+ | **(a)** Fix the app to match the spec | Fix the app code | Re-run: `npx playwright test tests/playwright/<name>.spec.ts` to verify fix, then re-run `/opsx:e2e <change-name>` to confirm full suite passes |
887
+ | **(b)** Update the spec to match the app | Edit the spec file | Then update the test assertion (→ option c), or regenerate the affected part of the test |
888
+ | **(c)** Update the test assertion | Fix the assertion in `tests/playwright/<name>.spec.ts` | Re-run: `npx playwright test tests/playwright/<name>.spec.ts` to verify, then re-run `/opsx:e2e <change-name>` for full suite |
889
+ | **(d)** Skip with `test.skip()` | Add `test.skip()` to the test | Note in `app-knowledge.md` → `Selector Fixes` table with reason "human escalation — skipped pending resolution" |
890
+
891
+ **Same choice ≥ 2 times without progress**: If user picks the same option twice but the test still doesn't pass, STOP and ask: "This was tried before without success. Are you sure the root cause is still the same, or has something changed?"
892
+
893
+ After the issue is resolved, re-run:
894
+ ```
895
+ /opsx:e2e <change-name>
896
+ ```
897
+ The existing `app-exploration.md` and `test-plan.md` will be reused (idempotent — Steps 4–6 will be fast).
898
+
899
+ ### 10. False Pass Detection + RAFT Detection
900
+
901
+ Run after test suite completes (even if all pass).
902
+
903
+ **False Pass patterns** (test passed but shouldn't have):
823
904
 
824
905
  - **Conditional visibility**: `if (locator.isVisible().catch(() => false))` — if test passes, locator may not exist
825
906
  - **Too fast**: < 200ms for a complex flow is suspicious
826
907
  - **No fresh auth context**: Protected routes without `browser.newContext()`
827
908
 
909
+ **RAFT detection** (Resource-Affected Flaky Test):
910
+
911
+ - Full suite: test fails → run test isolated → passes
912
+ - This is **NOT** a test bug or app bug. Mark as RAFT, add `test.skip()` in suite, note in report
913
+ - RAFTs are infrastructure coupling issues (CPU/memory/I/O contention), not fixable by changing test or app
914
+
828
915
  ### 11. Report results
829
916
 
830
917
  Read report at `openspec/reports/playwright-e2e-<name>-<timestamp>.md`. Present:
831
918
 
832
- - Summary table (tests, passed, failed, duration, status)
833
- - Auto-heal notes
919
+ - Summary table with failure type breakdown (App Bugs, Test Bugs/healed, Flaky-RAFT, Human Escalations)
920
+ - Failure Classification table (test, type, action, healed?)
921
+ - Auto-heal log (assertion vs actual comparison, fix applied, result)
922
+ - RAFT Summary (if any detected)
923
+ - Human Escalations (if any, with user decision)
834
924
  - Recommendations with `file:line` references
835
925
 
836
926
  Report template: `.claude/skills/openspec-e2e/templates/report.md`
@@ -851,9 +941,10 @@ Reference: `.claude/skills/openspec-e2e/templates/report.md`
851
941
  | JS errors or HTTP 5xx during exploration | **STOP** |
852
942
  | Sitemap fails ("all" mode) | Continue with homepage links fallback |
853
943
  | File already exists (app-exploration, test-plan, app-all.spec.ts, Page Objects) | Read and use — never regenerate |
854
- | Test fails (backend) | `test.skip()` + report |
855
- | Test fails (selector/assertion) | Healer: snapshot fixre-run (≤3) |
856
- | 3 heals failed | `test.skip()` if app bug; report if unclear |
944
+ | Test fails (network/backend) | **App Bug** — `test.skip()` + report |
945
+ | Test fails (selector/assertion) | **Test Bug/Ambiguous** Healer Phase 12 (≤3 attempts) |
946
+ | RAFT detected (suite fail, isolated pass) | **Flaky** — `test.skip()` in suite, note RAFT in report |
947
+ | Phase 3 escalation | **Human needed** — STOP + ask user |
857
948
  | False pass detected | Add "⚠️ Coverage Gap" to report |
858
949
 
859
950
  ## Guardrails
package/README.md CHANGED
@@ -20,23 +20,14 @@ openspec-pw init # Install Playwright E2E integration
20
20
 
21
21
  ## Supported AI Coding Assistants
22
22
 
23
- Auto-detects and installs commands for all 5 editors OpenSpec supports:
24
-
25
- | Editor | Path | Editor | Path |
26
- |--------|------|--------|------|
27
- | Claude Code | `.claude/` | Gemini CLI | `.gemini/` |
28
- | Cursor | `.cursor/` | GitHub Copilot | `.github/` |
29
- | Cline | `.clinerules/` | | |
30
-
31
- `openspec-pw init` auto-detects editors in your project and installs the right command files. Claude Code gets the full experience (skill + command + Playwright MCP). Other editors get command/workflow files with the complete E2E workflow.
23
+ Claude Code E2E workflow is driven by SKILL.md using Playwright MCP tools (`/opsx:e2e <change-name>`).
32
24
 
33
25
  ## Usage
34
26
 
35
- ### In Your AI Coding Assistant
27
+ ### In Claude Code
36
28
 
37
29
  ```bash
38
- /opsx:e2e my-feature # Claude Code
39
- /opsx-e2e my-feature # Cursor, Cline, Gemini CLI, GitHub Copilot
30
+ /opsx:e2e <change-name>
40
31
  ```
41
32
 
42
33
  ### CLI Commands
@@ -81,30 +72,42 @@ openspec-pw uninstall # Remove integration from the project
81
72
 
82
73
  └── 11. Report → openspec/reports/playwright-e2e-<name>.md
83
74
 
84
- ### Two Verification Layers
85
-
86
- | Layer | Command | What it checks |
87
- |-------|---------|----------------|
88
- | Static | `/opsx:verify` | Implementation matches artifacts |
89
- | E2E | `/opsx:e2e` | App works when running |
90
-
91
75
  ## Prerequisites
92
76
 
93
77
  1. **Node.js >= 20**
94
78
  2. **OpenSpec** initialized: `npm install -g @fission-ai/openspec && openspec init`
95
- 3. **One of 5 editors**: Claude Code, Cursor, Cline, Gemini CLI, GitHub Copilot (auto-detected)
96
- 4. **Claude Code only**: Playwright MCP — `claude mcp add playwright npx @playwright/mcp@latest`
79
+ 3. **Claude Code** with `.claude/` directory
80
+
81
+ After prerequisites, install Playwright MCP:
82
+ ```bash
83
+ claude mcp add playwright npx @playwright/mcp@latest
84
+ ```
97
85
 
98
86
  ## What `openspec-pw init` Does
99
87
 
100
- 1. Detects installed AI coding assistants (all 5 supported editors)
101
- 2. Installs E2E command/workflow files for each detected editor
102
- 3. Installs `/openspec-e2e` skill for Claude Code
103
- 4. Installs Playwright MCP globally for Claude Code (via `claude mcp add`)
104
- 5. Generates `tests/playwright/seed.spec.ts`, `auth.setup.ts`, `credentials.yaml`, `app-knowledge.md`
88
+ 1. Detects Claude Code in the project
89
+ 2. Installs E2E command (`/opsx:e2e`) and SKILL.md
90
+ 3. Syncs Healer tools from latest `@playwright/mcp`
91
+ 4. Generates `tests/playwright/seed.spec.ts`, `auth.setup.ts`, `credentials.yaml`, `app-knowledge.md`
105
92
 
106
93
  > **Note**: After running `openspec-pw init`, manually install Playwright browsers: `npx playwright install --with-deps`
107
94
 
95
+ ## First-Time Setup Checklist
96
+
97
+ Run through these steps in order when using the E2E workflow for the first time:
98
+
99
+ | Step | Command | If it fails |
100
+ |------|---------|-------------|
101
+ | 1. Install CLI | `npm install -g openspec-playwright` | Check Node.js version `node -v` (needs >= 20) |
102
+ | 2. Install OpenSpec | `npm install -g @fission-ai/openspec && openspec init` | `npm cache clean -f && npm install -g @fission-ai/openspec` |
103
+ | 3. Initialize E2E | `openspec-pw init` | Run `openspec-pw doctor` to see what's missing |
104
+ | 4. Install Playwright MCP | `claude mcp add playwright npx @playwright/mcp@latest` | `claude mcp list` to confirm installation |
105
+ | 5. Install browsers | `npx playwright install --with-deps` | macOS may need `xcode-select --install` first |
106
+ | 6. Start dev server | `npm run dev` (in a separate terminal) | Confirm port, set `BASE_URL` if non-standard |
107
+ | 7. Validate env | `npx playwright test tests/playwright/seed.spec.ts` | Check `webServer` in `playwright.config.ts` |
108
+ | 8. Configure auth (if needed) | See "Authentication" below | Debug with `npx playwright test --project=setup` |
109
+ | 9. Run first E2E | `/opsx:e2e <change-name>` | Check `openspec/reports/` for the report |
110
+
108
111
  ## Authentication
109
112
 
110
113
  If your app requires login, set up credentials once, then all tests run authenticated automatically.
@@ -142,10 +145,6 @@ Edit `tests/playwright/credentials.yaml`:
142
145
  - Configure test user credentials
143
146
  - Add multiple users for role-based tests
144
147
 
145
- ### MCP server (Claude Code only)
146
-
147
- Playwright MCP is installed globally via `claude mcp add` and enables the Healer Agent (auto-heals test failures via UI inspection). Restart Claude Code after setup to activate.
148
-
149
148
  ## Architecture
150
149
 
151
150
  ```
@@ -156,15 +155,12 @@ CLI (openspec-pw)
156
155
  ├── init → Installs commands, skill & templates to .claude/
157
156
  ├── update → Syncs commands, skill & templates from npm
158
157
  ├── run → Executes E2E tests with server lifecycle
159
- ├── verify → Checks implementation against artifacts
160
158
  └── doctor → Checks prerequisites
161
159
 
162
- Skill/Commands (per editor)
163
- ├── Claude Code → /opsx:e2e (skill) + /opsx:e2e (command) + MCP
164
- ├── Cursor → /opsx-e2e (command)
165
- ├── Cline /opsx-e2e (workflow)
166
- ├── Gemini CLI → /opsx-e2e (command)
167
- └── GitHub Copilot → /opsx-e2e (command)
160
+ Claude Code (/opsx:e2e)
161
+ ├── .claude/commands/opsx/e2e.md → Command file
162
+ ├── .claude/skills/openspec-e2e/ → SKILL.md + templates
163
+ └── @playwright/mcp Healer Agent tools
168
164
 
169
165
  Test Assets (tests/playwright/)
170
166
  ├── seed.spec.ts → Env validation
@@ -176,7 +172,7 @@ Exploration (openspec/changes/<name>/specs/playwright/)
176
172
  ├── app-exploration.md → This change's routes + verified selectors
177
173
  └── test-plan.md → This change's test cases
178
174
 
179
- Healer Agent (Claude Code + MCP only)
175
+ Healer Agent (@playwright/mcp)
180
176
  └── browser_snapshot, browser_navigate, browser_run_code, etc.
181
177
  ```
182
178
 
package/README.zh-CN.md CHANGED
@@ -10,6 +10,17 @@
10
10
  npm install -g openspec-playwright
11
11
  ```
12
12
 
13
+ ## 前置条件
14
+
15
+ 1. **Node.js >= 20**
16
+ 2. **OpenSpec** 已初始化: `npm install -g @fission-ai/openspec && openspec init`
17
+ 3. **Claude Code** 且项目中有 `.claude/` 目录
18
+
19
+ 安装 Playwright MCP:
20
+ ```bash
21
+ claude mcp add playwright npx @playwright/mcp@latest
22
+ ```
23
+
13
24
  ## 初始化
14
25
 
15
26
  ```bash
@@ -18,25 +29,18 @@ openspec init # 初始化 OpenSpec
18
29
  openspec-pw init # 安装 Playwright E2E 集成
19
30
  ```
20
31
 
21
- ## 支持的 AI 编码助手
22
-
23
- 自动检测并安装 OpenSpec 支持的全部 5 个编辑器的命令文件:
32
+ > **注意**:运行 `openspec-pw init` 后,手动安装 Playwright 浏览器:`npx playwright install --with-deps`
24
33
 
25
- | 编辑器 | 路径 | 编辑器 | 路径 |
26
- |--------|------|--------|------|
27
- | Claude Code | `.claude/` | Gemini CLI | `.gemini/` |
28
- | Cursor | `.cursor/` | GitHub Copilot | `.github/` |
29
- | Cline | `.clinerules/` | | |
34
+ ## 支持的 AI 编码助手
30
35
 
31
- `openspec-pw init` 会检测项目中安装了哪些编辑器并安装对应文件。Claude Code 获得完整体验(skill + 命令 + Playwright MCP)。其他编辑器获得包含完整 E2E 工作流的命令/工作流文件。
36
+ Claude Code E2E 工作流由 SKILL.md 驱动,使用 Playwright MCP 工具(`/opsx:e2e <change-name>`)。
32
37
 
33
38
  ## 使用
34
39
 
35
- ### 在 AI 编码助手中
40
+ ### 在 Claude Code 中
36
41
 
37
42
  ```bash
38
- /opsx:e2e my-feature # Claude Code
39
- /opsx-e2e my-feature # Cursor, Cline, Gemini CLI, GitHub Copilot
43
+ /opsx:e2e <change-name>
40
44
  ```
41
45
 
42
46
  ### CLI 命令
@@ -82,31 +86,30 @@ openspec-pw uninstall # 移除项目中的集成
82
86
  └── 11. 报告 → openspec/reports/playwright-e2e-<name>.md
83
87
  ```
84
88
 
85
- ### 两层验证
89
+ ## `openspec-pw init` 做了什么
86
90
 
87
- | 层级 | 命令 | 验证内容 |
88
- |------|------|---------|
89
- | 静态验证 | `/opsx:verify` | 实现是否符合 artifacts |
90
- | E2E 验证 | `/opsx:e2e` | 应用运行是否正常 |
91
+ 1. 检测项目中的 Claude Code
92
+ 2. 安装 E2E 命令(`/opsx:e2e`)和 SKILL.md
93
+ 3. 从最新 `@playwright/mcp` 同步 Healer 工具
94
+ 4. 生成 `tests/playwright/seed.spec.ts`、`auth.setup.ts`、`credentials.yaml`、`app-knowledge.md`
91
95
 
92
- ## 前置条件
96
+ ## 首次配置清单
93
97
 
94
- 1. **Node.js >= 20**
95
- 2. **OpenSpec** 已初始化: `npm install -g @fission-ai/openspec && openspec init`
96
- 3. **任一 5 编辑器**: Claude Code、Cursor、Cline、Gemini CLI、GitHub Copilot(自动检测)
97
- 4. **仅 Claude Code**: Playwright MCP — `claude mcp add playwright npx @playwright/mcp@latest`
98
+ 首次使用 E2E 工作流,按顺序执行以下步骤:
98
99
 
99
- ## `openspec-pw init` 做了什么
100
+ | 步骤 | 命令 | 失败时快速修复 |
101
+ |------|------|----------------|
102
+ | 1. 安装 CLI | `npm install -g openspec-playwright` | 检查 Node.js 版本 `node -v`(需 >= 20) |
103
+ | 2. 安装 OpenSpec | `npm install -g @fission-ai/openspec && openspec init` | `npm cache clean -f && npm install -g @fission-ai/openspec` |
104
+ | 3. 初始化 E2E | `openspec-pw init` | 运行 `openspec-pw doctor` 查看具体缺失项 |
105
+ | 4. 安装 Playwright MCP | `claude mcp add playwright npx @playwright/mcp@latest` | `claude mcp list` 确认安装成功 |
106
+ | 5. 安装浏览器 | `npx playwright install --with-deps` | macOS 可能需先运行 `xcode-select --install` |
107
+ | 6. 启动开发服务器 | `npm run dev`(在另一个终端) | 确认端口,配置 `BASE_URL` |
108
+ | 7. 验证环境 | `npx playwright test tests/playwright/seed.spec.ts` | 检查 `playwright.config.ts` 中的 `webServer` 配置 |
109
+ | 8. 配置认证(如需要) | 见下方"认证配置" | `npx playwright test --project=setup` 调试 |
110
+ | 9. 运行第一个 E2E | `/opsx:e2e <change-name>` | 查看 `openspec/reports/` 中的报告 |
100
111
 
101
- 1. 检测已安装的 AI 编码助手(支持全部 5 个编辑器)
102
- 2. 为每个检测到的编辑器安装 E2E 命令/工作流文件
103
- 3. 为 Claude Code 安装 `/openspec-e2e` skill
104
- 4. 为 Claude Code 全局安装 Playwright MCP(通过 `claude mcp add`)
105
- 5. 生成 `tests/playwright/seed.spec.ts`、`auth.setup.ts`、`credentials.yaml`、`app-knowledge.md`
106
-
107
- > **注意**:运行 `openspec-pw init` 后,手动安装 Playwright 浏览器:`npx playwright install --with-deps`
108
-
109
- ## 认证
112
+ ## 认证配置
110
113
 
111
114
  如果你的应用需要登录,配置一次凭证后,所有测试自动以已登录状态运行。
112
115
 
@@ -143,10 +146,6 @@ npx playwright test --project=setup
143
146
  - 配置测试用户凭证
144
147
  - 为角色测试添加多用户
145
148
 
146
- ### MCP 服务器(仅 Claude Code)
147
-
148
- Playwright MCP 通过 `claude mcp add` 全局安装,启用 Healer Agent(通过 UI 检查自动修复测试失败)。设置后需重启 Claude Code 生效。
149
-
150
149
  ## 架构
151
150
 
152
151
  ```
@@ -157,15 +156,12 @@ CLI (openspec-pw)
157
156
  ├── init → 安装命令、skill 和模板到 .claude/
158
157
  ├── update → 从 npm 同步命令、skill 和模板
159
158
  ├── run → 执行 E2E 测试并管理服务器生命周期
160
- ├── verify → 检查实现是否符合 artifacts
161
159
  └── doctor → 检查前置条件
162
160
 
163
- Skill/命令(按编辑器)
164
- ├── Claude Code → /opsx:e2e (skill) + /opsx:e2e (command) + MCP
165
- ├── Cursor → /opsx-e2e (command)
166
- ├── Cline /opsx-e2e (workflow)
167
- ├── Gemini CLI → /opsx-e2e (command)
168
- └── GitHub Copilot → /opsx-e2e (command)
161
+ Claude Code (/opsx:e2e)
162
+ ├── .claude/commands/opsx/e2e.md → 命令文件
163
+ ├── .claude/skills/openspec-e2e/ → SKILL.md + 模板
164
+ └── @playwright/mcp Healer Agent 工具
169
165
 
170
166
  测试资产 (tests/playwright/)
171
167
  ├── seed.spec.ts → 环境验证
@@ -177,7 +173,7 @@ Skill/命令(按编辑器)
177
173
  ├── app-exploration.md → 本次 change 的路由 + 已验证选择器
178
174
  └── test-plan.md → 本次 change 的测试用例
179
175
 
180
- Healer Agent(仅 Claude Code + MCP)
176
+ Healer Agent (@playwright/mcp)
181
177
  └── browser_snapshot, browser_navigate, browser_run_code 等
182
178
  ```
183
179
 
@@ -1,6 +1,5 @@
1
- import { existsSync, readFileSync } from "fs";
1
+ import { existsSync } from "fs";
2
2
  import { join } from "path";
3
- import { homedir } from "os";
4
3
  import { execSync } from "child_process";
5
4
  import chalk from "chalk";
6
5
  export async function doctor(options = {}) {
@@ -70,23 +69,20 @@ export async function doctor(options = {}) {
70
69
  message: "not installed",
71
70
  });
72
71
  }
73
- // Playwright MCP
74
- const homeDir = homedir();
75
- const claudeJsonPath = join(homeDir, ".claude.json");
72
+ // Playwright MCP — use `claude mcp list` as source of truth (platform-independent)
76
73
  let mcpInstalled = false;
77
- if (existsSync(claudeJsonPath)) {
78
- try {
79
- const claudeJson = JSON.parse(readFileSync(claudeJsonPath, "utf-8"));
80
- const globalMcp = claudeJson?.mcpServers ?? {};
81
- const localMcp = claudeJson?.projects?.[projectRoot]?.mcpServers ?? {};
82
- if (globalMcp["playwright"] || localMcp["playwright"]) {
83
- mcpInstalled = true;
84
- }
85
- }
86
- catch {
87
- // .claude.json missing or malformed — MCP not configured
74
+ try {
75
+ const output = execSync("claude mcp list", {
76
+ encoding: "utf-8",
77
+ timeout: 10000,
78
+ });
79
+ if (output.includes("playwright")) {
80
+ mcpInstalled = true;
88
81
  }
89
82
  }
83
+ catch {
84
+ // claude CLI not available or failed — MCP not configured
85
+ }
90
86
  checks.push({
91
87
  category: "Playwright MCP",
92
88
  name: "playwright-mcp",
@@ -1 +1 @@
1
- {"version":3,"file":"doctor.js","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"doctor.js","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"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"}
@@ -11,26 +11,18 @@ export interface CommandMeta {
11
11
  tags: string[];
12
12
  body: string;
13
13
  }
14
- /** Editor adapter Strategy Pattern */
15
- export interface EditorAdapter {
16
- toolId: string;
17
- hasSkill: boolean;
18
- getCommandPath(commandId: string): string;
19
- formatCommand(meta: CommandMeta): string;
20
- }
21
- /** Claude Code: .claude/commands/opsx/<id>.md + SKILL.md */
22
- declare const claudeAdapter: EditorAdapter;
23
- declare const ALL_ADAPTERS: EditorAdapter[];
24
- /** Detect which editors are installed by checking their config directories */
25
- export declare function detectEditors(projectRoot: string): EditorAdapter[];
26
- /** Build the shared command metadata */
14
+ /** Claude Code command file: .claude/commands/opsx/<id>.md */
15
+ export declare function formatClaudeCommand(meta: CommandMeta): string;
16
+ export declare function getClaudeCommandPath(id: string): string;
17
+ /** Build the command metadata for Claude Code */
27
18
  export declare function buildCommandMeta(body: string): CommandMeta;
28
- /** Install command files for all detected editors */
29
- export declare function installForAllEditors(body: string, adapters: EditorAdapter[], projectRoot: string): void;
30
- /** Install SKILL.md only for Claude Code */
19
+ /** Detect if Claude Code is installed */
20
+ export declare function hasClaudeCode(projectRoot: string): boolean;
21
+ /** Install command files and SKILL.md for Claude Code */
22
+ export declare function installForClaudeCode(body: string, projectRoot: string): void;
23
+ /** Install SKILL.md for Claude Code */
31
24
  export declare function installSkill(projectRoot: string, skillContent: string): void;
32
25
  /** Install project-level CLAUDE.md with employee-grade standards + OpenSpec context */
33
26
  export declare function installProjectClaudeMd(projectRoot: string, standardsContent: string): void;
34
27
  /** Read the employee-grade standards from a source file */
35
28
  export declare function readEmployeeStandards(srcPath: string): string;
36
- export { claudeAdapter, ALL_ADAPTERS };