digiqagent 1.2.0

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.
Files changed (33) hide show
  1. package/.cursor-plugin/plugin.json +29 -0
  2. package/README.md +197 -0
  3. package/agents/code-reviewer.md +48 -0
  4. package/bin/cli.js +210 -0
  5. package/package.json +41 -0
  6. package/skills/playwright-test-generator/SKILL.md +563 -0
  7. package/skills/playwright-test-generator/interaction-test-patterns.md +987 -0
  8. package/skills/playwright-test-generator/page-object-patterns.md +833 -0
  9. package/skills/postman-collection-generator/SKILL.md +310 -0
  10. package/skills/postman-collection-generator/collection-patterns.md +493 -0
  11. package/skills/postman-test-suite-generator/SKILL.md +653 -0
  12. package/skills/postman-test-suite-generator/test-scenario-patterns.md +612 -0
  13. package/skills/receiving-code-review/SKILL.md +213 -0
  14. package/skills/requesting-code-review/SKILL.md +105 -0
  15. package/skills/requesting-code-review/code-reviewer.md +146 -0
  16. package/skills/review-prompts/code-quality-reviewer-prompt.md +26 -0
  17. package/skills/review-prompts/spec-reviewer-prompt.md +61 -0
  18. package/skills/swagger-generator/SKILL.md +238 -0
  19. package/skills/swagger-generator/openapi-patterns.md +667 -0
  20. package/skills/systematic-debugging/CREATION-LOG.md +119 -0
  21. package/skills/systematic-debugging/SKILL.md +296 -0
  22. package/skills/systematic-debugging/condition-based-waiting-example.ts +158 -0
  23. package/skills/systematic-debugging/condition-based-waiting.md +115 -0
  24. package/skills/systematic-debugging/defense-in-depth.md +122 -0
  25. package/skills/systematic-debugging/find-polluter.sh +63 -0
  26. package/skills/systematic-debugging/root-cause-tracing.md +169 -0
  27. package/skills/systematic-debugging/test-academic.md +14 -0
  28. package/skills/systematic-debugging/test-pressure-1.md +58 -0
  29. package/skills/systematic-debugging/test-pressure-2.md +68 -0
  30. package/skills/systematic-debugging/test-pressure-3.md +69 -0
  31. package/skills/test-driven-development/SKILL.md +371 -0
  32. package/skills/test-driven-development/testing-anti-patterns.md +299 -0
  33. package/skills/verification-before-completion/SKILL.md +139 -0
