← Back to libraryQuestion 167 of 273
🎭PlaywrightBeginner

Auto-Waiting & Actionability Checks

📌 Definition:

Auto-waiting is Playwright's built-in mechanism that makes it wait for an element to be ready before acting on it. Before every action (click, fill, check, etc.), Playwright runs a series of actionability checks and only proceeds once they all pass, or throws once the timeout is reached.

📖 Detailed Explanation:

In Selenium, a command like click() fires immediately; if the element isn't ready yet you get an ElementNotInteractableException or a silent misclick, which is why teams bolt on explicit WebDriverWaits everywhere. Playwright inverts this: every action first waits for the element to be attached to the DOM, visible, stable (not animating), able to receive events (not obscured by an overlay), and enabled. Only then does it act. This waiting is automatic and applies to locator actions and web-first assertions alike, which is the single biggest reason Playwright tests are less flaky than equivalent Selenium tests. The default timeout is 30 seconds per action (configurable), and Playwright polls continuously rather than sleeping a fixed amount, so a check that becomes true after 200ms proceeds after ~200ms, not after a hard-coded pause.

🔑 Key Points:
  • Runs before every action — no need to sprinkle explicit waits through the test
  • Actionability checks: attached, visible, stable, receives events, enabled (plus editable for fill)
  • Polls until the condition is met or the timeout elapses — never a fixed sleep
  • Applies to auto-retrying assertions (expect(locator).toBeVisible()) too
  • waitForTimeout()/sleep is an anti-pattern — it either wastes time or is still too short
  • If an action times out, the error names exactly which actionability check failed
🌍 Real-World Example:

On an e-commerce checkout, clicking 'Place Order' is disabled until a spinner finishes validating the cart. In Selenium you'd add a WebDriverWait for the button to be clickable. In Playwright, page.getByRole('button', { name: 'Place Order' }).click() automatically waits for the button to become enabled and stop being covered by the spinner overlay before clicking — no explicit wait needed.

📎 Code Example:
// No explicit waits — auto-waiting handles readiness
await page.getByRole('button', { name: 'Place Order' }).click();

// Auto-retrying assertion: polls until visible or times out
await expect(page.getByText('Order confirmed')).toBeVisible();

// ANTI-PATTERN — brittle, do not do this:
// await page.waitForTimeout(3000);
// await page.locator('#place-order').click();

// Legitimate explicit wait: waiting on a specific network response
await Promise.all([
  page.waitForResponse(r => r.url().includes('/api/orders') && r.ok()),
  page.getByRole('button', { name: 'Place Order' }).click(),
]);
📎 Selenium Vs Playwright:
ConcernSeleniumPlaywright
Waiting modelManual explicit/implicit waitsAutomatic before every action
Default readinessNone — you add WebDriverWaitBuilt-in actionability checks
Flakiness sourceMissing/incorrect waitsRare with default auto-wait
Fixed sleepsCommon (Thread.sleep)Discouraged (waitForTimeout)
🎯 Scenario-Based Interview Question:

Scenario: A teammate migrating from Selenium adds await page.waitForTimeout(5000) before every click 'to fix flakiness'. The suite now takes 40 minutes and STILL fails intermittently on a slow CI runner. How do you explain and fix this?