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(); 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(); 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(); 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(); 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); }); it('embeds project enablement in configuration and returns to Agent assignment explicitly', () => { const onManageAgents = vi.fn(); render(); expect(screen.getByTestId('project-plugins-page').tagName).toBe('SECTION'); expect(screen.queryByRole('heading', { name: '项目插件' })).not.toBeInTheDocument(); fireEvent.click(screen.getByRole('button', { name: '分配给伙伴' })); expect(onManageAgents).toHaveBeenCalledTimes(1); }); });