This commit is contained in:
Stefan Lazic committed 2026-08-03 21:15:24 +01:00
1 parent ec5583f972
commit 546613e677
189 files changed
+9873 -655

No files matched your search

+59 -2
View File
@@ -30,10 +30,23 @@ test.describe("customer booking journeys", () => {
guest,
});
// Guest confirmation offers an account-creation prompt but doesn't force it.
// Guest confirmation offers an account-creation prompt but doesn't force it —
// and also a real, secured "View Appointment" link (Phase 7: a guest's only
// credential is a single-purpose access token in the URL, not the bare
// appointment id — see src/services/scheduling/guest-access.service.ts).
await expect(page.getByText("Want to manage this booking later?")).toBeVisible();
await expect(page.getByRole("link", { name: "Create an Account" })).toBeVisible();
await expect(page.getByRole("link", { name: "View Appointment" })).toHaveCount(0);
const viewLink = page.getByRole("link", { name: "View Appointment" });
await expect(viewLink).toBeVisible();
const href = await viewLink.getAttribute("href");
expect(href).toMatch(/^\/appointments\/[^/]+\?token=.+/);
// The token in the link actually works, and a guessed/blank token doesn't.
await page.goto(href!);
await expect(page.getByRole("heading", { name: "Appointment Details" })).toBeVisible();
const appointmentId = href!.split("?")[0]!.split("/").pop();
await page.goto(`/appointments/${appointmentId}`);
await expect(page.getByRole("heading", { name: "This page could not be found." })).toBeVisible();
});
test("2. registered customer books an appointment", async ({ page }) => {
@@ -151,4 +164,48 @@ test.describe("customer booking journeys", () => {
await page.getByRole("tab", { name: "Past" }).click();
await expect(completedCard).toBeVisible();
});
test("7. booking confirmation creates an in-app notification the customer can read and mark read", async ({ page }) => {
await signIn(page, SEEDED.registeredCustomer.email, SEEDED.registeredCustomer.password);
await page.goto("/book");
await completeBookingFlow(page, {
serviceName: SEEDED.services.beardTrim,
staffName: SEEDED.barberSkinfadeBeard.name,
});
// The bell in the persistent customer layout shows an unread badge — see (customer)/layout.tsx.
await page.goto("/home");
const bell = page.getByRole("link", { name: /Notifications, \d+ unread/ });
await expect(bell).toBeVisible();
await bell.click();
await expect(page).toHaveURL(/\/notifications/);
await expect(page.getByText("Booking confirmed").first()).toBeVisible();
await expect(page.getByRole("status", { name: "Unread" }).first()).toBeVisible();
// "Mark All Read" clears every unread notification — the nav badge disappears entirely
// afterward, regardless of how many notifications this account has accumulated.
await page.getByRole("button", { name: "Mark All Read" }).click();
await page.waitForLoadState("networkidle");
await expect(page.getByRole("status", { name: "Unread" })).toHaveCount(0);
await page.goto("/home");
await expect(page.getByRole("link", { name: "Notifications" })).toBeVisible();
await expect(page.getByRole("link", { name: /Notifications, \d+ unread/ })).toHaveCount(0);
});
test("8. customer sees their available loyalty reward on Home and the Loyalty page", async ({ page }) => {
// Seeded at exactly 10/10 stamps with one AVAILABLE reward (see prisma/seed.ts) —
// must run before owner-journeys.spec.ts's journey 10, which redeems it.
await signIn(page, SEEDED.secondCustomer.email, SEEDED.secondCustomer.password);
await page.goto("/home");
await expect(page.getByText(/Reward available/)).toBeVisible();
await page.getByText(/Reward available/).click();
await expect(page).toHaveURL(/\/loyalty/);
await expect(page.getByRole("heading", { name: "Loyalty", exact: true })).toBeVisible();
await expect(page.getByText("Reward available:")).toBeVisible();
await expect(page.getByText("stamp added").first()).toBeVisible();
});
});