// @vitest-environment jsdom import { fireEvent, render, screen } from '@testing-library/react'; import { expect, test, vi } from 'vitest'; import type { CreativeAgentSessionSnapshot, CreativeAgentSseEvent, CreativeAgentStage, } from '../../../packages/shared/src/contracts/creativeAgent'; import type { PuzzleCreativeTemplateProtocol } from '../../../packages/shared/src/contracts/puzzleCreativeTemplate'; import { CreativeAgentStageTimeline } from './CreativeAgentStageTimeline'; import { resolveCreativeAgentTargetSelectionStage } from './creativeAgentViewModel'; import { CreativeAgentWorkspace } from './CreativeAgentWorkspace'; function createTemplate( overrides: Partial = {}, ): PuzzleCreativeTemplateProtocol { return { templateId: 'puzzle.default-creative', title: '创意拼图', summary: '把图文灵感做成拼图。', previewImageSrc: null, supportedLevelMode: 'single_or_multi', minLevelCount: 1, maxLevelCount: 6, defaultLevelCount: 1, costRange: { minPoints: 2, maxPoints: 12, pricingUnit: 'point', reason: '按关卡数估算', }, requiredDraftFields: ['workTitle'], imagePolicy: { allowUploadedImageDirectly: true, allowGeneratedImages: true, allowPerLevelReferenceImage: true, defaultCandidateCountPerLevel: 1, }, ...overrides, }; } function createSession( overrides: Partial = {}, ): CreativeAgentSessionSnapshot { return { sessionId: 'creative-session-1', stage: 'target_ready', inputSummary: { text: '做一个生日拼图', entryContext: 'creation_home', images: [], materialSummary: '做一个生日拼图', unsupportedCapabilities: [ { playType: 'rpg', title: 'RPG', status: 'unsupported', reason: 'Phase 1 暂不开放', }, ], }, messages: [ { id: 'assistant-1', role: 'assistant', kind: 'chat', text: '拼图草稿已准备好。', createdAt: '2026-05-05T10:00:00.000Z', }, ], puzzleTemplateCatalog: [], puzzleTemplateSelection: null, puzzleImageGenerationPlan: null, targetBinding: { playType: 'puzzle', targetSessionId: 'puzzle-session-1', targetStage: 'puzzle-result', resultProfileId: 'puzzle-profile-1', }, updatedAt: '2026-05-05T10:00:00.000Z', ...overrides, }; } test('target ready session exposes the puzzle result entry action', () => { const onOpenTarget = vi.fn(); const eventLog: CreativeAgentSseEvent[] = [ { event: 'puzzle_cost_range', data: { sessionId: 'creative-session-1', costRange: { minPoints: 2, maxPoints: 12, pricingUnit: 'point', reason: '按关卡数估算', }, }, }, ]; render( {}} onSubmitMessage={() => {}} onConfirmTemplate={() => {}} onCancelTemplate={() => {}} onOpenTarget={onOpenTarget} />, ); expect(screen.getByText('拼图草稿已就绪')).toBeTruthy(); expect(screen.getByText('可以进入结果页继续编辑')).toBeTruthy(); expect(screen.getByText('预计 2-12 泥点')).toBeTruthy(); fireEvent.click(screen.getByRole('button', { name: '打开草稿' })); expect(onOpenTarget).toHaveBeenCalledTimes(1); }); test('waiting confirmation shows template catalog before template config dialog', () => { const onConfirmTemplate = vi.fn(); render( {}} onSubmitMessage={() => {}} onConfirmTemplate={onConfirmTemplate} onOpenTarget={() => {}} />, ); expect(screen.getByRole('button', { name: /创意拼图/u })).toBeTruthy(); expect(screen.getByRole('button', { name: /旅行记忆拼图/u })).toBeTruthy(); expect(screen.queryByRole('dialog', { name: '确认拼图模板' })).toBeNull(); fireEvent.click(screen.getByRole('button', { name: /旅行记忆拼图/u })); expect(screen.getByRole('dialog', { name: '确认拼图模板' })).toBeTruthy(); expect(screen.getByText('预计 4 到 16 泥点')).toBeTruthy(); fireEvent.click(screen.getByRole('button', { name: /确认/u })); expect(onConfirmTemplate).toHaveBeenCalledWith( expect.objectContaining({ templateId: 'puzzle.travel-memory', selectedLevelMode: 'multi_level', plannedLevelCount: 3, }), ); }); test('switching creative session clears pending template config dialog', () => { const firstSession = createSession({ sessionId: 'creative-session-first', stage: 'waiting_template_confirmation', targetBinding: null, puzzleTemplateCatalog: [createTemplate()], }); const secondSession = createSession({ sessionId: 'creative-session-second', stage: 'waiting_template_confirmation', targetBinding: null, puzzleTemplateCatalog: [], }); const { rerender } = render( {}} onSubmitMessage={() => {}} onConfirmTemplate={() => {}} onOpenTarget={() => {}} />, ); fireEvent.click(screen.getByRole('button', { name: /创意拼图/u })); expect(screen.getByRole('dialog', { name: '确认拼图模板' })).toBeTruthy(); rerender( {}} onSubmitMessage={() => {}} onConfirmTemplate={() => {}} onOpenTarget={() => {}} />, ); expect(screen.queryByRole('dialog', { name: '确认拼图模板' })).toBeNull(); }); test('target ready puzzle result binding resolves to puzzle-result stage', () => { expect(resolveCreativeAgentTargetSelectionStage('puzzle-result')).toBe( 'puzzle-result', ); expect( resolveCreativeAgentTargetSelectionStage('puzzle-agent-workspace'), ).toBe('puzzle-agent-workspace'); }); test('target ready timeline renders completed labels instead of active labels', () => { render(); expect(screen.getByText('素材已理解')).toBeTruthy(); expect(screen.getByText('构思已完成')).toBeTruthy(); expect(screen.getByText('草稿已生成')).toBeTruthy(); expect(screen.queryByText('正在理解素材')).toBeNull(); expect(screen.queryByText('正在构思')).toBeNull(); });