/** * E2E — Manager approval workflow. * Covers: M-01 (approvals queue), M-02 (approve / approve+note), * M-03 (reject with reason), M-04 (request edits, flag vendor ID). */ import { test, expect, type Page } from "@playwright/test"; const TECH = { email: "tech@pelagia.local", password: "tech1234" }; const MGR = { email: "manager@pelagia.local", password: "manager1234" }; async function login(page: Page, creds: typeof TECH) { await page.goto("/login"); await page.getByLabel(/email/i).fill(creds.email); await page.getByLabel(/password/i).fill(creds.password); await page.getByRole("button", { name: /sign in/i }).click(); await expect(page).not.toHaveURL(/login/); } /** Create + submit a PO as tech user, return the PO URL */ async function submitPoAsTech(page: Page, title: string): Promise { await login(page, 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("Engine filter"); await page.getByRole("spinbutton").first().fill("2"); await page.locator("input[placeholder='0.00']").fill("500"); await page.getByRole("button", { name: /submit for approval/i }).click(); await expect(page).toHaveURL(/\/po\//); return page.url(); } // ── M-01: Approvals queue ──────────────────────────────────────────────────── test("M-01 — manager sees approvals queue with pending POs", async ({ page }) => { await login(page, MGR); await page.goto("/approvals"); await expect(page.getByRole("heading", { name: /approval/i })).toBeVisible(); // Should have table or list of POs await expect(page.locator("table, [role='list']")).toBeVisible(); }); test("M-01 — manager can search approvals queue by PO number", async ({ page }) => { await login(page, MGR); await page.goto("/approvals"); const searchInput = page.getByPlaceholder(/search|po number/i); if (await searchInput.isVisible()) { await searchInput.fill("PO-"); await expect(searchInput).toHaveValue("PO-"); } }); // ── M-02: Approve ───────────────────────────────────────────────────────────── test("M-02 — manager can approve a submitted PO", async ({ page, context }) => { const title = `E2E_MGR_APPROVE_${Date.now()}`; await submitPoAsTech(page, title); const poUrl = page.url(); // Switch to manager session await context.clearCookies(); await login(page, MGR); await page.goto(poUrl); await expect(page.getByText(/under review/i)).toBeVisible(); await page.getByRole("button", { name: /^approve$/i }).click(); await expect(page.getByText(/approved/i)).toBeVisible(); }); test("M-02 — manager can approve with a note", async ({ page, context }) => { const title = `E2E_MGR_APPRNOTE_${Date.now()}`; await submitPoAsTech(page, title); const poUrl = page.url(); await context.clearCookies(); await login(page, MGR); await page.goto(poUrl); await page.getByRole("button", { name: /approve.*note/i }).click(); const noteInput = page.getByPlaceholder(/note|comment/i).first(); await noteInput.fill("Approved — please expedite delivery"); await page.getByRole("button", { name: /^approve$/i }).last().click(); await expect(page.getByText(/approved/i)).toBeVisible(); }); // ── M-03: Reject ───────────────────────────────────────────────────────────── test("M-03 — manager can reject a PO with a reason", async ({ page, context }) => { const title = `E2E_MGR_REJECT_${Date.now()}`; await submitPoAsTech(page, title); const poUrl = page.url(); await context.clearCookies(); await login(page, MGR); await page.goto(poUrl); await page.getByRole("button", { name: /reject/i }).click(); const noteInput = page.getByPlaceholder(/reason|note/i).first(); await noteInput.fill("Budget not available for this quarter"); await page.getByRole("button", { name: /^reject$/i }).last().click(); await expect(page.getByText(/rejected/i)).toBeVisible(); }); // ── M-04: Request edits ────────────────────────────────────────────────────── test("M-04 — manager can request edits with a note", async ({ page, context }) => { const title = `E2E_MGR_EDITS_${Date.now()}`; await submitPoAsTech(page, title); const poUrl = page.url(); await context.clearCookies(); await login(page, MGR); 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 add vendor ID and update quantity"); await page.getByRole("button", { name: /request edits/i }).last().click(); await expect(page.getByText(/edits requested/i)).toBeVisible(); }); // ── M-04: Flag vendor ID ────────────────────────────────────────────────────── test("M-04 — manager can flag a PO for vendor ID verification", async ({ page, context }) => { const title = `E2E_MGR_VENDOR_${Date.now()}`; await submitPoAsTech(page, title); const poUrl = page.url(); await context.clearCookies(); await login(page, MGR); await page.goto(poUrl); const vendorIdBtn = page.getByRole("button", { name: /request vendor id|vendor id/i }); await expect(vendorIdBtn).toBeVisible(); await vendorIdBtn.click(); await expect(page.getByText(/vendor id pending/i)).toBeVisible(); }); // ── Manager dashboard ───────────────────────────────────────────────────────── test("manager dashboard shows approvals queue link", async ({ page }) => { await login(page, MGR); await expect(page.getByRole("link", { name: /approval/i })).toBeVisible(); }); test("manager cannot create a new PO (no create_po permission)", async ({ page }) => { await login(page, MGR); await page.goto("/po/new"); // Should redirect away — managers can't create POs await expect(page).not.toHaveURL(/\/po\/new/); });