Merge remote-tracking branch 'origin/main'
# Conflicts: # .project-docs/20-architecture/module-map.md # src/pages/ImageCanvas/DesignConversationPane.tsx # tests/unit/image-canvas-page.test.tsx
This commit is contained in:
commit
64db6abbfb
17 files changed
+2437
-105
No files matched your search
@@ -69,6 +69,7 @@ function prepareWorkspace(overrides = {}) {
|
||||
pendingOperations: {},
|
||||
fieldDrafts: {},
|
||||
chatDraft: '',
|
||||
assistantActivities: {},
|
||||
assistantStreams: {},
|
||||
quoteBlockers: [],
|
||||
error: null,
|
||||
@@ -129,6 +130,37 @@ describe('AI Design Canvas page', () => {
|
||||
expect(screen.queryByText(/规格版本|编译器|生成策略|字段决策/)).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('does not disable the current composer for another Workspace command', () => {
|
||||
prepareWorkspace();
|
||||
useImageWorkspaceStore.setState({
|
||||
chatDraft: '当前项目的新想法',
|
||||
pendingOperations: {
|
||||
'operation-workspace-2': {
|
||||
id: 'operation-workspace-2',
|
||||
label: '发送创作想法',
|
||||
command: {
|
||||
kind: 'apply_input',
|
||||
workspaceId: 'workspace-2',
|
||||
sessionId: 'session-2',
|
||||
expectedDirectionRevision: 2,
|
||||
clientOperationId: 'operation-workspace-2',
|
||||
input: { kind: 'chat', message: '另一个项目还在处理' },
|
||||
},
|
||||
status: 'submitting',
|
||||
error: null,
|
||||
clearDraftPaths: [],
|
||||
clearChatDraft: true,
|
||||
baseRawTurnSequence: 1,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
render(<ImageCanvas />);
|
||||
|
||||
expect(screen.getByRole('textbox', { name: '告诉 AI 你想创作什么' })).toBeEnabled();
|
||||
expect(screen.getByRole('button', { name: '发送' })).toBeEnabled();
|
||||
});
|
||||
|
||||
it('places desktop production history in a full-height right rail', () => {
|
||||
setViewport(true);
|
||||
prepareWorkspace();
|
||||
@@ -194,6 +226,7 @@ describe('AI Design Canvas page', () => {
|
||||
error: null,
|
||||
clearDraftPaths: [],
|
||||
clearChatDraft: true,
|
||||
baseRawTurnSequence: 1,
|
||||
},
|
||||
},
|
||||
});
|
||||
@@ -231,6 +264,152 @@ describe('AI Design Canvas page', () => {
|
||||
expect(within(conversation).getByTestId('youth-creation-card')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('keeps each pending reply beside the user message that started it', () => {
|
||||
prepareWorkspace({ turns: [] });
|
||||
useImageWorkspaceStore.setState({
|
||||
pendingOperations: {
|
||||
'operation-chat-1': {
|
||||
id: 'operation-chat-1',
|
||||
label: '发送创作想法',
|
||||
command: {
|
||||
kind: 'apply_input',
|
||||
workspaceId: 'workspace-1',
|
||||
sessionId: 'session-1',
|
||||
expectedDirectionRevision: 4,
|
||||
clientOperationId: 'operation-chat-1',
|
||||
input: { kind: 'chat', message: '先画一只小熊' },
|
||||
},
|
||||
status: 'unknown',
|
||||
error: '结果尚未确认',
|
||||
clearDraftPaths: [],
|
||||
clearChatDraft: true,
|
||||
baseRawTurnSequence: 1,
|
||||
},
|
||||
'operation-chat-2': {
|
||||
id: 'operation-chat-2',
|
||||
label: '发送创作想法',
|
||||
command: {
|
||||
kind: 'apply_input',
|
||||
workspaceId: 'workspace-1',
|
||||
sessionId: 'session-1',
|
||||
expectedDirectionRevision: 4,
|
||||
clientOperationId: 'operation-chat-2',
|
||||
input: { kind: 'chat', message: '再补充一只小兔子' },
|
||||
},
|
||||
status: 'submitting',
|
||||
error: null,
|
||||
clearDraftPaths: [],
|
||||
clearChatDraft: true,
|
||||
baseRawTurnSequence: 1,
|
||||
},
|
||||
},
|
||||
assistantStreams: {
|
||||
'operation-chat-1': '小熊会站在月亮旁边。',
|
||||
},
|
||||
});
|
||||
|
||||
render(<ImageCanvas />);
|
||||
|
||||
const conversation = screen.getByTestId('image-workspace-conversation');
|
||||
const firstMessage = within(conversation).getByTestId('pending-design-chat-operation-chat-1');
|
||||
const firstReply = within(conversation)
|
||||
.getByTestId('streaming-design-assistant-operation-chat-1');
|
||||
const secondMessage = within(conversation).getByTestId('pending-design-chat-operation-chat-2');
|
||||
expect(firstMessage.compareDocumentPosition(firstReply) & Node.DOCUMENT_POSITION_FOLLOWING)
|
||||
.toBeTruthy();
|
||||
expect(firstReply.compareDocumentPosition(secondMessage) & Node.DOCUMENT_POSITION_FOLLOWING)
|
||||
.toBeTruthy();
|
||||
});
|
||||
|
||||
it('shows public AI activity under the submitted message and collapses it beside the final turn', async () => {
|
||||
const message = '画一只在月球踢球的熊猫';
|
||||
const { workspace } = prepareWorkspace({ turns: [] });
|
||||
useImageWorkspaceStore.setState({
|
||||
pendingOperations: {
|
||||
'operation-chat-1': {
|
||||
id: 'operation-chat-1',
|
||||
label: '发送创作想法',
|
||||
command: {
|
||||
kind: 'apply_input',
|
||||
workspaceId: 'workspace-1',
|
||||
sessionId: 'session-1',
|
||||
expectedDirectionRevision: 4,
|
||||
clientOperationId: 'operation-chat-1',
|
||||
input: { kind: 'chat', message },
|
||||
},
|
||||
status: 'submitting',
|
||||
error: null,
|
||||
clearDraftPaths: [],
|
||||
clearChatDraft: true,
|
||||
baseRawTurnSequence: 1,
|
||||
},
|
||||
},
|
||||
assistantActivities: {
|
||||
'operation-chat-1': {
|
||||
workspaceId: 'workspace-1',
|
||||
directionId: 'direction-1',
|
||||
status: 'active',
|
||||
turnId: null,
|
||||
steps: [
|
||||
{ stage: 'understanding', message: '正在听懂你刚补充的内容…' },
|
||||
{ stage: 'reviewing_context', message: '正在把它和前面的设计想法放在一起看…' },
|
||||
],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
render(<ImageCanvas />);
|
||||
|
||||
const conversation = screen.getByTestId('image-workspace-conversation');
|
||||
const pending = within(conversation).getByTestId('pending-design-chat-operation-chat-1');
|
||||
const activity = within(conversation).getByTestId('design-assistant-activity-operation-chat-1');
|
||||
expect(pending.compareDocumentPosition(activity) & Node.DOCUMENT_POSITION_FOLLOWING).toBeTruthy();
|
||||
expect(within(activity).getByRole('button', { name: /AI 处理过程/ }))
|
||||
.toHaveAttribute('aria-expanded', 'true');
|
||||
expect(within(activity).getByText('正在听懂你刚补充的内容…')).toBeInTheDocument();
|
||||
expect(within(activity).getAllByText('正在把它和前面的设计想法放在一起看…'))
|
||||
.not.toHaveLength(0);
|
||||
expect(within(activity).queryByText(/思考过程|推理内容/)).not.toBeInTheDocument();
|
||||
|
||||
act(() => {
|
||||
useImageWorkspaceStore.setState({
|
||||
workspace: {
|
||||
...workspace,
|
||||
turns: [{
|
||||
turnId: 'turn-2',
|
||||
rawTurnSequence: 2,
|
||||
userMessage: message,
|
||||
assistantMessage: '这个画面很有趣。你想让它更像漫画还是电影?',
|
||||
}],
|
||||
},
|
||||
pendingOperations: {},
|
||||
assistantActivities: {
|
||||
'operation-chat-1': {
|
||||
workspaceId: 'workspace-1',
|
||||
directionId: 'direction-1',
|
||||
status: 'completed',
|
||||
turnId: 'turn-2',
|
||||
steps: [
|
||||
{ stage: 'understanding', message: '正在听懂你刚补充的内容…' },
|
||||
{ stage: 'reviewing_context', message: '正在把它和前面的设计想法放在一起看…' },
|
||||
],
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
const completedActivity = within(conversation)
|
||||
.getByTestId('design-assistant-activity-operation-chat-1');
|
||||
await waitFor(() => {
|
||||
expect(within(completedActivity).getByRole('button', { name: /AI 处理过程/ }))
|
||||
.toHaveAttribute('aria-expanded', 'false');
|
||||
});
|
||||
expect(within(completedActivity).queryByText('正在听懂你刚补充的内容…'))
|
||||
.not.toBeInTheDocument();
|
||||
fireEvent.click(within(completedActivity).getByRole('button', { name: /AI 处理过程/ }));
|
||||
expect(within(completedActivity).getByText('正在听懂你刚补充的内容…')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('requests a Quote from the inline plan without starting a paid task', async () => {
|
||||
const { actions } = prepareWorkspace();
|
||||
const quotedWorkspace = designWorkspaceFixture({
|
||||
@@ -273,6 +452,7 @@ describe('AI Design Canvas page', () => {
|
||||
error: null,
|
||||
clearDraftPaths: [],
|
||||
clearChatDraft: true,
|
||||
baseRawTurnSequence: 1,
|
||||
},
|
||||
},
|
||||
});
|
||||
@@ -413,6 +593,7 @@ describe('AI Design Canvas page', () => {
|
||||
error: '网络已断开',
|
||||
clearDraftPaths: [],
|
||||
clearChatDraft: false,
|
||||
baseRawTurnSequence: null,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
Reference in new issue
Block a user