Map the tips payload onto the shape the client declares

Tips rendered as blank cards with unresolved placeholder text in their
accessibility labels. getTips was the one entity fetch in the client with no
translation from the wire's snake_case, so every tip arrived without the key
that selects its title and body, and the lookup silently missed.

The mapper now matches its siblings in that file. A test drives a card through
all eleven message keys the tips engine emits, so a twelfth added later fails
loudly instead of rendering an empty card, and two older tests that stubbed the
tips response as an empty object rather than an empty list have been corrected.
This commit is contained in:
2026-09-17 19:24:26 +02:00
parent b5321c5e8e
commit fd39bd9748
3 changed files with 71 additions and 7 deletions
+14 -6
View File
@@ -392,7 +392,7 @@ describe('apiFetch (shared request/response handling)', () => {
})
it('omits the Authorization header when no access token is set', async () => {
fetchMock.mockResolvedValue({ ok: true, status: 200, json: async () => ({}) })
fetchMock.mockResolvedValue({ ok: true, status: 200, json: async () => [] })
await api.getTips('ws1')
@@ -403,7 +403,7 @@ describe('apiFetch (shared request/response handling)', () => {
it('attaches the Authorization header when an access token is set', async () => {
api.setAccessToken('my-token')
fetchMock.mockResolvedValue({ ok: true, status: 200, json: async () => ({}) })
fetchMock.mockResolvedValue({ ok: true, status: 200, json: async () => [] })
await api.getTips('ws1')
@@ -1322,14 +1322,22 @@ describe('endpoint functions (representative coverage)', () => {
expect(result[0]!.createdAt).toBe('')
})
it('getTips fetches tips for a workspace', async () => {
const tips = [{ id: 't1', severity: 'info', messageKey: 'k', data: {} }]
fetchMock.mockResolvedValue({ ok: true, status: 200, json: async () => tips })
it('getTips fetches tips for a workspace and maps snake_case to camelCase', async () => {
// Regression test: the backend's real wire shape is snake_case
// (`message_key`, not `messageKey`) — see tips_service.py's `Tip`
// dataclass. A bare pass-through here left `tip.messageKey` undefined
// at runtime, which rendered every tip card blank.
const rawTips = [
{ id: 't1', severity: 'info', message_key: 'tips.use_vacation_before_expiry', data: { foo: 'bar' } },
]
fetchMock.mockResolvedValue({ ok: true, status: 200, json: async () => rawTips })
const result = await api.getTips('ws1')
expect(lastRequest()).toEqual({ url: '/api/tips/ws1', method: undefined })
expect(result).toEqual(tips)
expect(result).toEqual([
{ id: 't1', severity: 'info', messageKey: 'tips.use_vacation_before_expiry', data: { foo: 'bar' } },
])
})
it('getPlannedDays omits the query string when no childId is given', async () => {