openspec-playwright 0.1.51 → 0.1.52

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.
@@ -52,7 +52,7 @@ Read all files from `openspec/changes/<name>/specs/*.md`. Extract functional req
52
52
 
53
53
  Detect if auth is required only when BOTH conditions are met:
54
54
 
55
- **Condition A — Explicit markers**: "login", "signin", "logout", "authenticate", "protected", "authenticated", "session", "unauthorized"
55
+ **Condition A — Explicit markers**: "login", "signin", "logout", "authenticate", "protected", "authenticated", "session", "unauthorized", "jwt", "token", "refresh", "middleware"
56
56
 
57
57
  **Condition B — Context indicators**: Protected routes ("/dashboard", "/profile", "/admin"), role mentions ("admin", "user"), redirect flows
58
58
 
@@ -80,31 +80,11 @@ This validates: app server reachable, auth fixtures initialized, Playwright work
80
80
 
81
81
  **Prerequisites**: seed test pass. If auth is required, ensure `auth.setup.ts` has been run (Step 7). BASE_URL must be verified reachable (see 4.1).
82
82
 
83
- #### 4.1. Verify BASE_URL + Read app-knowledge.md + Extract routes from specs
83
+ #### 4.1. Verify BASE_URL + Read app-knowledge.md + Extract routes
84
84
 
85
- **First, verify BASE_URL is reachable**:
86
- ```javascript
87
- await browser_navigate(`${BASE_URL}/`)
88
- // Confirm page loaded → proceed
89
- // If error → check BASE_URL in seed.spec.ts or vite.config.ts
90
- ```
91
- A correct BASE_URL prevents wasted exploration time on unreachable routes.
92
-
93
- **Then**, read `tests/playwright/app-knowledge.md` to understand:
94
- - **Known risks**: SPA behavior, dynamic content patterns, auth method
95
- - **Project conventions**: preferred selector strategy, credential format, BASE_URL
96
-
97
- This is context, not constraint — explore with open eyes even if patterns differ from history.
98
-
99
- **Then**, read all files in `openspec/changes/<name>/specs/*.md`. Extract every URL, route, or path mentioned:
100
-
101
- - Full URLs: `http://localhost:3000/dashboard`, `BASE_URL + /admin`
102
- - Relative paths: `/dashboard`, `/api/auth/login`, `/admin/settings`
103
- - Infer routes from text: "navigate to the dashboard" → check if `/dashboard` exists
104
-
105
- Group routes by role:
106
- - **Guest routes**: `/`, `/login`, `/about` (no auth needed)
107
- - **Protected routes**: `/dashboard`, `/profile`, `/admin` (auth required)
85
+ 1. **Verify BASE_URL reachable**: `browser_navigate(BASE_URL)` if error, check seed.spec.ts or vite.config.ts
86
+ 2. **Read app-knowledge.md**: understand known risks (SPA, dynamic content) and project conventions
87
+ 3. **Read specs**: extract all URLs/paths, group by Guest vs Protected
108
88
 
109
89
  #### 4.2. Explore each route via Playwright MCP
110
90
 
@@ -148,66 +128,29 @@ Wait for page stability after navigation:
148
128
 
149
129
  From `browser_snapshot` output, extract **interactive elements** for each route:
150
130
 
151
- | Element type | What to capture | Priority |
131
+ | Element type | What to capture | Selector priority |
152
132
  |---|---|---|
153
- | **Buttons** | text, selector (`getByRole`, `getByLabel`, `data-testid`) | data-testid > role > label > text |
154
- | **Form fields** | name, type, label, selector | data-testid > name > label |
155
- | **Navigation links** | text, href, selector | text > href |
133
+ | **Buttons** | text, selector | `[data-testid]` > `getByRole` > `getByLabel` > `getByText` |
134
+ | **Form fields** | name, type, label, selector | `[data-testid]` > `name` > `label` |
135
+ | **Navigation links** | text, href, selector | `text` > `href` |
156
136
  | **Headings** | text content, selector | for assertions |
157
137
  | **Error messages** | text patterns, selector | for error path testing |
158
- | **Dynamic content** | structure (not content) — row counts, card layouts | for data-driven tests |
159
-
160
- **Selector strategy** (in priority order):
161
- 1. `[data-testid="..."]` — most stable, prefer these
162
- 2. `getByRole('button', { name: '...' })` — semantic, stable
163
- 3. `getByLabel('...')` — for form fields
164
- 4. `getByText('...')` — fallback, fragile
165
- 5. CSS selectors — last resort
138
+ | **Dynamic content** | structure — row counts, card layouts | for data-driven tests |
166
139
 
