pointing to /approvals/[id].
*
* approvals/[id]/page.tsx wraps ManagerEditPoForm in
* 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
{
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 — 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)"
);
});
});