155 lines
5.7 KiB
TypeScript
155 lines
5.7 KiB
TypeScript
/**
|
|
* User stories covered: Feature 21 — Edit highlight on resubmitted PO
|
|
* - When a MANAGER reviews a PO that was resubmitted after edits were requested,
|
|
* changed fields are visually highlighted (ring-, border-warning, bg-warning, etc.)
|
|
*
|
|
* Implementation context (po-detail.tsx):
|
|
* - The `resubmitSnapshot` is stored in the SUBMITTED action's metadata when
|
|
* a submitter resubmits after EDITS_REQUESTED.
|
|
* - po-detail.tsx uses `originalLineItems` and renders a diff label.
|
|
* - Changed line items get strikethrough styling (old values) and bold new values.
|
|
* - Header fields (vessel, vendor, etc.) use a ring/border highlight when changed.
|
|
*
|
|
* To drive this flow:
|
|
* 1. Tech submits PO
|
|
* 2. Manager requests edits (with a note)
|
|
* 3. Tech edits the PO and resubmits
|
|
* 4. Manager reviews — changed fields should be highlighted
|
|
*
|
|
* Created: 2026-05-17
|
|
*/
|
|
import { test, expect, type BrowserContext } from "@playwright/test";
|
|
import { login, USERS } from "./helpers/login";
|
|
|
|
async function driveEditHighlightFlow(
|
|
page: import("@playwright/test").Page,
|
|
context: BrowserContext,
|
|
title: string
|
|
): Promise<string> {
|
|
// Step 1: Submit as tech
|
|
await login(page, USERS.TECH);
|
|
await page.goto("/po/new");
|
|
await page.getByLabel(/title/i).fill(title);
|
|
await page.getByLabel(/vessel/i).selectOption({ index: 1 });
|
|
await page.getByLabel(/account/i).selectOption({ index: 1 });
|
|
await page.getByPlaceholder("Item description").fill("Original item");
|
|
await page.getByRole("spinbutton").first().fill("2");
|
|
await page.locator("input[placeholder='0.00']").fill("150");
|
|
await page.getByRole("button", { name: /submit for approval/i }).click();
|
|
await expect(page).toHaveURL(/\/po\//, { timeout: 15_000 });
|
|
const poUrl = page.url();
|
|
const poId = poUrl.split("/po/")[1].replace(/\/$/, "");
|
|
|
|
// Step 2: Manager requests edits
|
|
await context.clearCookies();
|
|
await login(page, USERS.MANAGER);
|
|
await page.goto(poUrl);
|
|
await page.getByRole("button", { name: /request edits/i }).click();
|
|
const noteInput = page.getByPlaceholder(/reason|note/i).first();
|
|
await noteInput.fill("Please update the quantity");
|
|
await page.getByRole("button", { name: /request edits/i }).last().click();
|
|
await expect(page.getByText(/edits requested/i)).toBeVisible({ timeout: 10_000 });
|
|
|
|
// Step 3: Tech edits and resubmits
|
|
await context.clearCookies();
|
|
await login(page, USERS.TECH);
|
|
await page.goto(poUrl);
|
|
await page.getByRole("link", { name: /edit/i }).click();
|
|
await expect(page).toHaveURL(/\/edit$/, { timeout: 10_000 });
|
|
|
|
// Change the quantity
|
|
const qtyInput = page.getByRole("spinbutton").first();
|
|
await qtyInput.click({ clickCount: 3 });
|
|
await qtyInput.fill("5");
|
|
|
|
await page.getByRole("button", { name: /resubmit|update/i }).click();
|
|
await expect(page).toHaveURL(/\/po\//, { timeout: 15_000 });
|
|
|
|
// Step 4: Manager should now see highlighted changes
|
|
await context.clearCookies();
|
|
await login(page, USERS.MANAGER);
|
|
|
|
// PO should now be in MGR_REVIEW again — navigate to the approval detail page
|
|
return `/approvals/${poId}`;
|
|
}
|
|
|
|
test.describe("Feature 21 — Edit highlights on resubmitted PO", () => {
|
|
test("US-21a: manager sees a diff indicator when a PO has been resubmitted after edits", async ({
|
|
page,
|
|
context,
|
|
}: {
|
|
page: import("@playwright/test").Page;
|
|
context: BrowserContext;
|
|
}) => {
|
|
let approvalUrl: string;
|
|
try {
|
|
approvalUrl = await driveEditHighlightFlow(
|
|
page,
|
|
context,
|
|
`E2E_EDITHIGHLIGHT_${Date.now()}`
|
|
);
|
|
} catch (err) {
|
|
test.skip(true, `Could not drive edit highlight flow: ${(err as Error).message}`);
|
|
return;
|
|
}
|
|
|
|
await page.goto(approvalUrl);
|
|
await page.waitForLoadState("networkidle");
|
|
|
|
// The approval detail page should display the PO detail with diff information.
|
|
// po-detail.tsx shows: "Submitter updated these line items after edits were requested."
|
|
// along with strikethrough on old values.
|
|
const diffLabel = page.getByText(
|
|
/submitter updated|edits were requested|previous values/i
|
|
);
|
|
const strikethrough = page.locator("s, del, [class*='line-through']");
|
|
|
|
const hasDiffLabel = await diffLabel.isVisible();
|
|
const hasStrikethrough = (await strikethrough.count()) > 0;
|
|
|
|
expect(hasDiffLabel || hasStrikethrough).toBeTruthy();
|
|
console.log(
|
|
hasDiffLabel
|
|
? "✓ Diff label visible (submitter updated line items after edits)"
|
|
: "✓ Strikethrough on changed line items visible"
|
|
);
|
|
});
|
|
|
|
test("US-21a: resubmitted PO shows at least one highlight or diff indicator", async ({
|
|
page,
|
|
context,
|
|
}: {
|
|
page: import("@playwright/test").Page;
|
|
context: BrowserContext;
|
|
}) => {
|
|
let approvalUrl: string;
|
|
try {
|
|
approvalUrl = await driveEditHighlightFlow(
|
|
page,
|
|
context,
|
|
`E2E_EDITHI2_${Date.now()}`
|
|
);
|
|
} catch (err) {
|
|
test.skip(true, `Could not drive edit highlight flow: ${(err as Error).message}`);
|
|
return;
|
|
}
|
|
|
|
await page.goto(approvalUrl);
|
|
await page.waitForLoadState("networkidle");
|
|
|
|
// Look for any visual highlighting: ring-*, border-warning, bg-warning,
|
|
// bg-yellow-*, strikethrough text, or the diff description paragraph
|
|
const highlights = page.locator(
|
|
"[class*='ring-'], [class*='border-warning'], [class*='bg-warning'], [class*='bg-yellow'], s, del, [class*='line-through']"
|
|
);
|
|
const diffText = page.getByText(/edits were requested|updated.*line items|previous values/i);
|
|
|
|
const highlightCount = await highlights.count();
|
|
const hasDiffText = await diffText.isVisible();
|
|
|
|
expect(highlightCount > 0 || hasDiffText).toBeTruthy();
|
|
console.log(
|
|
`✓ Found ${highlightCount} highlight element(s) and diff text: ${hasDiffText}`
|
|
);
|
|
});
|
|
});
|