mirror of
https://github.com/eigent-ai/eigent.git
synced 2026-07-22 15:43:40 +00:00
284 lines
7.8 KiB
TypeScript
284 lines
7.8 KiB
TypeScript
// ========= 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 Workspace from '@/components/Workspace';
|
|
import { createSyncedProjectInSpace } from '@/lib/spaceProject';
|
|
import { fireEvent, render, screen, waitFor } from '@testing-library/react';
|
|
import { MemoryRouter } from 'react-router-dom';
|
|
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
|
|
const mocks = vi.hoisted(() => {
|
|
const oldSetAttaches = vi.fn();
|
|
const newSetAttaches = vi.fn();
|
|
const newStartTask = vi.fn().mockResolvedValue(undefined);
|
|
const newChatState = {
|
|
activeTaskId: 'new-task',
|
|
tasks: {
|
|
'new-task': {
|
|
attaches: [],
|
|
},
|
|
},
|
|
setHasMessages: vi.fn(),
|
|
setAttaches: newSetAttaches,
|
|
startTask: newStartTask,
|
|
setHasWaitComfirm: vi.fn(),
|
|
};
|
|
const oldChatState = {
|
|
activeTaskId: 'old-task',
|
|
tasks: {
|
|
'old-task': {
|
|
attaches: [{ fileName: 'old.txt', filePath: '/old.txt' }],
|
|
messages: [],
|
|
hasMessages: false,
|
|
status: 'pending',
|
|
taskAssigning: [],
|
|
},
|
|
},
|
|
setAttaches: oldSetAttaches,
|
|
};
|
|
const projectState = {
|
|
activeProjectId: 'old-project',
|
|
projects: {
|
|
'old-project': {
|
|
id: 'old-project',
|
|
metadata: {},
|
|
mode: 'workforce',
|
|
},
|
|
},
|
|
navLeadByProjectId: {},
|
|
isEmptyProject: vi.fn(() => false),
|
|
setActiveProject: vi.fn(),
|
|
getActiveChatStore: vi.fn(() => ({
|
|
getState: () => newChatState,
|
|
})),
|
|
};
|
|
const spaceState = {
|
|
activeSpaceId: 'space-1',
|
|
spaces: {
|
|
'space-1': {
|
|
id: 'space-1',
|
|
sourceType: 'blank',
|
|
status: 'active',
|
|
},
|
|
},
|
|
projectsBySpaceId: {},
|
|
getProjectMeta: vi.fn(() => null),
|
|
setActiveSpace: vi.fn(),
|
|
};
|
|
const pageState = {
|
|
activeWorkspaceTab: 'workforce',
|
|
workspaceChatFocusRequestId: 0,
|
|
customAgentFolderPathByProjectId: {},
|
|
setActiveWorkspaceTab: vi.fn(),
|
|
};
|
|
|
|
return {
|
|
newChatState,
|
|
newStartTask,
|
|
newSetAttaches,
|
|
oldChatState,
|
|
oldSetAttaches,
|
|
pageState,
|
|
projectState,
|
|
spaceState,
|
|
};
|
|
});
|
|
|
|
vi.mock('@/hooks/useChatStoreAdapter', () => ({
|
|
default: () => ({
|
|
chatStore: mocks.oldChatState,
|
|
projectStore: mocks.projectState,
|
|
}),
|
|
}));
|
|
|
|
vi.mock('@/hooks/useModelConfigCheck', () => ({
|
|
useModelConfigCheck: () => ({ hasModel: true }),
|
|
}));
|
|
|
|
vi.mock('@/host', () => ({
|
|
useHost: () => ({ electronAPI: {} }),
|
|
}));
|
|
|
|
vi.mock('@/store/authStore', () => ({
|
|
getAuthStore: () => ({
|
|
language: 'en',
|
|
setLanguage: vi.fn(),
|
|
}),
|
|
useAuthStore: () => ({
|
|
modelType: 'local',
|
|
setWorkerList: vi.fn(),
|
|
}),
|
|
useWorkerList: () => [],
|
|
}));
|
|
|
|
vi.mock('@/store/pageTabStore', () => {
|
|
const usePageTabStore = Object.assign(
|
|
(selector: (state: typeof mocks.pageState) => unknown) =>
|
|
selector(mocks.pageState),
|
|
{ getState: () => mocks.pageState }
|
|
);
|
|
return { usePageTabStore };
|
|
});
|
|
|
|
vi.mock('@/store/projectRuntimeStore', () => {
|
|
const useProjectRuntimeStore = Object.assign(
|
|
(selector: (state: typeof mocks.projectState) => unknown) =>
|
|
selector(mocks.projectState),
|
|
{ getState: () => mocks.projectState }
|
|
);
|
|
return { useProjectRuntimeStore };
|
|
});
|
|
|
|
vi.mock('@/store/spaceStore', async (importOriginal) => {
|
|
const actual = await importOriginal<typeof import('@/store/spaceStore')>();
|
|
const useSpaceStore = Object.assign(
|
|
(selector: (state: typeof mocks.spaceState) => unknown) =>
|
|
selector(mocks.spaceState),
|
|
{ getState: () => mocks.spaceState }
|
|
);
|
|
return {
|
|
...actual,
|
|
useSpaceStore,
|
|
};
|
|
});
|
|
|
|
vi.mock('@/lib/spaceProject', () => ({
|
|
createSyncedProjectInSpace: vi.fn(),
|
|
}));
|
|
|
|
vi.mock('@/components/ChatBox/BottomBox', () => ({
|
|
default: ({ inputProps }: { inputProps: any }) => (
|
|
<div>
|
|
<input
|
|
aria-label="workspace-message"
|
|
value={inputProps.value}
|
|
onChange={(event) => inputProps.onChange(event.target.value)}
|
|
/>
|
|
<button
|
|
type="button"
|
|
onClick={() =>
|
|
inputProps.onFilesChange([
|
|
{ fileName: 'draft.txt', filePath: '/draft.txt' },
|
|
])
|
|
}
|
|
>
|
|
Attach draft
|
|
</button>
|
|
<button type="button" onClick={inputProps.onSend}>
|
|
Send
|
|
</button>
|
|
</div>
|
|
),
|
|
}));
|
|
|
|
vi.mock('@/components/AddWorker', () => ({
|
|
AddWorker: () => null,
|
|
}));
|
|
vi.mock('@/components/Workspace/SingleAgentList', () => ({
|
|
SingleAgentList: () => null,
|
|
}));
|
|
vi.mock('@/components/Workspace/WorkforceAgentList', () => ({
|
|
WorkforceAgentList: () => null,
|
|
}));
|
|
vi.mock('@/components/Workspace/WorkspaceAllSessions', () => ({
|
|
WorkspaceAllSessions: () => null,
|
|
}));
|
|
vi.mock('@/components/Workspace/WorkspaceCoworkPanel', () => ({
|
|
WorkspaceCoworkPanel: () => null,
|
|
}));
|
|
vi.mock('@/components/Workspace/WorkspaceExamplePrompts', () => ({
|
|
WorkspaceExamplePrompts: () => null,
|
|
}));
|
|
vi.mock('@/components/Workspace/WorkspaceInstructionMd', () => ({
|
|
WorkspaceInstructionMd: () => null,
|
|
}));
|
|
vi.mock('@/components/Workspace/WorkspaceProjectPicker', () => ({
|
|
WorkspaceProjectPicker: () => null,
|
|
}));
|
|
vi.mock('@/components/Workspace/WorkspaceRecentSessions', () => ({
|
|
WorkspaceRecentSessions: () => null,
|
|
}));
|
|
|
|
const renderWorkspace = () =>
|
|
render(
|
|
<MemoryRouter>
|
|
<Workspace />
|
|
</MemoryRouter>
|
|
);
|
|
|
|
describe('Workspace project creation', () => {
|
|
beforeEach(() => {
|
|
vi.clearAllMocks();
|
|
vi.mocked(createSyncedProjectInSpace).mockResolvedValue({
|
|
projectId: 'new-project',
|
|
spaceId: 'space-1',
|
|
});
|
|
mocks.newStartTask.mockResolvedValue(undefined);
|
|
});
|
|
|
|
it('creates a fresh project and sends only Workspace draft attachments', async () => {
|
|
renderWorkspace();
|
|
|
|
fireEvent.change(screen.getByLabelText('workspace-message'), {
|
|
target: { value: 'Start fresh work' },
|
|
});
|
|
fireEvent.click(screen.getByText('Attach draft'));
|
|
fireEvent.click(screen.getByText('Send'));
|
|
|
|
await waitFor(() => {
|
|
expect(createSyncedProjectInSpace).toHaveBeenCalledTimes(1);
|
|
});
|
|
expect(createSyncedProjectInSpace).toHaveBeenCalledWith(
|
|
expect.objectContaining({
|
|
spaceId: 'space-1',
|
|
name: 'Start fresh work',
|
|
})
|
|
);
|
|
expect(mocks.newStartTask).toHaveBeenCalledWith(
|
|
'new-task',
|
|
undefined,
|
|
undefined,
|
|
undefined,
|
|
'Start fresh work',
|
|
[{ fileName: 'draft.txt', filePath: '/draft.txt' }],
|
|
undefined,
|
|
'new-project',
|
|
'single-agent'
|
|
);
|
|
expect(mocks.oldSetAttaches).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it('guards against duplicate submissions while project creation is pending', async () => {
|
|
let resolveCreation:
|
|
| ((value: { projectId: string; spaceId: string }) => void)
|
|
| undefined;
|
|
vi.mocked(createSyncedProjectInSpace).mockImplementation(
|
|
() =>
|
|
new Promise((resolve) => {
|
|
resolveCreation = resolve;
|
|
})
|
|
);
|
|
renderWorkspace();
|
|
|
|
fireEvent.change(screen.getByLabelText('workspace-message'), {
|
|
target: { value: 'Only once' },
|
|
});
|
|
fireEvent.click(screen.getByText('Send'));
|
|
fireEvent.click(screen.getByText('Send'));
|
|
|
|
expect(createSyncedProjectInSpace).toHaveBeenCalledTimes(1);
|
|
resolveCreation?.({ projectId: 'new-project', spaceId: 'space-1' });
|
|
await waitFor(() => expect(mocks.newStartTask).toHaveBeenCalledTimes(1));
|
|
});
|
|
});
|