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