Files
Genarrative/src/components/custom-world-agent/CustomWorldAgentWorkspace.interaction.test.tsx

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: [],
}),
);
});