From 53c3ca56b8f2424964e1f99c1653b5cd5f24e796 Mon Sep 17 00:00:00 2001 From: Lucas Berger Date: Thu, 11 Jun 2026 07:38:41 -0400 Subject: [PATCH] fix(07-04): make calendar populated-state test non-vacuous (BL-01) + deterministic seed window (BL-02) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Deep review found the calendar 'populated state' assertions were vacuous: - getByText('Nothing here').toHaveCount(0) targeted CalendarShell's EmptyState, which CalendarShell NEVER renders (success branch always mounts ScheduleXCalendar; EmptyState.tsx is dead code, imported by nothing). The check was permanently green regardless of the seed — a regression dropping all events would have shipped green. - .sx-react-calendar-wrapper renders on any successful auth, with or without events, so it never proved the seed reached the UI. Replaced the dead-EmptyState check with a real DB→UI proof: assert the seeded event title 'Seeded Test Event' is rendered in the grid. Verified non-vacuous — passes with the seed on both profiles; with /api/events mocked to [] the title is absent (would fail). BL-02: the seed anchored the event at now+24h. Both phone profiles render the month-agenda view of the CURRENT month, so on a month's last day 'tomorrow' falls into the next month and vanishes from the grid, making the new visibility assertion date-fragile. Re-anchored to noon-today (UTC) — always today's local date, always in the current-month view. Verified: full 58-test suite passes both profiles; typecheck clean. Co-Authored-By: Claude Opus 4.8 --- apps/pwa/e2e/calendar.spec.ts | 13 +++++++++---- apps/pwa/e2e/global-setup.ts | 12 ++++++++++-- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/apps/pwa/e2e/calendar.spec.ts b/apps/pwa/e2e/calendar.spec.ts index 94ba8eb..d364048 100644 --- a/apps/pwa/e2e/calendar.spec.ts +++ b/apps/pwa/e2e/calendar.spec.ts @@ -67,14 +67,19 @@ test.describe('Rule 5 — populated calendar state', () => { // The Schedule-X React adapter emits a div.sx-react-calendar-wrapper. // Prefer a stable locator: the class name is documented in apps/pwa/src/styles/index.css. // No semantic role exists for the widget wrapper, so CSS class is the documented fallback. + // NOTE: the wrapper renders on any successful auth — this proves the grid mounts, NOT that + // the seed reached the UI. The DB→UI proof is the separate "seeded event is rendered" test. const calendarGrid = page.locator('.sx-react-calendar-wrapper') await expect(calendarGrid).toBeVisible() }) - test('EmptyState "Nothing here" is NOT present when events are seeded', async ({ page }) => { - // CalendarShell renders EmptyState when the events query succeeds with zero occurrences. - // With the seeded event, EmptyState must not appear. Use toHaveCount(0) for strict absence. - await expect(page.getByText('Nothing here')).toHaveCount(0) + test('seeded event "Seeded Test Event" is rendered in the grid (DB→UI proof)', async ({ page }) => { + // The one assertion that actually proves the seeded row flows DB → API → query → grid. + // global-setup seeds a timed event titled 'Seeded Test Event' (noon today) on calendar 10. + // Schedule-X renders the event with its title text inside the grid. If the seed broke, the + // /api/events join regressed, or hydration dropped events, THIS fails (unlike a wrapper / + // dead-EmptyState check, which would stay green). Deep-review BL-01. + await expect(page.getByText('Seeded Test Event').first()).toBeVisible() }) test('no horizontal overflow on populated /calendar (Rule 2)', async ({ page }) => { diff --git a/apps/pwa/e2e/global-setup.ts b/apps/pwa/e2e/global-setup.ts index c4189b6..be0e13b 100644 --- a/apps/pwa/e2e/global-setup.ts +++ b/apps/pwa/e2e/global-setup.ts @@ -92,9 +92,17 @@ export default async function globalSetup(): Promise { ) // Seed one timed (not all-day) calendar event on shared calendar id=10. - // Uses a future dtstart_utc so the event is visible in the UI's default "upcoming" view. + // Anchor to NOON TODAY (UTC) — deliberately NOT "tomorrow" (deep-review BL-02): + // both device profiles are phone-width and render the month-agenda view of the + // CURRENT month. On the last day of a month "tomorrow" rolls into the next month + // and disappears from the rendered grid, making any "seeded event is visible" + // assertion date-fragile. Noon-today lands on today's local calendar date in every + // project timezone and is always inside the current-month view. const uid = 'e2e-seed-event-001' - const futureStart = new Date(Date.now() + 24 * 60 * 60 * 1000) // tomorrow UTC + const _now = new Date() + const futureStart = new Date( + Date.UTC(_now.getUTCFullYear(), _now.getUTCMonth(), _now.getUTCDate(), 12, 0, 0), + ) // MariaDB TIMESTAMP requires 'YYYY-MM-DD HH:MM:SS' format, not ISO 8601 with 'T'. const futureStartUtc = futureStart .toISOString()