Most flaky Playwright tests are fixed by removing fixed timeouts, finding elements through locators that describe what the user sees, and giving each test its own state. Playwright already performs actionability checks and auto-waits for them before actions [Microsoft (Playwright)], so a test that still flakes usually waits on the wrong thing or shares state with another test. Retries should report flakiness, not hide it.
Fixed waits are the first thing to remove
A fixed wait pauses the test for a set time and hopes the page is ready. On a fast laptop the time is too long. On a busy continuous integration (CI) runner it is too short. Either way the test is guessing.
Playwright checks that an element is ready before acting on it. It auto-waits for all the relevant checks to pass, and fails the action with a TimeoutError if the checks do not pass within the given timeout [Microsoft (Playwright)]. A fixed wait before an action adds nothing to that, except run time and a new way to fail.
1// Fragile: waits a fixed time, then clicks by CSS id2await page.waitForTimeout(3000);3await page.click('#submit');45// Sturdier: finds the button by role and name; the click waits for it6await page.getByRole('button', { name: 'Submit' }).click();This matches research on flaky tests in general. Async Wait is one of the top three categories of flaky tests, next to Concurrency and Test Order Dependency [University of Illinois at Urbana-Champaign / ACM FSE], and many Async Wait flaky tests (54%) are fixed using waitFor [University of Illinois at Urbana-Champaign / ACM FSE]. Waiting on a condition beats waiting on a clock.
Locators should describe what the user sees
Locators are the central piece of Playwright's auto-waiting and retry-ability [Microsoft (Playwright)]. How you write them decides how often tests break for reasons unrelated to the product.
A selector tied to a CSS class or a deep element path breaks when a developer restyles the page. A locator based on role and visible name breaks only when the thing the user sees changes. Prefer role and label over class names and element paths. When no role fits, add a dedicated test attribute rather than relying on styling.
Check the final state the user sees through a locator too. Reading a value once and comparing it later skips the waiting that locators provide.
1await expect(page.getByRole('heading', { name: 'Order placed' })).toBeVisible();A test that fails because a button was renamed is not flaky. It fails every time until updated. That case is covered in what causes flaky tests.
Tests must not share state
If a test passes alone and fails in the full run, look for shared state. Test Order Dependency and Concurrency are the other two of the top three categories [University of Illinois at Urbana-Champaign / ACM FSE]. Both mean one test depends on something another test did.
Practical rules:
- Each test creates the records it needs, with unique names.
- Each test starts from a clean browser context, with its own cookies and storage.
- Log in through a setup step per test or per worker, not by reusing a session another test left behind.
- Tests that must run in sequence are marked that way on purpose, with a comment saying why.
Parallel runs make these problems visible sooner. That is useful. A suite that only passes in serial is hiding coupling.
Retries are a detector, not a fix
Retries are a common answer to flaky Playwright tests, and an easy one to misuse. A retry that turns a red run green removes the signal without removing the cause.
Use retries in CI, but treat every test that passed only on retry as flaky and record it. Review that list each week. Fix the tests at the top, and quarantine the ones you cannot fix yet, with an owner and a date. Keep retries off locally so developers see failures as they happen. The full trade-off is in retry or quarantine flaky tests, and collecting the signal is covered in detect flaky tests in CI.
Run on the browsers you ship to
Playwright can run tests on Chromium, WebKit and Firefox browsers as well as branded browsers such as Google Chrome and Microsoft Edge [Microsoft (Playwright)]. A test that passes in one engine and fails in another is often reported as flaky when it is a real browser difference.
Run the browsers your users use, and keep results separate per browser. A failure that appears in one engine only is a finding, not noise.
How AI Testing Platform relates to Playwright
AI Testing Platform takes a different route to the same problems: tests are plain-English steps in a YAML file, resolved against the live page at run time, with locator and layout changes healed and recorded for review. Each failed test gets a triage card with a proposed classification of app bug, test issue or environment. A side-by-side view is on compare with Playwright, and CI setup is in the CI docs.