lecture 1 of 180 completed

Where auto-waiting stops helping

Playwright waits for you almost everywhere. Knowing the few places it cannot help is what separates a junior from a mid-level engineer.

In your first weeks with Playwright you will think waiting is a solved problem. Most of the time it is. Then you hit a test that fails once a week and nobody can explain it.

Let's understand exactly what Playwright waits for, so you know when you are on your own.

Before every action, Playwright runs actionability checks. That means it waits until the element is attached to the page, visible, stable (not moving), and enabled. Only then does it click.

await page.getByRole('button', { name: 'Save' }).click();

You do not write any waiting code here. The click is the wait.

Assertions work the same way. expect with a locator retries until it passes or times out:

await expect(page.getByTestId('toast')).toContainText('Contact created');

Now here is what Playwright does not know about.

It does not know your app finished its work. Playwright sees the DOM. It cannot see that your app is still saving in the background. If a button becomes clickable before the click handler is attached, Playwright will happily click into nothing. This is a real app bug, and it is worth reporting when you find it.

It does not wait for values you read yourself.

const text = await page.getByTestId('cart-count').textContent(); // reads once
expect(text).toBe('1'); // plain check, no retry

textContent() reads the page one time. The plain expect does not retry. If the badge updates 50 milliseconds later, this fails.

Write it as a web-first assertion instead:

await expect(page.getByTestId('cart-count')).toHaveText('1'); // retries

I have reviewed a lot of pull requests, and this is the single most common mistake I see from people who are new to Playwright. The two lines look almost the same. Only one of them waits.

It does not wait between separate reads. If you read two values and compare them, the page can change between the two reads. When you need several conditions to be true at the same moment, assert on one thing that represents them both.

Remember this: if a value comes from a locator inside expect, it retries. If you pull the value out into a variable first, it does not.

Reference: Auto-waiting.