BXUP Track progress & test yourself — free
QA & Testing · Hands-on lab · ~5 min read
QA & TESTING · HANDS-ON LAB

Lab 02 · Exploratory Session with Playwright MCP

Charter an exploratory testing session, then let an LLM drive a real browser through the Playwright MCP server while you direct it toward risk areas. The demo app you will build in two minutes ships with seeded bugs; your session is scored against the known-bug list in the answer key.

Arjun is an SDET who has heard "AI can explore your app" a dozen times and believes none of it. This lab settles it with data: you stand up a small snack-ordering app with five planted defects, write a session charter the way session-based test management prescribes, and then tour the app twice — once directing the AI-driven browser, once hands-on-keyboard for anything the agent can't feel. The agent sees the app through accessibility-tree snapshots, which means it is genuinely good at some bug classes and structurally blind to others. Finding out which is which — with your own bug tally as evidence — is the lab.

Time, tier & cost

~90 minutes hands-on · Core tier (maps to module M3 and playbook PB-16). Cost: $0 with Claude Code free allowance or Copilot; the app is local.

Setup

You need Node 18+ and Claude Code (or any MCP-capable client such as VS Code Copilot agent mode).

mkdir -p qa-lab-02 && cd qa-lab-02
npm init -y && npm install express@4
claude mcp add playwright -- npx @playwright/mcp@latest

server.js — the app under test. Type it in as-is; do not fix anything you spot:

const express = require("express");
const app = express();
app.use(express.urlencoded({ extended: true }));
let orders = [];
const MENU = [
  { id: 1, name: "Samosa", price: 20, tag: "veg" },
  { id: 2, name: "Chicken Roll", price: 60, tag: "nonveg" },
  { id: 3, name: "Masala Chai", price: 15, tag: "veg" },
];
const page = (body) => `<!doctype html><html><head><title>SnackCart</title>
</head><body><h1>SnackCart</h1>${body}</body></html>`;
app.get("/", (req, res) => {
  const tag = req.query.tag || "all";
  const items = MENU; // filter applied below
  const rows = items
    .map(
      (m) =>
        `<li>${m.name} — ₹${m.price} [${m.tag}]
     <form method="POST" action="/order" style="display:inline">
       <input type="hidden" name="id" value="${m.id}">
       <input name="qty" value="1" size="2">
       <input name="note" placeholder="note (required)">
       <button>Add</button></form></li>`
    )
    .join("");
  res.send(
    page(`<a href="/?tag=veg">Veg only</a> | <a href="/?tag=all">All</a>
    <p>Showing: ${tag}</p><ul>${rows}</ul>
    <h2>Orders (${orders.length})</h2>
    <ol>${orders
      .map((o) => `<li>${o.name} x${o.qty} = ₹${o.total} (${o.note})</li>`)
      .join("")}</ol>`)
  );
});
app.post("/order", (req, res) => {
  const item = MENU.find((m) => m.id == req.body.id);
  const qty = parseInt(req.body.qty, 10) || 1;
  orders.push({
    name: item.name,
    qty,
    total: item.price * qty,
    note: req.body.note || "",
  });
  res.redirect("/");
});
app.listen(3000, () => console.log("SnackCart on :3000"));

Run it with node server.js and confirm http://localhost:3000 renders. Create session-sheet.md with the SBTM skeleton:

CHARTER:
TIMEBOX: 45 min (25 agent-driven, 20 manual)
RISK AREAS TOURED:
BUGS (candidate -> verified):
NOTES / QUESTIONS:
AGENT VS HUMAN TALLY:

Steps

  1. Write the charter before touching the app. In session-sheet.md, write a one-sentence charter in the standard form: "Explore ordering and filtering with form inputs and rapid interactions to discover data-integrity and validation bugs." A charter is a search strategy, not a script — if you could write the steps in advance, it would be a regression test, not exploration.
  2. Give the agent the charter, not instructions. In Claude Code, prompt: "Use the playwright MCP tools to open http://localhost:3000. You are my exploratory testing partner. Charter: [paste it]. Tour the app, try boundary and hostile inputs on every field, and report anything where observed behavior contradicts what the UI promises. After each finding, state exact repro steps." Let it run; capture its findings verbatim into the session sheet as candidates — nothing is a bug until you verify it.
  3. Direct the tour toward risk. Agents tour breadth-first and shallow. Redirect at least three times, e.g.: "Try the veg-only filter and check every listed item," "Order with quantity 0, -2, and 9999," "Submit an order with the note left empty," "Click Add twice very fast on the same item." Log which redirects came from your risk instinct — that is the human contribution you are measuring.
  4. Verify every candidate yourself. For each agent finding, reproduce it by hand in a real browser. Promote it to verified only if you can repro it; demote hallucinated findings (agents do report bugs that do not exist — a snapshot misread or a stale page state) and log them as such. Target: at least three verified bugs by the end of this step.
  5. Run the manual sweep. Spend 20 minutes hands-on where the agent is weakest: visual layout (does anything look wrong that reads fine in an accessibility tree?), timing feel, rapid double-submits, and the back button after a POST. Add findings to the sheet tagged HUMAN.
  6. Interrogate the totals. Ask the agent: "Add 2 Samosas and 3 Chais, then verify the order totals are arithmetically correct against the menu prices." Cross-check its arithmetic yourself — an agent confirming a wrong total as correct is a scored event in the answer key.
  7. Write three bug reports. Pick your three strongest verified bugs and write each as: title, environment, exact repro steps, expected vs actual, severity, and which charter risk area it fell under. These are the artifact, alongside the completed session sheet.
  8. Score the session. Open the answer key. Fill in the AGENT VS HUMAN TALLY: for each of the five seeded bugs — found by agent unprompted, found after your redirect, found only manually, or missed entirely. Then write a three-sentence debrief: where did the agent genuinely accelerate you, and where would trusting it alone have left bugs in production?
