01
Setup & Tooling
Academy Student - Academy EnrollmentBefore you write a single test, you need Playwright installed, configured, and running. This rank covers: npm init playwright@latest, the config file, codegen, trace viewer, and your first raw test against the Training Ground.
THE SETUP
Run npm init playwright@latest. Choose TypeScript. Accept the defaults. Open the Training Ground at /dojo/app and run npx playwright codegen against it. Record one action. That's your first test.
02
Locator Discipline
Academy Graduate - Graduation ExamYour first real tests. The goal isn't just green — it's tests that survive a redesign and actually catch bugs. The locator you reach for first decides both.
THE RULE
Locator priority: role / label / text → testid → CSS last. And never waitForTimeout — web-first assertions auto-wait.
✓ GOOD - role-based, web-first
search.spec.ts
import { test, expect } from '@playwright/test';
test('search narrows the menu', async ({ page }) => {
await page.goto('/dojo/app');
// role + label work because the
// markup is built for it
await page.getByRole('searchbox',
{ name: 'Search menu' }).fill('miso');
await expect(page.getByRole('listitem'))
.toHaveCount(1);
await expect(page.getByText('Miso Ramen'))
.toBeVisible();
});
✗ BAD - the common AI mistake
search.spec.ts
test('search', async ({ page }) => {
await page.goto('/dojo/app');
// brittle CSS, coupled to layout
await page.locator('div > input.search')
.fill('miso');
await page.waitForTimeout(2000);
// asserts existence, not the result
const n = await page.locator('.item').count();
expect(n).toBeGreaterThan(0);
});
Pitfalls
- Hardcoded waits hide races and make the suite slow. Use web-first assertions that retry until the condition is true.
- Asserting existence (
count() > 0) passes even when the value is wrong. Assert the real, user-visible state. - nth-child CSS chains break the moment a wrapper div moves. Reach for
testidbefore CSS.