During the workshop
Playwright workshop assignments.
Every assignment from the slides, in the same order. The UI workshop takes two evenings; the Going further assignments are for afterwards. Work at your own pace and open the hints when you get stuck.
Complete the preparation and keep booker-platform running with npm run dev. The app runs on http://localhost:3000; log in with user or admin, password password123.
Assignments · UI · Evening 1
Your first stable tests.
You build your own test suite for the booker-platform web app over two evenings. Tonight: a first test, then make it pass every time. Open the UI slides.
-
Assignment 1A · Your first test
Create
playwright/tests/ui/register.spec.ts. Start on the login page (/login):- Follow the link to Register and register a new user
- Log in with that user
- Assert that "No bookings yet" is shown
Write it yourself first, then compare with what codegen records. Prefer
getByRoleandgetByLabel, and add a web-first assertion to every step. Expect a strict mode violation or two: read the error before you fix it.Hints
- Start the recorder with
npx playwright codegen localhost:3000/login, or use "Record new" in the VS Code extension. - Registering only works after you check
getByLabel('I accept the terms and conditions'). - Strict mode violation on
getByLabel('Password')? Names match substrings, and "Confirm password" contains "password". Add{ exact: true }. - Two "Register" links on the login page? One is in the header. Scope to the page content:
page.getByRole('main').getByRole('link', { name: 'Register' }). - Web-first assertions wait for you:
await expect(locator).toBeVisible().
-
Assignment 1B · Debug & report
- Configure the HTML reporter and set
trace: 'on' - Duplicate your test from 1A a few times and enable
fullyParallel - Run with
--repeat-each 5. What breaks, and why? - Fix it with unique test data per test
- Open the report and a trace: which step is the slowest?
Hints
- Open the report with
npx playwright show-report; click a test to open its trace. - Every test registers the same user, and usernames must be unique.
createRandomUser()inplaywright/support/datafactories/user.factory.tsmakes a new user every time.
- Configure the HTML reporter and set
Finish 1A and 1B, and run git pull in booker-platform: it has starter files for assignment 2.
Assignments · UI · Evening 2
Scale up your suite.
Page objects and fixtures, a shared login, control over the network, and a suite that stays green when the app misbehaves.
-
Assignment 2 · Page objects & fixtures
- Finish the starter page objects in
playwright/support/pages/:LoginPageandRegisterPage - Refactor your test from 1A to use them
- Expose them as fixtures in
playwright/support/fixtures/ui.fixture.ts - Bonus: add a
BookingWizardpage object, book a room and verify it in My bookings
Hints
- Put locators in the constructor and user actions (
login(username, password)) in methods. Keep assertions in the test. - Import
testandexpectfromui.fixture.tsin every spec. - The payment form in the wizard accepts card
4242 4242 4242 4242with any future expiry date and any CVC.
- Finish the starter page objects in
-
Assignment 3 · Authentication
- Write
auth.setup.tsthat saves the state foruserandadmin - Add
ui-userandui-adminprojects that depend onsetup - Make your tests start logged in. Compare the duration in the report: faster?
- Bonus: log in with
POST /api/auth/loginand setbooker.tokeninstead of using the login form
Hints
- Save the state with
page.context().storageState({ path })and load it withuse: { storageState }in the project. - Add
playwright/.authto.gitignore. - Start logged out in one file:
test.use({ storageState: { cookies: [], origins: [] } }). - Bonus: open a page first, then
page.localStorage.setItem('booker.token', token).
- Write
-
Assignment 4 · Network & API
Network
- Mock
POST /api/bookingsto return409and assert the "Room not available" alert - Bonus: patch the rooms response so a fake room shows up
Hybrid
- Seed a room (as admin) and a booking (as user) via the API, then verify them in the UI
- Or book in the UI and verify via
GET /api/bookings/my-bookings - Bonus: move the seeding into a fixture
Hints
- Mock with
page.route()androute.fulfill({ status, json }). Register the route before the request happens. - Patch a real response:
const response = await route.fetch(), thenroute.fulfill({ response, json }). Copy a real room and change its number, so every field is there. - Use the
requestfixture for API calls. The API docs are at/api-docs. - Run your hybrid test twice. A 409 the second time? The dates are still booked: clean up, or let a fixture do it.
- Mock
-
Assignment 5 · Flaky-test clinic
The trainer switches on chaos mode: slow rooms, flaky bookings, random order and a cookie pop-up.
- Make your suite green again
- Without
waitForTimeoutand without adding retries - Use what you learned: traces, web-first assertions, locators by name,
addLocatorHandler,toPass
Practising on your own? Add a
chaosproject toplaywright.config.tsand runnpx playwright test --project chaos:{ name: 'chaos', use: { extraHTTPHeaders: { 'x-booker-flags': 'slow-rooms,flaky-booking,random-order,popup-cookie', }, }, }Hints
- Open a trace of a failing test first. What did the page look like?
page.addLocatorHandler()clicks the cookie pop-up ("We value your privacy") away whenever it shows up.- Random order: find rooms by name, not by position.
- Flaky bookings fail before the booking is saved, so it's safe to retry just that step with
expect(...).toPass().
Assignments · UI · Going further
After the workshop.
Self-study: one assignment per topic in the Going further part of the UI slides.
-
Assignment 6 · Time, contexts & environment
Pick one (or more):
- Cancel a booking (confirm dialog) and download its invoice; check the file name and content
- Two contexts: a guest books, the admin approves, the guest sees the status change
page.clock: the check-in countdown on My bookings reaches "is open" at 15:00- Add a
mobileproject and run your tests with thenl-NLlocale and dark mode - Admin: upload a room image and reorder the rooms with drag & drop
- Open the terms in a new tab, then pay in the payment widget (shadow DOM)
Hints
- Downloads: start
page.waitForEvent('download')before you click. - Contexts:
browser.newContext({ storageState: 'playwright/.auth/admin.json' })gives you a second user in the same test. Declare it withawait using, so it's closed even when the test fails. - Clock: use
page.clock.setFixedTime(). Withinstall()pluspauseAt()the app's timers stop too, and the page keeps loading. - Mobile: combine
devices['Pixel 7']withlocaleandcolorScheme. - Upload with
setInputFiles();playwright/test-data/avatar.pngis a test image. Drag withlocator.dragTo(). - New tab:
context.waitForEvent('page'). The payment form is in an open shadow root, so ordinary locators reach it. - Test card
4242 4242 4242 4242(any future date and CVC) is accepted;4000 0000 0000 0002is declined.
-
Assignment 7 · Visual regression
- Take a screenshot of the home page; run the test twice. Does it pass?
- Mask the "deal of the day" banner and update the baseline
- Then hide the banner with
stylePathinstead. Which do you prefer? - Now open the page with
?test=1(test mode). Do you still need either? - Bonus: switch the snapshot to WebP and compare the file size
Hints
await expect(page).toHaveScreenshot('home.png', { mask: [locator] })- Update the baselines with
npx playwright test --update-snapshots. - Baselines are per operating system, so a baseline from your laptop won't match in CI.
-
Assignment 8 · Accessibility
- Run an axe scan on each step of the booking wizard. How many violations?
- Attach the results to the report with
testInfo.attach() - Bonus: add an aria snapshot of the main navigation and the booking summary
Hints
new AxeBuilder({ page }).analyze()from@axe-core/playwright.- Aria snapshots:
await expect(locator).toMatchAriaSnapshot(); let Playwright write the first version with--update-snapshots.
-
Assignment 9 · Continuous integration
- Fork booker-platform and add a GitHub Actions workflow that runs your UI tests
- Upload the HTML report as an artifact; download it and open a trace
- Bonus: shard over two jobs and merge the reports
Hints
- In CI, run
npx playwright install --with-depsandnpm run setupbefore the tests. - Upload the report with
actions/upload-artifactandif: ${{ !cancelled() }}, so you also get it when tests fail. - No need to unzip the downloaded artifact:
npx playwright show-report playwright-report.zipopens it. - Sharding:
--shard 1/2with theblobreporter, thennpx playwright merge-reports.
-
Assignment 10 · AI-assisted testing (optional)
- Run
npx playwright init-agentsin booker-platform - Let the planner write a plan for "cancel a booking", then generate the test
- Review the result like a colleague's pull request. Would you merge it?
Review checklist
- Are the assertions meaningful?
- Does it use role-based locators?
- Is the test data isolated?
- No
waitForTimeout?
- Run
Assignments · API
API testing with Playwright.
You test the booker-platform REST API directly. The starter files are in playwright/tests/api; the Swagger docs are at http://localhost:3000/api-docs. Open the API slides.
-
Assignment 1 · Authentication
- Open
playwright/tests/api/assignment1.spec.ts - Register a new (random) user
- Log in with that user and store the token
- Log out with that token
- Assert status codes, headers and the response body
Read the Swagger docs and the README. When in doubt: ask 😉
- Open
-
Assignment 2 · Rooms & bookings
- Continue with
playwright/tests/api/assignment2.spec.ts - Find a room and conclude none is to your liking
- Add a new room as admin. Construction will surely be done before you go 🙂
- Book your new room as a regular user
- The kids bring the flu home from daycare: cancel the booking and verify it's cancelled
Tip: the API rejects bookings in the past and bookings that overlap. Check the status codes in Swagger.
- Continue with
-
Bonus · Improve the setup
Start from
playwright/tests/api/bonus.spec.ts(a copy of assignment 2):- How could we improve our setup?
- Can we make our tests more readable? (fixtures, helpers)
- Can we reuse the same authenticated state for all tests?
- Can we check that every response is structured correctly, without validating it in every test?
After the workshop
Compare with the solutions.
Reference solutions for every assignment are on the solutions branch of booker-platform. Try it yourself first: there's more than one good answer.
git fetch origin && git switch solutions