Merge origin/main and preserve nonblocking project entry
Some checks failed
Electron E2E / Electron E2E (macos-latest) (push) Has been cancelled
Electron E2E / Electron E2E (ubuntu-latest) (push) Has been cancelled
Electron E2E / Electron E2E (windows-latest) (push) Has been cancelled

This commit is contained in:
inman
2026-09-07 12:53:55 +08:00
66 changed files with 4068 additions and 519 deletions

View File

@@ -426,6 +426,21 @@ async function installCodingFirstChatHost(
ok: true,
data: { status, ok: status >= 200 && status < 300, json },
});
const browserSnapshot = (overrides: Record<string, unknown> = {}) => ({
browserId: null,
projectId: project.id,
projectPath: null,
state: 'closed',
generation: 0,
url: '',
title: '',
visible: false,
bounds: null,
canGoBack: false,
canGoForward: false,
eventCursor: 0,
...overrides,
});
ipcMain.removeHandler('hostapi:fetch');
ipcMain.handle('hostapi:fetch', async (
@@ -528,6 +543,47 @@ async function installCodingFirstChatHost(
: [],
});
}
if (path.startsWith('/api/agent-browser/state?')) {
return respond({ success: true, browser: browserSnapshot() });
}
if (path === '/api/agent-browser/open' && method === 'POST') {
return respond({
success: true,
browser: browserSnapshot({
browserId: 'browser-e2e',
state: 'attached',
generation: 1,
url: body?.url,
visible: true,
bounds: body?.bounds,
}),
});
}
if (path === '/api/agent-browser/present' && method === 'POST') {
return respond({
success: true,
browser: browserSnapshot({
browserId: 'browser-e2e',
state: 'attached',
generation: 1,
url: 'http://127.0.0.1:4173/',
visible: body?.visible === true,
bounds: body?.bounds ?? null,
}),
});
}
if (path === '/api/agent-browser/diagnostics' && method === 'POST') {
return respond({ success: true, browser: browserSnapshot() });
}
if (path === '/api/agent-browser/cdp/events' && method === 'POST') {
return respond({
success: true,
page: { events: [], nextCursor: 0, hasMore: false },
});
}
if (path === '/api/agent-browser/close' && method === 'POST') {
return respond({ success: true, browser: browserSnapshot() });
}
if (path === '/api/coding/projects/conversations' && method === 'POST') {
state.conversationCreated = true;
return respond({ conversation }, 201);
@@ -1039,6 +1095,20 @@ test('PI feature UI isolates Conversations and exposes queue, interaction, model
await expect(conversationHeader.getByRole('button', { name: '归档' })).toHaveCount(0);
await expect(conversationHeader.getByRole('button', { name: '智能体设置' })).toHaveCount(0);
await expect(conversationHeader.getByRole('button', { name: '打开编程工具' })).toHaveCount(0);
const browserToggle = conversationHeader.getByRole('button', { name: '打开开发浏览器' });
await expect(browserToggle).toBeVisible();
await browserToggle.click();
await expect(page.getByTestId('agent-browser-panel')).toBeVisible();
await expect(page.getByTestId('agent-browser-diagnostics')).toHaveAttribute('data-state', 'closed');
await expect.poll(async () => {
const state = await readState(electronApp);
return state.captured.some((request) => request.path.startsWith('/api/agent-browser/state?'));
}).toBe(true);
expect((await readState(electronApp)).captured.some(
(request) => request.path === '/api/agent-browser/diagnostics',
)).toBe(false);
await page.getByRole('button', { name: '关闭开发浏览器' }).last().click();
await expect(page.getByTestId('agent-browser-panel')).toHaveCount(0);
await expect(page.getByRole('button', { name: '项目设置' })).toHaveCount(1);
await expect(page.getByTestId('coding-conversation-sidebar')).toBeVisible();
const activeProcess = page.getByTestId('coding-process-group');

View File

@@ -4,7 +4,7 @@ import path from 'node:path';
import { closeElectronApp, expect, getStableWindow, test } from './fixtures/electron';
test.describe('Unified project plugin workspace', () => {
test('enables and disables a project plugin without configuring it, then opens Agent assignment', async ({ launchElectronApp }) => {
test('enables and disables a project-wide official plugin without configuring or assigning it', async ({ launchElectronApp }) => {
const projectPath = await mkdtemp(path.join(tmpdir(), 'makelore-project-plugins-e2e-'));
const app = await launchElectronApp({ skipSetup: true });
try {
@@ -104,6 +104,9 @@ test.describe('Unified project plugin workspace', () => {
})).toBeAttached();
await page.getByRole('button', { name: '查看开发数据服务详情' }).click();
await expect(page.getByText('按平台包含,不按单次插件调用扣点。')).toBeVisible();
await expect(page.getByRole('heading', { name: '生效范围' })).toBeVisible();
await expect(page.getByText(/启用当前项目后.*无需单独分配/)).toBeVisible();
await expect(page.getByRole('button', { name: '分配开发数据服务给伙伴' })).toHaveCount(0);
const localProjectId = await app.evaluate(() => (
(globalThis as typeof globalThis & { __projectPluginE2E?: { localProjectId: string } }).__projectPluginE2E?.localProjectId
));
@@ -121,8 +124,6 @@ test.describe('Unified project plugin workspace', () => {
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 }> };