The suite could not pass on the test database: global setup now seeds the shared dev/Test123 login with a superuser Roles doc and the dashboard rank, and points its own Payload connection at the test database. Playwright runs one worker with generous test and expect timeouts because first navigation to a route triggers a cold webpack compile, and gains E2E_BROWSER_PATH to swap the browser binary when the installed Vivaldi crashes under automation. The landing page gains the missing h1 the guest specs assert, login flows wait for the redirect to land, strict-mode locator collisions get exact or level-scoped matches, the session banner test re-signs the login claims with a short expiry using the runtime secret, and the reservation dialog click forces the dispatch and verifies the dialog opened (the trigger goes aria-hidden behind the modal mid-click, which stranded the retry loop).
58 lines
2.1 KiB
JavaScript
58 lines
2.1 KiB
JavaScript
import { test, expect } from "@playwright/test";
|
|
|
|
test("banner shows countdown + extend button", async ({ page, context }) => {
|
|
// Craft near-expiry token and set it as cookie
|
|
const { SignJWT } = await import("jose");
|
|
// IMPORTANT: this must be the RUNTIME payload secret, not .env's
|
|
// PAYLOAD_SECRET. A previous session confirmed the Payload AI plugin
|
|
// rewrites the secret at runtime; the value below is stable for this repo
|
|
// (verified empirically by signing a login token and verifying it).
|
|
const SECRET = "f80003be79c28ca283c2cbfde2984356";
|
|
const key = new TextEncoder().encode(SECRET);
|
|
|
|
// Login to get a real session, then re-sign the EXACT same claims with a
|
|
// short exp (echoing all claims keeps Payload's token verification happy).
|
|
const loginRes = await fetch("http://localhost:3000/api/users/login", {
|
|
method: "POST",
|
|
headers: { "Content-Type": "application/json" },
|
|
body: JSON.stringify({ username: "dev", password: "Test123" }),
|
|
});
|
|
const data = await loginRes.json();
|
|
const claims = JSON.parse(Buffer.from(data.token.split(".")[1], "base64url").toString());
|
|
|
|
const now = Math.floor(Date.now() / 1000);
|
|
const near = await new SignJWT({ ...claims, iat: now, exp: now + 600 })
|
|
.setProtectedHeader({ alg: "HS256", typ: "JWT" })
|
|
.setIssuedAt(now)
|
|
.setExpirationTime(now + 600)
|
|
.sign(key);
|
|
|
|
// Set cookie
|
|
await context.addCookies([
|
|
{
|
|
name: "payload-token",
|
|
value: near,
|
|
domain: "localhost",
|
|
path: "/",
|
|
expires: now + 600,
|
|
},
|
|
]);
|
|
|
|
// Navigate to home
|
|
await page.goto("http://localhost:3000/");
|
|
await page.waitForSelector("role=status", { timeout: 10000 });
|
|
|
|
// Check banner is visible
|
|
const banner = page.locator("role=status").first();
|
|
await expect(banner).toBeVisible();
|
|
|
|
// Check countdown
|
|
const countdown = banner.locator(".font-mono.tabular-nums").first();
|
|
await expect(countdown).toHaveText(/0\d:\d{2}/); // should show ~10:00
|
|
|
|
// Check extend button
|
|
const extendBtn = banner.locator("button:has-text('Extend')");
|
|
await expect(extendBtn).toBeVisible();
|
|
|
|
console.log("Banner check PASSED");
|
|
});
|