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/web/e2e/tests/portal-data.spec.ts
T
Flea Flicker fa9aa5cff1 feat(e2e): add Playwright E2E test suite for critical user journeys (GRO-306)
Implements the automated Playwright E2E suite as the pre-UAT gate following
the UAT failures identified in GRO-299. Creates 5 test files in apps/web/e2e/:

- portal-auth.spec.ts: verifies client portal auth (client name shown, not "Hi, Guest")
- portal-data.spec.ts: verifies portal sections render without auth gates
- admin-services.spec.ts: asserts no duplicate service names in admin/services and booking wizard
- admin-reports.spec.ts: verifies reports page shows non-zero data for last 60 days
- console-health.spec.ts: asserts no 404s for favicon/PWA assets and no JS exceptions

Also adds:
- apps/web/e2e/ with Playwright config targeting groombook.dev.farh.net
- Shared fixtures with storageState-based auth via dev login selector
- test:e2e npm script in apps/web/package.json
- web-e2e CI job targeting PRs (runs after deploy-dev)

Note: Tests 1 & 2 (portal auth/data) depend on GRO-300 being deployed.
Tests 3-5 run against current dev state.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-03-31 21:43:06 +00:00

89 lines
2.9 KiB
TypeScript

import { test, expect } from "./fixtures.js";
/**
* E2E test: Portal Data Integrity (GRO-306)
*
* Verifies that the client portal sections render correctly with actual data
* and don't show auth-gate messages after login.
*
* DEPENDENCY: Requires GRO-300 to be deployed. Tests 1 & 2 share this dependency.
*
* Journey:
* 1. Login as client
* 2. Navigate to appointments section — assert no "Please sign in", content renders
* 3. Navigate to pets section — assert content renders (or explicit empty state)
* 4. Navigate to billing section — assert no JS errors, section renders
*/
test.describe("Portal Data Integrity", () => {
test.beforeEach(async ({ clientPage }) => {
await clientPage.goto("/");
await clientPage.waitForLoadState("networkidle");
});
test("appointments section renders without auth gate", async ({
clientPage,
}) => {
// Click the Appointments nav item
const appointmentsNav = clientPage.getByRole("button", { name: /appointments/i });
await appointmentsNav.click();
await clientPage.waitForLoadState("networkidle");
// Must NOT show "Please sign in" gate
await expect(
clientPage.locator("text=Please sign in")
).not.toBeVisible({ timeout: 5000 });
// The section heading or nav should indicate we're in appointments
await expect(
clientPage.locator("text=Appointments")
).toBeVisible();
});
test("pets section renders with content or explicit empty state", async ({
clientPage,
}) => {
// Click the My Pets nav item
const petsNav = clientPage.getByRole("button", { name: /my pets/i });
await petsNav.click();
await clientPage.waitForLoadState("networkidle");
// Must NOT show auth gate
await expect(
clientPage.locator("text=Please sign in")
).not.toBeVisible({ timeout: 5000 });
// Should show either pet content or a legitimate empty state
const hasPetsContent =
(await clientPage.locator("text=Add a pet").isVisible()) ||
(await clientPage.locator("text=No pets").isVisible()) ||
(await clientPage.locator('[role="button"]').count()) > 0;
expect(hasPetsContent).toBeTruthy();
});
test("billing section renders without JS errors", async ({ clientPage }) => {
// Capture console errors
const consoleErrors: string[] = [];
clientPage.on("console", (msg) => {
if (msg.type() === "error") {
consoleErrors.push(msg.text());
}
});
// Click the Billing nav item
const billingNav = clientPage.getByRole("button", { name: /billing/i });
await billingNav.click();
await clientPage.waitForLoadState("networkidle");
// Must NOT show auth gate
await expect(
clientPage.locator("text=Please sign in")
).not.toBeVisible({ timeout: 5000 });
// No JS exceptions on this section
const jsExceptions = consoleErrors.filter(
(e) => !e.includes("favicon") && !e.includes("404")
);
expect(jsExceptions).toHaveLength(0);
});
});