feat: remove Code project prompt and Skills controls

This commit is contained in:
鲨鱼辣椒 committed 2026-10-14 10:44:54 +08:00
1 parent 350f7d740b
commit 140f6be8db
6 files changed
+240 -37

No files matched your search

@@ -0,0 +1,148 @@
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 { ProjectConfiguration } from '@/pages/ProjectConfiguration';
import type { ProviderAccount, ProviderVendorInfo } from '@/lib/providers';
import type { CodingProjectAgent, CodingProjectConfig, CodingProjectSummary } from '@/types/coding-project';
const codingState = vi.hoisted(() => ({
activeProject: null as CodingProjectSummary | null,
load: vi.fn().mockResolvedValue(undefined),
removeProject: vi.fn(),
}));
const configState = vi.hoisted(() => ({
configsByProjectId: {} as Record<string, CodingProjectConfig>,
knowledgeByProjectId: {} as Record<string, string[]>,
load: vi.fn().mockResolvedValue(undefined),
save: vi.fn<(projectId: string, config: CodingProjectConfig) => Promise<void>>().mockResolvedValue(undefined),
uploadKnowledge: vi.fn(),
remove: vi.fn(),
}));
const providerState = vi.hoisted(() => ({
accounts: [] as ProviderAccount[],
vendors: [] as ProviderVendorInfo[],
refreshProviderSnapshot: vi.fn().mockResolvedValue(undefined),
}));
const getCodingSkills = vi.hoisted(() => vi.fn().mockResolvedValue({ skills: [] }));
vi.mock('@/stores/coding-workspace', () => ({
useCodingWorkspaceStore: (selector: (state: typeof codingState) => unknown) => selector(codingState),
}));
vi.mock('@/stores/project-config', () => ({
useProjectConfigStore: (selector: (state: typeof configState) => unknown) => selector(configState),
}));
vi.mock('@/stores/providers', () => ({
useProviderStore: (selector: (state: typeof providerState) => unknown) => selector(providerState),
}));
vi.mock('@/lib/coding-product-tools', () => ({ getCodingSkills }));
vi.mock('@/pages/Plugins', () => ({ Plugins: () => null }));
vi.mock('@/components/works/ProjectPublishAction', () => ({ ProjectPublishAction: () => null }));
const now = '2026-10-14T00:00:00.000Z';
const project: CodingProjectSummary = {
id: 'project-config-controls',
name: 'Local project',
createdAt: now,
updatedAt: now,
lastOpenedAt: now,
};
const defaultAgent: CodingProjectAgent = {
id: 'default-agent',
avatarId: 'default',
roleName: '伙伴',
name: 'Builder',
builtIn: false,
enabled: true,
skillIds: ['project-skill', 'unknown-legacy-skill'],
responsibility: { mission: 'Build', owns: [], boundaries: [], collaborators: [], principles: [] },
prompt: ' 原有项目提示词\n保留换行与空格。 ',
archivedAt: null,
pinned: true,
model: { accountId: 'account-1', modelId: 'model-default', thinkingLevel: 'off' },
modelResolution: 'resolved',
createdAt: now,
updatedAt: now,
};
const historicalAgent: CodingProjectAgent = {
...defaultAgent,
id: 'historical-agent',
name: 'Historical builder',
prompt: '历史会话专用提示词\n不要替换为默认项目提示词。',
skillIds: ['historical-only-skill'],
model: { accountId: 'account-1', modelId: 'model-history', thinkingLevel: 'high' },
archivedAt: '2026-10-13T00:00:00.000Z',
pinned: false,
};
const config: CodingProjectConfig = {
schemaVersion: 2,
projectType: 'custom',
initialized: true,
defaultAgentId: defaultAgent.id,
agents: [defaultAgent, historicalAgent],
knowledgeDirectory: 'knowledge',
createdAt: now,
updatedAt: now,
};
describe('ProjectConfiguration model-only controls', () => {
beforeEach(() => {
vi.clearAllMocks();
codingState.activeProject = project;
configState.configsByProjectId = { [project.id]: structuredClone(config) };
providerState.accounts = [{
id: 'account-1',
vendorId: 'custom',
label: 'Work account',
authMode: 'api_key',
model: 'model-default',
fallbackModels: ['model-history', 'model-new'],
enabled: true,
isDefault: true,
createdAt: now,
updatedAt: now,
}];
});
it.each([
{ route: '/project-config', heading: '默认编程配置', target: defaultAgent },
{ route: '/project-config?agent=historical-agent', heading: '历史会话编程配置', target: historicalAgent },
])('changes only the selected Agent model at $route and preserves hidden configuration', async ({ route, heading, target }) => {
render(<MemoryRouter initialEntries={[route]}><ProjectConfiguration /></MemoryRouter>);
expect(await screen.findByRole('heading', { name: heading })).toBeVisible();
const settings = within(screen.getByTestId('project-default-settings'));
const model = settings.getByRole('combobox', { name: '默认模型' });
const save = screen.getByRole('button', { name: '保存项目配置' });
expect(model).toBeEnabled();
expect(model).toHaveValue(JSON.stringify([target.model!.accountId, target.model!.modelId]));
expect(save).toBeEnabled();
expect(settings.getByText('默认模型用于新对话;已有对话的模型和消息记录保持不变。')).toBeVisible();
expect(settings.getAllByRole('combobox')).toHaveLength(1);
expect(screen.queryByRole('textbox')).not.toBeInTheDocument();
expect(screen.queryByRole('checkbox')).not.toBeInTheDocument();
expect(screen.queryByText('项目提示词')).not.toBeInTheDocument();
expect(screen.queryByText('项目 Skills')).not.toBeInTheDocument();
expect(getCodingSkills).not.toHaveBeenCalled();
fireEvent.change(model, { target: { value: JSON.stringify(['account-1', 'model-new']) } });
fireEvent.click(save);
await waitFor(() => expect(configState.save).toHaveBeenCalledOnce());
expect(configState.save).toHaveBeenCalledWith(project.id, {
...config,
agents: config.agents.map((agent) => agent.id === target.id ? {
...agent,
model: {
accountId: 'account-1',
modelId: 'model-new',
thinkingLevel: 'off',
reasoningChoice: { mode: 'default' },
},
updatedAt: expect.any(String),
} : agent),
});
await waitFor(() => expect(save).toBeEnabled());
expect(codingState.load).toHaveBeenCalledOnce();
expect(configState.configsByProjectId[project.id]).toEqual(config);
expect(getCodingSkills).not.toHaveBeenCalled();
});
});