Ctrl K
ComunidadGuías…

e2e/shopping-proposal.spec.ts

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

Archivo original ↗Preguntar a KiBot
import { expect, test } from "@playwright/test";
import { randomUUID } from "node:crypto";

test("canonical proposal copy and download include hidden selected quotes and unresolved requirements", async ({ page }) => {
  await page.addInitScript(() => {
    Object.defineProperty(navigator, "clipboard", { configurable: true, value: { writeText: () => Promise.reject(new DOMException("Synthetic clipboard denial", "NotAllowedError")) } });
  });
  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();
  const csrf = (await page.context().cookies()).find(cookie => cookie.name === "forge_csrf")!.value;
  const headers = () => ({ "x-csrf-token": csrf, "idempotency-key": randomUUID() });
  const post = async (path: string, data: object) => {
    const response = await page.request.post(`/api/v1${path}`, { headers: headers(), data });
    expect(response.ok(), await response.text()).toBe(true);
    return (await response.json()).data;
  };
  const id = `proposal-${randomUUID()}`, revision = `${id}-r1`;
  const root = `/projects/${id}/revisions/${revision}`;
  await post("/projects/with-initial-revision", { project: { id, name: "Synthetic complete shopping proposal", status: "planned" }, revision: { id: revision, name: "Initial", status: "concept", fabricationRoute: "none" } });
  const observedAt = new Date().toISOString();
  for (const fixture of [
    { name: "Proposal visible connectors", currency: "EUR", packageQuantity: 4, priceMinor: 250, shippingMinor: 100, taxIncluded: "yes" },
    { name: "Proposal hidden spacers", currency: "USD", packageQuantity: 3, priceMinor: 90, taxIncluded: "unknown" },
    { name: "Proposal unresolved bracket" }
  ]) {
    const line = await post(`/project-revisions/${revision}/bom`, { name: fixture.name, requiredQuantity: 7, unit: "each", role: "consumed", optional: false, constraints: {}, alternatives: [] });
    if (!fixture.currency) continue;
    const quote = await post(`${root}/requirement-offers`, { bomLineId: line.id, expectedBomLineVersion: line.version, supplier: `Synthetic ${fixture.currency} supplier`, title: `Recorded ${fixture.currency} package`, url: `https://supplier.example/${fixture.currency.toLowerCase()}`, packageQuantity: fixture.packageQuantity, packageUnit: "each", priceMinor: fixture.priceMinor, currency: fixture.currency, ...(fixture.shippingMinor === undefined ? {} : { shippingMinor: fixture.shippingMinor }), taxIncluded: fixture.taxIncluded, observedAt, validForDays: 30 });
    const chosen = await page.request.put(`/api/v1${root}/offer-choice`, { headers: headers(), data: { bomLineId: line.id, offerId: quote.id, expectedVersion: 0, expectedBomLineVersion: line.version, confirmedFit: true } });
    expect(chosen.ok(), await chosen.text()).toBe(true);
  }
  await page.goto(`/#/projects/${id}/offers`); await page.reload();
  await page.getByLabel("Search quote requirements").fill("Proposal visible connectors");
  await expect(page.locator(".sourcing-requirement")).toHaveCount(1);
  await expect(page.locator(".sourcing-requirement")).toContainText("Proposal visible connectors");
  await expect(page.locator(".sourcing-requirement")).not.toContainText("Proposal hidden spacers");
  const reads: URL[] = [];
  page.on("request", request => {
    const url = new URL(request.url());
    if (request.method() === "GET" && url.pathname.endsWith("/sourcing") && url.searchParams.get("limit") === "100") reads.push(url);
  });
  const copy = page.getByRole("button", { name: "Copy proposal", exact: true });
  await expect(copy).toBeEnabled(); await copy.click();
  const manual = page.getByRole("textbox", { name: "Shopping proposal text", exact: true });
  await expect(manual).toBeVisible(); await expect(manual).toBeFocused();
  await expect(page.getByText(/Clipboard access was unavailable/u)).toBeVisible();
  const copied = await manual.inputValue();
  const verify = (text: string) => {
    expect(text).toContain("3 requirements in the full revision");
    for (const name of ["Proposal visible connectors", "Proposal hidden spacers", "Proposal unresolved bracket"]) expect(text).toContain(name);
    expect(text).toContain("Recorded EUR package"); expect(text).toContain("Recorded USD package");
    expect(text).toContain("2 packs supply 8 each"); expect(text).toContain("3 packs supply 9 each");
    expect(text).toMatch(/6\.00 EUR/u); expect(text).toMatch(/2\.70 USD/u);
    expect(text).toContain("shipping incomplete"); expect(text).toContain("tax not fully confirmed");
    expect(text).toContain("https://supplier.example/eur"); expect(text).toContain("https://supplier.example/usd");
    expect(text).toContain(`Observed: ${observedAt}`); expect(text).toContain("No supplier observation selected.");
    expect(text).toContain("Nothing is purchased, reserved, received or physically confirmed");
  };
  verify(copied);
  expect(await manual.evaluate(element => { const field = element as HTMLTextAreaElement; return field.selectionStart === 0 && field.selectionEnd === field.value.length; })).toBe(true);
  const downloading = page.waitForEvent("download");
  await page.getByRole("button", { name: "Download proposal text", exact: true }).click();
  const download = await downloading;
  expect(download.suggestedFilename()).toMatch(/^shopping-proposal-\d{4}-\d{2}-\d{2}\.txt$/u);
  const stream = await download.createReadStream(); expect(stream).not.toBeNull();
  const chunks: Buffer[] = []; for await (const chunk of stream!) chunks.push(Buffer.from(chunk));
  verify(Buffer.concat(chunks).toString("utf8"));
  expect(reads).toHaveLength(4);
  expect(reads.every(url => url.searchParams.get("filter") === "all" && !url.searchParams.has("query"))).toBe(true);
  await expect(page.getByLabel("Search quote requirements")).toHaveValue("Proposal visible connectors");
  await expect(page.locator(".sourcing-requirement")).toHaveCount(1);
});