openspec-playwright 0.1.57 → 0.1.58
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.
|
@@ -10,58 +10,64 @@ metadata:
|
|
|
10
10
|
|
|
11
11
|
## Input
|
|
12
12
|
|
|
13
|
-
- **Change name**: `/opsx:e2e <name>` or
|
|
14
|
-
- **Specs**: `openspec/changes/<name>/specs/*.md`
|
|
13
|
+
- **Change name**: `/opsx:e2e <name>` or `/opsx:e2e all` (full app exploration, no OpenSpec needed)
|
|
14
|
+
- **Specs**: `openspec/changes/<name>/specs/*.md` (if change mode)
|
|
15
15
|
- **Credentials**: `E2E_USERNAME` + `E2E_PASSWORD` env vars
|
|
16
16
|
|
|
17
17
|
## Output
|
|
18
18
|
|
|
19
|
-
- **Test file**: `tests/playwright/<name>.spec.ts`
|
|
19
|
+
- **Test file**: `tests/playwright/<name>.spec.ts` (e.g. `app-all.spec.ts` for "all")
|
|
20
20
|
- **Auth setup**: `tests/playwright/auth.setup.ts` (if auth required)
|
|
21
21
|
- **Report**: `openspec/reports/playwright-e2e-<name>-<timestamp>.md`
|
|
22
|
-
- **Test plan**: `openspec/changes/<name>/specs/playwright/test-plan.md`
|
|
22
|
+
- **Test plan**: `openspec/changes/<name>/specs/playwright/test-plan.md` (change mode only)
|
|
23
23
|
|
|
24
24
|
## Architecture
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
Two modes, same pipeline:
|
|
27
27
|
|
|
28
|
-
|
|
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` |
|
|
29
32
|
|
|
30
|
-
|
|
33
|
+
Both modes update `app-knowledge.md` and `app-exploration.md`. All `.spec.ts` files run together as regression suite.
|
|
31
34
|
|
|
32
35
|
## Steps
|
|
33
36
|
|
|
34
|
-
### 1. Select the change
|
|
37
|
+
### 1. Select the change or mode
|
|
35
38
|
|
|
36
|
-
|
|
37
|
-
-
|
|
38
|
-
-
|
|
39
|
-
-
|
|
39
|
+
**Change mode** (`/opsx:e2e <name>`):
|
|
40
|
+
- Use provided name, or infer from context, or auto-select if only one exists
|
|
41
|
+
- If ambiguous → `openspec list --json` + AskUserQuestion
|
|
42
|
+
- Verify specs exist: `openspec status --change "<name>" --json`
|
|
43
|
+
- If specs empty → **STOP: E2E requires specs.** Use "all" mode instead.
|
|
40
44
|
|
|
41
|
-
|
|
45
|
+
**"all" mode** (`/opsx:e2e all` — no OpenSpec needed):
|
|
46
|
+
- Announce: "Mode: full app exploration"
|
|
47
|
+
- Discover routes via:
|
|
48
|
+
1. Navigate to `${BASE_URL}/sitemap.xml` (if exists)
|
|
49
|
+
2. Navigate to `${BASE_URL}/` → extract all links from snapshot
|
|
50
|
+
3. Fallback common paths: `/`, `/login`, `/dashboard`, `/admin`, `/profile`, `/api/`
|
|
51
|
+
- Group routes: Guest vs Protected (by attempting direct access)
|
|
42
52
|
|
|
43
|
-
|
|
44
|
-
```bash
|
|
45
|
-
openspec status --change "<name>" --json
|
|
46
|
-
```
|
|
47
|
-
If `openspec/changes/<name>/specs/` is empty, inform the user and stop. E2E requires specs.
|
|
53
|
+
### 2. Detect auth
|
|
48
54
|
|
|
49
|
-
|
|
55
|
+
**Change mode**: Read specs and extract functional requirements. Detect auth from keywords.
|
|
50
56
|
|
|
51
|
-
|
|
57
|
+
**"all" mode**: Detect auth by attempting to access known protected paths (e.g. `/dashboard`, `/profile`). If redirected to `/login` → auth required.
|
|
52
58
|
|
|
53
|
-
|
|
59
|
+
**Auth detection — both modes** (BOTH conditions required):
|
|
54
60
|
|
|
55
61
|
**Condition A — Explicit markers**: "login", "signin", "logout", "authenticate", "protected", "authenticated", "session", "unauthorized", "jwt", "token", "refresh", "middleware"
|
|
56
62
|
|
|
57
63
|
**Condition B — Context indicators**: Protected routes ("/dashboard", "/profile", "/admin"), role mentions ("admin", "user"), redirect flows
|
|
58
64
|
|
|
59
|
-
**Exclude false positives
|
|
65
|
+
**Exclude false positives**: HTTP header examples (`Authorization: Bearer ...`) and code snippets do not count.
|
|
60
66
|
|
|
61
|
-
**Confidence
|
|
62
|
-
- High (auto-proceed): Multiple
|
|
63
|
-
- Medium (proceed with note): Single
|
|
64
|
-
- Low (skip auth): No
|
|
67
|
+
**Confidence**:
|
|
68
|
+
- High (auto-proceed): Multiple markers AND context indicators
|
|
69
|
+
- Medium (proceed with note): Single marker, context unclear
|
|
70
|
+
- Low (skip auth): No markers found
|
|
65
71
|
|
|
66
72
|
### 3. Validate environment
|
|
67
73
|
|
|
@@ -80,11 +86,11 @@ Explore to collect real DOM data before writing test plan. This eliminates blind
|
|
|
80
86
|
|
|
81
87
|
**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
88
|
|
|
83
|
-
#### 4.1. Verify BASE_URL + Read app-knowledge.md
|
|
89
|
+
#### 4.1. Verify BASE_URL + Read app-knowledge.md
|
|
84
90
|
|
|
85
|
-
1. **Verify BASE_URL**: `browser_navigate(BASE_URL)` →
|
|
86
|
-
2. **Read app-knowledge.md**:
|
|
87
|
-
3. **
|
|
91
|
+
1. **Verify BASE_URL**: `browser_navigate(BASE_URL)` → if HTTP 5xx → **STOP: backend error. Fix app first.**
|
|
92
|
+
2. **Read app-knowledge.md**: known risks, project conventions
|
|
93
|
+
3. **Routes** (from Step 1): use already-discovered routes — no need to re-extract
|
|
88
94
|
|
|
89
95
|
#### 4.2. Explore each route via Playwright MCP
|
|
90
96
|
|
|
@@ -188,139 +194,80 @@ Read `tests/playwright/app-knowledge.md` as context for cross-change patterns.
|
|
|
188
194
|
|
|
189
195
|
### 5. Generate test plan
|
|
190
196
|
|
|
191
|
-
|
|
197
|
+
> **"all" mode: skip this step — go directly to Step 6.**
|
|
192
198
|
|
|
193
|
-
**
|
|
194
|
-
- `openspec/changes/<name>/specs/*.md` — functional requirements
|
|
195
|
-
- `openspec/changes/<name>/specs/playwright/app-exploration.md` — **real routes and verified selectors**
|
|
196
|
-
- `tests/playwright/app-knowledge.md` — project-level shared knowledge
|
|
199
|
+
**Change mode**: Create `openspec/changes/<name>/specs/playwright/test-plan.md`.
|
|
197
200
|
|
|
198
|
-
|
|
199
|
-
- List each functional requirement as a test case
|
|
200
|
-
- Mark with `@role(user|admin|guest|none)` and `@auth(required|none)`
|
|
201
|
-
- Include happy path AND error paths
|
|
202
|
-
- Reference the **real route URL** and **verified selectors** from app-exploration.md
|
|
201
|
+
**Read inputs**: specs, app-exploration.md, app-knowledge.md
|
|
203
202
|
|
|
204
|
-
|
|
203
|
+
**Create test cases**: functional requirement → test case, with `@role` and `@auth` tags. Reference verified selectors from app-exploration.md.
|
|
205
204
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
### 6. Generate test file
|
|
205
|
+
Template: `openspec/schemas/playwright-e2e/templates/test-plan.md`
|
|
209
206
|
|
|
210
|
-
|
|
207
|
+
**Idempotency**: If test-plan.md exists → read and use, do NOT regenerate.
|
|
211
208
|
|
|
212
|
-
|
|
213
|
-
- `openspec/changes/<name>/specs/playwright/test-plan.md` — test cases
|
|
214
|
-
- `openspec/changes/<name>/specs/playwright/app-exploration.md` — **verified routes and selectors**
|
|
215
|
-
- `tests/playwright/app-knowledge.md` — project-level patterns and conventions
|
|
216
|
-
- `tests/playwright/seed.spec.ts` — code pattern and page object structure
|
|
209
|
+
### 6. Generate test file
|
|
217
210
|
|
|
218
|
-
|
|
211
|
+
**"all" mode** → `tests/playwright/app-all.spec.ts` (smoke regression):
|
|
212
|
+
- For each discovered route: navigate → assert HTTP 200 → assert ready signal visible
|
|
213
|
+
- No detailed assertions — just "this page loads without crashing"
|
|
214
|
+
- This is a regression baseline — catches when existing pages break
|
|
219
215
|
|
|
220
|
-
**
|
|
216
|
+
**Change mode** → `tests/playwright/<name>.spec.ts` (functional):
|
|
217
|
+
- Read: test-plan.md, app-exploration.md, app-knowledge.md, seed.spec.ts
|
|
218
|
+
- For each test case: verify selectors in real browser, then write Playwright code
|
|
221
219
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
4. Wait for page ready: browser_wait_for with a key element
|
|
229
|
-
5. Verify: browser_snapshot to confirm page loaded
|
|
230
|
-
6. For each selector in the test case:
|
|
231
|
-
a. Check if selector exists in app-exploration.md
|
|
232
|
-
b. If yes → verify it's still valid via browser_snapshot
|
|
233
|
-
c. If no → find equivalent from current snapshot
|
|
234
|
-
d. Selector priority: see 4.3 table
|
|
235
|
-
7. Write test code with verified selectors
|
|
236
|
-
8. If selector cannot be verified → note it for Healer (Step 9)
|
|
237
|
-
```
|
|
220
|
+
**Selector verification (change mode)**:
|
|
221
|
+
1. Navigate to route with correct auth state
|
|
222
|
+
2. browser_snapshot to confirm page loaded
|
|
223
|
+
3. For each selector: verify from current snapshot (see 4.3 table for priority)
|
|
224
|
+
4. Write test code with verified selectors
|
|
225
|
+
5. If selector unverifiable → note for Healer (Step 9)
|
|
238
226
|
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
**Generate** Playwright code for each verified test case:
|
|
227
|
+
**Output format**:
|
|
242
228
|
- Follow `seed.spec.ts` structure
|
|
243
|
-
- Prefer `data-testid` selectors (see 4.3 table for priority)
|
|
244
|
-
- Include happy path AND error/edge cases
|
|
245
229
|
- Use `test.describe(...)` for grouping
|
|
246
230
|
- Each test: `test('描述性名称', async ({ page }) => { ... })`
|
|
231
|
+
- Prefer `data-testid` selectors (see 4.3 table)
|
|
247
232
|
|
|
248
|
-
|
|
233
|
+
**Code examples — same for both modes:**
|
|
249
234
|
|
|
250
|
-
**🚫 False Pass** — never silently skip when element is missing:
|
|
251
235
|
```typescript
|
|
252
|
-
//
|
|
236
|
+
// 🚫 False Pass — never silently skip when element is missing
|
|
253
237
|
if (await btn.isVisible().catch(() => false)) { ... }
|
|
254
238
|
// ✅ CORRECT
|
|
255
239
|
await expect(page.getByRole('button', { name: '取消' })).toBeVisible();
|
|
256
|
-
```
|
|
257
|
-
|
|
258
|
-
**🚫 Permission filtering** — use `@tag` with `--grep`, not projects:
|
|
259
|
-
```typescript
|
|
260
|
-
test('admin only', { tag: '@admin' }, async ({ page }) => { ... });
|
|
261
|
-
// Run with: npx playwright test --grep "@admin"
|
|
262
|
-
```
|
|
263
240
|
|
|
264
|
-
|
|
265
|
-
```typescript
|
|
241
|
+
// 🚫 Auth guard — test with FRESH browser context
|
|
266
242
|
test('redirects unauthenticated user to login', async ({ browser }) => {
|
|
267
243
|
const freshPage = await browser.newContext().newPage();
|
|
268
244
|
await freshPage.goto(`${BASE_URL}/dashboard`);
|
|
269
245
|
await expect(freshPage).toHaveURL(/login|auth/);
|
|
270
246
|
});
|
|
271
|
-
```
|
|
272
|
-
|
|
273
|
-
#### Playwright API Guardrails
|
|
274
|
-
|
|
275
|
-
These are the most common mistakes that cause test failures. **Always follow these rules:**
|
|
276
|
-
|
|
277
|
-
**API calls — use `page.request` directly:**
|
|
278
|
-
```typescript
|
|
279
|
-
// ❌ WRONG — page.evaluate with fetch has timeout, CORS, and context issues
|
|
280
|
-
const result = await page.evaluate(async () => {
|
|
281
|
-
const res = await fetch('/api/data');
|
|
282
|
-
return res.json();
|
|
283
|
-
});
|
|
284
247
|
|
|
285
|
-
// ✅
|
|
248
|
+
// ✅ API calls — use page.request directly
|
|
286
249
|
const res = await page.request.get(`${BASE_URL}/api/data`);
|
|
287
250
|
expect(res.status()).toBe(200);
|
|
288
|
-
const data = await res.json();
|
|
289
251
|
```
|
|
290
252
|
|
|
291
|
-
**Browser context — use `close()` for BrowserContext, no cleanup for APIRequestContext:**
|
|
292
253
|
```typescript
|
|
293
|
-
// ✅ BrowserContext — close
|
|
254
|
+
// ✅ BrowserContext — close when done
|
|
294
255
|
const context = await browser.newContext();
|
|
295
|
-
await context.close();
|
|
256
|
+
await context.close();
|
|
296
257
|
|
|
297
258
|
// ✅ APIRequestContext — page.request is already one, no cleanup needed
|
|
298
259
|
const res = await page.request.get(`${BASE_URL}/api/data`);
|
|
299
|
-
// No dispose() or close() needed
|
|
300
260
|
```
|
|
301
261
|
|
|
302
|
-
**File uploads — use `setInputFiles()`, NOT `page.evaluate()` + fetch:**
|
|
303
262
|
```typescript
|
|
304
|
-
//
|
|
305
|
-
await page.evaluate(() => {
|
|
306
|
-
const input = document.querySelector('input[type=file]');
|
|
307
|
-
// ...
|
|
308
|
-
});
|
|
309
|
-
|
|
310
|
-
// ✅ CORRECT
|
|
263
|
+
// ✅ File uploads — use setInputFiles()
|
|
311
264
|
await page.locator('input[type="file"]').setInputFiles('/path/to/file.pdf');
|
|
312
|
-
```
|
|
313
|
-
|
|
314
|
-
**Form submissions — prefer Playwright locators, not JS clicks:**
|
|
315
|
-
```typescript
|
|
316
|
-
// ❌ WRONG — bypasses Playwright's actionability checks
|
|
317
|
-
await page.evaluate(() => document.querySelector('button[type=submit]').click());
|
|
318
265
|
|
|
319
|
-
// ✅
|
|
266
|
+
// ✅ Form submissions — prefer Playwright locators
|
|
320
267
|
await page.getByRole('button', { name: 'Submit' }).click();
|
|
321
268
|
```
|
|
322
269
|
|
|
323
|
-
|
|
270
|
+
Always include error path tests: API 500, 404, network timeout, invalid input.
|
|
324
271
|
|
|
325
272
|
If the file exists → diff against test-plan, add only missing test cases.
|
|
326
273
|
|
|
@@ -429,13 +376,13 @@ Reference: `openspec/schemas/playwright-e2e/templates/report.md`
|
|
|
429
376
|
|
|
430
377
|
| Scenario | Behavior |
|
|
431
378
|
|----------|----------|
|
|
432
|
-
| No specs | Stop — E2E requires specs |
|
|
433
|
-
|
|
|
434
|
-
| App has JS errors or HTTP 5xx during exploration | **STOP** — see app-knowledge.md → Architecture
|
|
435
|
-
|
|
|
379
|
+
| No specs (change mode) | Stop — E2E requires specs. Use "all" mode instead. |
|
|
380
|
+
| Sitemap discovery fails ("all" mode) | Continue — use homepage links + common paths fallback |
|
|
381
|
+
| App has JS errors or HTTP 5xx during exploration | **STOP** — see app-knowledge.md → Architecture for restart instructions |
|
|
382
|
+
| app-all.spec.ts exists | Read and use (never regenerate — regression baseline) |
|
|
436
383
|
| app-exploration.md exists | Read and use (never regenerate) |
|
|
437
384
|
| app-knowledge.md exists | Read and use (append new patterns only) |
|
|
438
|
-
| test-plan.md exists | Read and use (never regenerate) |
|
|
385
|
+
| test-plan.md exists (change mode) | Read and use (never regenerate) |
|
|
439
386
|
| auth.setup.ts exists | Verify format (update only if stale) |
|
|
440
387
|
| playwright.config.ts exists | Preserve all fields (add only missing) |
|
|
441
388
|
| Test fails (backend) | `test.skip()` + report |
|
|
Binary file
|
package/package.json
CHANGED
package/release-notes.md
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
## What's Changed
|
|
2
2
|
|
|
3
|
-
-
|
|
3
|
+
- feat(SKILL): unify "all" and "change" modes — same pipeline, different scope
|
|
4
4
|
|
|
5
|
-
**Full Changelog**: https://github.com/wxhou/openspec-playwright/releases/tag/v0.1.
|
|
5
|
+
**Full Changelog**: https://github.com/wxhou/openspec-playwright/releases/tag/v0.1.58
|
|
Binary file
|