/** * E2E — Submitter user journey. * Covers: S-01 (create PO), S-02 (save draft), S-03 (submit for approval), * S-05 (view status), S-07 (edit and resubmit), S-08 (confirm receipt page visible). */ import { test, expect, type Page } from "@playwright/test"; import { fillFirstLineItem, fillPoHeader } from "./helpers/login"; 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/, { timeout: 20_000 }); } async function fillNewPoForm(page: Page, title: string) { await page.goto("/po/new"); await fillPoHeader(page, title); await fillFirstLineItem(page, { name: "Test marine part", quantity: "5", unitPrice: "100", }); } // ── S-02: Save as draft ────────────────────────────────────────────────────── test("S-02 — submitter can save a PO as draft", async ({ page }) => { await login(page, TECH); const title = `E2E_DRAFT_${Date.now()}`; await fillNewPoForm(page, title); await page.getByRole("button", { name: /save as draft/i }).click(); await expect(page).toHaveURL(/\/po\//); await expect(page.getByText("Draft")).toBeVisible(); }); // ── S-01 + S-03: Create and submit ────────────────────────────────────────── test("S-01 + S-03 — submitter can create a PO and submit for approval", async ({ page }) => { await login(page, TECH); const title = `E2E_SUBMIT_${Date.now()}`; await fillNewPoForm(page, title); await page.getByRole("button", { name: /submit for approval/i }).click(); await expect(page).toHaveURL(/\/po\//); await expect(page.getByText(/under review/i)).toBeVisible(); }); // ── S-01: Line items with GST ──────────────────────────────────────────────── test("S-01 — new PO form shows GST rate dropdown on line items", async ({ page }) => { await login(page, TECH); await page.goto("/po/new"); // GST select should be visible with 18% default const gstSelect = page.locator("select").filter({ hasText: "18%" }); await expect(gstSelect).toBeVisible(); }); // ── S-01: T&C structured fields ────────────────────────────────────────────── test("S-01 — new PO form shows structured T&C section with fixed first line", async ({ page }) => { await login(page, TECH); await page.goto("/po/new"); await expect(page.getByText(/please quote this purchase order/i)).toBeVisible(); await expect(page.locator('input[name="tcDelivery"]')).toBeVisible(); await expect(page.locator('input[name="tcPaymentTerms"]')).toBeVisible(); }); // ── S-05: View status and activity ────────────────────────────────────────── test("S-05 — submitter can view the status and activity trail of their PO", async ({ page }) => { await login(page, TECH); // Go to My Orders await page.goto("/my-orders"); await expect(page.getByRole("heading", { name: /my orders|orders/i })).toBeVisible(); // Should have at least the seeded POs or any created ones // Click the first PO if available const firstLink = page.locator("a[href^='/po/']").first(); if (await firstLink.count() > 0) { await firstLink.click(); await expect(page.getByText(/activity/i)).toBeVisible(); } }); // ── S-07: Edit PO when edits requested ────────────────────────────────────── test("S-07 — submitter sees edit form pre-populated with existing values", async ({ page }) => { await login(page, TECH); const title = `E2E_EDIT_${Date.now()}`; await fillNewPoForm(page, title); await page.getByRole("button", { name: /save as draft/i }).click(); await expect(page).toHaveURL(/\/po\//); // Click Edit await page.getByRole("link", { name: /edit/i }).click(); await expect(page).toHaveURL(/\/edit$/); // Form should be pre-populated await expect(page.locator('input[name="title"]')).toHaveValue(title); await expect(page.getByPlaceholder("Item name *")).toHaveValue("Test marine part"); }); // ── S-08: Confirm receipt page ─────────────────────────────────────────────── test("S-08 — receipt confirmation page is accessible at /po/[id]/receipt", async ({ page }) => { await login(page, TECH); // Use the seeded PAID_DELIVERED PO if present, otherwise just check the route exists // The seed creates a PO in PAID_DELIVERED state await page.goto("/my-orders"); const paidLink = page.getByText(/paid|confirm receipt/i).first(); if (await paidLink.isVisible()) { await paidLink.click(); // Should see confirm receipt CTA await expect(page.getByRole("link", { name: /confirm receipt/i })).toBeVisible(); } }); // ── Navigation ─────────────────────────────────────────────────────────────── test("submitter dashboard shows New PO button", async ({ page }) => { await login(page, TECH); await expect(page.getByRole("link", { name: /new (po|purchase order)/i })).toBeVisible(); }); test("submitter cannot access approvals queue", async ({ page }) => { await login(page, TECH); await page.goto("/approvals"); // Should redirect or show dashboard (not the approvals queue) await expect(page).not.toHaveURL(/approvals/); });