Skip to content
Independent guides for QA & test automationRSSEditorial policy
QA Vibes

Practice

Why did this test pass?

Three case files from the practice shop: predict what a test does on a buggy build, find out why, see the repair, then try the same reasoning on a different bug. Every result is a real Playwright run.

How the cases work

Each case starts from a bug report and a test that got the wrong answer. You predict what the test does before you see the result, pick the reason, and then get the repaired test and a different bug to try the same habit on.

The builds are the practice lab's: clean is the correct shop, and v01 to v10 each have one bug. The cases give some of those bugs away, so if you want to find them yourself first, do the practice lab before this page.

Where the results come from

We ran every test on this page with Playwright 1.63.0 in Chromium on 21 September 2026, using the practice lab's config unchanged. Error messages are Playwright's own, cut to the lines that explain the result; … marks a long attribute we removed.

Every test here is also run against the shop each time this site changes, and the site's test suite fails if any pass or fail shown on this page stops being true.

The spec file

Every test on this page belongs in one file under this header, for example tests/diagnose.spec.ts. The tests are plain JavaScript, and Playwright runs them unchanged from a .ts file. Run it with the practice lab's config to see these results yourself.

import { test, expect } from "@playwright/test";

// The practice shop's published demo password (see /practice). It unlocks nothing else.
const SHOP_PASSWORD = "practice-only";

test.beforeEach(async ({ page }) => {
  await page.goto("/practice/shop");
  await page.getByLabel("Username").fill("shopper");
  await page.getByLabel("Password").fill(SHOP_PASSWORD);
  await page.getByRole("button", { name: "Log in" }).click();
});

async function addToCart(page, name) {
  await page.getByTestId("product").filter({ hasText: name }).getByRole("button", { name: "Add to cart" }).click();
}

async function checkOut(page, names) {
  for (const name of names) await addToCart(page, name);
  await page.getByTestId("cart-link").click();
  await page.getByRole("button", { name: "Checkout" }).click();
  await page.getByTestId("firstName").fill("Ada");
  await page.getByTestId("lastName").fill("Lovelace");
  await page.getByTestId("postalCode").fill("10115");
  await page.getByRole("button", { name: "Continue" }).click();
}

Case 1 · build v02

A green test for a broken Remove button

Support ticket: “I removed the hoodie from my cart and the mug disappeared instead.” The build it came from is variant v02. The team's remove test is green on it.

test("remove leaves one item", async ({ page }) => {
  await addToCart(page, "Assertion Mug");
  await addToCart(page, "Locator Hoodie");
  await page.getByTestId("cart-link").click();
  await page.getByTestId("cart-item").filter({ hasText: "Locator Hoodie" }).getByRole("button", { name: "Remove" }).click();
  await expect(page.getByTestId("cart-item")).toHaveCount(1);
});
1. Predict. Before you look: on v02, what does this test do?

Case 2 · build v04

The right assertion with the wrong cart

Support ticket: “I bought two things and the subtotal was less than the prices added up.” The build is variant v04. The subtotal test is green on it, and it compares the exact amount.

test("the subtotal matches the price", async ({ page }) => {
  await checkOut(page, ["Assertion Mug"]);
  await expect(page.getByTestId("subtotal")).toHaveText("$12.99");
});
1. Predict. Before you look: the assertion is exact, to the cent. What does it do on v04?

Case 3 · build v02

A red test that caught nothing

A colleague wrote a new remove test and ran it against v02, the Remove bug from the first case. It failed. “Caught it,” they say.

test("remove a product", async ({ page }) => {
  await addToCart(page, "Assertion Mug");
  await addToCart(page, "Locator Hoodie");
  await page.getByTestId("cart-link").click();
  await page.getByRole("button", { name: "Remove" }).click();
  await expect(page.getByTestId("cart-item")).toHaveCount(1);
});
1. Predict. Did this test catch the Remove bug?

Limits

The answers are written for these tests. The page checks which option you picked, not your reasoning, and it can't run code you write. For that, write your own suite and score it in the practice lab, which tells you which bugs it catches and which assertion habits it's missing.

Timings and the aka suggestions in Playwright's errors can differ from run to run. Pass or fail doesn't, and that is what the site re-checks.