98 lines
8.3 KiB
TypeScript
98 lines
8.3 KiB
TypeScript
import { closeElectronApp, expect, getStableWindow, test } from './fixtures/electron';
|
|
|
|
test('personal chat displays thinking, tools and Markdown, then restores them after reload', 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');
|
|
// Cloud provider is deterministic here; the real history HTTP/PG boundary is tested in Yuxi.
|
|
await app.evaluate(({ ipcMain, BrowserWindow }) => {
|
|
BrowserWindow.getAllWindows()[0].setSize(1180, 820);
|
|
let complete = false;
|
|
Object.assign(globalThis, { finishRenderRun: () => { complete = true; } });
|
|
const slug = 'ml-' + 'a'.repeat(32);
|
|
const configuration = { model: 'test', tools: [], knowledges: [], mcps: [], skills: [], preload_skills: [], subagents: [], tool_approval_mode: 'always_trust', max_execution_steps: 300, max_output_tokens: 4096, max_run_seconds: 600 };
|
|
const draft = { slug, name: '渲染测试助手', purpose: '整理资料', system_prompt: '', configuration,
|
|
draft_revision: 1, published_version: 1, enabled: true, updated_at: '' };
|
|
const run = { agent_run_id: 'render-run', request_id: 'render-request', thread_id: 'render-thread', agent_slug: slug, status: 'running', output: '', version: '1' };
|
|
const thread = { thread_id: 'render-thread', client_thread_id: 'client', agent_slug: slug, title: '整理资料', mode: 'published', updated_at: '', run_id: run.agent_run_id, unread: false, status: 'running' };
|
|
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; body?: string }) => {
|
|
const path = request.path ?? '';
|
|
const body = request.body ? JSON.parse(request.body) : {};
|
|
if (path === '/api/auth/session/sync') return result({ success: true, session: { accessToken: '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: '创作者', completed: true, version: 1 } });
|
|
if (path.endsWith('/recovery')) return result({ recent: null, pending: [] });
|
|
if (path.endsWith('/recovery/recent')) return result({});
|
|
if (path.endsWith('/agents') || path.endsWith('/bootstrap')) return result({ agents: [draft], next_cursor: null });
|
|
if (path.endsWith('/actions')) {
|
|
if (body.operation === 'threads') return result({ threads: [thread], next_offset: null });
|
|
if (body.operation === 'history') return result({ thread_id: thread.thread_id, run: { ...run, status: complete ? 'completed' : 'running' }, next_offset: null, messages: complete ? [{
|
|
id: 1, message_id: 'answer', role: 'assistant', run_id: run.agent_run_id, request_id: run.request_id, created_at: '',
|
|
content: '## 整理结果\n\n| 项目 | 状态 |\n| --- | --- |\n| 资料 | 已核对 |\n\n**可以使用**',
|
|
reasoning_content: '先核对来源,再整理结果。',
|
|
tool_calls: [{ id: 'lookup', name: '资料检索', args: { query: '实际检索资料' }, status: 'success', tool_call_result: { content: '找到两条资料' } }],
|
|
}] : [] });
|
|
if (body.operation === 'run') return result({ ...run, status: complete ? 'completed' : 'running' });
|
|
if (body.operation === 'viewed') return result({});
|
|
if (body.operation === 'files') return result({ files: [] });
|
|
if (body.operation === 'attachments') return result({ attachments: [] });
|
|
if (body.operation === 'catalog') return result({ models: [{ id: 'test', name: '测试模型' }], resources: {}, pricing: null });
|
|
if (body.operation === 'access') return result({ published_version: 1, enabled: true, versions: [], grants: [], applications: [], share_url: '', api_url: '' });
|
|
if (body.operation === 'channelBindings') return result({ items: [], available_accounts: [] });
|
|
return result({});
|
|
}
|
|
if (path.startsWith('/api/cloud-agents/agents/')) return result(draft);
|
|
return result({ success: true });
|
|
});
|
|
});
|
|
await page.addInitScript(() => {
|
|
const sources: EventTarget[] = [];
|
|
Object.assign(window, { renderSources: sources });
|
|
class FixtureSource extends EventTarget {
|
|
onopen = null; onerror = null; close() {}
|
|
constructor(url: string) { super(); if (url.includes('/cloud-agents/runs/')) sources.push(this); }
|
|
}
|
|
Object.defineProperty(window, 'EventSource', { value: FixtureSource });
|
|
});
|
|
await page.goto(applicationUrl);
|
|
await page.getByTestId('ai-module-option-cloud_agents').click();
|
|
await page.getByRole('button', { name: /渲染测试助手.*打开对话/ }).click();
|
|
const emit = async (items: unknown[], cursor: string) => page.evaluate(({ items, cursor }) => {
|
|
const sources = (window as unknown as { renderSources: EventTarget[] }).renderSources;
|
|
sources.at(-1)!.dispatchEvent(new MessageEvent('messages', { lastEventId: cursor, data: JSON.stringify({ run_id: 'render-run', thread_id: 'render-thread', payload: { items } }) }));
|
|
}, { items, cursor });
|
|
await expect.poll(() => page.evaluate(() => (window as unknown as { renderSources: EventTarget[] }).renderSources.length)).toBeGreaterThan(0);
|
|
await emit([{ stream_event: { type: 'message_delta', message_id: 'answer', reasoning_content: '先核对来源,再整理结果。' } }], '100-1');
|
|
const process = page.getByTestId('cloud-message-process');
|
|
await expect(page.getByLabel('智能体思考过程')).toBeVisible();
|
|
await emit([
|
|
{ stream_event: { type: 'tool_call', message_id: 'answer', tool_call_id: 'lookup', name: '资料检索', args: { query: '测试资料' } } },
|
|
{ event: { method: 'tools', data: { event: 'tool-started', tool_call_id: 'lookup', input: { query: '实际检索资料' } } } },
|
|
{ event: { method: 'tools', data: { event: 'tool-finished', tool_call_id: 'lookup', output: { content: '找到两条资料' } } } },
|
|
{ stream_event: { type: 'message_delta', message_id: 'answer', content: '## 整理结果\n\n| 项目 | 状态 |\n| --- | --- |\n| 资料 | 已核对 |\n\n**可以使用**' } },
|
|
], '100-2');
|
|
await expect(page.getByRole('heading', { name: '整理结果' })).toBeVisible();
|
|
await expect(page.getByRole('table')).toBeVisible();
|
|
await process.locator(':scope > summary').click();
|
|
await process.getByText('资料检索 · 已完成', { exact: true }).click();
|
|
await expect(process.getByText('找到两条资料', { exact: true })).toBeVisible();
|
|
await expect(process.getByText('"实际检索资料"', { exact: false })).toBeVisible();
|
|
await page.screenshot({ path: testInfo.outputPath('personal-chat-process.png') });
|
|
await app.evaluate(() => (globalThis as unknown as { finishRenderRun: () => void }).finishRenderRun());
|
|
await page.evaluate(() => (window as unknown as { renderSources: EventTarget[] }).renderSources.at(-1)!.dispatchEvent(new MessageEvent('end', { data: JSON.stringify({ thread_id: 'render-thread', payload: { status: 'completed' } }) })));
|
|
await expect(page.getByRole('heading', { name: '整理结果' })).toHaveCount(1);
|
|
await page.reload();
|
|
await page.getByRole('button', { name: /渲染测试助手.*打开对话/ }).click();
|
|
await expect(page.getByRole('heading', { name: '整理结果' })).toBeVisible();
|
|
await page.getByTestId('cloud-message-process').locator(':scope > summary').click();
|
|
await expect(page.getByLabel('智能体思考过程')).toHaveText('先核对来源,再整理结果。');
|
|
await expect(page.getByText('资料检索 · 已完成', { exact: true })).toBeVisible();
|
|
await page.screenshot({ path: testInfo.outputPath('personal-chat-reopened.png') });
|
|
} finally { await closeElectronApp(app); }
|
|
});
|