ChatDetail.test.tsx 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. import { fireEvent, render, screen, waitFor } from '@testing-library/react';
  2. import { beforeEach, expect, it, vi } from 'vitest';
  3. import ChatDetailPage from '@/pages/ChatDetail';
  4. import { apiRequest } from '@/services/api';
  5. vi.mock('@/services/api', () => ({
  6. apiRequest: vi.fn(),
  7. jsonOptions: vi.fn((_method, body) => ({ body })),
  8. }));
  9. const overview = {
  10. chat_id: '-100',
  11. title: '测试群',
  12. type: 'supergroup',
  13. accessible: true,
  14. bot_status: 'administrator',
  15. bot_privileges: ['can_delete_messages', 'can_restrict_members'],
  16. permissions: {},
  17. automation: {
  18. chat_id: '-100',
  19. auto_replies: [],
  20. blacklist_words: ['广告'],
  21. risk_rules: [
  22. {
  23. rule_id: 'ads',
  24. name: '广告拦截',
  25. enabled: true,
  26. keywords: ['广告'],
  27. match_images: true,
  28. match_links: true,
  29. actions: ['delete', 'warn'],
  30. duration_seconds: 3600,
  31. },
  32. ],
  33. identity_monitor: { enabled: true, notify_in_chat: false },
  34. welcome: { enabled: false, text: '', media: null },
  35. captcha_enabled: false,
  36. chatbot_enabled: false,
  37. antiflood_enabled: true,
  38. },
  39. };
  40. const recentMember = {
  41. user: {
  42. id: '42',
  43. username: 'xiaoming',
  44. first_name: '小明',
  45. last_name: '同学',
  46. is_bot: false,
  47. is_deleted: false,
  48. },
  49. status: 'recent',
  50. privileges: [],
  51. last_seen_at: '2026-07-24T12:00:00Z',
  52. };
  53. beforeEach(() => {
  54. vi.clearAllMocks();
  55. vi.mocked(apiRequest).mockImplementation(async (path: string) => {
  56. if (path === '/chats/-100') return overview;
  57. if (path.endsWith('/admins')) return { items: [] };
  58. if (path.includes('/members/recent')) return { items: [recentMember] };
  59. if (path.includes('/members/identity-changes')) {
  60. return {
  61. items: [{
  62. change_id: 'change-1',
  63. chat_id: '-100',
  64. user_id: '42',
  65. before: { username: 'xiaoming', display_name: '小明 同学' },
  66. after: { username: 'daming', display_name: '大明 同学' },
  67. changed_fields: ['username', 'display_name'],
  68. observed_at: '2026-07-24T12:30:00Z',
  69. }],
  70. };
  71. }
  72. if (path.includes('/members/search')) {
  73. return { items: [{ ...recentMember, status: 'member' }] };
  74. }
  75. return {};
  76. });
  77. });
  78. it('展示多条内容风控规则并可编辑限时封禁', async () => {
  79. render(<ChatDetailPage />);
  80. fireEvent.click(await screen.findByText('自动化与风控'));
  81. expect(await screen.findByText('内容风控')).toBeInTheDocument();
  82. expect(screen.getByText('广告拦截')).toBeInTheDocument();
  83. expect(screen.getByText('关键词 1 个')).toBeInTheDocument();
  84. expect(screen.getByText('图片')).toBeInTheDocument();
  85. expect(screen.getByText('链接')).toBeInTheDocument();
  86. expect(screen.getAllByText('删除消息').length).toBeGreaterThan(0);
  87. expect(screen.getByText('警告')).toBeInTheDocument();
  88. expect(screen.getByText('1 小时')).toBeInTheDocument();
  89. fireEvent.click(screen.getByRole('button', { name: '编辑 广告拦截' }));
  90. expect(await screen.findByText('编辑风控规则')).toBeInTheDocument();
  91. expect(screen.getByText('图片消息')).toBeInTheDocument();
  92. expect(screen.getByText('链接消息')).toBeInTheDocument();
  93. expect(screen.getByLabelText('封禁时长')).toHaveValue('60');
  94. });
  95. it('展示最近活跃成员并支持中文昵称搜索', async () => {
  96. render(<ChatDetailPage />);
  97. fireEvent.click(await screen.findByText('成员与管理员'));
  98. expect(await screen.findByText('小明 同学')).toBeInTheDocument();
  99. const input = screen.getByPlaceholderText('昵称、姓名、@username 或用户 ID');
  100. fireEvent.change(input, { target: { value: '小明' } });
  101. fireEvent.click(screen.getByRole('button', { name: /查\s*询/ }));
  102. await waitFor(() => {
  103. expect(apiRequest).toHaveBeenCalledWith(
  104. '/chats/-100/members/search?query=%E5%B0%8F%E6%98%8E',
  105. );
  106. });
  107. expect(screen.getByText('大明 同学')).toBeInTheDocument();
  108. expect(screen.getByText(/昵称:小明 同学.*大明 同学/)).toBeInTheDocument();
  109. });
  110. it('配置成员资料监控与群内提醒', async () => {
  111. render(<ChatDetailPage />);
  112. fireEvent.click(await screen.findByText('自动化与风控'));
  113. const monitor = screen.getByRole('switch', { name: '用户名与昵称变更监控' });
  114. const notify = screen.getByRole('switch', { name: '资料变更时在群内提醒' });
  115. expect(monitor).toBeChecked();
  116. expect(notify).not.toBeChecked();
  117. fireEvent.click(notify);
  118. fireEvent.click(screen.getByRole('button', { name: /保\s*存\s*自\s*动\s*化\s*配\s*置/ }));
  119. fireEvent.click(await screen.findByRole('button', { name: /确\s*认\s*更\s*新/ }));
  120. await waitFor(() => {
  121. const call = vi.mocked(apiRequest).mock.calls.find(([path]) => path === '/chats/-100/automation');
  122. expect(call?.[1]?.body).toMatchObject({
  123. identity_monitor: { enabled: true, notify_in_chat: true },
  124. confirm: true,
  125. });
  126. });
  127. });