Pro Tip

The agent reads the accessibility tree, not pixels. Any bug whose only symptom is visual (alignment, truncation, color) or temporal (races, double-click windows) is yours to find — steer the agent at state and data, keep look-and-feel for the manual sweep.

Verify

  • The charter was written before the session and names a target, a resource, and an information goal.
  • The session sheet shows at least three redirects you issued and why.
  • At least three bugs are verified with hand-reproduced steps; any agent hallucinations are logged as demoted candidates.
  • The tally attributes every seeded bug to agent / redirect / human / missed.
  • Three filed-quality bug reports exist.

Self-grading rubric (pass = 7/10):

Dimension012
Charter qualityMissing or a scriptPresent, vagueTarget + resource + goal, drove the tour
Seeded bugs found≤2 of 53–4 of 5All 5
Verification disciplineAgent claims accepted rawMost verifiedEvery candidate hand-verified, hallucinations logged
Direction skillAgent left on autopilotSome redirects3+ risk-driven redirects, logged
Debrief honestyNoneGenericNames agent strengths and blind spots with evidence
▸Answer key — the five seeded bugs and expected attributionoptional deep dive — click to expand

Bug 1 — Veg filter is a lie. /?tag=veg updates the "Showing: veg" label but items = MENU never applies the filter, so Chicken Roll still lists. Detection: agent finds this reliably if redirected to "check every listed item against the filter"; unprompted, agents often click the link, see the label change, and report the filter as working — a confirmation-bias miss worth calling out in your debrief. Expected attribution: redirect.

Bug 2 — Negative and zero quantities accepted. parseInt(req.body.qty) happily takes -2 (order total ₹-120) and 0 is coerced fine too (|| 1 only catches NaN, not 0 — actually parseInt("0") is 0, which is falsy, so qty 0 silently becomes 1: a second, subtler wrong behavior inside the same field). Full credit requires catching both the negative total and the 0-becomes-1 coercion. Expected attribution: agent, after your hostile-input redirect.

Bug 3 — "Required" note is not enforced. The placeholder promises "note (required)" but the server accepts empty notes (req.body.note || ""). A promise made by the UI and broken by the backend — exactly the "contradicts what the UI promises" class the prompt asked for. Expected attribution: agent or redirect.

Bug 4 — Double-submit duplicates orders. Two fast clicks on Add post twice; there is no idempotency, disabled-state, or redirect-before-second-click protection. The accessibility-tree loop is too slow to trigger this reliably — the agent clicks once per action cycle. Expected attribution: human (manual sweep). If your agent "found" it, check whether it actually reproduced it or just theorized; theorizing without repro is a candidate, not a bug.

Bug 5 — Refresh-after-order resubmits (POST/redirect works, back button doesn't). The app does redirect after POST, but browser back to the form then forward, or a rapid back during the redirect window, can repost. More practically: there is no cap on qty — 9999 chais for ₹149,985 goes straight through, no confirmation, no limit. Score either the repost-window finding or the missing-upper-bound finding as bug 5; both are business-rule absences the agent only surfaces under your boundary redirect. Expected attribution: redirect or human.

Scoring the arithmetic check (step 6): totals in this app are actually computed correctly per line — the seeded trap is that the agent, asked to "verify," sometimes asserts correctness without extracting the numbers. If your agent showed its arithmetic (prices × qty per line), log that as good grounding; if it answered "totals are correct" with no numbers, log it as an unverified claim — the same discipline you apply to human testers applies to agents.

Typical honest outcome: agent unprompted 1–2, agent-with-redirects 2, human 1–2, hallucinated candidates 0–2. If the agent found all five unprompted, your redirects probably leaked the answers — note that in the debrief.

Key Takeaway:

An LLM driving Playwright MCP is a tireless exploratory partner with a structural blind spot: it perceives the app as an accessibility tree, so it excels at state, data, and promise-vs-behavior bugs and cannot feel timing, layout, or rapid interaction. Charter it, direct it at risk, verify everything it claims — the session is yours, the agent is the instrument.

This is the free reading view. Open BXUP to track your progress, run the hands-on labs, and prove it with original, exam-pattern assessments.
© Anvi Innovate · BXUP — Be Exponentially Upskilled · BXUP is an independent learning platform, not affiliated with or endorsed by AWS, Microsoft, Google, NVIDIA, IBM, Salesforce, Databricks, Oracle or Anthropic. All trademarks belong to their respective owners. · bxup.in