/** * 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 { fillFirstLineItem, fillPoHeader, login, USERS } from "./helpers/login"; async function driveEditHighlightFlow( page: import("@playwright/test").Page, context: BrowserContext, title: string ): Promise { // Step 1: Submit as tech await login(page, USERS.TECH); await page.goto("/po/new"); await fillPoHeader(page, title); await fillFirstLineItem(page, { name: "Original item", quantity: "2", unitPrice: "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}` ); }); });