fix(agents): explain missing channel targets
This commit is contained in:
1 parent
13336cfe12
commit
8289f27f3e
5 files changed
+222
-9
No files matched your search
@@ -1,5 +1,5 @@
|
||||
import { act, cleanup, fireEvent, render, screen, waitFor, within } from '@testing-library/react';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
import { MemoryRouter, Route, Routes, useLocation } 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';
|
||||
@@ -23,7 +23,16 @@ 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>); }
|
||||
function LocationProbe() {
|
||||
const location = useLocation();
|
||||
return <output data-testid="location-path">{location.pathname + location.search}</output>;
|
||||
}
|
||||
function renderPage(entry = '/cloud-agents/channels') {
|
||||
return render(<MemoryRouter initialEntries={[entry]}><Routes>
|
||||
<Route path="/cloud-agents/channels" element={<ChannelAccounts />} />
|
||||
<Route path="/cloud-agents" element={<div>智能体列表</div>} />
|
||||
</Routes><LocationProbe /></MemoryRouter>);
|
||||
}
|
||||
async function addWechat(fakeTimers = false) {
|
||||
fireEvent.click(await screen.findByRole('button', { name: '添加微信', exact: true }));
|
||||
const dialog = screen.getByRole('dialog', { name: '添加微信', exact: true });
|
||||
@@ -80,6 +89,69 @@ beforeEach(() => {
|
||||
});
|
||||
afterEach(() => { cleanup(); vi.useRealTimers(); });
|
||||
|
||||
it.each([
|
||||
['no Agents', []],
|
||||
['only drafts', [{ ...agentA, published_version: null }]],
|
||||
['only disabled Agents', [{ ...agentA, enabled: false }]],
|
||||
] satisfies [string, CloudAgentDraft[]][])('guides a workspace with %s to My Agents without starting a binding', async (_name, unavailable) => {
|
||||
agents = unavailable; accounts = [];
|
||||
renderPage();
|
||||
expect(await screen.findByRole('heading', { name: '还没有可连接的智能体', exact: true })).toBeVisible();
|
||||
expect(screen.getByText('先创建一个智能体,配置完成后点击“开始使用”,就能连接到微信。', { exact: true })).toBeVisible();
|
||||
expect(screen.queryByRole('button', { name: '添加第一个微信', exact: true })).not.toBeInTheDocument();
|
||||
expect(screen.getByRole('button', { name: '添加微信', exact: true })).toBeEnabled();
|
||||
fireEvent.click(screen.getByRole('button', { name: '去我的智能体', exact: true }));
|
||||
expect(await screen.findByText('智能体列表', { exact: true })).toBeVisible();
|
||||
expect(screen.getByTestId('location-path')).toHaveTextContent('/cloud-agents?view=mine');
|
||||
expect(writes()).toEqual([]);
|
||||
});
|
||||
|
||||
it('uses the same preparation hint in the add dialog without duplicating it on the page', async () => {
|
||||
agents = []; accounts = [];
|
||||
renderPage();
|
||||
await screen.findByRole('heading', { name: '还没有可连接的智能体', exact: true });
|
||||
fireEvent.click(screen.getByRole('button', { name: '添加微信', exact: true }));
|
||||
const dialog = screen.getByRole('dialog', { name: '添加微信', exact: true });
|
||||
expect(within(dialog).getByRole('heading', { name: '还没有可连接的智能体', exact: true })).toBeVisible();
|
||||
expect(within(dialog).getByText('先创建一个智能体,配置完成后点击“开始使用”,就能连接到微信。', { exact: true })).toBeVisible();
|
||||
expect(screen.getAllByRole('heading', { name: '还没有可连接的智能体', exact: true, hidden: true })).toHaveLength(1);
|
||||
expect(within(dialog).getByRole('button', { name: '下一步:扫码连接', exact: true })).toBeDisabled();
|
||||
fireEvent.click(within(dialog).getByRole('button', { name: '去我的智能体', exact: true }));
|
||||
expect(await screen.findByText('智能体列表', { exact: true })).toBeVisible();
|
||||
expect(screen.getByTestId('location-path')).toHaveTextContent('/cloud-agents?view=mine');
|
||||
expect(writes()).toEqual([]);
|
||||
});
|
||||
|
||||
it('waits for the Agent read before showing a preparation hint', async () => {
|
||||
accounts = [];
|
||||
let finish!: (value: { agents: CloudAgentDraft[]; next_cursor: null }) => void;
|
||||
api.list.mockImplementation(() => new Promise(resolve => { finish = resolve; }));
|
||||
renderPage();
|
||||
expect(screen.getByText('正在查看你的微信绑定…', { exact: true })).toBeVisible();
|
||||
expect(screen.queryByRole('heading', { name: '还没有可连接的智能体', exact: true })).not.toBeInTheDocument();
|
||||
expect(screen.queryByRole('button', { name: '去我的智能体', exact: true })).not.toBeInTheDocument();
|
||||
await act(async () => finish({ agents: [], next_cursor: null }));
|
||||
expect(await screen.findByRole('heading', { name: '还没有可连接的智能体', exact: true })).toBeVisible();
|
||||
expect(writes()).toEqual([]);
|
||||
});
|
||||
|
||||
it('keeps existing WeChat cards manageable when no connectable Agent remains', async () => {
|
||||
agents = [];
|
||||
renderPage();
|
||||
expect(await screen.findByRole('heading', { name: '还没有可连接的智能体', exact: true })).toBeVisible();
|
||||
expect(screen.getByRole('article', { name: '原有微信微信', exact: true })).toBeVisible();
|
||||
expect(screen.getByRole('article', { name: '备用微信微信', exact: true })).toBeVisible();
|
||||
const dialog = await manage();
|
||||
expect(within(dialog).getByRole('button', { name: '暂停使用', exact: true })).toBeEnabled();
|
||||
expect(within(dialog).getByRole('button', { name: '解除绑定', exact: true })).toBeEnabled();
|
||||
expect(within(dialog).getByRole('button', { name: '断开微信连接', exact: true })).toBeEnabled();
|
||||
fireEvent.click(within(dialog).getByRole('button', { name: '暂停使用', exact: true }));
|
||||
await waitFor(() => expect(calls('pauseChannelAccount')).toHaveLength(1));
|
||||
expect(accounts[0]).toMatchObject({ enabled: false, health: 'connected', target_agent_slug: agentA.slug });
|
||||
expect(calls('createChannelAccount')).toHaveLength(0);
|
||||
expect(calls('routeChannelAccount')).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('shows binding cards and never fetches or embeds conversation or activity panels', async () => {
|
||||
renderPage();
|
||||
const card = await screen.findByRole('article', { name: '原有微信微信', exact: true });
|
||||
@@ -89,6 +161,7 @@ it('shows binding cards and never fetches or embeds conversation or activity pan
|
||||
expect(screen.queryByRole('dialog')).not.toBeInTheDocument();
|
||||
expect(screen.queryByText('微信对话', { exact: true })).not.toBeInTheDocument();
|
||||
expect(screen.queryByText('活动记录', { exact: true })).not.toBeInTheDocument();
|
||||
expect(screen.queryByRole('heading', { name: '还没有可连接的智能体', exact: true })).not.toBeInTheDocument();
|
||||
expect(writes()).toEqual([]);
|
||||
expect(api.call.mock.calls.every(([operation]) => operation === 'channelAccounts')).toBe(true);
|
||||
});
|
||||
@@ -295,13 +368,19 @@ it('does not call a blocked or unconfirmed existing account ready to use', async
|
||||
});
|
||||
|
||||
|
||||
it('keeps adding unavailable after a failed initial read instead of showing a false empty account list', async () => {
|
||||
api.call.mockRejectedValue(new Error('微信列表暂时不可用'));
|
||||
it.each(['accounts', 'agents'])('keeps adding unavailable after a failed initial %s read instead of showing a false empty list', async source => {
|
||||
agents = [];
|
||||
const message = source === 'accounts' ? '微信列表暂时不可用' : '智能体列表暂时不可用';
|
||||
if (source === 'accounts') api.call.mockRejectedValue(new Error(message));
|
||||
else api.list.mockRejectedValue(new Error(message));
|
||||
renderPage();
|
||||
await screen.findByText('微信列表暂时不可用', { exact: true });
|
||||
await screen.findByText(message, { exact: true });
|
||||
expect(screen.getByRole('button', { name: '添加微信', exact: true })).toBeDisabled();
|
||||
expect(screen.queryByText('还没有微信绑定', { exact: true })).not.toBeInTheDocument();
|
||||
expect(screen.queryByRole('heading', { name: '还没有可连接的智能体', exact: true })).not.toBeInTheDocument();
|
||||
expect(screen.queryByRole('button', { name: '去我的智能体', exact: true })).not.toBeInTheDocument();
|
||||
installMock();
|
||||
api.list.mockImplementation(async () => ({ agents, next_cursor: null }));
|
||||
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();
|
||||
|
||||
Reference in new issue
Block a user