pelagia-portal/App/tests/e2e/submitter-journey.spec.ts
2026-05-18 23:18:58 +05:30

134 lines
6.1 KiB
TypeScript

/**
* 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";
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/);
}
async function fillNewPoForm(page: Page, title: string) {
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 });
// Line item
await page.getByPlaceholder("Item description").fill("Test marine part");
await page.getByRole("spinbutton").first().fill("5");
await page.locator("input[placeholder='0.00']").fill("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.getByLabel(/delivery/i).first()).toBeVisible();
await expect(page.getByLabel(/payment terms/i)).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.getByLabel(/title/i)).toHaveValue(title);
await expect(page.getByPlaceholder("Item description")).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/);
});