Flaky tests are technical debt.
Be careful with loops and manual delays in your e2e tests.
Loop .all()
You can leverage locator.all() to get an array of Locator objects for all elements matching the locator's selector.
It's helpful to check counts and other associated values.
Fantastic, but avoid misusage, such as naive for loops ❌:
const items = await page.locator('.myselector').all()
for (item in items) // or (for i=0 ... etc)
It will likely introduce flakiness and improper handling of async locators.
✅ Playwright recommend using allInnerTexts(), allTextContents, count(), or filter() instead.
✅ If you absolutely need a for loop, do this:
for (const item of items) {
await item.scrollIntoViewIfNeeded();
await expect(item).toBeVisible();
}
However, be aware that Playwright usually provides bulk actions on locators.
Timing Violations
Some tests can miss their targets, which is often due to dynamic DOM changes or unready pages.
Even if Playwright already adds some magic for you, it's usually better to make your tests as explicit as possible.
Instead of doing this ❌:
const anchor = await expect(page.locator('#anchor');
You may do this ✅:
await expect(page.locator('#target')).toBeVisible();
await page.locator('#target').scrollIntoViewIfNeeded();
Source: Playwright - Auto-waiting
Convenient configuration
Playwright can retry tests:
import { defineConfig } from '@playwright/test';
export default defineConfig({
retries: 5,
});
You get categorized results, such as:
Passed: Succeeds on first attempt
Flaky: Fails initially but passes on retry
Failed: Fails all 5 attempts
❌ Don't use retries to ignore flaky tests
✅ Flag it, fix it
Top comments (0)