Home / Articles

Three Playwright Agents: Planner, Generator and Healer on a Live Cart App

A Playwright framework where one agent plans coverage, one generates specs from live browser sessions and one heals failures, giving 22 parallel tests in about 16 seconds.

Publisher: REACTAPP.TOP8 min read1753 words

This article is published in English.

Sources: Planner, Generator, Healer: How I Ran a Full Playwright Suite With Three AI Agents And Playwright MCP

Most people have watched the demo: prompt a model, receive a Playwright script, run it once, and see it fall apart a sprint later. A working QA team follows a different loop of explore, plan, automate and maintain, and the aim here was to give an agent ownership of each of those stages. The result is a Playwright framework that can be reused, with three specialist agents behind it:

  • Planner explores the live application and writes the test plan.
  • Generator turns a single planned scenario into a single working spec.
  • Healer executes the suite, works out why tests fail and repairs the underlying cause.

The demo target was a small cart app with a flow from login to products, cart, checkout and a thank-you page. It ended up with 22 automated scenarios covering happy paths, negative cases and validation edge cases. They run in parallel, on top of page objects that the agents maintain themselves. The Playwright HTML report showed 22 passed, 0 failed, 0 flaky, with the whole suite done in 16.2 seconds.

Groundwork before any agent runs

Three things have to exist first. Most walkthroughs skip them, and they decide whether the generated output is usable at all.

Playwright's MCP test server. Agents shouldn't guess at the DOM, so they drive a real browser. One config file wires them up:

{
  "servers": {
    "playwright-test": {
      "type": "stdio",
      "command": "npx",
      "args": ["playwright", "run-test-mcp-server"]
    }
  }
}

That gives each agent tools such as browser_navigate, browser_snapshot and browser_click, plus test_run and test_debug for the Healer.

A framework contract. One framework.md file lays out the folders and conventions: selectors belong in pages/, user journeys in flows/, and addresses plus credentials in .env. Each agent reads it before doing anything, so the results look like a single code base rather than three personal styles.

A seed test. One tiny spec opens the real home page and asserts something stable. Each generated test begins from that known-good state.

An agent is only as good as the rails it runs on, and these three are the rails.

The Planner: deciding what deserves a test

The Planner writes no code at all. It opens the application, moves around like a curious tester and maps what exists: forms, authentication, the cart, checkout steps, validation messages and empty states.

Its routine:

  1. Call planner_setup_page one time.
  2. Explore with the browser tools, reading DOM snapshots instead of screenshots.
  3. Pick out the main journeys and the ways each can fail.
  4. Save a markdown plan to specs/<feature>-plan.md.

Each scenario in the plan carries a title, numbered steps, an expected outcome, an assumption about starting state (a fresh session unless stated) and the file path it will become.

The checkout plan came out as 22 scenarios in sections for Login, Cart, Checkout and one full end-to-end journey. The structure pays off later, because the Generator turns each section name into a test.describe block and the plan and code stay aligned by construction.

Why a separate stage? Coverage is a thinking problem. When one prompt must plan and code at once, the planning gets worse. Plan-only output can be reviewed and edited before a single line of test code exists.

The Generator: making one scenario real

The Generator handles exactly one scenario at a time, never the whole plan. For each one it:

  1. Runs generator_setup_page to reach the seed state.
  2. Executes every step live in the browser, treating the step text as intent.
  3. Reads back the execution log with generator_read_log, which holds the locators and waits that really worked.
  4. Calls generator_write_test to emit a single spec file.

Step three is where the value comes from. Nothing is remembered from training data. What lands in the file is a record of a session that has just passed against the real interface.

The output obeys the framework rules automatically:

// spec: specs/checkout-e2e-plan.md
// seed: tests/web/seed.spec.js
import { test, expect } from '../../../fixtures';
import { users } from '../../../testdata';test.describe.configure({ mode: 'parallel' });test.describe('E2E checkout journey', () => {
  test('positive: login → add product → checkout → thank you → home', async ({ flow }) => {
    await flow.completeHappyPathPurchase();
  });
});

Notably absent from the output are CSS selectors, a literal password and URL strings. Selectors get moved into page objects, and multi-step journeys become methods on a flow class:

async completeHappyPathPurchase(user = users.standard, info = checkout.valid) {
  await this.goToCheckoutInformation(user);
  await this.fillValidInformation(info);
  await this.continueToOverview();
  await this.app.checkoutOverview.expectProductVisible(products.first.name);
  await this.finishOrder();
  await this.backHomeToProducts();
}

