Ctrl K
ComunidadGuías…

e2e/inventory-first-use.spec.ts

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
import { openPartStockMatch, openProjectSection, navigateWorkspace } from "./workspace-controls";
import { expect, test, type Page } from "@playwright/test";
import { randomUUID } from "node:crypto";

async function signIn(page: Page) {
  await page.getByLabel("Workspace password").fill("demo-password-please-change");
  await page.getByRole("button", { name: "Sign in", exact: true }).click();
}

for (const width of [390, 1440]) {
  test(`an owned item returns to its requirement after ${width === 390 ? "creation" : "count"} retry and sign-in at ${width}px`, async ({ page }) => {
    await page.setViewportSize({ width, height: 900 });
    await page.goto("/"); await signIn(page);
    await expect(page.getByRole("heading", { name: "Projects", exact: true })).toBeVisible();
    const projectId = `owned-item-${randomUUID()}`, revisionId = `${projectId}-r1`;
    const name = `Synthetic first-use connector ${width}`;
    const csrf = (await page.context().cookies()).find(cookie => cookie.name === "forge_csrf")!.value;
    const project = await page.request.post("/api/v1/projects/with-initial-revision", { headers: { "x-csrf-token": csrf, "idempotency-key": randomUUID() }, data: { project: { id: projectId, name: `Synthetic owned item project ${width}`, status: "planned", description: "Synthetic first-use inventory regression." }, revision: { id: revisionId, name: "Initial", status: "concept", fabricationRoute: "none" } } });
    expect(project.ok()).toBe(true);
    await page.goto(`/#/projects/${projectId}/plan`); await page.reload();
    await page.getByRole("button", { name: "Add first part", exact: true }).click();
    const requirement = page.getByRole("dialog", { name: "Add a part", exact: true });
    await requirement.getByLabel("Part name", { exact: true }).fill(name);
    await requirement.getByLabel("Amount needed", { exact: true }).fill("2");
    await requirement.getByLabel("Specification (optional)").fill("Check the connector polarity before use.");
    await openPartStockMatch(page);
    await requirement.getByRole("button", { name: "Add an owned item", exact: true }).click();
    await page.getByRole("dialog", { name: "Add to inventory", exact: true }).getByRole("button", { name: "Cancel", exact: true }).click();
    await expect(requirement.getByLabel("Amount needed", { exact: true })).toHaveValue("2");
    await expect(requirement.getByRole("button", { name: "Add part", exact: true })).toBeFocused();
    await expect(requirement.getByRole("button", { name: "Clear owned item selection", exact: true })).toHaveCount(0);
    await openPartStockMatch(page);
    await requirement.getByRole("button", { name: "Add an owned item", exact: true }).click();
    await page.getByRole("combobox", { name: "What are you adding?", exact: true }).selectOption("electronic");
    await page.getByRole("button", { name: "Continue", exact: true }).click();
    const capture = page.getByRole("dialog", { name: "Add an inventory item", exact: true });
    await expect(capture.getByLabel("Name", { exact: true })).toHaveValue(name);
    await capture.getByRole("button", { name: "Specifications & identity", exact: true }).click();
    await capture.getByLabel("Details and specifications (optional)").fill("JST-PH, 2 pins, 2.0 mm pitch");
    await capture.getByLabel("Recorded quantity", { exact: true }).fill("5");
    await capture.getByRole("checkbox", { name: "I have counted these", exact: true }).check();
    const createWrites: Array<{ key: string | undefined; body: string | null }> = [];
    let itemId = "";
    await page.route(/\/api\/v1\/inventory$/u, async route => {
      createWrites.push({ key: route.request().headers()["idempotency-key"], body: route.request().postData() });
      if (width === 390 && createWrites.length === 2) return route.fulfill({ status: 401, json: { error: { code: "unauthenticated", message: "Synthetic sign-in required." } } });
      const response = await route.fetch();
      expect(response.ok()).toBe(true);
      const body = await response.json(); itemId = body.data.id;
      if (width === 390 && createWrites.length === 1) return route.abort("failed");
      return route.fulfill({ response });
    });
    const countWrites: Array<{ key: string | undefined; body: string | null }> = [];
    await page.route(/\/api\/v1\/inventory\/[^/]+\/count$/u, async route => {
      countWrites.push({ key: route.request().headers()["idempotency-key"], body: route.request().postData() });
      if (width === 1440 && countWrites.length === 1) return route.abort("failed");
      if (width === 1440 && countWrites.length === 2) return route.fulfill({ status: 401, json: { error: { code: "unauthenticated", message: "Synthetic sign-in required." } } });
      return route.continue();
    });
    await capture.getByRole("button", { name: "Add item and review count", exact: true }).click();
    if (width === 390) {
      await expect(capture.getByRole("button", { name: "Retry unchanged item", exact: true })).toBeVisible();
      await expect(capture.getByLabel("Name", { exact: true })).toBeDisabled();
      await capture.getByRole("button", { name: "Retry unchanged item", exact: true }).click();
      await expect(page.getByLabel("Workspace password")).toBeVisible();
      await expect(page.locator("body")).not.toContainText(name);
      await signIn(page);
      await expect(capture.getByLabel("Recorded quantity", { exact: true })).toHaveValue("5");
      expect(createWrites).toHaveLength(2);
      await capture.getByRole("button", { name: "Retry unchanged item", exact: true }).click();
      expect(createWrites[1]).toEqual(createWrites[0]);
    }
    const review = page.getByRole("alertdialog", { name: "Review physical count", exact: true });
    await expect(review).toBeVisible();
    expect(countWrites).toHaveLength(0);
    const uncounted = await page.request.get(`/api/v1/inventory/${itemId}`);
    expect((await uncounted.json()).evidence.state).toBe("unknown");
    await review.getByRole("button", { name: "Confirm physical count", exact: true }).click();
    if (width === 1440) {
      await review.getByRole("button", { name: "Retry unchanged observation", exact: true }).click();
      await expect(page.getByLabel("Workspace password")).toBeVisible();
      await expect(page.locator("body")).not.toContainText(name);
      await signIn(page);
      await expect(review).toBeVisible();
      expect(countWrites).toHaveLength(2);
      await review.getByRole("button", { name: "Retry unchanged observation", exact: true }).click();
    }
    await expect(review).toHaveCount(0);
    const drawer = page.getByRole("dialog", { name: new RegExp(`^${name}`, "u") });
    await expect(drawer.getByText("Physical count saved", { exact: true })).toBeVisible();
    await expect(drawer.getByLabel("Counted quantity", { exact: true })).toHaveCount(0);
    expect(await drawer.evaluate(element => element.scrollWidth <= element.clientWidth)).toBe(true);
    await drawer.getByRole("button", { name: `Return to ${name}`, exact: true }).click();
    await expect(requirement.getByLabel("Part name", { exact: true })).toHaveValue(name);
    await expect(requirement.getByRole("button", { name: "Add part", exact: true })).toBeFocused();
    await expect(requirement.getByLabel("Amount needed", { exact: true })).toHaveValue("2");
    await expect(requirement.getByLabel("Unit", { exact: true })).toHaveValue("each");
    await expect(requirement.getByRole("button", { name: "Clear owned item selection", exact: true })).toBeVisible();
    await expect(requirement.getByLabel("Specification (optional)")).toHaveValue("Check the connector polarity before use.");
    await requirement.getByRole("button", { name: "Add part", exact: true }).click();
    await expect(requirement).toHaveCount(0);
    if (width === 390) {
      await page.getByRole("main").evaluate(element => { element.scrollTop = 0; });
      await page.evaluate(() => window.scrollTo(0, 0));
      await expect.poll(() => page.evaluate(() => window.scrollY)).toBe(0);
      await expect.poll(() => page.getByRole("main").evaluate(element => element.scrollTop)).toBe(0);
      const requirementRow = page.locator(".bom-row").filter({ hasText: name });
      await expect(requirementRow).toBeVisible();
      await expect.poll(() => requirementRow.evaluate(element => { const bounds = element.getBoundingClientRect(); return bounds.top >= 0 && bounds.bottom <= window.innerHeight; })).toBe(true);
    }
    const lines = await page.request.get(`/api/v1/project-revisions/${revisionId}/bom`);
    expect(lines.ok(), await lines.text()).toBe(true);
    const savedLines = await lines.json();
    expect(Array.isArray(savedLines)).toBe(true);
    const saved = savedLines.find((line: { name: string }) => line.name === name);
    expect(saved).toMatchObject({ itemId, requiredQuantity: 2, unit: "each", notes: "Check the connector polarity before use." });
    expect(createWrites).toHaveLength(width === 390 ? 3 : 1);
    expect(countWrites).toHaveLength(width === 1440 ? 3 : 1);
    for (const attempt of createWrites) expect(attempt).toEqual(createWrites[0]);
    for (const attempt of countWrites) expect(attempt).toEqual(countWrites[0]);
    expect(await page.evaluate(() => document.documentElement.scrollWidth <= innerWidth)).toBe(true);
  });
}

