feat(agents): 新增云端智能体入口与草稿编辑
This commit is contained in:
65
tests/e2e/cloud-agents.spec.ts
Normal file
65
tests/e2e/cloud-agents.spec.ts
Normal file
@@ -0,0 +1,65 @@
|
||||
import { closeElectronApp, expect, getStableWindow, test } from './fixtures/electron';
|
||||
|
||||
test('personal cloud Agent creation, draft save and leave protection in Electron', async ({ launchElectronApp }, testInfo) => {
|
||||
const app = await launchElectronApp({ skipSetup: true });
|
||||
try {
|
||||
const page = await getStableWindow(app);
|
||||
await expect(page.getByTestId('ai-module-selection-page')).toBeVisible();
|
||||
const applicationUrl = page.url();
|
||||
await page.goto('about:blank');
|
||||
// This UI test owns the Host API boundary; real cloud HTTP/PG is tested separately.
|
||||
await app.evaluate(({ ipcMain }) => {
|
||||
let draft: Record<string, unknown> | null = null;
|
||||
const result = (json: unknown) => ({ ok: true, data: { status: 200, ok: true, json } });
|
||||
ipcMain.removeHandler('hostapi:fetch');
|
||||
ipcMain.handle('hostapi:fetch', async (_event, request: { path?: string; method?: string; body?: string }) => {
|
||||
const path = request.path ?? '';
|
||||
const method = request.method ?? 'GET';
|
||||
if (path === '/api/auth/session/sync') return result({
|
||||
success: true, session: { accessToken: 'ui-fixture', tokenType: 'Bearer', expiresAt: Date.now() + 3600000, lastActiveAt: Date.now(), canRefresh: false },
|
||||
});
|
||||
if (path === '/api/auth/me') return result({
|
||||
success: true, user: { username: 'creator', userId: 'creator', tenantId: null, deptId: null, authorities: [] },
|
||||
moduleAccess: { programming: true, design: true, robot: true, cloud_agents: true },
|
||||
});
|
||||
if (path === '/api/works/user/agent-profile') return result({
|
||||
success: true, profile: { display_name: '创作者', age: null, gender: null, avatar_url: null, share_age_with_agents: false, share_gender_with_agents: false, analysis_enabled: true, completed: true, version: 1, updated_at: '2026-09-10T00:00:00Z' },
|
||||
});
|
||||
if (path.startsWith('/api/cloud-agents/')) {
|
||||
const body = typeof request.body === 'string' ? JSON.parse(request.body) : request.body ?? {};
|
||||
if (method === 'POST') draft = {
|
||||
slug: 'ml-' + 'a'.repeat(32), name: body.name, purpose: body.purpose, system_prompt: body.purpose,
|
||||
draft_revision: 1, updated_at: '2026-09-10T00:00:00Z',
|
||||
};
|
||||
if (method === 'PATCH') draft = { ...draft, name: body.name, purpose: body.purpose, system_prompt: body.system_prompt, draft_revision: 2 };
|
||||
if (method === 'GET' && (path.endsWith('/agents') || path.endsWith('/bootstrap'))) return result({ agents: draft ? [draft] : [], next_cursor: null });
|
||||
return result(draft);
|
||||
}
|
||||
return result({ success: true });
|
||||
});
|
||||
});
|
||||
await page.goto(applicationUrl);
|
||||
await page.getByTestId('ai-module-option-cloud_agents').click();
|
||||
await expect(page.getByTestId('sidebar-cloud-agents-navigation')).toBeVisible();
|
||||
await expect(page.getByTestId('sidebar-robot-navigation')).toHaveCount(0);
|
||||
await page.getByRole('button', { name: '创建第一个智能体' }).click();
|
||||
await page.getByLabel('名称', { exact: true }).fill('我的写作搭档');
|
||||
await page.getByLabel('用途', { exact: true }).fill('整理灵感,把想法写成清晰的文章');
|
||||
await page.getByRole('button', { name: '创建草稿', exact: true }).click();
|
||||
await expect(page.getByTestId('cloud-agent-editor')).toBeVisible();
|
||||
await page.getByLabel('角色与指令').fill('使用简洁自然的中文。先理解我的目的,再协助梳理结构。');
|
||||
await page.getByTestId('sidebar-module-switcher-trigger').click();
|
||||
await expect(page.getByRole('dialog', { name: '未保存的修改' })).toBeVisible();
|
||||
await page.getByRole('button', { name: '继续编辑', exact: true }).click();
|
||||
await page.getByRole('button', { name: '保存草稿', exact: true }).click();
|
||||
await expect(page.getByRole('button', { name: '已保存', exact: true })).toBeVisible();
|
||||
await page.screenshot({ path: testInfo.outputPath('cloud-agent-editor.png') });
|
||||
await page.getByTestId('sidebar-module-switcher-trigger').click();
|
||||
await expect(page.getByTestId('ai-module-selection-page')).toBeVisible();
|
||||
await page.getByTestId('ai-module-option-cloud_agents').click();
|
||||
await page.getByRole('button', { name: /我的写作搭档/ }).click();
|
||||
await expect(page.getByLabel('角色与指令')).toHaveValue('使用简洁自然的中文。先理解我的目的,再协助梳理结构。');
|
||||
} finally {
|
||||
await closeElectronApp(app);
|
||||
}
|
||||
});
|
||||
@@ -81,7 +81,7 @@ test.describe('Makelore module navigation without setup flow', () => {
|
||||
}
|
||||
});
|
||||
|
||||
test('keeps the three module entries in a proportional vertical stack while resizing the window', async ({ launchElectronApp }) => {
|
||||
test('keeps the four module entries in a proportional vertical stack while resizing the window', async ({ launchElectronApp }) => {
|
||||
const app = await launchElectronApp({ skipSetup: true });
|
||||
|
||||
try {
|
||||
@@ -103,7 +103,7 @@ test.describe('Makelore module navigation without setup flow', () => {
|
||||
}));
|
||||
|
||||
const initialMetrics = await readCardMetrics();
|
||||
expect(initialMetrics).toHaveLength(3);
|
||||
expect(initialMetrics).toHaveLength(4);
|
||||
await expect(moduleCards.first().locator('.module-option-card-arrow')).toHaveCount(0);
|
||||
await expect(moduleCards.first().locator('.module-option-artwork')).toHaveCount(0);
|
||||
await expect.poll(async () => await moduleCards.first().evaluate((element) => {
|
||||
@@ -155,7 +155,7 @@ test.describe('Makelore module navigation without setup flow', () => {
|
||||
await expect.poll(async () => (await readCardMetrics())[0]?.width ?? 0).toBeLessThan(initialWidth - 1);
|
||||
|
||||
const resizedMetrics = await readCardMetrics();
|
||||
expect(resizedMetrics).toHaveLength(3);
|
||||
expect(resizedMetrics).toHaveLength(4);
|
||||
expect(Math.max(...resizedMetrics.map((card) => card.left)) - Math.min(...resizedMetrics.map((card) => card.left))).toBeLessThan(1);
|
||||
expect(resizedMetrics.map((card) => card.top)).toEqual(
|
||||
[...resizedMetrics].sort((top, bottom) => top.top - bottom.top).map((card) => card.top),
|
||||
@@ -195,7 +195,7 @@ test.describe('Makelore module navigation without setup flow', () => {
|
||||
const page = await getStableWindow(app);
|
||||
await expect(page.getByTestId('ai-module-selection-page')).toBeVisible();
|
||||
|
||||
await expect(page.locator('[data-testid^="ai-module-option-"]')).toHaveCount(3);
|
||||
await expect(page.locator('[data-testid^="ai-module-option-"]')).toHaveCount(4);
|
||||
await expect(page.getByTestId('ai-module-option-learning')).toHaveCount(0);
|
||||
await page.evaluate(() => {
|
||||
window.location.hash = '#/learning';
|
||||
|
||||
Reference in New Issue
Block a user