Developers

Testing

The test suites — PHPUnit functional tests, Vitest unit tests, Playwright end-to-end.

Tests live with the code: core keeps three PHPUnit suites in tests/Unit, Functional and Integration — alongside Foundry factories and stubs; client keeps Vitest specs in tests/unit/ and tests/components/, and the Playwright suite in tests/e2e/. All commands below run inside the DDEV environments.

Core — PHPUnit

ddev test                             # whole suite
ddev test --testsuite Unit            # one suite (Unit · Functional · Integration)
ddev test tests/Functional/Api        # one directory or file
ddev test --filter=testPinMessage     # one method

ddev test prepares the test database on first run (schema, JWT keys) — no manual setup. The functional suite drives the real HTTP kernel against MariaDB; each test runs inside a transaction that’s rolled back afterwards (DAMA), so tests are isolated and fast. Fixtures are built with Foundry factories (tests/Factory/). External services are stubbed for the Unit and Functional suites: no real Mercure publishes, in-memory search and presence — so those two need only MariaDB. The Integration suite deliberately runs against real Valkey/Redis, which is the only automated coverage of the Redis-backed stores.

Static analysis and code style gate every change:

ddev composer phpstan   # level 7
ddev composer checkcs   # or fixcs to auto-fix

Client — Vitest (unit)

ddev npm test           # or run test:watch / run test:coverage

These run fully in-process — no browser, no backend. tests/unit/ covers pure logic (utils, reducers, cache patchers); tests/components/ renders components against a mocked API layer. Flows that need a real server — auth, realtime, permissions — are covered by E2E instead.

Client — Playwright (end-to-end)

ddev e2e                              # headless; resets core's test DB first
ddev npm run test:e2e:headed          # watch the browser
ddev npm run test:e2e:ui              # Playwright UI mode

The E2E suite needs both stacks running (core + client), and ddev setup run once in the client to write .env.local and fetch the Playwright browser. ddev e2e resets core’s test database first (it calls core’s reset-test-db command), then runs the specs in parallel workers — each worker seeds its own isolated community and accounts, so they never share mutable state.

Tip

E2E is the slowest suite — the usual loop is unit + typecheck + lint while iterating, one ddev e2e before a PR.