167
140
  #### 4.4. Write app-exploration.md
168
141
 
169
142
  Output: `openspec/changes/<name>/specs/playwright/app-exploration.md`
170
143
 
171
- ```markdown
172
- # App Exploration — <name>
173
- Generated: <timestamp>
174
- BASE_URL: <from env or seed.spec.ts>
175
-
176
- ## Route: /
177
- - **Auth**: none
178
- - **URL**: ${BASE_URL}/
179
- - **Ready signal**: page has heading
180
- - **Elements**:
181
- - login link: `a:text("登录")`
182
- - signup link: `[data-testid="signup-link"]`
183
- - **Screenshot**: `__screenshots__/index.png`
184
-
185
- ## Route: /dashboard (user)
186
- - **Auth**: required (storageState: playwright/.auth/user.json)
187
- - **URL**: ${BASE_URL}/dashboard
188
- - **Ready signal**: [data-testid="dashboard-heading"] visible
189
- - **Elements**:
190
- - heading: `[data-testid="page-title"]`
191
- - logout btn: `[data-testid="logout-btn"]`
192
- - profile form: `form >> input[name="displayName"]`
193
- - settings link: `nav >> text=Settings`
194
- - **Screenshot**: `__screenshots__/dashboard-user.png`
195
-
196
- ## Route: /admin (admin)
197
- - **Auth**: required (storageState: playwright/.auth/admin.json)
198
- - **URL**: ${BASE_URL}/admin
199
- - **Ready signal**: [data-testid="admin-panel"] visible
200
- - **Elements**:
201
- - admin panel: `[data-testid="admin-panel"]`
202
- - user table: `table#user-table tbody tr`
203
- - add user btn: `[data-testid="add-user-btn"]`
204
- - delete btn: `button:has-text("Delete")`
205
- - **Screenshot**: `__screenshots__/admin-panel.png`
206
-
207
- ## Exploration Notes
208
- - Route /admin → user gets redirected to /login (no admin role)
209
- - /dashboard loads user-specific data (test assertions should use toContainText, not toHaveText)
210
- ```
144
+ Use template: `openspec/schemas/playwright-e2e/templates/app-exploration.md`
145
+
146
+ Key fields per route:
147
+ - **URL**: `${BASE_URL}<path>`
148
+ - **Auth**: none / required (storageState: `<path>`)
149
+ - **Ready signal**: how to know the page is loaded
150
+ - **Elements**: interactive elements with verified selectors (see 4.3 table)
151
+ - **Screenshot**: `__screenshots__/<slug>.png`
152
+
153
+ After exploration, add route-level notes (redirects, dynamic content → see 4.5).
211
154
 
212
155
  #### 4.5. Edge cases
213
156
 
@@ -256,18 +199,9 @@ Create test cases:
256
199
  - List each functional requirement as a test case
257
200
  - Mark with `@role(user|admin|guest|none)` and `@auth(required|none)`
258
201
  - Include happy path AND error paths
259
- - Reference the **real route URL** from app-exploration.md for each test
260
- - Reference **verified selectors** from app-exploration.md instead of inferring
261
-
262
- ```markdown
263
- ### User can view dashboard
264
- - **Route**: /dashboard (from app-exploration.md)
265
- - **Auth**: required (user storageState)
266
- - **Test steps**:
267
- 1. Go to `/dashboard`
268
- 2. Assert page heading: `[data-testid="page-title"]` contains "Dashboard"
269
- 3. Assert logout button visible: `[data-testid="logout-btn"]`
270
- ```
202
+ - Reference the **real route URL** and **verified selectors** from app-exploration.md
203
+
204
+ Example: see `openspec/schemas/playwright-e2e/templates/test-plan.md`
271
205
 
272
206
  **Idempotency**: If test-plan.md already exists → read it, use it, do NOT regenerate.
273
207
 
@@ -283,7 +217,7 @@ Create `tests/playwright/<name>.spec.ts`:
283
217
 
284
218
  #### Verify selectors before writing
285
219
 
286
- **For each test case, verify selectors in a real browser BEFORE writing the test code.** This is the most important step — do not skip it.
220
+ **For each test case, verify selectors in a real browser BEFORE writing test code.**
287
221
 
288
222
  ```
289
223
  For each test case in test-plan.md:
