212 lines
5.6 KiB
TypeScript
212 lines
5.6 KiB
TypeScript
/* @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 { CustomWorldAgentSessionSnapshot } from '../../../packages/shared/src/contracts/customWorldAgent';
|
|
import { CustomWorldAgentWorkspace } from './CustomWorldAgentWorkspace';
|
|
|
|
const baseSession: CustomWorldAgentSessionSnapshot = {
|
|
sessionId: 'custom-world-agent-session-1',
|
|
currentTurn: 4,
|
|
anchorContent: {
|
|
worldPromise: {
|
|
hook: '一个被潮雾改写航线秩序的群岛世界。',
|
|
differentiator: '所有通路都要向未知代价借路。',
|
|
desiredExperience: '压迫、潮湿、悬疑',
|
|
},
|
|
playerFantasy: {
|
|
playerRole: '玩家是被迫返乡的旧航路继承人。',
|
|
corePursuit: '查清沉船夜背后的真相。',
|
|
fearOfLoss: '一旦失败,就会再次失去唯一还活着的旧友。',
|
|
},
|
|
themeBoundary: null,
|
|
playerEntryPoint: null,
|
|
coreConflict: null,
|
|
keyRelationships: [],
|
|
hiddenLines: null,
|
|
iconicElements: null,
|
|
},
|
|
progressPercent: 58,
|
|
lastAssistantReply: '世界和玩家视角已经有了,下一步我想把最明面的冲突钉住。',
|
|
stage: 'collecting_intent',
|
|
focusCardId: null,
|
|
creatorIntent: {},
|
|
creatorIntentReadiness: {
|
|
isReady: false,
|
|
completedKeys: ['world_hook', 'player_premise'],
|
|
missingKeys: ['theme_and_tone', 'core_conflict', 'relationship_seed', 'iconic_element'],
|
|
},
|
|
anchorPack: {},
|
|
lockState: {},
|
|
draftProfile: null,
|
|
messages: [
|
|
{
|
|
id: 'message-1',
|
|
role: 'assistant',
|
|
kind: 'chat',
|
|
text: '先告诉我你想做一个怎样的世界。',
|
|
createdAt: '2026-04-17T12:00:00.000Z',
|
|
relatedOperationId: null,
|
|
},
|
|
],
|
|
draftCards: [],
|
|
pendingClarifications: [],
|
|
suggestedActions: [],
|
|
recommendedReplies: [],
|
|
qualityFindings: [],
|
|
assetCoverage: {
|
|
roleAssets: [],
|
|
sceneAssets: [],
|
|
allRoleAssetsReady: false,
|
|
allSceneAssetsReady: false,
|
|
},
|
|
updatedAt: '2026-04-17T12:00:00.000Z',
|
|
};
|
|
|
|
beforeEach(() => {
|
|
if (!Element.prototype.scrollIntoView) {
|
|
Element.prototype.scrollIntoView = () => {};
|
|
}
|
|
});
|
|
|
|
test('workspace sends summary request from progress area', async () => {
|
|
const user = userEvent.setup();
|
|
const onSubmitMessage = vi.fn();
|
|
|
|
render(
|
|
<CustomWorldAgentWorkspace
|
|
session={baseSession}
|
|
activeOperation={null}
|
|
onBack={() => {}}
|
|
onSubmitMessage={onSubmitMessage}
|
|
onExecuteAction={() => {}}
|
|
/>,
|
|
);
|
|
|
|
await user.click(screen.getByRole('button', { name: '总结当前设定' }));
|
|
|
|
expect(onSubmitMessage).toHaveBeenCalledWith(
|
|
expect.objectContaining({
|
|
text: '请总结一下当前已经成形的世界设定。',
|
|
}),
|
|
);
|
|
});
|
|
|
|
test('workspace enables quick fill after at least two turns and submits quick fill request', async () => {
|
|
const user = userEvent.setup();
|
|
const onSubmitMessage = vi.fn();
|
|
|
|
render(
|
|
<CustomWorldAgentWorkspace
|
|
session={baseSession}
|
|
activeOperation={null}
|
|
onBack={() => {}}
|
|
onSubmitMessage={onSubmitMessage}
|
|
onExecuteAction={() => {}}
|
|
/>,
|
|
);
|
|
|
|
await user.click(screen.getByRole('button', { name: '补充剩余设定' }));
|
|
|
|
expect(onSubmitMessage).toHaveBeenCalledWith(
|
|
expect.objectContaining({
|
|
text: '请补充剩余设定。',
|
|
quickFillRequested: true,
|
|
}),
|
|
);
|
|
});
|
|
|
|
test('workspace hides quick fill before two turns', () => {
|
|
render(
|
|
<CustomWorldAgentWorkspace
|
|
session={{
|
|
...baseSession,
|
|
currentTurn: 1,
|
|
}}
|
|
activeOperation={null}
|
|
onBack={() => {}}
|
|
onSubmitMessage={() => {}}
|
|
onExecuteAction={() => {}}
|
|
/>,
|
|
);
|
|
|
|
expect(screen.queryByRole('button', { name: '补全剩余设定' })).toBeNull();
|
|
});
|
|
|
|
test('workspace exposes draft action when progress reaches 100', async () => {
|
|
const user = userEvent.setup();
|
|
const onExecuteAction = vi.fn();
|
|
|
|
render(
|
|
<CustomWorldAgentWorkspace
|
|
session={{
|
|
...baseSession,
|
|
progressPercent: 100,
|
|
stage: 'foundation_review',
|
|
}}
|
|
activeOperation={null}
|
|
onBack={() => {}}
|
|
onSubmitMessage={() => {}}
|
|
onExecuteAction={onExecuteAction}
|
|
/>,
|
|
);
|
|
|
|
await user.click(screen.getByRole('button', { name: '生成游戏设定草稿' }));
|
|
|
|
expect(onExecuteAction).toHaveBeenCalledWith({
|
|
action: 'draft_foundation',
|
|
});
|
|
});
|
|
|
|
test('workspace hides draft action before progress reaches 100', () => {
|
|
render(
|
|
<CustomWorldAgentWorkspace
|
|
session={{
|
|
...baseSession,
|
|
progressPercent: 99,
|
|
}}
|
|
activeOperation={null}
|
|
onBack={() => {}}
|
|
onSubmitMessage={() => {}}
|
|
onExecuteAction={() => {}}
|
|
/>,
|
|
);
|
|
|
|
expect(
|
|
screen.queryByRole('button', { name: '生成游戏设定草稿' }),
|
|
).toBeNull();
|
|
});
|
|
|
|
test('workspace submits recommended reply from thread', async () => {
|
|
const user = userEvent.setup();
|
|
const onSubmitMessage = vi.fn();
|
|
|
|
render(
|
|
<CustomWorldAgentWorkspace
|
|
session={{
|
|
...baseSession,
|
|
recommendedReplies: ['继续补充这个世界的核心冲突'],
|
|
}}
|
|
activeOperation={null}
|
|
onBack={() => {}}
|
|
onSubmitMessage={onSubmitMessage}
|
|
onExecuteAction={() => {}}
|
|
/>,
|
|
);
|
|
|
|
await user.click(
|
|
screen.getByRole('button', { name: '继续补充这个世界的核心冲突' }),
|
|
);
|
|
|
|
expect(onSubmitMessage).toHaveBeenCalledWith(
|
|
expect.objectContaining({
|
|
text: '继续补充这个世界的核心冲突',
|
|
quickFillRequested: false,
|
|
focusCardId: null,
|
|
selectedCardIds: [],
|
|
}),
|
|
);
|
|
});
|