20f45d6c7b
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
62 lines
2.7 KiB
TypeScript
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('')
|
|
})
|
|
})
|