|
|
@@ -0,0 +1,309 @@
|
|
|
+import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
|
+import { render, screen, fireEvent, waitFor, act } from '@testing-library/react';
|
|
|
+import { useForm, FormProvider } from 'react-hook-form';
|
|
|
+import { zodResolver } from '@hookform/resolvers/zod';
|
|
|
+import { z } from 'zod';
|
|
|
+import { AreaSelectForm } from '../../src/components/AreaSelectForm';
|
|
|
+import { Button } from '@d8d/shared-ui-components/components/ui/button';
|
|
|
+import { Form } from '@d8d/shared-ui-components/components/ui/form';
|
|
|
+
|
|
|
+// 测试用的schema
|
|
|
+const TestSchema = z.object({
|
|
|
+ province: z.string().min(1, '省份不能为空'),
|
|
|
+ city: z.string().min(1, '城市不能为空'),
|
|
|
+ district: z.string().optional(),
|
|
|
+});
|
|
|
+
|
|
|
+type TestFormData = z.infer<typeof TestSchema>;
|
|
|
+
|
|
|
+// Mock AreaSelect 组件
|
|
|
+vi.mock('../../src/components/AreaSelect', () => ({
|
|
|
+ AreaSelect: vi.fn(({ value, onChange, disabled, required }) => {
|
|
|
+ return (
|
|
|
+ <div data-testid="area-select">
|
|
|
+ <select
|
|
|
+ data-testid="province-select"
|
|
|
+ value={value?.provinceId || ''}
|
|
|
+ onChange={(e) => {
|
|
|
+ const newValue = {
|
|
|
+ ...value,
|
|
|
+ provinceId: e.target.value ? Number(e.target.value) : undefined,
|
|
|
+ };
|
|
|
+ onChange(newValue);
|
|
|
+ }}
|
|
|
+ disabled={disabled}
|
|
|
+ // 移除 required 属性,让 react-hook-form 处理验证
|
|
|
+ >
|
|
|
+ <option value="">选择省份</option>
|
|
|
+ <option value="1">北京市</option>
|
|
|
+ <option value="2">上海市</option>
|
|
|
+ </select>
|
|
|
+ <select
|
|
|
+ data-testid="city-select"
|
|
|
+ value={value?.cityId || ''}
|
|
|
+ onChange={(e) => {
|
|
|
+ const newValue = {
|
|
|
+ ...value,
|
|
|
+ cityId: e.target.value ? Number(e.target.value) : undefined,
|
|
|
+ };
|
|
|
+ onChange(newValue);
|
|
|
+ }}
|
|
|
+ disabled={disabled}
|
|
|
+ // 移除 required 属性,让 react-hook-form 处理验证
|
|
|
+ >
|
|
|
+ <option value="">选择城市</option>
|
|
|
+ <option value="3">北京市</option>
|
|
|
+ <option value="4">上海市</option>
|
|
|
+ </select>
|
|
|
+ <select
|
|
|
+ data-testid="district-select"
|
|
|
+ value={value?.districtId || ''}
|
|
|
+ onChange={(e) => {
|
|
|
+ const newValue = {
|
|
|
+ ...value,
|
|
|
+ districtId: e.target.value ? Number(e.target.value) : undefined,
|
|
|
+ };
|
|
|
+ onChange(newValue);
|
|
|
+ }}
|
|
|
+ disabled={disabled}
|
|
|
+ >
|
|
|
+ <option value="">选择区县</option>
|
|
|
+ <option value="5">东城区</option>
|
|
|
+ <option value="6">黄浦区</option>
|
|
|
+ </select>
|
|
|
+ </div>
|
|
|
+ );
|
|
|
+ }),
|
|
|
+}));
|
|
|
+
|
|
|
+// 测试组件
|
|
|
+const TestForm = () => {
|
|
|
+ const form = useForm<TestFormData>({
|
|
|
+ resolver: zodResolver(TestSchema),
|
|
|
+ defaultValues: {
|
|
|
+ province: '',
|
|
|
+ city: '',
|
|
|
+ district: '',
|
|
|
+ },
|
|
|
+ mode: 'onSubmit', // 使用onSubmit模式,这是默认值
|
|
|
+ });
|
|
|
+
|
|
|
+ const onSubmit = vi.fn();
|
|
|
+
|
|
|
+ return (
|
|
|
+ <FormProvider {...form}>
|
|
|
+ <Form {...form}>
|
|
|
+ <form onSubmit={form.handleSubmit(onSubmit)}>
|
|
|
+ <AreaSelectForm<TestFormData>
|
|
|
+ provinceName="province"
|
|
|
+ cityName="city"
|
|
|
+ districtName="district"
|
|
|
+ label="地区选择"
|
|
|
+ required={true}
|
|
|
+ control={form.control}
|
|
|
+ />
|
|
|
+ <Button type="submit" data-testid="submit-button">
|
|
|
+ 提交
|
|
|
+ </Button>
|
|
|
+ </form>
|
|
|
+ </Form>
|
|
|
+ </FormProvider>
|
|
|
+ );
|
|
|
+};
|
|
|
+
|
|
|
+describe('AreaSelectForm 集成测试', () => {
|
|
|
+ beforeEach(() => {
|
|
|
+ vi.clearAllMocks();
|
|
|
+ });
|
|
|
+
|
|
|
+ it('应该正确渲染AreaSelectForm组件', () => {
|
|
|
+ render(<TestForm />);
|
|
|
+
|
|
|
+ expect(screen.getByText('地区选择')).toBeInTheDocument();
|
|
|
+ // 检查是否包含必填标记(星号)
|
|
|
+ const label = screen.getByText('地区选择');
|
|
|
+ expect(label.parentElement).toContainHTML('*');
|
|
|
+ expect(screen.getByTestId('area-select')).toBeInTheDocument();
|
|
|
+ });
|
|
|
+
|
|
|
+ it('应该显示省份和城市的验证错误当表单提交时', async () => {
|
|
|
+ let formRef: any;
|
|
|
+ const TestFormWithRef = () => {
|
|
|
+ const form = useForm<TestFormData>({
|
|
|
+ resolver: zodResolver(TestSchema),
|
|
|
+ defaultValues: {
|
|
|
+ province: '',
|
|
|
+ city: '',
|
|
|
+ district: '',
|
|
|
+ },
|
|
|
+ mode: 'onSubmit',
|
|
|
+ });
|
|
|
+
|
|
|
+ formRef = form;
|
|
|
+
|
|
|
+ const onSubmit = vi.fn();
|
|
|
+
|
|
|
+ return (
|
|
|
+ <FormProvider {...form}>
|
|
|
+ <Form {...form}>
|
|
|
+ <form onSubmit={form.handleSubmit(onSubmit)}>
|
|
|
+ <AreaSelectForm<TestFormData>
|
|
|
+ provinceName="province"
|
|
|
+ cityName="city"
|
|
|
+ districtName="district"
|
|
|
+ label="地区选择"
|
|
|
+ required={true}
|
|
|
+ control={form.control}
|
|
|
+ />
|
|
|
+ <Button type="submit" data-testid="submit-button">
|
|
|
+ 提交
|
|
|
+ </Button>
|
|
|
+ </form>
|
|
|
+ </Form>
|
|
|
+ </FormProvider>
|
|
|
+ );
|
|
|
+ };
|
|
|
+
|
|
|
+ render(<TestFormWithRef />);
|
|
|
+
|
|
|
+ // 初始状态不应该有验证错误
|
|
|
+ expect(screen.queryByText('省份不能为空')).not.toBeInTheDocument();
|
|
|
+ expect(screen.queryByText('城市不能为空')).not.toBeInTheDocument();
|
|
|
+
|
|
|
+ // 提交表单(不选择任何省份和城市)
|
|
|
+ const submitButton = screen.getByTestId('submit-button');
|
|
|
+ await act(async () => {
|
|
|
+ fireEvent.click(submitButton);
|
|
|
+ });
|
|
|
+
|
|
|
+ // 等待一下,让验证完成
|
|
|
+ await new Promise(resolve => setTimeout(resolve, 100));
|
|
|
+
|
|
|
+ // 等待验证错误显示
|
|
|
+ await waitFor(() => {
|
|
|
+ expect(screen.getByText('省份不能为空')).toBeInTheDocument();
|
|
|
+ expect(screen.getByText('城市不能为空')).toBeInTheDocument();
|
|
|
+ }, { timeout: 3000 });
|
|
|
+ });
|
|
|
+
|
|
|
+ it('应该正确更新表单字段值当选择省份和城市时', async () => {
|
|
|
+ render(<TestForm />);
|
|
|
+
|
|
|
+ // 选择省份
|
|
|
+ const provinceSelect = screen.getByTestId('province-select');
|
|
|
+ fireEvent.change(provinceSelect, { target: { value: '1' } });
|
|
|
+
|
|
|
+ // 选择城市
|
|
|
+ const citySelect = screen.getByTestId('city-select');
|
|
|
+ fireEvent.change(citySelect, { target: { value: '3' } });
|
|
|
+
|
|
|
+ // 提交表单
|
|
|
+ const submitButton = screen.getByTestId('submit-button');
|
|
|
+ fireEvent.click(submitButton);
|
|
|
+
|
|
|
+ // 不应该有验证错误
|
|
|
+ await waitFor(() => {
|
|
|
+ expect(screen.queryByText('省份不能为空')).not.toBeInTheDocument();
|
|
|
+ expect(screen.queryByText('城市不能为空')).not.toBeInTheDocument();
|
|
|
+ });
|
|
|
+ });
|
|
|
+
|
|
|
+ it('应该显示验证错误当只选择省份不选择城市时', async () => {
|
|
|
+ render(<TestForm />);
|
|
|
+
|
|
|
+ // 只选择省份
|
|
|
+ const provinceSelect = screen.getByTestId('province-select');
|
|
|
+ fireEvent.change(provinceSelect, { target: { value: '1' } });
|
|
|
+
|
|
|
+ // 提交表单
|
|
|
+ const submitButton = screen.getByTestId('submit-button');
|
|
|
+ fireEvent.click(submitButton);
|
|
|
+
|
|
|
+ // 应该只有城市验证错误
|
|
|
+ await waitFor(() => {
|
|
|
+ expect(screen.queryByText('省份不能为空')).not.toBeInTheDocument();
|
|
|
+ expect(screen.getByText('城市不能为空')).toBeInTheDocument();
|
|
|
+ });
|
|
|
+ });
|
|
|
+
|
|
|
+ it('应该显示验证错误当只选择城市不选择省份时', async () => {
|
|
|
+ render(<TestForm />);
|
|
|
+
|
|
|
+ // 只选择城市
|
|
|
+ const citySelect = screen.getByTestId('city-select');
|
|
|
+ fireEvent.change(citySelect, { target: { value: '3' } });
|
|
|
+
|
|
|
+ // 提交表单
|
|
|
+ const submitButton = screen.getByTestId('submit-button');
|
|
|
+ fireEvent.click(submitButton);
|
|
|
+
|
|
|
+ // 应该只有省份验证错误
|
|
|
+ await waitFor(() => {
|
|
|
+ expect(screen.getByText('省份不能为空')).toBeInTheDocument();
|
|
|
+ expect(screen.queryByText('城市不能为空')).not.toBeInTheDocument();
|
|
|
+ });
|
|
|
+ });
|
|
|
+
|
|
|
+ it('应该正确处理区县字段(可选)', async () => {
|
|
|
+ render(<TestForm />);
|
|
|
+
|
|
|
+ // 选择省份和城市
|
|
|
+ const provinceSelect = screen.getByTestId('province-select');
|
|
|
+ const citySelect = screen.getByTestId('city-select');
|
|
|
+ fireEvent.change(provinceSelect, { target: { value: '1' } });
|
|
|
+ fireEvent.change(citySelect, { target: { value: '3' } });
|
|
|
+
|
|
|
+ // 选择区县(可选)
|
|
|
+ const districtSelect = screen.getByTestId('district-select');
|
|
|
+ fireEvent.change(districtSelect, { target: { value: '5' } });
|
|
|
+
|
|
|
+ // 提交表单
|
|
|
+ const submitButton = screen.getByTestId('submit-button');
|
|
|
+ fireEvent.click(submitButton);
|
|
|
+
|
|
|
+ // 不应该有验证错误
|
|
|
+ await waitFor(() => {
|
|
|
+ expect(screen.queryByText('省份不能为空')).not.toBeInTheDocument();
|
|
|
+ expect(screen.queryByText('城市不能为空')).not.toBeInTheDocument();
|
|
|
+ });
|
|
|
+ });
|
|
|
+
|
|
|
+ it('应该支持禁用状态', () => {
|
|
|
+ const DisabledTestForm = () => {
|
|
|
+ const form = useForm<TestFormData>({
|
|
|
+ resolver: zodResolver(TestSchema),
|
|
|
+ defaultValues: {
|
|
|
+ province: '',
|
|
|
+ city: '',
|
|
|
+ district: '',
|
|
|
+ },
|
|
|
+ });
|
|
|
+
|
|
|
+ return (
|
|
|
+ <FormProvider {...form}>
|
|
|
+ <Form {...form}>
|
|
|
+ <AreaSelectForm<TestFormData>
|
|
|
+ provinceName="province"
|
|
|
+ cityName="city"
|
|
|
+ districtName="district"
|
|
|
+ label="地区选择"
|
|
|
+ required={true}
|
|
|
+ disabled={true}
|
|
|
+ control={form.control}
|
|
|
+ />
|
|
|
+ </Form>
|
|
|
+ </FormProvider>
|
|
|
+ );
|
|
|
+ };
|
|
|
+
|
|
|
+ render(<DisabledTestForm />);
|
|
|
+
|
|
|
+ const provinceSelect = screen.getByTestId('province-select');
|
|
|
+ const citySelect = screen.getByTestId('city-select');
|
|
|
+ const districtSelect = screen.getByTestId('district-select');
|
|
|
+
|
|
|
+ expect(provinceSelect).toBeDisabled();
|
|
|
+ expect(citySelect).toBeDisabled();
|
|
|
+ expect(districtSelect).toBeDisabled();
|
|
|
+ });
|
|
|
+});
|