Files
Genarrative/apps/ai-game-creator-shell/tests/designReplyAnimation.test.tsx
T
k88936 594063a1e0 测试:修正 AGC 测试文件的类型错误
- 按 vitest 0.34 的参数元组口径改写 mock 泛型
- 修正 noUncheckedIndexedAccess 下的下标访问
- 补齐与当前类型脱节的测试桩字段
- 按 scripts/*.mjs 的运行时契约补局部类型声明
2026-10-03 17:41:30 +08:00

102 lines
3.4 KiB
TypeScript

// @vitest-environment jsdom
import { act, cleanup, renderHook } from '@testing-library/react';
import { afterEach, beforeEach, expect, it, vi } from 'vitest';
import type { DesignView } from '../src/app/types';
import { useDesignReplyAnimation } from '../src/view/project-development/planning/useDesignReplyAnimation';
beforeEach(() => {
vi.useFakeTimers();
});
afterEach(() => {
cleanup();
vi.useRealTimers();
});
function view(messages: DesignView['messages'], running = false): DesignView {
return {
session: {
sessionId: 's',
projectId: 'p',
currentPhase: 'concept',
approvedPhases: [],
pendingApproval: null,
pendingClarification: null,
turnIndex: 1,
lastError: null,
},
messages,
running,
canRetry: false,
};
}
it('keeps each reply progress across tool snapshots and repeated completion', () => {
const { result } = renderHook(useDesignReplyAnimation);
const history = { id: 'history', role: 'assistant', text: '历史' };
const first = { id: 't:response:0', role: 'assistant', text: '第一条回复' };
const second = { id: 't:response:1', role: 'assistant', text: '第二条回复' };
act(() => {
result.current.reset([history.id]);
result.current.receiveText(first.id, first.text);
vi.advanceTimersByTime(100);
});
expect(result.current.replies[0]!.visible).toBe('第一');
act(() =>
result.current.receiveView(
view(
[history, first, { id: 'tool', role: 'tool', text: '工具已完成' }],
true,
),
),
);
expect(result.current.replies).toHaveLength(1);
expect(result.current.replies[0]!.visible).toBe('第一');
act(() => result.current.receiveText(second.id, second.text));
const terminal = view([history, first, second]);
act(() => {
result.current.receiveView(terminal);
result.current.receiveView(terminal);
});
expect(result.current.replies.map((reply) => reply.visible)).toEqual([
'第一',
'',
]);
act(() => vi.advanceTimersByTime(500));
act(() => result.current.receiveView(terminal));
expect(result.current.replies.map((reply) => reply.visible)).toEqual([
first.text,
second.text,
]);
});
it('resets only an unpersisted retry attempt and discards failed partial output', () => {
const { result } = renderHook(useDesignReplyAnimation);
act(() => {
result.current.receiveText('t:response:0', '尝试失败');
vi.advanceTimersByTime(100);
});
expect(result.current.replies[0]!.visible).toBe('尝试');
act(() => result.current.receiveText('t:response:0', ''));
expect(result.current.replies[0]!.visible).toBe('');
const saved = { id: 't:response:0', role: 'assistant', text: '成功' };
act(() => {
result.current.receiveText(saved.id, saved.text);
result.current.receiveView(view([saved], true));
vi.advanceTimersByTime(100);
result.current.receiveText(saved.id, '');
result.current.receiveText('t:response:1', '未被接受的文本');
result.current.receiveView(view([saved]));
});
expect(result.current.replies).toEqual([
{ messageId: saved.id, target: '成功', visible: '成功', persisted: true },
]);
act(() => {
result.current.receiveText('t:response:2', '连接中断');
result.current.discardUnpersisted();
});
expect(result.current.replies).toHaveLength(1);
act(() => result.current.reset([saved.id]));
expect(result.current.replies).toEqual([]);
});