@@ -297,7 +231,7 @@ For each test case in test-plan.md:
297
231
  a. Check if selector exists in app-exploration.md
298
232
  b. If yes → verify it's still valid via browser_snapshot
299
233
  c. If no → find equivalent from current snapshot
300
- d. Selector priority: data-testid > getByRole > getByLabel > getByText
234
+ d. Selector priority: see 4.3 table
301
235
  7. Write test code with verified selectors
302
236
  8. If selector cannot be verified → note it for Healer (Step 9)
303
237
  ```
@@ -306,7 +240,7 @@ This ensures every selector in the generated test code has been validated agains
306
240
 
307
241
  **Generate** Playwright code for each verified test case:
308
242
  - Follow `seed.spec.ts` structure
309
- - Prefer `data-testid`; fallback to `getByRole`, `getByLabel`, `getByText`
243
+ - Prefer `data-testid` selectors (see 4.3 table for priority)
310
244
  - Include happy path AND error/edge cases
311
245
  - Use `test.describe(...)` for grouping
312
246
  - Each test: `test('描述性名称', async ({ page }) => { ... })`
@@ -340,34 +274,29 @@ test('redirects unauthenticated user to login', async ({ browser }) => {
340
274
 
341
275
  These are the most common mistakes that cause test failures. **Always follow these rules:**
342
276
 
343
- **API calls — use `request` API, NOT `page.evaluate()`:**
277
+ **API calls — use `page.request` directly:**
344
278
  ```typescript
345
- // ❌ WRONG — page.evaluate timeout, wrong context, CORS issues
279
+ // ❌ WRONG — page.evaluate with fetch has timeout, CORS, and context issues
346
280
  const result = await page.evaluate(async () => {
347
281
  const res = await fetch('/api/data');
348
282
  return res.json();
349
283
  });
350
284
 
351
- // ✅ CORRECT — direct HTTP, no browser context, fast
285
+ // ✅ CORRECT — page.request is already an APIRequestContext, use directly
352
286
  const res = await page.request.get(`${BASE_URL}/api/data`);
353
287
  expect(res.status()).toBe(200);
354
288
  const data = await res.json();
355
289
  ```
356
290
 
357
- **Browser context cleanup — `dispose()` NOT `close()`:**
291
+ **Browser context — use `close()` for BrowserContext, no cleanup for APIRequestContext:**
358
292
  ```typescript
359
- // WRONG — close() is not a function on APIRequestContext
360
- const ctx = await page.request.newContext();
361
- await ctx.dispose(); // actually correct, but this is CommonContext
293
+ // BrowserContext — close it when done
362
294
  const context = await browser.newContext();
363
- await context.close(); // ← WRONG
295
+ await context.close(); // ← correct
364
296
 
