import { mount } from 'enzyme';
import React, { Component, useState } from 'react';
import { act } from 'react-dom/test-utils';
import scrollIntoView from 'scroll-into-view-if-needed';
import classNames from 'classnames';
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, sleep } from '../../../tests/utils';
import ConfigProvider from '../../config-provider';
import Drawer from '../../drawer';
import zhCN from '../../locale/zh_CN';
import Modal from '../../modal';
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.mockImplementation(() => {});
const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
async function change(container, index, value, executeMockTimer) {
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);
}
}
beforeEach(() => {
jest.useRealTimers();
scrollIntoView.mockReset();
});
afterEach(() => {
errorSpy.mockReset();
});
afterAll(() => {
errorSpy.mockRestore();
scrollIntoView.mockRestore();
});
describe('noStyle Form.Item', () => {
it('work', async () => {
jest.useFakeTimers();
const onChange = jest.fn();
const { container } = render(
,
);
await change(container, 0, '', true);
expect(container.querySelectorAll('.ant-form-item-with-help').length).toBeTruthy();
expect(container.querySelectorAll('.ant-form-item-has-error').length).toBeTruthy();
expect(onChange).toHaveBeenCalled();
jest.useRealTimers();
});
it('should clean up', async () => {
jest.useFakeTimers();
const Demo = () => {
const [form] = Form.useForm();
return (
{
await sleep(0);
try {
await form.validateFields();
} catch (e) {
// do nothing
}
}}
/>
{() => {
const aaa = form.getFieldValue('aaa');
if (aaa === '1') {
return (
);
}
return (
);
}}
);
};
const { container } = render();
await change(container, 0, '1', true);
expect(container.querySelector('.ant-form-item-explain').textContent).toEqual('aaa');
await change(container, 0, '2', true);
expect(container.querySelector('.ant-form-item-explain').textContent).toEqual('ccc');
await change(container, 0, '1', true);
expect(container.querySelector('.ant-form-item-explain').textContent).toEqual('aaa');
jest.useRealTimers();
});
});
it('`shouldUpdate` should work with render props', () => {
mount(
{() => null}
,
);
expect(errorSpy).toHaveBeenCalledWith(
'Warning: [antd: Form.Item] `children` of render props only work with `shouldUpdate` or `dependencies`.',
);
});
it("`shouldUpdate` shouldn't work with `dependencies`", () => {
mount(
{() => null}
,
);
expect(errorSpy).toHaveBeenCalledWith(
"Warning: [antd: Form.Item] `shouldUpdate` and `dependencies` shouldn't be used together. See https://ant.design/components/form/#dependencies.",
);
});
it('`name` should not work with render props', () => {
mount(
{() => null}
,
);
expect(errorSpy).toHaveBeenCalledWith(
"Warning: [antd: Form.Item] Do not use `name` with `children` of render props since it's not a field.",
);
});
it('children is array has name props', () => {
mount(
one
two
,
);
expect(errorSpy).toHaveBeenCalledWith(
'Warning: [antd: Form.Item] `children` is array of render props cannot have `name`.',
);
});
describe('scrollToField', () => {
function test(name, genForm) {
it(name, () => {
let callGetForm;
const Demo = () => {
const { props, getForm } = genForm();
callGetForm = getForm;
return (
);
};
const wrapper = mount(, { attachTo: document.body });
expect(scrollIntoView).not.toHaveBeenCalled();
const form = callGetForm();
form.scrollToField('test', {
block: 'start',
});
const inputNode = document.getElementById('scroll_test');
expect(scrollIntoView).toHaveBeenCalledWith(inputNode, {
block: 'start',
scrollMode: 'if-needed',
});
wrapper.unmount();
});
}
// hooks
test('useForm', () => {
const [form] = Form.useForm();
return {
props: { form },
getForm: () => form,
};
});
// ref
test('ref', () => {
let form;
return {
props: {
ref: instance => {
form = instance;
},
},
getForm: () => form,
};
});
});
it('scrollToFirstError', async () => {
const onFinishFailed = jest.fn();
const wrapper = mount(
,
{ attachTo: document.body },
);
expect(scrollIntoView).not.toHaveBeenCalled();
wrapper.find('form').simulate('submit');
await sleep(50);
const inputNode = document.getElementById('test');
expect(scrollIntoView).toHaveBeenCalledWith(inputNode, {
block: 'center',
scrollMode: 'if-needed',
});
expect(onFinishFailed).toHaveBeenCalled();
wrapper.unmount();
});
it('Form.Item should support data-*、aria-* and custom attribute', () => {
const wrapper = mount(
text
,
);
expect(wrapper.render()).toMatchSnapshot();
});
it('warning when use `name` but children is not validate element', () => {
mount(
text
,
);
expect(errorSpy).toHaveBeenCalledWith(
'Warning: [antd: Form.Item] `name` is only used for validate React element. If you are using Form.Item as layout display, please remove `name` instead.',
);
});
it('dynamic change required', () => {
const wrapper = mount(
({ required: getFieldValue('light') })]}
>
,
);
expect(wrapper.find('.ant-form-item-required')).toHaveLength(0);
wrapper.find('input[type="checkbox"]').simulate('change', { target: { checked: true } });
wrapper.update();
expect(wrapper.find('.ant-form-item-required')).toHaveLength(1);
});
describe('should show related className when customize help', () => {
it('normal', () => {
const wrapper = mount(
,
);
expect(wrapper.exists('.ant-form-item-with-help')).toBeTruthy();
});
it('empty string', () => {
const wrapper = mount(
,
);
expect(wrapper.exists('.ant-form-item-with-help')).toBeTruthy();
});
});
it('warning when use v3 function', () => {
Form.create();
expect(errorSpy).toHaveBeenCalledWith(
'Warning: [antd: Form] antd v4 removed `Form.create`. Please remove or use `@ant-design/compatible` instead.',
);
});
// https://github.com/ant-design/ant-design/issues/20706
it('Error change should work', async () => {
jest.useFakeTimers();
const { container } = render(
{
if (value === 'p') {
return Promise.reject(new Error('not a p'));
}
return Promise.resolve();
},
},
]}
>
,
);
/* eslint-disable no-await-in-loop */
for (let i = 0; i < 3; i += 1) {
await change(container, 0, 'bamboo', true);
await change(container, 0, '', true);
expect(container.querySelector('.ant-form-item-explain').textContent).toEqual(
"'name' is required",
);
await change(container, 0, 'p', true);
await sleep(100);
expect(container.querySelector('.ant-form-item-explain').textContent).toEqual('not a p');
}
/* eslint-enable */
jest.useRealTimers();
});
// https://github.com/ant-design/ant-design/issues/20813
it('should update help directly when provided', () => {
function App() {
const [message, updateMessage] = React.useState('');
return (