This repository has been archived on 2026-05-24. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
app/apps/e2e/tests/navigation.spec.ts
T
groombook-paperclip[bot] 1cf1f19e1d Improve admin UI visual design — polish look and feel (#59)
* Improve admin UI visual design — polish look and feel

- Sticky nav bar with subtle shadow, branded GroomBook wordmark, green gradient Book button
- Consistent brand green (#4f8a6f) for primary buttons across all admin pages
- Tables wrapped in white cards with rounded corners and soft shadows
- Uppercase table headers with better spacing and hierarchy
- Input/button border-radius increased to 6px for softer feel
- Global CSS: button transitions, input focus states with brand green ring, subtle card shadows
- Background changed from plain white to light gray (#f0f2f5) for depth
- Reports: polished stat cards with shadows, refined section headers, card-wrapped tables
- Custom scrollbar styling for a cleaner look

Closes groombook/groombook#58

Co-Authored-By: Paperclip <noreply@paperclip.ing>

* Fix test selectors for branded nav text

- Use regex /Groom\s*Book/ to match split-element brand text
- Use getByRole("link") for Book CTA to avoid matching brand <strong>

Co-Authored-By: Paperclip <noreply@paperclip.ing>

* Fix brand text test to handle split-element rendering

The nav brand was changed to <span>Groom</span>Book for color styling,
but getByText with a regex can't match text split across child elements.
Use a custom text matcher that checks the STRONG element's textContent.

Co-Authored-By: Paperclip <noreply@paperclip.ing>

* Fix E2E tests for split-element brand name

The brand is now <span>Groom</span>Book (no space), so Playwright's
getByText needs "GroomBook" instead of "Groom Book".

Co-Authored-By: Paperclip <noreply@paperclip.ing>

---------

Co-authored-by: Groom Book CTO <cto@groombook.dev>
Co-authored-by: Paperclip <noreply@paperclip.ing>
Co-authored-by: Groom Book CTO <cto@groombook.app>
2026-03-19 03:33:34 +00:00

91 lines
3.2 KiB
TypeScript

import { test, expect } from "@playwright/test";
/**
* Navigation smoke tests — verifies that each page loads without errors.
* These tests mock all API calls so they can run without a live backend.
*/
test.beforeEach(async ({ page }) => {
// Intercept all API calls and return empty defaults so pages render.
// Reports endpoints need shaped responses (not bare []) to avoid render crashes.
await page.route("/api/**", (route) => {
const url = route.request().url();
if (url.includes("/api/reports/summary")) {
return route.fulfill({
json: {
from: "",
to: "",
revenue: { totalCents: 0, paidInvoices: 0 },
appointments: { total: 0, completed: 0, cancelled: 0, noShow: 0 },
clients: { total: 0, new: 0 },
},
});
}
if (url.includes("/api/reports/revenue")) {
return route.fulfill({ json: { byPeriod: [], byGroomer: [] } });
}
if (url.includes("/api/reports/appointments")) {
return route.fulfill({ json: { byPeriod: [] } });
}
if (url.includes("/api/reports/services")) {
return route.fulfill({ json: { rows: [] } });
}
if (url.includes("/api/reports/clients")) {
return route.fulfill({
json: { newClients: [], activeInPeriodCount: 0, churnRisk: [], churnRiskTotal: 0 },
});
}
// Appointments, clients, services, staff, invoices, book, etc.
return route.fulfill({ json: [] });
});
});
test("customer portal loads at root", async ({ page }) => {
await page.goto("/");
await expect(page.getByRole("navigation").getByText("Paws & Reflect")).toBeVisible();
await expect(page.locator("nav")).toBeVisible();
});
test("admin appointments page loads", async ({ page }) => {
await page.goto("/admin");
await expect(page.getByText("GroomBook")).toBeVisible();
// Calendar/appointments view renders
await expect(page.locator("nav")).toBeVisible();
});
test("admin clients page loads", async ({ page }) => {
await page.goto("/admin/clients");
await expect(page.getByText("GroomBook")).toBeVisible();
await expect(page.getByRole("link", { name: "Clients" })).toBeVisible();
});
test("admin services page loads", async ({ page }) => {
await page.goto("/admin/services");
await expect(page.getByText("GroomBook")).toBeVisible();
await expect(page.getByRole("link", { name: "Services" })).toBeVisible();
});
test("admin staff page loads", async ({ page }) => {
await page.goto("/admin/staff");
await expect(page.getByText("GroomBook")).toBeVisible();
await expect(page.getByRole("link", { name: "Staff" })).toBeVisible();
});
test("admin invoices page loads", async ({ page }) => {
await page.goto("/admin/invoices");
await expect(page.getByText("GroomBook")).toBeVisible();
await expect(page.getByRole("link", { name: "Invoices" })).toBeVisible();
});
test("admin reports page loads", async ({ page }) => {
await page.goto("/admin/reports");
await expect(page.getByText("GroomBook")).toBeVisible();
await expect(page.getByRole("link", { name: "Reports" })).toBeVisible();
});
test("admin booking portal loads", async ({ page }) => {
await page.goto("/admin/book");
await expect(page.getByText("Book an Appointment")).toBeVisible();
await expect(page.getByText("Choose a service")).toBeVisible();
});