Every test also receives its own browser context, page-object graph and flow instance through fixtures. With no shared mutable state, the suite is parallel-safe from the start and not as an afterthought. That separates a script from a framework.

The Healer: fixing the right layer

Generating tests is a one-time win. Maintenance is the permanent cost, and that is where the third agent earns its place. Its loop:

  1. test_run executes the suite and collects failures.
  2. test_debug pauses on each failure.
  3. It inspects the live snapshot, console, network traffic and the failing assertion.
  4. It diagnoses the cause: a changed locator, shifted timing, a changed contract or a genuine product bug.
  5. It repairs the source and not the symptom, updating pages/ when the UI moved and api/endpoints/ when a contract moved.
  6. It re-runs that test and moves to the next failure.

Two guardrails keep it trustworthy. It never weakens an assertion just to get a green run. If the product is wrong and the test is right, it marks the test with test.fixme() and a comment describing the real behavior, so the failure stays visible and doesn't get buried. It also avoids discouraged APIs such as networkidle as a patch over a race.

The benefit is concentration of change. Rename a button and a single page-object method is edited, after which every test using it recovers. Since 22 tests share 325 lines of page objects spread over 10 classes, a redesign doesn't sink the suite. Taken together, each agent owns a clear part of the repository, and the shared page-object layer is what holds the whole arrangement up.

Running it from start to finish

The setup commands:

1. Copy .env.example → .env, set BASE_URL (+ credentials)
2. npm install && npx playwright install

And the agent runs, in order:

3. Run the Planner
      → specs/checkout-e2e-plan.md   (22 scenarios, reviewed by me)4. Run the Generator, scenario by scenario
      → tests/web/login/login.spec.js
      → tests/web/cart/cart.spec.js
      → tests/web/checkout/checkout.spec.js
      → tests/web/e2e/checkout-journey.spec.js5. npm test        (4 workers, fully parallel)6. Any red? Run the Healer → re-run → green

Moving to another product leaves the agents untouched. Set BASE_URL to the new product, redo the page objects and test data, then issue the same three prompts again. Because the agents read framework.md and .env instead of a hard-coded product name, the pipeline is portable and not bespoke.

How much faster is it, really?

A claim like "AI made it ten times faster" shouldn't be accepted without a breakdown. Writing an end-to-end test by hand is rarely about typing. It means opening DevTools, hunting for a stable selector, finding the button inside an odd wrapper, re-running to see the assertion fire too early, and starting over. Most of the cost lives in that discovery loop, and the Generator absorbs it by executing steps live and reporting what worked.

The gains appear at several stages:

  • Deciding coverage. By hand it's ad hoc and often skips negative cases. The Planner enumerates happy, negative and edge scenarios up front.
  • Finding locators. Manual DevTools hunting per element gives way to locators captured from a session that already passed.
  • First green test. The write, run, fix and repeat cycle turns into generation from a verified transcript.
  • Keeping the suite alive. Grepping every spec for a broken selector gives way to one page-object fix that heals every dependent test.

The biggest savings come from the second through twentieth test. The first one carries real setup cost for page objects, fixtures and the seed. From then on a new scenario is mostly assembly from existing parts, and the cost per test drops quickly.

Speed of execution comes from somewhere else entirely. Parallelism isn't an AI feature. It's a design rule that the agents must respect. Each test gets a fresh context from fixtures and none mutates shared module state, so the config runs fully parallel: four workers locally, two in CI with two retries, with traces and video recorded only when a test fails. Any suite that reuses one login session can't run like that without risk. This one can because one scenario equals one independent test was a rule the Generator had to follow in every file.

Four lessons worth borrowing

  • Keep roles narrow. A single sprawling prompt that plans, codes and debugs yields muddy results. Three focused roles, each with its own tools, leave something reviewable behind at every stage.
  • Trust execution over memory. Locators confirmed in a running browser are more reliable than ones guessed from docs.
  • Build the skeleton before the agents. Page objects, fixtures, env handling and parallel rules are what make AI output a framework and not a heap of scripts. Writing framework.md improved quality more than any prompt adjustment.
  • Repair is the real deliverable. Producing a test once is easy. An agent that keeps 22 tests passing after a UI refresh is what justifies adopting the approach.

Who this fits

QA engineers who spend more time repairing locators than designing coverage will see the benefit. So will teams adopting Playwright who want AI inside a disciplined structure and not replacing it, and anyone comparing Copilot or Cursor agents against Playwright's MCP test server.

None of this replaces QA. The judgment about what matters, what counts as a real bug and what coverage is missing stays with a person. The agents take the exploring, the boilerplate and the repair grind, which makes the loop plan, generate, run, heal and run again.