feat: 階段 6 工具四待辦事項(全域待辦、類型自訂、到點提醒、確認後執行並回報)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,118 @@
|
||||
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')
|
||||
})
|
||||
Reference in New Issue
Block a user