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,
});
});
});