import { fireEvent, render, screen, waitFor } from '@testing-library/react'; import { beforeEach, expect, it, vi } from 'vitest'; import ChatDetailPage from '@/pages/ChatDetail'; import { apiRequest } from '@/services/api'; vi.mock('@/services/api', () => ({ apiRequest: vi.fn(), jsonOptions: vi.fn((_method, body) => ({ body })), })); const overview = { chat_id: '-100', title: '测试群', type: 'supergroup', accessible: true, bot_status: 'administrator', bot_privileges: ['can_delete_messages', 'can_restrict_members'], permissions: {}, automation: { chat_id: '-100', auto_replies: [], blacklist_words: ['广告'], risk_rules: [ { rule_id: 'ads', name: '广告拦截', enabled: true, keywords: ['广告'], match_images: true, match_links: true, actions: ['delete', 'warn'], duration_seconds: 3600, }, ], identity_monitor: { enabled: true, notify_in_chat: false }, welcome: { enabled: false, text: '', media: null }, captcha_enabled: false, chatbot_enabled: false, antiflood_enabled: true, }, }; const recentMember = { user: { id: '42', username: 'xiaoming', first_name: '小明', last_name: '同学', is_bot: false, is_deleted: false, }, status: 'recent', privileges: [], last_seen_at: '2026-07-24T12:00:00Z', }; beforeEach(() => { vi.clearAllMocks(); vi.mocked(apiRequest).mockImplementation(async (path: string) => { if (path === '/chats/-100') return overview; if (path.endsWith('/admins')) return { items: [] }; if (path.includes('/members/recent')) return { items: [recentMember] }; if (path.includes('/members/identity-changes')) { return { items: [{ change_id: 'change-1', chat_id: '-100', user_id: '42', before: { username: 'xiaoming', display_name: '小明 同学' }, after: { username: 'daming', display_name: '大明 同学' }, changed_fields: ['username', 'display_name'], observed_at: '2026-07-24T12:30:00Z', }], }; } if (path.includes('/members/search')) { return { items: [{ ...recentMember, status: 'member' }] }; } return {}; }); }); it('展示多条内容风控规则并可编辑限时封禁', async () => { render(); fireEvent.click(await screen.findByText('自动化与风控')); expect(await screen.findByText('内容风控')).toBeInTheDocument(); expect(screen.getByText('广告拦截')).toBeInTheDocument(); expect(screen.getByText('关键词 1 个')).toBeInTheDocument(); expect(screen.getByText('图片')).toBeInTheDocument(); expect(screen.getByText('链接')).toBeInTheDocument(); expect(screen.getAllByText('删除消息').length).toBeGreaterThan(0); expect(screen.getByText('警告')).toBeInTheDocument(); expect(screen.getByText('1 小时')).toBeInTheDocument(); fireEvent.click(screen.getByRole('button', { name: '编辑 广告拦截' })); expect(await screen.findByText('编辑风控规则')).toBeInTheDocument(); expect(screen.getByText('图片消息')).toBeInTheDocument(); expect(screen.getByText('链接消息')).toBeInTheDocument(); expect(screen.getByLabelText('封禁时长')).toHaveValue('60'); }); it('展示最近活跃成员并支持中文昵称搜索', async () => { render(); fireEvent.click(await screen.findByText('成员与管理员')); expect(await screen.findByText('小明 同学')).toBeInTheDocument(); const input = screen.getByPlaceholderText('昵称、姓名、@username 或用户 ID'); fireEvent.change(input, { target: { value: '小明' } }); fireEvent.click(screen.getByRole('button', { name: /查\s*询/ })); await waitFor(() => { expect(apiRequest).toHaveBeenCalledWith( '/chats/-100/members/search?query=%E5%B0%8F%E6%98%8E', ); }); expect(screen.getByText('大明 同学')).toBeInTheDocument(); expect(screen.getByText(/昵称:小明 同学.*大明 同学/)).toBeInTheDocument(); }); it('配置成员资料监控与群内提醒', async () => { render(); fireEvent.click(await screen.findByText('自动化与风控')); const monitor = screen.getByRole('switch', { name: '用户名与昵称变更监控' }); const notify = screen.getByRole('switch', { name: '资料变更时在群内提醒' }); expect(monitor).toBeChecked(); expect(notify).not.toBeChecked(); fireEvent.click(notify); fireEvent.click(screen.getByRole('button', { name: /保\s*存\s*自\s*动\s*化\s*配\s*置/ })); fireEvent.click(await screen.findByRole('button', { name: /确\s*认\s*更\s*新/ })); await waitFor(() => { const call = vi.mocked(apiRequest).mock.calls.find(([path]) => path === '/chats/-100/automation'); expect(call?.[1]?.body).toMatchObject({ identity_monitor: { enabled: true, notify_in_chat: true }, confirm: true, }); }); });