BusinessAssistant.test.tsx 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. import { fireEvent, render, screen, waitFor } from '@testing-library/react';
  2. import { beforeEach, expect, test, vi } from 'vitest';
  3. import BusinessAssistantPage from '@/pages/BusinessAssistant';
  4. import { apiRequest } from '@/services/api';
  5. vi.mock('@/services/api', () => ({
  6. apiRequest: vi.fn(),
  7. jsonOptions: vi.fn((method, body) => ({ method, body: JSON.stringify(body) })),
  8. }));
  9. vi.mock('@/contexts/BotAccess', () => ({
  10. useBotAccess: () => ({
  11. bot: {
  12. bot_id: 'primary',
  13. label: 'Test Bot',
  14. enabled: true,
  15. bot_token_configured: true,
  16. sudo_users_id: [],
  17. log_group_id: '0',
  18. gban_log_group_id: '0',
  19. message_dump_chat: '0',
  20. role_ids: ['business_assistant_manager'],
  21. permissions: ['business_assistant.manage'],
  22. identity: {
  23. id: '999',
  24. username: 'business_bot',
  25. name: 'Business Bot',
  26. can_connect_to_business: true,
  27. },
  28. business_assistant: {
  29. base_url: 'https://models.example.com/v1',
  30. api_key_configured: true,
  31. model: 'support-model',
  32. timeout_seconds: 30,
  33. max_output_tokens: 600,
  34. configured: true,
  35. },
  36. ready_to_connect: true,
  37. runtime: { state: 'running' },
  38. },
  39. }),
  40. }));
  41. const settings = {
  42. connection_id: 'conn-a',
  43. assistant_enabled: true,
  44. system_prompt: '只依据知识库回答。',
  45. language: 'zh-CN',
  46. tone: 'professional',
  47. account_daily_limit: 200,
  48. customer_daily_limit: 20,
  49. human_pause_hours: 24,
  50. notification_destination: 'both',
  51. ops_group_id: '-100123',
  52. timezone: 'Asia/Shanghai',
  53. digest_enabled: false,
  54. digest_time: '09:00',
  55. handoff_message: '请稍候,已转人工。',
  56. unsupported_message: '该消息需要人工处理。',
  57. };
  58. beforeEach(() => {
  59. vi.mocked(apiRequest).mockClear();
  60. vi.mocked(apiRequest).mockImplementation(async (path) => {
  61. if (path === '/business-assistant/status') {
  62. return {
  63. polling_state: 'running',
  64. business_mode_supported: true,
  65. webhook_conflict: false,
  66. webhook_url: '',
  67. model_configured: true,
  68. usage: { ai_calls: 8, customers: 3, conversations: 3, handoffs: 1, human_paused: 1 },
  69. };
  70. }
  71. if (path.startsWith('/business-assistant/connections')) {
  72. return {
  73. items: [
  74. {
  75. connection_id: 'conn-a',
  76. user: { id: '900', username: 'owner', first_name: '店主' },
  77. user_chat_id: '900',
  78. rights: { can_reply: true, can_read_messages: true },
  79. is_enabled: true,
  80. last_event_at: '2026-08-14T08:00:00Z',
  81. settings,
  82. },
  83. ],
  84. total: 1,
  85. page: 1,
  86. page_size: 100,
  87. };
  88. }
  89. if (path.startsWith('/business-assistant/settings')) return settings;
  90. if (path.startsWith('/business-assistant/knowledge')) {
  91. return {
  92. items: [
  93. {
  94. entry_id: 'entry-1',
  95. connection_id: 'conn-a',
  96. question: '营业时间是什么?',
  97. aliases: ['几点营业'],
  98. keywords: ['营业时间'],
  99. answer: '每天 09:00 至 18:00 营业。',
  100. tags: ['营业'],
  101. priority: 10,
  102. enabled: true,
  103. },
  104. ],
  105. total: 1,
  106. page: 1,
  107. page_size: 20,
  108. };
  109. }
  110. if (path.startsWith('/business-assistant/conversations')) {
  111. return {
  112. items: [
  113. {
  114. conversation_id: 'conversation-1',
  115. connection_id: 'conn-a',
  116. chat_id: '501',
  117. customer: { id: '501', first_name: '客户甲' },
  118. status: 'handoff',
  119. summary: '客户询问退款。',
  120. handoff_reason: 'sensitive_request',
  121. last_message_at: '2026-08-14T08:01:00Z',
  122. },
  123. ],
  124. total: 1,
  125. page: 1,
  126. page_size: 20,
  127. };
  128. }
  129. if (path.startsWith('/business-assistant/usage')) {
  130. return { ai_calls: 8, customers: 3, conversations: 3, handoffs: 1, human_paused: 1 };
  131. }
  132. throw new Error(`Unexpected request: ${path}`);
  133. });
  134. });
  135. test('展示运行状态、连接权限,并用最新搜索词查询知识库', async () => {
  136. render(<BusinessAssistantPage />);
  137. expect(screen.getByRole('heading', { name: '智能接待' })).toBeInTheDocument();
  138. expect((await screen.findAllByText('店主')).length).toBeGreaterThan(0);
  139. expect(screen.getByText('允许回复')).toBeInTheDocument();
  140. expect(screen.getByText('允许标记已读')).toBeInTheDocument();
  141. fireEvent.click(await screen.findByRole('tab', { name: /知识库/ }));
  142. expect(await screen.findByText('营业时间是什么?')).toBeInTheDocument();
  143. const search = screen.getByLabelText('搜索知识库');
  144. fireEvent.change(search, { target: { value: '配送' } });
  145. fireEvent.keyDown(search, { key: 'Enter', code: 'Enter' });
  146. await waitFor(() => {
  147. expect(
  148. vi.mocked(apiRequest).mock.calls.some(([path]) =>
  149. String(path).includes('query=%E9%85%8D%E9%80%81'),
  150. ),
  151. ).toBe(true);
  152. });
  153. });