Regression bench: the old build's 38 scenarios mapped in tests/e2e/BENCH.md, 20 new Playwright tests, CSP-violation guard on every test

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
2026-10-30 18:17:19 -06:00
co-authored by Claude Opus 5.5
parent c52edfa9b1
commit a5246e2aa5
26 changed files with 999 additions and 31 deletions
+156
View File
@@ -0,0 +1,156 @@
import { expect, test } from "./helpers";
import { makeDraft, runSuffix } from "./pmhelpers";
import { actor, api, closeAll, packet, type Actor } from "./shophelpers";
import { getJson, type Json } from "./whhelpers";
// Phase 10, the old tunits on the new UI. clerk.clara makes a project with three units at once (the Add units sheet's
// "Several units"), a duplicate unit number is refused, a packet moves to another unit through the two-step picker,
// and a closed unit takes no new packet. clerk.leo (Clerical supervisor) renumbers a unit; the packet follows, the
// old number stays an alias and cannot be taken again. clara has no Renumber, and the warehouse cannot move a packet.
let RUN = "";
let PROJECT = "";
let projectId = 0;
const units: Record<string, Json> = {};
let packetId = 0;
async function unitsOf(a: Actor): Promise<Json[]> {
const p = await getJson(a.page.request, `/api/projects/${projectId}`);
return (p.units ?? []) as Json[];
}
test.describe.serial("projects and units", () => {
test("tunits: a new project with three units at once; a duplicate unit is refused", async ({ browser }, info) => {
RUN = runSuffix(info.project.name);
PROJECT = `U${RUN}`;
const clara = await actor(browser, "clerk.clara");
const page = clara.page;
await page.goto("/clerical");
await page.getByRole("button", { name: "New project" }).click();
const proj = page.getByRole("dialog", { name: "New project" });
// typed in lower case: the number is kept in capitals
await proj.getByLabel("Project number").fill(PROJECT.toLowerCase());
await proj.getByLabel("Project name").fill("CI units");
await proj.getByLabel("Customer", { exact: true }).fill("CI Customer");
await proj.getByRole("button", { name: "Create project" }).click();
const add = page.getByRole("dialog", { name: "Add units" });
await expect(add).toBeVisible();
await add.getByRole("group", { name: "One or several" }).getByRole("button", { name: "Several units" }).click();
await add.getByLabel("How many").fill("3");
await add.getByLabel("Starting lot").fill("001");
await expect(add.getByText(`Numbers: ${PROJECT}001, ${PROJECT}002, ${PROJECT}003`)).toBeVisible();
await add.getByLabel("Description (all of them)").fill("CI separator");
await add.getByRole("button", { name: "Add", exact: true }).click();
await expect(page).toHaveURL(/\/clerical\/projects\/\d+$/);
projectId = Number(new URL(page.url()).pathname.split("/").pop());
await expect(page.getByRole("heading", { level: 1, name: `${PROJECT} · CI units` })).toBeVisible();
for (const lot of ["001", "002", "003"]) {
await expect(page.getByRole("row", { name: new RegExp(`${PROJECT}${lot}`) })).toBeVisible();
}
for (const u of await unitsOf(clara)) units[String(u.lot)] = u;
expect(Object.keys(units).sort()).toEqual(["001", "002", "003"]);
// The same unit number again is refused with the reason
await page.getByRole("button", { name: "Add units" }).click();
await expect(add).toBeVisible();
await add.getByRole("group", { name: "One or several" }).getByRole("button", { name: "One unit" }).click();
await add.getByLabel("Lot", { exact: true }).fill("002");
await add.getByRole("button", { name: "Add", exact: true }).click();
await expect(add.getByRole("alert")).toHaveText(`${PROJECT}002 already exists as a unit.`);
await add.getByRole("button", { name: "Cancel" }).click();
expect((await unitsOf(clara)).length).toBe(3);
await closeAll(clara);
});
test("tunits: a packet moves to another unit through the two-step picker; the warehouse cannot move one", async ({
browser,
}) => {
const clara = await actor(browser, "clerk.clara");
const u1 = units["001"] as Json;
const made = await makeDraft(clara, { id: Number(u1.id), number: String(u1.number), lot: "001" }, `Move spool ${RUN}`);
packetId = made.id;
const page = clara.page;
await page.goto(`/packets/${packetId}`);
await page.getByRole("button", { name: "Move", exact: true }).click();
const sheet = page.getByRole("dialog", { name: "Move to another unit" });
await expect(sheet).toBeVisible();
// The project is picked already (the packet's own); step two is the unit
await expect(sheet.getByText(PROJECT, { exact: true }).first()).toBeVisible();
// Moving to its own unit is refused in the sheet
await sheet.getByLabel("Unit", { exact: true }).selectOption({ label: `${PROJECT}001 · CI separator` });
await sheet.getByRole("button", { name: "Move packet" }).click();
await expect(sheet.getByRole("alert")).toHaveText("The packet is in that unit already.");
await sheet.getByLabel("Unit", { exact: true }).selectOption({ label: `${PROJECT}002 · CI separator` });
await sheet.getByLabel("Reason (optional)").fill("Built on the second unit");
await sheet.getByRole("button", { name: "Move packet" }).click();
await expect(page.getByText("Packet moved.")).toBeVisible();
await expect.poll(async () => String((await packet(clara, packetId)).unit.number)).toBe(`${PROJECT}002`);
await closeAll(clara);
// The warehouse may not move a packet (server check)
const wade = await actor(browser, "wh.wade");
const u3 = units["003"] as Json;
await api(wade, "post", `/api/packets/${packetId}/move`, { unit_id: u3.id }, { status: 403 });
expect(String((await packet(wade, packetId)).unit.number)).toBe(`${PROJECT}002`);
await closeAll(wade);
});
test("tunits: a Clerical supervisor renumbers a unit; its packet follows and the old number is an alias", async ({
browser,
}) => {
const clara = await actor(browser, "clerk.clara");
const u2 = units["002"] as Json;
await clara.page.goto(`/clerical/units/${u2.id}`);
await expect(clara.page.getByRole("heading", { level: 1, name: new RegExp(`^${PROJECT}002`) })).toBeVisible();
await expect(clara.page.getByRole("button", { name: "Renumber" })).toHaveCount(0);
await closeAll(clara);
const leo = await actor(browser, "clerk.leo");
const page = leo.page;
await page.goto(`/clerical/units/${u2.id}`);
await page.getByRole("button", { name: "Renumber" }).click();
const sheet = page.getByRole("dialog", { name: "Renumber unit" });
await expect(sheet).toBeVisible();
await sheet.getByLabel("New lot").fill("010");
await sheet.getByRole("button", { name: "Change number" }).click();
await expect(sheet.getByRole("alert")).toHaveText("Give a reason.");
await sheet.getByLabel("Reason").fill("Customer tag numbers changed");
await sheet.getByRole("button", { name: "Change number" }).click();
await expect(page.getByText(`Unit renumbered to ${PROJECT}010`)).toBeVisible();
await expect(page.getByRole("heading", { level: 1, name: new RegExp(`^${PROJECT}010`) })).toBeVisible();
// The packet follows its unit
expect(String((await packet(leo, packetId)).unit.number)).toBe(`${PROJECT}010`);
// The old number still finds the unit, as an alias, and cannot be used again
const found = await getJson(page.request, `/api/lookup/number?n=${PROJECT}002`);
expect(found.kind).toBe("unit");
expect(Number(found.id)).toBe(Number(u2.id));
expect(found.alias).toBeTruthy();
const again = await api(leo, "post", `/api/projects/${projectId}/units`, { lot: "002" }, { status: 409 });
expect(String(again.detail ?? again.message ?? JSON.stringify(again))).toContain(`old number of ${PROJECT}010`);
await closeAll(leo);
});
test("tunits: a closed unit takes no new packet", async ({ browser }) => {
const clara = await actor(browser, "clerk.clara");
const page = clara.page;
const u3 = units["003"] as Json;
await page.goto(`/clerical/units/${u3.id}`);
await expect(page.getByRole("button", { name: "Add packet" })).toBeVisible();
await page.getByRole("button", { name: "Edit unit" }).click();
const sheet = page.getByRole("dialog", { name: "Edit unit" });
await sheet.getByLabel("Status").selectOption({ label: "Closed" });
await sheet.getByRole("button", { name: "Save changes" }).click();
await expect(page.getByText("Unit saved")).toBeVisible();
await expect(page.getByText(/· Closed$/).first()).toBeVisible();
await expect(page.getByRole("button", { name: "Add packet" })).toHaveCount(0);
// The server refuses a packet on it too
const r = await page.request.post("/api/packets", {
headers: { Origin: new URL(page.url()).origin, "X-Requested-With": "PacketStation" },
data: { unit_id: u3.id, dept_code: "03", op: "WELD-PART", title: `Closed ${RUN}` },
});
expect(r.status(), await r.text()).toBeGreaterThanOrEqual(400);
expect(r.status()).toBeLessThan(500);
await closeAll(clara);
});
});