pelagia-portal/App/tests/e2e/mobile/manager-approvals.spec.ts
2026-05-18 23:18:58 +05:30

153 lines
5.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* User stories covered: Feature 17 — Mobile Manager approval queue as cards
* - MANAGER at 375×812 sees /approvals as PO cards (not a table)
* - Tapping a card navigates to /approvals/[id]
* - ManagerEditPoForm is NOT visible at mobile viewport on approval detail page
* - Approve/Reject action buttons ARE visible at mobile
*
* Design: approvals/page.tsx renders:
* - <div class="hidden md:block"> for the desktop table
* - <div class="md:hidden space-y-3"> for mobile cards
* Each mobile card is wrapped in a <Link> pointing to /approvals/[id].
*
* approvals/[id]/page.tsx wraps ManagerEditPoForm in <div class="hidden md:block">
* so it is CSS-hidden at mobile, but ApprovalActions renders unconditionally.
*
* 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 };
/** Ensure there is at least one PO in MGR_REVIEW status and return its approval URL. */
async function ensurePendingPo(
page: import("@playwright/test").Page,
context: BrowserContext,
title: string
): Promise<string> {
await login(page, USERS.TECH);
const poUrl = await submitPo(page, title);
const poId = poUrl.split("/po/")[1].replace(/\/$/, "");
await context.clearCookies();
await login(page, USERS.MANAGER);
return `/approvals/${poId}`;
}
test.describe("Feature 17 — Mobile Manager approval queue", () => {
test("US-17a: MANAGER at mobile viewport sees /approvals — mobile cards rendered", async ({
page,
context,
}: {
page: import("@playwright/test").Page;
context: BrowserContext;
}) => {
// Create a PO in MGR_REVIEW state first
await ensurePendingPo(page, context, `E2E_MOBILE_MGR_${Date.now()}`);
await page.setViewportSize(MOBILE_VIEWPORT);
await page.goto("/approvals");
await page.waitForLoadState("networkidle");
await expect(
page.getByRole("heading", { name: /approval/i })
).toBeVisible();
// Desktop table should be hidden (md:hidden class hides it on mobile)
// Mobile cards container should be visible
const mobileCards = page.locator(".md\\:hidden.space-y-3, [class*='md:hidden']").first();
// At minimum, verify at least one card link to /approvals/ exists
const cardLinks = page.locator("a[href*='/approvals/']");
const cardCount = await cardLinks.count();
expect(cardCount).toBeGreaterThan(0);
console.log(`${cardCount} approval card link(s) visible at mobile viewport`);
});
test("US-17b: tapping a card navigates to /approvals/[id]", async ({
page,
context,
}: {
page: import("@playwright/test").Page;
context: BrowserContext;
}) => {
const approvalDetailUrl = await ensurePendingPo(
page,
context,
`E2E_MOBILE_CARD_NAV_${Date.now()}`
);
await page.setViewportSize(MOBILE_VIEWPORT);
await page.goto("/approvals");
await page.waitForLoadState("networkidle");
// Click the first card link
const cardLink = page.locator("a[href*='/approvals/']").first();
await expect(cardLink).toBeVisible();
await cardLink.click();
await expect(page).toHaveURL(/\/approvals\/.+/, { timeout: 10_000 });
console.log(`✓ Tapping card navigated to ${page.url()}`);
});
test("US-17c: ManagerEditPoForm is NOT visible at mobile viewport on approval detail", async ({
page,
context,
}: {
page: import("@playwright/test").Page;
context: BrowserContext;
}) => {
const approvalDetailUrl = await ensurePendingPo(
page,
context,
`E2E_MOBILE_EDIT_FORM_${Date.now()}`
);
await page.setViewportSize(MOBILE_VIEWPORT);
await page.goto(approvalDetailUrl);
await page.waitForLoadState("networkidle");
// ManagerEditPoForm is inside <div class="hidden md:block"> — CSS hidden on mobile
// The form contains "Edit Line Items" or similar heading
// At 375px, Tailwind's md breakpoint (768px) is not active, so hidden md:block = invisible
const editForm = page
.getByRole("heading", { name: /edit line items|amend/i })
.first();
await expect(editForm).not.toBeVisible();
console.log("✓ Manager line-item edit form is not visible at mobile viewport");
});
test("US-17c: Approve/Reject action buttons ARE visible at mobile viewport", async ({
page,
context,
}: {
page: import("@playwright/test").Page;
context: BrowserContext;
}) => {
const approvalDetailUrl = await ensurePendingPo(
page,
context,
`E2E_MOBILE_ACTIONS_${Date.now()}`
);
await page.setViewportSize(MOBILE_VIEWPORT);
await page.goto(approvalDetailUrl);
await page.waitForLoadState("networkidle");
// ApprovalActions renders Approve and Reject buttons without any responsive hiding
// If manager has no signature, a warning is shown instead — check for either
const approveBtn = page.getByRole("button", { name: /^approve$/i });
const signatureWarning = page.getByText(/signature required/i);
const hasApproveBtn = await approveBtn.isVisible();
const hasSignatureWarning = await signatureWarning.isVisible();
expect(hasApproveBtn || hasSignatureWarning).toBeTruthy();
console.log(
hasApproveBtn
? "✓ Approve button visible at mobile viewport"
: "✓ Signature-required warning shown at mobile viewport (manager needs signature)"
);
});
});