openspec-playwright 0.1.50 → 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
 
@@ -78,25 +78,13 @@ This validates: app server reachable, auth fixtures initialized, Playwright work
78
78
 
79
79
  **Before writing test plan, explore the app to collect real DOM data.** This is the most critical step — it eliminates blind selector guessing.
80
80
 
81
- **Prerequisites**: seed test pass. If auth is required, ensure `auth.setup.ts` has been run (Step 7).
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. Read app-knowledge.md + Extract routes from specs
83
+ #### 4.1. Verify BASE_URL + Read app-knowledge.md + Extract routes
84
84
 
85
- **First**, read `tests/playwright/app-knowledge.md` to understand:
86
- - **Known risks**: SPA behavior, dynamic content patterns, auth method
87
- - **Project conventions**: preferred selector strategy, credential format, BASE_URL
88
-
89
- This is context, not constraint — explore with open eyes even if patterns differ from history.
90
-
91
- **Then**, read all files in `openspec/changes/<name>/specs/*.md`. Extract every URL, route, or path mentioned:
92
-
93
- - Full URLs: `http://localhost:3000/dashboard`, `BASE_URL + /admin`
94
- - Relative paths: `/dashboard`, `/api/auth/login`, `/admin/settings`
95
- - Infer routes from text: "navigate to the dashboard" → check if `/dashboard` exists
96
-
97
- Group routes by role:
98
- - **Guest routes**: `/`, `/login`, `/about` (no auth needed)
99
- - **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
100
88
 
101
89
  #### 4.2. Explore each route via Playwright MCP
102
90
 
@@ -140,66 +128,29 @@ Wait for page stability after navigation:
140
128
 
141
129
  From `browser_snapshot` output, extract **interactive elements** for each route:
142
130
 
143
- | Element type | What to capture | Priority |
131
+ | Element type | What to capture | Selector priority |
144
132
  |---|---|---|
145
- | **Buttons** | text, selector (`getByRole`, `getByLabel`, `data-testid`) | data-testid > role > label > text |
146
- | **Form fields** | name, type, label, selector | data-testid > name > label |
147
- | **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` |
148
136
  | **Headings** | text content, selector | for assertions |
149
137
  | **Error messages** | text patterns, selector | for error path testing |
150
- | **Dynamic content** | structure (not content) — row counts, card layouts | for data-driven tests |
151
-
152
- **Selector strategy** (in priority order):
153
- 1. `[data-testid="..."]` — most stable, prefer these
154
- 2. `getByRole('button', { name: '...' })` — semantic, stable
155
- 3. `getByLabel('...')` — for form fields
156
- 4. `getByText('...')` — fallback, fragile
157
- 5. CSS selectors — last resort
138
+ | **Dynamic content** | structure — row counts, card layouts | for data-driven tests |
158
139
 
159
140
  #### 4.4. Write app-exploration.md
160
141
 
161
142
  Output: `openspec/changes/<name>/specs/playwright/app-exploration.md`
162
143
 
163
- ```markdown
164
- # App Exploration — <name>
165
- Generated: <timestamp>
166
- BASE_URL: <from env or seed.spec.ts>
167
-
168
- ## Route: /
169
- - **Auth**: none
170
- - **URL**: ${BASE_URL}/
171
- - **Ready signal**: page has heading
172
- - **Elements**:
173
- - login link: `a:text("登录")`
174
- - signup link: `[data-testid="signup-link"]`
175
- - **Screenshot**: `__screenshots__/index.png`
176
-
177
- ## Route: /dashboard (user)
178
- - **Auth**: required (storageState: playwright/.auth/user.json)
179
- - **URL**: ${BASE_URL}/dashboard
180
- - **Ready signal**: [data-testid="dashboard-heading"] visible
181
- - **Elements**:
182
- - heading: `[data-testid="page-title"]`
183
- - logout btn: `[data-testid="logout-btn"]`
184
- - profile form: `form >> input[name="displayName"]`
185
- - settings link: `nav >> text=Settings`
186
- - **Screenshot**: `__screenshots__/dashboard-user.png`
187
-
188
- ## Route: /admin (admin)
189
- - **Auth**: required (storageState: playwright/.auth/admin.json)
190
- - **URL**: ${BASE_URL}/admin
191
- - **Ready signal**: [data-testid="admin-panel"] visible
192
- - **Elements**:
193
- - admin panel: `[data-testid="admin-panel"]`
194
- - user table: `table#user-table tbody tr`
195
- - add user btn: `[data-testid="add-user-btn"]`
196
- - delete btn: `button:has-text("Delete")`
197
- - **Screenshot**: `__screenshots__/admin-panel.png`
198
-
199
- ## Exploration Notes
200
- - Route /admin → user gets redirected to /login (no admin role)
201
- - /dashboard loads user-specific data (test assertions should use toContainText, not toHaveText)
202
- ```
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).
203
154
 
204
155
  #### 4.5. Edge cases
205
156
 
@@ -248,18 +199,9 @@ Create test cases:
248
199
  - List each functional requirement as a test case
249
200
  - Mark with `@role(user|admin|guest|none)` and `@auth(required|none)`
250
201
  - Include happy path AND error paths
251
- - Reference the **real route URL** from app-exploration.md for each test
252
- - Reference **verified selectors** from app-exploration.md instead of inferring
253
-
254
- ```markdown
255
- ### User can view dashboard
256
- - **Route**: /dashboard (from app-exploration.md)
257
- - **Auth**: required (user storageState)
258
- - **Test steps**:
259
- 1. Go to `/dashboard`
260
- 2. Assert page heading: `[data-testid="page-title"]` contains "Dashboard"
261
- 3. Assert logout button visible: `[data-testid="logout-btn"]`
262
- ```
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`
263
205
 
