141 lines
5.2 KiB
TypeScript
141 lines
5.2 KiB
TypeScript
import { fireEvent, render, screen, waitFor, within } from '@testing-library/react';
|
|
import { MemoryRouter } from 'react-router-dom';
|
|
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
import { CharacterScene } from '@/pages/Makelore';
|
|
import { useAuthStore } from '@/stores/auth';
|
|
import { codingWorkspaceStore } from '@/stores/coding-workspace';
|
|
import { usePartnerProfilesStore } from '@/stores/partner-profiles';
|
|
import { useProviderStore } from '@/stores/providers';
|
|
|
|
const hostApiFetchMock = vi.fn();
|
|
|
|
vi.mock('@/lib/host-api', () => ({
|
|
hostApiFetch: (...args: unknown[]) => hostApiFetchMock(...args),
|
|
ensureHostApiToken: vi.fn().mockResolvedValue('test-token'),
|
|
}));
|
|
|
|
vi.mock('@/lib/api-client', () => ({
|
|
invokeIpc: vi.fn(),
|
|
}));
|
|
|
|
describe('Makelore character scene partner profiles', () => {
|
|
beforeEach(() => {
|
|
vi.clearAllMocks();
|
|
hostApiFetchMock.mockResolvedValue({ skills: [] });
|
|
useAuthStore.setState({
|
|
initialized: true,
|
|
loading: false,
|
|
error: null,
|
|
accessToken: null,
|
|
refreshToken: null,
|
|
tokenType: null,
|
|
expiresAt: null,
|
|
user: null,
|
|
});
|
|
codingWorkspaceStore.setState({ activeProject: null, projects: [], conversations: [] });
|
|
usePartnerProfilesStore.setState({ subagents: [], projectRuntimeConfigsByProjectId: {}, profilesById: {} });
|
|
useProviderStore.setState({
|
|
statuses: [],
|
|
accounts: [],
|
|
vendors: [],
|
|
defaultAccountId: null,
|
|
loading: false,
|
|
error: null,
|
|
});
|
|
});
|
|
|
|
it('starts without partner presets and exposes manual creation', () => {
|
|
render(<MemoryRouter><CharacterScene /></MemoryRouter>);
|
|
|
|
expect(screen.getByTestId('partner-empty-state')).toBeInTheDocument();
|
|
expect(screen.getByTestId('partner-create-open')).toBeInTheDocument();
|
|
expect(screen.queryByText(/角色预设/)).not.toBeInTheDocument();
|
|
});
|
|
|
|
it('creates and saves a custom partner without calling a default-agent route', async () => {
|
|
render(<MemoryRouter><CharacterScene /></MemoryRouter>);
|
|
|
|
fireEvent.click(screen.getByTestId('partner-create-open'));
|
|
const drawer = await screen.findByRole('dialog');
|
|
fireEvent.change(within(drawer).getByTestId('partner-create-name'), { target: { value: '研究伙伴' } });
|
|
fireEvent.change(within(drawer).getByTestId('partner-create-prompt'), { target: { value: '先分析资料,再给出可验证的结论。' } });
|
|
fireEvent.click(within(drawer).getByRole('button', { name: '创建伙伴' }));
|
|
|
|
await waitFor(() => expect(usePartnerProfilesStore.getState().subagents).toHaveLength(1));
|
|
const [partner] = usePartnerProfilesStore.getState().subagents;
|
|
expect(partner).toMatchObject({
|
|
displayName: '研究伙伴',
|
|
prompt: '先分析资料,再给出可验证的结论。',
|
|
templateRoleId: 'custom',
|
|
});
|
|
expect(hostApiFetchMock.mock.calls.some(([path]) => String(path).includes('/api/coding/agents'))).toBe(false);
|
|
});
|
|
|
|
it('routes Skill management through the merged plugin module', () => {
|
|
render(<MemoryRouter><CharacterScene /></MemoryRouter>);
|
|
|
|
expect(screen.getByTestId('resource-card-plugins')).toHaveTextContent('可选与已安装');
|
|
expect(screen.queryByTestId('resource-card-skills')).not.toBeInTheDocument();
|
|
});
|
|
|
|
it('defers the automatic model refresh and applies it only after an explicit click', async () => {
|
|
const importUserModelConfig = vi.fn()
|
|
.mockResolvedValueOnce({
|
|
account: {
|
|
id: 'niancode-user-models',
|
|
vendorId: 'custom',
|
|
label: 'Makelore Models',
|
|
authMode: 'api_key',
|
|
enabled: true,
|
|
isDefault: true,
|
|
createdAt: '2026-08-17T00:00:00.000Z',
|
|
updatedAt: '2026-08-17T00:00:00.000Z',
|
|
},
|
|
importedModels: ['gpt-4.1-mini'],
|
|
runtimeRefreshRequired: true,
|
|
})
|
|
.mockResolvedValueOnce({
|
|
account: {
|
|
id: 'niancode-user-models',
|
|
vendorId: 'custom',
|
|
label: 'Makelore Models',
|
|
authMode: 'api_key',
|
|
enabled: true,
|
|
isDefault: true,
|
|
createdAt: '2026-08-17T00:00:00.000Z',
|
|
updatedAt: '2026-08-17T00:00:00.000Z',
|
|
},
|
|
importedModels: ['gpt-4.1-mini'],
|
|
runtimeRefreshRequired: true,
|
|
});
|
|
useAuthStore.setState({
|
|
accessToken: 'access-token',
|
|
getValidAccessToken: vi.fn().mockResolvedValue('access-token'),
|
|
});
|
|
useProviderStore.setState({
|
|
refreshProviderSnapshot: vi.fn().mockResolvedValue(undefined),
|
|
importUserModelConfig,
|
|
});
|
|
|
|
render(<MemoryRouter><CharacterScene /></MemoryRouter>);
|
|
|
|
fireEvent.click(screen.getByTestId('resource-card-models'));
|
|
|
|
expect(await screen.findByTestId('makelore-runtime-refresh-pending')).toBeVisible();
|
|
expect(importUserModelConfig).toHaveBeenNthCalledWith(1, 'access-token', {
|
|
runtimeRefresh: 'defer',
|
|
});
|
|
|
|
fireEvent.click(screen.getByTestId('makelore-model-config-refresh-button'));
|
|
|
|
await waitFor(() => {
|
|
expect(importUserModelConfig).toHaveBeenNthCalledWith(2, 'access-token', {
|
|
runtimeRefresh: 'apply',
|
|
});
|
|
});
|
|
await waitFor(() => {
|
|
expect(screen.queryByTestId('makelore-runtime-refresh-pending')).not.toBeInTheDocument();
|
|
});
|
|
});
|
|
});
|