After this lesson, you will be able to: Automate a browser with Playwright to test a real user flow end to end, and run those tests headless in CI.
End-to-end tests sit at the top of the pyramid: they drive the actual app the way a user does, clicking through a browser. Playwright is the modern standard. This lesson writes a real e2e test for a login-to-dashboard flow and covers why these tests are powerful but kept few.
An e2e test launches a real browser, navigates your app, fills forms, clicks buttons, and asserts on what the user sees. It is the only layer that proves the whole stack works together: frontend, backend, database, auth. That power costs speed and stability: e2e tests are slow (tens of seconds) and the most prone to flakiness (timing, animations, network). So you write a handful for the critical journeys (sign up, log in, checkout) and rely on unit and integration tests for everything else.
Playwright auto-waits for elements, which removes most of the flaky sleep() calls older tools needed.
// e2e/login.spec.tsimport { test, expect } from '@playwright/test';test('user can log in and reach the dashboard', async ({ page }) => {await page.goto('/login');await page.getByLabel('Password').fill('correct-horse-battery');await page.getByRole('button', { name: 'Sign in' }).click();// Auto-waits for navigation + the heading to appear.await expect(page.getByRole('heading', { name: 'Dashboard' })).toBeVisible();await expect(page).toHaveURL(/\/dashboard/);});test('wrong password shows an error', async ({ page }) => {await page.goto('/login');await page.getByLabel('Password').fill('wrong');await page.getByRole('button', { name: 'Sign in' }).click();await expect(page.getByText('Invalid email or password')).toBeVisible();});// Setup: npm init playwright@latest Run: npx playwright test
Prefer role- and label-based selectors (getByRole, getByLabel, getByText) over CSS classes or test ids tied to structure. They survive refactors and assert that the app is actually accessible, which is what a real user (and a screen reader) relies on. Reserve a data-testid only when no accessible handle exists. Selecting by brittle CSS paths is the number-one source of e2e flakiness.
Pick the best answer.
Sign in and purchase access to unlock this lesson.