import { openBuildTool, clickProjectAction, navigateWorkspace, openProjectSection } from "./workspace-controls"; import { test, expect, type Page } from "@playwright/test"; async function login(page: Page) { await page.goto("/"); await page.getByLabel("Workspace password").fill("demo-password-please-change"); await page.getByRole("button", { name: "Sign in", exact: true }).click(); await expect(page.getByRole("heading", { name: "Projects", exact: true })).toBeVisible(); await navigateWorkspace(page, "Projects"); } async function guided(page: Page, name: string) { await clickProjectAction(page, "New project"); await page.getByRole("button", { name: "Import a parts list (CSV)", exact: true }).click(); await page.getByLabel("Guided project name").fill(name); await page.getByRole("button", { name: "Project details, optional", exact: true }).click(); await page.getByLabel("Guided project goal").fill("Synthetic acceptance of reviewed maker requirements."); await page.getByLabel("Guided build approach").selectOption("printed"); await expect(page.getByRole("button", { name: "Import requirements CSV", exact: true })).toHaveAttribute("aria-expanded", "true"); await page.getByLabel("BOM CSV text").fill("name,quantity,unit\nPrinted bracket,5,each\nM3 mounting screw,7,each"); await page.getByRole("button", { name: "Review CSV mapping", exact: true }).click(); await page.getByRole("button", { name: "Use mapped requirements in draft", exact: true }).click(); await page.getByRole("button", { name: "Task groups, optional", exact: true }).click(); await page.getByLabel("Setup task groups").fill("Design review\nAssembly"); await page.getByRole("button", { name: "Preview complete project", exact: true }).click(); await expect(page.getByRole("heading", { name: `Review ${name}`, exact: true })).toBeVisible(); await page.getByRole("button", { name: "Create reviewed project", exact: true }).click(); await expect(page.getByRole("heading", { name, exact: true })).toBeVisible(); } for (const width of [1440, 390]) test(`reviewed setup, append and quotes are usable at ${width}px`, async ({ page }) => { await page.setViewportSize({ width, height: 900 }); await login(page); await guided(page, `Release workshop ${width}`); await expect(page.locator(".bom-row")).toHaveCount(2); const inventoryBefore = await (await page.request.get("/api/v1/inventory")).json(); await clickProjectAction(page, "Import requirements from CSV"); await page.getByLabel("Requirements CSV text").fill("name,qty,unit\nReview spacer,2,each"); await page.getByRole("button", { name: "Map import columns", exact: true }).click(); await page.getByRole("button", { name: "Preview requirement append", exact: true }).click(); await expect(page.getByRole("heading", { name: "Review 1 new requirements", exact: true })).toBeVisible(); await page.getByRole("button", { name: "Confirm append requirements", exact: true }).click(); await expect(page.locator(".bom-row")).toHaveCount(3); await page.keyboard.press("Escape"); await openProjectSection(page, "To source"); await expect(page.getByRole("heading", { name: "Supplier quotes for this project", exact: true })).toBeVisible(); const row = page.locator(".sourcing-requirement").filter({ has: page.getByRole("heading", { name: "M3 mounting screw", exact: true }) }); await row.getByRole("button", { name: "Record quote for M3 mounting screw", exact: true }).click(); await row.getByLabel("Supplier", { exact: true }).fill("Synthetic supplier"); await row.getByLabel("Supplier source URL").fill("https://supplier.example/fasteners"); await row.getByLabel("Quantity per pack", { exact: true }).fill("4"); await row.getByLabel("Pack price", { exact: true }).fill("2.50"); await row.getByLabel("Shipping for this quote, blank if unknown").fill("2.00"); await row.getByLabel("Quoted tax included").selectOption("yes"); await row.getByRole("button", { name: "Save quote", exact: true }).click(); await row.getByLabel("I checked that this quoted item meets the current requirement").check(); await row.getByRole("button", { name: "Use reviewed quote", exact: true }).click(); await expect(page.locator(".sourcing-totals")).toContainText("€7.00"); expect(await (await page.request.get("/api/v1/inventory")).json()).toEqual(inventoryBefore); expect(await page.evaluate(() => document.documentElement.scrollWidth <= innerWidth)).toBe(true); await page.reload(); await expect(page.getByRole("heading", { name: "Supplier quotes for this project", exact: true })).toBeVisible(); await expect(page.locator(".sourcing-totals")).toContainText("€7.00"); }); test("a maker saves repeated plate plans and workstream progress without consuming stock", async ({ page }) => { await login(page); await guided(page, "Release repeated plates"); await page.getByRole("tab", { name: "Build", exact: true }).click(); await openBuildTool(page, "print"); await page.getByRole("button", { name: "Create build plan", exact: true }).click(); await page.getByRole("button", { name: "Add build part", exact: true }).click(); await page.getByLabel("Build part 1 name").fill("Bracket"); await page.getByLabel("Build part 1 quantity").fill("5"); await page.getByRole("button", { name: "Add plate layout", exact: true }).click(); await page.getByLabel("Plate 1 runs").fill("2"); await page.getByLabel("Plate 1 quantity Bracket").fill("3"); await page.getByRole("button", { name: "Review build plan", exact: true }).click(); await page.getByRole("button", { name: "Save build plan", exact: true }).click(); const coverage = page.getByRole("table").filter({ has: page.getByText("Part coverage", { exact: true }) }); await expect(coverage).toContainText("Bracket"); await expect(coverage.getByRole("row").last()).toContainText("6"); await openBuildTool(page, "tasks"); await page.getByRole("button", { name: "Add task group", exact: true }).click(); await page.getByLabel("Task group name", { exact: true }).fill("Fit validation"); await page.getByRole("button", { name: "Create task group", exact: true }).click(); await page.locator(".workstream-row > [data-disclosure='trigger']").filter({ hasText: "Fit validation" }).click(); await page.getByLabel("Status for Fit validation", { exact: true }).selectOption("in_progress"); await page.locator(".workstream-row").filter({ hasText: "Fit validation" }).getByRole("button", { name: "Save task group progress", exact: true }).click(); await expect(page.locator(".workstream-row > [data-disclosure='trigger']").filter({ hasText: "Fit validation" })).toContainText("In progress"); await page.reload(); await page.getByRole("tab", { name: "Build", exact: true }).click(); await openBuildTool(page, "print"); await expect(page.locator(".build-planning")).toContainText("2 planned runs"); await clickProjectAction(page, "Revision history"); await page.getByRole("button", { name: /Read revision 1:/u }).click(); await expect(page.getByRole("region", { name: "Read-only revision snapshot" })).toContainText("2 requirements"); });