Skip to content

Testing guide

This repo is a teaching project for Playwright. Work through the course home for the hands-on path. House style for humans and agents: AGENTS.md. AI tools: AI-TESTING.md.

Clone the repository on GitHub to run tests. Try the live demo app in the browser.

Learning path (mapped to labs)

StepConceptModule
0Course loop00 Start here, learn-lab-coach skill (local clone)
1Config, projects, webServer, workers01 Overview
2First test and .env02 First test
3Locators, web-first, ARIA03 ARIA snapshots
4UI Mode, traces04 Debugging
5Auth setup and storageState06 Auth setup
6beforeEach, fixtures, helpers07 Fixtures
7Network mocking and API request08 Network and API
8Isolation (workers: 1, mock reset)Config and base-test.ts on GitHub
9AI writing path09 AI path, AI testing
10ExercisesExercises (GitHub)
BonusClassic POM (optional)11 Page objects

Learn style from manage-lists-*. Treat lists/* as broader product coverage, not the primary style guide.

Suite map

Canonical teaching files

FileWhat it teaches
tests/logged-in/manage-lists-before-each.spec.tsShared beforeEach with multiple tests in one file
tests/logged-in/manage-lists-fixtures.spec.tsCustom fixtures, test.step, ARIA snapshots
tests/helpers/list-fixtures.tsOptional list fixtures on one test export (pick the lightest seed)
tests/helpers/list-utilities.tsReusable flow helpers (createList, addMovie, …)
tests/logged-in/lessons/*.spec.tsFocused lessons (network abort, multi-tab, viewport, AI rewrite)
tests/helpers/base-test.tsAuto mock-API reset for logged-in tests

Logged-out examples (tests/logged-out/)

FileConcepts
auth.spec.tsLogin/logout UI, permission gate
search.spec.tsSearch + ARIA snapshots (local helper; canonical for this flow)
lessons/pom-search.spec.tsSame search flows via a Page Object (comparison only)
sort-by.spec.tsSorting UI + page.route JSON fixtures
movie.spec.ts / movie-list.spec.tsDetail pages, links, mocking external sites
navigation.spec.tsMenus, genres, test.use({ viewport })
pagination.spec.tsPagination
api.spec.tsrequest against the mock TMDB API
dark-mode.spec.tsTheme switching
person.spec.ts / not-found.spec.tsPerson page, 404

Logged-in coverage

PathRole
tests/logged-in/login.setup.tsAuth project dependency → storageState
manage-lists-*.spec.tsStyle guide for list tests
tests/logged-in/lists/*.spec.ts@agent feature coverage (create, edit, delete, …)
Standalone @agent filesDistinct patterns: multi-list delete, private share + guest, auth gate
tests/logged-in/seed.spec.tsAgent seed (skipped in normal runs)

Fixture tiers

List tests share mock-api state, so playwright.config.ts sets workers: 1. Each logged-in test resets the mock API via the _resetMockApi auto-fixture in tests/helpers/base-test.ts. Designing fixtures that own their data is how you unlock parallel workers later.

Import one test and request only the fixtures you need:

typescript
import { expect, test } from '../../helpers/list-fixtures';

test('empty state', async ({ emptyListPage }) => { /* ... */ });
test('add a movie', async ({ listWithMoviesPage }) => { /* ... */ });
test('share a list', async ({ listPage }) => { /* ... */ });
FixturePage stateUse when
emptyListPageNew list, no movies, on Add/RemoveEmpty-state UI, choose-image-without-movies
listWithMoviesPageList + 3 movies, no cover, on Add/RemoveAdd/remove/search/cover flows
listPageFull seed: 3 movies, cover image, on View ListEdit, share, my-lists, navigation, view flows

Helpers such as createList, addMovie, and selectCoverImage live in tests/helpers/list-utilities.ts.

Web-first assertions

Prefer role- and label-based locators over CSS classes or DOM structure:

typescript
await expect(page.getByRole('button', { name: 'Share' })).toBeVisible();
await expect(page.getByRole('list', { name: 'movies' })).toMatchAriaSnapshot(`...`);

Avoid waitForTimeout, force: true, and synchronous .count() checks. Use expect(locator).toHaveCount(n) instead.

The movie add-item search UI exposes role="status", aria-busy, and aria-label="Movie search results" so tests can wait on accessible signals instead of timers. Poster images in the dropdown use empty alt so button names stay as movie titles.

Use test.step to keep traces and reports readable. Prefer expect.soft only when you intentionally want multiple assertions before failing the test (see lessons).

Guest / logged-out contexts

Auth tokens live in localStorage, not only cookies. For guest scenarios while logged-in setup exists, open a fresh context:

typescript
const guestContext = await browser.newContext({
  storageState: { cookies: [], origins: [] },
});
const guestPage = await guestContext.newPage();

See tests/logged-in/access-list-without-authentication.spec.ts and tests/logged-in/share-private-list.spec.ts.

Helpers vs Page Objects

This suite uses flow helpers (list-utilities.ts) and fixtures instead of classic Page Object classes. Helpers stay thin, compose with fixtures, and match how Playwright Test Agents generate steps. Playwright's own docs also lean on locators and fixtures. Default here is helpers.

One optional POM example exists for teams and video/AI demos that already live in that pattern:

  • Page object: tests/pages/search-page.ts
  • Lesson spec: tests/logged-out/lessons/pom-search.spec.ts
  • Helper-style twin: tests/logged-out/search.spec.ts (canonical search coverage)

Do not POM list flows, and do not rewrite the suite. Course walkthrough: 11 Bonus: page objects.

Config notes worth learning

  • Projects: setuplogged-in chrome (depends on setup + storageState); chromium for logged-out.
  • webServer: starts mock API (:4000) and Next app (:3000).
  • baseURL: http://127.0.0.1:3000/ (avoid localhost / IPv6 mismatch with cookies).
  • Artifacts: trace on first retry; screenshot/video on failure. Use Trace Viewer and UI Mode while learning.
  • Mobile projects are commented in config; viewport lessons use test.use({ viewport }) instead.

@agent specs

Specs tagged @agent were generated by Playwright’s test planner/generator and consolidated under tests/logged-in/lists/ by feature (create, edit, delete, add-movies, and so on).

A few flows stay as standalone files when they teach a distinct pattern: multi-list delete, private share + guest context, and the auth gate.

Generator runs may emit one file per scenario; this repo consolidates by feature afterward. Review and rewrite generated tests against the fixture/ARIA style in manage-lists-*. See AI testing.

Running tests

bash
npx playwright test --ui          # interactive
npx playwright test tests/logged-in/lists   # list feature specs only
npx playwright test --grep @agent   # agent-generated coverage only
npx playwright test tests/logged-in/lessons # teaching lessons only
npx playwright test tests/logged-out/lessons/pom-search.spec.ts --project=chromium  # optional POM example

CI shards the suite across four machines. Each shard still uses one Playwright worker because the mock list store is process-global.