365
- // ✅ CORRECT
366
- const context = await browser.newContext();
367
- await context.close(); // close() is correct for BrowserContext
368
-
369
- // For APIRequestContext (from page.request):
370
- // No cleanup needed — it's managed by Playwright automatically
297
+ // ✅ APIRequestContext — page.request is already one, no cleanup needed
298
+ const res = await page.request.get(`${BASE_URL}/api/data`);
299
+ // No dispose() or close() needed
371
300
  ```
372
301
 
373
302
  **File uploads — use `setInputFiles()`, NOT `page.evaluate()` + fetch:**
@@ -465,18 +394,15 @@ If tests fail → use Playwright MCP tools to inspect UI, fix selectors, re-run.
465
394
  | **Selector changed** | Element not found | `browser_snapshot` → fix selector → re-run |
466
395
  | **Assertion mismatch** | Wrong content/value | `browser_snapshot` → compare → fix assertion → re-run |
467
396
  | **Timing issue** | `waitFor`/`page.evaluate` timeout | Switch to `request` API or add `waitFor` → re-run |
468
- | **Wrong API usage** | `ctx.close is not a function` | Fix: `browser.newContext()` `close()`; `request.newContext()` no cleanup needed |
469
- | **Auth expired** | 401 Unauthorized | Token may have expired — if long suite, recommend splitting or re-auth |
470
- | **page.evaluate failure** | `fetch` in browser context, CORS errors | Switch to `page.request` API → re-run |
397
+ | **page.evaluate with fetch** | `fetch` in browser context, CORS errors | Switch to `page.request` APIre-run |
471
398
 
472
- 3. **Attempt heal** (≤3 times): snapshot → fix → re-run
399
+ 3. **Heal** (≤3 attempts): snapshot → fix → re-run
473
400
  4. **After 3 failures**: collect evidence checklist → `test.skip()` if app bug, report recommendation if test bug
474
401
 
475
402
  ### 10. False Pass Detection
476
403
 
477
- Run after test suite completes (even if all pass):
478
-
479
- - **Conditional logic**: Look for `if (locator.isVisible().catch(() => false))` — if test passes, locator may not exist
404
+ Run after test suite completes (even if all pass). Common patterns (see Step 6 Anti-Pattern Warnings for fixes):
405
+ - **Conditional visibility**: `if (locator.isVisible().catch(() => false))` — if test passes, locator may not exist
480
406
  - **Too fast**: < 200ms for a complex flow is suspicious
481
407
  - **No fresh auth context**: Protected routes without `browser.newContext()`
482
408
 
@@ -489,45 +415,13 @@ Read report at `openspec/reports/playwright-e2e-<name>-<timestamp>.md`. Present:
489
415
  - Auto-heal notes
490
416
  - Recommendations with `file:line` references
491
417
 
418
+ Report template: `openspec/schemas/playwright-e2e/templates/report.md`
419
+
492
420
  **Update tasks.md** if all tests pass: find E2E-related items, append `✅ Verified via Playwright E2E (<timestamp>)`.
493
421
 
494
422
  ## Report Structure
495
423
 
496
- ```markdown
497
- # Playwright E2E Report — <name>
498
-
499
- ## Summary
500
- | Tests | Passed | Failed | Duration | Status |
501
- |-------|--------|--------|----------|--------|
502
- | N | N | N | Xm Xs | ✅/❌ |
503
-
504
- ## Results
505
- ### Passed
506
- | Test | Duration | Notes |
507
- |------|----------|-------|
508
- | ... | ... | ... |
509
-
510
- ### Failed
511
- | Test | Error | Recommendation |
512
- |------|-------|----------------|
513
- | ... | ... | file:line — fix |
514
-
515
- ## Auto-Heal Log
516
- - Attempt N: selector fix → result
517
-
518
- ## Coverage
519
- - [x] Requirement 1
520
- - [ ] Requirement 2 (unverified)
521
-
522
- ## ⚠️ Coverage Gaps
523
- > Tests passed but coverage gaps detected.
524
-
525
- | Test | Gap | Recommendation |
526
- |------|-----|----------------|
527
- | ... | Conditional visibility check | file:line — use `expect().toBeVisible()` |
528
- | ... | Auth guard uses inherited session | Add fresh context test |
529
- | ... | Suspiciously fast execution (<200ms) | Verify test logic executed |
530
- ```
424
+ Reference: `openspec/schemas/playwright-e2e/templates/report.md`
531
425
 
532
426
  ## Graceful Degradation
533
427
 
@@ -551,7 +445,7 @@ Read report at `openspec/reports/playwright-e2e-<name>-<timestamp>.md`. Present:
551
445
  - Read specs from `openspec/changes/<name>/specs/` as source of truth
552
446
  - Do NOT generate tests that contradict the specs
553
447
  - **DO generate real, runnable Playwright test code** — not placeholders or TODOs
554
- - Do NOT overwrite files outside: `specs/playwright/`, `tests/playwright/`, `openspec/reports/`, `playwright.config.ts`, `auth.setup.ts`, `app-exploration.md`, `app-knowledge.md`
448
+ - Do NOT overwrite files outside: `specs/playwright/`, `tests/playwright/`, `openspec/reports/`, `playwright.config.ts`, `auth.setup.ts`
555
449
  - **Always explore before generating** — Step 4 is mandatory for accurate selectors
556
450
  - Cap auto-heal at 3 attempts
557
451
  - If no change specified → always ask user to select
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openspec-playwright",
3
- "version": "0.1.51",
3
+ "version": "0.1.52",
4
4
  "description": "OpenSpec + Playwright E2E verification setup tool for Claude Code",
5
5
  "type": "module",
6
6
  "bin": {
package/release-notes.md CHANGED
@@ -1,5 +1,5 @@
1
1
  ## What's Changed
2
2
 
3
- - fix(SKILL): add BASE_URL smoke check before exploration (Step 4.1)
3
+ - refactor(SKILL): trim 107 lines remove duplication, inline templates, emotional language
4
4
 
5
- **Full Changelog**: https://github.com/wxhou/openspec-playwright/releases/tag/v0.1.51
5
+ **Full Changelog**: https://github.com/wxhou/openspec-playwright/releases/tag/v0.1.52
Binary file