frontend
E2E tests against the real stack, not mocks
E2E tests against the real stack, not mocks
Unit tests verify functions or components in isolation, usually with dependencies mocked. Playwright drives a real browser against the actual running app — click, fill, wait for the real network response — the way a real user does. It catches integration failures unit tests structurally can't: frontend and backend disagreeing once wired together, an auth flow breaking across a real redirect, anything that only appears with real network latency.
The e2e CI job spins up real Postgres and Redis containers, builds the actual backend, runs real migrations against a real test database, builds the shared frontend packages, then runs Playwright against the genuinely running stack — not a mocked backend, not stubbed responses. Deliberately more expensive than mocking, because it's the only way to catch a class of bug where the frontend and backend each look correct alone and disagree together.
The cost is speed and flakiness. Spinning up a database, running migrations, building packages and launching real servers is slow, and timing-sensitive waits, network conditions and animation transitions all become sources of failure. That's exactly why it's its own longer CI job rather than mixed into the fast lint-and-typecheck jobs — a slow, occasionally flaky suite shouldn't block the fast feedback loop of every push.
E2E_START_SERVERS is a CI-specific toggle telling the setup to boot the dev servers as part of the run, rather than assuming they're already up — true for local interactive development, never true on a cold CI runner. That kind of environment-aware toggle is what makes one suite work both against a server a developer already has open and against a completely cold start.
With three separate apps, the setup has to decide which need coverage and point Playwright at the right base URL for each — one config hardcoded to one URL wouldn't cover a flow spanning multiple apps, like a staff action in admin that a customer should then see reflected in web.
With no one else reviewing changes, this is one of the few automated nets that can catch a broken user-facing flow before it reaches production. That said — I got caught trusting my own Playwright specs once, when they turned out to only cover flows I'd already found and fixed. A regression suite is a record of past bugs, not a search for new ones.