import { describe, it, expect, vi, beforeEach } from 'vitest';
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import DisabledPersonSelector from '../../src/components/DisabledPersonSelector';
import { disabilityClientManager } from '../../src/api/disabilityClient';
import type { DisabledPersonData } from '../../src/api/types';
// 完整的mock响应对象
const createMockResponse = (status: number, data?: any) => ({
status,
ok: status >= 200 && status < 300,
body: null,
bodyUsed: false,
statusText: status === 200 ? 'OK' : status === 201 ? 'Created' : status === 204 ? 'No Content' : 'Error',
headers: new Headers(),
url: '',
redirected: false,
type: 'basic' as ResponseType,
json: async () => data || {},
text: async () => '',
blob: async () => new Blob(),
arrayBuffer: async () => new ArrayBuffer(0),
formData: async () => new FormData(),
clone: function() { return this; }
});
// Mock API client
vi.mock('../../src/api/disabilityClient', () => {
const mockDisabilityClient = {
searchDisabledPersons: {
$get: vi.fn(() => Promise.resolve(createMockResponse(200, {
data: [
{
id: 1,
name: '张三',
gender: '男',
idCard: '110101199001011234',
disabilityId: 'CJZ20240001',
disabilityType: '视力残疾',
disabilityLevel: '一级',
idAddress: '北京市东城区',
phone: '13800138000',
province: '北京市',
city: '北京市',
district: '东城区',
isInBlackList: 0,
createTime: '2024-01-01T00:00:00Z',
updateTime: '2024-01-01T00:00:00Z'
},
{
id: 2,
name: '李四',
gender: '女',
idCard: '110101199001011235',
disabilityId: 'CJZ20240002',
disabilityType: '听力残疾',
disabilityLevel: '二级',
idAddress: '上海市黄浦区',
phone: '13800138001',
province: '上海市',
city: '上海市',
district: '黄浦区',
isInBlackList: 1, // 黑名单人员
createTime: '2024-01-01T00:00:00Z',
updateTime: '2024-01-01T00:00:00Z'
}
],
total: 2
})))
},
getAllDisabledPersons: {
$get: vi.fn(() => Promise.resolve(createMockResponse(200, {
data: [
{
id: 3,
name: '王五',
gender: '男',
idCard: '110101199001011236',
disabilityId: 'CJZ20240003',
disabilityType: '肢体残疾',
disabilityLevel: '三级',
idAddress: '广州市天河区',
phone: '13800138002',
province: '广东省',
city: '广州市',
district: '天河区',
isInBlackList: 0,
createTime: '2024-01-01T00:00:00Z',
updateTime: '2024-01-01T00:00:00Z'
}
],
total: 1
})))
}
};
const mockClientManager = {
get: vi.fn(() => mockDisabilityClient),
init: vi.fn(() => mockDisabilityClient),
reset: vi.fn(),
getInstance: vi.fn(() => mockClientManager)
};
return {
disabilityClientManager: mockClientManager,
disabilityClient: mockDisabilityClient
};
});
// Mock AreaSelect组件
vi.mock('@d8d/area-management-ui', () => ({
AreaSelect: ({ value, onChange, placeholder }: any) => (
)
}));
// Mock shared-ui-components
vi.mock('@d8d/shared-ui-components/components/ui/dialog', () => ({
Dialog: ({ children, open, onOpenChange }: any) => open ? (
{children}
) : null,
DialogContent: ({ children, className }: any) => (
{children}
),
DialogHeader: ({ children }: any) => (
{children}
),
DialogTitle: ({ children }: any) => (
{children}
),
DialogFooter: ({ children, className }: any) => (
{children}
)
}));
vi.mock('@d8d/shared-ui-components/components/ui/button', () => ({
Button: ({ children, onClick, disabled, variant, ...props }: any) => (
)
}));
vi.mock('@d8d/shared-ui-components/components/ui/input', () => ({
Input: ({ value, onChange, placeholder, ...props }: any) => (
)
}));
vi.mock('@d8d/shared-ui-components/components/ui/select', () => ({
Select: ({ children }: any) => (
{children}
),
SelectTrigger: ({ children }: any) => (
{children}
),
SelectValue: ({ placeholder }: any) => (
{placeholder}
),
SelectContent: ({ children }: any) => (
{children}
),
SelectItem: ({ children, value }: any) => (
)
}));
vi.mock('@d8d/shared-ui-components/components/ui/label', () => ({
Label: ({ children, htmlFor }: any) => (
)
}));
vi.mock('@d8d/shared-ui-components/components/ui/table', () => ({
Table: ({ children, ...props }: any) => (
),
TableHeader: ({ children }: any) => {children},
TableBody: ({ children }: any) => {children},
TableRow: ({ children, onClick, className, ...props }: any) => (
{children}
),
TableHead: ({ children, className }: any) => {children} | ,
TableCell: ({ children }: any) => {children} | ,
}));
vi.mock('@d8d/shared-ui-components/components/admin/DataTablePagination', () => ({
DataTablePagination: ({ currentPage, pageSize, totalCount, onPageChange, ...props }: any) => (
第{currentPage}页,共{Math.ceil(totalCount / pageSize)}页
)
}));
vi.mock('@d8d/shared-ui-components/components/ui/checkbox', () => ({
Checkbox: ({ checked, onCheckedChange, disabled, ...props }: any) => (
onCheckedChange && onCheckedChange(e.target.checked)}
disabled={disabled}
data-testid={props['data-testid'] || 'checkbox'}
{...props}
/>
)
}));
vi.mock('@d8d/shared-ui-components/components/ui/alert', () => ({
Alert: ({ children, variant }: any) => (
{children}
),
AlertDescription: ({ children }: any) => (
{children}
)
}));
vi.mock('lucide-react', () => ({
AlertCircle: () => ⚠️
}));
describe('DisabledPersonSelector', () => {
let queryClient: QueryClient;
let onOpenChange: ReturnType;
let onSelect: ReturnType;
beforeEach(() => {
queryClient = new QueryClient({
defaultOptions: {
queries: {
retry: false,
},
},
});
onOpenChange = vi.fn();
onSelect = vi.fn();
vi.clearAllMocks();
});
const renderComponent = (props = {}) => {
return render(
);
};
it('应该渲染对话框和搜索区域', () => {
renderComponent();
expect(screen.getByTestId('dialog')).toBeInTheDocument();
expect(screen.getByTestId('dialog-title')).toHaveTextContent('选择残疾人');
// 检查搜索字段
expect(screen.getByTestId('search-name-input')).toBeInTheDocument();
expect(screen.getByTestId('area-select')).toBeInTheDocument();
expect(screen.getByTestId('search-button')).toBeInTheDocument();
expect(screen.getByTestId('reset-button')).toBeInTheDocument();
});
it('应该显示残疾人列表', async () => {
renderComponent();
// 等待数据加载
await waitFor(() => {
expect(screen.getByTestId('data-table')).toBeInTheDocument();
});
// 检查表格数据
expect(screen.getByText('张三')).toBeInTheDocument();
expect(screen.getByText('男')).toBeInTheDocument();
expect(screen.getByText('110101199001011234')).toBeInTheDocument();
});
it('应该处理搜索功能', async () => {
renderComponent();
// 输入搜索关键词
const searchInput = screen.getByTestId('search-name-input');
fireEvent.change(searchInput, { target: { value: '张三' } });
// 点击搜索按钮
const searchButton = screen.getByTestId('search-button');
fireEvent.click(searchButton);
// 验证搜索API被调用
await waitFor(() => {
expect(disabilityClientManager.get().searchDisabledPersons.$get).toHaveBeenCalledWith({
query: {
keyword: '张三',
skip: 0,
take: 10
}
});
});
});
it('应该处理重置搜索', () => {
renderComponent();
// 输入搜索关键词
const searchInput = screen.getByTestId('search-name-input');
fireEvent.change(searchInput, { target: { value: '张三' } });
// 点击重置按钮
const resetButton = screen.getByTestId('reset-button');
fireEvent.click(resetButton);
// 验证搜索输入被清空
expect(searchInput).toHaveValue('');
});
it('应该处理单选模式', async () => {
renderComponent({ mode: 'single' });
await waitFor(() => {
expect(screen.getByTestId('data-table')).toBeInTheDocument();
});
// 点击表格行选择人员
const firstRow = screen.getByTestId('table-row-0');
fireEvent.click(firstRow);
// 验证选择回调被调用
expect(onSelect).toHaveBeenCalledWith(expect.objectContaining({
id: 1,
name: '张三'
}));
expect(onOpenChange).toHaveBeenCalledWith(false);
});
it('应该处理多选模式', async () => {
renderComponent({ mode: 'multiple' });
await waitFor(() => {
expect(screen.getByTestId('data-table')).toBeInTheDocument();
});
// 应该显示多选相关的UI
expect(screen.getAllByTestId('checkbox')).toHaveLength(3); // 全选复选框 + 每行复选框
// 点击确认批量选择按钮(初始时禁用)
const confirmButton = screen.getByTestId('confirm-batch-button');
expect(confirmButton).toBeDisabled();
});
it('应该处理黑名单人员确认', async () => {
renderComponent({ mode: 'single' });
await waitFor(() => {
expect(screen.getByTestId('data-table')).toBeInTheDocument();
});
// 点击黑名单人员(李四)
const blacklistRow = screen.getByTestId('table-row-1');
fireEvent.click(blacklistRow);
// 应该显示黑名单确认对话框
await waitFor(() => {
expect(screen.getByTestId('alert')).toBeInTheDocument();
expect(screen.getByTestId('alert-description')).toHaveTextContent('您选择的人员在黑名单中,是否确认选择?');
});
// 点击确认选择
const confirmButton = screen.getByTestId('confirm-blacklist-button');
fireEvent.click(confirmButton);
// 验证选择回调被调用
expect(onSelect).toHaveBeenCalledWith(expect.objectContaining({
id: 2,
name: '李四',
isInBlackList: 1
}));
expect(onOpenChange).toHaveBeenCalledWith(false);
});
it('应该处理禁用的人员', async () => {
renderComponent({
mode: 'single',
disabledIds: [1] // 禁用张三
});
await waitFor(() => {
expect(screen.getByTestId('data-table')).toBeInTheDocument();
});
// 点击禁用的人员应该没有反应
const disabledRow = screen.getByTestId('table-row-0');
fireEvent.click(disabledRow);
expect(onSelect).not.toHaveBeenCalled();
});
it('应该处理分页', async () => {
renderComponent();
await waitFor(() => {
expect(screen.getByTestId('pagination')).toBeInTheDocument();
});
// 点击下一页
const nextPageButton = screen.getByTestId('next-page');
fireEvent.click(nextPageButton);
// 验证API被调用(第2页)
await waitFor(() => {
expect(disabilityClientManager.get().getAllDisabledPersons.$get).toHaveBeenCalledWith({
query: {
skip: 10,
take: 10
}
});
});
});
it('应该处理对话框关闭', () => {
renderComponent();
// 点击取消按钮
const cancelButton = screen.getByTestId('cancel-button');
fireEvent.click(cancelButton);
expect(onOpenChange).toHaveBeenCalledWith(false);
});
});