Files
hope-events/e2e/fixtures.ts
joshuaandClaude Opus 5.5 f99b84858e Session gaps: per-session waitlist, door session picker, all-sessions choice
- Waitlist per session: entries for pick-sessions tickets name a session;
  offers use that session's free places, never run past its start, and
  claim links tick it. Held offers count towards session capacity.
- At the door: the Select items pop-up has the session picker, with
  today's session preselected for new attendees.
- On pick-sessions events every ticket shows the session list straight
  away. "All sessions" tickets record the ticked sessions
  (RegistrationOption.sessionIds) at one price; scanning, attendance,
  certificates, reminders and capacity follow them.
- Fix: per-session stock limits weren't enforced (takenCount filtered
  WaitlistEntry by a sessionId column that didn't exist, and the error
  was swallowed), and the public event page lost its stock info.
- Fix: editing a door registration with picked sessions couldn't be saved.
- Fix: the offline-scanning service worker served /_next/static cache-first
  forever, so dev browsers that had opened the scanner ran stale code.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 11:38:21 +02:00

49 lines
1.9 KiB
TypeScript

import { test as base, expect, type Page } from "@playwright/test";
import fs from "fs";
import path from "path";
export type Role = "admin" | "supervisor" | "staff" | "user";
export type Seed = {
users: Record<Role, { id: string; name: string; email: string; token: string }>;
events: { free: string; paid: string; course: string; dropIn: string; conference: string };
course: { sessions: { id: string; label: string }[]; ticketId: string; qrCode: string };
};
export const API = "http://localhost:5100";
// Written by e2e/support/prepare-db.js when the e2e backend starts, so read lazily.
let cached: Seed | null = null;
export function seed(): Seed {
if (!cached) cached = JSON.parse(fs.readFileSync(path.join(__dirname, ".data", "seed.json"), "utf8")) as Seed;
return cached;
}
/** Signs the page in as a seeded user (the token goes where the app keeps it). */
export async function loginAs(page: Page, role: Role) {
await page.addInitScript((token) => {
localStorage.setItem("hope_events_token", token);
}, seed().users[role].token);
}
export const test = base.extend({
page: async ({ page }, use) => {
// Skip the cookie notice so it never covers what the tests click.
await page.addInitScript(() => localStorage.setItem("hope_events_cookie_consent", "declined"));
await use(page);
},
});
/** Calls the e2e API as a seeded user. */
export async function api<T = unknown>(page: Page, role: Role, method: string, url: string, body?: unknown): Promise<T> {
const res = await page.request.fetch(`${API}${url}`, {
method,
headers: { Authorization: `Bearer ${seed().users[role].token}`, "Content-Type": "application/json" },
data: body === undefined ? undefined : JSON.stringify(body),
});
const text = await res.text();
if (!res.ok()) throw new Error(`${method} ${url} → ${res.status()}: ${text}`);
return (text ? JSON.parse(text) : null) as T;
}
export { expect };