@@ -0,0 +1,563 @@
1
+ ---
2
+ name: playwright-test-generator
3
+ description: Use when generating Playwright end-to-end test scripts for Angular or React frontend applications by analyzing components, click events, forms, routing, modals, and user interactions
4
+ ---
5
+
6
+ # Playwright Test Generator
7
+
8
+ ## Overview
9
+
10
+ Analyze a frontend codebase — Angular or React — and produce a complete suite of Playwright end-to-end tests using the Page Object Model pattern. Tests cover every route, every form, every click handler, every modal, and every interactive element with both positive and negative scenarios.
11
+
12
+ **Core principle:** If a user can interact with it, there must be a test for it. If it can fail, there must be a test proving the failure is handled.
13
+
14
+ ## When to Use
15
+
16
+ - Frontend project needs E2E test coverage
17
+ - New Angular or React application with no Playwright tests
18
+ - Existing application with incomplete or outdated E2E tests
19
+ - QA handoff — provide runnable Playwright scripts
20
+ - Pre-release verification — structured UI test pass
21
+
22
+ ## Step 0 — Detect Project Type
23
+
24
+ **This step is mandatory. Do it first, before anything else.**
25
+
26
+ Examine the project root to determine the frontend framework:
27
+
28
+ | Signal | Framework |
29
+ |--------|-----------|
30
+ | `angular.json` or `nx.json` with Angular projects | Angular |
31
+ | `@angular/core` in `package.json` dependencies | Angular |
32
+ | `react` and `react-dom` in `package.json` dependencies | React |
33
+ | `next` in `package.json` dependencies | React (Next.js) |
34
+ | `gatsby` in `package.json` dependencies | React (Gatsby) |
35
+ | `.jsx` / `.tsx` files containing JSX syntax | React |
36
+ | `*.component.ts` + `*.component.html` file pairs | Angular |
37
+
38
+ Also detect the **component library** in use — this determines selectors:
39
+
40
+ | Library | Framework | Selector Patterns |
41
+ |---------|-----------|-------------------|
42
+ | `@angular/material` | Angular | `mat-input`, `mat-select`, `mat-dialog-container`, `mat-table`, `mat-paginator`, `mat-tab-group` |
43
+ | `ng-zorro-antd` | Angular | `nz-input`, `nz-select`, `nz-modal`, `nz-table`, `nz-pagination` |
44
+ | `primeng` | Angular | `p-inputtext`, `p-dropdown`, `p-dialog`, `p-table`, `p-paginator` |
45
+ | `@mui/material` | React | `MuiTextField`, `MuiSelect`, `MuiDialog`, `MuiDataGrid`, `MuiPagination` |
46
+ | `antd` | React | `ant-input`, `ant-select`, `ant-modal`, `ant-table`, `ant-pagination` |
47
+ | `@chakra-ui/react` | React | Chakra uses standard HTML with `data-*` attrs |
48
+ | None / custom | Either | Standard HTML elements |
49
+
50
+ **If the project is not Angular or React, STOP.** Inform the developer this skill supports Angular and React only. Suggest alternatives for Vue, Svelte, etc.
51
+
52
+ ## Step 1 — Map Pages and Routes
53
+
54
+ Build a complete sitemap from the routing configuration.
55
+
56
+ ### Angular
57
+
58
+ Scan for routing modules:
59
+ - `app-routing.module.ts` or `app.routes.ts` (standalone)
60
+ - Lazy-loaded feature routing modules
61
+ - `RouterModule.forRoot(...)` and `RouterModule.forChild(...)`
62
+ - Route guards (`canActivate`, `canDeactivate`) — note which routes are protected
63
+
64
+ Extract:
65
+ ```
66
+ / → HomeComponent (public)
67
+ /login → LoginComponent (public)
68
+ /dashboard → DashboardComponent (auth guard)
69
+ /users → UserListComponent (auth guard)
70
+ /users/:id → UserDetailComponent (auth guard)
71
+ /users/new → UserFormComponent (auth + role guard)
72
+ /settings → SettingsComponent (auth guard)
73
+ ```
74
+
75
+ ### React
76
+
77
+ Scan for route definitions:
78
+ - `<Route>` elements in React Router (`react-router-dom`)
79
+ - `<BrowserRouter>`, `<Routes>`, `<Route>`
80
+ - Next.js: `app/` directory structure (App Router) or `pages/` directory (Pages Router)
81
+ - Protected route wrapper components
82
+ - `useNavigate`, `useLocation`, `useParams` usage
83
+
84
+ Extract the same route → component → guard mapping.
85
+
86
+ ## Step 2 — Discover Components and Interactions
87
+
88
+ For each page/route, scan its component tree and catalog every interactive element.
89
+
90
+ ### Angular Component Scanning
91
+
92
+ Scan `*.component.html` templates and `*.component.ts` class files:
93
+
94
+ | Template Pattern | Interaction Type | Test Action |
95
+ |-----------------|-----------------|-------------|
96
+ | `(click)="handler()"` | Click event | `click()` |
97
+ | `(dblclick)="handler()"` | Double-click | `dblclick()` |
98
+ | `(submit)="handler()"` | Form submission | Fill + submit |
99
+ | `[(ngModel)]="field"` | Two-way binding input | `fill()` |
100
+ | `[formGroup]` / `formControlName` | Reactive form field | `fill()` + validation |
101
+ | `routerLink="/path"` | Navigation link | `click()` + assert URL |
102
+ | `[routerLink]="['/path', id]"` | Dynamic navigation | `click()` + assert URL |
103
+ | `*ngIf="condition"` | Conditional display | Assert visible/hidden |
104
+ | `*ngFor="let item of items"` | List rendering | Assert count, content |
105
+ | `<mat-dialog>` / `MatDialog.open()` | Modal dialog | Open, verify, close |
106
+ | `<mat-menu>` | Dropdown menu | Open, select, close |
107
+ | `<mat-tab-group>` | Tab navigation | Click tabs, verify content |
108
+ | `<mat-table>` | Data table | Sort, paginate, row actions |
109
+ | `<mat-select>` | Select dropdown | Open, pick option |
110
+ | `<mat-checkbox>` / `<mat-radio>` | Toggle inputs | Check/uncheck |
111
+ | `<mat-datepicker>` | Date picker | Open, select date |
112
+ | `<input type="file">` | File upload | Set input files |
113
+ | `<mat-snack-bar>` / toast | Notification | Assert appears + content |
114
+
115
+ ### React Component Scanning
116
+
117
+ Scan `*.tsx` / `*.jsx` files:
118
+
119
+ | Code Pattern | Interaction Type | Test Action |
120
+ |-------------|-----------------|-------------|
121
+ | `onClick={handler}` | Click event | `click()` |
122
+ | `onDoubleClick={handler}` | Double-click | `dblclick()` |
123
+ | `onSubmit={handler}` | Form submission | Fill + submit |
124
+ | `onChange={handler}` on `<input>` | Input change | `fill()` |
125
+ | `useForm()` / `<Formik>` / `react-hook-form` | Form with validation | Fill + validate |
126
+ | `<Link to="/path">` | Navigation link | `click()` + assert URL |
127
+ | `useNavigate()` | Programmatic navigation | Trigger + assert URL |
128
+ | `useState` controlling modal/drawer | Modal/drawer toggle | Open, verify, close |
129
+ | `<Dialog>` / `<Modal>` | Modal dialog | Open, verify, close |
130
+ | `<Drawer>` | Side drawer | Open, verify, close |
131
+ | `<Tabs>` / `<Tab>` | Tab navigation | Click tabs, verify content |
132
+ | `<Select>` / `<Autocomplete>` | Select dropdown | Open, pick option |
133
+ | `<Checkbox>` / `<Switch>` | Toggle inputs | Check/uncheck |
134
+ | `<DataGrid>` / `<Table>` | Data table | Sort, paginate, row actions |
135
+ | `<input type="file">` | File upload | Set input files |
136
+ | `<Snackbar>` / `toast()` | Notification | Assert appears + content |
137
+ | `{condition && <Component>}` | Conditional render | Assert visible/hidden |
138
+ | `{items.map(item => ...)}` | List rendering | Assert count, content |
139
+
140
+ ### Interaction Catalog Output
141
+
142
+ For each component, produce a structured list:
143
+
144
+ ```
145
+ Page: /users
146
+ Component: UserListComponent
147
+ Interactions:
148
+ - CLICK: "Add User" button → navigates to /users/new
149
+ - CLICK: Edit icon per row → navigates to /users/:id
150
+ - CLICK: Delete icon per row → opens confirm dialog
151
+ - TABLE: sortable columns (Name, Email, Created)
152
+ - TABLE: pagination (page size selector, next/prev)
153
+ - SEARCH: filter input → filters table rows
154
+ - MODAL: delete confirmation → confirm/cancel
155
+ - EMPTY STATE: "No users found" when list empty
156
+ ```
157
+
158
+ ## Step 3 — Build Test Plan Per Page
159
+
160
+ For each page, define positive and negative scenarios.
161
+
162
+ ### Positive Scenarios (Happy Path)
163
+
164
+ Every page must have:
165
+ - **Page load** — correct URL, title, key elements visible
166
+ - **Content display** — expected text, images, data rendered
167
+ - **Navigation** — links lead to correct destinations
168
+ - **Forms** — valid data submission succeeds, success feedback shown
169
+ - **Tables** — data displayed, sorting works, pagination works
170
+ - **Modals** — open with correct content, actions execute, close properly
171
+ - **Search/filter** — produces expected filtered results
172
+
173
+ ### Negative Scenarios (Error/Edge Cases)
174
+
175
+ Every page with forms must have:
176
+ - **Empty required fields** — submit with each required field blank, one at a time, verify field-specific error
177
+ - **Invalid format** — wrong email format, phone format, date format
178
+ - **Boundary values** — too short (min length), too long (max length), out of range numbers
179
+ - **Special characters** — SQL injection attempts, XSS strings, Unicode edge cases
180
+ - **Double submit** — rapid double-click on submit button doesn't create duplicates
181
+
182
+ Every page with auth requirements must have:
183
+ - **Unauthenticated access** — redirect to login
184
+ - **Insufficient permissions** — forbidden message or hidden elements
185
+
186
+ Every page with data must have:
187
+ - **Empty state** — no data displays appropriate message/illustration
188
+ - **Error state** — API failure shows error message, retry option
189
+ - **Loading state** — skeleton/spinner during data fetch
190
+
191
+ ## Step 4 — Generate Playwright Test Files
192
+
193
+ ### File Structure
194
+
195
+ ```
196
+ e2e/
197
+ playwright.config.ts
198
+ pages/
199
+ base.page.ts
200
+ login.page.ts
201
+ dashboard.page.ts
202
+ user-list.page.ts
203
+ user-form.page.ts
204
+ tests/
205
+ auth/
206
+ login.spec.ts
207
+ logout.spec.ts
208
+ users/
209
+ user-list.spec.ts
210
+ user-form.spec.ts
211
+ user-delete.spec.ts
212
+ navigation/
213
+ routing.spec.ts
214
+ protected-routes.spec.ts
215
+ responsive/
216
+ mobile-layout.spec.ts
217
+ tablet-layout.spec.ts
218
+ accessibility/
219
+ keyboard-navigation.spec.ts
220
+ aria-labels.spec.ts
221
+ fixtures/
222
+ test-data.ts
223
+ auth.setup.ts
224
+ ```
225
+
226
+ ### Naming Conventions
227
+
228
+ | Element | Convention | Example |
229
+ |---------|-----------|---------|
230
+ | Test files | `kebab-case.spec.ts` | `user-list.spec.ts` |
231
+ | Page objects | `kebab-case.page.ts` | `user-list.page.ts` |
232
+ | POM class | PascalCase + `Page` | `UserListPage` |
233
+ | Test describe | Feature name | `'User List'` |
234
+ | Test name | Action + expected result | `'should display validation error when email is empty'` |
235
+ | Fixtures | `kebab-case.ts` | `test-data.ts` |
236
+
237
+ ### Locator Strategy
238
+
239
+ Use locators in this priority order. Never skip to CSS selectors without trying higher-priority options:
240
+
241
+ | Priority | Method | When to Use | Example |
242
+ |----------|--------|-------------|---------|
243
+ | 1 | `data-testid` | Best — stable, decoupled from UI | `page.getByTestId('submit-btn')` |
244
+ | 2 | Role + name | Accessible elements | `page.getByRole('button', { name: 'Submit' })` |
245
+ | 3 | Label | Form inputs | `page.getByLabel('Email')` |
246
+ | 4 | Placeholder | Inputs without labels | `page.getByPlaceholder('Enter email')` |
247
+ | 5 | Text | Static text content | `page.getByText('Welcome back')` |
248
+ | 6 | CSS selector | Last resort | `page.locator('.custom-widget')` |
249
+
250
+ **If the codebase lacks `data-testid` attributes**, note this in the output and recommend adding them. Generate tests using role/label/text locators, but include a comment suggesting `data-testid` for stability.
251
+
252
+ ### Positive Test Example
253
+
254
+ ```typescript
255
+ import { test, expect } from '@playwright/test';
256
+ import { LoginPage } from '../pages/login.page';
257
+
258
+ test.describe('Login', () => {
259
+ let loginPage: LoginPage;
260
+
261
+ test.beforeEach(async ({ page }) => {
262
+ loginPage = new LoginPage(page);
263
+ await loginPage.goto();
264
+ });
265
+
266
+ test('should login with valid credentials', async ({ page }) => {
267
+ await loginPage.fillEmail('admin@example.com');
268
+ await loginPage.fillPassword('SecureP@ss123');
269
+ await loginPage.clickSubmit();
270
+
271
+ await expect(page).toHaveURL(/\/dashboard/);
272
+ await expect(page.getByRole('heading', { name: /dashboard/i })).toBeVisible();
273
+ });
274
+
275
+ test('should display welcome message after login', async ({ page }) => {
276
+ await loginPage.login('admin@example.com', 'SecureP@ss123');
277
+ await expect(page.getByText(/welcome/i)).toBeVisible();
278
+ });
279
+ });
280
+ ```
281
+
282
+ ### Negative Test Example
283
+
284
+ ```typescript
285
+ test.describe('Login - Validation', () => {
286
+ test('should show error when email is empty', async ({ page }) => {
287
+ const loginPage = new LoginPage(page);
288
+ await loginPage.goto();
289
+ await loginPage.fillPassword('SecureP@ss123');
290
+ await loginPage.clickSubmit();
291
+
292
+ await expect(loginPage.emailError).toBeVisible();
293
+ await expect(loginPage.emailError).toContainText(/required|email/i);
294
+ });
295
+
296
+ test('should show error for invalid email format', async ({ page }) => {
297
+ const loginPage = new LoginPage(page);
298
+ await loginPage.goto();
299
+ await loginPage.fillEmail('not-an-email');
300
+ await loginPage.fillPassword('SecureP@ss123');
301
+ await loginPage.clickSubmit();
302
+
303
+ await expect(loginPage.emailError).toBeVisible();
304
+ await expect(loginPage.emailError).toContainText(/valid|format|email/i);
305
+ });
306
+
307
+ test('should show error for incorrect credentials', async ({ page }) => {
308
+ const loginPage = new LoginPage(page);
309
+ await loginPage.goto();
310
+ await loginPage.login('wrong@example.com', 'WrongPass123');
311
+
312
+ await expect(loginPage.generalError).toBeVisible();
313
+ await expect(loginPage.generalError).toContainText(/invalid|incorrect|failed/i);
314
+ });
315
+
316
+ test('should not submit form when required fields are empty', async ({ page }) => {
317
+ const loginPage = new LoginPage(page);
318
+ await loginPage.goto();
319
+ await loginPage.clickSubmit();
320
+
321
+ await expect(page).toHaveURL(/\/login/);
322
+ await expect(loginPage.emailError).toBeVisible();
323
+ await expect(loginPage.passwordError).toBeVisible();
324
+ });
325
+ });
326
+ ```
327
+
328
+ ### Responsive Test Example
329
+
330
+ ```typescript
331
+ import { test, expect, devices } from '@playwright/test';
332
+
333
+ test.describe('Responsive - Mobile', () => {
334
+ test.use({ ...devices['iPhone 13'] });
335
+
336
+ test('should show hamburger menu on mobile', async ({ page }) => {
337
+ await page.goto('/');
338
+ await expect(page.getByRole('button', { name: /menu/i })).toBeVisible();
339
+ await expect(page.getByRole('navigation')).not.toBeVisible();
340
+ });
341
+
342
+ test('should open sidebar on hamburger click', async ({ page }) => {
343
+ await page.goto('/');
344
+ await page.getByRole('button', { name: /menu/i }).click();
345
+ await expect(page.getByRole('navigation')).toBeVisible();
346
+ });
347
+ });
348
+
349
+ test.describe('Responsive - Desktop', () => {
350
+ test.use({ viewport: { width: 1440, height: 900 } });
351
+
352
+ test('should show sidebar navigation on desktop', async ({ page }) => {
353
+ await page.goto('/dashboard');
354
+ await expect(page.getByRole('navigation')).toBeVisible();
355
+ });
356
+ });
357
+ ```
358
+
359
+ ### Accessibility Test Example
360
+
361
+ ```typescript
362
+ import { test, expect } from '@playwright/test';
363
+ import AxeBuilder from '@axe-core/playwright';
364
+
365
+ test.describe('Accessibility', () => {
366
+ test('login page has no accessibility violations', async ({ page }) => {
367
+ await page.goto('/login');
368
+ const results = await new AxeBuilder({ page }).analyze();
369
+ expect(results.violations).toEqual([]);
370
+ });
371
+
372
+ test('form inputs are keyboard navigable', async ({ page }) => {
373
+ await page.goto('/login');
374
+ await page.keyboard.press('Tab');
375
+ await expect(page.getByLabel('Email')).toBeFocused();
376
+ await page.keyboard.press('Tab');
377
+ await expect(page.getByLabel('Password')).toBeFocused();
378
+ await page.keyboard.press('Tab');
379
+ await expect(page.getByRole('button', { name: /submit|login/i })).toBeFocused();
380
+ });
381
+
382
+ test('modal can be closed with Escape key', async ({ page }) => {
383
+ await page.goto('/users');
384
+ await page.getByRole('button', { name: /delete/i }).first().click();
385
+ await expect(page.getByRole('dialog')).toBeVisible();
386
+ await page.keyboard.press('Escape');
387
+ await expect(page.getByRole('dialog')).not.toBeVisible();
388
+ });
389
+ });
390
+ ```
391
+
392
+ ## Step 5 — Generate Page Objects
393
+
394
+ Create a Page Object Model class for every page. See @page-object-patterns.md for complete patterns.
395
+
396
+ ### Base Page
397
+
398
+ Every POM extends a base class:
399
+
400
+ ```typescript
401
+ import { Page, Locator } from '@playwright/test';
402
+
403
+ export class BasePage {
404
+ constructor(protected page: Page) {}
405
+
406
+ async goto(path: string) {
407
+ await this.page.goto(path);
408
+ }
409
+
410
+ async getTitle(): Promise<string> {
411
+ return this.page.title();
412
+ }
413
+
414
+ get loadingSpinner(): Locator {
415
+ return this.page.getByRole('progressbar');
416
+ }
417
+
418
+ async waitForLoad() {
419
+ await this.loadingSpinner.waitFor({ state: 'hidden', timeout: 10000 });
420
+ }
421
+
422
+ get toastMessage(): Locator {
423
+ return this.page.getByRole('alert');
424
+ }
425
+ }
426
+ ```
427
+
428
+ ### Page Object Rules
429
+
430
+ - One POM per page/route
431
+ - Locators as getter properties (not methods)
432
+ - Actions as async methods (fill, click, submit)
433
+ - Assertions stay in test files, not in POMs
434
+ - Composite actions for common flows (e.g., `login(email, password)`)
435
+ - No test logic in POMs — they are pure page interaction wrappers
436
+
437
+ ## Step 6 — Generate Playwright Config
438
+
439
+ If `playwright.config.ts` does not already exist, generate one:
440
+
441
+ ```typescript
442
+ import { defineConfig, devices } from '@playwright/test';
443
+
444
+ export default defineConfig({
445
+ testDir: './e2e/tests',
446
+ fullyParallel: true,
447
+ forbidOnly: !!process.env.CI,
448
+ retries: process.env.CI ? 2 : 0,
449
+ workers: process.env.CI ? 1 : undefined,
450
+ reporter: [
451
+ ['html', { open: 'never' }],
452
+ ['list']
453
+ ],
454
+ use: {
455
+ baseURL: 'http://localhost:4200', // Angular default; use 3000 for React
456
+ trace: 'on-first-retry',
457
+ screenshot: 'only-on-failure',
458
+ video: 'retain-on-failure',
459
+ },
460
+ projects: [
461
+ {
462
+ name: 'chromium',
463
+ use: { ...devices['Desktop Chrome'] },
464
+ },
465
+ {
466
+ name: 'firefox',
467
+ use: { ...devices['Desktop Firefox'] },
468
+ },
469
+ {
470
+ name: 'webkit',
471
+ use: { ...devices['Desktop Safari'] },
472
+ },
473
+ {
474
+ name: 'mobile-chrome',
475
+ use: { ...devices['Pixel 5'] },
476
+ },
477
+ {
478
+ name: 'mobile-safari',
479
+ use: { ...devices['iPhone 13'] },
480
+ },
481
+ ],
482
+ webServer: {
483
+ command: 'npm start', // Adjust per project
484
+ url: 'http://localhost:4200',
485
+ reuseExistingServer: !process.env.CI,
486
+ },
487
+ });
488
+ ```
489
+
490
+ Adjust `baseURL`, `webServer.command`, and `webServer.url` based on the detected framework:
491
+ - **Angular:** port `4200`, command `ng serve`
492
+ - **React (CRA):** port `3000`, command `react-scripts start`
493
+ - **React (Vite):** port `5173`, command `vite`
494
+ - **Next.js:** port `3000`, command `next dev`
495
+
496
+ ## Step 7 — Verify
497
+
498
+ Before claiming test generation is complete:
499
+
500
+ ### Coverage Verification
501
+
502
+ - [ ] Every route from the router config has at least one test file
503
+ - [ ] Every form has positive tests (valid submission) and negative tests (each validation rule)
504
+ - [ ] Every click handler has a test verifying the expected outcome
505
+ - [ ] Every modal/dialog has open, content verification, and close tests (button, overlay, Escape)
506
+ - [ ] Every table has sort, paginate, and empty state tests
507
+ - [ ] Every dropdown/select has open, select option, and close tests
508
+ - [ ] Auth-protected routes have unauthenticated redirect tests
509
+ - [ ] Role-based features have permission tests
510
+
511
+ ### Quality Verification
512
+
513
+ - [ ] No hardcoded `waitForTimeout()` calls — use proper Playwright auto-waiting
514
+ - [ ] No flaky CSS-only selectors when role/label/testid alternatives exist
515
+ - [ ] Every `test()` has at least one `expect()` assertion
516
+ - [ ] Page objects cover all page interactions (no raw locators in test files)
517
+ - [ ] Test names describe the scenario: `should [action] when [condition]`
518
+ - [ ] Test data uses realistic values, not `test` or `foo`
519
+
520
+ ### Structural Verification
521
+
522
+ - [ ] `playwright.config.ts` generated with correct baseURL and webServer
523
+ - [ ] Page objects follow the base class pattern
524
+ - [ ] Tests organized by feature in subdirectories
525
+ - [ ] Responsive tests use device presets, not arbitrary viewport sizes
526
+ - [ ] Accessibility tests use `@axe-core/playwright` for automated checks
527
+
528
+ Apply `digiqagent:verification-before-completion` — evidence before claims.
529
+
530
+ ## Red Flags — STOP and Fix
531
+
532
+ | Problem | Fix |
533
+ |---------|-----|
534
+ | Using `page.waitForTimeout(5000)` | Replace with `await expect(locator).toBeVisible()` or proper auto-wait |
535
+ | Using `page.locator('#app > div:nth-child(3) > button')` | Use `page.getByRole('button', { name: '...' })` or `data-testid` |
536
+ | Test has no assertions | Add `expect()` calls — a test without assertions proves nothing |
537
+ | Assertions in page objects | Move assertions to test files; POMs are interaction wrappers only |
538
+ | All tests in one massive file | Split by feature/page into separate `.spec.ts` files |
539
+ | Form has only positive tests | Add negative tests for every validation rule |
540
+ | Auth routes have no guard tests | Add unauthenticated redirect test |
541
+ | No responsive tests | Add mobile/tablet viewport tests for layout changes |
542
+ | No accessibility tests | Add keyboard navigation and `@axe-core` tests |
543
+ | Hardcoded test data in tests | Extract to fixtures or `test-data.ts` |
544
+ | Using `page.evaluate()` for simple interactions | Use Playwright's built-in actions (`click`, `fill`, `check`) |
545
+ | Tests depend on execution order | Each test should be independent; use `beforeEach` for setup |
546
+ | No POM — raw locators everywhere | Generate page objects; tests should read like user stories |
547
+
548
+ ## Common Patterns
549
+
550
+ See @page-object-patterns.md for Page Object Model reference:
551
+ - Angular Material component POMs
552
+ - React MUI component POMs
553
+ - Login, form, table, and modal POM examples
554
+
555
+ See @interaction-test-patterns.md for test code patterns:
556
+ - Forms, clicks, navigation, modals, dropdowns, tables
557
+ - Responsive, accessibility, auth flows, error handling
558
+
559
+ ## Integration with Other Skills
560
+
561
+ - Use `digiqagent:verification-before-completion` before claiming tests are complete
562
+ - Use `digiqagent:test-driven-development` if writing tests for new features being built
563
+ - Use `digiqagent:systematic-debugging` when Playwright tests fail unexpectedly