test("a just-created item removed elsewhere does not block reconnection", async ({ page }) => {
  await page.goto("/"); await signIn(page);
  await expect(page.getByRole("heading", { name: "Projects", exact: true })).toBeVisible();
  await navigateWorkspace(page, "Inventory");
  await page.getByRole("button", { name: "Add item", exact: true }).click();
  await page.getByRole("combobox", { name: "What are you adding?", exact: true }).selectOption("electronic");
  await page.getByRole("button", { name: "Continue", exact: true }).click();
  const name = `Synthetic removable item ${randomUUID()}`;
  await page.getByLabel("Name", { exact: true }).fill(name);
  const created = page.waitForResponse(response => response.request().method() === "POST" && new URL(response.url()).pathname === "/api/v1/inventory");
  await page.getByRole("button", { name: "Add item", exact: true }).click();
  const item = (await (await created).json()).data;
  const drawer = page.getByRole("dialog", { name: new RegExp(`^${name}`, "u") });
  await expect(drawer).toBeVisible();
  const csrf = (await page.context().cookies()).find(cookie => cookie.name === "forge_csrf")!.value;
  const removed = await page.request.delete(`/api/v1/inventory/${item.id}`, { headers: { "x-csrf-token": csrf, "if-match": String(item.version), "idempotency-key": randomUUID() } });
  expect(removed.ok()).toBe(true);
  let missingReads = 0;
  await page.route(`**/api/v1/inventory/${item.id}`, async route => {
    if (route.request().method() === "PATCH") return route.fulfill({ status: 401, json: { error: { code: "unauthenticated", message: "Synthetic sign-in required." } } });
    missingReads++;
    return route.continue();
  });
  await drawer.getByRole("button", { name: "Edit item", exact: true }).click();
  await drawer.getByLabel("Name", { exact: true }).fill(`${name} edited`);
  await drawer.getByRole("button", { name: "Save changes", exact: true }).click();
  await expect(page.getByLabel("Workspace password")).toBeVisible();
  await signIn(page);
  await expect(page.getByRole("heading", { name: "Inventory", exact: true })).toBeVisible();
  await expect(page.getByRole("dialog")).toHaveCount(0);
  await expect(page.getByLabel("Workspace password")).toHaveCount(0);
  expect(missingReads).toBe(1);
});


