141 lines
4.7 KiB
TypeScript
141 lines
4.7 KiB
TypeScript
/**
|
||
* User stories covered: Feature 18 — Mobile Accounts payment actions
|
||
* - ACCOUNTS at 375×812 can load /payments without Desktop Required overlay
|
||
* - MGR_APPROVED PO shows "Start Payment Processing" button
|
||
* - SENT_FOR_PAYMENT PO shows payment reference input and "Confirm Payment Sent" button
|
||
*
|
||
* Created: 2026-05-17
|
||
*/
|
||
import { test, expect, type BrowserContext } from "@playwright/test";
|
||
import { login, USERS, submitPo } from "../helpers/login";
|
||
|
||
const MOBILE_VIEWPORT = { width: 375, height: 812 };
|
||
|
||
async function createApprovedPo(
|
||
page: import("@playwright/test").Page,
|
||
context: BrowserContext,
|
||
title: string
|
||
): Promise<string> {
|
||
await login(page, USERS.TECH);
|
||
const poUrl = await submitPo(page, title);
|
||
|
||
await context.clearCookies();
|
||
await login(page, USERS.MANAGER);
|
||
await page.goto(poUrl);
|
||
await page.getByRole("button", { name: /^approve$/i }).click();
|
||
await expect(page.getByText(/approved/i)).toBeVisible({ timeout: 10_000 });
|
||
|
||
return poUrl;
|
||
}
|
||
|
||
test.describe("Feature 18 — Mobile Accounts payment actions", () => {
|
||
test("US-18a: ACCOUNTS at mobile viewport — /payments loads with NO Desktop Required overlay", async ({
|
||
page,
|
||
}) => {
|
||
await page.setViewportSize(MOBILE_VIEWPORT);
|
||
await login(page, USERS.ACCOUNTS);
|
||
await page.goto("/payments");
|
||
|
||
// Should NOT see the Desktop Required overlay (ACCOUNTS is a mobile-enabled role)
|
||
await expect(page.getByText("Desktop Required")).not.toBeVisible();
|
||
await expect(
|
||
page.getByRole("heading", { name: /payment queue/i })
|
||
).toBeVisible();
|
||
console.log(
|
||
"✓ ACCOUNTS at mobile sees Payment Queue (no Desktop Required overlay)"
|
||
);
|
||
});
|
||
|
||
test("US-18a: ACCOUNTS at mobile — /payments page shows payment queue content", async ({
|
||
page,
|
||
}) => {
|
||
await page.setViewportSize(MOBILE_VIEWPORT);
|
||
await login(page, USERS.ACCOUNTS);
|
||
await page.goto("/payments");
|
||
await page.waitForLoadState("networkidle");
|
||
|
||
// The page shows either a list of PO cards or an empty-state message
|
||
const hasQueue =
|
||
(await page.locator("[class*='space-y']").count()) > 0 ||
|
||
(await page.getByText(/no orders in the payment queue/i).isVisible());
|
||
|
||
expect(hasQueue).toBeTruthy();
|
||
console.log("✓ Payment queue content rendered at mobile viewport");
|
||
});
|
||
|
||
test("US-18b: MGR_APPROVED PO shows Start Payment Processing button at mobile", async ({
|
||
page,
|
||
context,
|
||
}: {
|
||
page: import("@playwright/test").Page;
|
||
context: BrowserContext;
|
||
}) => {
|
||
const poUrl = await createApprovedPo(
|
||
page,
|
||
context,
|
||
`E2E_MOBILE_PAY_${Date.now()}`
|
||
);
|
||
|
||
await context.clearCookies();
|
||
await page.setViewportSize(MOBILE_VIEWPORT);
|
||
await login(page, USERS.ACCOUNTS);
|
||
await page.goto(poUrl);
|
||
await page.waitForLoadState("networkidle");
|
||
|
||
const processBtn = page.getByRole("button", {
|
||
name: /process payment|start payment/i,
|
||
});
|
||
await expect(processBtn).toBeVisible({ timeout: 10_000 });
|
||
console.log("✓ Start Payment Processing button visible at mobile viewport for MGR_APPROVED PO");
|
||
});
|
||
|
||
test("US-18c: SENT_FOR_PAYMENT PO shows reference input and Confirm button at mobile", async ({
|
||
page,
|
||
context,
|
||
}: {
|
||
page: import("@playwright/test").Page;
|
||
context: BrowserContext;
|
||
}) => {
|
||
// Create and move to SENT_FOR_PAYMENT
|
||
const poUrl = await createApprovedPo(
|
||
page,
|
||
context,
|
||
`E2E_MOBILE_SENT_${Date.now()}`
|
||
);
|
||
|
||
await context.clearCookies();
|
||
await page.setViewportSize(MOBILE_VIEWPORT);
|
||
await login(page, USERS.ACCOUNTS);
|
||
await page.goto(poUrl);
|
||
|
||
// Start payment processing
|
||
await page.getByRole("button", { name: /process payment|start payment/i }).click();
|
||
await expect(page.getByText(/sent for payment/i)).toBeVisible({
|
||
timeout: 10_000,
|
||
});
|
||
|
||
// Now we should see payment reference input and confirm button
|
||
const refInput = page.getByPlaceholder(/reference|ref/i).first();
|
||
await expect(refInput).toBeVisible({ timeout: 5_000 });
|
||
|
||
const confirmBtn = page.getByRole("button", {
|
||
name: /confirm payment|mark.*paid/i,
|
||
});
|
||
await expect(confirmBtn).toBeVisible();
|
||
console.log(
|
||
"✓ Payment reference input and Confirm Payment button visible at mobile on SENT_FOR_PAYMENT PO"
|
||
);
|
||
});
|
||
|
||
test("US-18a: ACCOUNTS mobile bottom nav is visible on /payments", async ({
|
||
page,
|
||
}) => {
|
||
await page.setViewportSize(MOBILE_VIEWPORT);
|
||
await login(page, USERS.ACCOUNTS);
|
||
|
||
// MobileBottomNav renders for ACCOUNTS — should be visible at mobile
|
||
const bottomNav = page.locator("nav.md\\:hidden, nav[class*='md:hidden']");
|
||
await expect(bottomNav).toBeVisible();
|
||
console.log("✓ Mobile bottom navigation bar visible for ACCOUNTS at mobile viewport");
|
||
});
|
||
});
|