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 | null = null; let recent: unknown = null; let limits: { request_limit_points: string | null; daily_limit_points: string | null } = { request_limit_points: null, daily_limit_points: null }; const jobs: Record[] = []; 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('/recovery')) return result({ recent, pending: [] }); if (path.endsWith('/recovery/recent')) { recent = body; return result({ saved: true }); } if (path.endsWith('/actions')) { const input = body.input ?? {}; if (body.operation === 'catalog') return result({ models: [{ id: 'test-model', name: '创作模型' }], resources: {}, pricing: null }); if (body.operation === 'knowledge') return result({ databases: [], models: [] }); 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 === 'threads') return result({ threads: [], next_offset: null }); if (body.operation === 'publish') { if (draft) draft.published_version = 1; return result({ version: 1, draft_revision: 2 }); } 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先从一个真实的观察开始。' }], 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') 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', configuration, published_version: null, enabled: true, }; if (method === 'PATCH') draft = { ...draft, name: body.name, purpose: body.purpose, system_prompt: body.system_prompt, configuration: body.configuration, 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.getByLabel('模型', { exact: true }).selectOption('test-model'); 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 expect(page.getByTestId('cloud-agent-editor')).toBeVisible(); await expect(page.getByLabel('角色与指令')).toHaveValue('使用简洁自然的中文。先理解我的目的,再协助梳理结构。'); await page.getByRole('button', { name: '发布与访问', exact: true }).click(); await page.getByLabel('每次任务上限(词元点数)').fill('2.50'); await page.getByLabel('每日上限(词元点数)').fill('10.00'); await page.getByRole('button', { name: '保存费用上限' }).click(); await expect(page.getByRole('button', { name: '保存费用上限' })).toBeDisabled(); await page.getByRole('button', { name: '发布当前草稿' }).click(); await expect(page.getByText('当前发布版本 1')).toBeVisible(); await page.getByRole('button', { name: '对话', exact: true }).click(); await page.getByLabel('消息', { exact: true }).fill('每天早上八点半给我三个创作方向'); await page.getByRole('button', { name: '发送', exact: true }).click(); await expect(page.getByRole('heading', { name: '创作建议' })).toBeVisible(); await page.getByRole('button', { name: '查看并配置自动任务' }).click(); await expect(page.getByLabel('任务名称')).toHaveValue('每天整理选题'); await expect(page.getByLabel('执行时间')).toHaveValue('08:30'); await expect(page.getByLabel('保存后启用')).not.toBeChecked(); await page.getByLabel('保存后启用').check(); await expect(page.getByText('启用前确认')).toBeVisible(); await page.getByRole('button', { name: '保存并启用' }).click(); await expect(page.getByRole('heading', { name: '每天整理选题' })).toBeVisible(); await expect(page.getByText(/每天 08:30/)).toBeVisible(); await page.getByRole('button', { name: '添加任务', exact: true }).click(); await expect(page.getByLabel('保存后启用')).not.toBeChecked(); await page.getByRole('button', { name: '取消', exact: true }).click(); await page.screenshot({ path: testInfo.outputPath('cloud-agent-schedules.png') }); } finally { await closeElectronApp(app); } });