UserFilterBar.test.tsx 5.05 KB
// REQ-USR-003: UserFilterBar 筛选栏单测(BR2/BR3/BR4/BR7/BR10/D2/D3)
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { screen, within } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { renderShell } from './renderShell';
import UserFilterBar from '../../src/pages/usr/UserList/UserFilterBar';
import { DEFAULT_QUERY } from '../../src/pages/usr/UserList/constants';
import type { UserListQuery } from '../../src/api/types';

function setup(overrides?: Partial<UserListQuery>) {
  const onChangeQueryField = vi.fn();
  const onChangeMatchType = vi.fn();
  const onChangeQueryValue = vi.fn();
  const onSearch = vi.fn();
  const onClear = vi.fn();
  const query: UserListQuery = { ...DEFAULT_QUERY, ...overrides };
  renderShell(
    <UserFilterBar
      query={query}
      onChangeQueryField={onChangeQueryField}
      onChangeMatchType={onChangeMatchType}
      onChangeQueryValue={onChangeQueryValue}
      onSearch={onSearch}
      onClear={onClear}
    />,
    { preloadedAuth: { token: 't', user: { id: 1, sUserName: 'a', sUserType: 'x', sLanguage: '中文' } } },
  );
  return { onChangeQueryField, onChangeMatchType, onChangeQueryValue, onSearch, onClear };
}

describe('UserFilterBar', () => {
  beforeEach(() => {
    vi.clearAllMocks();
  });

  it('renders defaults 用户名 / 包含 and empty value', () => {
    setup();
    const fieldSel = within(screen.getByTestId('filter-query-field'));
    expect(fieldSel.getByText('用户名')).toBeInTheDocument();
    const matchSel = within(screen.getByTestId('filter-match-type'));
    expect(matchSel.getByText('包含')).toBeInTheDocument();
    const input = screen.getByTestId('filter-query-value').querySelector('input')!;
    expect(input.value).toBe('');
  });

  it('query field options match enum (BR4)', async () => {
    const user = userEvent.setup();
    setup();
    // 展开查询字段下拉
    const combobox = within(screen.getByTestId('filter-query-field')).getByRole('combobox');
    await user.click(combobox);
    // AntD options 渲染为 role=option(在 document.body 的下拉层)
    const options = await screen.findAllByRole('option');
    const labels = options.map((o) => o.textContent);
    for (const label of ['用户名', '员工名', '用户号', '部门', '用户类型', '作废', '登录日期', '制单人']) {
      expect(labels).toContain(label);
    }
    expect(options).toHaveLength(8);
  });

  it('match type options are 包含/不包含/等于 (BR4)', async () => {
    const user = userEvent.setup();
    setup();
    const combobox = within(screen.getByTestId('filter-match-type')).getByRole('combobox');
    await user.click(combobox);
    const options = await screen.findAllByRole('option');
    const labels = options.map((o) => o.textContent);
    expect(labels).toEqual(['包含', '不包含', '等于']);
  });

  it('scope select shows 全部用户 only (D2)', () => {
    setup();
    const scope = within(screen.getByTestId('filter-scope'));
    expect(scope.getByText('全部用户')).toBeInTheDocument();
  });

  it('Enter in value triggers onSearch (BR7)', async () => {
    const user = userEvent.setup();
    const { onSearch } = setup();
    const input = screen.getByTestId('filter-query-value').querySelector('input')!;
    input.focus();
    await user.keyboard('{Enter}');
    expect(onSearch).toHaveBeenCalledTimes(1);
  });

  it('click 搜索 calls onSearch / click 清空 calls onClear (BR7/BR10)', async () => {
    const user = userEvent.setup();
    const { onSearch, onClear } = setup();
    await user.click(screen.getByTestId('btn-search'));
    expect(onSearch).toHaveBeenCalledTimes(1);
    await user.click(screen.getByTestId('btn-clear'));
    expect(onClear).toHaveBeenCalledTimes(1);
  });

  it('typing value calls onChangeQueryValue', async () => {
    const user = userEvent.setup();
    const { onChangeQueryValue } = setup();
    const input = screen.getByTestId('filter-query-value').querySelector('input')!;
    await user.type(input, 'x');
    expect(onChangeQueryValue).toHaveBeenCalled();
    expect(onChangeQueryValue).toHaveBeenLastCalledWith('x');
  });

  it('changing query field select calls onChangeQueryField', async () => {
    const user = userEvent.setup();
    const { onChangeQueryField } = setup();
    const combobox = within(screen.getByTestId('filter-query-field')).getByRole('combobox');
    await user.click(combobox);
    const options = await screen.findAllByRole('option');
    const target = options.find((o) => o.textContent === '员工名')!;
    await user.click(target);
    // AntD Select.onChange 透传 (value, option),回调首参即选中值
    expect(onChangeQueryField).toHaveBeenCalled();
    expect(onChangeQueryField.mock.calls[0][0]).toBe('员工名');
  });

  it('more toggle ▾ is placeholder (no extra callback) (D3)', async () => {
    const user = userEvent.setup();
    const { onSearch, onChangeQueryField } = setup();
    await user.click(screen.getByTestId('filter-more'));
    expect(onSearch).not.toHaveBeenCalled();
    expect(onChangeQueryField).not.toHaveBeenCalled();
  });
});