playwright-e2e-mcp 0.3.2 → 0.4.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.
- package/README.md +77 -19
- package/dist/cli/init.d.ts +1 -1
- package/dist/cli/init.js.map +1 -1
- package/dist/http.d.ts +1 -1
- package/dist/http.js +1 -1
- package/dist/http.js.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/server.d.ts +2 -2
- package/dist/server.js +16 -3
- package/dist/server.js.map +1 -1
- package/dist/tools/analyze-history.d.ts +3 -3
- package/dist/tools/audit-accessibility.d.ts +184 -0
- package/dist/tools/audit-accessibility.js +162 -0
- package/dist/tools/audit-accessibility.js.map +1 -0
- package/dist/tools/compare-visual-state.d.ts +39 -39
- package/dist/tools/diagnose-flaky.d.ts +38 -0
- package/dist/tools/diagnose-flaky.js +196 -1
- package/dist/tools/diagnose-flaky.js.map +1 -1
- package/dist/tools/inspect-page.d.ts +38 -38
- package/dist/tools/probe-script.d.ts +2 -0
- package/dist/tools/probe-script.js +50 -0
- package/dist/tools/probe-script.js.map +1 -1
- package/dist/tools/run-test.d.ts +14 -1
- package/dist/tools/run-test.js +51 -4
- package/dist/tools/run-test.js.map +1 -1
- package/dist/tools/shared.d.ts +38 -11
- package/dist/tools/shared.js.map +1 -1
- package/dist/tools/suggest-fix.d.ts +13 -13
- package/dist/tools/validate-selector.d.ts +21 -21
- package/dist/types/index.d.ts +5 -0
- package/dist/types/index.js.map +1 -1
- package/dist/utils/change-analyzer.js.map +1 -1
- package/dist/utils/image-diff.js.map +1 -1
- package/dist/utils/locator-expr.d.ts +1 -1
- package/dist/utils/locator-expr.js.map +1 -1
- package/dist/utils/logger.js.map +1 -1
- package/dist/utils/playwright-runner.js +6 -0
- package/dist/utils/playwright-runner.js.map +1 -1
- package/dist/utils/project-detector.js.map +1 -1
- package/dist/utils/trace-reader.js +6 -1
- package/dist/utils/trace-reader.js.map +1 -1
- package/package.json +13 -6
- package/skill/playwright-e2e-mcp/SKILL.md +41 -7
- package/skill/playwright-e2e-mcp/references/setup.md +5 -3
- package/skill/playwright-e2e-mcp/references/tools.md +26 -1
- package/skill/playwright-e2e-mcp/references/writing-tests.md +170 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: playwright-e2e-mcp
|
|
3
|
-
description: Runs, debugs and fixes Playwright end-to-end tests through the playwright-e2e-mcp server, using structured run results, trace-based failure diagnostics, verified locators, self-healing fixes that re-run and revert if still red, per-test flaky verdicts that separate parallelism
|
|
3
|
+
description: Runs, debugs and fixes Playwright end-to-end tests through the playwright-e2e-mcp server, using structured run results grouped by root cause, trace-based failure diagnostics, verified locators, self-healing fixes that re-run and revert if still red, per-test flaky verdicts that separate parallelism, test-order and timing bugs, signed-in page inspection, WCAG accessibility audits and local run history, plus a guide to writing tests that stay green. Use when the user asks to run Playwright tests, fix a failing or broken e2e test, find out why a test is flaky or which tests are unstable, repair a selector that no longer matches, write or review a Playwright test, check a page for accessibility or visual regressions.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Playwright E2E with playwright-e2e-mcp
|
|
@@ -13,22 +13,26 @@ and concrete next steps. It can also propose and apply a verified fix for a brok
|
|
|
13
13
|
locator, inspect pages behind a login, run long suites in the background and keep
|
|
14
14
|
a local history of every run.
|
|
15
15
|
|
|
16
|
-
This skill is written for playwright-e2e-mcp 0.
|
|
16
|
+
This skill is written for playwright-e2e-mcp 0.4.0 and works with 0.2.0 or
|
|
17
17
|
newer. If `suggest-fix`, `get-run-status` or `analyze-history` is missing, the
|
|
18
18
|
server is older than 0.2.0: use the steps that do not need those tools and tell
|
|
19
19
|
the user to update it. `diagnose-flaky`'s per-test table and `isolate` option
|
|
20
|
-
arrived in 0.3.0
|
|
20
|
+
arrived in 0.3.0; `audit-accessibility`, `diagnose-flaky`'s `order` option and
|
|
21
|
+
root-cause grouping in `run-test` arrived in 0.4.0.
|
|
21
22
|
|
|
22
23
|
## Before you start
|
|
23
24
|
|
|
24
25
|
1. Check that the server's tools are available. They are named `run-test`,
|
|
25
26
|
`get-run-status`, `get-failure`, `suggest-fix`, `inspect-page`,
|
|
26
27
|
`validate-selector`, `list-tests`, `generate-e2e-test`,
|
|
27
|
-
`compare-visual-state`, `diagnose-flaky` and
|
|
28
|
+
`compare-visual-state`, `audit-accessibility`, `diagnose-flaky` and
|
|
29
|
+
`analyze-history` (your client
|
|
28
30
|
may prefix them, e.g. `mcp__playwright-e2e__run-test`).
|
|
29
31
|
2. If they are all missing, stop and tell the user the MCP server is not
|
|
30
32
|
connected. Point them to `references/setup.md` for the one-line install for
|
|
31
|
-
their client
|
|
33
|
+
their client (`npx -y playwright-e2e-mcp init` sets up every client the
|
|
34
|
+
project uses). Writing or reviewing tests by hand still works: follow
|
|
35
|
+
`references/writing-tests.md`. Do not fall back to shelling out to `npx playwright test` unless
|
|
32
36
|
the user asks. If only some are missing, the server may run a smaller tool
|
|
33
37
|
profile (`--tools=core` exposes `run-test`, `get-run-status`, `get-failure`,
|
|
34
38
|
`suggest-fix`, `diagnose-flaky` and `list-tests`); work with what is there and
|
|
@@ -49,7 +53,10 @@ Follow these steps in order. Do not edit code before step 3.
|
|
|
49
53
|
Omit `testFiles` only when the user asks for the whole suite. Failing tests are
|
|
50
54
|
retried once by default, so a test that passes on retry is reported as
|
|
51
55
|
**flaky**, not failed. For a long suite pass `background: true` and poll
|
|
52
|
-
`get-run-status` with `waitSeconds: 30`.
|
|
56
|
+
`get-run-status` with `waitSeconds: 30`. When several tests fail the same
|
|
57
|
+
way, the result says how many distinct causes there are and shows each
|
|
58
|
+
cause once: fix the cause, not each test (a dead server or one changed
|
|
59
|
+
shared component often fails dozens of tests).
|
|
53
60
|
2. **Read the failure.** For each failure, call `get-failure` with its 1-based
|
|
54
61
|
`index`. Read the message and code frame, expected vs actual, the failure kind,
|
|
55
62
|
the DOM snapshot at failure, the failed action and its selector, failed network
|
|
@@ -138,6 +145,20 @@ failing ones with one worker, and reads each one:
|
|
|
138
145
|
- **flaky even alone**: the race is inside the test or the app.
|
|
139
146
|
- **fails alone too**: a real bug, not parallelism.
|
|
140
147
|
|
|
148
|
+
When a test fails in the full suite but `diagnose-flaky` on that test alone says
|
|
149
|
+
**NOT REPRODUCING**, suspect test order: call it again with `order: true` and
|
|
150
|
+
the spec file(s) in `testFiles` (Playwright 1.64+). It runs the files in random
|
|
151
|
+
orders with one worker, replays a failing order, and reads each test:
|
|
152
|
+
|
|
153
|
+
- **order dependent**: fails after certain tests and fails again on replay. The
|
|
154
|
+
result names the test it **relies on** (state it needs another test to create)
|
|
155
|
+
or is **broken by** (state another test leaves behind), and the
|
|
156
|
+
`--shuffle=<seed>` command that reproduces it. Fix it by giving the test its
|
|
157
|
+
own setup (`beforeEach`, a fixture, its own data) and cleaning up in the test
|
|
158
|
+
that leaks; do not pin the file to serial mode.
|
|
159
|
+
- **flaky regardless of order**: the failing order passed on replay: timing.
|
|
160
|
+
- **fails in every order**: a real bug.
|
|
161
|
+
|
|
141
162
|
Pass `separateProcesses: true` only when the flake seems tied to global setup or
|
|
142
163
|
a cold start, since one process per run is slower. `get-failure` works on the
|
|
143
164
|
last failing diagnose run straight after, and each repetition is recorded in the
|
|
@@ -158,9 +179,20 @@ pass/fail timeline.
|
|
|
158
179
|
file it writes, then `run-test` it. Pass `write: false` to preview without
|
|
159
180
|
writing.
|
|
160
181
|
- **Set up CI:** when the user wants the same diagnosis on every pull request,
|
|
161
|
-
point them to the GitHub Action (`uses: xsignon-secure/E2E@v0.
|
|
182
|
+
point them to the GitHub Action (`uses: xsignon-secure/E2E@v0.4.0`) or the
|
|
162
183
|
`npx -y playwright-e2e-mcp ci` command in `references/setup.md`. It writes the
|
|
163
184
|
run, flaky verdicts and fix diffs to the job summary and one PR comment.
|
|
185
|
+
- **Accessibility:** `audit-accessibility` with `url` runs axe-core against
|
|
186
|
+
WCAG 2.2 A/AA (`standard` picks another level or `best-practice`). It takes
|
|
187
|
+
the same `storageState`, `headers` and `actions` as `inspect-page`, so audit
|
|
188
|
+
the signed-in pages and open dialogs, not only the landing page. Each
|
|
189
|
+
violation lists the offending elements as verified locators and the fix; fix
|
|
190
|
+
the worst (`critical`, `serious`) first, re-run the audit, then add the
|
|
191
|
+
`AxeBuilder` check it prints to the spec that reaches that page so it stays
|
|
192
|
+
fixed. Pass `minImpact: "serious"` for a short list.
|
|
193
|
+
- **Write or review a test:** follow `references/writing-tests.md` (locators,
|
|
194
|
+
web-first assertions, auth setup, test data, network mocking, flake
|
|
195
|
+
patterns), with or without the server.
|
|
164
196
|
- **Visual regression:** `compare-visual-state` with `url` and a `name`. The first
|
|
165
197
|
call saves a baseline under `.pw-mcp/visual/`; later calls report changed
|
|
166
198
|
regions and color shifts and write a diff image. Use `action: "baseline"` only
|
|
@@ -194,3 +226,5 @@ When you finish, tell the user, in this order:
|
|
|
194
226
|
- `references/failure-kinds.md`: failure kinds and error codes, and what to do.
|
|
195
227
|
- `references/example-session.md`: a worked fix-a-failing-test session with
|
|
196
228
|
`suggest-fix`.
|
|
229
|
+
- `references/writing-tests.md`: how to write Playwright tests that stay green,
|
|
230
|
+
and the review checklist.
|
|
@@ -5,10 +5,11 @@ This skill only gives the agent instructions. The tools come from the
|
|
|
5
5
|
|
|
6
6
|
## Requirements
|
|
7
7
|
|
|
8
|
-
- playwright-e2e-mcp **0.2.0 or newer**, 0.
|
|
8
|
+
- playwright-e2e-mcp **0.2.0 or newer**, 0.4.0 recommended. `suggest-fix`,
|
|
9
9
|
`get-run-status`, `analyze-history`, verified locators and signed-in page
|
|
10
10
|
inspection arrived in 0.2.0; per-test flaky verdicts, `isolate`, tool profiles
|
|
11
|
-
and the `ci` command in 0.3.0
|
|
11
|
+
and the `ci` command in 0.3.0; `audit-accessibility`, the `order` check
|
|
12
|
+
(needs Playwright 1.64+) and root-cause grouping in 0.4.0. Check with
|
|
12
13
|
`npx -y playwright-e2e-mcp --version`.
|
|
13
14
|
|
|
14
15
|
- Node.js 20 or newer.
|
|
@@ -80,6 +81,7 @@ Set these in the server's `env` block. None of them are secrets except
|
|
|
80
81
|
| `PW_MCP_ALLOWED_ROOTS` | none | Extra directories allowed as `projectRoot` (`:`-separated, `;` on Windows) |
|
|
81
82
|
| `PW_MCP_BLOCK_PRIVATE_URLS` | off for stdio | `1` makes the URL tools refuse loopback, private-network and cloud-metadata addresses |
|
|
82
83
|
| `PW_MCP_STORAGE_STATE` | none | Default Playwright storageState JSON (inside the project) for the page tools, so signed-in pages open logged in |
|
|
84
|
+
| `PW_MCP_SHUFFLE_SEEDS` | random | Seeds for `diagnose-flaky`'s `order` pass (comma separated), to replay reported orders |
|
|
83
85
|
| `PW_MCP_HISTORY` | on | `0` stops recording runs to `<project>/.playwright-e2e-mcp/history.jsonl` (this turns off `analyze-history`) |
|
|
84
86
|
| `LOG_LEVEL` | `info` | `debug`, `info`, `warn`, `error` or `silent` |
|
|
85
87
|
| `PW_MCP_HTTP_TOKEN` | none | HTTP bridge only: bearer token that unlocks all tools |
|
|
@@ -110,7 +112,7 @@ Exit code 0 means passed, 1 tests failed, 2 the run itself failed. On GitHub
|
|
|
110
112
|
Actions it also writes to the job summary. As an Action:
|
|
111
113
|
|
|
112
114
|
```yaml
|
|
113
|
-
- uses: xsignon-secure/E2E@v0.
|
|
115
|
+
- uses: xsignon-secure/E2E@v0.4.0
|
|
114
116
|
with:
|
|
115
117
|
flaky-runs: 3
|
|
116
118
|
suggest: 2
|
|
@@ -24,6 +24,9 @@ Runs Playwright and returns stats, failures, failure kinds and fix hints.
|
|
|
24
24
|
|
|
25
25
|
Traces are captured on failure automatically so `get-failure` can show the DOM.
|
|
26
26
|
Clients that send a progress token get live progress while the run goes.
|
|
27
|
+
Failures that share a kind and first error line are grouped (0.4.0+): each
|
|
28
|
+
cause is shown once in full, the other tests under it as one line each, and
|
|
29
|
+
their `index` numbers stay the ones `get-failure` takes.
|
|
27
30
|
|
|
28
31
|
## get-failure
|
|
29
32
|
|
|
@@ -113,6 +116,7 @@ Baselines live in `.pw-mcp/visual/`.
|
|
|
113
116
|
| `testFiles` | string[] | Default: tests that failed in the last run |
|
|
114
117
|
| `runs` | number | 2 to 10 (default 3) |
|
|
115
118
|
| `isolate` | boolean | Repeat fully parallel (`workers` default 4), then repeat the failing tests with one worker (0.3.0+) |
|
|
119
|
+
| `order` | boolean | Run the target files in `runs` random orders with one worker (`--shuffle`, Playwright 1.64+), replay a failing order, and name the test it relies on or is broken by (0.4.0+) |
|
|
116
120
|
| `separateProcesses` | boolean | One Playwright process per run instead of one `--repeat-each` process; slower (0.3.0+) |
|
|
117
121
|
| `browser`, `headed`, `workers`, `config` | | As `run-test` |
|
|
118
122
|
| `timeoutMs` | number | Per run (default 120000); the single `--repeat-each` process gets this × `runs` |
|
|
@@ -121,9 +125,30 @@ Runs with retries disabled and returns, per test, passes, failures, duration
|
|
|
121
125
|
spread, first error and `CONSISTENTLY FAILING`, `FLAKY` or `NOT REPRODUCING`,
|
|
122
126
|
plus the verdict block for the worst test. With `isolate`, a second table reads
|
|
123
127
|
each failing test as "fails only in parallel", "flaky even alone" or "fails
|
|
124
|
-
alone too".
|
|
128
|
+
alone too". With `order`, a third section reads each test as "order
|
|
129
|
+
dependent", "flaky regardless of order" or "fails in every order", with the
|
|
130
|
+
`--shuffle=<seed>` command that reproduces it. `PW_MCP_SHUFFLE_SEEDS=1,2,3`
|
|
131
|
+
on the server replays given seeds instead of random ones. Versions before 0.3.0 return one run-level verdict and a per-run
|
|
125
132
|
table instead.
|
|
126
133
|
|
|
134
|
+
## audit-accessibility
|
|
135
|
+
|
|
136
|
+
Runs axe-core (0.4.0+) on a live page and returns WCAG violations, worst first.
|
|
137
|
+
|
|
138
|
+
| Argument | Type | Notes |
|
|
139
|
+
| --- | --- | --- |
|
|
140
|
+
| `url` | string | Required |
|
|
141
|
+
| `standard` | `wcag2a` \| `wcag2aa` \| `wcag21aa` \| `wcag22aa` \| `best-practice` \| `all` | Default `wcag22aa` (WCAG 2.0 to 2.2, A and AA) |
|
|
142
|
+
| `minImpact` | `critical` \| `serious` \| `moderate` \| `minor` | Hide violations below this impact |
|
|
143
|
+
| `selector` | string | Audit only this part of the page |
|
|
144
|
+
| `storageState`, `headers`, `actions`, `viewport` | | As `inspect-page` |
|
|
145
|
+
| `waitFor`, `waitUntil`, `timeoutMs` | | `waitUntil` default `load`; `timeoutMs` default 60000 |
|
|
146
|
+
|
|
147
|
+
Each violation has its rule id, impact, WCAG criteria, a help link, up to three
|
|
148
|
+
offending elements as verified locators with what to change, and the count of
|
|
149
|
+
the rest. The result ends with an `@axe-core/playwright` check to paste into
|
|
150
|
+
the spec.
|
|
151
|
+
|
|
127
152
|
## suggest-fix
|
|
128
153
|
|
|
129
154
|
| Argument | Type | Notes |
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
# Writing Playwright tests that stay green
|
|
2
|
+
|
|
3
|
+
Use this when you write a new test, extend one, or review one. It applies with or
|
|
4
|
+
without the MCP server; where a tool helps, it is named.
|
|
5
|
+
|
|
6
|
+
## 1. Locators
|
|
7
|
+
|
|
8
|
+
Pick the first that works, and check it with `validate-selector` (exactly 1 match)
|
|
9
|
+
or take it from `inspect-page` with `view: "locators"`:
|
|
10
|
+
|
|
11
|
+
1. `page.getByRole('button', { name: 'Place order' })`: what a user and a screen
|
|
12
|
+
reader see. Add `exact: true` when the name is a prefix of another.
|
|
13
|
+
2. `page.getByLabel('Email')`, `page.getByPlaceholder('Search')` for form fields.
|
|
14
|
+
3. `page.getByText('Order confirmed')` for non-interactive content.
|
|
15
|
+
4. `page.getByTestId('checkout')` when the app has test ids (set a custom
|
|
16
|
+
attribute with `testIdAttribute` in the config).
|
|
17
|
+
5. A short CSS selector on a stable attribute, as a last resort.
|
|
18
|
+
|
|
19
|
+
Narrow instead of indexing: `page.getByRole('listitem').filter({ hasText: 'Milk' })`,
|
|
20
|
+
`row.getByRole('button', { name: 'Delete' })`. Avoid `nth()`, `:nth-child`,
|
|
21
|
+
XPath, generated class names (`.css-1x2y3z`) and long descendant chains. Locators
|
|
22
|
+
are strict: an action on a locator that matches several elements throws, which
|
|
23
|
+
is a feature. Fix the locator rather than adding `.first()`.
|
|
24
|
+
|
|
25
|
+
## 2. Waiting and assertions
|
|
26
|
+
|
|
27
|
+
- Use web-first assertions, which retry until the timeout:
|
|
28
|
+
`await expect(page.getByRole('alert')).toHaveText('Saved')`, `toBeVisible()`,
|
|
29
|
+
`toHaveURL(/\/orders\/\d+/)`, `toHaveCount(3)`, `toBeEnabled()`.
|
|
30
|
+
- Never `expect(await locator.isVisible()).toBe(true)` or
|
|
31
|
+
`expect(await locator.textContent())`: they read once and do not wait.
|
|
32
|
+
- Never `page.waitForTimeout(…)`. Wait for the thing you need: an assertion, a
|
|
33
|
+
response, or a URL.
|
|
34
|
+
- Actions already wait for the element to be visible, enabled and stable; do
|
|
35
|
+
not add `waitForSelector` before `click`.
|
|
36
|
+
- For a value that is not in the DOM, poll: `await expect.poll(() => api.count()).toBe(3)`.
|
|
37
|
+
- Wait for a request triggered by an action by starting the wait first:
|
|
38
|
+
|
|
39
|
+
```ts
|
|
40
|
+
const saved = page.waitForResponse((r) => r.url().includes('/api/orders') && r.ok());
|
|
41
|
+
await page.getByRole('button', { name: 'Save' }).click();
|
|
42
|
+
await saved;
|
|
43
|
+
```
|
|
44
|
+
- `waitForLoadState('networkidle')` is discouraged: apps with polling or
|
|
45
|
+
analytics never go idle. Assert on the content you need instead.
|
|
46
|
+
- Use `expect.soft` only when later checks still make sense after a failure.
|
|
47
|
+
|
|
48
|
+
## 3. Signing in
|
|
49
|
+
|
|
50
|
+
Log in once, not in every test. Use a setup project that saves the session and
|
|
51
|
+
make the other projects depend on it:
|
|
52
|
+
|
|
53
|
+
```ts
|
|
54
|
+
// tests/auth.setup.ts
|
|
55
|
+
import { test as setup, expect } from '@playwright/test';
|
|
56
|
+
setup('sign in', async ({ page }) => {
|
|
57
|
+
await page.goto('/login');
|
|
58
|
+
await page.getByLabel('Email').fill(process.env.E2E_USER!);
|
|
59
|
+
await page.getByLabel('Password').fill(process.env.E2E_PASSWORD!);
|
|
60
|
+
await page.getByRole('button', { name: 'Sign in' }).click();
|
|
61
|
+
await expect(page.getByRole('heading', { name: 'Dashboard' })).toBeVisible();
|
|
62
|
+
await page.context().storageState({ path: 'playwright/.auth/user.json' });
|
|
63
|
+
});
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
```ts
|
|
67
|
+
// playwright.config.ts
|
|
68
|
+
projects: [
|
|
69
|
+
{ name: 'setup', testMatch: /.*\.setup\.ts/ },
|
|
70
|
+
{ name: 'chromium', use: { ...devices['Desktop Chrome'], storageState: 'playwright/.auth/user.json' }, dependencies: ['setup'] },
|
|
71
|
+
],
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Keep `playwright/.auth/` in `.gitignore`. Use one file per role (admin, member).
|
|
75
|
+
Credentials come from environment variables, never from the spec. Pass the same
|
|
76
|
+
file as `storageState` to `inspect-page`, `validate-selector` and
|
|
77
|
+
`audit-accessibility` to look at signed-in pages.
|
|
78
|
+
|
|
79
|
+
## 4. Test data and isolation
|
|
80
|
+
|
|
81
|
+
Every test must pass alone, in any order, and in parallel:
|
|
82
|
+
|
|
83
|
+
- Create the data a test needs in the test or a fixture, ideally through the API
|
|
84
|
+
(`request.post('/api/products', { data })`), not through the UI and not by
|
|
85
|
+
relying on a test that ran earlier.
|
|
86
|
+
- Make it unique per worker and run:
|
|
87
|
+
`` `user-${testInfo.workerIndex}-${Date.now()}@example.com` ``.
|
|
88
|
+
- Clean up what you created, or create it in a namespace that is reset.
|
|
89
|
+
- Do not share mutable module or `globalThis` state between tests.
|
|
90
|
+
|
|
91
|
+
Check it: `diagnose-flaky` with `isolate: true` finds tests that only fail in
|
|
92
|
+
parallel; `order: true` finds tests that depend on another test running first.
|
|
93
|
+
|
|
94
|
+
## 5. Network and time
|
|
95
|
+
|
|
96
|
+
- Mock third parties (payments, maps, analytics) with `page.route`, and mock your
|
|
97
|
+
own API only for states that are hard to reach (errors, empty lists):
|
|
98
|
+
|
|
99
|
+
```ts
|
|
100
|
+
await page.route('**/api/orders', (route) => route.fulfill({ status: 500, json: { error: 'down' } }));
|
|
101
|
+
```
|
|
102
|
+
- Replay a recorded session with `page.routeFromHAR` when the backend is slow or
|
|
103
|
+
unstable.
|
|
104
|
+
- Control time with `page.clock` (`install`, `setFixedTime`, `fastForward`)
|
|
105
|
+
instead of waiting for timers, and pin `timezoneId` and `locale` in the config
|
|
106
|
+
when dates are on screen.
|
|
107
|
+
|
|
108
|
+
## 6. Structure
|
|
109
|
+
|
|
110
|
+
- One user journey per test, named for the behavior: `places an order with a saved card`.
|
|
111
|
+
- Group steps with `await test.step('pay', async () => { … })` so traces and
|
|
112
|
+
reports read like the journey.
|
|
113
|
+
- Prefer fixtures for setup and shared helpers; use a page object only for a
|
|
114
|
+
page several specs drive. Keep assertions in the test, not in page objects.
|
|
115
|
+
- Tag suites (`test('…', { tag: '@smoke' }, …)`) and run them with `--grep @smoke`.
|
|
116
|
+
|
|
117
|
+
## 7. Config that prevents trouble
|
|
118
|
+
|
|
119
|
+
```ts
|
|
120
|
+
export default defineConfig({
|
|
121
|
+
fullyParallel: true,
|
|
122
|
+
forbidOnly: !!process.env.CI,
|
|
123
|
+
retries: process.env.CI ? 2 : 0,
|
|
124
|
+
use: { baseURL: 'http://localhost:3000', trace: 'on-first-retry' },
|
|
125
|
+
webServer: { command: 'npm run start', url: 'http://localhost:3000', reuseExistingServer: !process.env.CI },
|
|
126
|
+
});
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
`webServer` starts the app so runs never fail with "connection refused". Traces
|
|
130
|
+
on retry are what `get-failure` reads.
|
|
131
|
+
|
|
132
|
+
## 8. Flake patterns
|
|
133
|
+
|
|
134
|
+
| Symptom | Usual cause | Fix |
|
|
135
|
+
| --- | --- | --- |
|
|
136
|
+
| Passes locally, times out in CI | Fixed sleeps, slow CI machine, no `webServer` | Web-first assertions; `webServer`; wait for the response |
|
|
137
|
+
| "strict mode violation" | Locator matches several elements | Filter or scope the locator |
|
|
138
|
+
| Element "detached from the DOM" | List re-renders after data loads | Assert the loaded state first, then act |
|
|
139
|
+
| Fails only with other tests | Shared user or data | Unique data per test or worker (`isolate: true` confirms) |
|
|
140
|
+
| Fails only after certain tests | Order dependence | Own setup per test (`order: true` names the test) |
|
|
141
|
+
| Wrong date or number format | Time zone or locale | Set `timezoneId`, `locale`, `page.clock` |
|
|
142
|
+
| Click does nothing | Overlay, animation, or element not ready | Assert the overlay is gone; assert enabled |
|
|
143
|
+
|
|
144
|
+
Never fix flakiness with retries, longer timeouts or `waitForTimeout` alone.
|
|
145
|
+
|
|
146
|
+
## 9. Accessibility and structure checks
|
|
147
|
+
|
|
148
|
+
- Run `audit-accessibility` on new pages, then keep them clean with
|
|
149
|
+
`@axe-core/playwright`:
|
|
150
|
+
|
|
151
|
+
```ts
|
|
152
|
+
import AxeBuilder from '@axe-core/playwright';
|
|
153
|
+
const { violations } = await new AxeBuilder({ page }).withTags(['wcag2a', 'wcag2aa', 'wcag21a', 'wcag21aa', 'wcag22aa']).analyze();
|
|
154
|
+
expect(violations.map((v) => v.id)).toEqual([]);
|
|
155
|
+
```
|
|
156
|
+
- Lock the structure of a component with
|
|
157
|
+
`await expect(page.getByRole('navigation')).toMatchAriaSnapshot(…)`.
|
|
158
|
+
- For visual checks, `compare-visual-state` locally or `toHaveScreenshot()` in
|
|
159
|
+
the suite, with animations disabled and dynamic content masked.
|
|
160
|
+
|
|
161
|
+
## 10. Review checklist
|
|
162
|
+
|
|
163
|
+
- [ ] Every locator is role, label, text or test id based, and unique.
|
|
164
|
+
- [ ] No `waitForTimeout`, no assertions on one-off reads.
|
|
165
|
+
- [ ] The test creates its own data and passes alone and in any order.
|
|
166
|
+
- [ ] Sign-in comes from a saved session, credentials from the environment.
|
|
167
|
+
- [ ] Third-party calls are mocked; time-dependent output is pinned.
|
|
168
|
+
- [ ] The test name describes the behavior; steps are grouped.
|
|
169
|
+
- [ ] It ran green with `run-test`, and three times in a row with
|
|
170
|
+
`diagnose-flaky` (`runs: 3`) before you call it done.
|