Files
orca/src/shared/native-chat-task-list.test.ts
T
Brennan BensonandMerge Sim d15a6df224 Render task checklists with update diffs and a composer progress panel (#19230)
* Render native chat task lists with incremental checklist updates

* Keep native chat task-list review plan out of repository root

* Render live Codex plan notifications through task checklists

* fix(native-chat): keep checklist test fixture within shared boundary

* Keep agent task progress in one composer panel

* Restore inline task checklists and historical update diffs

---------

Co-authored-by: Merge Sim <sim@local>
2026-09-08 23:50:32 -07:00

139 lines
4.7 KiB
TypeScript

import { describe, expect, it } from 'vitest'
import {
diffNativeChatTaskLists,
nativeChatTaskLabel,
normalizeNativeChatTaskList,
type NativeChatTask,
type NativeChatTaskList
} from './native-chat-task-list'
const task = (content: string, status: NativeChatTask['status'] = 'pending'): NativeChatTask => ({
content,
status
})
const list = (...tasks: NativeChatTask[]): NativeChatTaskList => ({ tasks })
describe('normalizeNativeChatTaskList', () => {
it('normalizes Claude tasks and uses activeForm only while in progress', () => {
const result = normalizeNativeChatTaskList('TodoWrite', {
todos: [
{ content: 'Read', status: 'completed', activeForm: 'Reading' },
{ content: 'Write', status: 'in_progress', activeForm: 'Writing' },
{ content: 'Test', status: 'pending', activeForm: 'Testing' }
]
})!
expect(result.tasks.map(nativeChatTaskLabel)).toEqual(['Read', 'Writing', 'Test'])
expect(result.tasks.map((entry) => entry.status)).toEqual([
'completed',
'in_progress',
'pending'
])
})
it('normalizes Codex JSON-string arguments and explanation', () => {
expect(
normalizeNativeChatTaskList(
'update_plan',
JSON.stringify({
explanation: 'Proceed with verification',
plan: [{ step: 'Test', status: 'in_progress' }]
})
)
).toEqual({ explanation: 'Proceed with verification', tasks: [task('Test', 'in_progress')] })
})
it('defaults unknown/missing statuses and ignores invalid entries', () => {
expect(
normalizeNativeChatTaskList(' TodoWrite ', {
todos: [
null,
[],
4,
{},
{ content: ' ' },
{ content: 7 },
{ content: ' One ', status: 'unknown', activeForm: 4 },
{ content: 'Two' }
]
})
).toEqual(list(task('One'), task('Two')))
})
it.each([undefined, null, 42, [], '{', '{}', { todos: null }, { todos: [{}] }])(
'returns null for malformed input %j',
(input) => {
expect(normalizeNativeChatTaskList('TodoWrite', input)).toBeNull()
}
)
it('keeps empty lists valid and recognizes only exact tool families', () => {
expect(normalizeNativeChatTaskList('update_plan', { plan: [] })).toEqual(list())
expect(normalizeNativeChatTaskList('TodoWrite', { todos: [] })).toEqual(list())
expect(normalizeNativeChatTaskList('mcp__server__TodoWrite', { todos: [] })).toBeNull()
expect(normalizeNativeChatTaskList('ExitPlanMode', { plan: [] })).toBeNull()
expect(normalizeNativeChatTaskList('update_plan', { todos: [] })).toBeNull()
})
})
describe('diffNativeChatTaskLists', () => {
it('reports completions and starts, omitting unchanged tasks', () => {
expect(
diffNativeChatTaskLists(
list(task('Read', 'in_progress'), task('Write'), task('Test')),
list(task('Read', 'completed'), task('Write', 'in_progress'), task('Test'))
)
).toEqual([
{ kind: 'completed', task: task('Read', 'completed') },
{ kind: 'started', task: task('Write', 'in_progress') }
])
})
it('ignores reorder-only updates and explanation changes', () => {
expect(
diffNativeChatTaskLists(list(task('A'), task('B')), {
tasks: [task('B'), task('A')],
explanation: 'Reordered'
})
).toEqual([])
})
it('matches duplicate contents by occurrence', () => {
expect(
diffNativeChatTaskLists(
list(task('A'), task('A', 'in_progress')),
list(task('A', 'completed'), task('A', 'in_progress'))
)
).toEqual([{ kind: 'completed', task: task('A', 'completed') }])
})
it('reports renamed content as an addition and removal', () => {
expect(diffNativeChatTaskLists(list(task('Old')), list(task('New')))).toEqual([
{ kind: 'added', task: task('New') },
{ kind: 'removed', task: task('Old') }
])
})
it('reports resets, reopening, and activeForm-only edits', () => {
const changed = { ...task('C', 'in_progress'), activeForm: 'Checking C' }
expect(
diffNativeChatTaskLists(
list(task('A', 'completed'), task('B', 'completed'), task('C', 'in_progress')),
list(task('A'), task('B', 'in_progress'), changed)
)
).toEqual([
{ kind: 'pending', task: task('A') },
{ kind: 'started', task: task('B', 'in_progress') },
{ kind: 'updated', task: changed }
])
})
it('reports clearing a list and removing a duplicate', () => {
expect(diffNativeChatTaskLists(list(task('A')), list())).toEqual([
{ kind: 'removed', task: task('A') }
])
expect(diffNativeChatTaskLists(list(task('A'), task('A')), list(task('A')))).toEqual([
{ kind: 'removed', task: task('A') }
])
})
})