Keep Push closed over a cached plan; bound kept week mutations
A plan_changed reread answered from the service worker's cache left the old plan shown with Push and Retry open. Push now needs a plan read from the server (Plan.cached, set from cacheFallbackHeader), and the push failure's Retry reads the plan again while the plan shown is cached. Settled swaps and pushes were kept for every week ever touched. When a week is shown, the settled ones of every other week with none pending are now removed; pending ones still survive leaving the week and coming back. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -5,7 +5,7 @@ import { afterEach, describe, expect, it, vi } from 'vitest'
|
||||
import { ApiProvider, createApiClient, createQueryClient, createVerifiedSession } from '../api/index.ts'
|
||||
import { createNetworkStatus } from '../pwa/networkStatus.ts'
|
||||
import { errorResponse, fakeLocation, jsonResponse, ME } from '../test/fetchStub.ts'
|
||||
import { planKey, usePlan, useSwapDay, type Plan } from './api.ts'
|
||||
import { planKey, usePlan, usePushToCalendar, useSwapDay, type Plan } from './api.ts'
|
||||
|
||||
const WEEK = '2026-10-05'
|
||||
const PLAN = `/api/v1/plans/${WEEK}`
|
||||
@@ -203,3 +203,70 @@ describe('useSwapDay', () => {
|
||||
await act(async () => friday.send(jsonResponse(200, planBody())))
|
||||
})
|
||||
})
|
||||
|
||||
describe('week mutations', () => {
|
||||
const NEXT = '2026-10-12'
|
||||
const LATER = '2026-10-19'
|
||||
|
||||
// weekMutations lists the swaps and pushes the mutation cache holds for
|
||||
// the week starting on weekStart, with their status.
|
||||
function weekMutations(queryClient: QueryClient, weekStart: string) {
|
||||
return queryClient
|
||||
.getMutationCache()
|
||||
.findAll({ mutationKey: ['plans', weekStart] })
|
||||
.map((mutation) => `${String(mutation.options.mutationKey![2])} ${mutation.state.status}`)
|
||||
}
|
||||
|
||||
it('keeps settled swaps and pushes while their week is shown, and leaves them behind once it is not, unless one is pending', async () => {
|
||||
const nextPush = deferred()
|
||||
stubServer({
|
||||
[`GET ${PLAN}`]: () => jsonResponse(200, planBody()),
|
||||
[`PUT ${FRIDAY}`]: () => jsonResponse(200, planBody({ 4: { id: 'q1', name: 'Lemon orzo', is_new: true } })),
|
||||
[`POST ${PLAN}/push-calendar`]: () => errorResponse(500, 'internal'),
|
||||
[`GET /api/v1/plans/${NEXT}`]: () => errorResponse(404, 'not_found'),
|
||||
[`POST /api/v1/plans/${NEXT}/push-calendar`]: () => nextPush.reply,
|
||||
[`GET /api/v1/plans/${LATER}`]: () => errorResponse(404, 'not_found'),
|
||||
})
|
||||
const api = createApiClient(fakeLocation('/plan'), createNetworkStatus(window), createVerifiedSession(localStorage))
|
||||
await api.loadMe()
|
||||
const queryClient = createQueryClient()
|
||||
const wrapper = ({ children }: { children: ReactNode }) => (
|
||||
<ApiProvider client={api}>
|
||||
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
|
||||
</ApiProvider>
|
||||
)
|
||||
const hook = renderHook(
|
||||
({ week }) => ({ plan: usePlan(week), swap: useSwapDay(week), push: usePushToCalendar(week) }),
|
||||
{ wrapper, initialProps: { week: WEEK } },
|
||||
)
|
||||
await waitFor(() => expect(hook.result.current.plan.isSuccess).toBe(true))
|
||||
|
||||
// A swap and a push of the week settle: both stay while it is shown,
|
||||
// the push's failure speaking for the week.
|
||||
act(() => hook.result.current.swap.mutate(ORZO))
|
||||
await waitFor(() => expect(hook.result.current.swap.pending).toBe(false))
|
||||
act(() => hook.result.current.push.mutate())
|
||||
await waitFor(() => expect(hook.result.current.push.error).not.toBeNull())
|
||||
expect(weekMutations(queryClient, WEEK)).toEqual(['swap success', 'push error'])
|
||||
|
||||
// Another week shown leaves them behind; a push sent there stays
|
||||
// pending across showing a third week.
|
||||
hook.rerender({ week: NEXT })
|
||||
await waitFor(() => expect(hook.result.current.plan.isSuccess).toBe(true))
|
||||
expect(weekMutations(queryClient, WEEK)).toEqual([])
|
||||
act(() => hook.result.current.push.mutate())
|
||||
hook.rerender({ week: LATER })
|
||||
await waitFor(() => expect(hook.result.current.plan.isSuccess).toBe(true))
|
||||
expect(weekMutations(queryClient, NEXT)).toEqual(['push pending'])
|
||||
hook.rerender({ week: NEXT })
|
||||
expect(hook.result.current.push.pending).toBe(true)
|
||||
|
||||
// Once it settles, it stays while its week is shown and is left behind
|
||||
// once another is.
|
||||
await act(async () => nextPush.send(errorResponse(500, 'internal')))
|
||||
await waitFor(() => expect(hook.result.current.push.error).not.toBeNull())
|
||||
expect(weekMutations(queryClient, NEXT)).toEqual(['push error'])
|
||||
hook.rerender({ week: LATER })
|
||||
expect(weekMutations(queryClient, NEXT)).toEqual([])
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user