Provides concrete code-level fixes for each of the eight recurring flake patterns in flake-pattern-reference: replacing fixed sleeps with framework auto-waits, isolating state in beforeEach fixtures, stable role-based locators, mocking network + clock, seeding RNG, closing leaked resources, and the per-worker DB-schema fix for shared parallel state. Use when a flake is already classified by pattern and you need the specific code change to apply; to classify the pattern first use flake-pattern-reference or flake-axis-bisection, and to quarantine the test while the fix is in review use flaky-test-quarantine.
76
95%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
View guide
Passed
No findings from the security scan
Deep reference for flake-remediation-guide SKILL.md. The Pattern 5
(network / external service) and Pattern 6 (locator drift) code fixes,
split out of the main guide so the four core-pattern fixes stay in front.
Root cause: the test reaches a real network endpoint that is slow, rate-limited, or unavailable in CI.
page.route(urlPattern, handler) intercepts every request matching the
pattern and stalls it until you call fulfill, continue, or abort
(pw-network):
await page.route('**/api/users', route =>
route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify([{ id: 1, name: 'Alice' }]),
})
);
await page.goto('/users');
await expect(page.getByRole('listitem')).toHaveCount(1);Use browserContext.route() instead of page.route() when the request
originates from a popup or a new page (pw-api).
Block non-essential traffic (images, analytics) to speed up tests:
await page.route('**/*.{png,jpg,jpeg,gif,webp}', route => route.abort());Mock Service Worker intercepts fetch and XHR at the Node.js level for unit and integration tests (msw-start):
import { http, HttpResponse } from 'msw';
import { setupServer } from 'msw/node';
const server = setupServer(
http.get('https://api.example.com/user', () =>
HttpResponse.json({ id: 'abc-123', name: 'Alice' })
)
);
beforeAll(() => server.listen());
afterEach(() => server.resetHandlers()); // clean per-test overrides
afterAll(() => server.close());Isolate them in a separate Playwright project or Jest project with a
--testPathPattern that CI runs outside the main gate. The main merge
gate only runs mocked suites.
Root cause: selectors matched by CSS class, position, or text that shifts with unrelated UI changes.
Playwright recommends getByRole() as the primary locator strategy
because it reflects how users and assistive technology perceive the
page (pw-best-practices):
// Before - CSS class breaks on a design-system update
await page.locator('button.btn-primary.checkout-btn').click();
// After - survives CSS changes; tied to accessible role + name
await page.getByRole('button', { name: 'Checkout' }).click();Fallback order: getByRole > getByTestId > getByLabel / getByText
CSS/XPath (last resort).
<div class="card" data-testid="product-card-42">...</div>await page.getByTestId('product-card-42').click();Playwright locators are strict by default: if a locator matches more than one element, the action throws rather than silently acting on the first match (pw-locators):
// Throws immediately if two buttons match - forces you to be more specific
await page.getByRole('button', { name: 'Delete' }).click();Narrow an ambiguous locator with .filter():
await page
.getByRole('listitem')
.filter({ hasText: 'Product 42' })
.getByRole('button', { name: 'Delete' })
.click();