openspec-playwright 0.1.71 → 0.1.73
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 +243 -58
- package/dist/commands/init.js +23 -3
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/update.js +10 -0
- package/dist/commands/update.js.map +1 -1
- package/employee-standards.md +1 -1
- package/package.json +1 -1
- package/templates/app-exploration.md +11 -0
- package/templates/e2e-test.ts +18 -17
- package/templates/pages/BasePage.ts +171 -0
- package/templates/seed.spec.ts +7 -13
- package/templates/test-plan.md +77 -0
|
@@ -32,13 +32,7 @@ Two modes, same pipeline:
|
|
|
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
|
-
**Role mapping
|
|
36
|
-
|
|
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 |
|
|
35
|
+
> **Role mapping**: Planner (Step 4–5) → test-plan.md; Generator (Step 6) → `.spec.ts` with verified selectors; Healer (Step 9) → repairs failures via MCP.
|
|
42
36
|
|
|
43
37
|
## Testing principles
|
|
44
38
|
|
|
@@ -184,6 +178,72 @@ From `browser_snapshot` output, extract **interactive elements** for each route:
|
|
|
184
178
|
| **Headings** | text content, selector | for assertions |
|
|
185
179
|
| **Error messages** | text patterns, selector | for error path testing |
|
|
186
180
|
| **Dynamic content** | structure — row counts, card layouts | for data-driven tests |
|
|
181
|
+
| **Special elements** | type, selector, dimensions | for canvas/iframe/Shadow DOM test strategies |
|
|
182
|
+
|
|
183
|
+
#### 4.3.1. Detect special elements
|
|
184
|
+
|
|
185
|
+
From `browser_snapshot` + `browser_evaluate`, identify these special elements per route:
|
|
186
|
+
|
|
187
|
+
**Special element detection matrix:**
|
|
188
|
+
|
|
189
|
+
| Element | Snapshot signal | Evaluate supplement | Exploration priority |
|
|
190
|
+
| ------- | --------------- | ---------------------------------------------- | ------------------- |
|
|
191
|
+
| `<canvas>` | `role="img"`, `tagName="CANVAS"` | `canvas.getContext('2d'/'webgl')`, `width`, `height` | High |
|
|
192
|
+
| `<iframe>` | `role="iframe"`, `src` attribute | `frameLocator` available | High |
|
|
193
|
+
| Shadow DOM | `role="generic"` with no children | Check `shadowRoot` via evaluate | Medium |
|
|
194
|
+
| Rich text editor | `[contenteditable]`, `role="textbox"` | `innerHTML`, `getContent()` | Medium |
|
|
195
|
+
| Video / Audio | `role="application"` or name contains "video"/"audio" | `evaluate` checks both `<video>` and `<audio>` tags | Medium |
|
|
196
|
+
| Date picker | specific `data-testid` or class patterns | Click triggers → evaluate value | Low (skip unless specs mention) |
|
|
197
|
+
| Drag-and-drop | drag events in JS | Simulate DnD via coordinate clicks | Low |
|
|
198
|
+
| Infinite scroll | Dynamic row insertion | Count elements before/after scroll | Low |
|
|
199
|
+
| WebSocket / SSE | No DOM signal | Check `browser_console_messages` for WS events | Low |
|
|
200
|
+
|
|
201
|
+
**For each detected special element, capture:**
|
|
202
|
+
|
|
203
|
+
```javascript
|
|
204
|
+
// Canvas — get metadata (check WebGL first to avoid consuming 2D context)
|
|
205
|
+
const canvasData = await browser_evaluate(() => {
|
|
206
|
+
const c = document.querySelector('canvas');
|
|
207
|
+
if (!c) return null;
|
|
208
|
+
// getContext consumes the context — check WebGL2 first, then WebGL1, then 2D
|
|
209
|
+
let context = 'unknown';
|
|
210
|
+
if (c.getContext('webgl2')) context = 'webgl2';
|
|
211
|
+
else if (c.getContext('webgl')) context = 'webgl';
|
|
212
|
+
else if (c.getContext('2d')) context = '2d';
|
|
213
|
+
return {
|
|
214
|
+
id: c.id || '',
|
|
215
|
+
context,
|
|
216
|
+
width: c.width,
|
|
217
|
+
height: c.height,
|
|
218
|
+
};
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
// Iframe — record frameLocator
|
|
222
|
+
// Note: iframe has src or name attribute
|
|
223
|
+
|
|
224
|
+
// Rich text editor — get content
|
|
225
|
+
const editorContent = await browser_evaluate(() => {
|
|
226
|
+
const el = document.querySelector('[contenteditable]');
|
|
227
|
+
return el ? { tag: el.tagName, content: el.innerHTML, length: el.textContent.length } : null;
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
// Video / Audio — get state via evaluate (snapshot doesn't expose tagName)
|
|
231
|
+
const mediaState = await browser_evaluate(() => {
|
|
232
|
+
const v = document.querySelector('video');
|
|
233
|
+
if (v) return { type: 'video', paused: v.paused, duration: v.duration };
|
|
234
|
+
const a = document.querySelector('audio');
|
|
235
|
+
if (a) return { type: 'audio', paused: a.paused, duration: a.duration };
|
|
236
|
+
return null;
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
// contenteditable — detect via evaluate
|
|
240
|
+
const isContentEditable = await browser_evaluate(() => {
|
|
241
|
+
const el = document.querySelector('[contenteditable]');
|
|
242
|
+
return !!el;
|
|
243
|
+
});
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
Record findings in `app-exploration.md` → **Special Elements Detected** table.
|
|
187
247
|
|
|
188
248
|
#### 4.4. Write app-exploration.md
|
|
189
249
|
|
|
@@ -274,6 +334,85 @@ Template: `templates/test-plan.md`
|
|
|
274
334
|
|
|
275
335
|
**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.
|
|
276
336
|
|
|
337
|
+
**Test coverage — special elements**: Check `app-exploration.md` → **Special Elements Detected** table. For each special element:
|
|
338
|
+
|
|
339
|
+
```typescript
|
|
340
|
+
// Canvas — screenshot + dimensions
|
|
341
|
+
test('canvas renders with correct dimensions', async ({ page }) => {
|
|
342
|
+
await page.goto(`${BASE_URL}/<route>`);
|
|
343
|
+
const canvas = page.locator('canvas');
|
|
344
|
+
await expect(canvas).toBeVisible();
|
|
345
|
+
const box = await canvas.boundingBox();
|
|
346
|
+
expect(box.width).toBeGreaterThan(0);
|
|
347
|
+
await canvas.screenshot({ path: '__screenshots__/canvas.png' });
|
|
348
|
+
});
|
|
349
|
+
|
|
350
|
+
// Canvas — 2D pixel verification
|
|
351
|
+
test('canvas 2D content is not blank', async ({ page }) => {
|
|
352
|
+
await page.goto(`${BASE_URL}/<route>`);
|
|
353
|
+
const hasContent = await page.evaluate(() => {
|
|
354
|
+
const c = document.querySelector('canvas');
|
|
355
|
+
if (!c) return false;
|
|
356
|
+
const ctx = c.getContext('2d');
|
|
357
|
+
if (!ctx) return false;
|
|
358
|
+
const data = ctx.getImageData(0, 0, c.width, c.height).data;
|
|
359
|
+
return data.some((v, i) => i % 4 !== 3 && v !== 0); // non-transparent non-black pixel
|
|
360
|
+
});
|
|
361
|
+
expect(hasContent).toBe(true);
|
|
362
|
+
});
|
|
363
|
+
|
|
364
|
+
// Canvas — WebGL screenshot
|
|
365
|
+
test('canvas WebGL renders', async ({ page }) => {
|
|
366
|
+
await page.goto(`${BASE_URL}/<route>`);
|
|
367
|
+
const canvas = page.locator('canvas');
|
|
368
|
+
await expect(canvas).toBeVisible();
|
|
369
|
+
await canvas.screenshot({ path: '__screenshots__/webgl.png' });
|
|
370
|
+
// No pixel comparison — WebGL rendering may vary
|
|
371
|
+
});
|
|
372
|
+
|
|
373
|
+
// Iframe — switch context
|
|
374
|
+
test('iframe content is accessible', async ({ page }) => {
|
|
375
|
+
await page.goto(`${BASE_URL}/<route>`);
|
|
376
|
+
const frame = page.frameLocator('iframe[name="<name>"]');
|
|
377
|
+
await expect(frame.locator('<selector-inside-frame>')).toBeVisible();
|
|
378
|
+
});
|
|
379
|
+
|
|
380
|
+
// Rich text editor — evaluate content
|
|
381
|
+
test('editor content persists', async ({ page }) => {
|
|
382
|
+
await page.goto(`${BASE_URL}/<route>`);
|
|
383
|
+
const editor = page.locator('[contenteditable]');
|
|
384
|
+
await editor.click();
|
|
385
|
+
await page.keyboard.type('Hello E2E');
|
|
386
|
+
const content = await page.evaluate(() => {
|
|
387
|
+
const el = document.querySelector('[contenteditable]');
|
|
388
|
+
return el?.textContent;
|
|
389
|
+
});
|
|
390
|
+
expect(content).toContain('Hello E2E');
|
|
391
|
+
});
|
|
392
|
+
|
|
393
|
+
// Video — playback state
|
|
394
|
+
test('video can be played', async ({ page }) => {
|
|
395
|
+
await page.goto(`${BASE_URL}/<route>`);
|
|
396
|
+
const video = page.locator('video');
|
|
397
|
+
await expect(video).toBeVisible();
|
|
398
|
+
await video.evaluate((v: HTMLVideoElement) => { v.play(); });
|
|
399
|
+
const isPlaying = await video.evaluate((v: HTMLVideoElement) => !v.paused);
|
|
400
|
+
expect(isPlaying).toBe(true);
|
|
401
|
+
});
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
See `templates/test-plan.md` → **Special Element Test Cases** for full templates.
|
|
405
|
+
|
|
406
|
+
```typescript
|
|
407
|
+
// 🚫 Avoid for special elements:
|
|
408
|
+
await canvas.screenshot() // screenshot alone — no dimension/size assertion
|
|
409
|
+
await expect(canvas).toHaveScreenshot() // pixel-to-pixel comparison for WebGL
|
|
410
|
+
|
|
411
|
+
// ✅ Always:
|
|
412
|
+
const box = await canvas.boundingBox();
|
|
413
|
+
expect(box.width).toBeGreaterThan(0);
|
|
414
|
+
```
|
|
415
|
+
|
|
277
416
|
**Output format**:
|
|
278
417
|
|
|
279
418
|
- Follow `seed.spec.ts` structure
|
|
@@ -281,52 +420,110 @@ Template: `templates/test-plan.md`
|
|
|
281
420
|
- Each test: `test('描述性名称', async ({ page }) => { ... })`
|
|
282
421
|
- Prefer `data-testid` selectors (see 4.3 table)
|
|
283
422
|
|
|
423
|
+
#### 6.1. Use BasePage for shared navigation and selectors
|
|
424
|
+
|
|
425
|
+
Read `tests/playwright/pages/BasePage.ts` for shared utilities:
|
|
426
|
+
- `goto(path)` — navigation with configurable `waitUntil`
|
|
427
|
+
- `byTestId(id)`, `byRole(role, opts)`, `byLabel(label)`, `byText(text)`, `byPlaceholder(text)` — selector helpers in priority order
|
|
428
|
+
- `click(locator)`, `fill(locator, value)` — safe interactions with built-in `scrollIntoViewIfNeeded`
|
|
429
|
+
- `waitForToast(text?)`, `waitForLoad(spinnerSelector?)` — wait utilities
|
|
430
|
+
- `reload()` — page reload with hydration
|
|
431
|
+
|
|
432
|
+
**AppPage pattern** — extend BasePage for page-specific selectors:
|
|
433
|
+
|
|
434
|
+
```typescript
|
|
435
|
+
// tests/playwright/pages/LoginPage.ts
|
|
436
|
+
import { BasePage } from './BasePage';
|
|
437
|
+
import type { Page } from '@playwright/test';
|
|
438
|
+
|
|
439
|
+
export class LoginPage extends BasePage {
|
|
440
|
+
get usernameInput() { return this.byLabel('用户名'); }
|
|
441
|
+
get passwordInput() { return this.byLabel('密码'); }
|
|
442
|
+
get submitBtn() { return this.byRole('button', { name: '登录' }); }
|
|
443
|
+
|
|
444
|
+
constructor(page: Page) { super(page); }
|
|
445
|
+
|
|
446
|
+
async login(user: string, pass: string) {
|
|
447
|
+
await this.goto('/login');
|
|
448
|
+
await this.usernameInput.fill(user);
|
|
449
|
+
await this.passwordInput.fill(pass);
|
|
450
|
+
await this.submitBtn.click();
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
```
|
|
454
|
+
|
|
455
|
+
```typescript
|
|
456
|
+
// tests/playwright/<name>.spec.ts
|
|
457
|
+
import { LoginPage } from '../pages/LoginPage';
|
|
458
|
+
|
|
459
|
+
test('user can login', async ({ page }) => {
|
|
460
|
+
const loginPage = new LoginPage(page);
|
|
461
|
+
await loginPage.login('user@example.com', 'password123');
|
|
462
|
+
await loginPage.expectURL(/dashboard/);
|
|
463
|
+
});
|
|
464
|
+
```
|
|
465
|
+
|
|
466
|
+
**If a shared page object doesn't exist yet**: define it inline in the spec AND write it to `tests/playwright/pages/<PageName>.ts` so future tests can reuse it.
|
|
467
|
+
|
|
468
|
+
#### 6.2. Selector anti-patterns
|
|
469
|
+
|
|
470
|
+
```typescript
|
|
471
|
+
// 🚫 Fragile — CSS class selectors break on style refactors
|
|
472
|
+
page.locator('.notification-bell')
|
|
473
|
+
page.locator('.header-bar')
|
|
474
|
+
page.locator('.skeleton-overlay')
|
|
475
|
+
|
|
476
|
+
// ✅ Robust — semantic selectors survive style changes
|
|
477
|
+
page.getByRole('button', { name: '通知' })
|
|
478
|
+
page.getByTestId('header-bar')
|
|
479
|
+
page.getByText('加载中')
|
|
480
|
+
|
|
481
|
+
// 🚫 Fragile — CSS ID selectors can duplicate in React HMR
|
|
482
|
+
page.locator('#avatarBtn')
|
|
483
|
+
page.locator('#userAvatarBtn')
|
|
484
|
+
|
|
485
|
+
// ✅ Robust — prefer role/label/testid over CSS ID
|
|
486
|
+
page.getByTestId('user-avatar')
|
|
487
|
+
page.getByRole('button', { name: '用户菜单' })
|
|
488
|
+
|
|
489
|
+
// 🚫 Missing wait — leads to random CI failures
|
|
490
|
+
await page.locator('.submit-btn').click();
|
|
491
|
+
|
|
492
|
+
// ✅ Safe — scroll into view first
|
|
493
|
+
await page.locator('.submit-btn').scrollIntoViewIfNeeded();
|
|
494
|
+
await page.locator('.submit-btn').click();
|
|
495
|
+
|
|
496
|
+
// ✅ Better — use BasePage click with built-in wait
|
|
497
|
+
const app = new AppPage(page);
|
|
498
|
+
await app.click(app.byRole('button', { name: '提交' }));
|
|
499
|
+
```
|
|
500
|
+
|
|
284
501
|
**Code examples — UI first:**
|
|
285
502
|
|
|
286
503
|
```typescript
|
|
287
|
-
// ✅ UI 测试
|
|
504
|
+
// ✅ UI 测试
|
|
288
505
|
await page.goto(`${BASE_URL}/orders`);
|
|
289
506
|
await page.getByRole("button", { name: "新建订单" }).click();
|
|
290
507
|
await page.getByLabel("订单名称").fill("Test Order");
|
|
291
508
|
await page.getByRole("button", { name: "提交" }).click();
|
|
292
509
|
await expect(page.getByText("订单创建成功")).toBeVisible();
|
|
293
510
|
|
|
294
|
-
// ✅ Error path
|
|
511
|
+
// ✅ Error path
|
|
295
512
|
await page.goto(`${BASE_URL}/orders`);
|
|
296
|
-
await page.getByRole("button", { name: "新建订单" }).click();
|
|
297
513
|
await page.getByRole("button", { name: "提交" }).click();
|
|
298
514
|
await expect(page.getByRole("alert")).toContainText("名称不能为空");
|
|
299
515
|
|
|
300
|
-
// ✅ API fallback
|
|
516
|
+
// ✅ API fallback (only when UI cannot reach the scenario)
|
|
301
517
|
const res = await page.request.get(`${BASE_URL}/api/orders/99999`);
|
|
302
518
|
expect(res.status()).toBe(404);
|
|
303
|
-
```
|
|
304
|
-
|
|
305
|
-
```typescript
|
|
306
|
-
// 🚫 False Pass — 元素不存在时静默跳过
|
|
307
|
-
if (await btn.isVisible().catch(() => false)) { ... }
|
|
308
|
-
|
|
309
|
-
// ✅ CORRECT
|
|
310
|
-
await expect(page.getByRole('button', { name: '取消' })).toBeVisible();
|
|
311
519
|
|
|
312
|
-
//
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
// ✅ CORRECT — 通过 UI 登录
|
|
316
|
-
await page.goto(`${BASE_URL}/login`);
|
|
317
|
-
await page.getByLabel('邮箱').fill(process.env.E2E_USERNAME);
|
|
318
|
-
await page.getByLabel('密码').fill(process.env.E2E_PASSWORD);
|
|
319
|
-
await page.getByRole('button', { name: '登录' }).click();
|
|
320
|
-
await expect(page).toHaveURL(/dashboard/);
|
|
321
|
-
```
|
|
322
|
-
|
|
323
|
-
```typescript
|
|
324
|
-
// ✅ Fresh browser context for auth guard
|
|
325
|
-
test("unauthenticated user redirected to login", async ({ browser }) => {
|
|
520
|
+
// ✅ Auth guard — fresh browser context (no cookies)
|
|
521
|
+
test("redirects to login when unauthenticated", async ({ browser }) => {
|
|
326
522
|
const freshPage = await browser.newContext().newPage();
|
|
327
523
|
await freshPage.goto(`${BASE_URL}/dashboard`);
|
|
328
524
|
await expect(freshPage).toHaveURL(/login|auth/);
|
|
329
525
|
});
|
|
526
|
+
|
|
330
527
|
// ✅ Session — logout clears protected state
|
|
331
528
|
await page.getByRole("button", { name: "退出登录" }).click();
|
|
332
529
|
await expect(page).toHaveURL(/login|auth/);
|
|
@@ -334,21 +531,17 @@ const freshPage2 = await browser.newContext().newPage();
|
|
|
334
531
|
await freshPage2.goto(`${BASE_URL}/dashboard`);
|
|
335
532
|
await expect(freshPage2).toHaveURL(/login|auth/); // session revoked
|
|
336
533
|
|
|
337
|
-
// ✅ Browser history — SPA back/forward
|
|
534
|
+
// ✅ Browser history — SPA back/forward
|
|
338
535
|
await page.goto(`${BASE_URL}/list`);
|
|
339
536
|
await page.getByRole("link", { name: "详情" }).first().click();
|
|
340
537
|
await expect(page).toHaveURL(/detail/);
|
|
341
538
|
await page.goBack();
|
|
342
539
|
await expect(page).toHaveURL(/list/);
|
|
343
|
-
await page.goForward();
|
|
344
|
-
await expect(page).toHaveURL(/detail/);
|
|
345
540
|
|
|
346
|
-
// ✅ File uploads
|
|
541
|
+
// ✅ File uploads
|
|
347
542
|
await page.locator('input[type="file"]').setInputFiles("/path/to/file.pdf");
|
|
348
543
|
```
|
|
349
544
|
|
|
350
|
-
Always include error path tests: UI validation messages, network failure, invalid input. Use `page.request` only for scenarios confirmed unreachable via UI.
|
|
351
|
-
|
|
352
545
|
If the file exists → diff against test-plan, add only missing test cases.
|
|
353
546
|
|
|
354
547
|
### 7. Configure auth (if required)
|
|
@@ -434,14 +627,12 @@ If tests fail → use Playwright MCP tools to inspect UI, fix selectors, re-run.
|
|
|
434
627
|
|
|
435
628
|
### 10. False Pass Detection
|
|
436
629
|
|
|
437
|
-
Run after test suite completes (even if all pass). Common patterns
|
|
630
|
+
Run after test suite completes (even if all pass). Common patterns:
|
|
438
631
|
|
|
439
632
|
- **Conditional visibility**: `if (locator.isVisible().catch(() => false))` — if test passes, locator may not exist
|
|
440
633
|
- **Too fast**: < 200ms for a complex flow is suspicious
|
|
441
634
|
- **No fresh auth context**: Protected routes without `browser.newContext()`
|
|
442
635
|
|
|
443
|
-
Report any gaps in a **⚠️ Coverage Gap** section.
|
|
444
|
-
|
|
445
636
|
### 11. Report results
|
|
446
637
|
|
|
447
638
|
Read report at `openspec/reports/playwright-e2e-<name>-<timestamp>.md`. Present:
|
|
@@ -460,22 +651,16 @@ Reference: `templates/report.md`
|
|
|
460
651
|
|
|
461
652
|
## Graceful Degradation
|
|
462
653
|
|
|
463
|
-
| Scenario
|
|
464
|
-
|
|
|
465
|
-
| No specs (change mode)
|
|
466
|
-
|
|
|
467
|
-
|
|
|
468
|
-
| app-all
|
|
469
|
-
|
|
|
470
|
-
|
|
|
471
|
-
|
|
|
472
|
-
|
|
|
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 |
|
|
654
|
+
| Scenario | Behavior |
|
|
655
|
+
| ------- | ------- |
|
|
656
|
+
| No specs / app-exploration.md missing (change mode) | **STOP** |
|
|
657
|
+
| JS errors or HTTP 5xx during exploration | **STOP** |
|
|
658
|
+
| Sitemap fails ("all" mode) | Continue with homepage links fallback |
|
|
659
|
+
| File already exists (app-exploration, test-plan, app-all) | Read and use — never regenerate |
|
|
660
|
+
| Test fails (backend) | `test.skip()` + report |
|
|
661
|
+
| Test fails (selector/assertion) | Healer: snapshot → fix → re-run (≤3) |
|
|
662
|
+
| 3 heals failed | `test.skip()` if app bug; report if unclear |
|
|
663
|
+
| False pass detected | Add "⚠️ Coverage Gap" to report |
|
|
479
664
|
|
|
480
665
|
## Guardrails
|
|
481
666
|
|
package/dist/commands/init.js
CHANGED
|
@@ -103,6 +103,11 @@ export async function init(options) {
|
|
|
103
103
|
console.log(chalk.blue("\n─── Generating Seed Test ───"));
|
|
104
104
|
await generateSeedTest(projectRoot);
|
|
105
105
|
}
|
|
106
|
+
// 6b. Generate shared pages directory
|
|
107
|
+
if (options.seed !== false) {
|
|
108
|
+
console.log(chalk.blue("\n─── Generating Shared Pages ───"));
|
|
109
|
+
await generateSharedPages(projectRoot);
|
|
110
|
+
}
|
|
106
111
|
// 7. Generate app-knowledge.md
|
|
107
112
|
console.log(chalk.blue("\n─── Generating App Knowledge ───"));
|
|
108
113
|
await generateAppKnowledge(projectRoot);
|
|
@@ -120,12 +125,13 @@ export async function init(options) {
|
|
|
120
125
|
console.log(chalk.gray(" 2. Customize tests/playwright/credentials.yaml with your test user"));
|
|
121
126
|
console.log(chalk.gray(" 3. Set credentials: export E2E_USERNAME=xxx E2E_PASSWORD=yyy"));
|
|
122
127
|
console.log(chalk.gray(" 4. Run auth setup: npx playwright test --project=setup"));
|
|
128
|
+
console.log(chalk.gray(" 5. Page objects: extend tests/playwright/pages/BasePage.ts for shared selectors"));
|
|
123
129
|
const hasClaude = existsSync(join(projectRoot, ".claude"));
|
|
124
130
|
if (hasClaude) {
|
|
125
|
-
console.log(chalk.gray("
|
|
131
|
+
console.log(chalk.gray(" 6. In Claude Code, run: /opsx:e2e <change-name>"));
|
|
126
132
|
}
|
|
127
|
-
console.log(chalk.gray(` ${hasClaude ? "
|
|
128
|
-
console.log(chalk.gray(` ${hasClaude ? "
|
|
133
|
+
console.log(chalk.gray(` ${hasClaude ? "7." : "6."} Or: openspec-pw run <change-name>`));
|
|
134
|
+
console.log(chalk.gray(` ${hasClaude ? "8." : "7."} Or: openspec-pw doctor to verify setup\n`));
|
|
129
135
|
console.log(chalk.bold("How it works:"));
|
|
130
136
|
console.log(chalk.gray(" /opsx:e2e reads your OpenSpec specs and runs Playwright"));
|
|
131
137
|
console.log(chalk.gray(" E2E tests through a three-agent pipeline:"));
|
|
@@ -177,6 +183,20 @@ async function generateAppKnowledge(projectRoot) {
|
|
|
177
183
|
console.log(chalk.green(" ✓ Generated: tests/playwright/app-knowledge.md"));
|
|
178
184
|
}
|
|
179
185
|
}
|
|
186
|
+
async function generateSharedPages(projectRoot) {
|
|
187
|
+
const pagesDir = join(projectRoot, "tests", "playwright", "pages");
|
|
188
|
+
mkdirSync(pagesDir, { recursive: true });
|
|
189
|
+
const basePageSrc = join(TEMPLATE_DIR, "pages", "BasePage.ts");
|
|
190
|
+
const basePageDest = join(pagesDir, "BasePage.ts");
|
|
191
|
+
if (existsSync(basePageDest)) {
|
|
192
|
+
console.log(chalk.gray(" - pages/BasePage.ts already exists, skipping"));
|
|
193
|
+
}
|
|
194
|
+
else if (existsSync(basePageSrc)) {
|
|
195
|
+
writeFileSync(basePageDest, readFileSync(basePageSrc));
|
|
196
|
+
console.log(chalk.green(" ✓ Generated: tests/playwright/pages/BasePage.ts"));
|
|
197
|
+
console.log(chalk.gray(" (Extend BasePage to create page objects: pages/LoginPage.ts, etc.)"));
|
|
198
|
+
}
|
|
199
|
+
}
|
|
180
200
|
// Install SKILL reference templates (format guides for LLM to read)
|
|
181
201
|
function installSkillTemplates(projectRoot) {
|
|
182
202
|
const SKILL_DIR = join(projectRoot, ".claude", "skills", "openspec-e2e");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"init.js","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"init.js","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"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"}
|
package/dist/commands/update.js
CHANGED
|
@@ -65,6 +65,8 @@ export async function update(options) {
|
|
|
65
65
|
const skillContent = readFileSync(skillSrc, "utf-8");
|
|
66
66
|
installSkill(projectRoot, skillContent);
|
|
67
67
|
}
|
|
68
|
+
// Clean up deprecated schema installed by pre-v0.1.71 versions
|
|
69
|
+
cleanupDeprecatedSchema(projectRoot);
|
|
68
70
|
// Sync SKILL reference templates
|
|
69
71
|
syncSkillTemplates(tmpDir, projectRoot);
|
|
70
72
|
rmSync(tmpDir, { recursive: true, force: true });
|
|
@@ -133,6 +135,14 @@ export async function update(options) {
|
|
|
133
135
|
console.log(chalk.gray(" Then run openspec-pw run <change-name> to verify.\n"));
|
|
134
136
|
}
|
|
135
137
|
}
|
|
138
|
+
// Clean up deprecated openspec/schemas/playwright-e2e/ from pre-v0.1.71 versions
|
|
139
|
+
function cleanupDeprecatedSchema(projectRoot) {
|
|
140
|
+
const deprecatedSchemaPath = join(projectRoot, "openspec", "schemas", "playwright-e2e");
|
|
141
|
+
if (existsSync(deprecatedSchemaPath)) {
|
|
142
|
+
rmSync(deprecatedSchemaPath, { recursive: true, force: true });
|
|
143
|
+
console.log(chalk.green(" ✓ Cleaned up deprecated openspec/schemas/playwright-e2e/ (v0.1.71 refactor — no longer needed)"));
|
|
144
|
+
}
|
|
145
|
+
}
|
|
136
146
|
// Sync SKILL reference templates from extracted tarball to project
|
|
137
147
|
function syncSkillTemplates(tmpDir, projectRoot) {
|
|
138
148
|
if (!existsSync(join(projectRoot, ".claude")))
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"update.js","sourceRoot":"","sources":["../../src/commands/update.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"update.js","sourceRoot":"","sources":["../../src/commands/update.ts"],"names":[],"mappings":"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"}
|
package/employee-standards.md
CHANGED
package/package.json
CHANGED
|
@@ -39,6 +39,17 @@ BASE_URL: <from env or seed.spec.ts>
|
|
|
39
39
|
- <any dynamic content that was observed>
|
|
40
40
|
- <test assertions should use toContainText, not toHaveText for user-specific data>
|
|
41
41
|
|
|
42
|
+
### Special Elements Detected
|
|
43
|
+
|
|
44
|
+
| Element | Type | Context | Dimensions | Test Strategy |
|
|
45
|
+
|---------|------|---------|------------|---------------|
|
|
46
|
+
| | | | | |
|
|
47
|
+
| | | | | |
|
|
48
|
+
|
|
49
|
+
> **Special element type legend**: `canvas-2d` | `canvas-webgl` | `iframe` | `shadow-dom` | `contenteditable` | `video` | `audio` | `datepicker` | `drag-drop` | `infinite-scroll`
|
|
50
|
+
>
|
|
51
|
+
> **Test strategy**: See `templates/test-plan.md` → **Special Element Test Cases**
|
|
52
|
+
|
|
42
53
|
## Exploration Failures
|
|
43
54
|
|
|
44
55
|
| Route | Error | Notes |
|
package/templates/e2e-test.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { test, expect, Page } from '@playwright/test';
|
|
2
|
+
import { BasePage } from './pages/BasePage';
|
|
2
3
|
|
|
3
4
|
// ──────────────────────────────────────────────
|
|
4
5
|
// Test plan: <change-name>
|
|
@@ -8,24 +9,24 @@ import { test, expect, Page } from '@playwright/test';
|
|
|
8
9
|
const BASE_URL = process.env.BASE_URL || 'http://localhost:3000';
|
|
9
10
|
|
|
10
11
|
/**
|
|
11
|
-
* Page Object Pattern
|
|
12
|
+
* Page Object Pattern
|
|
13
|
+
*
|
|
14
|
+
* Extend BasePage to add page-specific selectors and actions.
|
|
15
|
+
* Example:
|
|
16
|
+
* class LoginPage extends BasePage {
|
|
17
|
+
* get usernameInput() { return this.byLabel('用户名'); }
|
|
18
|
+
* get passwordInput() { return this.byLabel('密码'); }
|
|
19
|
+
* get submitBtn() { return this.byRole('button', { name: '登录' }); }
|
|
20
|
+
* async login(user: string, pass: string) {
|
|
21
|
+
* await this.usernameInput.fill(user);
|
|
22
|
+
* await this.passwordInput.fill(pass);
|
|
23
|
+
* await this.submitBtn.click();
|
|
24
|
+
* }
|
|
25
|
+
* }
|
|
12
26
|
*/
|
|
13
|
-
class AppPage {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
async goto(path: string = '/') {
|
|
17
|
-
await this.page.goto(`${BASE_URL}${path}`);
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
async getByTestId(id: string) {
|
|
21
|
-
return this.page.locator(`[data-testid="${id}"]`);
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
async waitForToast(message?: string) {
|
|
25
|
-
if (message) {
|
|
26
|
-
await this.page.getByText(message, { state: 'visible' }).waitFor();
|
|
27
|
-
}
|
|
28
|
-
}
|
|
27
|
+
class AppPage extends BasePage {
|
|
28
|
+
// Add page-specific selectors here
|
|
29
|
+
// Example: get heading() { return this.byRole('heading'); }
|
|
29
30
|
}
|
|
30
31
|
|
|
31
32
|
function createPage(page: Page): AppPage {
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
// BasePage — shared navigation and selector utilities for all page objects
|
|
2
|
+
// Extends this class in tests/playwright/pages/<PageName>.ts to build page objects
|
|
3
|
+
// Customize: add page-specific selectors as getters or methods
|
|
4
|
+
|
|
5
|
+
import { Page, Locator, expect } from '@playwright/test';
|
|
6
|
+
|
|
7
|
+
const BASE_URL = process.env.BASE_URL || 'http://localhost:3000';
|
|
8
|
+
|
|
9
|
+
export class BasePage {
|
|
10
|
+
protected page: Page;
|
|
11
|
+
|
|
12
|
+
constructor(page: Page) {
|
|
13
|
+
this.page = page;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// ─── Navigation ──────────────────────────────────────────────────────────────
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Navigate to a path. Uses 'domcontentloaded' by default for speed.
|
|
20
|
+
* Pass { waitUntil: 'networkidle' } for SPAs that fetch data on mount.
|
|
21
|
+
*/
|
|
22
|
+
async goto(
|
|
23
|
+
path: string,
|
|
24
|
+
options?: { waitUntil?: 'domcontentloaded' | 'load' | 'networkidle' | 'commit' },
|
|
25
|
+
) {
|
|
26
|
+
const url = path.startsWith('http') ? path : `${BASE_URL}${path}`;
|
|
27
|
+
await this.page.goto(url, {
|
|
28
|
+
waitUntil: options?.waitUntil ?? 'domcontentloaded',
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// ─── Selector helpers — use in priority order ───────────────────────────────
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Best: data-testid survives style changes and text refactors.
|
|
36
|
+
* Use when the dev team adds data-testid to elements.
|
|
37
|
+
*/
|
|
38
|
+
byTestId(id: string): Locator {
|
|
39
|
+
return this.page.getByTestId(id);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Good: semantic role selectors survive DOM restructuring.
|
|
44
|
+
* Use for buttons, links, form fields, dialogs.
|
|
45
|
+
*/
|
|
46
|
+
byRole(role: Parameters<typeof this.page.getByRole>[0], options?: { name?: string | RegExp; exact?: boolean }): Locator {
|
|
47
|
+
return this.page.getByRole(role, options);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Good: label selectors are stable for form fields.
|
|
52
|
+
* Use for inputs, selects, textareas with visible labels.
|
|
53
|
+
*/
|
|
54
|
+
byLabel(label: string | RegExp, options?: { exact?: boolean }): Locator {
|
|
55
|
+
return this.page.getByLabel(label, options);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Okay: text selectors are visible to users but may break on copy changes.
|
|
60
|
+
* Use for assertions, not interactions.
|
|
61
|
+
*/
|
|
62
|
+
byText(text: string | RegExp, options?: { exact?: boolean }): Locator {
|
|
63
|
+
return this.page.getByText(text, options);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Fallback: placeholder text. Stable for form inputs.
|
|
68
|
+
*/
|
|
69
|
+
byPlaceholder(text: string | RegExp, options?: { exact?: boolean }): Locator {
|
|
70
|
+
return this.page.getByPlaceholder(text, options);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// ─── Safe interactions ──────────────────────────────────────────────────────
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Click with automatic scroll-into-view. Prevents "element not interactable" errors.
|
|
77
|
+
*/
|
|
78
|
+
async click(selector: Locator | string, options?: Parameters<Locator['click']>[0]) {
|
|
79
|
+
const el = typeof selector === 'string' ? this.page.locator(selector) : selector;
|
|
80
|
+
await el.scrollIntoViewIfNeeded();
|
|
81
|
+
await el.click(options);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Fill with automatic scroll-into-view. Clears existing value first.
|
|
86
|
+
*/
|
|
87
|
+
async fill(selector: Locator | string, value: string) {
|
|
88
|
+
const el = typeof selector === 'string' ? this.page.locator(selector) : selector;
|
|
89
|
+
await el.scrollIntoViewIfNeeded();
|
|
90
|
+
await el.fill(value);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Type with character-by-character input. Triggers keydown/keyup events.
|
|
95
|
+
* Use for editors and inputs that listen to keystroke events.
|
|
96
|
+
*/
|
|
97
|
+
async type(selector: Locator | string, text: string) {
|
|
98
|
+
const el = typeof selector === 'string' ? this.page.locator(selector) : selector;
|
|
99
|
+
await el.scrollIntoViewIfNeeded();
|
|
100
|
+
await el.click();
|
|
101
|
+
await this.page.keyboard.type(text);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// ─── Wait utilities ─────────────────────────────────────────────────────────
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Wait for a toast / snackbar / alert message to appear and optionally assert its text.
|
|
108
|
+
*/
|
|
109
|
+
async waitForToast(text?: string | RegExp, timeout = 5000): Promise<Locator> {
|
|
110
|
+
const toast = text
|
|
111
|
+
? this.page.getByText(text, { exact: false }).last()
|
|
112
|
+
: this.page.locator('[role="alert"], .toast, .snackbar, [aria-live="polite"]').last();
|
|
113
|
+
await toast.waitFor({ state: 'visible', timeout });
|
|
114
|
+
return toast;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Wait for a spinner / loading indicator to disappear.
|
|
119
|
+
* Call after navigation or async actions.
|
|
120
|
+
* Times out silently — callers should add their own assertion if needed.
|
|
121
|
+
*/
|
|
122
|
+
async waitForLoad(spinnerSelector = '[role="progressbar"], .spinner, .loading', timeout = 10000) {
|
|
123
|
+
const spinner = this.page.locator(spinnerSelector);
|
|
124
|
+
if (await spinner.isVisible().catch(() => false)) {
|
|
125
|
+
await spinner.waitFor({ state: 'hidden', timeout }).catch(() => {
|
|
126
|
+
// Spinner did not disappear within timeout — caller should assert if needed
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Wait for URL to match a pattern. Use after redirects or navigation clicks.
|
|
133
|
+
*/
|
|
134
|
+
async waitForURL(pattern: string | RegExp, timeout = 10000) {
|
|
135
|
+
await this.page.waitForURL(pattern, { timeout });
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// ─── Assertion shortcuts ───────────────────────────────────────────────────
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Assert current URL matches pattern (after navigation or redirect).
|
|
142
|
+
*/
|
|
143
|
+
async expectURL(pattern: string | RegExp) {
|
|
144
|
+
await expect(this.page).toHaveURL(pattern);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Assert page displays specific text.
|
|
149
|
+
* Note: exact option only applies to string text; RegExp is always partial match.
|
|
150
|
+
*/
|
|
151
|
+
async expectText(text: string | RegExp) {
|
|
152
|
+
await expect(this.page.getByText(text)).toBeVisible();
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// ─── Auth helpers ──────────────────────────────────────────────────────────
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Assert user is on a guest (login) page.
|
|
159
|
+
* Use after logout or to verify auth guard redirects correctly.
|
|
160
|
+
*/
|
|
161
|
+
async expectGuest() {
|
|
162
|
+
await expect(this.page.getByRole('button', { name: /login|sign in|登录/i })).toBeVisible();
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Reload page and wait for hydration (for SPAs).
|
|
167
|
+
*/
|
|
168
|
+
async reload(waitFor = 'domcontentloaded') {
|
|
169
|
+
await this.page.reload({ waitUntil: waitFor as 'domcontentloaded' });
|
|
170
|
+
}
|
|
171
|
+
}
|
package/templates/seed.spec.ts
CHANGED
|
@@ -4,25 +4,19 @@
|
|
|
4
4
|
|
|
5
5
|
import { test, expect, Page, ConsoleMessage } from '@playwright/test';
|
|
6
6
|
import { existsSync } from 'fs';
|
|
7
|
+
import { BasePage } from './pages/BasePage';
|
|
7
8
|
|
|
8
9
|
// Customize these for your application
|
|
9
10
|
const BASE_URL = process.env.BASE_URL || 'http://localhost:3000';
|
|
10
11
|
|
|
11
12
|
/**
|
|
12
|
-
* Page Object Pattern -
|
|
13
|
+
* Page Object Pattern - extends BasePage for shared utilities
|
|
14
|
+
* Add your app's common selectors here
|
|
15
|
+
* Example:
|
|
16
|
+
* get loginButton() { return this.byRole('button', { name: '登录' }); }
|
|
13
17
|
*/
|
|
14
|
-
class AppPage {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
async goto(path: string = '/') {
|
|
18
|
-
await this.page.goto(`${BASE_URL}${path}`);
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
// Add your app's common selectors here
|
|
22
|
-
// Example:
|
|
23
|
-
// async getLoginButton() {
|
|
24
|
-
// return this.page.locator('button[data-testid="login"]');
|
|
25
|
-
// }
|
|
18
|
+
class AppPage extends BasePage {
|
|
19
|
+
// Add page-specific selectors here
|
|
26
20
|
}
|
|
27
21
|
|
|
28
22
|
/**
|
package/templates/test-plan.md
CHANGED
|
@@ -22,3 +22,80 @@ Generated from: `openspec/changes/<change-name>/specs/`
|
|
|
22
22
|
|
|
23
23
|
**Error paths:**
|
|
24
24
|
- ...
|
|
25
|
+
|
|
26
|
+
## Special Element Test Cases
|
|
27
|
+
|
|
28
|
+
### Canvas — 2D Rendering
|
|
29
|
+
|
|
30
|
+
- **Route**: `/<page>`
|
|
31
|
+
- **Role**: `@role(<role>)`
|
|
32
|
+
- **Auth**: `@auth(required|none)`
|
|
33
|
+
- **Type**: `canvas-2d`
|
|
34
|
+
- **Element**: `<canvas id="...">` or `canvas`
|
|
35
|
+
|
|
36
|
+
**Test approach:**
|
|
37
|
+
1. Navigate to page
|
|
38
|
+
2. Assert canvas visible + dimensions > 0
|
|
39
|
+
3. (Optional) Screenshot for baseline archive
|
|
40
|
+
|
|
41
|
+
**Assertions:**
|
|
42
|
+
- `canvas.boundingBox().width > 0`
|
|
43
|
+
- Screenshot archived for manual review
|
|
44
|
+
|
|
45
|
+
### Canvas — WebGL Rendering
|
|
46
|
+
|
|
47
|
+
- **Route**: `/<page>`
|
|
48
|
+
- **Type**: `canvas-webgl`
|
|
49
|
+
- **Element**: `<canvas>` with WebGL context
|
|
50
|
+
|
|
51
|
+
**Test approach:**
|
|
52
|
+
1. Navigate to page
|
|
53
|
+
2. Assert canvas visible + correct dimensions
|
|
54
|
+
3. Screenshot (no pixel comparison — rendering may vary)
|
|
55
|
+
|
|
56
|
+
### Iframe — Content Accessible
|
|
57
|
+
|
|
58
|
+
- **Route**: `/<page>`
|
|
59
|
+
- **Type**: `iframe`
|
|
60
|
+
- **Element**: `<iframe name="..." src="...">`
|
|
61
|
+
|
|
62
|
+
**Test approach:**
|
|
63
|
+
1. Navigate to page
|
|
64
|
+
2. Use `frameLocator` to switch context
|
|
65
|
+
3. Assert element inside iframe is visible
|
|
66
|
+
|
|
67
|
+
### Rich Text Editor — Content Persists
|
|
68
|
+
|
|
69
|
+
- **Route**: `/<page>`
|
|
70
|
+
- **Type**: `contenteditable`
|
|
71
|
+
- **Element**: `[contenteditable]`, CodeMirror, Monaco
|
|
72
|
+
|
|
73
|
+
**Test approach:**
|
|
74
|
+
1. Navigate to page
|
|
75
|
+
2. Click editor → type content
|
|
76
|
+
3. Evaluate `textContent` or `innerHTML`
|
|
77
|
+
4. Assert content matches input
|
|
78
|
+
|
|
79
|
+
### Video — Playback Control
|
|
80
|
+
|
|
81
|
+
- **Route**: `/<page>`
|
|
82
|
+
- **Type**: `video`
|
|
83
|
+
- **Element**: `<video>`
|
|
84
|
+
|
|
85
|
+
**Test approach:**
|
|
86
|
+
1. Navigate to page
|
|
87
|
+
2. Call `video.play()` via `page.evaluate()`
|
|
88
|
+
3. Assert `!video.paused`
|
|
89
|
+
|
|
90
|
+
### Audio — Playback Control
|
|
91
|
+
|
|
92
|
+
- **Route**: `/<page>`
|
|
93
|
+
- **Type**: `audio`
|
|
94
|
+
- **Element**: `<audio>`
|
|
95
|
+
|
|
96
|
+
**Test approach:**
|
|
97
|
+
1. Navigate to page
|
|
98
|
+
2. Call `audio.play()` via `page.evaluate()`
|
|
99
|
+
3. Assert `!audio.paused`
|
|
100
|
+
|
|
101
|
+
> **Reference**: See `app-exploration.md` → **Special Elements Detected** table for per-route specifics.
|