Files
hope-events/e2e/pick-sessions.spec.ts
T
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

95 lines
5.0 KiB
TypeScript

import { test, expect, seed, loginAs, api } from "./fixtures";
type Registration = { id: string; eventId: string; user?: { name: string }; registrationOptions: { sessionId?: string | null; sessionIds?: string[]; priceSnapshot: number; quantity: number }[] };
type WaitlistEntry = { eventId: string; sessionId: string | null; quantity: number };
test.describe("pick sessions", () => {
test("book two classes of a drop-in event; a full class can't be ticked", async ({ page }) => {
await loginAs(page, "user");
await page.goto(`/register/${seed().events.dropIn}`);
await expect(page.getByText("R50.00 per session").first()).toBeVisible();
await page.getByRole("button", { name: "+" }).first().click();
const picker = page.getByRole("group", { name: "Choose sessions" });
await expect(picker.getByText("Full")).toBeVisible();
await expect(picker.getByRole("checkbox").first()).toBeDisabled();
await picker.getByRole("checkbox").nth(1).check();
await picker.getByRole("checkbox").nth(2).check();
await expect(page.getByText("Total: R100.00")).toBeVisible();
await page.getByRole("button", { name: "Register", exact: true }).click();
await page.waitForURL(/\/registration\/success/);
const regs = await api<Registration[]>(page, "user", "GET", "/api/registrations/myregistrations");
const reg = regs.find((r) => r.eventId === seed().events.dropIn);
expect(reg?.registrationOptions.filter((o) => o.sessionId)).toHaveLength(2);
});
test("an all-sessions pass: tick the days, pay one price", async ({ page }) => {
await loginAs(page, "user");
await page.goto(`/register/${seed().events.conference}`);
await expect(page.getByText("R200.00", { exact: true })).toBeVisible();
await expect(page.getByText("per session")).toHaveCount(0);
const picker = page.getByRole("group", { name: "Choose sessions" });
await picker.getByRole("checkbox").nth(0).check();
await picker.getByRole("checkbox").nth(2).check();
await expect(page.getByText("Total: R200.00")).toBeVisible();
await page.getByRole("button", { name: "Register", exact: true }).click();
await page.waitForURL(/\/registration\/success/);
const regs = await api<Registration[]>(page, "user", "GET", "/api/registrations/myregistrations");
const line = regs.find((r) => r.eventId === seed().events.conference)?.registrationOptions[0];
expect(line?.sessionId ?? null).toBeNull();
expect(line?.sessionIds).toHaveLength(2);
expect(line?.priceSnapshot).toBe(200);
expect(line?.quantity).toBe(1);
});
test("join the waitlist for a full class", async ({ page }) => {
await loginAs(page, "admin");
await page.goto(`/register/${seed().events.dropIn}`);
await page.getByRole("button", { name: "+" }).first().click();
const picker = page.getByRole("group", { name: "Choose sessions" });
await picker.getByRole("button", { name: "Join waitlist" }).click();
await expect(page.getByText(/Drop-in — Class 1/)).toBeVisible();
await page.locator("form").filter({ hasText: "Join the waitlist" }).getByRole("button", { name: "Join waitlist" }).click();
await expect(page.getByText(/You're on the waitlist/)).toBeVisible();
const mine = await api<WaitlistEntry[]>(page, "admin", "GET", "/api/waitlist/mine");
const entry = mine.find((e) => e.eventId === seed().events.dropIn);
expect(entry?.sessionId).toBeTruthy();
expect(entry?.quantity).toBe(1);
});
test("at the door: book a class for a walk-in", async ({ page }) => {
await loginAs(page, "supervisor");
await page.goto("/dashboard/supervisor/at-the-door");
const value = await page.locator("option", { hasText: "E2E Drop-in Class" }).getAttribute("value");
// The page picks a default event once its list loads; choose ours after that.
await page.waitForLoadState("networkidle");
await page.locator("select").first().selectOption(value!);
await page.waitForLoadState("networkidle");
await expect(page.locator("select").first()).toHaveValue(value!);
await page.getByPlaceholder("Name, email or phone…").fill("Door Walk In");
await page.getByText('New attendee "Door Walk In"…').click();
await page.getByPlaceholder("Email address").fill("door-walkin@e2e.test");
await page.getByRole("button", { name: "Next — Choose Items" }).click();
await expect(page.getByText("R 50.00 per session")).toBeVisible();
await page.getByRole("button", { name: "+" }).first().click();
const picker = page.getByRole("group", { name: "Choose sessions" });
await picker.getByRole("checkbox").nth(1).check();
await expect(page.getByText("Total: R 50.00")).toBeVisible();
await page.getByRole("button", { name: "Confirm Registration" }).click();
await expect(page.getByText("Select Items")).toBeHidden();
const regs = await api<Registration[]>(page, "supervisor", "GET", `/api/registrations/event/${seed().events.dropIn}`);
const reg = regs.find((r) => r.user?.name === "Door Walk In");
expect(reg?.registrationOptions.filter((o) => o.sessionId)).toHaveLength(1);
});
});