Open this lesson in your favourite AI. It'll walk you through the why, explain the demo, and quiz you on the try-it list.
Playwright is the modern standard for browser automation. It replaced Selenium as the industry default because it's faster (async by design), more reliable (auto-waits eliminate most explicit waits), works across Chromium, Firefox, and WebKit, and has a first-class TypeScript API. The key mental model: Playwright controls a real browser — it clicks buttons, fills forms, reads text, and takes screenshots exactly as a user would. The 10-line 'hello world' gets you to a working browser test faster than any other framework, and that foundation scales to thousands of test cases.
Playwright's async API auto-waits for elements to be actionable before interacting with them, which eliminates the explicit sleep and waitForSelector calls that make Selenium tests brittle. Installing the package, pointing it at a URL, and calling expect(page).toHaveTitle() gets you a real browser assertion in under ten minutes. Getting this baseline working first means every subsequent feature — mocking, fixtures, CI — is built on a foundation you've already verified.
// 1. Install: npm init -y && npm install -D @playwright/test && npx playwright install chromium
// 2. Save as: tests/home.spec.ts
// 3. Run: npx playwright test
import { test, expect } from '@playwright/test';
test('page title is correct', async ({ page }) => {
await page.goto('https://playwright.dev');
await expect(page).toHaveTitle(/Playwright/);
console.log('✓ Page title contains "Playwright"');
});
test('search input is visible and accepts text', async ({ page }) => {
await page.goto('https://playwright.dev');
// Playwright auto-waits for element to be visible before acting
const searchBtn = page.getByRole('button', { name: 'Search' });
await expect(searchBtn).toBeVisible();
await searchBtn.click();
const searchInput = page.getByPlaceholder('Search docs');
await searchInput.fill('locators');
await expect(searchInput).toHaveValue('locators');
// Take a screenshot for evidence
await page.screenshot({ path: 'screenshots/search.png' });
console.log('✓ Search input works, screenshot saved');
});node main.js--headed flag (shows the browser window). Watch Playwright navigate and interact in real time. This is useful for debugging flaky tests.await page.waitForTimeout(2000) before the assertion (JavaScript) or page.wait_for_timeout(2000) (Python). This is an explicit wait — run the test and then remove it. Playwright's auto-wait means you almost never need explicit waits. When do you actually need them?https://example.com and add an assertion that the <h1> contains the text 'Example Domain': await expect(page.locator('h1')).toHaveText('Example Domain'). Run it — this is the most common E2E assertion pattern.https://demoqa.com/text-box, fill in 'Full Name', 'Email', and click Submit, then assert the output section shows the name. This is a complete form-submission test.Use these three in order. Each builds on the one before.
In one paragraph, explain what auto-wait means in Playwright and why it makes tests more reliable than Selenium's explicit waits. What is Playwright waiting for when you call `page.click()`?
Walk me through what happens when `await page.goto('https://example.com')` runs: what HTTP request is made, when does the function return (DOM content loaded vs networkidle vs commit), and what does Playwright do before letting you interact with the page?
I have a Playwright test that clicks a button, waits for a loading spinner to disappear, then asserts the table has 10 rows. The test passes locally but fails in CI 30% of the time. Walk me through the 5 most likely causes of this flakiness and a specific fix for each.