Files
F-r-ldraledigplannerare/frontend/src/utils/wishName.test.ts
T
jacobh2 20f45d6c7b Translate wish names in plan proposal output
Preset wish names follow the UI language in the wish editor, but the plan
proposal still rendered outcome.wishName straight from the stored
entity.name, so a proposal could show "Höstlov" with the UI in English.

The proposal now carries wish_name_message_key alongside each wish name,
and the frontend resolves it with the same helper the editor uses --
falling back to the stored literal when the key is absent or unknown.
Nothing is translated on the backend; text crosses the wire as a key.

Four surfaces carried a name and all four are covered: WindowOutcome
itself, the explanation summary's interpolated data, and two remedies --
including REMEDY_LOWER_PRIORITY_WISH, which names a *different* sibling
wish whose key can differ from the outcome's own.

The proposal response mapping is hand-written and has dropped new fields
twice before, so this is covered by an API-level test asserting the field
reaches the response body, not only a service test.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RXMhc9nT6sNz1SL4j8dznL
2026-09-14 22:51:15 +02:00

62 lines
2.7 KiB
TypeScript

import { describe, it, expect } from 'vitest'
import { resolveWishName, resolveWishNameFromData } from './wishName'
import { translate } from '@/i18n'
const t = (key: Parameters<typeof translate>[1]) => translate('sv', key)
describe('resolveWishName', () => {
it('translates through nameMessageKey when present, ignoring the stored name', () => {
// The core bug fix: the persisted `name` can be stale (saved while the
// UI was a different language) — the key, when present, is the source
// of truth for display.
const wish = { name: 'STALE_ENGLISH_NAME', nameMessageKey: 'wishEditor.seasonJul' }
expect(resolveWishName(wish, t)).toBe('Jul')
})
it('returns the literal name verbatim when nameMessageKey is null', () => {
const wish = { name: 'Resa till mormor', nameMessageKey: null }
expect(resolveWishName(wish, t)).toBe('Resa till mormor')
})
it('falls back to the literal name for a key this build does not recognise', () => {
// A stale key from an older/newer server build must never crash or
// render a raw, untranslated key string to the user.
const wish = { name: 'Sommar', nameMessageKey: 'wishEditor.thisKeyDoesNotExist' }
expect(resolveWishName(wish, t)).toBe('Sommar')
})
it('falls back to the literal name for an empty-string key', () => {
const wish = { name: 'Custom trip', nameMessageKey: '' }
expect(resolveWishName(wish, t)).toBe('Custom trip')
})
})
describe('resolveWishNameFromData', () => {
it('translates through wish_name_message_key when present, ignoring wish_name', () => {
const data = { wish_name: 'STALE_ENGLISH_NAME', wish_name_message_key: 'wishEditor.seasonJul' }
expect(resolveWishNameFromData(data, t)).toBe('Jul')
})
it('returns wish_name verbatim when wish_name_message_key is null', () => {
const data = { wish_name: 'Resa till mormor', wish_name_message_key: null }
expect(resolveWishNameFromData(data, t)).toBe('Resa till mormor')
})
it('returns wish_name verbatim when wish_name_message_key is absent', () => {
// A `data` bag from before this field existed, or one that never
// carried a wish name key at all.
const data = { wish_name: 'Sommar' }
expect(resolveWishNameFromData(data, t)).toBe('Sommar')
})
it('falls back to wish_name for a key this build does not recognise', () => {
const data = { wish_name: 'Sommar', wish_name_message_key: 'wishEditor.thisKeyDoesNotExist' }
expect(resolveWishNameFromData(data, t)).toBe('Sommar')
})
it('resolves to an empty string when wish_name is missing or not a string', () => {
expect(resolveWishNameFromData({}, t)).toBe('')
expect(resolveWishNameFromData({ wish_name: 42 }, t)).toBe('')
})
})