Playwright automatically waits for elements to be ready before performing actions. This eliminates the need for manual waits and makes tests more reliable.
Unlike many testing frameworks that require explicit waits, Playwright waits automatically for actionability checks.
// Waits for button to be visible, enabled, and stableawait page.click('button');await page.dblclick('button');await page.hover('button');await page.tap('button');
// Waits for input to be visible, enabled, and editableawait page.fill('input', 'text');await page.type('input', 'text');await page.press('input', 'Enter');
// Waits for element to be visible and enabledawait page.selectOption('select', 'option1');await page.check('checkbox');await page.uncheck('checkbox');await page.setChecked('checkbox', true);
Locators wait automatically when performing actions:
const button = page.locator('button');// Each of these waits automaticallyawait button.click();await button.fill('text');await button.selectOption('value');
From client/locator.ts:75-90:
private async _withElement<R>( task: (handle: ElementHandle, timeout?: number) => Promise<R>, options: { title: string, timeout?: number }): Promise<R> { const timeout = this._frame._timeout({ timeout: options.timeout }); const deadline = timeout ? monotonicTime() + timeout : 0; return await this._frame._wrapApiCall(async () => { // Wait for element to be attached const result = await this._frame._channel.waitForSelector({ selector: this._selector, strict: true, state: 'attached', timeout }); // Element is ready, perform action });}
// Wait for elementconst element = await page.waitForSelector('.dynamic-content');// With timeoutconst element = await page.waitForSelector('.dynamic-content', { timeout: 10000});
// Wait for page to loadawait page.waitForLoadState('load');await page.waitForLoadState('domcontentloaded');await page.waitForLoadState('networkidle');
// Wait for specific URLawait page.waitForURL('**/dashboard');// Wait for URL patternawait page.waitForURL(/\/user\/\d+/);// With load stateawait page.waitForURL('**/dashboard', { waitUntil: 'networkidle'});
// Set default timeout for all operationspage.setDefaultTimeout(60000);// Set default navigation timeoutpage.setDefaultNavigationTimeout(60000);// At context levelcontext.setDefaultTimeout(60000);context.setDefaultNavigationTimeout(60000);
// For SPAsawait page.goto('https://example.com', { waitUntil: 'networkidle' });// For static pagesawait page.goto('https://example.com', { waitUntil: 'domcontentloaded' });
Wait for Specific Conditions
Use waitForFunction for complex conditions.
// Wait for specific stateawait page.waitForFunction(() => { return window.appReady === true;});
Handle Dynamic Content
Wait for content to stabilize.
// Wait for list to be populatedawait page.waitForFunction(() => { return document.querySelectorAll('.item').length > 0;});await page.click('.item:first-child');
// Wait for all elements to appearawait Promise.all([ page.waitForSelector('.header'), page.waitForSelector('.content'), page.waitForSelector('.footer')]);
When waits timeout, Playwright provides helpful errors:
Timeout 30000ms exceeded.=========================== logs ===========================waiting for locator('button') locator resolved to <button>…</button> attempting click action waiting for element to be visible, enabled and stable element is visible and enabled element is not stable - waiting...============================================================