Three layers, run together — each one blind to what the others cover.
None of these three substitute for each other. A backend that's provably correct and a
frontend that's provably correct in isolation still don't prove the two are wired together
correctly for a real user in a real browser.
Pest 4
Backendcontrollers, models, jobs, validation
Proves
The right Inertia component and props come back, redirects and validation errors fire correctly, database state ends up right.
Blind to
Never renders a React component or executes any JavaScript.
Missed it:TeamMemberIndexTest.php posted via directly to the route — it never caught the frontend silently dropping that field on every real click.
Vitest
Frontend, in isolationReact Testing Library, mocked router
Proves
Given a set of props, the right thing renders — buttons shown or hidden, and clicking something calls the correct operation with the correct payload.
Blind to
Every router call is mocked — no backend code runs, nothing touches the database.
Missed it: a component can pass every test here while the button it's testing stays permanently disabled in the real DOM — see the !destinationId falsy-zero bug, caught only by a live click.
Playwright, by hand
Real browserreal HTTP, real database writes
Proves
The whole application actually works end-to-end, for a real user, on the actual seeded dev server.
Blind to
Nothing structurally — but it's manual, so it only covers what actually gets clicked through.
Caught it: a window.prompt() failure invisible to jsdom, and a docker.sock stale-socket bug — an infrastructure problem neither test suite could ever see.
∴
All three run together, not as substitutes — backend-correct plus frontend-correct-in-isolation still doesn't prove the two are wired together for a real user in a real browser.