// ========= Copyright 2025-2026 @ Eigent.ai All Rights Reserved. =========
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ========= Copyright 2025-2026 @ Eigent.ai All Rights Reserved. =========
import type { VanillaChatStore } from '@/store/chatStore';
import { AgentStep, ChatTaskStatus, SessionMode } from '@/types/constants';
import { render, screen } from '@testing-library/react';
import { describe, expect, it, vi } from 'vitest';
import { groupMessagesByQuery } from '@/components/ChatBox/ProjectSection';
import { UserQueryGroup } from '@/components/ChatBox/UserQueryGroup';
vi.mock('@/store/pageTabStore', () => ({
usePageTabStore: (selector: (state: any) => unknown) =>
selector({ openFilePreview: vi.fn() }),
}));
vi.mock('@/components/ChatBox/MessageItem/TaskWorkLogAccordion', () => ({
getTaskRunDisplayStatus: () => undefined,
TaskWorkLogAccordion: ({ taskId }: { taskId: string }) => (
),
}));
vi.mock('@/components/ChatBox/MessageItem/UserMessageCard', () => ({
UserMessageCard: ({ content }: { content: string }) => {content}
,
}));
vi.mock('@/components/ChatBox/MessageItem/AgentMessageCard', () => ({
AgentMessageCard: ({ content }: { content: string }) => {content}
,
}));
vi.mock('@/components/ChatBox/MessageItem/PreparingToExecuteTasks', () => ({
PreparingToExecuteTasks: () => ,
}));
vi.mock('@/components/ChatBox/MessageItem/NoticeCard', () => ({
NoticeCard: () => ,
}));
vi.mock('@/components/ChatBox/TaskBox/TaskCard', () => ({
TaskCard: () => ,
}));
vi.mock('@/components/ChatBox/TaskBox/PlanTaskBox', () => ({
PlanTaskBox: () => ,
}));
function createStore(messages: any[]): VanillaChatStore {
const state: any = {
activeTaskId: 'run-1',
tasks: {
'run-1': {
sessionMode: SessionMode.SINGLE_AGENT,
status: ChatTaskStatus.RUNNING,
messages,
streamingDecomposeText: '',
hasWaitComfirm: false,
isPending: false,
taskInfo: [],
taskAssigning: [],
taskRunning: [],
progressValue: 0,
summaryTask: '',
cotList: [],
activeAsk: 'single_agent',
askList: [],
},
},
addTaskInfo: vi.fn(),
updateTaskInfo: vi.fn(),
saveTaskInfo: vi.fn(),
deleteTaskInfo: vi.fn(),
setActiveAskList: vi.fn(),
setActiveAsk: vi.fn(),
setIsPending: vi.fn(),
addMessages: vi.fn(),
};
return {
getState: () => state,
subscribe: () => () => undefined,
} as VanillaChatStore;
}
function renderGroups(messages: any[]) {
const store = createStore(messages);
const groups = groupMessagesByQuery(messages);
const rendered = render(
<>
{groups.map((group, index) => (
undefined}
index={index}
taskId="run-1"
/>
))}
>
);
return { ...rendered, store };
}
describe('UserQueryGroup Run work-log ownership', () => {
it('keeps exactly one work log while a structured ASK is pending and after its reply', () => {
const prompt = { id: 'user-1', role: 'user', content: 'Build a report' };
const ask = {
id: 'ask-1',
role: 'agent',
step: AgentStep.ASK,
content: 'Which region?',
interaction: {
interaction_id: 'interaction-1',
interaction_type: 'question',
run_id: 'run-1',
question: 'Which region?',
},
};
const reply = { id: 'reply-1', role: 'user', content: 'Europe' };
const pending = renderGroups([prompt, ask]);
expect(screen.getAllByTestId('task-work-log')).toHaveLength(1);
pending.unmount();
renderGroups([prompt, ask, reply]);
expect(screen.getAllByTestId('task-work-log')).toHaveLength(1);
expect(screen.queryByText('Which region?')).not.toBeInTheDocument();
expect(screen.queryByText('Europe')).not.toBeInTheDocument();
});
it('does not give a transient ordinary follow-up a second copy of the old Run log', () => {
const messages = [
{ id: 'user-1', role: 'user', content: 'Build a report' },
{
id: 'agent-1',
role: 'agent',
step: AgentStep.END,
content: 'Done',
},
{ id: 'user-2', role: 'user', content: 'Add a chart' },
];
renderGroups(messages);
expect(screen.getAllByTestId('task-work-log')).toHaveLength(1);
});
it('moves a structured choice out of the chat flow and into the work log', () => {
const messages = [
{ id: 'user-1', role: 'user', content: 'Build a report' },
{
id: 'ask-1',
role: 'agent',
step: AgentStep.ASK,
content: 'Which format?',
interaction: {
interaction_id: 'interaction-1',
interaction_type: 'choice',
run_id: 'run-1',
question: 'Which format?',
},
},
];
renderGroups(messages);
expect(screen.queryByText('Which format?')).not.toBeInTheDocument();
expect(screen.getAllByTestId('task-work-log')).toHaveLength(1);
});
});