openspec-playwright 0.1.62 → 0.1.64
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/commands/CLAUDE.md +12 -0
- package/.claude/commands/opsx/CLAUDE.md +12 -0
- package/.claude/commands/opsx/e2e-body.md +5 -0
- package/.claude/commands/opsx/e2e.md +5 -1
- package/.claude/skills/CLAUDE.md +12 -0
- package/.claude/skills/openspec-e2e/CLAUDE.md +14 -0
- package/.claude/skills/openspec-e2e/SKILL.md +101 -77
- package/LICENSE +21 -0
- package/README.md +4 -3
- package/README.zh-CN.md +4 -3
- package/bin/CLAUDE.md +11 -0
- package/dist/CLAUDE.md +17 -0
- package/dist/commands/doctor.d.ts +4 -1
- package/dist/commands/doctor.js +110 -73
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/editors.js +149 -95
- package/dist/commands/editors.js.map +1 -1
- package/dist/commands/init.js +105 -97
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/mcpSync.js +46 -31
- package/dist/commands/mcpSync.js.map +1 -1
- package/dist/commands/run.d.ts +13 -0
- package/dist/commands/run.js +74 -51
- package/dist/commands/run.js.map +1 -1
- package/dist/commands/uninstall.d.ts +1 -0
- package/dist/commands/uninstall.js +133 -0
- package/dist/commands/uninstall.js.map +1 -0
- package/dist/commands/update.js +79 -68
- package/dist/commands/update.js.map +1 -1
- package/dist/index.js +33 -26
- package/dist/index.js.map +1 -1
- package/employee-standards.md +3 -3
- package/package.json +21 -1
- package/schemas/playwright-e2e/templates/playwright.config.ts +22 -22
- package/templates/CLAUDE.md +15 -0
- package/templates/seed.spec.ts +5 -3
- package/.github/workflows/release.yml +0 -81
- package/docs/plans/2026-03-26-openspec-playwright-design.md +0 -180
- package/openspec/schemas/playwright-e2e/schema.yaml +0 -56
- package/openspec/schemas/playwright-e2e/templates/e2e-test.ts +0 -55
- package/openspec/schemas/playwright-e2e/templates/playwright.config.ts +0 -52
- package/openspec/schemas/playwright-e2e/templates/report.md +0 -27
- package/openspec/schemas/playwright-e2e/templates/test-plan.md +0 -24
- package/openspec-playwright-0.1.62.tgz +0 -0
- package/release-notes.md +0 -5
- package/src/commands/doctor.ts +0 -115
- package/src/commands/editors.ts +0 -606
- package/src/commands/init.ts +0 -252
- package/src/commands/mcpSync.ts +0 -160
- package/src/commands/run.ts +0 -172
- package/src/commands/update.ts +0 -192
- package/src/index.ts +0 -47
- package/tests/editors.test.ts +0 -180
- package/tsconfig.json +0 -18
- package/vitest.config.ts +0 -9
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<claude-mem-context>
|
|
2
|
+
# Recent Activity
|
|
3
|
+
|
|
4
|
+
<!-- This section is auto-generated by claude-mem. Edit content outside the tags. -->
|
|
5
|
+
|
|
6
|
+
### Mar 27, 2026
|
|
7
|
+
|
|
8
|
+
| ID | Time | T | Title | Read |
|
|
9
|
+
| ----- | -------- | --- | ---------------------------------------------------------- | ---- |
|
|
10
|
+
| #5419 | 10:01 AM | 🟣 | Added Claude Code command opsx-e2e.md for E2E verification | ~213 |
|
|
11
|
+
|
|
12
|
+
</claude-mem-context>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<claude-mem-context>
|
|
2
|
+
# Recent Activity
|
|
3
|
+
|
|
4
|
+
<!-- This section is auto-generated by claude-mem. Edit content outside the tags. -->
|
|
5
|
+
|
|
6
|
+
### Mar 27, 2026
|
|
7
|
+
|
|
8
|
+
| ID | Time | T | Title | Read |
|
|
9
|
+
| ----- | -------- | --- | ----------------------------------------------------- | ---- |
|
|
10
|
+
| #5434 | 10:15 AM | 🔄 | Organized OpenSpec command files under opsx namespace | ~145 |
|
|
11
|
+
|
|
12
|
+
</claude-mem-context>
|
|
@@ -3,9 +3,11 @@ Run Playwright E2E verification for an OpenSpec change.
|
|
|
3
3
|
## Workflow
|
|
4
4
|
|
|
5
5
|
1. **Validate environment**: Run the seed test to confirm your app is reachable.
|
|
6
|
+
|
|
6
7
|
```bash
|
|
7
8
|
npx playwright test tests/playwright/seed.spec.ts --project=chromium
|
|
8
9
|
```
|
|
10
|
+
|
|
9
11
|
If it fails, fix your BASE_URL or start the dev server first.
|
|
10
12
|
|
|
11
13
|
2. **Select the change**: If no change name is provided, run `openspec list --json` and pick one. Then announce: "Using change: `<name>`".
|
|
@@ -23,10 +25,13 @@ Run Playwright E2E verification for an OpenSpec change.
|
|
|
23
25
|
- Use `browser.newContext()` for auth guard tests (fresh session, no cookies)
|
|
24
26
|
|
|
25
27
|
7. **Run tests**:
|
|
28
|
+
|
|
26
29
|
```bash
|
|
27
30
|
openspec-pw run <change-name>
|
|
28
31
|
```
|
|
32
|
+
|
|
29
33
|
Or with role filtering:
|
|
34
|
+
|
|
30
35
|
```bash
|
|
31
36
|
npx playwright test tests/playwright/<name>.spec.ts --grep "@<role>"
|
|
32
37
|
```
|
|
@@ -10,9 +10,11 @@ Run Playwright E2E verification for an OpenSpec change.
|
|
|
10
10
|
## Workflow
|
|
11
11
|
|
|
12
12
|
1. **Validate environment**: Run the seed test to confirm your app is reachable.
|
|
13
|
+
|
|
13
14
|
```bash
|
|
14
15
|
npx playwright test tests/playwright/seed.spec.ts --project=chromium
|
|
15
16
|
```
|
|
17
|
+
|
|
16
18
|
If it fails, fix your BASE_URL or start the dev server first.
|
|
17
19
|
|
|
18
20
|
2. **Select the change**: If no change name is provided, run `openspec list --json` and pick one. Then announce: "Using change: `<name>`".
|
|
@@ -30,10 +32,13 @@ Run Playwright E2E verification for an OpenSpec change.
|
|
|
30
32
|
- Use `browser.newContext()` for auth guard tests (fresh session, no cookies)
|
|
31
33
|
|
|
32
34
|
7. **Run tests**:
|
|
35
|
+
|
|
33
36
|
```bash
|
|
34
37
|
openspec-pw run <change-name>
|
|
35
38
|
```
|
|
39
|
+
|
|
36
40
|
Or with role filtering:
|
|
41
|
+
|
|
37
42
|
```bash
|
|
38
43
|
npx playwright test tests/playwright/<name>.spec.ts --grep "@<role>"
|
|
39
44
|
```
|
|
@@ -44,4 +49,3 @@ Run Playwright E2E verification for an OpenSpec change.
|
|
|
44
49
|
- Auto-heal up to 3 attempts
|
|
45
50
|
|
|
46
51
|
9. **Report**: Results are saved to `openspec/reports/playwright-e2e-<name>-<timestamp>.md`. Present the summary table to the user.
|
|
47
|
-
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<claude-mem-context>
|
|
2
|
+
# Recent Activity
|
|
3
|
+
|
|
4
|
+
<!-- This section is auto-generated by claude-mem. Edit content outside the tags. -->
|
|
5
|
+
|
|
6
|
+
### Mar 27, 2026
|
|
7
|
+
|
|
8
|
+
| ID | Time | T | Title | Read |
|
|
9
|
+
| ----- | ------- | --- | -------------------------------------------------- | ---- |
|
|
10
|
+
| #5372 | 9:44 AM | ✅ | Created skill directory structure for openspec-e2e | ~124 |
|
|
11
|
+
|
|
12
|
+
</claude-mem-context>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<claude-mem-context>
|
|
2
|
+
# Recent Activity
|
|
3
|
+
|
|
4
|
+
<!-- This section is auto-generated by claude-mem. Edit content outside the tags. -->
|
|
5
|
+
|
|
6
|
+
### Mar 27, 2026
|
|
7
|
+
|
|
8
|
+
| ID | Time | T | Title | Read |
|
|
9
|
+
| ----- | -------- | --- | ----------------------------------------------------------------------- | ---- |
|
|
10
|
+
| #5511 | 11:22 AM | ✅ | Extended guardrails to protect auth files from overwriting | ~181 |
|
|
11
|
+
| #5510 | 11:21 AM | 🟣 | Enhanced SKILL.md with Playwright auth project configuration | ~228 |
|
|
12
|
+
| #5427 | 10:05 AM | ✅ | Committed style fixes aligning SKILL.md with OpenSpec template standard | ~281 |
|
|
13
|
+
|
|
14
|
+
</claude-mem-context>
|
|
@@ -25,20 +25,20 @@ metadata:
|
|
|
25
25
|
|
|
26
26
|
Two modes, same pipeline:
|
|
27
27
|
|
|
28
|
-
| Mode
|
|
29
|
-
|
|
30
|
-
| Change | `/opsx:e2e <name>` | OpenSpec specs
|
|
31
|
-
| All
|
|
28
|
+
| Mode | Command | Route source | Output |
|
|
29
|
+
| ------ | ------------------ | ------------------------ | ----------------- |
|
|
30
|
+
| Change | `/opsx:e2e <name>` | OpenSpec specs | `<name>.spec.ts` |
|
|
31
|
+
| All | `/opsx:e2e all` | sitemap + homepage crawl | `app-all.spec.ts` |
|
|
32
32
|
|
|
33
33
|
Both modes update `app-knowledge.md` and `app-exploration.md`. All `.spec.ts` files run together as regression suite.
|
|
34
34
|
|
|
35
35
|
**Role mapping** (Playwright Test Agents terminology):
|
|
36
36
|
|
|
37
|
-
| Role
|
|
38
|
-
|
|
39
|
-
| Planner
|
|
40
|
-
| Generator | Step 6
|
|
41
|
-
| Healer
|
|
37
|
+
| Role | This SKILL | What it does |
|
|
38
|
+
| --------- | ---------- | ------------------------------------------------------------ |
|
|
39
|
+
| Planner | Step 4–5 | Explores app via Playwright MCP → produces test-plan.md |
|
|
40
|
+
| Generator | Step 6 | Transforms test-plan.md → `.spec.ts` with verified selectors |
|
|
41
|
+
| Healer | Step 9 | Executes tests, repairs failures via Playwright MCP |
|
|
42
42
|
|
|
43
43
|
## Testing principles
|
|
44
44
|
|
|
@@ -49,11 +49,13 @@ Both modes update `app-knowledge.md` and `app-exploration.md`. All `.spec.ts` fi
|
|
|
49
49
|
```
|
|
50
50
|
|
|
51
51
|
**API only as fallback** — Use `page.request` only when UI genuinely cannot cover the scenario:
|
|
52
|
+
|
|
52
53
|
- Triggering HTTP 5xx/4xx error responses (hard to reach via UI)
|
|
53
54
|
- Edge cases requiring pre-condition data that UI cannot set up
|
|
54
55
|
- Cases where Step 4 exploration confirmed no UI element exists
|
|
55
56
|
|
|
56
57
|
**Decision rule**:
|
|
58
|
+
|
|
57
59
|
```
|
|
58
60
|
Can this be tested through the UI?
|
|
59
61
|
→ Yes → page.getByRole/ByLabel/ByText + click/fill/type + assert UI
|
|
@@ -67,12 +69,14 @@ Can this be tested through the UI?
|
|
|
67
69
|
### 1. Select the change or mode
|
|
68
70
|
|
|
69
71
|
**Change mode** (`/opsx:e2e <name>`):
|
|
72
|
+
|
|
70
73
|
- Use provided name, or infer from context, or auto-select if only one exists
|
|
71
74
|
- If ambiguous → `openspec list --json` + AskUserQuestion
|
|
72
75
|
- Verify specs exist: `openspec status --change "<name>" --json`
|
|
73
76
|
- If specs empty → **STOP: E2E requires specs.** Use "all" mode instead.
|
|
74
77
|
|
|
75
78
|
**"all" mode** (`/opsx:e2e all` — no OpenSpec needed):
|
|
79
|
+
|
|
76
80
|
- Announce: "Mode: full app exploration"
|
|
77
81
|
- Discover routes via:
|
|
78
82
|
1. Navigate to `${BASE_URL}/sitemap.xml` (if exists)
|
|
@@ -95,6 +99,7 @@ Can this be tested through the UI?
|
|
|
95
99
|
**Exclude false positives**: HTTP header examples (`Authorization: Bearer ...`) and code snippets do not count.
|
|
96
100
|
|
|
97
101
|
**Confidence**:
|
|
102
|
+
|
|
98
103
|
- High (auto-proceed): Multiple markers AND context indicators
|
|
99
104
|
- Medium (proceed with note): Single marker, context unclear
|
|
100
105
|
- Low (skip auth): No markers found
|
|
@@ -102,6 +107,7 @@ Can this be tested through the UI?
|
|
|
102
107
|
### 3. Validate environment
|
|
103
108
|
|
|
104
109
|
Run the seed test before generating tests:
|
|
110
|
+
|
|
105
111
|
```bash
|
|
106
112
|
npx playwright test tests/playwright/seed.spec.ts --project=chromium
|
|
107
113
|
```
|
|
@@ -132,20 +138,22 @@ browser_navigate → browser_console_messages → browser_snapshot → browser_t
|
|
|
132
138
|
|
|
133
139
|
**After navigating, check for app-level errors**:
|
|
134
140
|
|
|
135
|
-
| Signal
|
|
136
|
-
|
|
137
|
-
| HTTP 5xx or unreachable
|
|
138
|
-
| JS error in console
|
|
139
|
-
| HTTP 404
|
|
140
|
-
| Auth required, no credentials | Missing auth setup
|
|
141
|
+
| Signal | Meaning | Action |
|
|
142
|
+
| ----------------------------- | --------------------------------- | --------------------------------------------------------------------------------------------- |
|
|
143
|
+
| HTTP 5xx or unreachable | Backend/server error | **STOP** — tell user: "App has a backend error (HTTP <code>). Fix it, then re-run /opsx:e2e." |
|
|
144
|
+
| JS error in console | App runtime error | **STOP** — tell user: "Page has JS errors. Fix them, then re-run /opsx:e2e." |
|
|
145
|
+
| HTTP 404 | Route not in app (metadata issue) | Continue — mark `⚠️ route not found` in app-exploration.md |
|
|
146
|
+
| Auth required, no credentials | Missing auth setup | Continue — skip protected routes, explore login page |
|
|
141
147
|
|
|
142
148
|
**For guest routes** (no auth):
|
|
149
|
+
|
|
143
150
|
```javascript
|
|
144
151
|
// Navigate directly
|
|
145
|
-
await browser_navigate(`${BASE_URL}/<route>`)
|
|
152
|
+
await browser_navigate(`${BASE_URL}/<route>`);
|
|
146
153
|
```
|
|
147
154
|
|
|
148
155
|
**For protected routes** (auth required):
|
|
156
|
+
|
|
149
157
|
```javascript
|
|
150
158
|
// Option A: use existing storageState (recommended)
|
|
151
159
|
// Option B: navigate to /login first, fill form, then navigate to target
|
|
@@ -153,11 +161,13 @@ await browser_navigate(`${BASE_URL}/<route>`)
|
|
|
153
161
|
```
|
|
154
162
|
|
|
155
163
|
**If credentials are not yet available**:
|
|
164
|
+
|
|
156
165
|
1. Skip protected routes — mark `⚠️ auth needed — explore after auth.setup.ts`
|
|
157
166
|
2. Explore the login page itself (guest route) — extract form selectors
|
|
158
167
|
3. After auth.setup.ts runs, re-run exploration for protected routes
|
|
159
168
|
|
|
160
169
|
Wait for page stability:
|
|
170
|
+
|
|
161
171
|
- Prefer `browser_wait_for` with text or selector
|
|
162
172
|
- Avoid `networkidle` / `load` — too slow or unreliable
|
|
163
173
|
- Ready signal: heading, spinner disappears, or URL change
|
|
@@ -166,14 +176,14 @@ Wait for page stability:
|
|
|
166
176
|
|
|
167
177
|
From `browser_snapshot` output, extract **interactive elements** for each route:
|
|
168
178
|
|
|
169
|
-
| Element type
|
|
170
|
-
|
|
171
|
-
| **Buttons**
|
|
172
|
-
| **Form fields**
|
|
173
|
-
| **Navigation links** | text, href, selector
|
|
174
|
-
| **Headings**
|
|
175
|
-
| **Error messages**
|
|
176
|
-
| **Dynamic content**
|
|
179
|
+
| Element type | What to capture | Selector priority |
|
|
180
|
+
| -------------------- | ------------------------------------ | ---------------------------------------------------------- |
|
|
181
|
+
| **Buttons** | text, selector | `[data-testid]` > `getByRole` > `getByLabel` > `getByText` |
|
|
182
|
+
| **Form fields** | name, type, label, selector | `[data-testid]` > `name` > `label` |
|
|
183
|
+
| **Navigation links** | text, href, selector | `text` > `href` |
|
|
184
|
+
| **Headings** | text content, selector | for assertions |
|
|
185
|
+
| **Error messages** | text patterns, selector | for error path testing |
|
|
186
|
+
| **Dynamic content** | structure — row counts, card layouts | for data-driven tests |
|
|
177
187
|
|
|
178
188
|
#### 4.4. Write app-exploration.md
|
|
179
189
|
|
|
@@ -182,6 +192,7 @@ Output: `openspec/changes/<name>/specs/playwright/app-exploration.md`
|
|
|
182
192
|
Use template: `openspec/schemas/playwright-e2e/templates/app-exploration.md`
|
|
183
193
|
|
|
184
194
|
Key fields per route:
|
|
195
|
+
|
|
185
196
|
- **URL**: `${BASE_URL}<path>`
|
|
186
197
|
- **Auth**: none / required (storageState: `<path>`)
|
|
187
198
|
- **Ready signal**: how to know the page is loaded
|
|
@@ -192,11 +203,11 @@ After exploration, add route-level notes (redirects, dynamic content → see 4.5
|
|
|
192
203
|
|
|
193
204
|
#### 4.5. Exploration behavior notes
|
|
194
205
|
|
|
195
|
-
| Situation
|
|
196
|
-
|
|
206
|
+
| Situation | Action |
|
|
207
|
+
| ------------------------------------------------- | ---------------------------------------------------------------- |
|
|
197
208
|
| SPA routing (URL changes but page doesn't reload) | Explore via navigation clicks from known routes, not direct URLs |
|
|
198
|
-
| Page loads but no interactive elements
|
|
199
|
-
| Dynamic content (user-specific)
|
|
209
|
+
| Page loads but no interactive elements | Wait longer for SPA hydration |
|
|
210
|
+
| Dynamic content (user-specific) | Record structure — use `toContainText`, not `toHaveText` |
|
|
200
211
|
|
|
201
212
|
**Idempotency**: If `app-exploration.md` already exists → read it, verify routes still match specs, update only new routes or changed pages.
|
|
202
213
|
|
|
@@ -204,20 +215,21 @@ After exploration, add route-level notes (redirects, dynamic content → see 4.5
|
|
|
204
215
|
|
|
205
216
|
After writing `app-exploration.md`, extract **project-level shared knowledge** and append to `tests/playwright/app-knowledge.md`:
|
|
206
217
|
|
|
207
|
-
| Section
|
|
208
|
-
|
|
209
|
-
| Architecture
|
|
210
|
-
| Credential Format
|
|
211
|
-
| Common Selector Patterns | New patterns discovered that apply across routes
|
|
212
|
-
| SPA Routing
|
|
213
|
-
| Project Conventions
|
|
214
|
-
| Selector Fixes
|
|
218
|
+
| Section | What to extract |
|
|
219
|
+
| ------------------------ | ------------------------------------------------------------------------- |
|
|
220
|
+
| Architecture | Monolith or separated? Backend port? Restart command? |
|
|
221
|
+
| Credential Format | Login endpoint, username format (email vs username) |
|
|
222
|
+
| Common Selector Patterns | New patterns discovered that apply across routes |
|
|
223
|
+
| SPA Routing | SPA framework, routing behavior |
|
|
224
|
+
| Project Conventions | BASE_URL, auth method, multi-user roles |
|
|
225
|
+
| Selector Fixes | Healed selectors (see Step 9) — route, old selector, new selector, reason |
|
|
215
226
|
|
|
216
227
|
Append only new/changed items — preserve existing content.
|
|
217
228
|
|
|
218
229
|
#### 4.7. After exploration
|
|
219
230
|
|
|
220
231
|
Pass `app-exploration.md` to:
|
|
232
|
+
|
|
221
233
|
- **Step 5 (Planner)**: reference real routes, auth states, and elements in test-plan.md
|
|
222
234
|
- **Step 6 (Generator)**: use verified selectors instead of inferring
|
|
223
235
|
|
|
@@ -242,15 +254,18 @@ Template: `openspec/schemas/playwright-e2e/templates/test-plan.md`
|
|
|
242
254
|
### 6. Generate test file
|
|
243
255
|
|
|
244
256
|
**"all" mode** → `tests/playwright/app-all.spec.ts` (smoke regression):
|
|
257
|
+
|
|
245
258
|
- For each discovered route: navigate → assert HTTP 200 → assert ready signal visible
|
|
246
259
|
- No detailed assertions — just "this page loads without crashing"
|
|
247
260
|
- This is a regression baseline — catches when existing pages break
|
|
248
261
|
|
|
249
262
|
**Change mode** → `tests/playwright/<name>.spec.ts` (functional):
|
|
263
|
+
|
|
250
264
|
- Read: test-plan.md, app-exploration.md, app-knowledge.md, seed.spec.ts
|
|
251
265
|
- For each test case: verify selectors in real browser, then write Playwright code
|
|
252
266
|
|
|
253
267
|
**Selector verification (change mode)**:
|
|
268
|
+
|
|
254
269
|
1. Navigate to route with correct auth state
|
|
255
270
|
2. browser_snapshot to confirm page loaded
|
|
256
271
|
3. For each selector: verify from current snapshot (see 4.3 table for priority)
|
|
@@ -260,6 +275,7 @@ Template: `openspec/schemas/playwright-e2e/templates/test-plan.md`
|
|
|
260
275
|
**Test coverage — empty states**: For list/detail pages, explore the empty state. If the app shows a "no data" UI when the list is empty, generate a test to verify it. Empty states are often missing from specs but are real user paths.
|
|
261
276
|
|
|
262
277
|
**Output format**:
|
|
278
|
+
|
|
263
279
|
- Follow `seed.spec.ts` structure
|
|
264
280
|
- Use `test.describe(...)` for grouping
|
|
265
281
|
- Each test: `test('描述性名称', async ({ page }) => { ... })`
|
|
@@ -270,16 +286,16 @@ Template: `openspec/schemas/playwright-e2e/templates/test-plan.md`
|
|
|
270
286
|
```typescript
|
|
271
287
|
// ✅ UI 测试 — 用户在界面上的真实操作
|
|
272
288
|
await page.goto(`${BASE_URL}/orders`);
|
|
273
|
-
await page.getByRole(
|
|
274
|
-
await page.getByLabel(
|
|
275
|
-
await page.getByRole(
|
|
276
|
-
await expect(page.getByText(
|
|
289
|
+
await page.getByRole("button", { name: "新建订单" }).click();
|
|
290
|
+
await page.getByLabel("订单名称").fill("Test Order");
|
|
291
|
+
await page.getByRole("button", { name: "提交" }).click();
|
|
292
|
+
await expect(page.getByText("订单创建成功")).toBeVisible();
|
|
277
293
|
|
|
278
294
|
// ✅ Error path — 通过 UI 触发错误
|
|
279
295
|
await page.goto(`${BASE_URL}/orders`);
|
|
280
|
-
await page.getByRole(
|
|
281
|
-
await page.getByRole(
|
|
282
|
-
await expect(page.getByRole(
|
|
296
|
+
await page.getByRole("button", { name: "新建订单" }).click();
|
|
297
|
+
await page.getByRole("button", { name: "提交" }).click();
|
|
298
|
+
await expect(page.getByRole("alert")).toContainText("名称不能为空");
|
|
283
299
|
|
|
284
300
|
// ✅ API fallback — 仅在 UI 无法触发时使用
|
|
285
301
|
const res = await page.request.get(`${BASE_URL}/api/orders/99999`);
|
|
@@ -306,13 +322,13 @@ await expect(page).toHaveURL(/dashboard/);
|
|
|
306
322
|
|
|
307
323
|
```typescript
|
|
308
324
|
// ✅ Fresh browser context for auth guard
|
|
309
|
-
test(
|
|
325
|
+
test("unauthenticated user redirected to login", async ({ browser }) => {
|
|
310
326
|
const freshPage = await browser.newContext().newPage();
|
|
311
327
|
await freshPage.goto(`${BASE_URL}/dashboard`);
|
|
312
328
|
await expect(freshPage).toHaveURL(/login|auth/);
|
|
313
329
|
});
|
|
314
330
|
// ✅ Session — logout clears protected state
|
|
315
|
-
await page.getByRole(
|
|
331
|
+
await page.getByRole("button", { name: "退出登录" }).click();
|
|
316
332
|
await expect(page).toHaveURL(/login|auth/);
|
|
317
333
|
const freshPage2 = await browser.newContext().newPage();
|
|
318
334
|
await freshPage2.goto(`${BASE_URL}/dashboard`);
|
|
@@ -320,7 +336,7 @@ await expect(freshPage2).toHaveURL(/login|auth/); // session revoked
|
|
|
320
336
|
|
|
321
337
|
// ✅ Browser history — SPA back/forward navigation
|
|
322
338
|
await page.goto(`${BASE_URL}/list`);
|
|
323
|
-
await page.getByRole(
|
|
339
|
+
await page.getByRole("link", { name: "详情" }).first().click();
|
|
324
340
|
await expect(page).toHaveURL(/detail/);
|
|
325
341
|
await page.goBack();
|
|
326
342
|
await expect(page).toHaveURL(/list/);
|
|
@@ -328,7 +344,7 @@ await page.goForward();
|
|
|
328
344
|
await expect(page).toHaveURL(/detail/);
|
|
329
345
|
|
|
330
346
|
// ✅ File uploads — UI 操作
|
|
331
|
-
await page.locator('input[type="file"]').setInputFiles(
|
|
347
|
+
await page.locator('input[type="file"]').setInputFiles("/path/to/file.pdf");
|
|
332
348
|
```
|
|
333
349
|
|
|
334
350
|
Always include error path tests: UI validation messages, network failure, invalid input. Use `page.request` only for scenarios confirmed unreachable via UI.
|
|
@@ -342,12 +358,14 @@ If the file exists → diff against test-plan, add only missing test cases.
|
|
|
342
358
|
- **Multi-user**: Separate `storageState` paths per role
|
|
343
359
|
|
|
344
360
|
**Credential format guidance**:
|
|
361
|
+
|
|
345
362
|
- If the app uses **email** for login → use `CHANGE_ME@example.com`
|
|
346
363
|
- If the app uses **username** (alphanumeric + underscore) → use `test_user_001` (more universal)
|
|
347
364
|
- Check existing test files or login page to determine the format
|
|
348
365
|
- Always set credentials via environment variables — never hardcode
|
|
349
366
|
|
|
350
367
|
**Prompt user**:
|
|
368
|
+
|
|
351
369
|
```
|
|
352
370
|
Auth required. To set up:
|
|
353
371
|
1. Customize tests/playwright/credentials.yaml
|
|
@@ -363,6 +381,7 @@ Auth required. To set up:
|
|
|
363
381
|
If missing → generate from `openspec/schemas/playwright-e2e/templates/playwright.config.ts`.
|
|
364
382
|
|
|
365
383
|
**Auto-detect BASE_URL** (in priority order):
|
|
384
|
+
|
|
366
385
|
1. `process.env.BASE_URL` if already set
|
|
367
386
|
2. `tests/playwright/seed.spec.ts` → extract `BASE_URL` value
|
|
368
387
|
3. Read `vite.config.ts` (or `vite.config.js`) → extract `server.port` + infer protocol (`https` if `server.https`, else `http`)
|
|
@@ -370,6 +389,7 @@ If missing → generate from `openspec/schemas/playwright-e2e/templates/playwrig
|
|
|
370
389
|
5. Fallback: `http://localhost:3000`
|
|
371
390
|
|
|
372
391
|
**Auto-detect dev command**:
|
|
392
|
+
|
|
373
393
|
1. `package.json` → scripts in order: `dev` → `start` → `serve` → `preview` → `npm run dev`
|
|
374
394
|
|
|
375
395
|
If playwright.config.ts exists → READ first, preserve ALL existing fields, add only missing `webServer` block.
|
|
@@ -385,27 +405,29 @@ The CLI handles: server lifecycle, port mismatch, report generation.
|
|
|
385
405
|
If tests fail → use Playwright MCP tools to inspect UI, fix selectors, re-run.
|
|
386
406
|
|
|
387
407
|
**Healer MCP tools** (in order of use):
|
|
408
|
+
|
|
388
409
|
<!-- MCP_VERSION: 0.0.70 -->
|
|
389
410
|
|
|
390
|
-
| Tool
|
|
391
|
-
|
|
392
|
-
| `browser_navigate`
|
|
393
|
-
| `browser_snapshot`
|
|
394
|
-
| `browser_console_messages` | Diagnose JS errors that may cause failures
|
|
395
|
-
| `browser_take_screenshot`
|
|
396
|
-
| `browser_run_code`
|
|
411
|
+
| Tool | Purpose |
|
|
412
|
+
| -------------------------- | ----------------------------------------------- |
|
|
413
|
+
| `browser_navigate` | Go to the failing test's page |
|
|
414
|
+
| `browser_snapshot` | Get page structure to find equivalent selectors |
|
|
415
|
+
| `browser_console_messages` | Diagnose JS errors that may cause failures |
|
|
416
|
+
| `browser_take_screenshot` | Visually compare before/after fixes |
|
|
417
|
+
| `browser_run_code` | Execute custom fix logic (optional) |
|
|
397
418
|
|
|
398
419
|
**Healer workflow**:
|
|
420
|
+
|
|
399
421
|
1. Read the failing test → identify failure type
|
|
400
422
|
2. Classify:
|
|
401
423
|
|
|
402
|
-
| Failure type
|
|
403
|
-
|
|
404
|
-
| **Network/backend**
|
|
405
|
-
| **Selector changed**
|
|
406
|
-
| **Assertion mismatch**
|
|
407
|
-
| **Timing issue**
|
|
408
|
-
| **page.evaluate with fetch** | `fetch` in browser context, CORS errors | Switch to `page.request` API → re-run
|
|
424
|
+
| Failure type | Signal | Action |
|
|
425
|
+
| ---------------------------- | --------------------------------------- | ----------------------------------------------------- |
|
|
426
|
+
| **Network/backend** | `fetch failed`, `net::ERR`, 5xx | `browser_console_messages` → `test.skip()` |
|
|
427
|
+
| **Selector changed** | Element not found | `browser_snapshot` → fix selector → re-run |
|
|
428
|
+
| **Assertion mismatch** | Wrong content/value | `browser_snapshot` → compare → fix assertion → re-run |
|
|
429
|
+
| **Timing issue** | `waitFor`/`page.evaluate` timeout | Switch to `request` API or add `waitFor` → re-run |
|
|
430
|
+
| **page.evaluate with fetch** | `fetch` in browser context, CORS errors | Switch to `page.request` API → re-run |
|
|
409
431
|
|
|
410
432
|
3. **Heal** (≤3 attempts): snapshot → fix → re-run. If healed successfully → append to `app-knowledge.md` → **Selector Fixes** table: route, old selector → new selector, reason.
|
|
411
433
|
4. **After 3 failures**: collect evidence checklist → `test.skip()` if app bug, report recommendation if test bug
|
|
@@ -413,6 +435,7 @@ If tests fail → use Playwright MCP tools to inspect UI, fix selectors, re-run.
|
|
|
413
435
|
### 10. False Pass Detection
|
|
414
436
|
|
|
415
437
|
Run after test suite completes (even if all pass). Common patterns (see Step 6 Anti-Pattern Warnings for fixes):
|
|
438
|
+
|
|
416
439
|
- **Conditional visibility**: `if (locator.isVisible().catch(() => false))` — if test passes, locator may not exist
|
|
417
440
|
- **Too fast**: < 200ms for a complex flow is suspicious
|
|
418
441
|
- **No fresh auth context**: Protected routes without `browser.newContext()`
|
|
@@ -422,6 +445,7 @@ Report any gaps in a **⚠️ Coverage Gap** section.
|
|
|
422
445
|
### 11. Report results
|
|
423
446
|
|
|
424
447
|
Read report at `openspec/reports/playwright-e2e-<name>-<timestamp>.md`. Present:
|
|
448
|
+
|
|
425
449
|
- Summary table (tests, passed, failed, duration, status)
|
|
426
450
|
- Auto-heal notes
|
|
427
451
|
- Recommendations with `file:line` references
|
|
@@ -436,22 +460,22 @@ Reference: `openspec/schemas/playwright-e2e/templates/report.md`
|
|
|
436
460
|
|
|
437
461
|
## Graceful Degradation
|
|
438
462
|
|
|
439
|
-
| Scenario
|
|
440
|
-
|
|
441
|
-
| No specs (change mode)
|
|
442
|
-
| Sitemap discovery fails ("all" mode)
|
|
443
|
-
| App has JS errors or HTTP 5xx during exploration | **STOP** — see app-knowledge.md → Architecture for restart instructions
|
|
444
|
-
| app-all.spec.ts exists
|
|
445
|
-
| app-exploration.md missing (change mode)
|
|
446
|
-
| app-exploration.md exists
|
|
447
|
-
| app-knowledge.md exists
|
|
448
|
-
| test-plan.md exists (change mode)
|
|
449
|
-
| auth.setup.ts exists
|
|
450
|
-
| playwright.config.ts exists
|
|
451
|
-
| Test fails (backend)
|
|
452
|
-
| Test fails (selector/assertion)
|
|
453
|
-
| 3 heals failed
|
|
454
|
-
| False pass detected
|
|
463
|
+
| Scenario | Behavior |
|
|
464
|
+
| ------------------------------------------------ | ------------------------------------------------------------------------------------- |
|
|
465
|
+
| No specs (change mode) | Stop — E2E requires specs. Use "all" mode instead. |
|
|
466
|
+
| Sitemap discovery fails ("all" mode) | Continue — use homepage links + common paths fallback |
|
|
467
|
+
| App has JS errors or HTTP 5xx during exploration | **STOP** — see app-knowledge.md → Architecture for restart instructions |
|
|
468
|
+
| app-all.spec.ts exists | Read and use (never regenerate — regression baseline) |
|
|
469
|
+
| app-exploration.md missing (change mode) | **STOP** — Step 4 exploration is mandatory. Explore before generating tests. |
|
|
470
|
+
| app-exploration.md exists | Read and use (verify routes still match specs — re-explore if page structure changed) |
|
|
471
|
+
| app-knowledge.md exists | Read and use (append new patterns only) |
|
|
472
|
+
| test-plan.md exists (change mode) | Read and use (never regenerate) |
|
|
473
|
+
| auth.setup.ts exists | Verify format (update only if stale) |
|
|
474
|
+
| playwright.config.ts exists | Preserve all fields (add only missing) |
|
|
475
|
+
| Test fails (backend) | `test.skip()` + report |
|
|
476
|
+
| Test fails (selector/assertion) | Healer: snapshot → fix → re-run (≤3) |
|
|
477
|
+
| 3 heals failed | Evidence checklist → app bug: `test.skip()`; unclear: report |
|
|
478
|
+
| False pass detected | Add "⚠️ Coverage Gap" to report |
|
|
455
479
|
|
|
456
480
|
## Guardrails
|
|
457
481
|
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024-present wxhou
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -20,7 +20,7 @@ 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
|
|
23
|
+
Auto-detects and installs commands for all 23 editors OpenSpec supports:
|
|
24
24
|
|
|
25
25
|
| Editor | Path | Editor | Path |
|
|
26
26
|
|--------|------|--------|------|
|
|
@@ -54,6 +54,7 @@ Auto-detects and installs commands for all 24 editors OpenSpec supports:
|
|
|
54
54
|
openspec-pw init # Initialize integration (one-time setup)
|
|
55
55
|
openspec-pw update # Update CLI and commands to latest version
|
|
56
56
|
openspec-pw doctor # Check prerequisites
|
|
57
|
+
openspec-pw uninstall # Remove integration from the project
|
|
57
58
|
```
|
|
58
59
|
|
|
59
60
|
## How It Works
|
|
@@ -100,12 +101,12 @@ openspec-pw doctor # Check prerequisites
|
|
|
100
101
|
|
|
101
102
|
1. **Node.js >= 20**
|
|
102
103
|
2. **OpenSpec** initialized: `npm install -g @fission-ai/openspec && openspec init`
|
|
103
|
-
3. **One of
|
|
104
|
+
3. **One of 23 editors**: Claude Code, Cursor, Windsurf, Cline, Continue, Amazon Q, Gemini CLI, GitHub Copilot, Kiro, Kilo Code, iFlow, CoStrict, OpenCode, Auggie, Factory, CodeBuddy, Codex, Pi, Qoder, Qwen Code, RooCode, Crush, Antigravity (auto-detected)
|
|
104
105
|
4. **Claude Code only**: Playwright MCP — `claude mcp add playwright npx @playwright/mcp@latest`
|
|
105
106
|
|
|
106
107
|
## What `openspec-pw init` Does
|
|
107
108
|
|
|
108
|
-
1. Detects installed AI coding assistants (all
|
|
109
|
+
1. Detects installed AI coding assistants (all 23 supported editors)
|
|
109
110
|
2. Installs E2E command/workflow files for each detected editor
|
|
110
111
|
3. Installs `/openspec-e2e` skill for Claude Code
|
|
111
112
|
4. Installs Playwright MCP globally for Claude Code (via `claude mcp add`)
|
package/README.zh-CN.md
CHANGED
|
@@ -20,7 +20,7 @@ openspec-pw init # 安装 Playwright E2E 集成
|
|
|
20
20
|
|
|
21
21
|
## 支持的 AI 编码助手
|
|
22
22
|
|
|
23
|
-
自动检测并安装 OpenSpec 支持的全部
|
|
23
|
+
自动检测并安装 OpenSpec 支持的全部 23 个编辑器的命令文件:
|
|
24
24
|
|
|
25
25
|
| 编辑器 | 路径 | 编辑器 | 路径 |
|
|
26
26
|
|--------|------|--------|------|
|
|
@@ -54,6 +54,7 @@ openspec-pw init # 安装 Playwright E2E 集成
|
|
|
54
54
|
openspec-pw init # 初始化集成(一次性设置)
|
|
55
55
|
openspec-pw update # 更新 CLI 和命令到最新版本
|
|
56
56
|
openspec-pw doctor # 检查前置条件
|
|
57
|
+
openspec-pw uninstall # 移除项目中的集成
|
|
57
58
|
```
|
|
58
59
|
|
|
59
60
|
## 工作原理
|
|
@@ -101,12 +102,12 @@ openspec-pw doctor # 检查前置条件
|
|
|
101
102
|
|
|
102
103
|
1. **Node.js >= 20**
|
|
103
104
|
2. **OpenSpec** 已初始化: `npm install -g @fission-ai/openspec && openspec init`
|
|
104
|
-
3. **任一
|
|
105
|
+
3. **任一 23 编辑器**: Claude Code、Cursor、Windsurf、Cline、Continue 等(自动检测)
|
|
105
106
|
4. **仅 Claude Code**: Playwright MCP — `claude mcp add playwright npx @playwright/mcp@latest`
|
|
106
107
|
|
|
107
108
|
## `openspec-pw init` 做了什么
|
|
108
109
|
|
|
109
|
-
1. 检测已安装的 AI 编码助手(支持全部
|
|
110
|
+
1. 检测已安装的 AI 编码助手(支持全部 23 个编辑器)
|
|
110
111
|
2. 为每个检测到的编辑器安装 E2E 命令/工作流文件
|
|
111
112
|
3. 为 Claude Code 安装 `/openspec-e2e` skill
|
|
112
113
|
4. 为 Claude Code 全局安装 Playwright MCP(通过 `claude mcp add`)
|
package/bin/CLAUDE.md
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
<claude-mem-context>
|
|
2
|
+
# Recent Activity
|
|
3
|
+
|
|
4
|
+
<!-- This section is auto-generated by claude-mem. Edit content outside the tags. -->
|
|
5
|
+
|
|
6
|
+
### Mar 26, 2026
|
|
7
|
+
|
|
8
|
+
| ID | Time | T | Title | Read |
|
|
9
|
+
|----|------|---|-------|------|
|
|
10
|
+
| #5347 | 6:14 PM | 🔄 | Removed complex wrapper script bin/openspec-pw.js | ~119 |
|
|
11
|
+
</claude-mem-context>
|
package/dist/CLAUDE.md
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
<claude-mem-context>
|
|
2
|
+
# Recent Activity
|
|
3
|
+
|
|
4
|
+
<!-- This section is auto-generated by claude-mem. Edit content outside the tags. -->
|
|
5
|
+
|
|
6
|
+
### Mar 26, 2026
|
|
7
|
+
|
|
8
|
+
| ID | Time | T | Title | Read |
|
|
9
|
+
|----|------|---|-------|------|
|
|
10
|
+
| #5344 | 6:14 PM | 🟣 | Openspec-playwright CLI tool with 4 commands implemented | ~294 |
|
|
11
|
+
|
|
12
|
+
### Mar 27, 2026
|
|
13
|
+
|
|
14
|
+
| ID | Time | T | Title | Read |
|
|
15
|
+
|----|------|---|-------|------|
|
|
16
|
+
| #5381 | 9:46 AM | 🟣 | Built openspec-pw CLI tool with init and doctor commands | ~206 |
|
|
17
|
+
</claude-mem-context>
|