Files
makelore/tests/e2e/cloud-agents.spec.ts
T

321 lines
27 KiB
TypeScript

import { closeElectronApp, expect, getStableWindow, test } from './fixtures/electron';
import { writeFile } from 'node:fs/promises';
test('personal cloud Agent identity, configuration and separate published chat in Electron', async ({ launchElectronApp }, testInfo) => {
const app = await launchElectronApp({ skipSetup: true });
const avatar = {
icon: '/uploads/avatars/writing-partner.png',
icon_url: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAwMCAO+ip1sAAAAASUVORK5CYII=',
};
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, BrowserWindow }, uploadedAvatar) => {
BrowserWindow.getAllWindows()[0].setSize(1366, 768);
let draft: Record<string, unknown> | null = null;
let recent: unknown = null;
let createInput: Record<string, unknown> | null = null;
const operations: string[] = [];
Reflect.set(globalThis, '__cloudAgentUiFixture', { snapshot: () => ({ draft, createInput, operations }) });
const versions: { version: number; draft_revision: number; created_at: string }[] = [];
let knowledgeCatalogLoads = 0;
let knowledgeCreated = false;
let knowledgeUploaded = false;
let knowledgeStage = -1;
const knowledgeFile = { file_id: 'document', name: '本体(Ontology)学习路线图.pdf', size: 34000, error: null };
let limits: { request_limit_points: string | null; daily_limit_points: string | null } = { request_limit_points: null, daily_limit_points: null };
const jobs: Record<string, unknown>[] = [];
const configuration = { model: '', tools: [], knowledges: [], mcps: [], skills: [], preload_skills: [], subagents: [], tool_approval_mode: 'default', max_execution_steps: 40, max_output_tokens: 4096, max_run_seconds: 600 };
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 (path.endsWith('/avatars/pick')) return result(uploadedAvatar);
if (path.endsWith('/knowledge/pick')) {
knowledgeUploaded = true;
return result({ ...knowledgeFile, status: 'uploaded', chunk_count: 0, available: false, processing_task: null });
}
if (path.endsWith('/recovery')) return result({ recent, pending: [] });
if (path.endsWith('/recovery/recent')) { recent = body; return result({ saved: true }); }
if (path.endsWith('/actions')) {
operations.push(body.operation);
const input = body.input ?? {};
if (body.operation === 'catalog') return result({
models: [{ id: 'test-model', name: '创作模型', description: '模型说明不应显示在配置页' }, { id: 'reasoning-model', name: '思考模型' }],
resources: { tools: [{ key: 'fixture-tool', name: '资料整理工具' }], skills: [{ key: 'fixture-skill', name: '文章整理 Skill' }] },
pricing: { unit: '词元点数', usage_unit: '词元', unit_size: 1000, points_per_unit: '9.99', version: 1 },
});
if (body.operation === 'knowledge') return result({ databases: knowledgeCreated ? [{ kb_id: 'kb', name: '我的创作资料' }] : [], models: ++knowledgeCatalogLoads === 1 ? [] : [{ id: 'fixture:test-embedding', name: '资料向量模型', dimension: 1024 }] });
if (body.operation === 'createKnowledge') { knowledgeCreated = true; return result({ kb_id: 'kb', name: '我的创作资料' }); }
if (body.operation === 'processKnowledge') { knowledgeStage = 0; return result({ task_id: 'process-document' }); }
if (body.operation === 'knowledgeFiles') {
const stage = knowledgeStage;
if (knowledgeStage >= 0) knowledgeStage++;
return result({ next_offset: null, files: knowledgeUploaded ? [
{ ...knowledgeFile, status: stage < 1 ? 'uploaded' : stage === 1 ? 'parsing' : stage === 2 ? 'indexing' : 'indexed',
available: stage >= 3, chunk_count: stage >= 3 ? 18 : 0,
processing_task: stage < 0 ? null : { task_id: 'process-document', status: stage === 0 ? 'pending' : stage < 3 ? 'running' : 'success', error: null } },
{ file_id: 'empty', name: '扫描文档.pdf', size: 2000, status: 'indexed', available: false, chunk_count: 0, error: null },
] : [] });
}
if (body.operation === 'resources') return result({ mcps: [], skills: [], subagents: [] });
if (body.operation === 'budget' || body.operation === 'saveBudget') {
if (body.operation === 'saveBudget') limits = input;
return result({ agent_slug: draft?.slug, ...limits, daily_committed_points: '0.00', timezone: 'Asia/Shanghai', unit: '词元点数', resets_at: '2026-09-11T00:00:00+08:00' });
}
if (body.operation === 'scheduleContext') return result({ version: 1, enabled: true, configuration, result_destination: '任务历史对话与活动', payer: 'creator' });
if (body.operation === 'channelSelfCallers') return result({ items: [] });
if (body.operation === 'channelBindings') return result({ items: [], available_accounts: [] });
if (body.operation === 'threads') return result({ threads: [], next_offset: null });
if (body.operation === 'publish') { const version = versions.length + 1; if (draft) draft.published_version = version; versions.push({ version, draft_revision: input.expected_revision, created_at: new Date().toISOString() }); return result({ version, draft_revision: input.expected_revision }); }
if (body.operation === 'access') return result({ published_version: draft?.published_version, enabled: true, share_url: 'niancode://agents/' + draft?.slug, versions, grants: [], applications: [] });
if (body.operation === 'costs') return result({ items: [], next_offset: null, unit: '词元点数', summary: { count: 0, settled_points: '0.00', pending_points: '0.00' } });
if (body.operation === 'schedules') return result({ jobs });
if (body.operation === 'createSchedule') { const job = { ...input, id: 'job-1', agent_slug: draft?.slug, next_run_at: '2026-09-11T01:00:00Z', runs: [] }; jobs.push(job); return result(job); }
if (body.operation === 'submit' || body.operation === 'preview') return result({ request_id: input.request_id, thread_id: 'thread-1', run_id: 'run-1', version: '1', status: 'dispatched' });
if (body.operation === 'history') return result({ thread_id: 'thread-1', next_offset: null,
queued_requests: [], schedule_proposals: [{ id: 'proposal', name: '每天整理选题', prompt: '每天给我三个适合个人创作的选题', cron_expression: '30 8 * * *', timezone: 'Asia/Shanghai' }],
messages: [{ id: 1, role: 'assistant', content: '## 创作建议\n先从一个真实的观察开始。\n\n' + '记录今天的一次观察,把具体场景、你的感受和想分享的发现写下来。\n\n'.repeat(22) }],
run: { agent_run_id: 'run-1', request_id: 'request-1', thread_id: 'thread-1', status: 'completed', version: '1' } });
if (body.operation === 'viewed') return result({ viewed: true });
if (body.operation === 'attachments') return result({ attachments: [] });
if (body.operation === 'files') return result({ files: [] });
throw new Error('Unexpected cloud fixture operation ' + body.operation);
}
if (method === 'POST') { createInput = body; draft = {
slug: 'ml-' + 'a'.repeat(32), name: body.name, purpose: body.purpose, system_prompt: body.purpose,
icon: body.icon ?? null, icon_url: body.icon === uploadedAvatar.icon ? uploadedAvatar.icon_url : null,
draft_revision: 1, updated_at: '2026-09-10T00:00:00Z',
configuration, published_version: null, enabled: true,
}; }
if (method === 'PATCH') draft = { ...draft, name: body.name, purpose: body.purpose, system_prompt: body.system_prompt,
icon: body.icon ?? null, icon_url: body.icon === uploadedAvatar.icon ? uploadedAvatar.icon_url : null,
configuration: body.configuration, draft_revision: Number(draft?.draft_revision ?? 1) + 1 };
if (method === 'GET' && (path.endsWith('/agents') || path.endsWith('/bootstrap'))) return result({ agents: draft ? [draft] : [], next_cursor: null });
return result(draft);
}
return result({ success: true });
});
}, avatar);
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);
const navigation = page.getByTestId('agent-titlebar-navigation');
await expect(navigation.getByRole('button', { name: '返回首页', exact: true })).toBeVisible();
await expect(page.getByRole('button', { name: '返回首页', exact: true })).toHaveCount(1);
const returnBounds = await navigation.getByRole('button', { name: '返回首页', exact: true }).boundingBox();
const collapseBounds = await navigation.getByRole('button', { name: '折叠侧栏', exact: true }).boundingBox();
expect(returnBounds).not.toBeNull();
expect(collapseBounds).not.toBeNull();
expect(Math.abs(returnBounds!.y - collapseBounds!.y)).toBeLessThanOrEqual(1);
expect(returnBounds!.x + returnBounds!.width).toBeLessThan(collapseBounds!.x);
await navigation.getByRole('button', { name: '折叠侧栏', exact: true }).click();
await page.mouse.move(700, 400);
await expect(navigation.getByRole('button', { name: '返回首页', exact: true })).toBeVisible();
await navigation.getByRole('button', { name: /展开侧栏|固定侧栏/ }).click();
await expect(page.getByTestId('sidebar-cloud-agents-navigation')).toBeVisible();
const fixtureState = () => app.evaluate(() => Reflect.get(globalThis, '__cloudAgentUiFixture').snapshot());
const messageOperations = async () => (await fixtureState()).operations.filter((operation: string) => operation === 'preview' || operation === 'submit');
await page.getByRole('button', { name: '创建我的第一个智能体', exact: true }).click();
await expect(page.getByRole('heading', { name: '创建智能体', exact: true })).toBeVisible();
await expect(page.getByLabel('名称', { exact: true })).toHaveValue('');
await expect(page.getByRole('textbox')).toHaveCount(1);
await expect(page.getByLabel('用途', { exact: true })).toHaveCount(0);
await expect(page.getByLabel('提示词', { exact: true })).toHaveCount(0);
await expect(page.getByLabel('模型', { exact: true })).toHaveCount(0);
await expect(page.getByRole('button', { name: /科学问答助手|故事搭档|英语陪练|整理小帮手|我自己想一个|先给我提示/ })).toHaveCount(0);
await expect(page.getByRole('button', { name: '上一步', exact: true })).toHaveCount(0);
await expect(page.getByRole('button', { name: '下一步', exact: true })).toBeDisabled();
await page.screenshot({ path: testInfo.outputPath('cloud-agent-create-empty-form.png') });
await page.getByLabel('名称', { exact: true }).fill('我的写作搭档');
await page.getByRole('button', { name: '上传头像', exact: true }).click();
await expect(page.getByRole('img', { name: '我的写作搭档头像', exact: true })).toHaveAttribute('src', avatar.icon_url);
await page.screenshot({ path: testInfo.outputPath('cloud-agent-create-identity.png') });
await page.getByRole('button', { name: '下一步', exact: true }).click();
const editor = page.getByTestId('cloud-agent-editor');
const instructionsTab = page.getByRole('tab', { name: '介绍与提示词', exact: true });
const advancedTab = page.getByRole('tab', { name: '高级设置', exact: true });
const prompt = page.getByLabel('提示词', { exact: true });
const introduction = page.getByLabel('一句话介绍', { exact: true });
const initialPrompt = ' 使用简洁自然的中文。\n先理解我的目的,再协助梳理结构。 ';
await expect(editor).toBeVisible();
await expect(page.getByTestId('titlebar-page-title')).toHaveText('我的智能体');
await expect(page.getByLabel('名称', { exact: true })).toHaveValue('我的写作搭档');
await page.getByRole('button', { name: '修改名称', exact: true }).click();
await expect(page.getByLabel('名称', { exact: true })).toBeFocused();
await expect(page.getByRole('button', { name: '修改头像', exact: true }).getByRole('img')).toHaveAttribute('src', avatar.icon_url);
await expect(introduction).toHaveValue('');
await expect(prompt).toHaveValue('');
await expect(page.getByLabel('模型', { exact: true })).toBeHidden();
await expect(page.getByRole('button', { name: /^(聊天|自动任务|使用与接入)$/ })).toHaveCount(0);
await expect(page.getByRole('region', { name: '草稿预览', exact: true })).toHaveCount(0);
await expect(page.getByLabel('消息', { exact: true })).toHaveCount(0);
await expect(page.getByText(/9\.99|模型说明不应显示在配置页/)).toHaveCount(0);
const created = await fixtureState();
expect(created.createInput).toMatchObject({ name: '我的写作搭档', icon: avatar.icon, purpose: '' });
expect(created.createInput).not.toHaveProperty('icon_url');
expect(await messageOperations()).toEqual([]);
await page.getByRole('button', { name: '修改头像', exact: true }).click();
const avatarDialog = page.getByRole('dialog', { name: '修改头像', exact: true });
await expect(avatarDialog.getByRole('img', { name: '我的写作搭档头像', exact: true })).toHaveAttribute('src', avatar.icon_url);
await avatarDialog.getByRole('button', { name: '完成', exact: true }).click();
await introduction.fill('一起梳理故事和文章');
await prompt.fill(initialPrompt);
await page.getByRole('button', { name: '开始使用', exact: true }).click();
await expect(advancedTab).toHaveAttribute('aria-selected', 'true');
await expect(page.getByRole('navigation', { name: '配置分类', exact: true })).toHaveCount(0);
await expect(page.getByLabel('模型', { exact: true })).toBeVisible();
await expect(page.getByLabel('模型', { exact: true })).toHaveValue('');
await expect(page.getByLabel('模型', { exact: true })).toHaveAttribute('required', '');
await expect(page.getByRole('button', { name: '确认开始使用', exact: true })).toHaveCount(0);
expect((await fixtureState()).operations).not.toContain('publish');
await page.getByLabel('模型', { exact: true }).selectOption('reasoning-model');
await expect(page.getByText('选它做事时能用的工具。', { exact: true })).toHaveCount(0);
await expect(page.getByText('给它一本手册,照着方法做。', { exact: true })).toHaveCount(0);
await expect(page.getByText('管理我的云端能力', { exact: true })).toHaveCount(0);
await expect(page.getByRole('group', { name: /^知识库/ })).toBeVisible();
await expect(page.getByRole('heading', { name: '费用上限', exact: true })).toHaveCount(0);
await expect(page.getByRole('heading', { name: '运行限制', exact: true })).toHaveCount(0);
await page.getByRole('button', { name: '选择工具箱', exact: true }).click();
await page.getByRole('checkbox', { name: '资料整理工具', exact: true }).check();
await instructionsTab.click();
await expect(introduction).toHaveValue('一起梳理故事和文章');
await expect(prompt).toHaveValue(initialPrompt);
await expect(page.getByLabel('模型', { exact: true })).toBeHidden();
await page.getByTestId('sidebar-module-switcher-trigger').click();
await expect(page.getByRole('dialog', { name: '未保存的修改' })).toBeVisible();
await page.getByRole('button', { name: '继续编辑', exact: true }).click();
await expect(prompt).toHaveValue(initialPrompt);
await page.getByRole('button', { name: '保存草稿', exact: true }).click();
await expect(page.getByRole('button', { name: '已保存', exact: true })).toBeDisabled();
expect((await fixtureState()).draft).toMatchObject({ purpose: '一起梳理故事和文章', system_prompt: initialPrompt, configuration: { model: 'reasoning-model', tools: ['fixture-tool'] } });
await editor.getByRole('button', { name: '我的智能体', exact: true }).click();
const card = page.getByRole('article', { name: '我的写作搭档', exact: true });
await expect(card.getByText('一起梳理故事和文章', { exact: true })).toBeVisible();
await card.getByRole('button', { name: '继续制作', exact: true }).click();
await introduction.fill('');
await page.getByRole('button', { name: '保存草稿', exact: true }).click();
await expect(page.getByRole('button', { name: '已保存', exact: true })).toBeDisabled();
await editor.getByRole('button', { name: '我的智能体', exact: true }).click();
await expect(card.getByText('一起梳理故事和文章', { exact: true })).toHaveCount(0);
await expect(card.locator('.agent-home-purpose')).toHaveCount(0);
await card.getByRole('button', { name: '继续制作', exact: true }).click();
await expect(introduction).toHaveValue('');
await expect(prompt).toHaveValue(initialPrompt);
await advancedTab.click();
await expect(page.getByRole('navigation', { name: '配置分类', exact: true })).toHaveCount(0);
await expect(page.getByLabel('模型', { exact: true })).toHaveValue('reasoning-model');
await page.screenshot({ path: testInfo.outputPath('cloud-agent-advanced-model.png') });
await instructionsTab.click();
await app.evaluate(({ BrowserWindow }) => { const win = BrowserWindow.getAllWindows()[0]; win.setMinimumSize(800, 600); win.setSize(980, 680); });
await expect(introduction).toBeInViewport();
await expect(prompt).toBeInViewport();
await expect.poll(() => editor.evaluate(el => el.scrollWidth - el.clientWidth)).toBeLessThanOrEqual(1);
for (const factor of [1.25, 1.5]) {
await app.evaluate(({ BrowserWindow }, zoom) => BrowserWindow.getAllWindows()[0].webContents.setZoomFactor(zoom), factor);
await expect(introduction).toBeInViewport();
await expect(page.getByRole('button', { name: '已保存', exact: true })).toBeInViewport();
await expect.poll(() => editor.evaluate(el => el.scrollWidth - el.clientWidth)).toBeLessThanOrEqual(1);
}
await app.evaluate(({ BrowserWindow }) => { const win = BrowserWindow.getAllWindows()[0]; win.webContents.setZoomFactor(1); win.setSize(1366, 768); });
await page.screenshot({ path: testInfo.outputPath('cloud-agent-introduction-prompt.png') });
expect(await messageOperations()).toEqual([]);
await page.getByRole('button', { name: '开始使用', exact: true }).click();
await expect(page.getByRole('button', { name: '确认开始使用', exact: true })).toHaveCount(0);
const formalChat = page.getByRole('region', { name: '智能体对话', exact: true });
await expect(formalChat).toBeVisible();
expect((await fixtureState()).operations.filter((operation: string) => operation === 'publish')).toHaveLength(1);
expect(await messageOperations()).toEqual([]);
await expect(formalChat.getByRole('heading', { name: 'Hi,今天想聊些什么?', exact: true })).toBeVisible();
await expect(formalChat.locator('.agent-chat-welcome').getByRole('img', { name: '我的写作搭档头像', exact: true })).toHaveAttribute('src', avatar.icon_url);
const conversationSources = page.getByRole('navigation', { name: '对话来源', exact: true });
const sourceBounds = await conversationSources.getByRole('button', { name: '客户端对话', exact: true }).boundingBox();
const adjustBounds = await conversationSources.getByRole('button', { name: '调整智能体', exact: true }).boundingBox();
expect(sourceBounds).not.toBeNull();
expect(adjustBounds).not.toBeNull();
expect(Math.abs(sourceBounds!.y + sourceBounds!.height / 2 - adjustBounds!.y - adjustBounds!.height / 2)).toBeLessThanOrEqual(1);
const example = formalChat.locator('.agent-chat-example').first();
const exampleText = await example.innerText();
await example.click();
await expect(formalChat.getByLabel('消息', { exact: true })).toHaveValue(exampleText);
expect(await messageOperations()).toEqual([]);
await formalChat.getByLabel('消息', { exact: true }).fill('');
await page.screenshot({ path: testInfo.outputPath('cloud-agent-chat-welcome.png') });
await formalChat.getByLabel('消息', { exact: true }).fill('先给我一些创作建议');
await formalChat.getByRole('button', { name: '发送', exact: true }).click();
await expect(formalChat.getByRole('heading', { name: '创作建议' })).toBeAttached();
expect(await messageOperations()).toEqual(['submit']);
await expect(formalChat.locator('.agent-chat-message[data-role="assistant"]').getByRole('img', { name: '我的写作搭档头像', exact: true })).toHaveAttribute('src', avatar.icon_url);
await expect(formalChat.getByLabel('消息', { exact: true })).toBeInViewport();
const transcriptBounds = await formalChat.getByTestId('cloud-chat-messages').boundingBox();
const composerBounds = await formalChat.getByTestId('cloud-chat-composer').boundingBox();
expect(transcriptBounds).not.toBeNull();
expect(composerBounds).not.toBeNull();
expect(transcriptBounds!.y + transcriptBounds!.height).toBeLessThanOrEqual(composerBounds!.y + 1);
await page.screenshot({ path: testInfo.outputPath('cloud-agent-chat-long-reply.png') });
await app.evaluate(({ BrowserWindow }) => { const win = BrowserWindow.getAllWindows()[0]; win.setSize(980, 680); win.webContents.setZoomFactor(1.5); });
await expect(formalChat.getByTestId('cloud-chat-composer')).toBeInViewport({ ratio: 1 });
await expect(formalChat.getByRole('button', { name: '发送', exact: true })).toBeInViewport({ ratio: 1 });
await expect(conversationSources.getByRole('button', { name: '调整智能体', exact: true })).toBeInViewport({ ratio: 1 });
await expect.poll(() => editor.evaluate(el => el.scrollWidth - el.clientWidth)).toBeLessThanOrEqual(1);
await expect.poll(() => formalChat.getByTestId('cloud-chat-messages').evaluate(el => el.clientHeight)).toBeGreaterThanOrEqual(80);
// Chromium screenshots can crop a zoomed Electron surface. Capture the
// isolated native fixture's complete window for the responsive visual check.
const compactWindowImage = await app.evaluate(async ({ BrowserWindow }) => {
const image = await BrowserWindow.getAllWindows()[0].capturePage();
return image.toPNG().toString('base64');
});
await writeFile(testInfo.outputPath('cloud-agent-chat-compact-window.png'), Buffer.from(compactWindowImage, 'base64'));
await app.evaluate(({ BrowserWindow }) => { const win = BrowserWindow.getAllWindows()[0]; win.webContents.setZoomFactor(1); win.setSize(1366, 768); });
await page.getByRole('button', { name: '调整智能体', exact: true }).click();
await expect(prompt).toHaveValue(initialPrompt);
await page.getByLabel('名称', { exact: true }).fill('我的新版写作搭档');
await expect(page.getByRole('button', { name: '应用最新修改', exact: true })).toBeDisabled();
await page.getByRole('button', { name: '保存草稿', exact: true }).click();
await expect(page.getByText('修改已保存,尚未应用', { exact: true })).toBeVisible();
await page.getByRole('button', { name: '应用最新修改', exact: true }).click();
await expect(page.getByRole('button', { name: '已应用', exact: true })).toBeDisabled();
await editor.getByRole('button', { name: '我的智能体', exact: true }).click();
await page.getByRole('article', { name: '我的新版写作搭档', exact: true }).getByRole('button', { name: '和它聊聊', exact: true }).click();
await expect(formalChat).toBeVisible();
await expect(page.getByRole('button', { name: /^(聊天|自动任务|使用与接入)$/ })).toHaveCount(0);
expect(await messageOperations()).toEqual(['submit']);
await page.screenshot({ path: testInfo.outputPath('cloud-agent-separate-chat.png') });
const operationsBeforeReentry = (await fixtureState()).operations;
await page.getByRole('button', { name: '返回首页', exact: true }).click();
await page.getByTestId('ai-module-option-cloud_agents').click();
await expect(page.getByRole('article', { name: '我的新版写作搭档', exact: true })).toBeVisible();
await expect(editor).toHaveCount(0);
await expect(formalChat).toHaveCount(0);
await page.screenshot({ path: testInfo.outputPath('cloud-agent-home-entry-navigation.png') });
await page.getByRole('article', { name: '我的新版写作搭档', exact: true }).getByRole('button', { name: '和它聊聊', exact: true }).click();
await expect(formalChat).toBeVisible();
expect(await messageOperations()).toEqual(['submit']);
const operationsAfterReentry = (await fixtureState()).operations;
expect(operationsAfterReentry.filter((operation: string) => operation === 'publish')).toEqual(operationsBeforeReentry.filter((operation: string) => operation === 'publish'));
} finally {
await app.evaluate(({ BrowserWindow }) => { for (const win of BrowserWindow.getAllWindows()) win.destroy(); }).catch(() => undefined);
await closeElectronApp(app);
}
});