UserBasicForm.test.tsx 5.88 KB
// REQ-USR-001 / REQ-USR-002: UserBasicForm 表单网格单测(字段/默认/只读/枚举/必填+格式/员工联动,BR1-BR9)
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { screen, waitFor, within } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { Form } from 'antd';
import { renderShell } from './renderShell';
import UserBasicForm from '../../src/pages/usr/UserDetail/UserBasicForm';
import { CREATE_DEFAULTS, type UserFormValues } from '../../src/pages/usr/UserDetail/constants';
import type { EmployeeOption } from '../../src/api/types';

const EMPLOYEES: EmployeeOption[] = [
  { value: 3, label: '张三', sEmployeeNo: 'zs' },
  { value: 4, label: '李四', sEmployeeNo: 'ls' },
];

interface HarnessProps {
  mode?: 'create' | 'edit';
  initialValues?: Partial<UserFormValues>;
  onSelectEmployee?: (v: number | null) => void;
  readonlyCreator?: string;
  exposeSubmit?: (submit: () => void) => void;
}

function Harness({
  mode = 'create',
  initialValues,
  onSelectEmployee = () => {},
  readonlyCreator,
  exposeSubmit,
}: HarnessProps) {
  const [form] = Form.useForm<UserFormValues>();
  exposeSubmit?.(() => form.submit());
  return (
    <Form
      form={form}
      initialValues={{ ...CREATE_DEFAULTS, ...initialValues }}
      onFinish={() => {}}
    >
      <UserBasicForm
        form={form}
        mode={mode}
        employees={EMPLOYEES}
        readonlyCreator={readonlyCreator}
        onSelectEmployee={onSelectEmployee}
      />
    </Form>
  );
}

function setup(props: HarnessProps = {}) {
  let submit = () => {};
  renderShell(
    <Harness {...props} exposeSubmit={(s) => { submit = s; }} />,
    { preloadedAuth: { token: 't', user: { id: 1, sUserName: 'a', sUserType: 'x', sLanguage: '中文' } } },
  );
  return { triggerSubmit: () => submit() };
}

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

  it('renders 8 labeled fields', () => {
    setup();
    expect(screen.getByText('创建时间')).toBeInTheDocument();
    expect(screen.getByText('制单人')).toBeInTheDocument();
    expect(screen.getByText('员工名')).toBeInTheDocument();
    expect(screen.getByText('用户名')).toBeInTheDocument();
    expect(screen.getByText('类型')).toBeInTheDocument();
    expect(screen.getByText('语言')).toBeInTheDocument();
    expect(screen.getByText('用户号')).toBeInTheDocument();
    expect(screen.getByText('单据修改权限')).toBeInTheDocument();
  });

  it('create mode username editable; edit mode username disabled', () => {
    const { unmount } = renderShell(
      <Harness mode="create" />,
      { preloadedAuth: { token: 't', user: { id: 1, sUserName: 'a', sUserType: 'x', sLanguage: '中文' } } },
    );
    expect(screen.getByTestId('field-username')).not.toBeDisabled();
    unmount();
    renderShell(
      <Harness mode="edit" initialValues={{ sUserName: 'zhangsan' }} />,
      { preloadedAuth: { token: 't', user: { id: 1, sUserName: 'a', sUserType: 'x', sLanguage: '中文' } } },
    );
    expect(screen.getByTestId('field-username')).toBeDisabled();
  });

  it('create mode defaults usertype 普通用户', () => {
    setup({ mode: 'create' });
    expect(within(screen.getByTestId('select-usertype')).getByText('普通用户')).toBeInTheDocument();
  });

  it('username format rule rejects short/invalid and required when empty', async () => {
    const user = userEvent.setup();
    const { triggerSubmit } = setup({ mode: 'create' });
    const input = screen.getByTestId('field-username') as HTMLInputElement;
    await user.type(input, 'ab');
    triggerSubmit();
    expect(await screen.findByText('用户名须为 3-20 位字母数字下划线')).toBeInTheDocument();
    await user.clear(input);
    triggerSubmit();
    expect(await screen.findByText('请输入用户名')).toBeInTheDocument();
  });

  it('userno required', async () => {
    const { triggerSubmit } = setup({ mode: 'create', initialValues: { sUserName: 'zhangsan', sUserNo: '' } });
    triggerSubmit();
    expect(await screen.findByText('请输入用户号')).toBeInTheDocument();
  });

  it('usertype/language selects expose enum options only', async () => {
    const user = userEvent.setup();
    setup({ mode: 'create' });
    await user.click(screen.getByTestId('select-usertype').querySelector('.ant-select-selector')!);
    await waitFor(() => expect(screen.getAllByText('超级管理员').length).toBeGreaterThan(0));
    await user.click(screen.getByTestId('select-language').querySelector('.ant-select-selector')!);
    await waitFor(() => {
      expect(screen.getByText('英文')).toBeInTheDocument();
      expect(screen.getByText('繁体')).toBeInTheDocument();
    });
  });

  it('create mode creator shows 保存后自动生成; edit shows readonlyCreator', () => {
    const { unmount } = renderShell(
      <Harness mode="create" />,
      { preloadedAuth: { token: 't', user: { id: 1, sUserName: 'a', sUserType: 'x', sLanguage: '中文' } } },
    );
    expect(screen.getByText('保存后自动生成')).toBeInTheDocument();
    unmount();
    renderShell(
      <Harness mode="edit" readonlyCreator="admin" />,
      { preloadedAuth: { token: 't', user: { id: 1, sUserName: 'a', sUserType: 'x', sLanguage: '中文' } } },
    );
    expect(screen.getByText('admin')).toBeInTheDocument();
  });

  it('selecting employee calls onSelectEmployee', async () => {
    const user = userEvent.setup();
    const onSelectEmployee = vi.fn();
    setup({ mode: 'create', onSelectEmployee });
    await user.click(screen.getByTestId('select-employee').querySelector('.ant-select-selector')!);
    await user.click(await screen.findByText('张三'));
    expect(onSelectEmployee).toHaveBeenCalledWith(3);
  });

  it('单据修改权限 checkbox default unchecked (create)', () => {
    setup({ mode: 'create' });
    const cb = screen.getByTestId('field-canmodify') as HTMLInputElement;
    expect(cb.checked).toBe(false);
  });
});