134 lines
6 KiB
TypeScript
134 lines
6 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";
|
|
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/);
|
|
});
|