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.
- package/.claude/skills/openspec-e2e/SKILL.md +120 -29
- package/README.md +34 -38
- package/README.zh-CN.md +40 -44
- package/dist/commands/doctor.js +12 -16
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/editors.d.ts +9 -17
- package/dist/commands/editors.js +20 -115
- package/dist/commands/editors.js.map +1 -1
- package/dist/commands/init.js +24 -20
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/run.d.ts +6 -0
- package/dist/commands/run.js +19 -12
- package/dist/commands/run.js.map +1 -1
- package/dist/commands/uninstall.js +26 -45
- package/dist/commands/uninstall.js.map +1 -1
- package/dist/commands/update.js +21 -17
- package/dist/commands/update.js.map +1 -1
- package/employee-standards.md +2 -0
- package/package.json +1 -1
- package/templates/report.md +37 -8
|
@@ -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
|
-
**
|
|
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
|
|
56
|
-
→ Yes → page.getByRole/ByLabel/ByText +
|
|
57
|
-
→ No →
|
|
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
|
-
-
|
|
196
|
-
-
|
|
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
|
|
814
|
+
**Healer — Phase 1: Triage**
|
|
802
815
|
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
|
806
|
-
|
|
|
807
|
-
| **
|
|
808
|
-
| **
|
|
809
|
-
| **
|
|
810
|
-
| **
|
|
811
|
-
| **
|
|
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
|
-
**
|
|
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
|
-
|
|
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
|
-
|
|
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 (
|
|
833
|
-
-
|
|
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) |
|
|
856
|
-
|
|
|
944
|
+
| Test fails (network/backend) | **App Bug** — `test.skip()` + report |
|
|
945
|
+
| Test fails (selector/assertion) | **Test Bug/Ambiguous** — Healer Phase 1→2 (≤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
|
-
|
|
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
|
|
27
|
+
### In Claude Code
|
|
36
28
|
|
|
37
29
|
```bash
|
|
38
|
-
/opsx:e2e
|
|
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. **
|
|
96
|
-
|
|
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
|
|
101
|
-
2. Installs E2E command
|
|
102
|
-
3.
|
|
103
|
-
4.
|
|
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
|
-
|
|
163
|
-
├──
|
|
164
|
-
├──
|
|
165
|
-
|
|
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 (
|
|
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
|
-
|
|
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
|
-
|
|
36
|
+
Claude Code — E2E 工作流由 SKILL.md 驱动,使用 Playwright MCP 工具(`/opsx:e2e <change-name>`)。
|
|
32
37
|
|
|
33
38
|
## 使用
|
|
34
39
|
|
|
35
|
-
### 在
|
|
40
|
+
### 在 Claude Code 中
|
|
36
41
|
|
|
37
42
|
```bash
|
|
38
|
-
/opsx:e2e
|
|
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
|
-
|
|
90
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
164
|
-
├──
|
|
165
|
-
├──
|
|
166
|
-
|
|
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
|
|
176
|
+
Healer Agent (@playwright/mcp)
|
|
181
177
|
└── browser_snapshot, browser_navigate, browser_run_code 等
|
|
182
178
|
```
|
|
183
179
|
|
package/dist/commands/doctor.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import { existsSync
|
|
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
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
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":"AAAA,OAAO,EAAE,UAAU,EAAE,
|
|
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
|
-
/**
|
|
15
|
-
export
|
|
16
|
-
|
|
17
|
-
|
|
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
|
-
/**
|
|
29
|
-
export declare function
|
|
30
|
-
/** Install SKILL.md
|
|
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 };
|