diff --git a/web/src/App.test.tsx b/web/src/App.test.tsx index f73ad84..e36d16f 100644 --- a/web/src/App.test.tsx +++ b/web/src/App.test.tsx @@ -88,7 +88,7 @@ describe('App routes', () => { it('/ renders the Home screen inside the shell', async () => { mockViewport(390) - stubFetchByPath({ ...SIGNED_IN, '/api/v1/home': () => jsonResponse(200, HOME) }) + stubFetchByPath({ ...SIGNED_IN, '/api/v1/home': () => jsonResponse(200, HOME), '/api/v1/rotation-reviews': () => jsonResponse(200, { reviews: [] }) }) renderAt('/') const heading = await screen.findByRole('heading', { level: 1, name: "Tonight's quest" }) expect(screen.getByRole('main')).toContainElement(heading) @@ -273,7 +273,13 @@ describe('App bootstrap', () => { it('reports a /me failure with a retry instead of rendering the routes', async () => { mockViewport(390) - stubFetch(errorResponse(500, 'internal'), jsonResponse(200, ME), jsonResponse(200, HOUSEHOLD_SETTINGS), jsonResponse(200, HOME)) + const mes = [errorResponse(500, 'internal'), jsonResponse(200, ME)] + stubFetchByPath({ + ...SIGNED_IN, + '/api/v1/me': () => mes.shift()!, + '/api/v1/home': () => jsonResponse(200, HOME), + '/api/v1/rotation-reviews': () => jsonResponse(200, { reviews: [] }), + }) renderAt('/') expect(await screen.findByRole('alert')).toHaveTextContent('MealQuest could not load: internal message') @@ -294,7 +300,7 @@ describe('App offline bootstrap', () => { // visitOnline is an earlier online visit on this device: /me answers with a // member, then the app is closed. async function visitOnline() { - stubFetchByPath({ ...SIGNED_IN, '/api/v1/home': () => jsonResponse(200, HOME) }) + stubFetchByPath({ ...SIGNED_IN, '/api/v1/home': () => jsonResponse(200, HOME), '/api/v1/rotation-reviews': () => jsonResponse(200, { reviews: [] }) }) const { unmount } = renderAt('/') expect(await screen.findByRole('heading', { name: 'Pizza night' })).toBeInTheDocument() unmount() diff --git a/web/src/home/HomePage.test.tsx b/web/src/home/HomePage.test.tsx index 5a2f838..490c166 100644 --- a/web/src/home/HomePage.test.tsx +++ b/web/src/home/HomePage.test.tsx @@ -3,10 +3,11 @@ import { createMemoryRouter, RouterProvider, useLocation } from 'react-router' import { afterEach, describe, expect, it, vi } from 'vitest' import { ApiProviders, HOME } from '../test/ApiProviders.tsx' import { declaredStyle } from '../test/cssRules.ts' -import { errorResponse, jsonResponse, stubFetch, stubFetchByPath } from '../test/fetchStub.ts' +import { errorResponse, jsonResponse, stubFetchByPath } from '../test/fetchStub.ts' import { expectMinHitArea } from '../test/hitArea.ts' import { HOME_PATH } from './api.ts' import { HomePage } from './HomePage.tsx' +import { ROTATION_REVIEWS_PATH } from './rotation.ts' // NOTHING_YET is the home of a household with no plan, no Side quests, no // Supply run and nothing unread. @@ -29,7 +30,7 @@ function Where() { } function renderHome(body: unknown) { - stubFetchByPath({ [HOME_PATH]: () => jsonResponse(200, body) }) + stubFetchByPath({ [HOME_PATH]: () => jsonResponse(200, body), [ROTATION_REVIEWS_PATH]: () => jsonResponse(200, { reviews: [] }) }) return renderRouter() } @@ -198,7 +199,11 @@ describe('HomePage', () => { }) it('reports a failure with Retry, and no made-up content', async () => { - stubFetch(errorResponse(500, 'internal'), jsonResponse(200, HOME)) + const homes = [errorResponse(500, 'internal'), jsonResponse(200, HOME)] + stubFetchByPath({ + [HOME_PATH]: () => homes.shift()!, + [ROTATION_REVIEWS_PATH]: () => jsonResponse(200, { reviews: [] }), + }) renderRouter() expect(await screen.findByRole('alert')).toHaveTextContent("Couldn't load tonight's quest. internal message") diff --git a/web/src/home/HomePage.tsx b/web/src/home/HomePage.tsx index 81c3196..2ceead1 100644 --- a/web/src/home/HomePage.tsx +++ b/web/src/home/HomePage.tsx @@ -12,6 +12,7 @@ import { Photo } from '../ui/Photo.tsx' import { Skeleton } from '../ui/Skeleton.tsx' import { useHome, type Home, type Origin, type Tonight } from './api.ts' import styles from './HomePage.module.css' +import { RotationCheck } from './RotationCheck.tsx' import { UpdatesSheet } from './UpdatesSheet.tsx' const ORIGIN_LABEL: Record = { regular: 'Regular', side_quest: 'Side quest' } @@ -108,7 +109,8 @@ function Tiles() { // HomePage is mock 1g, Home: today's date over "Tonight's quest" and the // Recent updates bell, the Tonight card (or the way to plan the week), the // Quick dinner and Cook from what I have tiles, and the status rows, all -// from GET /api/v1/home. +// from GET /api/v1/home. Above the tiles is the Rotation check, "Keep it in +// rotation?", while a tried dinner waits for the household's answer. // // The bell carries a grape dot while there are unread updates and opens the // Recent updates sheet, which marks them seen. @@ -141,6 +143,7 @@ export function HomePage() { void home.refetch()}>Couldn't load tonight's quest. {home.error.message} )} {data !== undefined && (data.tonight === null ? : )} + {home.isPending && (