Files

119 lines
5.1 KiB
TypeScript

import { expect, mock, test } from 'claude-code/testing'
const NOW = new Date(2026, 9, 15, 10, 0).getTime()
async function setup($: any, on: any, opts: { reply?: string; cwd?: string } = {}) {
const cwd = { value: opts.cwd ?? '/proj-a' }
const logs: string[] = []
const prompts: string[] = []
mock.store(on)
const clock = mock.clock(on, { now: NOW })
on('command.register', () => ({ value: { name: 'x' } }) as never)
on('session.start', () => ({ cwd: opts.cwd ?? '/proj-a' }) as never)
on('session.cwd', () => ({ value: cwd.value }))
on('command.run', () => ({ text: '' }))
on('prompt.compose', () => ({ sections: [] }))
on('prompt.submit', (_: any, e: any) => e as never)
on('ui.log', (_: any, e: any) => {
logs.push(String(e.text ?? e.message ?? JSON.stringify(e)))
return { value: undefined } as never
})
on('model.complete', (_: any, e: any) => {
prompts.push(String(e.prompt))
return { value: opts.reply === undefined ? { isAnswered: true, text: '已經處理好了', usage: {} } : { isAnswered: opts.reply !== '', text: opts.reply, usage: {} } } as never
})
await $.session.start({ source: 'startup', cwd: opts.cwd ?? '/proj-a' } as never)
return { logs, prompts, clock, cwd }
}
const todo = ($: any, args: string) => $.command.run({ command: 'anima-todo', args } as never)
const activate = ($: any) => $.command.run({ command: 'sao-asuna', args: '' } as never)
test('未啟動角色時待辦被拒絕並提示先啟動', async ($, on) => {
await setup($, on)
const r = await todo($, 'list')
expect(r.text).toContain('請先啟動角色')
})
test('啟動後建立、更新與列出待辦,記錄所屬專案', async ($, on) => {
await setup($, on)
await activate($)
expect((await todo($, 'add 開發 2026-10-16 09:30 重構路由')).text).toContain('#1')
expect((await todo($, 'add 提醒 2026-10-15 11:00 喝水')).text).toContain('#2')
expect((await todo($, 'update 1 研究 2026-10-17 08:00 讀文件')).text).toContain('已更新')
const list = (await todo($, 'list')).text
expect(list).toContain('#1 [研究] 2026-10-17 08:00 讀文件')
expect(list).toContain('專案:/proj-a')
expect(list.indexOf('#2')).toBeLessThan(list.indexOf('#1'))
})
test('類型預設四種且可自訂,未知類型被拒絕', async ($, on) => {
await setup($, on)
await activate($)
for (const t of ['提醒', '開發', '研究', '維護']) {
expect((await todo($, `add ${t} 2026-10-16 09:00 x`)).text).toContain('已建立')
}
expect((await todo($, 'add 運動 2026-10-16 09:00 跑步')).text).toContain('沒有「運動」')
expect((await todo($, 'type 運動')).text).toContain('已新增')
expect((await todo($, 'add 運動 2026-10-16 09:00 跑步')).text).toContain('已建立')
})
test('參數格式錯誤時顯示用法', async ($, on) => {
await setup($, on)
await activate($)
expect((await todo($, 'add 開發 明天 重構')).text).toContain('用法')
expect((await todo($, 'add 開發 2026-02-31 09:00 x')).text).toContain('用法')
})
test('到點時由角色提醒一次,確認前不執行', async ($, on) => {
const { logs, prompts } = await setup($, on)
await activate($)
await todo($, 'add 維護 2026-10-15 09:00 清理暫存')
await $.prompt.submit({ text: '嗨', command: undefined, isMeta: false } as never)
expect(logs.length).toBe(1)
expect(logs[0]).toContain('亞絲娜 >')
expect(logs[0]).toContain('清理暫存')
expect(logs[0]).toContain('run 1')
await $.prompt.submit({ text: '再一次', command: undefined, isMeta: false } as never)
expect(logs.length).toBe(1)
expect(prompts.some(p => p.includes('清理暫存'))).toBe(false)
})
test('未到點不提醒', async ($, on) => {
const { logs } = await setup($, on)
await activate($)
await todo($, 'add 提醒 2026-10-16 09:00 明天的事')
await $.prompt.submit({ text: '嗨', command: undefined, isMeta: false } as never)
expect(logs.length).toBe(0)
})
test('確認後執行,並由角色告知結果', async ($, on) => {
const { prompts } = await setup($, on, { reply: '清好了三個暫存目錄' })
await activate($)
await todo($, 'add 維護 2026-10-15 09:00 清理暫存')
const r = await todo($, 'run 1')
expect(prompts.some(p => p.includes('清理暫存'))).toBe(true)
expect(r.text).toBe('亞絲娜 > 清好了三個暫存目錄')
expect((await todo($, 'list')).text).toContain('完成')
expect((await todo($, 'run 1')).text).toContain('已經完成')
})
test('執行失敗時保留為待辦', async ($, on) => {
await setup($, on, { reply: '' })
await activate($)
await todo($, 'add 開發 2026-10-15 09:00 重構')
expect((await todo($, 'run 1')).text).toContain('執行失敗')
expect((await todo($, 'list')).text).not.toContain('完成')
})
test('待辦全域一份:換專案仍看得到', async ($, on) => {
const { cwd } = await setup($, on, { cwd: '/proj-a' })
await activate($)
await todo($, 'add 研究 2026-10-16 09:00 讀論文')
cwd.value = '/proj-b'
await todo($, 'add 研究 2026-10-16 10:00 另一專案的事')
const list = (await todo($, 'list')).text
expect(list).toContain('專案:/proj-a')
expect(list).toContain('專案:/proj-b')
})