Typorithm · medium

16. Capstone: a verified local role workflow

qatestingplaywrightindependent-practice
Saved on this device
Full lesson · 4 learning steps Open any exercise for focused practice
1

Step 1 of 4 · intro card

16. Capstone: a verified local role workflow

0%

Concept and contract

Connect browser actions, API postconditions and account isolation in one small project.

The final project should tell a short, complete story: a fictional user finds a role, saves it and sees the saved state after reload. Keep the data and environment controlled so a failure can be reproduced without a real account, external vacancy feed or production write.

Choose a few high-value observations rather than asserting every implementation detail. The combined filter needs exact visible membership; saving needs a stored postcondition; reload needs persistence within the current synthetic account. Other production risks remain outside this small lab.

Mechanism and worked example

The worked test signs in Alice, applies UA plus qa and expects one role.

The worked test signs in Alice, applies UA plus qa and expects one role. It saves QA Engineer, reloads and reads saved ids through the page’s own API context. The shared cookie jar connects browser interaction with the stored postcondition.

The local namespace keeps the test independent of other workers. A second account case is still needed: persistence for Alice does not prove isolation from Bob. The independent task adds that boundary without turning the project into a full identity platform.

Complete worked example

typescript

import { test, expect } from "../support/test.js";
test("complete local role workflow", async ({page}) => {
  await page.goto("/");
  await page.getByRole("button", {name:"Sign in",exact:true}).click();
  await expect(page.getByText("User: alice", {exact:true})).toBeVisible();
  await page.getByLabel("Country", {exact:true}).selectOption("UA");
  await page.getByLabel("Search roles").fill("qa");
  await expect(page.getByRole("list", {name:"Matching roles"}).getByRole("listitem")).toHaveCount(1);
  await page.getByRole("button", {name:"Save QA Engineer",exact:true}).click();
  await expect(page.getByRole("list", {name:"Saved roles"})).toContainText("r1");
  await page.reload();
  await expect(page.getByRole("list", {name:"Saved roles"})).toContainText("r1");
  expect(await (await page.request.get("/api/saved")).json()).toEqual({ids:["r1"]});
});
Reasoning
The browser actions are connected to exact membership and a persisted account-specific postcondition.

Counterexamples and common mistakes

The weak project stops after clicking Save and finding any success text.

The weak project stops after clicking Save and finding any success text. It may miss the wrong id, duplicate state, lost persistence or another account’s data. The final assertion should address the actual risk rather than a convenient nearby message.

Do not interpret the project as a full accessibility, security or capacity certification. Those topics have scoped checks in earlier lessons and require broader evidence in a real release. A useful capstone states its limits clearly.

Counterexample: what this check misses

typescript

// Clicking Save without checking the id or persisted state leaves the core contract unobserved.
Reasoning
A click alone does not prove which record was saved or whether it survives reload.

Independent practice: small task

Extend the workflow with a foreign-account read that must return 403 and an owner saved-list check that must remain unchanged.

Extend the workflow with a foreign-account read that must return 403 and an owner saved-list check that must remain unchanged. Start with the owner’s known r1, then make the denied request and read back the owner state.

Write a short result note naming the fixture, checks, outcomes and untested risks. Optional extension: add an error-and-recovery branch. The core task remains a small complete workflow rather than a framework-building exercise.

Step 1: Create and confirm Alice’s r1 state. Step 2: Check the foreign denial and unchanged owner list.

Practice starter: solve before reading the solution

typescript

import { test, expect } from "../support/test.js";
test("denied foreign read leaves owner state intact", async ({request}) => {
  // TODO: create owner state, make the denied request and inspect the owner list.
});
Reasoning
Establish owner state before testing a foreign-account denial.
  • Hint 1: A click must connect to a meaningful postcondition.
  • Hint 2: Keep all writes inside the local lab.

Solution, tests and trade-offs

The solution checks denial and unchanged owner data through the real local API.

The solution checks denial and unchanged owner data through the real local API. The worked browser scenario separately establishes the user-facing save and reload path. Together they provide complementary observations rather than assuming one green click proves every boundary.

The verifier runs all authored executable examples, checks the manual evidence fixtures and performs controlled failure experiments. The mobile app presents explanations, code and self-checks; actual browser automation runs on a computer through the downloadable lab.

Practice solution and acceptance checks

typescript

import { test, expect } from "../support/test.js";
test("denied foreign read leaves owner state intact", async ({request}) => {
  await request.post("/api/login", {data:{user:"alice"}});
  await request.put("/api/saved/r1");
  expect((await request.get("/api/accounts/bob/saved")).status()).toBe(403);
  expect(await (await request.get("/api/saved")).json()).toEqual({ids:["r1"]});
});
Reasoning
Denial and unchanged owner state are separate observations through the real local API.

Interview reasoning and next step

In interviews, present the chain from requirement to independent oracle, interaction, stored observation and conclusion.

In interviews, present the chain from requirement to independent oracle, interaction, stored observation and conclusion. Explain one deliberate wrong implementation that the test rejects. State which additional risks you would investigate next.

After completing the core tasks, revisit the optional extensions selectively. Progress comes from explaining why a check is useful and diagnosing a failure, not from memorizing every test-runner option or writing the most elaborate abstraction.

Optional stretch — after the core task works: Add one controlled failure-and-recovery workflow.

Computer practice: download the companion lab at https://applydjinn.com/typorithm/qa-lab-20261105.zip. Read its README, install the pinned tools and run the accepted examples before attempting a starter. The mobile reader preserves your reading and self-check progress; the local lab runs browser tests separately.

Connect browser actions, API postconditions and account isolation in one small project.