playwright/README.md
Spin up a full local E2E environment (backend, frontend, docker services, Playwright UI):
./bin/e2e-test-runner
This uses bin/mprocs-e2e.yaml under the hood. If you need to reset the E2E database,
trigger the reset-db process in the mprocs UI.
To run tests against an already-running PostHog instance:
LOGIN_USERNAME='[email protected]' LOGIN_PASSWORD="the-password" BASE_URL='http://localhost:8010' pnpm --filter=@posthog/playwright exec playwright test --ui
You might need to install Playwright first: pnpm --filter=@posthog/playwright exec playwright install
Use the /playwright-test skill to have Claude Code write and validate end-to-end tests for you.
It will explore the UI with Playwright MCP tools, plan the tests, implement them, and run them in a loop until they pass reliably (including a flakiness check with --repeat-each 10).
getByRole) or getByTestId() which maps to data-attr in our config. Add data-attr to components if needed.test.step().page-models/).if) in a test.Flaky tests are almost always due to not waiting for the right thing. Consider adding a better selector, an intermediate step like waiting for URL or page title to change, or waiting for a critical network request to complete.
Loose selectors cause strict mode violations. If a selector matches multiple elements, Playwright will show all matches — use the output to narrow down:
Error: locator.click: Error: strict mode violation: locator('text=Set a billing limit') resolved to 2 elements:
1) <span class="LemonButton__content">Set a billing limit</span> aka getByTestId('billing-limit-input-wrapper-product_analytics').getByRole('button', { name: 'Set a billing limit' })
2) <span class="LemonButton__content">Set a billing limit</span> aka getByTestId('billing-limit-input-wrapper-session_replay').getByRole('button', { name: 'Set a billing limit' })