Some checks failed
ci / Validate workspace (push) Successful in 12m32s
landing-page-ci / Validate landing page (push) Successful in 9m41s
landing-page-deploy / Deploy landing page (push) Failing after 5m23s
github-metrics / Generate repository metrics SVG (push) Failing after 2m3s
refresh-contributors-wall / Refresh contributors wall cache bust (push) Failing after 11s
This repository contains the open-design daemon CLI source code, built and packaged at https://helix-mind.ai/cli/open-design/latest.tgz for use by the HelixMind /design slash command. Licenses: Apache-2.0 (root) + MIT (skills/*)
115 lines
3.6 KiB
TypeScript
115 lines
3.6 KiB
TypeScript
// @vitest-environment jsdom
|
|
|
|
import { cleanup, fireEvent, render, screen, within } from '@testing-library/react';
|
|
import { afterEach, describe, expect, it, vi } from 'vitest';
|
|
import { AssistantMessage } from '../../src/components/AssistantMessage';
|
|
import type { AgentEvent, ChatMessage } from '../../src/types';
|
|
|
|
function messageWithEvents(events: AgentEvent[]): ChatMessage {
|
|
return {
|
|
id: 'assistant-1',
|
|
role: 'assistant',
|
|
content: '',
|
|
events,
|
|
startedAt: 1_000,
|
|
endedAt: 3_000,
|
|
};
|
|
}
|
|
|
|
describe('AssistantMessage unfinished todo state', () => {
|
|
afterEach(() => cleanup());
|
|
|
|
it('keeps Done for a completed latest TodoWrite fixture', () => {
|
|
render(
|
|
<AssistantMessage
|
|
message={messageWithEvents([
|
|
{
|
|
kind: 'tool_use',
|
|
id: 'todo-1',
|
|
name: 'TodoWrite',
|
|
input: { todos: [{ content: 'Ship layout', status: 'completed' }] },
|
|
},
|
|
])}
|
|
streaming={false}
|
|
projectId="project-1"
|
|
isLast
|
|
/>,
|
|
);
|
|
|
|
expect(screen.getByText('Done')).toBeTruthy();
|
|
expect(screen.queryByText('Stopped with unfinished work')).toBeNull();
|
|
expect(screen.queryByRole('button', { name: 'Continue remaining tasks' })).toBeNull();
|
|
});
|
|
|
|
it('shows unfinished state and passes unfinished todos to the continue callback', () => {
|
|
const onContinue = vi.fn();
|
|
render(
|
|
<AssistantMessage
|
|
message={messageWithEvents([
|
|
{
|
|
kind: 'tool_use',
|
|
id: 'todo-1',
|
|
name: 'TodoWrite',
|
|
input: {
|
|
todos: [
|
|
{ content: 'Draft layout', status: 'completed' },
|
|
{
|
|
content: 'Build components',
|
|
status: 'in_progress',
|
|
activeForm: 'Building components',
|
|
},
|
|
{ content: 'Run QA', status: 'pending' },
|
|
],
|
|
},
|
|
},
|
|
])}
|
|
streaming={false}
|
|
projectId="project-1"
|
|
isLast
|
|
onContinueRemainingTasks={onContinue}
|
|
/>,
|
|
);
|
|
|
|
expect(screen.getByText('Stopped with unfinished work')).toBeTruthy();
|
|
expect(screen.getByText('2 task(s) remain')).toBeTruthy();
|
|
const remainingList = screen.getByText('2 task(s) remain').closest('.unfinished-todos');
|
|
expect(remainingList).not.toBeNull();
|
|
expect(within(remainingList as HTMLElement).getByText('Building components')).toBeTruthy();
|
|
expect(within(remainingList as HTMLElement).getByText('Run QA')).toBeTruthy();
|
|
|
|
fireEvent.click(screen.getByRole('button', { name: 'Continue remaining tasks' }));
|
|
|
|
expect(onContinue).toHaveBeenCalledWith([
|
|
{
|
|
content: 'Build components',
|
|
status: 'in_progress',
|
|
activeForm: 'Building components',
|
|
},
|
|
{ content: 'Run QA', status: 'pending', activeForm: undefined },
|
|
]);
|
|
});
|
|
|
|
it('hides the continue button on older assistant turns', () => {
|
|
render(
|
|
<AssistantMessage
|
|
message={messageWithEvents([
|
|
{
|
|
kind: 'tool_use',
|
|
id: 'todo-1',
|
|
name: 'TodoWrite',
|
|
input: { todos: [{ content: 'Run QA', status: 'pending' }] },
|
|
},
|
|
])}
|
|
streaming={false}
|
|
projectId="project-1"
|
|
isLast={false}
|
|
onContinueRemainingTasks={vi.fn()}
|
|
/>,
|
|
);
|
|
|
|
expect(screen.getByText('Stopped with unfinished work')).toBeTruthy();
|
|
expect(screen.getByText('1 task(s) remain')).toBeTruthy();
|
|
expect(screen.queryByRole('button', { name: 'Continue remaining tasks' })).toBeNull();
|
|
});
|
|
});
|