+ )
+}
diff --git a/frontend/src/pages/ScenarioShowPage.test.tsx b/frontend/src/pages/ScenarioShowPage.test.tsx
index a73d6cd..b7bcd74 100644
--- a/frontend/src/pages/ScenarioShowPage.test.tsx
+++ b/frontend/src/pages/ScenarioShowPage.test.tsx
@@ -1,4 +1,5 @@
import { render, screen } from '@testing-library/react'
+import userEvent from '@testing-library/user-event'
import { http, HttpResponse } from 'msw'
import { MemoryRouter, Route, Routes } from 'react-router'
import { describe, expect, it } from 'vitest'
@@ -89,11 +90,11 @@ describe('ScenarioShowPage', () => {
// Priority: assert each bucket's own priority is shown somewhere within
// its own rendered content, rather than a page-wide loose number match
// (which could coincidentally match allocation/other digits instead).
- const rentPriorityMarker = await screen.findByText((_, element) =>
- element?.textContent === 'Priority 1',
+ const rentPriorityMarker = await screen.findByText(
+ (_, element) => element?.textContent === 'Priority 1',
)
- const diningPriorityMarker = await screen.findByText((_, element) =>
- element?.textContent === 'Priority 2',
+ const diningPriorityMarker = await screen.findByText(
+ (_, element) => element?.textContent === 'Priority 2',
)
expect(rentPriorityMarker).toBeInTheDocument()
expect(diningPriorityMarker).toBeInTheDocument()
@@ -121,7 +122,9 @@ describe('ScenarioShowPage', () => {
expect(
await screen.findByText(/this scenario has no buckets yet/i),
).toBeInTheDocument()
- expect(screen.queryByText('Priority', { exact: false })).not.toBeInTheDocument()
+ expect(
+ screen.queryByText('Priority', { exact: false }),
+ ).not.toBeInTheDocument()
})
it('renders the scenario name without a description paragraph when description is null', async () => {
@@ -181,7 +184,10 @@ describe('ScenarioShowPage', () => {
HttpResponse.json({ id: 'u1', email: 'a@b.com' }),
),
http.get(`http://localhost/api/scenarios/${SCENARIO_ID}`, () =>
- HttpResponse.json({ message: 'Internal Server Error' }, { status: 500 }),
+ HttpResponse.json(
+ { message: 'Internal Server Error' },
+ { status: 500 },
+ ),
),
)
@@ -191,4 +197,65 @@ describe('ScenarioShowPage', () => {
/could not load this scenario/i,
)
})
+
+ it('shows a newly added bucket after a successful add-bucket submit, via a refetch', async () => {
+ // Stateful GET: the first call (initial mount) returns an empty scenario,
+ // every call after the POST returns the scenario WITH the new bucket. This
+ // proves the add flow re-fetches the scenario rather than relying on an
+ // optimistic insert — the new bucket can only appear via the second GET.
+ let scenarioCreated = false
+ server.use(
+ http.get('http://localhost/api/me', () =>
+ HttpResponse.json({ id: 'u1', email: 'a@b.com' }),
+ ),
+ http.get(`http://localhost/api/scenarios/${SCENARIO_ID}`, () =>
+ HttpResponse.json({
+ '@context': '/api/contexts/Scenario',
+ '@id': `/api/scenarios/${SCENARIO_ID}`,
+ '@type': 'Scenario',
+ name: 'Emergency Fund',
+ description: 'Rainy day savings',
+ buckets: scenarioCreated
+ ? [
+ {
+ '@id': '/api/buckets/22222222-2222-2222-2222-222222222222',
+ '@type': 'Bucket',
+ name: 'Rent',
+ type: 'need',
+ priority: 1,
+ sortOrder: 0,
+ allocationType: 'fixed_limit',
+ allocationValue: 10000,
+ startingAmount: 0,
+ bufferMultiplier: '0.00',
+ },
+ ]
+ : [],
+ }),
+ ),
+ http.post('http://localhost/api/buckets', () => {
+ scenarioCreated = true
+ return HttpResponse.json(
+ { '@id': '/api/buckets/22222222-2222-2222-2222-222222222222' },
+ { status: 201 },
+ )
+ }),
+ )
+
+ const user = userEvent.setup()
+ renderPage()
+
+ expect(
+ await screen.findByText(/this scenario has no buckets yet/i),
+ ).toBeInTheDocument()
+
+ await user.click(screen.getByRole('button', { name: /add bucket/i }))
+ await user.type(screen.getByLabelText(/name/i), 'Rent')
+ await user.type(screen.getByLabelText(/allocation/i), '100')
+ await user.click(
+ screen.getByRole('button', { name: /save|create|submit/i }),
+ )
+
+ expect(await screen.findByText('Rent')).toBeInTheDocument()
+ })
})
diff --git a/frontend/src/pages/ScenarioShowPage.tsx b/frontend/src/pages/ScenarioShowPage.tsx
index c5c5882..fc4d5f4 100644
--- a/frontend/src/pages/ScenarioShowPage.tsx
+++ b/frontend/src/pages/ScenarioShowPage.tsx
@@ -7,6 +7,7 @@ import { ApiError, api } from '../lib/api'
import { bucketCapacity } from '../lib/bucketCapacity'
import { formatAllocation } from '../lib/bucketDisplay'
import type { Bucket, Scenario } from '../types/api'
+import AddBucketForm from './AddBucketForm'
type LoadState =
| { status: 'loading' }
@@ -44,6 +45,9 @@ function BucketCard({ bucket }: { bucket: Bucket }) {
export default function ScenarioShowPage() {
const { id } = useParams<{ id: string }>()
const [state, setState] = useState