feat: unify plugin workspace navigation
This commit is contained in:
@@ -3,8 +3,8 @@ import { tmpdir } from 'node:os';
|
||||
import path from 'node:path';
|
||||
import { closeElectronApp, expect, getStableWindow, test } from './fixtures/electron';
|
||||
|
||||
test.describe('Project Plugin Center', () => {
|
||||
test('enables selection without configuring Data Service or duplicating project IDs', async ({ launchElectronApp }) => {
|
||||
test.describe('Unified project plugin workspace', () => {
|
||||
test('enables and disables a project plugin without configuring it, then opens Agent assignment', async ({ launchElectronApp }) => {
|
||||
const projectPath = await mkdtemp(path.join(tmpdir(), 'makelore-project-plugins-e2e-'));
|
||||
const app = await launchElectronApp({ skipSetup: true });
|
||||
try {
|
||||
@@ -20,53 +20,109 @@ test.describe('Project Plugin Center', () => {
|
||||
await expect(page.getByTestId('project-configuration-page')).toBeVisible();
|
||||
|
||||
await app.evaluate(({ ipcMain }) => {
|
||||
const state = { enabled: false, dataServiceCalls: 0, localProjectId: '' };
|
||||
const state = {
|
||||
enabled: false,
|
||||
dataServiceCalls: 0,
|
||||
localProjectId: '',
|
||||
mutations: [] as Array<{ enabled: boolean; projectId: string }>,
|
||||
};
|
||||
const result = (json: unknown) => ({ ok: true, data: { status: 200, ok: true, json } });
|
||||
const project = () => ({
|
||||
schemaVersion: 1,
|
||||
project: {
|
||||
localProjectId: state.localProjectId,
|
||||
durableProjectId: '11111111-1111-4111-8111-111111111111',
|
||||
},
|
||||
policyStatus: 'current',
|
||||
unknownPluginIds: [],
|
||||
items: [{
|
||||
id: 'makelore.data-service', version: '1.0.0', displayName: '开发数据服务',
|
||||
description: '为当前项目提供数据。', contractVersion: 1, requiresBackend: true,
|
||||
enabled: state.enabled,
|
||||
state: state.enabled ? 'configuration_required' : 'disabled',
|
||||
backend: { status: 'unconfigured' },
|
||||
skills: [{ id: 'data-service', assignedAgentIds: [] }],
|
||||
capabilities: [{
|
||||
id: 'data-service.control',
|
||||
operations: [{
|
||||
id: 'inspect',
|
||||
billing: { mode: 'included', availability: 'available', notice: 'Fixed quotas apply' },
|
||||
tool: null,
|
||||
}],
|
||||
}],
|
||||
settingsSurface: 'data-service',
|
||||
}],
|
||||
});
|
||||
(globalThis as typeof globalThis & { __projectPluginE2E?: typeof state }).__projectPluginE2E = state;
|
||||
ipcMain.removeHandler('hostapi:fetch');
|
||||
ipcMain.handle('hostapi:fetch', async (_event, request: { path?: string; method?: string }) => {
|
||||
ipcMain.handle('hostapi:fetch', async (_event, request: { path?: string; method?: string; body?: string }) => {
|
||||
const requestPath = request.path ?? '';
|
||||
const method = (request.method ?? 'GET').toUpperCase();
|
||||
if (requestPath.startsWith('/api/works/data-service/')) {
|
||||
state.dataServiceCalls += 1;
|
||||
return { ok: false, error: { message: 'Enable must not configure Data Service' } };
|
||||
}
|
||||
if (requestPath.startsWith('/api/coding/plugins')) {
|
||||
if (request.method === 'PUT') state.enabled = true;
|
||||
else state.localProjectId = new URL(requestPath, 'https://makelore.local').searchParams.get('projectId') ?? '';
|
||||
return { ok: true, data: { status: 200, ok: true, json: {
|
||||
schemaVersion: 1,
|
||||
project: { localProjectId: state.localProjectId, durableProjectId: '11111111-1111-4111-8111-111111111111' },
|
||||
policyStatus: 'current',
|
||||
unknownPluginIds: [],
|
||||
items: [{
|
||||
id: 'makelore.data-service', version: '1.0.0', displayName: '开发数据服务', description: '为当前项目提供数据。',
|
||||
contractVersion: 1, requiresBackend: true,
|
||||
enabled: state.enabled, state: state.enabled ? 'configuration_required' : 'disabled', backend: { status: 'unconfigured' },
|
||||
skills: [{ id: 'data-service', assignedAgentIds: [] }],
|
||||
capabilities: [{ id: 'data-service.control', operations: [{ id: 'inspect', billing: { mode: 'included', availability: 'available', notice: 'Fixed quotas apply' }, tool: null }] }],
|
||||
settingsSurface: 'data-service',
|
||||
}],
|
||||
} } };
|
||||
if (requestPath.startsWith('/api/coding/plugin-marketplace/catalog')) return result({
|
||||
items: [{
|
||||
pluginId: 'makelore.data-service', title: '开发数据服务', summary: '为当前项目提供数据。',
|
||||
category: '系统', tags: ['data'], providerDisplayName: 'MakeLore', runtimeKind: 'bundled_typed',
|
||||
runtimeStatus: 'enabled', acquisition: 'system_included', usageBilling: 'included',
|
||||
includedOperationCount: 1, meteredOperationCount: 0, stableVersion: '1.0.0', betaVersion: null,
|
||||
}],
|
||||
nextCursor: null, total: 1, catalogGeneration: 1, etag: 'plugins-1',
|
||||
pricingVersionId: null, stale: false, fetchedAt: 1,
|
||||
});
|
||||
if (requestPath === '/api/coding/device-packages') {
|
||||
return result({ schemaVersion: 1, generation: 1, packages: [] });
|
||||
}
|
||||
return { ok: false, error: { message: `Unexpected Host API request: ${requestPath}` } };
|
||||
if (requestPath.startsWith('/api/coding/plugins')) {
|
||||
if (method === 'PUT') {
|
||||
const body = JSON.parse(request.body ?? '{}') as { enabled?: unknown; projectId?: unknown };
|
||||
state.enabled = body.enabled === true;
|
||||
state.mutations.push({ enabled: state.enabled, projectId: String(body.projectId ?? '') });
|
||||
} else {
|
||||
state.localProjectId = new URL(requestPath, 'https://makelore.local').searchParams.get('projectId') ?? '';
|
||||
}
|
||||
return result(project());
|
||||
}
|
||||
return { ok: false, error: { message: `Unexpected Host API request: ${method} ${requestPath}` } };
|
||||
});
|
||||
});
|
||||
|
||||
await page.getByTestId('sidebar-nav-project-plugins').click();
|
||||
await expect(page.getByTestId('project-plugins-page')).toBeVisible();
|
||||
await expect(page.getByText('当前包含,不按单次调用扣点')).toBeVisible();
|
||||
await page.getByTestId('sidebar-nav-plugins').click();
|
||||
await expect(page.getByTestId('plugins-page')).toBeVisible();
|
||||
await expect(page).toHaveURL(/\/plugins\?scope=project/);
|
||||
await page.getByRole('button', { name: '查看开发数据服务详情' }).click();
|
||||
await expect(page.getByText('按平台包含,不按单次插件调用扣点。')).toBeVisible();
|
||||
const localProjectId = await app.evaluate(() => (
|
||||
(globalThis as typeof globalThis & { __projectPluginE2E?: { localProjectId: string } }).__projectPluginE2E?.localProjectId
|
||||
));
|
||||
expect(localProjectId).toBeTruthy();
|
||||
await expect(page.getByText(localProjectId as string)).toHaveCount(0);
|
||||
await expect(page.getByText('11111111-1111-4111-8111-111111111111')).toHaveCount(0);
|
||||
await page.getByRole('button', { name: '启用到项目开发数据服务' }).click();
|
||||
await expect(page.getByRole('article').getByText('待配置')).toBeVisible();
|
||||
|
||||
await page.getByRole('button', { name: '启用开发数据服务到当前项目' }).click();
|
||||
await expect(page.getByRole('button', { name: '从当前项目禁用开发数据服务' })).toBeVisible();
|
||||
await expect(page.getByText('插件专属设置')).toBeVisible();
|
||||
await expect(page.getByRole('button', { name: '创建开发数据空间' })).toBeDisabled();
|
||||
const dataServiceCalls = await app.evaluate(() => (
|
||||
(globalThis as typeof globalThis & { __projectPluginE2E?: { dataServiceCalls: number } }).__projectPluginE2E?.dataServiceCalls
|
||||
|
||||
await page.getByRole('button', { name: '从当前项目禁用开发数据服务' }).click();
|
||||
await expect(page.getByRole('alertdialog')).toContainText('从当前项目禁用开发数据服务');
|
||||
await page.getByRole('button', { name: '确认从当前项目禁用' }).click();
|
||||
await expect(page.getByRole('button', { name: '启用开发数据服务到当前项目' })).toBeVisible();
|
||||
|
||||
await page.getByRole('button', { name: '分配开发数据服务给伙伴' }).click();
|
||||
await expect(page.getByTestId('project-configuration-page')).toBeVisible();
|
||||
const state = await app.evaluate(() => (
|
||||
(globalThis as typeof globalThis & {
|
||||
__projectPluginE2E?: { dataServiceCalls: number; mutations: Array<{ enabled: boolean; projectId: string }> };
|
||||
}).__projectPluginE2E
|
||||
));
|
||||
expect(dataServiceCalls).toBe(0);
|
||||
expect(state?.dataServiceCalls).toBe(0);
|
||||
expect(state?.mutations).toEqual([
|
||||
{ enabled: true, projectId: localProjectId },
|
||||
{ enabled: false, projectId: localProjectId },
|
||||
]);
|
||||
} finally {
|
||||
await closeElectronApp(app);
|
||||
await rm(projectPath, { recursive: true, force: true });
|
||||
|
||||
Reference in New Issue
Block a user