Fixes
Fixed an issue where media manager only shows files uploaded in specific sections. Fixed an issue with icon picker names. Removed jumbo rounding and a few other style tweaks.
This commit is contained in:
74 files changed
+1622
-195
No files matched your search
@@ -207,5 +207,80 @@ test.describe("customer booking journeys", () => {
|
||||
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();
|
||||
|
||||
// The owner-configured terms line (see prisma/seed.ts) renders on the customer card even
|
||||
// while a reward is available — previously this was silently replaced by synthesized
|
||||
// "N more to your reward" text and never showed the real configured terms at all.
|
||||
await expect(page.getByText("Valid for 12 months. One reward per visit.")).toBeVisible();
|
||||
});
|
||||
|
||||
test("9. clicking Book on a Home service preselects it in the booking draft", async ({ page }) => {
|
||||
await signIn(page, SEEDED.registeredCustomer.email, SEEDED.registeredCustomer.password);
|
||||
await page.goto("/home");
|
||||
|
||||
// The description stays visible on the Services tab row — the Book button doesn't hide it.
|
||||
const skinfadeRow = page
|
||||
.locator("div.flex.items-center.justify-between.gap-4.py-4")
|
||||
.filter({ hasText: SEEDED.services.skinfade });
|
||||
await expect(skinfadeRow).toBeVisible();
|
||||
|
||||
await skinfadeRow.getByRole("button", { name: "Book" }).click();
|
||||
|
||||
// No existing draft — preselectServiceAction creates a fresh one with just this service, then redirects straight to /book.
|
||||
await expect(page).toHaveURL(/\/book$/);
|
||||
const skinfadeToggle = page.getByRole("button", { name: new RegExp(`^${SEEDED.services.skinfade}`) });
|
||||
await expect(skinfadeToggle).toHaveAttribute("aria-pressed", "true");
|
||||
await expect(page.getByText("1 selected")).toBeVisible();
|
||||
|
||||
// A second service clicked from Home is added to the existing draft, not a replacement of it.
|
||||
await page.goto("/home");
|
||||
const beardRow = page
|
||||
.locator("div.flex.items-center.justify-between.gap-4.py-4")
|
||||
.filter({ hasText: SEEDED.services.beardTrim });
|
||||
await beardRow.getByRole("button", { name: "Book" }).click();
|
||||
|
||||
await expect(page).toHaveURL(/\/book$/);
|
||||
await expect(skinfadeToggle).toHaveAttribute("aria-pressed", "true");
|
||||
const beardToggle = page.getByRole("button", { name: new RegExp(`^${SEEDED.services.beardTrim}`) });
|
||||
await expect(beardToggle).toHaveAttribute("aria-pressed", "true");
|
||||
await expect(page.getByText("2 selected")).toBeVisible();
|
||||
|
||||
// Re-clicking Book for a service that's already selected doesn't add a duplicate.
|
||||
await page.goto("/home");
|
||||
await beardRow.getByRole("button", { name: "Book" }).click();
|
||||
await expect(page).toHaveURL(/\/book$/);
|
||||
await expect(page.getByText("2 selected")).toBeVisible();
|
||||
|
||||
// Deliberately doesn't complete the booking from here — this account's appointment history is
|
||||
// asserted against by name+staff in several other journeys in this file, and creating one more
|
||||
// here would make those lookups ambiguous. Journey 10 (a fresh guest) completes the flow end to end.
|
||||
});
|
||||
|
||||
test("10. a guest can also preselect a service from Home before booking", async ({ page }) => {
|
||||
await page.goto("/home");
|
||||
// Cold Turbopack first-visit compile — see signIn()'s note in helpers.ts; a click landing before
|
||||
// this settles is silently dropped, and this test (unlike the others) has no prior navigation to warm it.
|
||||
await page.waitForLoadState("networkidle");
|
||||
|
||||
const haircutRow = page
|
||||
.locator("div.flex.items-center.justify-between.gap-4.py-4")
|
||||
.filter({ hasText: SEEDED.services.haircut });
|
||||
await haircutRow.getByRole("button", { name: "Book" }).click();
|
||||
|
||||
await expect(page).toHaveURL(/\/book$/);
|
||||
await expect(page.getByRole("button", { name: new RegExp(`^${SEEDED.services.haircut}`) })).toHaveAttribute(
|
||||
"aria-pressed",
|
||||
"true",
|
||||
);
|
||||
|
||||
await completeBookingFlow(page, {
|
||||
servicesPreselected: true,
|
||||
guest: {
|
||||
firstName: "Preselect",
|
||||
lastName: "Guest",
|
||||
email: uniqueEmail("preselect-guest"),
|
||||
phone: "+44 7700 900902",
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -159,10 +159,20 @@ test.describe.serial("barber operations journeys", () => {
|
||||
expectedState,
|
||||
);
|
||||
|
||||
// Restore the seeded default so this test is repeatable.
|
||||
// Restore the seeded default so this test is repeatable. This save can itself surface an
|
||||
// unrelated conflict — the seed's own "genuinely future" fixture for Leo (see prisma/seed.ts,
|
||||
// appt-leo-future) is dated relative to whenever the seed happened to run and can
|
||||
// coincidentally land on a Sunday, which is off by default — so handle either outcome rather
|
||||
// than assuming a clean save (same pattern as owner-journeys.spec.ts journey 4's restore step).
|
||||
await page.getByRole("switch", { name: "Sunday working" }).click();
|
||||
await page.getByRole("button", { name: "Save", exact: true }).click();
|
||||
await expect(page.getByText("Saved.")).toBeVisible();
|
||||
const saveAnywayButton = page.getByRole("button", { name: "Save Anyway", exact: true });
|
||||
const savedStatus = page.getByText("Saved.");
|
||||
await expect(saveAnywayButton.or(savedStatus)).toBeVisible();
|
||||
if (await saveAnywayButton.isVisible()) {
|
||||
await saveAnywayButton.click();
|
||||
}
|
||||
await expect(savedStatus).toBeVisible();
|
||||
});
|
||||
|
||||
test("8. barber adds a recurring break", async ({ page }) => {
|
||||
@@ -230,12 +240,22 @@ test.describe.serial("barber operations journeys", () => {
|
||||
});
|
||||
|
||||
test("10. barber receives a new-booking notification", async ({ page }) => {
|
||||
// Journey 3 (this file runs serially) already booked Marcus a fresh appointment for Jordan
|
||||
// Blake — see NotificationService/booking-notifications.ts, notifyBookingCreated, which
|
||||
// Deliberately books its own fresh appointment rather than relying on journey 3's — every
|
||||
// other independent journey in this file follows that same "book a fresh appointment first"
|
||||
// rule (see this file's header comment), and this one was the exception, which made it fail
|
||||
// in confusing ways whenever it ran outside the full serial file (e.g. `-g` filtering past
|
||||
// journey 3). See NotificationService/booking-notifications.ts, notifyBookingCreated, which
|
||||
// queues STAFF_NEW_APPOINTMENT for the assigned barber on every booking.
|
||||
await signIn(page, SEEDED.owner.email, "password123");
|
||||
await page.goto("/staff");
|
||||
|
||||
await page.goto("/staff/book?q=Jordan");
|
||||
await page.getByRole("button", { name: /Jordan Blake/ }).click();
|
||||
await completeStaffBookingFlow(page, {
|
||||
serviceName: SEEDED.services.haircut,
|
||||
staffName: SEEDED.owner.name,
|
||||
});
|
||||
|
||||
await page.goto("/staff");
|
||||
const bell = page.getByRole("link", { name: /Notifications, \d+ unread/ });
|
||||
await expect(bell).toBeVisible();
|
||||
await bell.click();
|
||||
|
||||
Reference in new issue
Block a user