Files
session-notify/tests/unit/i18n.test.js
T

94 lines
3.9 KiB
JavaScript

// The dictionaries and the translator. The key sets are asserted equal because
// the translator falls back to whichever dictionary has a key: an asymmetric key
// would silently switch language instead of failing, and a key nobody reads is a
// defect this repository has removed once already.
import { test } from 'node:test'
import assert from 'node:assert/strict'
import { createTranslator, dictionaries, registerDictionaries } from '../../src/client/i18n/index.js'
import { zh } from '../../src/client/i18n/zh.js'
import { en } from '../../src/client/i18n/en.js'
test('both dictionaries carry exactly the same keys', () => {
assert.deepEqual(Object.keys(zh).sort(), Object.keys(en).sort())
assert.deepEqual(Object.keys(dictionaries).sort(), ['en', 'zh'])
})
test('every key carries copy in both languages', () => {
for (const [key, value] of Object.entries(zh)) {
assert.equal(typeof value, 'string', `${key} is not a string in zh`)
assert.notEqual(value, '', `${key} is empty in zh`)
}
for (const [key, value] of Object.entries(en)) {
assert.equal(typeof value, 'string', `${key} is not a string in en`)
assert.notEqual(value, '', `${key} is empty in en`)
}
})
test('the seat translation wins, and a key it does not know falls back to the dictionary', () => {
const locale = {
bind: (namespace) => {
assert.equal(namespace, 'session-notify')
return (key, params) => (key === 'notification.completion' ? `seat:${params?.title ?? ''}` : key)
},
getSnapshot: () => ({ active: 'zh' }),
}
const t = createTranslator(locale)
assert.equal(t('notification.completion', { title: 'X' }), 'seat:X')
assert.equal(t('body.untitled'), '未命名会话')
})
test('the active language selects the dictionary, and an unknown key renders itself', () => {
const t = createTranslator({ bind: () => undefined, getSnapshot: () => ({ active: 'zh-CN' }) })
assert.equal(t('body.untitled'), '未命名会话')
const english = createTranslator(undefined)
assert.equal(english('body.untitled'), 'Untitled conversation')
assert.equal(english('nope.not.a.key'), 'nope.not.a.key')
})
test('interpolation replaces every known placeholder and leaves unknown ones alone', () => {
const t = createTranslator({ getSnapshot: () => ({ active: 'en' }) })
assert.equal(t('body.approval', { title: 'A', tool: 'Bash' }), 'A: Bash is waiting for your approval')
assert.equal(t('body.approval', { title: 'A' }), 'A: {tool} is waiting for your approval')
assert.equal(t('body.untitled', { unused: 'x' }), 'Untitled conversation')
})
test('a locale service that throws on every call keeps the copy renderable', () => {
const t = createTranslator({
bind: () => {
throw new Error('no such namespace')
},
getSnapshot: () => {
throw new Error('no snapshot')
},
})
assert.equal(t('settings.title'), 'Session notifications')
})
test('registration prefers the typed form and falls back to one call per locale', () => {
const typed = []
const disposeTyped = registerDictionaries({ register: (ns, dict) => { typed.push([ns, dict]); return () => {} } })
assert.deepEqual(typed, [['session-notify', dictionaries]])
assert.equal(typeof disposeTyped, 'function')
const single = []
let disposed = 0
const disposeSingle = registerDictionaries({
register: (ns, id, dict) => {
if (typeof id === 'object') throw new Error('typed form unsupported')
single.push([ns, id, dict])
return () => { disposed += 1 }
},
})
assert.deepEqual(single.map(([ns, id]) => [ns, id]), [['session-notify', 'zh'], ['session-notify', 'en']])
disposeSingle()
assert.equal(disposed, 2)
})
test('a profile without a locale service is not an error', () => {
assert.equal(typeof registerDictionaries(undefined), 'function')
assert.doesNotThrow(() => registerDictionaries(undefined)())
assert.equal(createTranslator(undefined)('body.untitled'), 'Untitled conversation')
})