pelagia-portal/App/tests/e2e/po-export.spec.ts

123 lines
5.2 KiB
TypeScript

/**
* 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<string> {
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();
}
});