109 lines
12 KiB
TypeScript
109 lines
12 KiB
TypeScript
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 });
|
|
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 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 };
|
|
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], next_cursor: null } : 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 === 'channelBindings') return result({ items: [channel], available_accounts: [] });
|
|
if (operation === 'channelConversations') return result({ items: [session], next_offset: null });
|
|
if (operation === 'channelCallers' || 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') return result({ session_key: 'qr-session', status: verified ? 'confirmed' : 'verification_required', qrcode_url: 'https://login.example.test/confirm?token=fixture', message: '请确认微信验证码' });
|
|
if (operation === 'wechatBindVerification') { verified = true; return result({ session_key: 'qr-session', status: 'confirmed', message: '连接已确认' }); }
|
|
if (operation === 'createChannelPairing') return result({ invitation_id: 'pairing-a', binding_id: 'binding-a', provider_generation: 'generation-a', agent_slug: slug, kind: input.kind, expires_at: '2026-09-13T23:00:00Z', consumed: false, code: 'TEST1234' });
|
|
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 own.getByRole('button', { name: '允许执行', exact: true }).click();
|
|
await expect(own.getByRole('heading', { name: '演示文稿已完成' })).toBeVisible();
|
|
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<string, unknown> }[] }).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();
|
|
await expect(page.getByText('创作微信', { exact: true }).first()).toBeVisible();
|
|
await page.getByRole('region', { name: '个人微信渠道', exact: true }).scrollIntoViewIfNeeded();
|
|
await page.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-qr-verification.png') });
|
|
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 === 'wechatBindVerification'))).toBe(true);
|
|
if (await page.getByRole('button', { name: '关闭二维码', exact: true }).count()) await page.getByRole('button', { name: '关闭二维码', exact: true }).click();
|
|
await page.getByRole('button', { name: '使用权限', exact: true }).click();
|
|
await page.getByRole('button', { name: '生成本人配对码', exact: true }).click();
|
|
await expect(page.getByText('TEST1234', { exact: true })).toBeVisible();
|
|
await page.getByRole('button', { name: '隐藏', exact: true }).click();
|
|
await page.getByRole('button', { name: '连接微信', exact: true }).click();
|
|
await page.screenshot({ path: testInfo.outputPath('wechat-publication.png') });
|
|
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.getByRole('heading', { name: '发布到个人微信', exact: true }).scrollIntoViewIfNeeded();
|
|
await page.screenshot({ path: testInfo.outputPath('wechat-publication-compact.png') });
|
|
await page.getByRole('button', { name: '自动任务', exact: true }).click();
|
|
await page.getByRole('button', { name: '添加任务', exact: true }).click();
|
|
await expect(page.getByLabel('结果发送到')).toHaveValue('');
|
|
await expect(page.getByRole('option', { name: '创作微信 · 本人对话 1' })).toBeAttached();
|
|
await page.getByLabel('结果发送到').selectOption('self-caller');
|
|
await page.getByLabel('结果发送到').scrollIntoViewIfNeeded();
|
|
await page.screenshot({ path: testInfo.outputPath('wechat-schedule-target.png') });
|
|
} finally {
|
|
await app.evaluate(({ BrowserWindow }) => { for (const window of BrowserWindow.getAllWindows()) window.destroy(); }).catch(() => undefined);
|
|
await closeElectronApp(app);
|
|
}
|
|
});
|