Files
makelore/tests/unit/channel-accounts-page.test.tsx
T

395 lines
27 KiB
TypeScript

import { act, cleanup, fireEvent, render, screen, waitFor, within } from '@testing-library/react';
import { MemoryRouter } from 'react-router-dom';
import { afterEach, beforeEach, expect, it, vi } from 'vitest';
import { ChannelAccounts } from '@/pages/CloudAgents/ChannelAccounts';
import { EMPTY_CLOUD_CONFIGURATION, type CloudAgentDraft, type CloudUserChannelAccount, type CloudWechatBindState } from '../../shared/cloud-agents';
const api = vi.hoisted(() => ({ call: vi.fn(), list: vi.fn(), recovery: vi.fn(), resolvePending: vi.fn() }));
vi.mock('@/lib/cloud-agents-api', () => ({ cloudAgentsApi: api }));
vi.mock('@/pages/CloudAgents/CloudWechatQr', () => ({ CloudWechatQr: () => <img alt="个人微信登录二维码" src="data:image/png;base64,test" /> }));
const agentA: CloudAgentDraft = { slug: 'ml-' + 'a'.repeat(32), name: '写作助手', purpose: '', system_prompt: '', published_version: 2, draft_revision: 3, updated_at: '', configuration: EMPTY_CLOUD_CONFIGURATION, enabled: true };
const agentB: CloudAgentDraft = { ...agentA, slug: 'ml-' + 'b'.repeat(32), name: '资料助手', published_version: 4, draft_revision: 5 };
const legacy: CloudUserChannelAccount = { id: 'wechat-legacy', display_name: '原有微信', binding_id: 'binding-legacy', revision: 7, enabled: true,
status: 'active', health: 'connected', blockers: [], target_agent_slug: agentA.slug, target_agent_name: agentA.name,
target_agent_published_version: 2, provider_generation: 'generation-legacy' };
const spare: CloudUserChannelAccount = { id: 'wechat-spare', display_name: '备用微信', binding_id: 'binding-spare', revision: 2, enabled: false,
status: 'paused', health: 'login_required', blockers: ['target_required'], target_agent_slug: null, target_agent_name: null,
target_agent_published_version: null, provider_generation: 'generation-spare' };
let accounts: CloudUserChannelAccount[];
let agents: CloudAgentDraft[];
let qrStatus: CloudWechatBindState['status'];
let verifyStatus: CloudWechatBindState['status'];
const calls = (operation: string) => api.call.mock.calls.filter(([name]) => name === operation);
const writes = () => api.call.mock.calls.filter(([name]) => !['channelAccounts', 'channelAccountWechatBindStatus'].includes(name));
function renderPage(entry = '/cloud-agents/channels') { return render(<MemoryRouter initialEntries={[entry]}><ChannelAccounts /></MemoryRouter>); }
async function addWechat(fakeTimers = false) {
fireEvent.click(await screen.findByRole('button', { name: '添加微信', exact: true }));
const dialog = screen.getByRole('dialog', { name: '添加微信', exact: true });
fireEvent.change(within(dialog).getByLabelText('选择智能体', { exact: true }), { target: { value: agentA.slug } });
fireEvent.change(within(dialog).getByLabelText('微信备注(可选)', { exact: true }), { target: { value: '创作微信' } });
if (fakeTimers) vi.useFakeTimers();
await act(async () => fireEvent.click(within(dialog).getByRole('button', { name: '下一步:扫码连接', exact: true })));
return dialog;
}
async function manage(name = legacy.display_name) {
const card = await screen.findByRole('article', { name: name + '微信', exact: true });
fireEvent.click(within(card).getByRole('button', { name: /管理绑定|继续绑定/ }));
return screen.getByRole('dialog', { name: '管理微信绑定', exact: true });
}
function installMock() {
api.call.mockImplementation(async (operation: string, input: Record<string, unknown>) => {
if (operation === 'channelAccounts') return { items: accounts.map(item => ({ ...item })) };
if (operation === 'createChannelAccount') {
const channel = { ...spare, id: 'wechat-created', display_name: String(input.display_name), revision: 1 };
accounts.push(channel); return { channel };
}
const account = accounts.find(item => item.id === input.channel_account_id);
if (!account) throw new Error('Unknown fixture account');
if (operation === 'routeChannelAccount') {
const target = agents.find(item => item.slug === input.target_agent_slug);
Object.assign(account, { revision: account.revision + 1, target_agent_slug: target?.slug ?? null,
target_agent_name: target?.name ?? null, target_agent_published_version: target?.published_version ?? null,
enabled: input.enabled, blockers: target ? [] : ['target_required'] });
return { channel: { ...account } };
}
if (operation === 'enableChannelAccount' || operation === 'pauseChannelAccount') {
Object.assign(account, { revision: account.revision + 1, enabled: operation === 'enableChannelAccount' });
return { channel: { ...account } };
}
if (operation === 'disconnectChannelAccount') {
Object.assign(account, { revision: account.revision + 1, health: 'login_required', enabled: false });
return { channel: { ...account }, disconnected: true };
}
if (['channelAccountWechatBindStart', 'channelAccountWechatBindStatus', 'channelAccountWechatBindVerification'].includes(operation)) {
const status = operation === 'channelAccountWechatBindVerification' ? verifyStatus : qrStatus;
if (status === 'confirmed') account.health = 'connected';
return { session_key: 'qr-' + account.id, status, qrcode_url: 'https://login.example.test/qr' };
}
throw new Error('Unexpected fixture operation ' + operation);
});
}
beforeEach(() => {
vi.resetAllMocks();
accounts = [{ ...legacy }, { ...spare }]; agents = [agentA, agentB];
qrStatus = 'verification_required'; verifyStatus = 'pending';
api.list.mockImplementation(async () => ({ agents, next_cursor: null }));
api.recovery.mockResolvedValue({ recent: null, pending: [] });
installMock();
});
afterEach(() => { cleanup(); vi.useRealTimers(); });
it('shows binding cards and never fetches or embeds conversation or activity panels', async () => {
renderPage();
const card = await screen.findByRole('article', { name: '原有微信微信', exact: true });
expect(within(card).getByText(agentA.name, { exact: true })).toBeVisible();
expect(within(card).getByRole('button', { name: '管理绑定', exact: true })).toBeVisible();
expect(await screen.findByRole('article', { name: '备用微信微信', exact: true })).toBeVisible();
expect(screen.queryByRole('dialog')).not.toBeInTheDocument();
expect(screen.queryByText('微信对话', { exact: true })).not.toBeInTheDocument();
expect(screen.queryByText('活动记录', { exact: true })).not.toBeInTheDocument();
expect(writes()).toEqual([]);
expect(api.call.mock.calls.every(([operation]) => operation === 'channelAccounts')).toBe(true);
});
it('offers only available published Agents and needs a selection before connecting', async () => {
agents.push({ ...agentA, slug: 'ml-' + 'c'.repeat(32), name: '尚未制作好', published_version: null },
{ ...agentA, slug: 'ml-' + 'd'.repeat(32), name: '已停用助手', enabled: false });
renderPage();
fireEvent.click(await screen.findByRole('button', { name: '添加微信', exact: true }));
const dialog = screen.getByRole('dialog', { name: '添加微信', exact: true });
expect(within(dialog).getByRole('button', { name: '下一步:扫码连接', exact: true })).toBeDisabled();
expect(within(dialog).queryByRole('option', { name: /尚未制作好|已停用助手/ })).not.toBeInTheDocument();
expect(within(dialog).getByRole('option', { name: agentA.name, exact: true })).toBeInTheDocument();
expect(writes()).toEqual([]);
});
it('creates, associates then requests a QR without enabling the channel', async () => {
renderPage(); const dialog = await addWechat();
expect(await within(dialog).findByRole('img', { name: '个人微信登录二维码' })).toBeVisible();
expect(writes().map(([operation]) => operation)).toEqual(['createChannelAccount', 'routeChannelAccount', 'channelAccountWechatBindStart']);
expect(calls('routeChannelAccount')[0][1]).toMatchObject({ channel_account_id: 'wechat-created', target_agent_slug: agentA.slug, expected_revision: 1, enabled: false });
expect(accounts.find(item => item.id === 'wechat-created')).toMatchObject({ enabled: false });
expect(calls('enableChannelAccount')).toHaveLength(0);
});
it('keeps a pending verification visible and cannot claim usable or enable before confirmation', async () => {
renderPage(); const dialog = await addWechat();
const input = await within(dialog).findByLabelText('微信验证码', { exact: true });
fireEvent.change(input, { target: { value: '123456' } });
fireEvent.click(within(dialog).getByRole('button', { name: '提交验证码', exact: true }));
await waitFor(() => expect(calls('channelAccountWechatBindVerification')).toHaveLength(1));
expect(within(dialog).getByRole('img', { name: '个人微信登录二维码' })).toBeVisible();
expect(within(dialog).getByRole('button', { name: '完成绑定并启用', exact: true })).toBeDisabled();
expect(calls('enableChannelAccount')).toHaveLength(0);
});
it('closes a confirmed QR, enables only with an explicit click and reports success afterward', async () => {
verifyStatus = 'confirmed';
renderPage(); const dialog = await addWechat();
fireEvent.change(await within(dialog).findByLabelText('微信验证码', { exact: true }), { target: { value: '123456' } });
fireEvent.click(within(dialog).getByRole('button', { name: '提交验证码', exact: true }));
expect(await within(dialog).findByText('微信已连接', { exact: true })).toBeVisible();
expect(within(dialog).queryByRole('img', { name: '个人微信登录二维码' })).not.toBeInTheDocument();
expect(within(dialog).queryByLabelText('微信验证码')).not.toBeInTheDocument();
expect(calls('enableChannelAccount')).toHaveLength(0);
fireEvent.click(within(dialog).getByRole('button', { name: '完成绑定并启用', exact: true }));
await waitFor(() => expect(calls('enableChannelAccount')).toHaveLength(1));
expect(await within(dialog).findByRole('button', { name: '完成', exact: true })).toBeEnabled();
fireEvent.click(within(dialog).getByRole('button', { name: '完成', exact: true }));
expect(screen.queryByRole('dialog')).not.toBeInTheDocument();
expect(accounts.find(item => item.id === 'wechat-created')).toMatchObject({ enabled: true, health: 'connected', target_agent_slug: agentA.slug });
});
it('does not report binding success when enable fails after the QR is confirmed', async () => {
qrStatus = 'confirmed';
const base = api.call.getMockImplementation()!;
api.call.mockImplementation(async (operation, input) => { if (operation === 'enableChannelAccount') throw new Error('启用失败'); return base(operation, input); });
renderPage(); const dialog = await addWechat();
fireEvent.click(await within(dialog).findByRole('button', { name: '完成绑定并启用', exact: true }));
expect(await within(dialog).findByText(/启用失败/)).toBeVisible();
expect(within(dialog).queryByRole('button', { name: '完成', exact: true })).not.toBeInTheDocument();
expect(accounts.find(item => item.id === 'wechat-created')?.enabled).toBe(false);
});
it('ends QR polling once connected and does not automatically enable', async () => {
renderPage(); const dialog = await addWechat(true);
expect(within(dialog).getByRole('img', { name: '个人微信登录二维码' })).toBeVisible();
qrStatus = 'confirmed';
await act(async () => { await vi.advanceTimersByTimeAsync(2000); });
expect(within(dialog).queryByRole('img', { name: '个人微信登录二维码' })).not.toBeInTheDocument();
expect(within(dialog).getByRole('button', { name: '完成绑定并启用', exact: true })).toBeVisible();
await act(async () => { await vi.advanceTimersByTimeAsync(6000); });
expect(calls('channelAccountWechatBindStatus')).toHaveLength(1);
expect(calls('enableChannelAccount')).toHaveLength(0);
});
it('changes an existing association only after confirmation and keeps its login and enabled state', async () => {
renderPage(); const dialog = await manage();
fireEvent.change(within(dialog).getByLabelText('绑定的智能体', { exact: true }), { target: { value: agentB.slug } });
fireEvent.click(within(dialog).getByRole('button', { name: '保存更换', exact: true }));
expect(calls('routeChannelAccount')).toHaveLength(0);
const confirmation = screen.getByRole('dialog', { name: '更换绑定的智能体', exact: true });
fireEvent.click(within(confirmation).getByRole('button', { name: /确认更换/ }));
await waitFor(() => expect(calls('routeChannelAccount')).toHaveLength(1));
expect(calls('routeChannelAccount')[0][1]).toMatchObject({ channel_account_id: legacy.id, target_agent_slug: agentB.slug, expected_revision: 7, enabled: true });
expect(calls('createChannelAccount')).toHaveLength(0);
expect(calls('channelAccountWechatBindStart')).toHaveLength(0);
expect(accounts[0].health).toBe('connected');
});
it('lets independent WeChat accounts choose the same Agent without creating another slot', async () => {
accounts[1] = { ...spare, health: 'connected', target_agent_slug: agentB.slug, target_agent_name: agentB.name, target_agent_published_version: 4 };
renderPage(); const dialog = await manage(spare.display_name);
fireEvent.change(within(dialog).getByLabelText('绑定的智能体', { exact: true }), { target: { value: agentA.slug } });
fireEvent.click(within(dialog).getByRole('button', { name: '保存更换', exact: true }));
const confirmation = screen.getByRole('dialog', { name: '更换绑定的智能体', exact: true });
fireEvent.click(within(confirmation).getByRole('button', { name: /确认更换/ }));
await waitFor(() => expect(calls('routeChannelAccount')).toHaveLength(1));
expect(accounts.every(item => item.target_agent_slug === agentA.slug)).toBe(true);
expect(accounts[1].enabled).toBe(false);
expect(calls('createChannelAccount')).toHaveLength(0);
expect(calls('channelAccountWechatBindStart')).toHaveLength(0);
});
it('pauses and resumes an existing binding without clearing its target or login', async () => {
renderPage(); const dialog = await manage();
fireEvent.click(within(dialog).getByRole('button', { name: '暂停使用', exact: true }));
expect(await within(dialog).findByRole('button', { name: '恢复使用', exact: true })).toBeEnabled();
expect(accounts[0]).toMatchObject({ enabled: false, health: 'connected', target_agent_slug: agentA.slug });
fireEvent.click(within(dialog).getByRole('button', { name: '恢复使用', exact: true }));
await within(dialog).findByRole('button', { name: '暂停使用', exact: true });
expect(calls('pauseChannelAccount')[0][1]).toMatchObject({ channel_account_id: legacy.id, expected_revision: 7 });
expect(calls('enableChannelAccount')[0][1]).toMatchObject({ channel_account_id: legacy.id, expected_revision: 8 });
expect(calls('routeChannelAccount')).toHaveLength(0);
expect(calls('channelAccountWechatBindStart')).toHaveLength(0);
});
it('clears an association only after confirmation and preserves the WeChat login', async () => {
renderPage(); const dialog = await manage();
fireEvent.click(within(dialog).getByRole('button', { name: '解除绑定', exact: true }));
expect(calls('routeChannelAccount')).toHaveLength(0);
const confirmation = screen.getByRole('dialog', { name: '解除智能体绑定', exact: true });
fireEvent.click(within(confirmation).getByRole('button', { name: '确认解除', exact: true }));
await waitFor(() => expect(calls('routeChannelAccount')).toHaveLength(1));
expect(calls('routeChannelAccount')[0][1]).toMatchObject({ channel_account_id: legacy.id, target_agent_slug: null, enabled: false, expected_revision: 7 });
expect(accounts[0]).toMatchObject({ health: 'connected', target_agent_slug: null, enabled: false });
expect(calls('disconnectChannelAccount')).toHaveLength(0);
expect(calls('channelAccountWechatUnbind')).toHaveLength(0);
});
it('disconnects only after confirmation and retains the selected Agent and account card', async () => {
renderPage(); const dialog = await manage();
fireEvent.click(within(dialog).getByRole('button', { name: '断开微信连接', exact: true }));
expect(calls('disconnectChannelAccount')).toHaveLength(0);
const confirmation = screen.getByRole('dialog', { name: '断开微信连接', exact: true });
fireEvent.click(within(confirmation).getByRole('button', { name: '确认断开', exact: true }));
await waitFor(() => expect(calls('disconnectChannelAccount')).toHaveLength(1));
expect(accounts[0]).toMatchObject({ enabled: false, health: 'login_required', target_agent_slug: agentA.slug });
expect(calls('routeChannelAccount')).toHaveLength(0);
await within(dialog).findByRole('button', { name: '扫码连接', exact: true });
});
it('retries a failed association without creating another account and reuses the original request', async () => {
const base = api.call.getMockImplementation()!;
let attempts = 0;
api.call.mockImplementation(async (operation, input) => { if (operation === 'routeChannelAccount' && ++attempts === 1) throw new Error('绑定未完成'); return base(operation, input); });
renderPage(); const dialog = await addWechat();
expect(await within(dialog).findByText('绑定未完成', { exact: true })).toBeVisible();
expect(calls('createChannelAccount')).toHaveLength(1);
expect(calls('channelAccountWechatBindStart')).toHaveLength(0);
fireEvent.click(within(dialog).getByRole('button', { name: '下一步:扫码连接', exact: true }));
await within(dialog).findByRole('img', { name: '个人微信登录二维码' });
expect(calls('createChannelAccount')).toHaveLength(1);
expect(calls('routeChannelAccount')).toHaveLength(2);
expect(calls('routeChannelAccount')[0][1]).toEqual(calls('routeChannelAccount')[1][1]);
});
it('continues an unfinished account without creating another account slot', async () => {
accounts = [{ ...spare, health: 'connected' }];
renderPage(); const dialog = await manage(spare.display_name);
fireEvent.change(within(dialog).getByLabelText('绑定的智能体', { exact: true }), { target: { value: agentA.slug } });
fireEvent.click(within(dialog).getByRole('button', { name: '下一步:扫码连接', exact: true }));
expect(await within(dialog).findByRole('button', { name: '完成绑定并启用', exact: true })).toBeEnabled();
expect(calls('createChannelAccount')).toHaveLength(0);
expect(calls('channelAccountWechatBindStart')).toHaveLength(0);
expect(calls('routeChannelAccount')[0][1]).toMatchObject({ channel_account_id: spare.id, target_agent_slug: agentA.slug });
});
it('does not erase the old association or claim success after a rejected change', async () => {
const base = api.call.getMockImplementation()!;
api.call.mockImplementation(async (operation, input) => { if (operation === 'routeChannelAccount') throw new Error('渠道状态已变化'); return base(operation, input); });
renderPage(); const dialog = await manage();
fireEvent.change(within(dialog).getByLabelText('绑定的智能体', { exact: true }), { target: { value: agentB.slug } });
fireEvent.click(within(dialog).getByRole('button', { name: '保存更换', exact: true }));
fireEvent.click(within(screen.getByRole('dialog', { name: '更换绑定的智能体', exact: true })).getByRole('button', { name: '确认更换', exact: true }));
await screen.findByText('渠道状态已变化', { exact: true });
expect(accounts[0].target_agent_slug).toBe(agentA.slug);
expect(within(dialog).getByLabelText('绑定的智能体', { exact: true })).toHaveValue(agentB.slug);
expect(screen.queryByText('已更换绑定的智能体,微信连接保留。')).not.toBeInTheDocument();
});
it.each(['failed', 'expired'] as const)('cannot enable an unsuccessful %s QR connection and can explicitly refresh it', async (status) => {
qrStatus = status;
renderPage(); const dialog = await addWechat();
expect(await within(dialog).findByRole('button', { name: '刷新二维码', exact: true })).toBeEnabled();
expect(within(dialog).getByRole('button', { name: '完成绑定并启用', exact: true })).toBeDisabled();
expect(calls('enableChannelAccount')).toHaveLength(0);
qrStatus = 'confirmed';
fireEvent.click(within(dialog).getByRole('button', { name: '刷新二维码', exact: true }));
expect(await within(dialog).findByText('微信已连接', { exact: true })).toBeVisible();
expect(calls('channelAccountWechatBindStart')[1][1]).toMatchObject({ channel_account_id: 'wechat-created', force: true });
expect(calls('enableChannelAccount')).toHaveLength(0);
});
it('does not call a blocked or unconfirmed existing account ready to use', async () => {
accounts = [{ ...legacy, health: 'unknown' }, { ...legacy, id: spare.id, display_name: spare.display_name, blockers: ['worker_offline'] }];
renderPage();
const unknown = await screen.findByRole('article', { name: '原有微信微信', exact: true });
const blocked = screen.getByRole('article', { name: '备用微信微信', exact: true });
expect(within(unknown).queryByText('可以使用', { exact: true })).not.toBeInTheDocument();
expect(within(blocked).queryByText('可以使用', { exact: true })).not.toBeInTheDocument();
expect(calls('enableChannelAccount')).toHaveLength(0);
});
it('keeps adding unavailable after a failed initial read instead of showing a false empty account list', async () => {
api.call.mockRejectedValue(new Error('微信列表暂时不可用'));
renderPage();
await screen.findByText('微信列表暂时不可用', { exact: true });
expect(screen.getByRole('button', { name: '添加微信', exact: true })).toBeDisabled();
expect(screen.queryByText('还没有微信绑定', { exact: true })).not.toBeInTheDocument();
installMock();
fireEvent.click(screen.getByRole('button', { name: '刷新微信列表', exact: true }));
expect(await screen.findByRole('article', { name: '原有微信微信', exact: true })).toBeVisible();
expect(screen.getByRole('button', { name: '添加微信', exact: true })).toBeEnabled();
});
it('allows the optional WeChat remark to stay empty', async () => {
renderPage();
fireEvent.click(await screen.findByRole('button', { name: '添加微信', exact: true }));
const dialog = screen.getByRole('dialog', { name: '添加微信', exact: true });
fireEvent.change(within(dialog).getByLabelText('选择智能体', { exact: true }), { target: { value: agentA.slug } });
fireEvent.click(within(dialog).getByRole('button', { name: '下一步:扫码连接', exact: true }));
await within(dialog).findByRole('img', { name: '个人微信登录二维码' });
expect(calls('createChannelAccount')).toHaveLength(1);
expect(calls('createChannelAccount')[0][1].display_name).toMatch(/\S/);
});
it('completes a reconnected already enabled account without sending another enable request', async () => {
accounts = [{ ...legacy, health: 'login_required' }]; qrStatus = 'confirmed';
renderPage(); const dialog = await manage();
fireEvent.click(within(dialog).getByRole('button', { name: '扫码连接', exact: true }));
expect(await within(dialog).findByText('微信已连接', { exact: true })).toBeVisible();
fireEvent.click(within(dialog).getByRole('button', { name: '完成绑定并启用', exact: true }));
expect(await within(dialog).findByRole('button', { name: '完成', exact: true })).toBeVisible();
expect(calls('enableChannelAccount')).toHaveLength(0);
expect(calls('createChannelAccount')).toHaveLength(0);
expect(calls('routeChannelAccount')).toHaveLength(0);
});
it.each(['channelAccountWechatBindStart', 'channelAccountWechatBindVerification'])('blocks new slots and QR actions while %s has an uncertain result', async operation => {
accounts = [{ ...legacy, health: 'login_required' }];
api.recovery.mockResolvedValue({ recent: null, pending: [{ id: 'uncertain-wechat', operation, input: { channel_account_id: legacy.id }, created_at: '2026-10-12T00:00:00Z' }] });
renderPage('/cloud-agents/channels?account=' + legacy.id);
const dialog = await screen.findByRole('dialog', { name: '管理微信绑定', exact: true });
expect(screen.getByRole('button', { name: '添加微信', exact: true, hidden: true })).toBeDisabled();
expect(within(dialog).getByRole('button', { name: '扫码连接', exact: true })).toBeDisabled();
fireEvent.click(within(dialog).getByRole('button', { name: '扫码连接', exact: true }));
expect(calls('channelAccountWechatBindStart')).toHaveLength(0);
expect(calls('createChannelAccount')).toHaveLength(0);
expect(calls('enableChannelAccount')).toHaveLength(0);
});
it('uses the latest account state when enabling after QR confirmation', async () => {
qrStatus = 'confirmed'; renderPage(); const dialog = await addWechat();
await within(dialog).findByText('微信已连接', { exact: true });
const created = accounts.find(item => item.id === 'wechat-created')!;
created.revision = 77;
fireEvent.click(within(dialog).getByRole('button', { name: '完成绑定并启用', exact: true }));
await within(dialog).findByRole('button', { name: '完成', exact: true });
expect(calls('enableChannelAccount')[0][1]).toMatchObject({ channel_account_id: 'wechat-created', expected_revision: 77 });
});
it('does not enable when the authoritative account has become blocked after QR confirmation', async () => {
qrStatus = 'confirmed'; renderPage(); const dialog = await addWechat();
await within(dialog).findByText('微信已连接', { exact: true });
accounts.find(item => item.id === 'wechat-created')!.blockers = ['worker_offline'];
fireEvent.click(within(dialog).getByRole('button', { name: '完成绑定并启用', exact: true }));
expect(await within(dialog).findByText('微信还没有准备好,请刷新连接状态后再试。', { exact: true })).toBeVisible();
expect(calls('enableChannelAccount')).toHaveLength(0);
expect(within(dialog).queryByRole('button', { name: '完成', exact: true })).not.toBeInTheDocument();
});
it('does not silently enable another Agent if the association changes while scanning', async () => {
qrStatus = 'confirmed'; renderPage(); const dialog = await addWechat();
await within(dialog).findByText('微信已连接', { exact: true });
const created = accounts.find(item => item.id === 'wechat-created')!;
Object.assign(created, { target_agent_slug: agentB.slug, target_agent_name: agentB.name });
fireEvent.click(within(dialog).getByRole('button', { name: '完成绑定并启用', exact: true }));
expect(await within(dialog).findByText('绑定的智能体已发生变化,请返回上一步确认。', { exact: true })).toBeVisible();
expect(calls('enableChannelAccount')).toHaveLength(0);
expect(within(dialog).queryByRole('button', { name: '完成', exact: true })).not.toBeInTheDocument();
});
it('does not show successful enable or completed binding when the enable receipt remains disabled', async () => {
qrStatus = 'confirmed';
const base = api.call.getMockImplementation()!;
api.call.mockImplementation(async (operation, input) => {
if (operation === 'enableChannelAccount') return { channel: { ...accounts.find(item => item.id === input.channel_account_id)!, enabled: false } };
return base(operation, input);
});
renderPage(); const dialog = await addWechat();
fireEvent.click(await within(dialog).findByRole('button', { name: '完成绑定并启用', exact: true }));
await within(dialog).findByText('微信还没有准备好,请刷新连接状态后再试。', { exact: true });
expect(calls('enableChannelAccount')).toHaveLength(1);
expect(within(dialog).queryByRole('heading', { name: '绑定完成啦', exact: true })).not.toBeInTheDocument();
expect(within(dialog).queryByRole('button', { name: '完成', exact: true })).not.toBeInTheDocument();
expect(screen.queryByText('微信绑定已启用。', { exact: true })).not.toBeInTheDocument();
expect(accounts.find(item => item.id === 'wechat-created')?.enabled).toBe(false);
});