123 lines
5.2 KiB
TypeScript
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();
|
|
}
|
|
});
|