A learning repository that demonstrates Acceptance Test Driven Development (ATDD) using Playwright on a React application. The application itself is a Tic Tac Toe game — a deliberate choice to keep the domain familiar so attention stays on the testing patterns rather than the business logic.
This project is structured to teach:
- ATDD workflow — writing tests before implementation, using
then.skipto stub out behaviour and graduating tothenas each layer is built - Given/When/Then test structure — tests read as human-readable specifications using aliased Playwright wrappers
- Two-layer DSL architecture — a business-logic DSL layer (
*Dsl.ts) wraps a DOM-interaction Playwright layer (*Playwright.ts), decoupling tests from selectors and implementation details - Architecture boundaries — ESLint-enforced rules that prevent layers from importing outside their permitted scope
- Monorepo layout — three separate packages (
webapp,acceptance-tests,shared) with a single install
Create an acceptance test to update the game over screen to show the board state as well. Do not do the implementation yet.
The top level given will be "a Player wants to see the game board when a round has finished".
I want you to test both x and o winning, and draw situation.
Put those in separate nested givens like `given("player X wins"` etc
Now do the implementation
Run the build and fix any issues, and then run the UI tests and fix any issues with the UI tests
playwright-atdd-example/
├── webapp/ # React application (Vite + Zustand + Tailwind)
├── acceptance-tests/ # Playwright acceptance tests with DSL layer
├── shared/ # Shared types and utilities used by both packages
└── .agents/ # Agent skills for this project
See the README in each folder for more detail.
| Area | Tools |
|---|---|
| Application | React 19, Vite, React Router, Zustand, Tailwind CSS |
| Unit tests | Vitest |
| Acceptance tests | Playwright |
| Language | TypeScript |
| Package manager | pnpm (workspaces) |
Install the following tools before getting started. Each link points to the official installation guide for that tool.
| Tool | Purpose | Install guide |
|---|---|---|
| Node.js | JavaScript runtime | nvm (recommended) — install nvm, then run nvm install in this repo to pick up the .nvmrc version |
| pnpm | Package manager | pnpm installation |
| Git | Version control | Git downloads |
Windows users (WSL): This project is intended for Linux-based environments. Use WSL 2 with a Linux distribution rather than native Windows tooling.
Git line endings: On Windows/WSL, configure git to avoid line-ending conflicts:
git config --global core.autocrlf false git config --global core.longpaths true
- Prettier — code formatting
- ESLint — linting
- Vitest — unit test runner in the editor
- Playwright — acceptance test runner in the editor
- Tailwind CSS IntelliSense — Tailwind class suggestions
pnpm install
cd acceptance-tests && pnpm run setuppnpm run setup installs the Playwright browser binaries required to run acceptance tests.
cd webapp && npm run start:devThe app runs at http://localhost:5173 by default.
Run from the repository root unless otherwise noted.
| Command | Description |
|---|---|
pnpm build |
Build all packages — runs TypeScript checks, lint, and tests across the monorepo |
pnpm test |
Run all unit tests (Vitest) |
pnpm run typescript:checks |
Type-check all packages |
pnpm lint --fix |
Fix formatting and lint issues |
cd acceptance-tests && pnpm test:ui |
Run all acceptance tests |
cd acceptance-tests && pnpm test:ui -- src/tests/path/to/File.ui.ts |
Run a single acceptance test file |
Tests follow a Given/When/Then structure using aliased Playwright functions:
import {
given,
when,
then,
setup,
expect,
} from "@/playwright-alias/PlaywrightAlias";
given("a Player is on the home page", () => {
when("they navigate to the app", () => {
then("the title should be visible", async ({ ticTacToe }) => {
expect(await ticTacToe.home.getTitle()).toContain("Tic Tac Toe");
});
});
});given— the initial context (wrapstest.describe)when— the action or event being tested (wrapstest.describe)then— the expected outcome (wrapstest)setup— shared setup steps within agiven/whenblock (wrapstest.beforeEach)ticTacToe— the root DSL fixture, the entry point into the DSL layer
The acceptance-tests/ README explains the DSL architecture in more detail.
ESLint rules enforce the following import constraints:
state/**cannot import fromview/**shared/**cannot import fromview/**view/components/**cannot import fromview/pages/**acceptance-testsandwebappcannot import from each other — shared code lives inshared/