/** * E2E — Individual PO export (PDF + XLSX). * Verifies export buttons are present and the export endpoints respond correctly. */ 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 createDraftPo(page: Page, title: string): Promise { await page.goto("/po/new"); await fillPoHeader(page, title); await fillFirstLineItem(page, { name: "Test item for export", quantity: "1", unitPrice: "100", }); await page.getByRole("button", { name: /save as draft/i }).click(); await expect(page).toHaveURL(/\/po\//); return page.url(); } // ── Export buttons visible ──────────────────────────────────────────────────── test("Export PDF button is visible on PO detail page", async ({ page }) => { await login(page, TECH); await createDraftPo(page, `E2E_EXPORT_BTN_${Date.now()}`); await expect(page.getByRole("link", { name: /export pdf/i })).toBeVisible(); }); test("Export XLSX button is visible on PO detail page", async ({ page }) => { await login(page, TECH); await createDraftPo(page, `E2E_EXPORT_XLSX_BTN_${Date.now()}`); await expect(page.getByRole("link", { name: /export xlsx/i })).toBeVisible(); }); // ── Export endpoints respond ────────────────────────────────────────────────── test("PDF export endpoint returns HTML content", async ({ page }) => { await login(page, TECH); const poUrl = await createDraftPo(page, `E2E_PDF_EP_${Date.now()}`); // Extract PO id from URL: /po/{id} const poId = poUrl.split("/po/")[1].replace(/\/$/, ""); const response = await page.request.get(`/api/po/${poId}/export?format=pdf`); expect(response.status()).toBe(200); expect(response.headers()["content-type"]).toContain("text/html"); const body = await response.text(); expect(body).toContain("PURCHASE ORDER"); expect(body).toContain("PELAGIA MARINE SERVICES"); }); test("XLSX export endpoint returns a binary spreadsheet", async ({ page }) => { await login(page, TECH); const poUrl = await createDraftPo(page, `E2E_XLSX_EP_${Date.now()}`); const poId = poUrl.split("/po/")[1].replace(/\/$/, ""); const response = await page.request.get(`/api/po/${poId}/export?format=xlsx`); expect(response.status()).toBe(200); expect(response.headers()["content-type"]).toContain("spreadsheetml"); expect(response.headers()["content-disposition"]).toMatch(/\.xlsx/); }); test("export endpoint returns 401 for unauthenticated requests", async ({ page }) => { // Don't log in — use a fresh context const response = await page.request.get("/api/po/nonexistent-id/export?format=pdf"); expect([401, 404]).toContain(response.status()); }); // ── PDF content matches PO data ─────────────────────────────────────────────── test("PDF export contains PO number, company header and T&C section", async ({ page }) => { await login(page, TECH); const poUrl = await createDraftPo(page, `E2E_PDF_CONTENT_${Date.now()}`); const poId = poUrl.split("/po/")[1].replace(/\/$/, ""); const response = await page.request.get(`/api/po/${poId}/export?format=pdf`); const body = await response.text(); expect(body).toContain("PELAGIA MARINE SERVICES"); expect(body).toContain("PURCHASE ORDER"); expect(body).toContain("INSTRUCTIONS TO VENDORS"); // GST table headers expect(body).toContain("Taxable Cost"); expect(body).toContain("GST%"); expect(body).toContain("GRAND TOTAL"); // Signature block expect(body).toContain("Authorized Signatory"); }); test("PDF export shows the fixed T&C line 1", async ({ page }) => { await login(page, TECH); const poUrl = await createDraftPo(page, `E2E_PDF_TC_${Date.now()}`); const poId = poUrl.split("/po/")[1].replace(/\/$/, ""); const response = await page.request.get(`/api/po/${poId}/export?format=pdf`); const body = await response.text(); expect(body).toMatch(/please quote this purchase order/i); }); // ── Manager can also export ─────────────────────────────────────────────────── test("manager can see and use the Export PDF button on any PO detail", async ({ page }) => { await login(page, MGR); await page.goto("/approvals"); // Click first PO in the queue if available const firstPoLink = page.locator("a[href^='/po/']").first(); if (await firstPoLink.count() > 0) { await firstPoLink.click(); await expect(page.getByRole("link", { name: /export pdf/i })).toBeVisible(); } });