c489c92084
澄清卡片的自由回答输入框在 setAnswers 的 updater 里读 event.currentTarget.value。React 在事件派发结束后会把 currentTarget 置空,而 updater 要等渲染阶段才跑,读到的就是 null,抛出 "Cannot read properties of null (reading 'value')"。异常出在渲染阶段, 被 AuthenticatedClient 的 error boundary 接住,整页被换成 "客户端页面加载失败"。 手动输入、以及先选中选项再删除填充文本,走的都是这一个 onChange。 改成在事件里先取出 value 再交给 updater。新增的回归测试在 StrictMode 下渲染卡片(与 main.tsx 一致),覆盖这两条路径。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
75 lines
2.2 KiB
TypeScript
75 lines
2.2 KiB
TypeScript
// @vitest-environment jsdom
|
|
|
|
import { fireEvent, render, screen } from '@testing-library/react';
|
|
import { StrictMode } from 'react';
|
|
import { describe, expect, it } from 'vitest';
|
|
|
|
import type { AgentRuntimeUserInputRequest } from '../src/app/types';
|
|
import { AgentRuntimeUserInputCard } from '../src/features/agent-runtime/panels';
|
|
|
|
function clarificationRequest(): AgentRuntimeUserInputRequest {
|
|
return {
|
|
schemaVersion: 'agc.user_input_request.v1',
|
|
requestId: 'req-1',
|
|
agentId: 'project-supervisor',
|
|
taskId: 'task-1',
|
|
sessionId: 'session-1',
|
|
runId: 'run-1',
|
|
actionId: 'action-1',
|
|
status: 'pending',
|
|
questions: [
|
|
{
|
|
id: 'q1',
|
|
header: '第1轮·关键决定',
|
|
question: '这局游戏的重玩动力是什么?',
|
|
options: [
|
|
{ label: '分数驱动', description: '刷新纪录后重开' },
|
|
{ label: '成长驱动', description: '解锁新能力后重开' },
|
|
],
|
|
},
|
|
],
|
|
allowFreeform: true,
|
|
responseId: null,
|
|
requestedAt: 1,
|
|
updatedAt: 1,
|
|
};
|
|
}
|
|
|
|
describe('AgentRuntimeUserInputCard 澄清输入', () => {
|
|
it('手动输入自由回答不会让页面崩溃', () => {
|
|
render(
|
|
<StrictMode>
|
|
<AgentRuntimeUserInputCard
|
|
request={clarificationRequest()}
|
|
controlBusy={false}
|
|
/>
|
|
</StrictMode>,
|
|
);
|
|
|
|
const textarea = screen.getByLabelText('第1轮·关键决定 其他回答');
|
|
fireEvent.change(textarea, { target: { value: '玩家自己写的答案' } });
|
|
|
|
expect((textarea as HTMLTextAreaElement).value).toBe('玩家自己写的答案');
|
|
});
|
|
|
|
it('先选中选项再清空填充文本不会让页面崩溃', () => {
|
|
render(
|
|
<StrictMode>
|
|
<AgentRuntimeUserInputCard
|
|
request={clarificationRequest()}
|
|
controlBusy={false}
|
|
/>
|
|
</StrictMode>,
|
|
);
|
|
|
|
fireEvent.click(screen.getByText('分数驱动'));
|
|
const textarea = screen.getByLabelText(
|
|
'第1轮·关键决定 其他回答',
|
|
) as HTMLTextAreaElement;
|
|
expect(textarea.value).toBe('分数驱动');
|
|
|
|
fireEvent.change(textarea, { target: { value: '' } });
|
|
expect(textarea.value).toBe('');
|
|
});
|
|
});
|