test("category recovery keeps nested drafts until one approved Settings navigation", async ({ page }) => {
  await page.route("**/api/v1/inventory/categories?**", route => route.fulfill({ status: 503, json: { error: { code: "unavailable", message: "Synthetic category service unavailable." } } }));
  await page.goto("/"); await signIn(page);
  await page.getByRole("button", { name: "Open project Synthetic H2D desk lamp", exact: true }).click();
  await openProjectSection(page, "Parts");
  await page.getByRole("button", { name: "Add part", exact: true }).click();
  const requirement = page.getByRole("dialog", { name: "Add a part", exact: true });
  await requirement.getByLabel("Part name", { exact: true }).fill("Synthetic retained category-recovery connector");
  await requirement.getByLabel("Amount needed", { exact: true }).fill("3");
  await openPartStockMatch(page);
    await requirement.getByRole("button", { name: "Add an owned item", exact: true }).click();
  const capture = page.getByRole("dialog", { name: "Add to inventory", exact: true });
  await capture.getByRole("button", { name: "Open Settings", exact: true }).click();
  const guard = page.getByRole("alertdialog", { name: "Leave without saving?", exact: true });
  await guard.getByRole("button", { name: "Keep editing", exact: true }).click();
  await expect(capture).toBeVisible();
  await capture.getByRole("button", { name: "Cancel", exact: true }).click();
  await expect(requirement.getByLabel("Part name", { exact: true })).toHaveValue("Synthetic retained category-recovery connector");
  await expect(requirement.getByLabel("Amount needed", { exact: true })).toHaveValue("3");
  await openPartStockMatch(page);
    await requirement.getByRole("button", { name: "Add an owned item", exact: true }).click();
  await capture.getByRole("button", { name: "Open Settings", exact: true }).click();
  await guard.getByRole("button", { name: "Discard changes and leave", exact: true }).click();
  await expect(page.getByRole("heading", { name: "Settings", exact: true })).toBeVisible();
  await expect(page.getByRole("dialog")).toHaveCount(0);
  await expect(page.getByRole("alertdialog")).toHaveCount(0);
  await expect(page.getByRole("main")).toBeFocused();
  await navigateWorkspace(page, "Projects");
  await page.getByRole("button", { name: "Open project Synthetic H2D desk lamp", exact: true }).click();
  await openProjectSection(page, "Parts");
  await page.getByRole("button", { name: "Add part", exact: true }).click();
  await expect(requirement.getByLabel("Part name", { exact: true })).toHaveValue("");
  await expect(requirement.getByLabel("Amount needed", { exact: true })).toHaveValue("1");
});