250 lines
7.3 KiB
TypeScript
250 lines
7.3 KiB
TypeScript
// @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> = {},
|
|
): 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> = {},
|
|
): 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(
|
|
<CreativeAgentWorkspace
|
|
session={createSession()}
|
|
isBusy={false}
|
|
isStreaming={false}
|
|
error={null}
|
|
eventLog={eventLog}
|
|
onBack={() => {}}
|
|
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(
|
|
<CreativeAgentWorkspace
|
|
session={createSession({
|
|
stage: 'waiting_template_confirmation',
|
|
targetBinding: null,
|
|
puzzleTemplateCatalog: [
|
|
createTemplate(),
|
|
createTemplate({
|
|
templateId: 'puzzle.travel-memory',
|
|
title: '旅行记忆拼图',
|
|
summary: '把一次出行拆成地点、风景和故事节点拼图。',
|
|
defaultLevelCount: 3,
|
|
costRange: {
|
|
minPoints: 4,
|
|
maxPoints: 16,
|
|
pricingUnit: 'point',
|
|
reason: '按旅行节点估算',
|
|
},
|
|
}),
|
|
],
|
|
})}
|
|
isBusy={false}
|
|
isStreaming={false}
|
|
error={null}
|
|
eventLog={[]}
|
|
onBack={() => {}}
|
|
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(
|
|
<CreativeAgentWorkspace
|
|
session={firstSession}
|
|
isBusy={false}
|
|
isStreaming={false}
|
|
error={null}
|
|
eventLog={[]}
|
|
onBack={() => {}}
|
|
onSubmitMessage={() => {}}
|
|
onConfirmTemplate={() => {}}
|
|
onOpenTarget={() => {}}
|
|
/>,
|
|
);
|
|
|
|
fireEvent.click(screen.getByRole('button', { name: /创意拼图/u }));
|
|
|
|
expect(screen.getByRole('dialog', { name: '确认拼图模板' })).toBeTruthy();
|
|
|
|
rerender(
|
|
<CreativeAgentWorkspace
|
|
session={secondSession}
|
|
isBusy={false}
|
|
isStreaming={false}
|
|
error={null}
|
|
eventLog={[]}
|
|
onBack={() => {}}
|
|
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(<CreativeAgentStageTimeline stage={'target_ready' as CreativeAgentStage} />);
|
|
|
|
expect(screen.getByText('素材已理解')).toBeTruthy();
|
|
expect(screen.getByText('构思已完成')).toBeTruthy();
|
|
expect(screen.getByText('草稿已生成')).toBeTruthy();
|
|
expect(screen.queryByText('正在理解素材')).toBeNull();
|
|
expect(screen.queryByText('正在构思')).toBeNull();
|
|
});
|