264
206
  **Idempotency**: If test-plan.md already exists → read it, use it, do NOT regenerate.
265
207
 
@@ -275,7 +217,7 @@ Create `tests/playwright/<name>.spec.ts`:
275
217
 
276
218
  #### Verify selectors before writing
277
219
 
278
- **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.**
279
221
 
280
222
  ```
281
223
  For each test case in test-plan.md:
@@ -289,7 +231,7 @@ For each test case in test-plan.md:
289
231
  a. Check if selector exists in app-exploration.md
290
232
  b. If yes → verify it's still valid via browser_snapshot
291
233
  c. If no → find equivalent from current snapshot
292
- d. Selector priority: data-testid > getByRole > getByLabel > getByText
234
+ d. Selector priority: see 4.3 table
293
235
  7. Write test code with verified selectors
294
236
  8. If selector cannot be verified → note it for Healer (Step 9)
295
237
  ```
@@ -298,7 +240,7 @@ This ensures every selector in the generated test code has been validated agains
298
240
 
299
241
  **Generate** Playwright code for each verified test case:
300
242
  - Follow `seed.spec.ts` structure
301
- - Prefer `data-testid`; fallback to `getByRole`, `getByLabel`, `getByText`
243
+ - Prefer `data-testid` selectors (see 4.3 table for priority)
302
244
  - Include happy path AND error/edge cases
303
245
  - Use `test.describe(...)` for grouping
304
246
  - Each test: `test('描述性名称', async ({ page }) => { ... })`
