Yeda AI Tips · #072

Español

Never waitForTimeout — Wait for the Actual Network

Delete every fixed timeout in your test suite today. A waitForTimeout(5000) is a guess: when the app responds in 300 ms it wastes 4.7 seconds on every single run, and when CI is slow it still fails. Fixed sleeps are the only technique that makes a test suite slower and flakier at the same time — and both problems disappear the moment you wait on the real condition instead of the clock.

Why a fixed sleep can never be right

There is no correct number to put in a sleep. Pick it short and the test flakes on a loaded CI runner; pick it long and you pay the full delay on every green run. Multiply one 5-second sleep by 40 tests and 20 CI runs a day, and you're burning over an hour of machine time daily on waits that verify nothing.

Modern test runners agree. Playwright's own guidance is to rely on web-first assertions: "By using web first assertions Playwright will wait until the expected condition is met." Cypress lists "waiting for arbitrary time periods using cy.wait(Number)" as an explicit anti-pattern and tells you to "use route aliases or assertions to guard Cypress from proceeding until an explicit condition is met."

What to wait on instead

1. The network response. If the UI is waiting on a request, so should the test:

// Playwright — wait for the exact request the click triggers
const responsePromise = page.waitForResponse(
  (res) => res.url().includes('/api/users') && res.status() === 200
);
await page.getByTestId('fetch-users').click();
await responsePromise;
// Cypress — alias the route, then wait on the alias
cy.intercept('GET', '/users').as('getUsers');
cy.get('[data-testid="fetch-users"]').click();
cy.wait('@getUsers');

2. The visible result. Web-first assertions re-check the DOM "over and over, until the condition is met or until the timeout is reached" — with a 5-second default timeout in Playwright:

await expect(page.getByTestId('status')).toHaveText('Submitted');
await expect(page.getByRole('row')).toHaveCount(2);

Both resume the instant the condition is true. Fast app, fast test; slow network, patient test. Same code.

Rules of thumb

You wrote a sleep because…Replace it with
Data is still loadingwaitForResponse / aliased cy.wait('@route')
Element isn't there yetexpect(locator).toBeVisible() — auto-retries
Text hasn't updatedexpect(locator).toHaveText(...)
Animation is finishingAssert on the end state, not the transition
"It just needs a moment"You don't know the condition yet — find it first

Playwright actions also auto-wait on their own: click() runs actionability checks (visible, enabled, stable) before firing, so most pre-click sleeps were never needed at all.

Power-user moves

Resources

Shipping AI-assisted code? Yeda AI designs, audits, and ships production LLM systems.

Talk to us · More tips · Leer en español