Files
makelore/tests/unit/makelore-character-scene.test.tsx

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();
});
});
});