fix(07-04): make calendar populated-state test non-vacuous (BL-01) + deterministic seed window (BL-02)
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 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
f52b722b9b
commit
53c3ca56b8
@@ -67,14 +67,19 @@ test.describe('Rule 5 — populated calendar state', () => {
|
|||||||
// The Schedule-X React adapter emits a div.sx-react-calendar-wrapper.
|
// 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.
|
// 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.
|
// 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')
|
const calendarGrid = page.locator('.sx-react-calendar-wrapper')
|
||||||
await expect(calendarGrid).toBeVisible()
|
await expect(calendarGrid).toBeVisible()
|
||||||
})
|
})
|
||||||
|
|
||||||
test('EmptyState "Nothing here" is NOT present when events are seeded', async ({ page }) => {
|
test('seeded event "Seeded Test Event" is rendered in the grid (DB→UI proof)', async ({ page }) => {
|
||||||
// CalendarShell renders EmptyState when the events query succeeds with zero occurrences.
|
// The one assertion that actually proves the seeded row flows DB → API → query → grid.
|
||||||
// With the seeded event, EmptyState must not appear. Use toHaveCount(0) for strict absence.
|
// global-setup seeds a timed event titled 'Seeded Test Event' (noon today) on calendar 10.
|
||||||
await expect(page.getByText('Nothing here')).toHaveCount(0)
|
// 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 }) => {
|
test('no horizontal overflow on populated /calendar (Rule 2)', async ({ page }) => {
|
||||||
|
|||||||
@@ -92,9 +92,17 @@ export default async function globalSetup(): Promise<void> {
|
|||||||
)
|
)
|
||||||
|
|
||||||
// Seed one timed (not all-day) calendar event on shared calendar id=10.
|
// 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 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'.
|
// MariaDB TIMESTAMP requires 'YYYY-MM-DD HH:MM:SS' format, not ISO 8601 with 'T'.
|
||||||
const futureStartUtc = futureStart
|
const futureStartUtc = futureStart
|
||||||
.toISOString()
|
.toISOString()
|
||||||
|
|||||||
Reference in New Issue
Block a user