diff --git a/web/src/onboarding/OnboardingPage.test.tsx b/web/src/onboarding/OnboardingPage.test.tsx index a8760ce..e32f1cf 100644 --- a/web/src/onboarding/OnboardingPage.test.tsx +++ b/web/src/onboarding/OnboardingPage.test.tsx @@ -234,7 +234,7 @@ describe('onboarding step 2 (mock 1h)', () => { expect(regularNames()).toEqual(['Beef tacos', 'Pizza night']) }) - it('enables "Next: notifications" once there are 3 Regulars', async () => { + it('enables "Next: notifications" once the server has saved 3 Regulars', async () => { const server = await regularsStep([ { id: 'd1', name: 'Beef tacos' }, { id: 'd2', name: 'Pizza night' }, @@ -242,10 +242,13 @@ describe('onboarding step 2 (mock 1h)', () => { expect(next()).toBeDisabled() fireEvent.click(screen.getByRole('button', { name: 'Add Burgers' })) - await vi.waitFor(() => expect(next()).toBeEnabled()) + await screen.findByRole('heading', { level: 2, name: 'Your regulars · 3' }) + // The third chip shows at once, but Next waits for the server. + expect(next()).toBeDisabled() await vi.waitFor(() => expect(server.held).toHaveLength(1)) created(server, server.held[0]!, 'd3') await screen.findByRole('button', { name: 'Remove Burgers' }) + expect(next()).toBeEnabled() fireEvent.click(screen.getByRole('button', { name: 'Remove Pizza night' })) await vi.waitFor(() => expect(next()).toBeDisabled()) @@ -256,4 +259,37 @@ describe('onboarding step 2 (mock 1h)', () => { fireEvent.click(next()) expect(await screen.findByText('Step 3 of 3')).toBeInTheDocument() }) + + it('keeps "Next: notifications" disabled when the third add is refused', async () => { + const server = await regularsStep([ + { id: 'd1', name: 'Beef tacos' }, + { id: 'd2', name: 'Pizza night' }, + ]) + fireEvent.click(screen.getByRole('button', { name: 'Add Burgers' })) + await screen.findByRole('heading', { level: 2, name: 'Your regulars · 3' }) + expect(next()).toBeDisabled() + + await vi.waitFor(() => expect(server.held).toHaveLength(1)) + server.held[0]!.answer(errorResponse(409, 'duplicate_dinner')) + expect(await screen.findByRole('alert')).toHaveTextContent('Could not add Burgers: duplicate_dinner message') + expect(regularNames()).toEqual(['Beef tacos', 'Pizza night']) + expect(next()).toBeDisabled() + }) + + it('keeps "Next: notifications" disabled while a fourth add is unanswered', async () => { + const server = await regularsStep([ + { id: 'd1', name: 'Beef tacos' }, + { id: 'd2', name: 'Pizza night' }, + { id: 'd3', name: 'Chili' }, + ]) + expect(next()).toBeEnabled() + + fireEvent.click(screen.getByRole('button', { name: 'Add Burgers' })) + await screen.findByRole('heading', { level: 2, name: 'Your regulars · 4' }) + expect(next()).toBeDisabled() + await vi.waitFor(() => expect(server.held).toHaveLength(1)) + server.held[0]!.answer(errorResponse(409, 'duplicate_dinner')) + await screen.findByRole('alert') + expect(next()).toBeEnabled() + }) }) diff --git a/web/src/onboarding/RegularsStep.tsx b/web/src/onboarding/RegularsStep.tsx index 92d5fb5..07cc246 100644 --- a/web/src/onboarding/RegularsStep.tsx +++ b/web/src/onboarding/RegularsStep.tsx @@ -11,7 +11,8 @@ import { TextInput } from '../ui/TextInput.tsx' import { OnboardingStep } from './OnboardingStep.tsx' import styles from './Onboarding.module.css' -// MIN_REGULARS is how many Regulars the household adds before moving on. +// MIN_REGULARS is how many Regulars the server has saved before the +// household moves on. export const MIN_REGULARS = 3 // commonPicks are offered as one-tap Regulars until the household has them. @@ -41,6 +42,10 @@ export function RegularsStep() { const [draft, setDraft] = useState('') const list = regulars.data ?? [] const picks = commonPicks.filter((pick) => !list.some((regular) => sameName(regular.name, pick))) + // Next waits for every add to be answered, so a refused add's error shows + // here and an add the server takes back never counts towards the three. + const adding = list.some((regular) => regular.pending) + const saved = list.filter((regular) => !regular.pending).length function onSubmit(event: FormEvent) { event.preventDefault() @@ -57,7 +62,7 @@ export function RegularsStep() { action={