96 lines
5.8 KiB
TypeScript
96 lines
5.8 KiB
TypeScript
import { fireEvent, render, screen } from '@testing-library/react';
|
|
import { MemoryRouter } from 'react-router-dom';
|
|
import { describe, expect, it, vi } from 'vitest';
|
|
import { ProjectPluginsView } from '@/pages/ProjectPlugins';
|
|
import type { CodingPluginProject } from '@/lib/coding-plugins';
|
|
|
|
function projection(): CodingPluginProject {
|
|
return {
|
|
schemaVersion: 1,
|
|
project: { localProjectId: 'local-only-id', durableProjectId: '11111111-1111-4111-8111-111111111111' },
|
|
policyStatus: 'current',
|
|
unknownPluginIds: [],
|
|
items: [{
|
|
id: 'makelore.data-service', version: '1.0.0', displayName: '开发数据服务', description: '为当前项目提供 JSON 数据。',
|
|
contractVersion: 1, requiresBackend: true,
|
|
enabled: false, state: 'disabled', backend: { status: 'unconfigured' },
|
|
skills: [{ id: 'data-service', assignedAgentIds: ['agent-1'] }],
|
|
capabilities: [
|
|
{ id: 'data-service.control', operations: [{
|
|
id: 'inspect',
|
|
billing: { mode: 'included', availability: 'available', notice: 'Fixed quotas apply' },
|
|
tool: {
|
|
name: 'data_service_inspect', label: 'Inspect', description: 'Inspect data',
|
|
mutation: 'read', permissions: ['project.data.read'],
|
|
},
|
|
}] },
|
|
{ id: 'data-service.documents', operations: [{
|
|
id: 'put_document',
|
|
billing: { mode: 'external_account', availability: 'available', notice: 'Provider billed' },
|
|
tool: {
|
|
name: 'data_service_put_document', label: 'Put', description: 'Put data',
|
|
mutation: 'write', permissions: ['project.data.write'],
|
|
},
|
|
}] },
|
|
{ id: 'data-service.preview', operations: [{
|
|
id: 'get',
|
|
billing: { mode: 'platform_metered', availability: 'unavailable', notice: 'Pricing unavailable' },
|
|
tool: null,
|
|
}] },
|
|
],
|
|
settingsSurface: 'data-service',
|
|
}],
|
|
};
|
|
}
|
|
|
|
describe('Project Plugin Center', () => {
|
|
it('uses available/enabled language, text states, exact included copy, and never renders project IDs', () => {
|
|
const onSetEnabled = vi.fn();
|
|
render(<MemoryRouter><ProjectPluginsView projectName="Demo" projection={projection()} dataService={null} pending={{}} agentNames={{ 'agent-1': '小明' }} onRefresh={vi.fn()} onSetEnabled={onSetEnabled} onConfigure={vi.fn()} onReset={vi.fn()} onRemoveCollection={vi.fn()} onRemoveProject={vi.fn()} /></MemoryRouter>);
|
|
|
|
expect(screen.getByRole('heading', { name: '项目插件' })).toHaveClass('text-balance');
|
|
expect(screen.getAllByText('未启用').length).toBeGreaterThan(0);
|
|
expect(screen.getByText('当前包含,不按单次调用扣点')).toBeVisible();
|
|
expect(screen.getByText('小明')).toBeVisible();
|
|
expect(screen.getByText('data-service.control')).toBeVisible();
|
|
expect(screen.getByText('data-service.documents')).toBeVisible();
|
|
expect(screen.getByText('data-service.preview')).toBeVisible();
|
|
expect(screen.getByText('data_service_inspect')).toBeVisible();
|
|
expect(screen.getByText('SDK')).toBeVisible();
|
|
expect(screen.getByText('由外部服务商计费,不计入 Token Point。')).toBeVisible();
|
|
expect(screen.getByText('计费策略暂不可用,相关调用已停用。')).toBeVisible();
|
|
expect(screen.queryByText('local-only-id')).not.toBeInTheDocument();
|
|
expect(screen.queryByText('11111111-1111-4111-8111-111111111111')).not.toBeInTheDocument();
|
|
fireEvent.click(screen.getByRole('button', { name: '启用到项目开发数据服务' }));
|
|
expect(onSetEnabled).toHaveBeenCalledWith('makelore.data-service', true);
|
|
});
|
|
|
|
it('keeps the last project structure visible when refresh is degraded', () => {
|
|
render(<MemoryRouter><ProjectPluginsView projectName="Demo" projection={projection()} dataService={null} pending={{}} agentNames={{}} refreshError="network unavailable" onRefresh={vi.fn()} onSetEnabled={vi.fn()} onConfigure={vi.fn()} onReset={vi.fn()} onRemoveCollection={vi.fn()} onRemoveProject={vi.fn()} /></MemoryRouter>);
|
|
expect(screen.getByRole('status')).toHaveTextContent('刷新失败');
|
|
expect(screen.getByRole('article')).toBeVisible();
|
|
});
|
|
|
|
it('explains unknown billing and keeps invocation unavailable', () => {
|
|
const value = projection();
|
|
value.items[0].state = 'unavailable';
|
|
value.items[0].capabilities = [value.items[0].capabilities[0]];
|
|
value.items[0].capabilities[0].operations[0].billing = { mode: 'platform_metered', availability: 'unavailable', notice: 'pricing unavailable' };
|
|
render(<MemoryRouter><ProjectPluginsView projectName="Demo" projection={value} dataService={null} pending={{}} agentNames={{}} onRefresh={vi.fn()} onSetEnabled={vi.fn()} onConfigure={vi.fn()} onReset={vi.fn()} onRemoveCollection={vi.fn()} onRemoveProject={vi.fn()} /></MemoryRouter>);
|
|
expect(screen.getByText('计费策略暂不可用,相关调用已停用。')).toBeVisible();
|
|
expect(screen.getByRole('button', { name: '启用到项目开发数据服务' })).toBeDisabled();
|
|
});
|
|
|
|
it('shows retained unknown IDs without invented metadata and allows only explicit disable', () => {
|
|
const value = projection();
|
|
value.unknownPluginIds = ['makelore.removed'];
|
|
const onSetEnabled = vi.fn();
|
|
render(<MemoryRouter><ProjectPluginsView projectName="Demo" projection={value} dataService={null} pending={{}} agentNames={{}} onRefresh={vi.fn()} onSetEnabled={onSetEnabled} onConfigure={vi.fn()} onReset={vi.fn()} onRemoveCollection={vi.fn()} onRemoveProject={vi.fn()} /></MemoryRouter>);
|
|
expect(screen.getByText('配置保留、当前不可用')).toBeVisible();
|
|
expect(screen.getByText('makelore.removed')).toBeVisible();
|
|
expect(screen.queryByText(/makelore.removed.*v\d/u)).not.toBeInTheDocument();
|
|
fireEvent.click(screen.getByRole('button', { name: '从项目禁用保留插件 makelore.removed' }));
|
|
expect(onSetEnabled).toHaveBeenCalledWith('makelore.removed', false);
|
|
});
|
|
});
|