fix(design): show submitted messages immediately
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { fireEvent, render, screen, waitFor, within } from '@testing-library/react';
|
||||
import { act, fireEvent, render, screen, waitFor, within } from '@testing-library/react';
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
import { ImageWorkspaceApiError } from '@/lib/image-workspace';
|
||||
import { ImageCanvas } from '@/pages/ImageCanvas';
|
||||
@@ -126,6 +126,82 @@ describe('youth AI Design Canvas page', () => {
|
||||
expect(within(conversation).queryByText(unfinishedDraft)).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('shows a submitted message immediately and replaces it with the canonical turn', async () => {
|
||||
const message = '画一只在月球踢球的熊猫';
|
||||
const { workspace } = prepareWorkspace({ turns: [] });
|
||||
useImageWorkspaceStore.setState({
|
||||
chatDraft: message,
|
||||
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,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
render(<ImageCanvas />);
|
||||
|
||||
const conversation = screen.getByTestId('image-workspace-conversation');
|
||||
const pendingMessage = within(conversation).getByTestId('pending-design-chat-operation-chat-1');
|
||||
expect(within(pendingMessage).getByText(message)).toBeInTheDocument();
|
||||
expect(within(pendingMessage).getByText('发送中')).toBeInTheDocument();
|
||||
expect(within(conversation).queryByRole('heading', { name: '先把脑中的画面说出来' }))
|
||||
.not.toBeInTheDocument();
|
||||
expect(within(conversation).getByRole('textbox', { name: '告诉 AI 你想创作什么' }))
|
||||
.toHaveValue('');
|
||||
|
||||
act(() => {
|
||||
useImageWorkspaceStore.setState((state) => ({
|
||||
pendingOperations: {
|
||||
...state.pendingOperations,
|
||||
'operation-chat-1': {
|
||||
...state.pendingOperations['operation-chat-1']!,
|
||||
status: 'unknown',
|
||||
},
|
||||
},
|
||||
}));
|
||||
});
|
||||
expect(within(pendingMessage).getByText('正在确认')).toBeInTheDocument();
|
||||
expect(within(conversation).getByRole('textbox', { name: '告诉 AI 你想创作什么' }))
|
||||
.toHaveValue('');
|
||||
|
||||
act(() => {
|
||||
useImageWorkspaceStore.setState({
|
||||
workspace: {
|
||||
...workspace,
|
||||
turns: [{
|
||||
turnId: 'turn-2',
|
||||
rawTurnSequence: 2,
|
||||
userMessage: message,
|
||||
assistantMessage: '听起来很有趣!你希望画面更像漫画,还是更像电影?',
|
||||
}],
|
||||
},
|
||||
chatDraft: '',
|
||||
pendingOperations: {},
|
||||
});
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(within(conversation).getAllByText(message)).toHaveLength(1);
|
||||
});
|
||||
expect(within(conversation).queryByTestId('pending-design-chat-operation-chat-1'))
|
||||
.not.toBeInTheDocument();
|
||||
expect(within(conversation).queryByText('发送中')).not.toBeInTheDocument();
|
||||
expect(within(conversation).getByText(/更像漫画,还是更像电影/)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('invites a free description and does not turn a legacy decision prompt into a form', () => {
|
||||
const workspace = designWorkspaceFixture({
|
||||
form: designFormFixture({
|
||||
|
||||
@@ -223,6 +223,50 @@ describe('V2 Living Form store', () => {
|
||||
expect(useImageWorkspaceStore.getState().pendingOperations).toEqual({});
|
||||
});
|
||||
|
||||
it('publishes a pending chat operation before the server result settles', async () => {
|
||||
await loadedStore();
|
||||
const message = '画一只在月球踢球的熊猫';
|
||||
const submission = deferred<Awaited<ReturnType<typeof submitImageWorkspaceCommand>>>();
|
||||
submitCommandMock.mockReturnValueOnce(submission.promise);
|
||||
useImageWorkspaceStore.getState().setChatDraft(message);
|
||||
|
||||
const sendPromise = useImageWorkspaceStore.getState().sendChat();
|
||||
|
||||
expect(useImageWorkspaceStore.getState()).toMatchObject({
|
||||
chatDraft: message,
|
||||
pendingOperations: {
|
||||
'operation-1': {
|
||||
id: 'operation-1',
|
||||
status: 'submitting',
|
||||
clearChatDraft: true,
|
||||
command: {
|
||||
kind: 'apply_input',
|
||||
workspaceId: 'workspace-1',
|
||||
clientOperationId: 'operation-1',
|
||||
input: { kind: 'chat', message },
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const workspace = designWorkspaceFixture({
|
||||
turns: [{
|
||||
turnId: 'turn-2',
|
||||
rawTurnSequence: 2,
|
||||
userMessage: message,
|
||||
assistantMessage: '你希望画面更像漫画,还是更像电影?',
|
||||
}],
|
||||
});
|
||||
submission.resolve({ clientOperationId: 'operation-1', runId: 'run-2', workspace });
|
||||
await sendPromise;
|
||||
|
||||
expect(useImageWorkspaceStore.getState()).toMatchObject({
|
||||
workspace,
|
||||
chatDraft: '',
|
||||
pendingOperations: {},
|
||||
});
|
||||
});
|
||||
|
||||
it('settles a definitive reasoner failure without losing the chat draft', async () => {
|
||||
const source = await loadedStore();
|
||||
useImageWorkspaceStore.getState().setChatDraft('画一只会做饭的机器人');
|
||||
|
||||
Reference in New Issue
Block a user