diff --git a/src/components/big-fish-creation/BigFishAgentWorkspace.interaction.test.tsx b/src/components/big-fish-creation/BigFishAgentWorkspace.interaction.test.tsx new file mode 100644 index 00000000..c9d52eef --- /dev/null +++ b/src/components/big-fish-creation/BigFishAgentWorkspace.interaction.test.tsx @@ -0,0 +1,104 @@ +/* @vitest-environment jsdom */ + +import { render, screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import { beforeEach, expect, test, vi } from 'vitest'; + +import type { BigFishSessionSnapshotResponse } from '../../../packages/shared/src/contracts/bigFish'; +import { BigFishAgentWorkspace } from './BigFishAgentWorkspace'; + +const baseSession: BigFishSessionSnapshotResponse = { + sessionId: 'big-fish-session-1', + currentTurn: 3, + progressPercent: 64, + stage: 'collecting_anchors', + anchorPack: { + gameplayPromise: { + key: 'gameplayPromise', + label: '玩法承诺', + value: '从微光小鱼一路吞噬成长为深海巨兽', + status: 'confirmed', + }, + ecologyVisualTheme: { + key: 'ecologyVisualTheme', + label: '生态视觉主题', + value: '幽蓝珊瑚海沟', + status: 'confirmed', + }, + growthLadder: { + key: 'growthLadder', + label: '成长阶梯', + value: '', + status: 'missing', + }, + riskTempo: { + key: 'riskTempo', + label: '风险节奏', + value: '', + status: 'missing', + }, + }, + draft: null, + assetSlots: [], + assetCoverage: { + levelMainImageReadyCount: 0, + levelMotionReadyCount: 0, + backgroundReady: false, + requiredLevelCount: 8, + publishReady: false, + blockers: [], + }, + messages: [ + { + id: 'message-1', + role: 'assistant', + kind: 'chat', + text: '爽点和生态已经清楚,继续补剩余关键词。', + createdAt: '2026-04-24T10:00:00.000Z', + }, + ], + lastAssistantReply: '爽点和生态已经清楚,继续补剩余关键词。', + publishReady: false, + updatedAt: '2026-04-24T10:00:00.000Z', +}; + +beforeEach(() => { + if (!Element.prototype.scrollIntoView) { + Element.prototype.scrollIntoView = () => {}; + } +}); + +test('big fish workspace submits quick keyword fill request after two turns', async () => { + const user = userEvent.setup(); + const onSubmitMessage = vi.fn(); + + render( + {}} + onSubmitMessage={onSubmitMessage} + onExecuteAction={() => {}} + />, + ); + + await user.click(screen.getByRole('button', { name: '补充剩余关键字' })); + + expect(onSubmitMessage).toHaveBeenCalledWith( + expect.objectContaining({ + text: '请补充剩余关键字。', + }), + ); +}); + +test('big fish workspace hides keyword fill before two turns', () => { + render( + {}} + onSubmitMessage={() => {}} + onExecuteAction={() => {}} + />, + ); + + expect(screen.queryByRole('button', { name: '补充剩余关键字' })).toBeNull(); +}); diff --git a/src/components/big-fish-creation/BigFishAgentWorkspace.tsx b/src/components/big-fish-creation/BigFishAgentWorkspace.tsx index d15bf12c..bd9673ab 100644 --- a/src/components/big-fish-creation/BigFishAgentWorkspace.tsx +++ b/src/components/big-fish-creation/BigFishAgentWorkspace.tsx @@ -84,6 +84,17 @@ export function BigFishAgentWorkspace({ isStreamingReply={Boolean(streamingReplyText)} isBusy={isBusy} error={error} + quickActions={[ + { + key: 'summarize', + label: '总结当前设定', + }, + { + key: 'quickFill', + label: '补充剩余关键字', + minTurn: 2, + }, + ]} onBack={onBack} onSubmitText={(text) => { onSubmitMessage({ @@ -94,6 +105,15 @@ export function BigFishAgentWorkspace({ onPrimaryAction={() => { onExecuteAction({ action: 'big_fish_compile_draft' }); }} + onQuickAction={(action) => { + onSubmitMessage({ + clientMessageId: createCreationAgentClientMessageId('big-fish'), + text: + action.key === 'quickFill' + ? '请补充剩余关键字。' + : '请总结一下当前已经成形的大鱼吃小鱼设定。', + }); + }} /> ); } diff --git a/src/components/puzzle-agent/PuzzleAgentWorkspace.interaction.test.tsx b/src/components/puzzle-agent/PuzzleAgentWorkspace.interaction.test.tsx new file mode 100644 index 00000000..34363323 --- /dev/null +++ b/src/components/puzzle-agent/PuzzleAgentWorkspace.interaction.test.tsx @@ -0,0 +1,103 @@ +/* @vitest-environment jsdom */ + +import { render, screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import { beforeEach, expect, test, vi } from 'vitest'; + +import type { PuzzleAgentSessionSnapshot } from '../../../packages/shared/src/contracts/puzzleAgentSession'; +import { PuzzleAgentWorkspace } from './PuzzleAgentWorkspace'; + +const baseSession: PuzzleAgentSessionSnapshot = { + sessionId: 'puzzle-session-1', + currentTurn: 3, + progressPercent: 62, + stage: 'collecting_anchors', + anchorPack: { + themePromise: { + key: 'themePromise', + label: '题材承诺', + value: '雾港遗迹拼图', + status: 'confirmed', + }, + visualSubject: { + key: 'visualSubject', + label: '画面主体', + value: '潮雾中的灯塔与断桥', + status: 'confirmed', + }, + visualMood: { + key: 'visualMood', + label: '视觉气质', + value: '', + status: 'missing', + }, + compositionHooks: { + key: 'compositionHooks', + label: '拼图记忆点', + value: '', + status: 'missing', + }, + tagsAndForbidden: { + key: 'tagsAndForbidden', + label: '标签与禁忌', + value: '', + status: 'missing', + }, + }, + draft: null, + messages: [ + { + id: 'message-1', + role: 'assistant', + kind: 'chat', + text: '画面主体已经清楚,继续收束剩余关键词。', + createdAt: '2026-04-24T10:00:00.000Z', + }, + ], + lastAssistantReply: '画面主体已经清楚,继续收束剩余关键词。', + publishedProfileId: null, + suggestedActions: [], + resultPreview: null, + updatedAt: '2026-04-24T10:00:00.000Z', +}; + +beforeEach(() => { + if (!Element.prototype.scrollIntoView) { + Element.prototype.scrollIntoView = () => {}; + } +}); + +test('puzzle workspace submits quick keyword fill request after two turns', async () => { + const user = userEvent.setup(); + const onSubmitMessage = vi.fn(); + + render( + {}} + onSubmitMessage={onSubmitMessage} + onExecuteAction={() => {}} + />, + ); + + await user.click(screen.getByRole('button', { name: '补充剩余关键字' })); + + expect(onSubmitMessage).toHaveBeenCalledWith( + expect.objectContaining({ + text: '请补充剩余关键字。', + }), + ); +}); + +test('puzzle workspace hides keyword fill before two turns', () => { + render( + {}} + onSubmitMessage={() => {}} + onExecuteAction={() => {}} + />, + ); + + expect(screen.queryByRole('button', { name: '补充剩余关键字' })).toBeNull(); +}); diff --git a/src/components/puzzle-agent/PuzzleAgentWorkspace.tsx b/src/components/puzzle-agent/PuzzleAgentWorkspace.tsx index b31803f9..f7cf70ed 100644 --- a/src/components/puzzle-agent/PuzzleAgentWorkspace.tsx +++ b/src/components/puzzle-agent/PuzzleAgentWorkspace.tsx @@ -100,6 +100,17 @@ export function PuzzleAgentWorkspace({ isStreamingReply={Boolean(streamingReplyText)} isBusy={isBusy} error={error} + quickActions={[ + { + key: 'summarize', + label: '总结当前设定', + }, + { + key: 'quickFill', + label: '补充剩余关键字', + minTurn: 2, + }, + ]} onBack={onBack} onSubmitText={(text) => { onSubmitMessage({ @@ -110,6 +121,15 @@ export function PuzzleAgentWorkspace({ onPrimaryAction={() => { onExecuteAction({ action: 'compile_puzzle_draft' }); }} + onQuickAction={(action) => { + onSubmitMessage({ + clientMessageId: createCreationAgentClientMessageId('puzzle'), + text: + action.key === 'quickFill' + ? '请补充剩余关键字。' + : '请总结一下当前已经成形的拼图设定。', + }); + }} /> ); }