lecture 9 of 180 completed

Waiting properly

A fixed pause is a guess. Too short and the test fails; too long and every run wastes time.

Saving a contact takes about 700 milliseconds. So the obvious thing to write is this:

await page.getByTestId('save-contact').click();
await page.waitForTimeout(1000);
await expect(page.getByTestId('toast')).toContainText('Contact created');

Wait one second, then check. It works on your machine. Please do not do it, and here is why.

A fixed pause is a guess about how long something takes, and you lose that guess in both directions.

When it is too short, the test fails for no real reason. Your laptop is fast. The build server is usually slower and busier. A pause that always works locally will fail there.

When it is too long, you pay for it forever. One extra second in eighty tests is more than a minute added to every single run.

The right approach is to wait for the thing you actually want, not for time. In Playwright, you already have it:

await page.getByTestId('save-contact').click();
await expect(page.getByTestId('toast')).toContainText('Contact created');

That assertion retries. It checks the page again and again until the toast appears or the timeout is reached. If the save takes 200ms, your test continues after 200ms. If it takes 3 seconds, it waits 3 seconds. It is never too early and never wastes time.

This is the single biggest reason modern Playwright suites are more stable than older ones. Actions wait for elements to be ready, and assertions retry until they pass.

So when you see waitForTimeout in a code review, treat it as a small bug. There is almost always a condition you could wait for instead: an element appearing, a value changing, or a page finishing its navigation.

Remember this: wait for a condition, never for a number.

Reference: Auto-waiting.

now prove it

Reading is the setup. Solve these problems in the editor. We break the app on purpose to check your test would catch it.