// spec: e2e/plans/chore-scheduler.plan.md — Section 10: SSE Real-Time Updates import { test, expect } from '@playwright/test' import { createChild, createChoreTask, assignChore, setDaysSchedule, deleteSchedule, choreCard, todayDayIndex, nonTodayDayIndex, todayISO, } from './helpers' const CHILD_NAME = 'SSEScheduleChild' const CHORE_NAME = 'SSEScheduleChore' test.describe('SSE Real-Time Updates', () => { test.describe.configure({ mode: 'serial' }) let childId = '' let choreId = '' test.beforeAll(async ({ request }) => { childId = await createChild(request, CHILD_NAME) choreId = await createChoreTask(request, CHORE_NAME) await assignChore(request, childId, [choreId]) }) test.afterAll(async ({ request }) => { if (childId) await request.delete(`/api/child/${childId}`) if (choreId) await request.delete(`/api/task/${choreId}`) }) test.beforeEach(async ({ request }) => { await deleteSchedule(request, childId, choreId) }) test('10.1 Setting a schedule via API reflects in an already-open parent view', async ({ page, request, }) => { await page.goto(`/parent/${childId}`) const card = choreCard(page, CHORE_NAME) await card.waitFor() // No schedule: card should be visible and not grayed out await expect(card).not.toHaveClass(/chore-inactive/) // Set a non-today schedule via API (triggers SSE chore_schedule_modified) await setDaysSchedule(request, childId, choreId, [nonTodayDayIndex()], { hour: 8, minute: 0 }) // SSE fires → card should become grayed out (chore-inactive) await expect(card).toHaveClass(/chore-inactive/, { timeout: 5000 }) }) test('10.2 Deleting a schedule via API un-grays the chore in parent view', async ({ page, request, }) => { // Seed a non-today schedule first await setDaysSchedule(request, childId, choreId, [nonTodayDayIndex()], { hour: 8, minute: 0 }) await page.goto(`/parent/${childId}`) const card = choreCard(page, CHORE_NAME) await card.waitFor() await expect(card).toHaveClass(/chore-inactive/) // Delete schedule via API await request.delete(`/api/child/${childId}/task/${choreId}/schedule`) // SSE fires → card returns to normal await expect(card).not.toHaveClass(/chore-inactive/, { timeout: 5000 }) }) test('10.3 Extending time via API reflects in an already-open parent view', async ({ page, request, }) => { // Set today schedule with past deadline (chore starts expired) await setDaysSchedule(request, childId, choreId, [todayDayIndex()], { hour: 0, minute: 1 }) await page.goto(`/parent/${childId}`) const card = choreCard(page, CHORE_NAME) await card.waitFor() await expect(card.getByText('TOO LATE')).toBeVisible() // Extend via API await request.post(`/api/child/${childId}/task/${choreId}/extend`, { data: { date: todayISO() }, }) // SSE chore_time_extended fires → TOO LATE badge disappears await expect(card.getByText('TOO LATE')).not.toBeVisible({ timeout: 5000 }) }) })