Browser tests
Install Playwright
Add Playwright Test, browser binaries, configuration, and one smoke test to the Books interface project.
Browser tests cover the whole flow: the page, the browser’s behavior, your JavaScript, the API, and storage, all working together. They’re the slowest tests we’ll write, and the only ones that prove a reader can add a book.
Playwright is the tool I use for this. It drives real browsers and comes with its own test runner.
Install it
Run the initializer inside the Books project:
npm init playwright@latest
It asks a few questions. Pick TypeScript, keep the tests folder, and let it download the browsers. Start with Chromium only. Add Firefox and WebKit later, when cross-browser bugs are an actual risk you’ve seen.
You’ll get a playwright.config.ts and an example test. Run everything with:
npx playwright test
Or open the UI mode, which lets you watch each step:
npx playwright test --ui
Let Playwright own the server
The tests need the Books app running. Put the server in the config so local runs and CI use the same entry point:
import { defineConfig } from '@playwright/test'
export default defineConfig({
use: { baseURL: 'http://127.0.0.1:3000' },
webServer: {
command: 'npm run start',
url: 'http://127.0.0.1:3000',
reuseExistingServer: !process.env.CI
}
})
webServer.url is the readiness check. Playwright polls it and starts the tests only when it answers. baseURL lets tests navigate to /books instead of hardcoding a host.
reuseExistingServer is a convenience for your laptop: if you already have the dev server up, Playwright uses it. In CI it’s false, so the test run owns the server it exercises and can’t pick up a stale process.
The first smoke test
Replace the example test with one that opens the books page:
import { test, expect } from '@playwright/test'
test('shows the books page', async ({ page }) => {
await page.goto('/books')
await expect(page.getByRole('heading', { name: 'Books' })).toBeVisible()
})
Run npx playwright test and you should see:
Running 1 test using 1 worker
✓ 1 books.spec.ts:3:1 › shows the books page (1.2s)
1 passed (3.5s)
This is a smoke test. It proves the app starts, the route loads, and the heading reaches the accessibility tree. It doesn’t prove creation, persistence, or error handling.
Add more flows only when they protect a real user risk. A browser suite that repeats every unit-level edge case becomes slow, and its failures become vague.
Try this: stop the app server and run the test once. Watch Playwright start the server itself and wait for it. Then change the heading name to Library, run again, and read the failure before you fix it.
Lesson completed