test(ui-user-questions): 覆盖多选预选的计时与回看卡片

This commit is contained in:
yudshj committed 2026-10-27 13:47:34 +08:00
1 parent bf8d8494ba
commit 07c3c10d2c
4 files changed
+56 -12

No files matched your search

@@ -15,8 +15,8 @@
en: d152484eb41ac6b4
zh: 09388d293f9be9cb
/deepseek-ai-dsh-client-ui-user-questions/use-this-package:
en: 56368ea7909468c0
zh: b212ffa1f0fda6a5
en: f63c46f9fc2edb1f
zh: 82ad1b2e56a61a52
/deepseek-ai-dsh-client-ui-user-questions/use-this-package/answering:
en: 8900fbf9cbd72083
zh: 67a951d58a029a58
+1 -1
View File
@@ -25,7 +25,7 @@ The Web client shows an agent's question beside the chat input. Users choose opt
<a id="use-this-package"></a>
## Use this package
When the agent asks a question, the attached card temporarily occupies the composer seat: answer each question, navigate with the pager, or skip it. A single-select question’s first choice marked “Recommended” starts selected, as does every marked choice of a multi-select question; but remains an unsubmitted draft and does not pause a timed countdown. Clicking a single-select choice advances immediately. Enter on a focused option attempts to submit the batch without selecting that option; missing answers return to the first incomplete question. Enter in a text answer continues the flow, while Shift+Enter breaks a line instead (during IME composition Enter only confirms the input candidate without advancing).
When the agent asks a question, the attached card temporarily occupies the composer seat: answer each question, navigate with the pager, or skip it. When a single-select question’s first choice is marked “Recommended”, it starts selected; in a multi-select question, every marked choice starts selected. These preselections remain unsubmitted drafts and do not pause a timed countdown. Clicking a single-select choice advances immediately. Enter on a focused option attempts to submit the batch without selecting that option; missing answers return to the first incomplete question. Enter in a text answer continues the flow, while Shift+Enter breaks a line instead (during IME composition Enter only confirms the input candidate without advancing).
### Answering
@@ -25,7 +25,7 @@ Web 客户端在聊天输入框旁显示 agent(智能体)的提问。用户
<a id="use-this-package"></a>
## 使用本包
当 agent 提问时,附着式卡片会暂时占用编辑器位置:回答每个问题、用翻页器导航,或跳过它。单选题中首个带推荐标记的选项、多选题中所有带推荐标记的选项会预先选中,但仍只是未提交的草稿,不会暂停计时倒计时。点击单选选项会立即前进。聚焦选项时按 Enter 会尝试提交整组答案,不会选中该选项;若有缺失答案,则回到第一道未完成的问题。文本答案中的 Enter 会继续流程,Shift+Enter 则换行(IME 组合输入期间按 Enter 只会确认输入候选,不会前进)。
当 agent 提问时,附着式卡片会暂时占用编辑器位置:回答每个问题、用翻页器导航,或跳过它。单选题的第一个选项带推荐标记时会预先选中;多选题中所有带推荐标记的选项都会预先选中。这些预选仍只是未提交的草稿,不会暂停计时倒计时。点击单选选项会立即前进。聚焦选项时按 Enter 会尝试提交整组答案,不会选中该选项;若有缺失答案,则回到第一道未完成的问题。文本答案中的 Enter 会继续流程,Shift+Enter 则换行(IME 组合输入期间按 Enter 只会确认输入候选,不会前进)。
### 作答
@@ -246,28 +246,50 @@ describe('QuestionComposer', () => {
options: [{ label: 'Unit (Recommended)' }, { label: 'Lint' }, { label: 'Types(推荐)' }],
}])
render(<QuestionComposer matched={carrier} {...kit} />)
expect(screen.getByRole('checkbox', { name: /Unit/ }).getAttribute('aria-checked')).toBe('true')
expect(screen.getByRole('checkbox', { name: /Lint/ }).getAttribute('aria-checked')).toBe('false')
expect(screen.getByRole('checkbox', { name: /Types/ }).getAttribute('aria-checked')).toBe('true')
expect(screen.getByRole('checkbox', { name: 'Unit' }).getAttribute('aria-checked')).toBe('true')
expect(screen.getByRole('checkbox', { name: 'Lint' }).getAttribute('aria-checked')).toBe('false')
expect(screen.getByRole('checkbox', { name: 'Types' }).getAttribute('aria-checked')).toBe('true')
fireEvent.keyDown(screen.getByRole('checkbox', { name: /Lint/ }), { key: 'Enter' })
fireEvent.keyDown(screen.getByRole('checkbox', { name: 'Lint' }), { key: 'Enter' })
expect(answer).toHaveBeenCalledWith(answerBatch([
{ id: 'checks', selected: ['Unit (Recommended)', 'Types(推荐)'] },
]))
})
it('keeps several recommended multi-select defaults without pausing a timed wait', () => {
const { carrier } = wait([
{
id: 'checks', question: 'Which checks?', multiSelect: true,
options: [{ label: 'Unit (Recommended)' }, { label: 'Lint' }, { label: 'Types (Recommended)' }],
},
{ id: 'note', question: 'Anything else?' },
], Date.now() + 30_000)
try {
const view = render(<QuestionComposer matched={carrier} {...kit} />)
fireEvent.click(screen.getByLabelText('下一题'))
view.unmount()
render(<QuestionComposer matched={carrier} {...kit} />)
expect(carrier.snapshot().countdown?.running).toBe(true)
fireEvent.click(screen.getByLabelText('上一题'))
expect(screen.getByRole('checkbox', { name: 'Unit' }).getAttribute('aria-checked')).toBe('true')
expect(screen.getByRole('checkbox', { name: 'Types' }).getAttribute('aria-checked')).toBe('true')
} finally {
carrier.timeout()
}
})
it('starts only a recommended first choice of a single-select question selected', () => {
const { carrier } = wait([
{ id: 'first', question: 'First?', options: [{ label: 'A (Recommended)' }, { label: 'B (Recommended)' }] },
{ id: 'later', question: 'Later?', options: [{ label: 'C' }, { label: 'D (Recommended)' }] },
])
render(<QuestionComposer matched={carrier} {...kit} />)
expect(screen.getByRole('radio', { name: /A/ }).getAttribute('aria-checked')).toBe('true')
expect(screen.getByRole('radio', { name: /B/ }).getAttribute('aria-checked')).toBe('false')
expect(screen.getByRole('radio', { name: 'A' }).getAttribute('aria-checked')).toBe('true')
expect(screen.getByRole('radio', { name: 'B' }).getAttribute('aria-checked')).toBe('false')
fireEvent.click(screen.getByLabelText('下一题'))
expect(screen.getByRole('radio', { name: /C/ }).getAttribute('aria-checked')).toBe('false')
expect(screen.getByRole('radio', { name: /D/ }).getAttribute('aria-checked')).toBe('false')
expect(screen.getByRole('radio', { name: 'C' }).getAttribute('aria-checked')).toBe('false')
expect(screen.getByRole('radio', { name: 'D' }).getAttribute('aria-checked')).toBe('false')
})
it('treats Enter on an unselected option as submit, without selecting it', () => {
@@ -1023,6 +1045,28 @@ describe('review card', () => {
expect(screen.queryByPlaceholderText('输入你的答案')).toBeNull()
})
it('shows recorded multi-select answers without restoring recommended defaults', () => {
const questions: PendingQuestion['questions'] = [
{
id: 'checks', question: 'Which checks?', multiSelect: true,
options: [{ label: 'Unit (Recommended)' }, { label: 'Lint' }, { label: 'Types (Recommended)' }],
},
{
id: 'unrecorded', question: 'Which targets?', multiSelect: true,
options: [{ label: 'Web (Recommended)' }, { label: 'Desktop' }],
},
]
const carrier = new PendingQuestion(SID, questions, ToolCallId(REVIEWED_CALL), undefined, [{ id: 'checks', selected: ['Lint'] }])
carrier.attachSeat({ hide: vi.fn() })
render(<QuestionComposer matched={carrier} {...kit} />)
expect(screen.getByRole('checkbox', { name: 'Unit' }).getAttribute('aria-checked')).toBe('false')
expect(screen.getByRole('checkbox', { name: 'Lint' }).getAttribute('aria-checked')).toBe('true')
expect(screen.getByRole('checkbox', { name: 'Types' }).getAttribute('aria-checked')).toBe('false')
fireEvent.click(screen.getByLabelText('下一题'))
expect(screen.getByRole('checkbox', { name: 'Web' }).getAttribute('aria-checked')).toBe('false')
})
it('reads the record back instead of a draft the live card left under the same key', () => {
kit.actions.replace(PendingQuestion.keyOf(SID, REVIEWED_CALL), {
index: 2,