| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379 |
- import { describe, it, expect, vi, beforeEach, Mock } from 'vitest';
- import { render, screen, fireEvent, waitFor } from '@testing-library/react';
- import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
- import SalarySelector from '../../src/components/SalarySelector';
- import { salaryClientManager } from '../../src/api/salaryClient';
- // AreaSelect is mocked below
- // Mock AreaSelect组件
- vi.mock('@d8d/area-management-ui/components', () => ({
- AreaSelect: vi.fn(({ value, onChange, disabled, required }) => (
- <div data-testid="area-select">
- <label>
- {required ? '选择区域*' : '选择区域'}
- <select
- data-testid="province-select"
- value={value?.provinceId || ''}
- onChange={(e) => onChange?.({ ...value, provinceId: e.target.value ? Number(e.target.value) : undefined })}
- disabled={disabled}
- >
- <option value="">选择省份</option>
- <option value="110000">北京市</option>
- <option value="310000">上海市</option>
- <option value="440000">广东省</option>
- </select>
- <select
- data-testid="city-select"
- value={value?.cityId || ''}
- onChange={(e) => onChange?.({ ...value, cityId: e.target.value ? Number(e.target.value) : undefined })}
- disabled={disabled || !value?.provinceId}
- >
- <option value="">选择城市</option>
- <option value="110100">北京市辖区</option>
- <option value="310100">上海市辖区</option>
- <option value="440100">广州市</option>
- </select>
- </label>
- </div>
- ))
- }));
- // 完整的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/salaryClient', () => {
- const mockSalaryClient = {
- byProvinceCity: {
- $get: vi.fn()
- }
- };
- const mockClientManager = {
- get: vi.fn(() => mockSalaryClient),
- reset: vi.fn()
- };
- return {
- salaryClientManager: mockClientManager,
- salaryClient: mockSalaryClient
- };
- });
- describe('薪资选择器集成测试', () => {
- let queryClient: QueryClient;
- let mockOnChange: Mock;
- let mockSalaryClient: any;
- beforeEach(() => {
- queryClient = new QueryClient({
- defaultOptions: {
- queries: {
- retry: false,
- },
- },
- });
- mockOnChange = vi.fn();
- // 从模拟中获取mock客户端
- mockSalaryClient = salaryClientManager.get();
- vi.clearAllMocks();
- });
- const renderComponent = (props = {}) => {
- return render(
- <QueryClientProvider client={queryClient}>
- <SalarySelector onChange={mockOnChange} {...props} />
- </QueryClientProvider>
- );
- };
- it('应该正确渲染薪资选择器组件', () => {
- renderComponent();
- // 检查区域选择器 - 使用更精确的选择器,因为现在有两个"选择区域"文本
- expect(screen.getAllByText('选择区域').length).toBeGreaterThanOrEqual(1);
- expect(screen.getByTestId('area-select')).toBeInTheDocument();
- // 检查初始状态提示
- expect(screen.getByText('请先选择省份和城市以查询薪资信息')).toBeInTheDocument();
- });
- it('应该选择区域后查询薪资', async () => {
- // Mock成功的薪资查询
- mockSalaryClient.byProvinceCity.$get.mockResolvedValueOnce(
- createMockResponse(200, {
- id: 1,
- provinceId: 110000,
- cityId: 110100,
- districtId: null,
- basicSalary: 5000.00,
- allowance: 1000.00,
- insurance: 500.00,
- housingFund: 800.00,
- totalSalary: 4700.00,
- updateTime: '2024-01-01T00:00:00Z'
- })
- );
- renderComponent();
- // 选择省份
- const provinceSelect = screen.getByTestId('province-select');
- fireEvent.change(provinceSelect, { target: { value: '110000' } });
- // 选择城市
- const citySelect = screen.getByTestId('city-select');
- fireEvent.change(citySelect, { target: { value: '110100' } });
- // 验证API调用
- await waitFor(() => {
- expect(mockSalaryClient.byProvinceCity.$get).toHaveBeenCalledWith({
- query: {
- provinceId: 110000,
- cityId: 110100
- }
- });
- });
- // 验证onChange被调用
- expect(mockOnChange).toHaveBeenCalledWith({
- provinceId: 110000,
- cityId: 110100,
- salary: undefined,
- salaryDetail: undefined
- });
- // 等待薪资信息显示
- await waitFor(() => {
- expect(screen.getByText('薪资信息')).toBeInTheDocument();
- expect(screen.getByText('自动查询薪资模式')).toBeInTheDocument();
- expect(screen.getByText('基本工资')).toBeInTheDocument();
- expect(screen.getByText('¥5000.00')).toBeInTheDocument();
- expect(screen.getByText('总薪资')).toBeInTheDocument();
- expect(screen.getByText('¥4700.00')).toBeInTheDocument();
- });
- // 验证最终的onChange调用(包含薪资详情)
- await waitFor(() => {
- expect(mockOnChange).toHaveBeenCalledWith({
- provinceId: 110000,
- cityId: 110100,
- salary: 4700.00,
- salaryDetail: expect.objectContaining({
- basicSalary: 5000.00,
- allowance: 1000.00,
- insurance: 500.00,
- housingFund: 800.00
- })
- });
- });
- });
- it('应该处理区域无薪资记录的情况', async () => {
- // Mock 404响应(无薪资记录)
- mockSalaryClient.byProvinceCity.$get.mockResolvedValueOnce(
- createMockResponse(404, { code: 404, message: '该省份城市的薪资记录不存在' })
- );
- renderComponent({ allowManualAdjust: true });
- // 选择区域
- const provinceSelect = screen.getByTestId('province-select');
- fireEvent.change(provinceSelect, { target: { value: '440000' } });
- const citySelect = screen.getByTestId('city-select');
- fireEvent.change(citySelect, { target: { value: '440100' } });
- // 检查无薪资记录提示
- await waitFor(() => {
- expect(screen.getByText('该区域暂无薪资记录')).toBeInTheDocument();
- expect(screen.getByText('您可以切换到手动模式输入薪资,或联系管理员添加该区域的薪资标准')).toBeInTheDocument();
- });
- });
- it('应该支持手动调整薪资模式', async () => {
- // Mock成功的薪资查询
- mockSalaryClient.byProvinceCity.$get.mockResolvedValueOnce(
- createMockResponse(200, {
- id: 1,
- provinceId: 110000,
- cityId: 110100,
- basicSalary: 5000.00,
- allowance: 1000.00,
- insurance: 500.00,
- housingFund: 800.00,
- totalSalary: 4700.00
- })
- );
- renderComponent({ allowManualAdjust: true });
- // 选择区域
- const provinceSelect = screen.getByTestId('province-select');
- fireEvent.change(provinceSelect, { target: { value: '110000' } });
- const citySelect = screen.getByTestId('city-select');
- fireEvent.change(citySelect, { target: { value: '110100' } });
- // 等待自动查询完成并显示切换按钮
- await waitFor(() => {
- expect(screen.getByText('自动查询薪资模式')).toBeInTheDocument();
- expect(screen.getByText('切换到手动调整模式')).toBeInTheDocument();
- });
- // 切换到手动模式
- const toggleButton = screen.getByText('切换到手动调整模式');
- fireEvent.click(toggleButton);
- // 检查手动模式
- await waitFor(() => {
- expect(screen.getByText('手动调整薪资模式')).toBeInTheDocument();
- expect(screen.getByLabelText('手动输入薪资')).toBeInTheDocument();
- expect(screen.getByDisplayValue('4700')).toBeInTheDocument(); // 自动查询的总薪资
- });
- // 修改手动薪资
- const salaryInput = screen.getByLabelText('手动输入薪资');
- fireEvent.change(salaryInput, { target: { value: '6000' } });
- // 验证onChange被调用
- expect(mockOnChange).toHaveBeenCalledWith({
- provinceId: 110000,
- cityId: 110100,
- salary: 6000,
- salaryDetail: undefined
- });
- // 检查总薪资显示(等待状态更新)
- await waitFor(() => {
- // 查找包含"6000.00"的文本
- expect(screen.getByText(/6000\.00/)).toBeInTheDocument();
- });
- // 切换回自动模式
- fireEvent.click(screen.getByText('切换回自动查询模式'));
- // 验证重新查询
- await waitFor(() => {
- expect(mockSalaryClient.byProvinceCity.$get).toHaveBeenCalledTimes(2); // 初始查询 + 重新查询
- });
- });
- it('应该禁用手动调整时隐藏切换按钮', async () => {
- // Mock成功的薪资查询
- mockSalaryClient.byProvinceCity.$get.mockResolvedValueOnce(
- createMockResponse(200, {
- id: 1,
- provinceId: 110000,
- cityId: 110100,
- basicSalary: 5000.00,
- allowance: 1000.00,
- insurance: 500.00,
- housingFund: 800.00,
- totalSalary: 4700.00
- })
- );
- renderComponent({ allowManualAdjust: false });
- // 选择区域
- const provinceSelect = screen.getByTestId('province-select');
- fireEvent.change(provinceSelect, { target: { value: '110000' } });
- const citySelect = screen.getByTestId('city-select');
- fireEvent.change(citySelect, { target: { value: '110100' } });
- // 等待自动查询完成
- await waitFor(() => {
- expect(screen.getByText('自动查询薪资模式')).toBeInTheDocument();
- });
- // 检查切换按钮不存在
- expect(screen.queryByText('切换到手动调整模式')).not.toBeInTheDocument();
- });
- it('应该处理API查询错误', async () => {
- // Mock API错误
- mockSalaryClient.byProvinceCity.$get.mockRejectedValueOnce(new Error('网络错误'));
- renderComponent();
- // 选择区域
- const provinceSelect = screen.getByTestId('province-select');
- fireEvent.change(provinceSelect, { target: { value: '110000' } });
- const citySelect = screen.getByTestId('city-select');
- fireEvent.change(citySelect, { target: { value: '110100' } });
- // 检查错误提示
- await waitFor(() => {
- expect(screen.getByText('查询薪资信息失败,请检查网络连接或稍后重试')).toBeInTheDocument();
- });
- });
- it('应该支持外部值控制', async () => {
- const initialValue = {
- provinceId: 110000,
- cityId: 110100,
- salary: 5000,
- salaryDetail: {
- id: 1,
- provinceId: 110000,
- cityId: 110100,
- basicSalary: 5000.00,
- allowance: 1000.00,
- insurance: 500.00,
- housingFund: 800.00,
- totalSalary: 4700.00
- } as any
- };
- // Mock API调用,返回与外部值相同的数据
- mockSalaryClient.byProvinceCity.$get.mockResolvedValueOnce(
- createMockResponse(200, initialValue.salaryDetail)
- );
- renderComponent({ value: initialValue });
- // 检查区域选择器已设置值
- const provinceSelect = screen.getByTestId('province-select') as HTMLSelectElement;
- const citySelect = screen.getByTestId('city-select') as HTMLSelectElement;
- expect(provinceSelect.value).toBe('110000');
- expect(citySelect.value).toBe('110100');
- // 检查薪资信息显示
- await waitFor(() => {
- expect(screen.getByText('薪资信息')).toBeInTheDocument();
- expect(screen.getByText('¥5000.00')).toBeInTheDocument(); // 基本工资
- expect(screen.getByText('¥4700.00')).toBeInTheDocument(); // 总薪资
- });
- });
- it('应该支持禁用状态', () => {
- renderComponent({ disabled: true });
- // 检查区域选择器被禁用
- const provinceSelect = screen.getByTestId('province-select');
- expect(provinceSelect).toBeDisabled();
- });
- it('应该支持必填验证', () => {
- renderComponent({ required: true });
- // 检查必填标记
- expect(screen.getByText('选择区域*')).toBeInTheDocument();
- });
- });
|