Your login works and your payment code works. Each piece passes its own test. Yet a real user still gets stuck halfway through checkout, because the break lives in the seam where two pieces meet for the first time. This chapter gets you a handful of tests that walk each whole journey, front to back, the way a real user would.
7.3.1Drive the app like a user
An end-to-end test opens a real browser and drives your whole app the way a person does, clicking through real screens and typing into real fields. Where a unit test checks one function alone, an end-to-end test exercises every layer at once: the button, the code behind it, the database, and the screen that comes back.
A tool like Playwright runs that browser for you, invisibly and in seconds. Your agent writes the script; you just name the journey it should walk.
7.3.2Cover the critical journeys
You already know your critical paths from the safety-net chapter. A critical journey is one of those paths walked end to end:
- sign up, then land on the dashboard
- add an item, then pay for it
- create the one thing your app exists to make
Pick three or four. Here is one test, for a new user who signs up and creates their first project:
test("a new user signs up and creates a project", async ({ page }) => {
await page.goto("/signup");
await page.fill("#email", "[email protected]");
await page.fill("#password", "hunter2pw");
await page.click("text=Create account");
await expect(page).toHaveURL("/welcome");
await page.click("text=New project");
await page.fill("#title", "My first project");
await page.click("text=Save");
await expect(page.locator("text=My first project")).toBeVisible();
});
7.3.3Keep them few and stable
End-to-end tests are slow and heavier to maintain than the small ones, so keep them to a handful, the few journeys that lose you customers when they break.
Keep them stable, too. A flaky test passes and fails at random on the very same code. It trains you to shrug at red, and a net you have learned to ignore is no net at all.
Watch out: Flakiness almost always means the test waits on a fixed timer instead of a real condition. Tell your agent to wait for the element or the URL to appear, never for a set number of seconds.
7.3.4Run them before every ship
A journey test is only worth writing if it actually runs. Run the whole set before every ship. Then a break lands on your screen while you can still fix it, not in a message from an angry user.
Make it part of the checks your agent runs before it calls a task done, the same gate you set up for the safety net. A later chapter makes that gate run on its own, on every change.
This prompt sets your agent up to write them:
Act as a senior engineer writing end-to-end tests
for my app. For each journey I list below, write one
test that drives a real browser through the whole
flow the way a user would: visit the page, fill the
fields, click through every step, and assert on what
the user should finally see. Use Playwright. Keep
the set small and stable: wait for real elements and
URLs, never fixed timers. Then add the suite to the
checks you run before calling any task done, and
tell me which journeys you covered.
My critical user journeys:
Do this now: paste the prompt, list your three or four critical journeys, and let your agent write one end-to-end test for each, then wire the suite into the checks it runs before every ship.