// The copy table: what a candidate becomes on screen, and how the settings page // describes the newest delivery. Assertions use the real Chinese dictionary, so // a key rename in the dictionaries and a rename here cannot drift apart. import { test } from 'node:test' import assert from 'node:assert/strict' import { createCopy } from '../../src/client/core/copy.js' import { createTranslator } from '../../src/client/i18n/index.js' const t = createTranslator({ getSnapshot: () => ({ active: 'zh' }) }) const { copyFor, deliveryFacts, replayValue } = createCopy(t) /** One trigger candidate with every field a caller may leave empty. */ function candidate(overrides) { return { kind: 'completion', sessionId: 'session-1', title: '标题', detail: '', pendingKind: '', ...overrides } } test('a finished conversation names the trigger as the title and the conversation as the body', () => { assert.deepEqual(copyFor(candidate()), { kind: 'completion', title: '会话已完成', body: '标题 已完成这一轮回答', }) }) test('an empty title falls back to the untitled copy', () => { assert.equal(copyFor(candidate({ title: '' })).body, '未命名会话 已完成这一轮回答') }) test('an approval names the waiting tool when the client publishes one', () => { const notice = copyFor(candidate({ kind: 'approval', pendingKind: 'approval', detail: 'Bash' })) assert.deepEqual(notice, { kind: 'approval', title: '需要授权', body: '标题:工具 Bash 正在等待你的授权', }) }) test('an approval without a tool name still says what is happening', () => { const notice = copyFor(candidate({ kind: 'approval', pendingKind: 'approval', detail: '' })) assert.equal(notice.body, '标题:有工具正在等待你的授权') }) test('a question carries the question text', () => { assert.deepEqual( copyFor(candidate({ kind: 'question', pendingKind: 'question', detail: '要继续吗?' })), { kind: 'question', title: '需要回答', body: '标题 · 要继续吗?' }, ) assert.equal(copyFor(candidate({ kind: 'question', pendingKind: 'question', detail: '' })).body, '标题:正在等待你的回答') }) test('a plan review says what it is instead of repeating the plan text', () => { // A plan review arrives as `kind: 'question'` with its own discriminator, so the // discriminator has to be read first — otherwise the plan-review copy is unreachable. const review = copyFor(candidate({ kind: 'question', pendingKind: 'plan-review', detail: '计划正文…' })) assert.equal(review.kind, 'question') assert.equal(review.body, '标题:计划正在等待你确认') assert.equal(review.title, '需要回答') }) test('a delivery that never happened has no facts to show', () => { assert.equal(deliveryFacts(null, t), null) assert.equal(deliveryFacts(undefined, t), null) }) test('the newest delivery is split into time, channel and what it was', () => { assert.deepEqual( deliveryFacts({ outcome: 'system', shown: true, at: '17:52:30', title: '会话已完成' }, t), { at: '17:52:30', outcome: '系统通知(系统已确认弹出)', title: '会话已完成' }, ) assert.equal(deliveryFacts({ outcome: 'system', shown: undefined, at: '1:00:00', title: 'X' }, t).outcome, '系统通知(系统没有回报,回到窗口时补发轻弹窗)') assert.equal(deliveryFacts({ outcome: 'system', shown: false, at: '1:00:00', title: 'X' }, t).outcome, '系统通知没弹出来,回到窗口时补发轻弹窗') assert.equal(deliveryFacts({ outcome: 'system-refused-threw', at: '1:00:00', title: 'X' }, t).outcome, '系统通知被拒绝,改用了轻弹窗') assert.equal(deliveryFacts({ outcome: 'replay', at: '1:00:00', title: 'X' }, t).outcome, '应用内轻弹窗(回到窗口时补发)') assert.equal(deliveryFacts({ outcome: 'popup', at: '1:00:00', title: 'X' }, t).outcome, '应用内轻弹窗') assert.equal(deliveryFacts({ outcome: 'popup', at: '1:00:00', title: undefined }, t).title, '') }) test('the replay value is short enough for one status line', () => { assert.equal(replayValue(0, t), '没有') assert.equal(replayValue(3, t), '3 条') })