@@ -332,34 +274,29 @@ test('redirects unauthenticated user to login', async ({ browser }) => {
332
274
 
333
275
  These are the most common mistakes that cause test failures. **Always follow these rules:**
334
276
 
335
- **API calls — use `request` API, NOT `page.evaluate()`:**
277
+ **API calls — use `page.request` directly:**
336
278
  ```typescript
337
- // ❌ WRONG — page.evaluate timeout, wrong context, CORS issues
279
+ // ❌ WRONG — page.evaluate with fetch has timeout, CORS, and context issues
338
280
  const result = await page.evaluate(async () => {
339
281
  const res = await fetch('/api/data');
340
282
  return res.json();
341
283
  });
342
284
 
343
- // ✅ CORRECT — direct HTTP, no browser context, fast
285
+ // ✅ CORRECT — page.request is already an APIRequestContext, use directly
344
286
  const res = await page.request.get(`${BASE_URL}/api/data`);
345
287
  expect(res.status()).toBe(200);
346
288
  const data = await res.json();
347
289
  ```
348
290
 
349
- **Browser context cleanup — `dispose()` NOT `close()`:**
291
+ **Browser context — use `close()` for BrowserContext, no cleanup for APIRequestContext:**
350
292
  ```typescript
351
- // WRONG — close() is not a function on APIRequestContext
352
- const ctx = await page.request.newContext();
353
- await ctx.dispose(); // actually correct, but this is CommonContext
354
- const context = await browser.newContext();
355
- await context.close(); // ← WRONG
356
-
357
- // ✅ CORRECT
293
+ // BrowserContext — close it when done
358
294
  const context = await browser.newContext();
359
- await context.close(); // close() is correct for BrowserContext
295
+ await context.close(); // correct
360
296
 
361
- // For APIRequestContext (from page.request):
362
- // 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
363
300
  ```
364
301
 
365
302
  **File uploads — use `setInputFiles()`, NOT `page.evaluate()` + fetch:**
@@ -457,18 +394,15 @@ If tests fail → use Playwright MCP tools to inspect UI, fix selectors, re-run.
457
394
  | **Selector changed** | Element not found | `browser_snapshot` → fix selector → re-run |
458
395
  | **Assertion mismatch** | Wrong content/value | `browser_snapshot` → compare → fix assertion → re-run |
459
396
  | **Timing issue** | `waitFor`/`page.evaluate` timeout | Switch to `request` API or add `waitFor` → re-run |
460
- | **Wrong API usage** | `ctx.close is not a function` | Fix: `browser.newContext()` `close()`; `request.newContext()` no cleanup needed |
461
- | **Auth expired** | 401 Unauthorized | Token may have expired — if long suite, recommend splitting or re-auth |
462
- | **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 |
463
398
 
464
- 3. **Attempt heal** (≤3 times): snapshot → fix → re-run
399
+ 3. **Heal** (≤3 attempts): snapshot → fix → re-run
465
400
  4. **After 3 failures**: collect evidence checklist → `test.skip()` if app bug, report recommendation if test bug
466
401
 
467
402
  ### 10. False Pass Detection
468
403
 
469
- Run after test suite completes (even if all pass):
470
-
471
- - **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
472
406
  - **Too fast**: < 200ms for a complex flow is suspicious
473
407
  - **No fresh auth context**: Protected routes without `browser.newContext()`
474
408
 
@@ -481,45 +415,13 @@ Read report at `openspec/reports/playwright-e2e-<name>-<timestamp>.md`. Present:
481
415
  - Auto-heal notes
482
416
  - Recommendations with `file:line` references
483
417
 
418
+ Report template: `openspec/schemas/playwright-e2e/templates/report.md`
419
+
484
420
  **Update tasks.md** if all tests pass: find E2E-related items, append `✅ Verified via Playwright E2E (<timestamp>)`.
485
421
 
486
422
  ## Report Structure
487
423
 
488
- ```markdown
489
- # Playwright E2E Report — <name>
490
-
491
- ## Summary
492
- | Tests | Passed | Failed | Duration | Status |
493
- |-------|--------|--------|----------|--------|
494
- | N | N | N | Xm Xs | ✅/❌ |
495
-
496
- ## Results
497
- ### Passed
498
- | Test | Duration | Notes |
499
- |------|----------|-------|
500
- | ... | ... | ... |
501
-
502
- ### Failed
503
- | Test | Error | Recommendation |
504
- |------|-------|----------------|
505
- | ... | ... | file:line — fix |
506
-
507
- ## Auto-Heal Log
508
- - Attempt N: selector fix → result
509
-
510
- ## Coverage
511
- - [x] Requirement 1
512
- - [ ] Requirement 2 (unverified)
513
-
514
- ## ⚠️ Coverage Gaps
515
- > Tests passed but coverage gaps detected.
516
-
517
- | Test | Gap | Recommendation |
518
- |------|-----|----------------|
519
- | ... | Conditional visibility check | file:line — use `expect().toBeVisible()` |
520
- | ... | Auth guard uses inherited session | Add fresh context test |
521
- | ... | Suspiciously fast execution (<200ms) | Verify test logic executed |
522
- ```
424
+ Reference: `openspec/schemas/playwright-e2e/templates/report.md`
523
425
 
524
426
  ## Graceful Degradation
525
427
 
@@ -543,7 +445,7 @@ Read report at `openspec/reports/playwright-e2e-<name>-<timestamp>.md`. Present:
543
445
  - Read specs from `openspec/changes/<name>/specs/` as source of truth
544
446
  - Do NOT generate tests that contradict the specs
545
447
  - **DO generate real, runnable Playwright test code** — not placeholders or TODOs
546
- - 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`
547
449
  - **Always explore before generating** — Step 4 is mandatory for accurate selectors
548
450
  - Cap auto-heal at 3 attempts
549
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.50",
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): clarify auth flow when credentials not yet available
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.50
5
+ **Full Changelog**: https://github.com/wxhou/openspec-playwright/releases/tag/v0.1.52
Binary file