<commit_before>import type { ChangeEventHandler } from 'react';
import React, { useState } from 'react';
import scrollIntoView from 'scroll-into-view-if-needed';
import classNames from 'classnames';
import type { ColProps } from 'antd/es/grid';
import type { FormInstance } from '..';
import Form from '..';
import * as Util from '../util';
import Button from '../../button';
import Input from '../../input';
import Select from '../../select';
import Upload from '../../upload';
import Cascader from '../../cascader';
import Checkbox from '../../checkbox';
import DatePicker from '../../date-picker';
import InputNumber from '../../input-number';
import Radio from '../../radio';
import Switch from '../../switch';
import TreeSelect from '../../tree-select';
import mountTest from '../../../tests/shared/mountTest';
import rtlTest from '../../../tests/shared/rtlTest';
import { fireEvent, render, screen, pureRender, waitFakeTimer } from '../../../tests/utils';
import ConfigProvider from '../../config-provider';
import Drawer from '../../drawer';
import zhCN from '../../locale/zh_CN';
import Modal from '../../modal';
import type { NamePath } from '../interface';

const { RangePicker } = DatePicker;
const { TextArea } = Input;

jest.mock('scroll-into-view-if-needed');

describe('Form', () => {
  mountTest(Form);
  mountTest(Form.Item);

  rtlTest(Form);
  rtlTest(Form.Item);

  (scrollIntoView as any).mockImplementation(() => {});
  const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
  const warnSpy = jest.spyOn(console, 'warn').mockImplementation(() => {});

  // const change = async (
  //   container: ReturnType<typeof render>['container'],
  //   index: number,
  //   value: string,
  //   executeMockTimer: boolean,
  // ) => {
  //   fireEvent.change(container.querySelectorAll('input')?.[index], { target: { value } });
  //   await sleep(200);

  //   if (executeMockTimer) {
  //     for (let i = 0; i < 10; i += 1) {
  //       act(() => {
  //         jest.runAllTimers();
  //       });
  //     }
  //     await sleep(1);
  //   }
  // };

  const changeValue = async (
    input: HTMLElement | null | number,
    value: string,
    advTimer = 1000,
  ) => {
    let element: HTMLElement;

    if (typeof input === 'number') {
      element = document.querySelectorAll('input')[input];
    }

    expect(element!).toBeTruthy();

    fireEvent.change(element!, {
      target: {
        value,
      },
    });

    if (advTimer) {
      await waitFakeTimer(advTimer / 20, 20);
    }
  };

  beforeEach(() => {
    document.body.innerHTML = '';
    jest.useFakeTimers();
    (scrollIntoView as any).mockReset();
  });

  afterEach(() => {
    errorSpy.mockReset();
  });

  afterAll(() => {
    jest.clearAllTimers();
    jest.useRealTimers();
    errorSpy.mockRestore();
    warnSpy.mockRestore();
    (scrollIntoView as any).mockRestore();
  });

  describe('noStyle Form.Item', () => {
    it('should show alert when form field is required but empty', async () => {
      const onChange = jest.fn();

      const { container } = render(
        <Form>
          <Form.Item>
            <Form.Item name="test" label="test" initialValue="bamboo" rules={[{ required: true }]}>
              <Input onChange={onChange} />
            </Form.Item>
          </Form.Item>
        </Form>,
      );

      // user type something and clear
      await changeValue(0, 'test');
      await changeValue(0, '');

      // should show alert with correct message and show correct styles
      expect(container.querySelector('.ant-form-item-explain-error')).toHaveTextContent(
        "'test' is required",
      );
      expect(container.querySelector('.ant-input-status-error')).toBeTruthy();
      expect(container.querySelector('.ant-form-item-has-error')).toBeTruthy();

      expect(onChange).toHaveBeenCalled();
    });

    it('should clean up', async () => {
      const Demo: React.FC = () => {
        const [form] = Form.useForm();

        const onChange = async () => {
          // Wait a while and then some logic to validate
          await waitFakeTimer();

          try {
            await form.validateFields();
          } catch (err) {
            // do nothing
          }
        };

        return (
          <Form form={form} initialValues={{ aaa: '2' }}>
            <Form.Item name="aaa">
              <Input onChange={onChange} />
            </Form.Item>
            <Form.Item shouldUpdate noStyle>
              {() => {
                const aaa = form.getFieldValue('aaa');

                if (aaa === '1') {
                  return (
                    <Form.Item name="bbb" rules={[{ required: true, message: 'aaa' }]}>
                      <Input />
                    </Form.Item>
                  );
                }

                return (
                  <Form.Item>
                    <Form.Item name="ccc" rules={[{ required: true, message: 'ccc' }]} noStyle>
                      <Input />
                    </Form.Item>
                  </Form.Item>
                );
              }}
            </Form.Item>
          </Form>
        );
      };

      const { container } = render(<Demo />);

      await changeValue(0, '1');
      await waitFakeTimer();
      expect(container.querySelector('.ant-form-item-explain-error')).toHaveTextContent('aaa');

      await changeValue(0, '2');
      await waitFakeTimer();
      expect(container.querySelector('.ant-form-item-explain-error')).toHaveTextContent('ccc');

      await changeValue(0, '1');
      await waitFakeTimer();
      expect(container.querySelector('.ant-form-item-explain-error')).toHaveTextContent('aaa');
    });
  });

  it('`shouldUpdate` should work with render props', () => {
    render(
      <Form>
        <Form.Item>{() => null}</Form.Item>
      </Form>,
    );
    expect(errorSpy).toHaveBeenCalledWith(
      'Warning: [antd: Form.Item] `children` of render props only work with `shouldUpdate` or `dependencies`.',
    );
  });<commit_msg>test: fix form test case (#38276)<commit_after>import type { ChangeEventHandler } from 'react';
import React, { useState } from 'react';
import scrollIntoView from 'scroll-into-view-if-needed';
import classNames from 'classnames';
import type { ColProps } from 'antd/es/grid';
import type { FormInstance } from '..';
import Form from '..';
import * as Util from '../util';
import Button from '../../button';
import Input from '../../input';
import Select from '../../select';
import Upload from '../../upload';
import Cascader from '../../cascader';
import Checkbox from '../../checkbox';
import DatePicker from '../../date-picker';
import InputNumber from '../../input-number';
import Radio from '../../radio';
import Switch from '../../switch';
import TreeSelect from '../../tree-select';
import mountTest from '../../../tests/shared/mountTest';
import rtlTest from '../../../tests/shared/rtlTest';
import { fireEvent, render, screen, pureRender, waitFakeTimer } from '../../../tests/utils';
import ConfigProvider from '../../config-provider';
import Drawer from '../../drawer';
import zhCN from '../../locale/zh_CN';
import Modal from '../../modal';
import type { NamePath } from '../interface';

const { RangePicker } = DatePicker;
const { TextArea } = Input;

jest.mock('scroll-into-view-if-needed');

describe('Form', () => {
  mountTest(Form);
  mountTest(Form.Item);

  rtlTest(Form);
  rtlTest(Form.Item);

  (scrollIntoView as any).mockImplementation(() => {});
  const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
  const warnSpy = jest.spyOn(console, 'warn').mockImplementation(() => {});

  // const change = async (
  //   container: ReturnType<typeof render>['container'],
  //   index: number,
  //   value: string,
  //   executeMockTimer: boolean,
  // ) => {
  //   fireEvent.change(container.querySelectorAll('input')?.[index], { target: { value } });
  //   await sleep(200);

  //   if (executeMockTimer) {
  //     for (let i = 0; i < 10; i += 1) {
  //       act(() => {
  //         jest.runAllTimers();
  //       });
  //     }
  //     await sleep(1);
  //   }
  // };

  const changeValue = async (
    input: HTMLElement | null | number,
    value: string,
    advTimer = 1000,
  ) => {
    let element: HTMLElement;

    if (typeof input === 'number') {
      element = document.querySelectorAll('input')[input];
    }

    expect(element!).toBeTruthy();

    fireEvent.change(element!, {
      target: {
        value,
      },
    });

    if (advTimer) {
      await waitFakeTimer(advTimer / 20, 20);
    }
  };

  beforeEach(() => {
    document.body.innerHTML = '';
    jest.useFakeTimers();
    (scrollIntoView as any).mockReset();
  });

  afterEach(() => {
    errorSpy.mockReset();
  });

  afterAll(() => {
    jest.clearAllTimers();
    jest.useRealTimers();
    errorSpy.mockRestore();
    warnSpy.mockRestore();
    (scrollIntoView as any).mockRestore();
  });

  describe('noStyle Form.Item', () => {
    it('should show alert when form field is required but empty', async () => {
      const onChange = jest.fn();

      const { container } = render(
        <Form>
          <Form.Item>
            <Form.Item name="test" label="test" initialValue="bamboo" rules={[{ required: true }]}>
              <Input onChange={onChange} />
            </Form.Item>
          </Form.Item>
        </Form>,
      );

      // user type something and clear
      await changeValue(0, 'test');
      await changeValue(0, '');

      // should show alert with correct message and show correct styles
      expect(container.querySelector('.ant-form-item-explain-error')).toHaveTextContent(
        "'test' is required",
      );
      expect(container.querySelector('.ant-input-status-error')).toBeTruthy();
      expect(container.querySelector('.ant-form-item-has-error')).toBeTruthy();

      expect(onChange).toHaveBeenCalled();
    });

    it('should clean up', async () => {
      const Demo: React.FC = () => {
        const [form] = Form.useForm();

        const onChange = async () => {
          // Wait a while and then some logic to validate
          await waitFakeTimer();

          try {
            await form.validateFields();
          } catch (err) {
            // do nothing
          }
        };

        return (
          <Form form={form} initialValues={{ aaa: '2' }}>
            <Form.Item name="aaa">
              <Input onChange={onChange} />
            </Form.Item>
            <Form.Item shouldUpdate noStyle>
              {() => {
                const aaa = form.getFieldValue('aaa');

                if (aaa === '1') {
                  return (
                    <Form.Item name="bbb" rules={[{ required: true, message: 'aaa' }]}>
                      <Input />
                    </Form.Item>
                  );
                }

                return (
                  <Form.Item>
                    <Form.Item name="ccc" rules={[{ required: true, message: 'ccc' }]} noStyle>
                      <Input />
                    </Form.Item>
                  </Form.Item>
                );
              }}
            </Form.Item>
          </Form>
        );
      };

      const { container } = render(<Demo />);

      await changeValue(0, '1');
      await waitFakeTimer(2000);
      expect(container.querySelector('.ant-form-item-explain-error')).toHaveTextContent('aaa');

      await changeValue(0, '2');
      await waitFakeTimer(2000);
      expect(container.querySelector('.ant-form-item-explain-error')).toHaveTextContent('ccc');

      await changeValue(0, '1');
      await waitFakeTimer(2000);
      expect(container.querySelector('.ant-form-item-explain-error')).toHaveTextContent('aaa');
    });
  });

  it('`shouldUpdate` should work with render props', () => {
    render(
      <Form>
        <Form.Item>{() => null}</Form.Item>
      </Form>,
    );
    expect(errorSpy).toHaveBeenCalledWith(
      'Warning: [antd: Form.Item] `children` of render props only work with `shouldUpdate` or `dependencies`.',
    );
  });
{'fraction_non_alphanumeric': 0.1754182540997184, 'fraction_numerical': 0.00571475898625145, 'mean_word_length': 2.147810218978102, 'pattern_counts': {'":': 0, '<': 61, '<?xml version=': 0, '>': 95, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6874864', 'n_tokens_mistral': 3573, 'n_tokens_neox': 3506, 'n_words': 801}