import { closeElectronApp, expect, getStableWindow, test } from './fixtures/electron'; test('own WeChat conversation approval and generated PPT delivery in the desktop workspace', async ({ launchElectronApp }, testInfo) => { const app = await launchElectronApp({ skipSetup: true }); const agentSlug = 'ml-' + 'a'.repeat(32); const secondAgentSlug = 'ml-' + 'b'.repeat(32); try { const page = await getStableWindow(app); await expect(page.getByTestId('ai-module-selection-page')).toBeVisible(); const applicationUrl = page.url(); await page.goto('about:blank'); await app.evaluate(({ ipcMain, BrowserWindow }) => { BrowserWindow.getAllWindows()[0].setSize(1366, 900); const calls: { operation: string; input: unknown }[] = []; Object.assign(globalThis, { cloudChannelFixtureCalls: calls }); let approved = false; let verified = false; const slug = 'ml-' + 'a'.repeat(32); const configuration = { model: 'test-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 draft = { slug, name: '我的创作助手', purpose: '整理选题与制作演示文稿', system_prompt: '使用简洁自然的中文', draft_revision: 2, updated_at: '2026-09-13T00:00:00Z', configuration, published_version: 1, enabled: true, archived: false }; const secondSlug = 'ml-' + 'b'.repeat(32); const secondDraft = { ...draft, slug: secondSlug, name: '资料整理助手', draft_revision: 1, published_version: 1 }; const session = { session_id: 'self-session', caller_id: 'self-caller', agent_slug: slug, agent_name: draft.name, binding_id: 'binding-a', provider_generation: 'generation-a', channel_account_id: 'wechat-a', access_mode: 'self_only', grant_state: 'authorized', session_state: 'active', sequence: 1, thread_id: 'channel-thread', created_at: '2026-09-13T01:00:00Z' }; const channel = { id: 'wechat-a', address: 'wechat:personal', display_name: '创作微信', target_agent_address: 'yuxi:makelore:' + slug, route_revision: 3, provider_generation: 'generation-a', binding_id: 'binding-a', enabled: true, status: 'active', worker_online: true, agent_slug: slug, published_version: 1, desired_state: 'enabled', sync_state: 'ready', health: 'connected', last_confirmed_at: '2026-09-13T01:00:00Z', revision: 3, blockers: [], access_mode: 'self_only', policy_revision: 0, target_agent_slug: slug, target_agent_name: draft.name, target_agent_published_version: 1 }; const spare = { id: 'wechat-b', address: 'wechat:spare', display_name: '备用微信', target_agent_address: null, route_revision: 1, provider_generation: 'generation-b', binding_id: 'binding-b', enabled: false, status: 'paused', worker_online: true, agent_slug: null, published_version: null, desired_state: 'disabled', sync_state: 'ready', health: 'login_required', last_confirmed_at: null, revision: 1, blockers: ['target_required'], access_mode: 'self_only', policy_revision: 0, target_agent_slug: null, target_agent_name: null, target_agent_published_version: null }; let channelAccounts = [channel, spare]; 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 ?? ''; 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-13T00:00:00Z' } }); if (!path.startsWith('/api/cloud-agents/')) return result({ success: true }); const body = typeof request.body === 'string' ? JSON.parse(request.body) : request.body ?? {}; if (path.endsWith('/recovery')) return result({ recent: null, pending: [] }); if (path.endsWith('/recovery/recent')) return result({ saved: true }); if (path.endsWith('/files/save-channel')) { calls.push({ operation: 'downloadChannelArtifact', input: body }); return result({ saved: true }); } if (!path.endsWith('/actions')) return result(path.endsWith('/agents') || path.endsWith('/bootstrap') ? { agents: [draft, secondDraft], next_cursor: null } : path.endsWith(secondSlug) ? secondDraft : draft); const { operation, input = {} } = body; calls.push({ operation, input }); if (operation === 'threads') return result({ threads: [], next_offset: null }); if (operation === 'history') return result({ thread_id: 'desktop-thread', messages: [], run: null, next_offset: null }); if (operation === 'attachments' || operation === 'files') return result({ attachments: [], files: [] }); if (operation === 'catalog') return result({ models: [{ id: 'test-model', name: '创作模型' }], resources: {}, pricing: null }); if (operation === 'access') return result({ published_version: 1, enabled: true, versions: [], grants: [], applications: [], share_url: 'niancode://agents/' + slug }); if (operation === 'budget') return result({ request_limit_points: '2.50', daily_limit_points: '10.00', daily_committed_points: '0.20' }); if (operation === 'costs') return result({ items: [], next_offset: null, summary: { count: 0, settled_points: '0', pending_points: '0' } }); if (operation === 'channelAccounts') return result({ items: channelAccounts }); if (operation === 'routeChannelAccount') { const target = input.target_agent_slug === secondSlug ? secondDraft : input.target_agent_slug === slug ? draft : null; channelAccounts = channelAccounts.map(item => item.id !== input.channel_account_id ? item : { ...item, revision: item.revision + 1, enabled: input.enabled, target_agent_slug: target?.slug ?? null, target_agent_name: target?.name ?? null, target_agent_published_version: target?.published_version ?? null, agent_slug: target?.slug ?? null, published_version: target?.published_version ?? null, target_agent_address: target ? 'yuxi:makelore:' + target.slug : null, blockers: target ? [] : ['target_required'] }); return result({ channel: channelAccounts.find(item => item.id === input.channel_account_id), target_agent: target?.slug ?? null }); } if (operation === 'enableChannelAccount' || operation === 'pauseChannelAccount') { const enabled = operation === 'enableChannelAccount'; channelAccounts = channelAccounts.map(item => item.id !== input.channel_account_id ? item : { ...item, revision: item.revision + 1, enabled, desired_state: enabled ? 'enabled' : 'disabled' }); return result({ channel: channelAccounts.find(item => item.id === input.channel_account_id) }); } if (operation === 'disconnectChannelAccount') return result({ channel: { ...channelAccounts.find(item => item.id === input.channel_account_id), health: 'login_required', enabled: false }, disconnected: true }); if (operation === 'channelBindings') return result({ items: channelAccounts.filter(item => item.target_agent_slug === input.slug), available_accounts: [] }); if (operation === 'channelConversations') return result({ items: [session], next_offset: null }); if (operation === 'channelSelfCallers') return result({ items: [{ ...session, paired_ws_account_id: 'creator', revoked_at: null }] }); if (operation === 'channelActivity') return result({ items: [], next_offset: null }); if (operation === 'wechatBindStart' || operation === 'wechatBindStatus' || operation === 'channelAccountWechatBindStart' || operation === 'channelAccountWechatBindStatus') return result({ session_key: 'qr-session', status: verified ? 'confirmed' : 'verification_required', qrcode_url: 'https://login.example.test/confirm?token=fixture', message: '请确认微信验证码' }); if (operation === 'wechatBindVerification' || operation === 'channelAccountWechatBindVerification') { verified = true; channelAccounts = channelAccounts.map(item => item.id === input.channel_account_id ? { ...item, health: 'connected' } : item); return result({ session_key: 'qr-session', status: 'confirmed', message: '连接已确认' }); } if (operation === 'channelConversation') return result({ ...session, messages: [{ id: 1, role: 'user', content: '把今天的创作建议整理成演示文稿。' }, ...(approved ? [{ id: 2, role: 'assistant', content: '## 演示文稿已完成\n共 7 页,文件已保存在本次会话中。' }] : [])], queued_requests: [], next_offset: null, run: { agent_run_id: 'channel-run', request_id: 'channel-request', thread_id: 'channel-thread', agent_slug: slug, status: approved ? 'completed' : 'interrupted', output: '', version: '1', ...(approved ? {} : { interrupt: { approval: { action_requests: [{ name: 'execute', args: { command: 'python create_presentation.py' } }] } } }) } }); if (operation === 'channelConversationControl') { approved = true; return result({ session_id: session.session_id, operation_id: input.operation_id, action: input.action, status: 'accepted', run_id: 'channel-run' }); } if (operation === 'channelFiles') return result({ session_id: session.session_id, thread_id: session.thread_id, files: !approved ? [] : input.path === '/' ? [{ name: 'outputs', path: '/outputs', directory_path: '/outputs', is_dir: true, size: 0 }] : [{ name: '创作建议.pptx', path: '/outputs/创作建议.pptx', directory_path: '/outputs', is_dir: false, size: 40960 }] }); if (operation === 'schedules') return result({ jobs: [] }); if (operation === 'scheduleContext') return result({ version: 1, enabled: true, configuration, result_destination: '任务历史', payer: 'creator' }); throw new Error('Unexpected channel UI fixture operation ' + operation); }); }); await page.goto(applicationUrl); await page.getByTestId('ai-module-option-cloud_agents').click(); await page.getByRole('button', { name: /我的创作助手/ }).click(); await page.getByRole('button', { name: '微信对话', exact: true }).click(); const own = page.getByRole('region', { name: '微信会话', exact: true }); await expect(own.getByTestId('wechat-run-status')).toContainText('任务状态:等待你的确认'); await own.getByRole('button', { name: '允许执行', exact: true }).click(); await expect(own.getByRole('heading', { name: '演示文稿已完成' })).toBeVisible(); await expect(own.getByTestId('wechat-run-status')).toContainText('任务状态:已完成'); await own.getByRole('button', { name: '保存 创作建议.pptx', exact: true }).click(); await expect(own.getByText('已保存 创作建议.pptx', { exact: true })).toBeVisible(); await page.screenshot({ path: testInfo.outputPath('wechat-own-deliverables.png') }); const calls = await app.evaluate(() => (globalThis as typeof globalThis & { cloudChannelFixtureCalls: { operation: string; input: Record }[] }).cloudChannelFixtureCalls); expect(calls.find(call => call.operation === 'channelConversationControl')?.input).toMatchObject({ session_id: 'self-session', run_id: 'channel-run', action: 'resume', decision: { decisions: [{ type: 'approve' }] } }); expect(calls.find(call => call.operation === 'downloadChannelArtifact')?.input).toEqual({ session_id: 'self-session', path: '/outputs/创作建议.pptx' }); expect(calls.some(call => call.operation === 'resume' || call.operation === 'submit')).toBe(false); await page.getByRole('button', { name: '发布与访问', exact: true }).click(); const linked = page.getByRole('region', { name: '已关联渠道', exact: true }); await expect(linked.getByText('创作微信', { exact: true })).toBeVisible(); await expect(page.getByRole('region', { name: '个人微信渠道', exact: true })).toHaveCount(0); await linked.scrollIntoViewIfNeeded(); await page.screenshot({ path: testInfo.outputPath('agent-linked-channels-shortcut.png') }); await linked.getByRole('button', { name: /创作微信/ }).click(); await expect(page.getByRole('heading', { name: '微信', exact: true })).toBeVisible(); await expect(page.getByRole('button', { name: /创作微信.*我的创作助手/ })).toBeVisible(); await expect(page.getByRole('button', { name: /备用微信.*未分配智能体/ })).toBeVisible(); await page.screenshot({ path: testInfo.outputPath('wechat-account-list.png'), fullPage: true }); await page.getByRole('button', { name: /备用微信.*未分配智能体/ }).click(); const spareDetail = page.getByRole('article', { name: '备用微信账号详情', exact: true }); await spareDetail.getByRole('button', { name: '扫码连接', exact: true }).click(); await expect(page.getByRole('img', { name: '个人微信登录二维码', exact: true })).toHaveAttribute('src', /^data:image\//); await page.getByLabel('微信验证码', { exact: true }).scrollIntoViewIfNeeded(); await page.screenshot({ path: testInfo.outputPath('wechat-unassigned-qr-and-target.png'), fullPage: true }); await page.getByLabel('微信验证码', { exact: true }).fill('123456'); await page.getByRole('button', { name: '提交验证码', exact: true }).click(); await expect.poll(async () => app.evaluate(() => (globalThis as typeof globalThis & { cloudChannelFixtureCalls: { operation: string; input: unknown }[] }).cloudChannelFixtureCalls.some(call => call.operation === 'channelAccountWechatBindVerification'))).toBe(true); if (await page.getByRole('button', { name: '关闭二维码', exact: true }).count()) await page.getByRole('button', { name: '关闭二维码', exact: true }).click(); await spareDetail.getByLabel('目标智能体', { exact: true }).selectOption(agentSlug); await spareDetail.getByRole('button', { name: '保存目标', exact: true }).click(); await expect.poll(async () => app.evaluate(() => (globalThis as typeof globalThis & { cloudChannelFixtureCalls: { operation: string; input: Record }[] }).cloudChannelFixtureCalls .filter(call => call.operation === 'routeChannelAccount').some(call => call.input.channel_account_id === 'wechat-b' && call.input.target_agent_slug === 'ml-' + 'a'.repeat(32)))).toBe(true); await expect(page.getByRole('button', { name: /备用微信.*我的创作助手/ })).toBeVisible(); await page.getByRole('button', { name: /创作微信.*我的创作助手/ }).click(); const legacyDetail = page.getByRole('article', { name: '创作微信账号详情', exact: true }); const qrStartsBeforeSwitch = await app.evaluate(() => (globalThis as typeof globalThis & { cloudChannelFixtureCalls: { operation: string }[] }).cloudChannelFixtureCalls.filter(call => call.operation === 'channelAccountWechatBindStart').length); await legacyDetail.getByLabel('目标智能体', { exact: true }).selectOption(secondAgentSlug); await legacyDetail.getByRole('button', { name: '保存目标', exact: true }).click(); await expect.poll(async () => app.evaluate(() => (globalThis as typeof globalThis & { cloudChannelFixtureCalls: { operation: string; input: Record }[] }).cloudChannelFixtureCalls .filter(call => call.operation === 'routeChannelAccount').some(call => call.input.channel_account_id === 'wechat-a' && call.input.target_agent_slug === 'ml-' + 'b'.repeat(32) && call.input.enabled === true))).toBe(true); expect(await app.evaluate(() => (globalThis as typeof globalThis & { cloudChannelFixtureCalls: { operation: string }[] }).cloudChannelFixtureCalls.filter(call => call.operation === 'channelAccountWechatBindStart').length)).toBe(qrStartsBeforeSwitch); await expect(page.getByRole('button', { name: '活动记录', exact: true })).toBeVisible(); await expect(page.getByRole('button', { name: '微信对话', exact: true })).toBeVisible(); await expect(page.getByText(/本人|受邀|邀请|使用范围|使用权限|授权调用者/)).toHaveCount(0); await app.evaluate(({ BrowserWindow }) => { const win = BrowserWindow.getAllWindows()[0]; win.setMinimumSize(800, 600); win.setSize(980, 680); }); await expect.poll(() => page.getByTestId('main-content').evaluate(element => element.scrollWidth - element.clientWidth)).toBeLessThanOrEqual(1); await page.screenshot({ path: testInfo.outputPath('wechat-account-detail-compact.png'), fullPage: true }); expect(await app.evaluate(() => (globalThis as typeof globalThis & { cloudChannelFixtureCalls: { operation: string }[] }).cloudChannelFixtureCalls.some(call => ['channelPolicy', 'createChannelPairing', 'channelCallers', 'revokeChannelCaller'].includes(call.operation)))).toBe(false); } finally { await app.evaluate(({ BrowserWindow }) => { for (const window of BrowserWindow.getAllWindows()) window.destroy(); }).catch(() => undefined); await closeElectronApp(app); } });