2022-09-28 12:01:20 +08:00
import type { ChangeEventHandler } from 'react' ;
import React , { useState } from 'react' ;
2019-12-23 18:33:08 +08:00
import scrollIntoView from 'scroll-into-view-if-needed' ;
2022-09-28 12:01:20 +08:00
import userEvent from '@testing-library/user-event' ;
2022-07-14 11:01:39 +08:00
import classNames from 'classnames' ;
2022-09-28 12:01:20 +08:00
import type { ColProps } from 'antd/es/grid' ;
import type { FormInstance } from '..' ;
2019-07-03 20:14:39 +08:00
import Form from '..' ;
2021-12-12 14:58:49 +08:00
import * as Util from '../util' ;
2019-07-03 20:14:39 +08:00
import Button from '../../button' ;
2022-06-06 23:39:00 +08:00
import Input from '../../input' ;
2021-12-12 14:58:49 +08:00
import Select from '../../select' ;
2022-07-18 14:21:52 +08:00
import Upload from '../../upload' ;
2022-04-29 20:48:10 +08:00
import Cascader from '../../cascader' ;
2022-06-06 23:39:00 +08:00
import Checkbox from '../../checkbox' ;
2022-04-29 20:48:10 +08:00
import DatePicker from '../../date-picker' ;
import InputNumber from '../../input-number' ;
2022-06-06 23:39:00 +08:00
import Radio from '../../radio' ;
2022-04-29 20:48:10 +08:00
import Switch from '../../switch' ;
2022-06-06 23:39:00 +08:00
import TreeSelect from '../../tree-select' ;
2019-08-26 22:53:20 +08:00
import mountTest from '../../../tests/shared/mountTest' ;
2020-01-02 19:10:16 +08:00
import rtlTest from '../../../tests/shared/rtlTest' ;
2022-09-28 12:01:20 +08:00
import {
fireEvent ,
render ,
sleep ,
act ,
screen ,
pureRender ,
waitFakeTimer ,
} from '../../../tests/utils' ;
2021-12-31 16:56:21 +08:00
import ConfigProvider from '../../config-provider' ;
2022-06-06 23:39:00 +08:00
import Drawer from '../../drawer' ;
2022-01-13 13:50:18 +08:00
import zhCN from '../../locale/zh_CN' ;
2022-06-06 23:39:00 +08:00
import Modal from '../../modal' ;
2022-09-28 12:01:20 +08:00
import type { NamePath } from '../interface' ;
2019-07-03 20:14:39 +08:00
2022-04-29 20:48:10 +08:00
const { RangePicker } = DatePicker ;
const { TextArea } = Input ;
2019-12-23 18:33:08 +08:00
jest . mock ( 'scroll-into-view-if-needed' ) ;
2019-07-04 15:00:11 +08:00
2019-07-03 20:14:39 +08:00
describe ( 'Form' , ( ) = > {
2019-08-26 22:53:20 +08:00
mountTest ( Form ) ;
mountTest ( Form . Item ) ;
2020-01-02 19:10:16 +08:00
rtlTest ( Form ) ;
rtlTest ( Form . Item ) ;
2022-09-28 12:01:20 +08:00
( scrollIntoView as any ) . mockImplementation ( ( ) = > { } ) ;
2019-07-03 20:14:39 +08:00
const errorSpy = jest . spyOn ( console , 'error' ) . mockImplementation ( ( ) = > { } ) ;
2022-09-28 12:01:20 +08:00
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 } } ) ;
2020-07-28 17:54:46 +08:00
await sleep ( 200 ) ;
2021-06-04 14:44:41 +08:00
if ( executeMockTimer ) {
2022-04-06 11:07:15 +08:00
for ( let i = 0 ; i < 10 ; i += 1 ) {
act ( ( ) = > {
jest . runAllTimers ( ) ;
} ) ;
}
2021-06-04 14:44:41 +08:00
await sleep ( 1 ) ;
}
2022-09-28 12:01:20 +08:00
} ;
2019-07-03 20:14:39 +08:00
2019-07-04 15:00:11 +08:00
beforeEach ( ( ) = > {
2019-12-27 16:50:44 +08:00
jest . useRealTimers ( ) ;
2022-09-28 12:01:20 +08:00
( scrollIntoView as any ) . mockReset ( ) ;
2019-07-04 15:00:11 +08:00
} ) ;
2019-07-03 20:14:39 +08:00
afterEach ( ( ) = > {
errorSpy . mockReset ( ) ;
} ) ;
afterAll ( ( ) = > {
errorSpy . mockRestore ( ) ;
2022-09-28 12:01:20 +08:00
warnSpy . mockRestore ( ) ;
( scrollIntoView as any ) . mockRestore ( ) ;
2019-07-03 20:14:39 +08:00
} ) ;
2020-07-21 20:51:36 +08:00
describe ( 'noStyle Form.Item' , ( ) = > {
2022-09-28 12:01:20 +08:00
it ( 'should show alert when form field is required but empty' , async ( ) = > {
2020-07-21 20:51:36 +08:00
const onChange = jest . fn ( ) ;
2019-07-03 20:14:39 +08:00
2022-04-06 11:07:15 +08:00
const { container } = render (
2020-07-21 20:51:36 +08:00
< Form >
< Form.Item >
2022-09-28 12:01:20 +08:00
< Form.Item name = "test" label = "test" initialValue = "bamboo" rules = { [ { required : true } ] } >
2020-07-21 20:51:36 +08:00
< Input onChange = { onChange } / >
< / Form.Item >
2019-07-03 20:14:39 +08:00
< / Form.Item >
2020-07-21 20:51:36 +08:00
< / Form > ,
) ;
2022-09-28 12:01:20 +08:00
// user type something and clear
await userEvent . type ( screen . getByLabelText ( 'test' ) , 'test' ) ;
await userEvent . clear ( screen . getByLabelText ( 'test' ) ) ;
// should show alert with correct message and show correct styles
await expect ( screen . findByRole ( 'alert' ) ) . resolves . toHaveTextContent ( "'test' is required" ) ;
expect ( screen . getByLabelText ( 'test' ) ) . toHaveClass ( 'ant-input-status-error' ) ;
2022-04-06 11:07:15 +08:00
expect ( container . querySelectorAll ( '.ant-form-item-has-error' ) . length ) . toBeTruthy ( ) ;
2020-07-21 20:51:36 +08:00
expect ( onChange ) . toHaveBeenCalled ( ) ;
} ) ;
it ( 'should clean up' , async ( ) = > {
2021-06-04 14:44:41 +08:00
jest . useFakeTimers ( ) ;
2022-09-28 12:01:20 +08:00
const Demo : React.FC = ( ) = > {
2020-07-21 20:51:36 +08:00
const [ form ] = Form . useForm ( ) ;
return (
< Form form = { form } initialValues = { { aaa : '2' } } >
< Form.Item name = "aaa" >
< Input
onChange = { async ( ) = > {
await sleep ( 0 ) ;
try {
await form . validateFields ( ) ;
2022-09-28 12:01:20 +08:00
} catch {
2020-07-21 20:51:36 +08:00
// do nothing
}
} }
/ >
< / 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 >
) ;
}
2019-07-03 20:14:39 +08:00
2020-07-21 20:51:36 +08:00
return (
< Form.Item >
< Form.Item name = "ccc" rules = { [ { required : true , message : 'ccc' } ] } noStyle >
< Input / >
< / Form.Item >
< / Form.Item >
) ;
} }
< / Form.Item >
< / Form >
) ;
} ;
2019-07-03 20:14:39 +08:00
2022-04-19 20:06:32 +08:00
const { container } = render ( < Demo / > ) ;
2022-04-06 11:07:15 +08:00
await change ( container , 0 , '1' , true ) ;
2022-09-28 12:01:20 +08:00
expect ( screen . getByRole ( 'alert' ) ) . toHaveTextContent ( 'aaa' ) ;
2022-04-06 11:07:15 +08:00
await change ( container , 0 , '2' , true ) ;
2022-09-28 12:01:20 +08:00
expect ( screen . getByRole ( 'alert' ) ) . toHaveTextContent ( 'ccc' ) ;
2022-04-06 11:07:15 +08:00
await change ( container , 0 , '1' , true ) ;
2022-09-28 12:01:20 +08:00
expect ( screen . getByRole ( 'alert' ) ) . toHaveTextContent ( 'aaa' ) ;
2021-06-04 14:44:41 +08:00
jest . useRealTimers ( ) ;
2020-07-21 20:51:36 +08:00
} ) ;
2019-07-03 20:14:39 +08:00
} ) ;
it ( '`shouldUpdate` should work with render props' , ( ) = > {
2022-09-28 12:01:20 +08:00
render (
2019-07-03 20:14:39 +08:00
< Form >
< Form.Item > { ( ) = > null } < / Form.Item >
< / Form > ,
) ;
expect ( errorSpy ) . toHaveBeenCalledWith (
2020-07-03 16:25:29 +08:00
'Warning: [antd: Form.Item] `children` of render props only work with `shouldUpdate` or `dependencies`.' ,
) ;
} ) ;
it ( "`shouldUpdate` shouldn't work with `dependencies`" , ( ) = > {
2022-09-28 12:01:20 +08:00
render (
2020-07-03 16:25:29 +08:00
< Form >
< Form.Item shouldUpdate dependencies = { [ ] } >
{ ( ) = > null }
< / Form.Item >
< / Form > ,
) ;
expect ( errorSpy ) . toHaveBeenCalledWith (
"Warning: [antd: Form.Item] `shouldUpdate` and `dependencies` shouldn't be used together. See https://ant.design/components/form/#dependencies." ,
2019-07-03 20:14:39 +08:00
) ;
} ) ;
2020-09-18 16:53:18 +08:00
2020-01-07 19:17:51 +08:00
it ( '`name` should not work with render props' , ( ) = > {
2022-09-28 12:01:20 +08:00
render (
2020-01-07 19:17:51 +08:00
< Form >
< Form.Item name = "test" shouldUpdate >
{ ( ) = > null }
< / Form.Item >
< / Form > ,
) ;
expect ( errorSpy ) . toHaveBeenCalledWith (
"Warning: [antd: Form.Item] Do not use `name` with `children` of render props since it's not a field." ,
) ;
} ) ;
2020-09-18 16:53:18 +08:00
2020-01-01 20:07:10 +08:00
it ( 'children is array has name props' , ( ) = > {
2022-09-28 12:01:20 +08:00
render (
2020-01-01 20:07:10 +08:00
< Form >
< Form.Item name = "test" >
< div > one < / div >
< div > two < / div >
< / Form.Item >
< / Form > ,
) ;
expect ( errorSpy ) . toHaveBeenCalledWith (
'Warning: [antd: Form.Item] `children` is array of render props cannot have `name`.' ,
) ;
} ) ;
2019-07-04 15:00:11 +08:00
2022-08-24 17:07:50 +08:00
it ( 'input element should have the prop aria-describedby pointing to the help id when there is a help message' , ( ) = > {
2022-09-28 12:01:20 +08:00
const { container } = pureRender (
2022-08-24 17:07:50 +08:00
< Form >
< Form.Item name = "test" help = "This is a help" >
< input / >
< / Form.Item >
< / Form > ,
) ;
2022-09-28 12:01:20 +08:00
expect ( container . querySelector ( 'input' ) ? . getAttribute ( 'aria-describedby' ) ) . toBe ( 'test_help' ) ;
expect ( container . querySelector ( '.ant-form-item-explain' ) ? . id ) . toBe ( 'test_help' ) ;
2022-08-24 17:07:50 +08:00
} ) ;
it ( 'input element should not have the prop aria-describedby pointing to the help id when there is a help message and name is not defined' , ( ) = > {
2022-09-28 12:01:20 +08:00
const { container } = render (
2022-08-24 17:07:50 +08:00
< Form >
< Form.Item help = "This is a help" >
< input / >
< / Form.Item >
< / Form > ,
) ;
2022-09-28 12:01:20 +08:00
expect ( container . querySelector ( 'input' ) ? . getAttribute ( 'aria-describedby' ) ) . toBeFalsy ( ) ;
expect ( container . querySelector ( '.ant-form-item-explain' ) ? . id ) . toBeFalsy ( ) ;
2022-08-24 17:07:50 +08:00
} ) ;
it ( 'input element should have the prop aria-describedby concatenated with the form name pointing to the help id when there is a help message' , ( ) = > {
2022-09-28 12:01:20 +08:00
const { container } = render (
2022-08-24 17:07:50 +08:00
< Form name = "form" >
< Form.Item name = "test" help = "This is a help" >
< input / >
< / Form.Item >
< / Form > ,
) ;
2022-09-28 12:01:20 +08:00
expect ( container . querySelector ( 'input' ) ? . getAttribute ( 'aria-describedby' ) ) . toBe (
'form_test_help' ,
) ;
expect ( container . querySelector ( '.ant-form-item-explain' ) ? . id ) . toBe ( 'form_test_help' ) ;
2022-08-24 17:07:50 +08:00
} ) ;
it ( 'input element should have the prop aria-describedby pointing to the help id when there are errors' , async ( ) = > {
2022-09-28 12:01:20 +08:00
jest . useFakeTimers ( ) ;
const { container } = pureRender (
2022-08-24 17:07:50 +08:00
< Form >
< Form.Item name = "test" rules = { [ { len : 3 } , { type : 'number' } ] } >
< input / >
< / Form.Item >
< / Form > ,
) ;
2022-09-28 12:01:20 +08:00
fireEvent . change ( container . querySelector ( 'input' ) ! , { target : { value : 'Invalid number' } } ) ;
2022-08-24 17:07:50 +08:00
2022-09-28 12:01:20 +08:00
await waitFakeTimer ( ) ;
expect ( container . querySelector ( 'input' ) ? . getAttribute ( 'aria-describedby' ) ) . toBe ( 'test_help' ) ;
expect ( container . querySelector ( '.ant-form-item-explain' ) ? . id ) . toBe ( 'test_help' ) ;
2022-08-24 17:07:50 +08:00
2022-09-28 12:01:20 +08:00
jest . clearAllTimers ( ) ;
jest . useRealTimers ( ) ;
2022-08-24 17:07:50 +08:00
} ) ;
it ( 'input element should have the prop aria-invalid when there are errors' , async ( ) = > {
2022-09-28 12:01:20 +08:00
const { container } = render (
2022-08-24 17:07:50 +08:00
< Form >
< Form.Item name = "test" rules = { [ { len : 3 } , { type : 'number' } ] } >
< input / >
< / Form.Item >
< / Form > ,
) ;
2022-09-28 12:01:20 +08:00
fireEvent . change ( container . querySelector ( 'input' ) ! , { target : { value : 'Invalid number' } } ) ;
2022-08-24 17:07:50 +08:00
await sleep ( 800 ) ;
2022-09-28 12:01:20 +08:00
expect ( container . querySelector ( 'input' ) ? . getAttribute ( 'aria-invalid' ) ) . toBe ( 'true' ) ;
2022-08-24 17:07:50 +08:00
} ) ;
2022-09-28 12:01:20 +08:00
it ( 'input element should have the prop aria-required when the prop `required` is true' , ( ) = > {
const { container } = render (
2022-08-24 17:07:50 +08:00
< Form >
< Form.Item name = "test" required >
< input / >
< / Form.Item >
< / Form > ,
) ;
2022-09-28 12:01:20 +08:00
expect ( container . querySelector ( 'input' ) ? . getAttribute ( 'aria-required' ) ) . toBe ( 'true' ) ;
2022-08-24 17:07:50 +08:00
} ) ;
2022-09-28 12:01:20 +08:00
it ( 'input element should have the prop aria-required when there is a rule with required' , ( ) = > {
const { container } = render (
2022-08-24 17:07:50 +08:00
< Form >
< Form.Item name = "test" rules = { [ { required : true } ] } >
< input / >
< / Form.Item >
< / Form > ,
) ;
2022-09-28 12:01:20 +08:00
expect ( container . querySelector ( 'input' ) ? . getAttribute ( 'aria-required' ) ) . toBe ( 'true' ) ;
2022-08-24 17:07:50 +08:00
} ) ;
it ( 'input element should have the prop aria-describedby pointing to the extra id when there is a extra message' , ( ) = > {
2022-09-28 12:01:20 +08:00
const { container } = render (
2022-08-24 17:07:50 +08:00
< Form >
< Form.Item name = "test" extra = "This is a extra message" >
< input / >
< / Form.Item >
< / Form > ,
) ;
2022-09-28 12:01:20 +08:00
expect ( container . querySelector ( 'input' ) ? . getAttribute ( 'aria-describedby' ) ) . toBe ( 'test_extra' ) ;
expect ( container . querySelector ( '.ant-form-item-extra' ) ? . id ) . toBe ( 'test_extra' ) ;
2022-08-24 17:07:50 +08:00
} ) ;
it ( 'input element should not have the prop aria-describedby pointing to the extra id when there is a extra message and name is not defined' , ( ) = > {
2022-09-28 12:01:20 +08:00
const { container } = render (
2022-08-24 17:07:50 +08:00
< Form >
< Form.Item extra = "This is a extra message" >
< input / >
< / Form.Item >
< / Form > ,
) ;
2022-09-28 12:01:20 +08:00
expect ( container . querySelector ( 'input' ) ? . getAttribute ( 'aria-describedby' ) ) . toBeFalsy ( ) ;
expect ( container . querySelector ( '.ant-form-item-extra' ) ? . id ) . toBeFalsy ( ) ;
2022-08-24 17:07:50 +08:00
} ) ;
it ( 'input element should have the prop aria-describedby pointing to the help and extra id when there is a help and extra message' , ( ) = > {
2022-09-28 12:01:20 +08:00
const { container } = render (
2022-08-24 17:07:50 +08:00
< Form >
< Form.Item name = "test" help = "This is a help" extra = "This is a extra message" >
< input / >
< / Form.Item >
< / Form > ,
) ;
2022-09-28 12:01:20 +08:00
expect ( container . querySelector ( 'input' ) ? . getAttribute ( 'aria-describedby' ) ) . toBe (
'test_help test_extra' ,
) ;
2022-08-24 17:07:50 +08:00
} ) ;
2019-07-04 15:00:11 +08:00
describe ( 'scrollToField' , ( ) = > {
2022-09-28 12:01:20 +08:00
const test = ( name : string , genForm : ( ) = > any ) = > {
2019-07-04 15:00:11 +08:00
it ( name , ( ) = > {
2022-09-28 12:01:20 +08:00
let callGetForm : any ;
2019-07-04 15:00:11 +08:00
2022-09-28 12:01:20 +08:00
const Demo : React.FC = ( ) = > {
2019-07-04 15:00:11 +08:00
const { props , getForm } = genForm ( ) ;
callGetForm = getForm ;
return (
< Form name = "scroll" { ...props } >
< Form.Item name = "test" >
< Input / >
< / Form.Item >
< / Form >
) ;
} ;
2022-09-28 12:01:20 +08:00
render ( < Demo / > ) ;
2019-07-04 15:00:11 +08:00
expect ( scrollIntoView ) . not . toHaveBeenCalled ( ) ;
2020-01-14 22:50:49 +08:00
const form = callGetForm ( ) ;
form . scrollToField ( 'test' , {
block : 'start' ,
} ) ;
const inputNode = document . getElementById ( 'scroll_test' ) ;
expect ( scrollIntoView ) . toHaveBeenCalledWith ( inputNode , {
block : 'start' ,
scrollMode : 'if-needed' ,
} ) ;
2019-07-04 15:00:11 +08:00
} ) ;
2022-09-28 12:01:20 +08:00
} ;
2019-07-04 15:00:11 +08:00
// hooks
test ( 'useForm' , ( ) = > {
const [ form ] = Form . useForm ( ) ;
return {
props : { form } ,
getForm : ( ) = > form ,
} ;
} ) ;
// ref
test ( 'ref' , ( ) = > {
2022-09-28 12:01:20 +08:00
let form : any ;
2019-07-04 15:00:11 +08:00
return {
props : {
2022-09-28 12:01:20 +08:00
ref : ( instance : any ) = > {
2019-07-04 15:00:11 +08:00
form = instance ;
} ,
} ,
getForm : ( ) = > form ,
} ;
} ) ;
} ) ;
2019-11-13 11:48:20 +08:00
2020-02-19 17:38:46 +08:00
it ( 'scrollToFirstError' , async ( ) = > {
const onFinishFailed = jest . fn ( ) ;
2022-09-28 12:01:20 +08:00
render (
2020-12-11 14:35:46 +08:00
< Form scrollToFirstError = { { block : 'center' } } onFinishFailed = { onFinishFailed } >
2020-02-19 17:38:46 +08:00
< Form.Item name = "test" rules = { [ { required : true } ] } >
< input / >
< / Form.Item >
2022-09-28 12:01:20 +08:00
< Form.Item >
< Button htmlType = "submit" > Submit < / Button >
< / Form.Item >
2020-02-19 17:38:46 +08:00
< / Form > ,
) ;
expect ( scrollIntoView ) . not . toHaveBeenCalled ( ) ;
2022-09-28 12:01:20 +08:00
await userEvent . click ( screen . getByRole ( 'button' , { name : /submit/i } ) ) ;
2020-12-11 14:35:46 +08:00
const inputNode = document . getElementById ( 'test' ) ;
expect ( scrollIntoView ) . toHaveBeenCalledWith ( inputNode , {
block : 'center' ,
scrollMode : 'if-needed' ,
} ) ;
2020-02-19 17:38:46 +08:00
expect ( onFinishFailed ) . toHaveBeenCalled ( ) ;
} ) ;
2019-11-13 11:48:20 +08:00
it ( 'Form.Item should support data-*、aria-* and custom attribute' , ( ) = > {
2022-09-28 12:01:20 +08:00
const { container } = render (
2019-11-13 11:48:20 +08:00
< Form >
2022-09-28 12:01:20 +08:00
{ /* @ts-ignore */ }
2019-11-13 11:48:20 +08:00
< Form.Item data - text = "123" aria - hidden = "true" cccc = "bbbb" >
text
< / Form.Item >
< / Form > ,
) ;
2022-09-28 12:01:20 +08:00
expect ( container . firstChild ) . toMatchSnapshot ( ) ;
2019-11-13 11:48:20 +08:00
} ) ;
2019-12-11 16:08:59 +08:00
it ( 'warning when use `name` but children is not validate element' , ( ) = > {
2022-09-28 12:01:20 +08:00
render (
2019-12-11 16:08:59 +08:00
< Form >
< Form.Item name = "warning" > text < / Form.Item >
< / Form > ,
) ;
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.' ,
) ;
} ) ;
2019-12-12 12:05:59 +08:00
2022-09-28 12:01:20 +08:00
it ( 'dynamic change required' , async ( ) = > {
render (
2019-12-12 12:05:59 +08:00
< Form >
< Form.Item label = "light" name = "light" valuePropName = "checked" >
< input type = "checkbox" / >
< / Form.Item >
< Form.Item
label = "bamboo"
name = "bamboo"
dependencies = { [ 'light' ] }
rules = { [ ( { getFieldValue } ) = > ( { required : getFieldValue ( 'light' ) } ) ] }
>
< input / >
< / Form.Item >
< / Form > ,
) ;
2022-09-28 12:01:20 +08:00
// should not show alert by default
expect ( screen . queryByRole ( 'alert' ) ) . not . toBeInTheDocument ( ) ;
2019-12-12 12:05:59 +08:00
2022-09-28 12:01:20 +08:00
// click to change the light field value to true
await userEvent . click ( screen . getByLabelText ( 'light' ) ) ;
// user input something and clear
await userEvent . type ( screen . getByLabelText ( 'bamboo' ) , '1' ) ;
await userEvent . clear ( screen . getByLabelText ( 'bamboo' ) ) ;
// should show alert says that the field is required
await expect ( screen . findByRole ( 'alert' ) ) . resolves . toHaveTextContent ( "'bamboo' is required" ) ;
} ) ;
it ( 'should show alert with string when help is non-empty string' , async ( ) = > {
render (
< Form >
< Form.Item help = "good" >
< input / >
< / Form.Item >
< / Form > ,
) ;
await expect ( screen . findByRole ( 'alert' ) ) . resolves . toHaveTextContent ( 'good' ) ;
} ) ;
it ( 'should show alert with empty string when help is empty string' , async ( ) = > {
render (
< Form >
< Form.Item help = "" >
< input / >
< / Form.Item >
< / Form > ,
) ;
await expect ( screen . findByRole ( 'alert' ) ) . resolves . toHaveTextContent ( '' ) ;
2019-12-12 12:05:59 +08:00
} ) ;
2019-12-27 16:50:44 +08:00
2022-01-10 15:58:53 +08:00
describe ( 'should show related className when customize help' , ( ) = > {
it ( 'normal' , ( ) = > {
2022-09-28 12:01:20 +08:00
const { container } = render (
2022-01-10 15:58:53 +08:00
< Form >
< Form.Item help = "good" >
< input / >
< / Form.Item >
< / Form > ,
) ;
2022-09-28 12:01:20 +08:00
expect ( container . querySelector ( '.ant-form-item-with-help' ) ) . toBeTruthy ( ) ;
2022-01-10 15:58:53 +08:00
} ) ;
it ( 'empty string' , ( ) = > {
2022-09-28 12:01:20 +08:00
const { container } = render (
2022-01-10 15:58:53 +08:00
< Form >
< Form.Item help = "" >
< input / >
< / Form.Item >
< / Form > ,
) ;
2022-09-28 12:01:20 +08:00
expect ( container . querySelector ( '.ant-form-item-with-help' ) ) . toBeTruthy ( ) ;
2022-01-10 15:58:53 +08:00
} ) ;
2019-12-27 16:50:44 +08:00
} ) ;
2019-12-30 12:13:58 +08:00
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.' ,
) ;
} ) ;
2020-01-07 16:20:18 +08:00
// https://github.com/ant-design/ant-design/issues/20706
it ( 'Error change should work' , async ( ) = > {
2021-06-04 14:44:41 +08:00
jest . useFakeTimers ( ) ;
2022-04-06 11:07:15 +08:00
const { container } = render (
2020-01-07 16:20:18 +08:00
< Form >
< Form.Item
name = "name"
2022-09-28 12:01:20 +08:00
label = "test"
2020-01-07 16:20:18 +08:00
rules = { [
{ required : true } ,
{
validator : ( _ , value ) = > {
if ( value === 'p' ) {
return Promise . reject ( new Error ( 'not a p' ) ) ;
}
return Promise . resolve ( ) ;
} ,
} ,
] }
>
< Input / >
< / Form.Item >
< / Form > ,
) ;
/* eslint-disable no-await-in-loop */
for ( let i = 0 ; i < 3 ; i += 1 ) {
2022-04-06 11:07:15 +08:00
await change ( container , 0 , 'bamboo' , true ) ;
await change ( container , 0 , '' , true ) ;
2022-09-28 12:01:20 +08:00
expect ( container . querySelector ( '.ant-form-item-explain' ) ? . textContent ) . toEqual (
2022-04-06 11:07:15 +08:00
"'name' is required" ,
) ;
await change ( container , 0 , 'p' , true ) ;
2020-06-09 18:10:43 +08:00
await sleep ( 100 ) ;
2022-09-28 12:01:20 +08:00
expect ( container . querySelector ( '.ant-form-item-explain' ) ? . textContent ) . toEqual ( 'not a p' ) ;
2020-01-07 16:20:18 +08:00
}
/* eslint-enable */
2021-06-04 14:44:41 +08:00
jest . useRealTimers ( ) ;
2020-01-07 16:20:18 +08:00
} ) ;
2020-01-14 14:45:22 +08:00
// https://github.com/ant-design/ant-design/issues/20813
2022-09-28 12:01:20 +08:00
it ( 'should update help directly when provided' , async ( ) = > {
const App : React.FC = ( ) = > {
2020-01-14 14:45:22 +08:00
const [ message , updateMessage ] = React . useState ( '' ) ;
return (
< Form >
< Form.Item label = "hello" help = { message } >
< Input / >
< / Form.Item >
< Button onClick = { ( ) = > updateMessage ( 'bamboo' ) } / >
< / Form >
) ;
2022-09-28 12:01:20 +08:00
} ;
render ( < App / > ) ;
// should show initial text
await expect ( screen . findByRole ( 'alert' ) ) . resolves . toHaveTextContent ( '' ) ;
2020-01-14 14:45:22 +08:00
2022-09-28 12:01:20 +08:00
await userEvent . click ( screen . getByRole ( 'button' ) ) ;
// should show bamboo alert without opacity and hide first alert with opacity: 0
await expect ( screen . findByRole ( 'alert' ) ) . resolves . toHaveTextContent ( 'bamboo' ) ;
2020-01-14 14:45:22 +08:00
} ) ;
2020-01-17 11:50:06 +08:00
it ( 'warning when use `dependencies` but `name` is empty & children is not a render props' , ( ) = > {
2022-09-28 12:01:20 +08:00
render (
2020-01-17 11:50:06 +08:00
< Form >
< Form.Item dependencies = { [ ] } > text < / Form.Item >
< / Form > ,
) ;
expect ( errorSpy ) . toHaveBeenCalledWith (
'Warning: [antd: Form.Item] Must set `name` or use render props when `dependencies` is set.' ,
) ;
} ) ;
// https://github.com/ant-design/ant-design/issues/20948
it ( 'not repeat render when Form.Item is not a real Field' , async ( ) = > {
2022-09-28 12:01:20 +08:00
jest . useFakeTimers ( ) ;
2020-01-17 11:50:06 +08:00
const shouldNotRender = jest . fn ( ) ;
2022-09-28 12:01:20 +08:00
const StaticInput : React.FC = ( ) = > {
2020-01-17 11:50:06 +08:00
shouldNotRender ( ) ;
return < Input / > ;
} ;
const shouldRender = jest . fn ( ) ;
2022-09-28 12:01:20 +08:00
const DynamicInput : React.FC = ( ) = > {
2020-01-17 11:50:06 +08:00
shouldRender ( ) ;
return < Input / > ;
} ;
2022-09-28 12:01:20 +08:00
const formRef = React . createRef < FormInstance > ( ) ;
2020-01-17 11:50:06 +08:00
2022-09-28 12:01:20 +08:00
pureRender (
< Form ref = { formRef } >
< Form.Item >
< StaticInput / >
< / Form.Item >
< Form.Item name = "light" >
< DynamicInput / >
< / Form.Item >
< / Form > ,
2020-01-17 11:50:06 +08:00
) ;
expect ( shouldNotRender ) . toHaveBeenCalledTimes ( 1 ) ;
expect ( shouldRender ) . toHaveBeenCalledTimes ( 1 ) ;
2022-09-28 12:01:20 +08:00
formRef . current ? . setFieldsValue ( { light : 'bamboo' } ) ;
await waitFakeTimer ( ) ;
2020-01-17 11:50:06 +08:00
expect ( shouldNotRender ) . toHaveBeenCalledTimes ( 1 ) ;
expect ( shouldRender ) . toHaveBeenCalledTimes ( 2 ) ;
2022-09-28 12:01:20 +08:00
jest . clearAllTimers ( ) ;
jest . useRealTimers ( ) ;
2020-01-17 11:50:06 +08:00
} ) ;
2020-01-20 16:09:59 +08:00
it ( 'empty help should also render' , ( ) = > {
2022-09-28 12:01:20 +08:00
const { container } = render (
2020-01-20 16:09:59 +08:00
< Form.Item help = "" >
< input / >
< / Form.Item > ,
) ;
2022-09-28 12:01:20 +08:00
expect ( container . querySelectorAll ( '.ant-form-item-explain' ) . length ) . toBeTruthy ( ) ;
2020-01-20 16:09:59 +08:00
} ) ;
2020-02-01 20:09:29 +08:00
2020-07-13 12:02:15 +08:00
it ( 'Form.Item with `help` should display error style when validate failed' , async ( ) = > {
2022-04-06 11:07:15 +08:00
const { container } = render (
2020-07-13 12:02:15 +08:00
< Form >
2022-04-06 11:07:15 +08:00
< Form.Item
name = "test"
2022-09-28 12:01:20 +08:00
label = "test"
2022-04-06 11:07:15 +08:00
help = "help"
initialValue = "bamboo"
rules = { [ { required : true , message : 'message' } ] }
>
2020-07-13 12:02:15 +08:00
< Input / >
< / Form.Item >
< / Form > ,
) ;
2022-04-06 11:07:15 +08:00
await change ( container , 0 , '' , true ) ;
expect ( container . querySelector ( '.ant-form-item' ) ) . toHaveClass ( 'ant-form-item-has-error' ) ;
2022-09-28 12:01:20 +08:00
expect ( container . querySelector ( '.ant-form-item-explain' ) ! . textContent ) . toEqual ( 'help' ) ;
2021-06-04 14:44:41 +08:00
jest . useRealTimers ( ) ;
2020-07-13 12:02:15 +08:00
} ) ;
2022-09-28 12:01:20 +08:00
it ( 'clear validation message when' , async ( ) = > {
2021-06-04 14:44:41 +08:00
jest . useFakeTimers ( ) ;
2022-04-06 11:07:15 +08:00
const { container } = render (
2020-09-18 16:53:18 +08:00
< Form >
2022-09-28 12:01:20 +08:00
< Form.Item name = "test" label = "test" rules = { [ { required : true , message : 'message' } ] } >
2020-09-18 16:53:18 +08:00
< Input / >
< / Form.Item >
< / Form > ,
) ;
2022-09-28 12:01:20 +08:00
2022-04-06 11:07:15 +08:00
await change ( container , 0 , '1' , true ) ;
expect ( container . querySelectorAll ( '.ant-form-item-explain' ) . length ) . toBeFalsy ( ) ;
2021-06-04 14:44:41 +08:00
2022-04-06 11:07:15 +08:00
await change ( container , 0 , '' , true ) ;
expect ( container . querySelectorAll ( '.ant-form-item-explain' ) . length ) . toBeTruthy ( ) ;
2021-06-04 14:44:41 +08:00
2022-04-06 11:07:15 +08:00
await change ( container , 0 , '123' , true ) ;
2020-09-18 16:53:18 +08:00
await sleep ( 800 ) ;
2022-04-06 11:07:15 +08:00
expect ( container . querySelectorAll ( '.ant-form-item-explain' ) . length ) . toBeFalsy ( ) ;
2021-06-04 14:44:41 +08:00
jest . useRealTimers ( ) ;
2020-09-18 16:53:18 +08:00
} ) ;
2020-02-01 20:09:29 +08:00
// https://github.com/ant-design/ant-design/issues/21167
it ( '`require` without `name`' , ( ) = > {
2022-09-28 12:01:20 +08:00
render (
< Form.Item label = "test" name = "test" required >
2020-02-01 20:09:29 +08:00
< input / >
< / Form.Item > ,
) ;
2022-09-28 12:01:20 +08:00
expect ( screen . getByTitle ( 'test' ) ) . toHaveClass ( 'ant-form-item-required' ) ;
2020-02-01 20:09:29 +08:00
} ) ;
2020-02-02 22:28:05 +08:00
it ( '0 is a validate Field' , ( ) = > {
2022-09-28 12:01:20 +08:00
render (
< Form.Item name = { 0 } label = "0" >
2020-02-02 22:28:05 +08:00
< input / >
< / Form.Item > ,
) ;
2022-09-28 12:01:20 +08:00
// if getByLabelText can get element, then it is a validate field with form control and label
expect ( screen . getByLabelText ( '0' ) ) . toBeInTheDocument ( ) ;
2020-02-02 22:28:05 +08:00
} ) ;
2020-02-03 17:54:33 +08:00
it ( '`null` triggers warning and is treated as `undefined`' , ( ) = > {
2022-09-28 12:01:20 +08:00
render (
< Form.Item name = { null as unknown as NamePath } label = "test" >
2020-02-03 17:54:33 +08:00
< input / >
< / Form.Item > ,
) ;
2022-09-28 12:01:20 +08:00
// if getByLabelText can get element, then it is a validate field with form control and label
expect ( screen . queryByLabelText ( 'test' ) ) . not . toBeInTheDocument ( ) ;
2020-02-03 17:54:33 +08:00
expect ( errorSpy ) . toHaveBeenCalledWith (
'Warning: [antd: Form.Item] `null` is passed as `name` property' ,
) ;
} ) ;
2020-02-18 15:38:00 +08:00
// https://github.com/ant-design/ant-design/issues/21415
2022-09-28 12:01:20 +08:00
it ( 'should not throw error when Component.props.onChange is null' , ( ) = > {
const CustomComponent : React.FC = ( ) = > (
< input onChange = { null as unknown as ChangeEventHandler < HTMLInputElement > } / >
) ;
render (
< Form >
< Form.Item name = "custom" >
< CustomComponent / >
< / Form.Item >
< / Form > ,
) ;
const handle = async ( ) = > {
await userEvent . type ( screen . getByRole ( 'textbox' ) , 'aaa' ) ;
} ;
expect ( handle ) . not . toThrow ( ) ;
2020-02-18 15:38:00 +08:00
} ) ;
2020-03-03 12:10:32 +08:00
2022-09-28 12:01:20 +08:00
it ( 'change `help` should not warning' , async ( ) = > {
const Demo : React.FC = ( ) = > {
const [ error , setError ] = React . useState ( false ) ;
2020-03-03 12:10:32 +08:00
return (
< Form >
< Form.Item
help = { error ? 'This is an error msg' : undefined }
validateStatus = { error ? 'error' : '' }
label = "Username"
name = "username"
>
< input / >
< / Form.Item >
< Form.Item >
< button type = "button" onClick = { ( ) = > setError ( ! error ) } >
Trigger
< / button >
< / Form.Item >
< / Form >
) ;
} ;
2022-09-28 12:01:20 +08:00
render ( < Demo / > ) ;
await userEvent . click ( screen . getByRole ( 'button' ) ) ;
2020-03-03 12:10:32 +08:00
expect ( errorSpy ) . not . toHaveBeenCalled ( ) ;
} ) ;
2020-03-06 12:07:55 +08:00
2020-03-03 21:57:06 +08:00
it ( '`label` support template' , async ( ) = > {
2022-09-28 12:01:20 +08:00
render (
2020-03-03 21:57:06 +08:00
// eslint-disable-next-line no-template-curly-in-string
< Form validateMessages = { { required : '${label} is good!' } } >
< Form.Item name = "test" label = "Bamboo" rules = { [ { required : true } ] } >
< input / >
< / Form.Item >
2022-09-28 12:01:20 +08:00
< Form.Item >
< Button htmlType = "submit" > Submit < / Button >
< / Form.Item >
2020-03-03 21:57:06 +08:00
< / Form > ,
) ;
2022-09-28 12:01:20 +08:00
await userEvent . click ( screen . getByRole ( 'button' ) ) ;
await expect ( screen . findByRole ( 'alert' ) ) . resolves . toHaveTextContent ( 'Bamboo is good!' ) ;
2020-03-03 21:57:06 +08:00
} ) ;
2020-03-06 18:12:39 +08:00
2022-01-13 13:50:18 +08:00
// https://github.com/ant-design/ant-design/issues/33691
it ( 'should keep upper locale in nested ConfigProvider' , async ( ) = > {
2022-09-28 12:01:20 +08:00
render (
2022-01-13 13:50:18 +08:00
< ConfigProvider locale = { zhCN } >
< ConfigProvider >
< Form >
< Form.Item name = "test" label = "Bamboo" rules = { [ { required : true } ] } >
< input / >
< / Form.Item >
2022-09-28 12:01:20 +08:00
< Form.Item >
< Button htmlType = "submit" > Submit < / Button >
< / Form.Item >
2022-01-13 13:50:18 +08:00
< / Form >
< / ConfigProvider >
2021-12-31 16:56:21 +08:00
< / ConfigProvider > ,
) ;
2022-09-28 12:01:20 +08:00
await userEvent . click ( screen . getByRole ( 'button' ) ) ;
await expect ( screen . findByRole ( 'alert' ) ) . resolves . toHaveTextContent ( '请输入Bamboo' ) ;
2021-12-31 16:56:21 +08:00
} ) ;
2021-06-30 11:32:34 +08:00
it ( '`name` support template when label is not provided' , async ( ) = > {
2022-09-28 12:01:20 +08:00
render (
2021-06-30 11:32:34 +08:00
// eslint-disable-next-line no-template-curly-in-string
< Form validateMessages = { { required : '${label} is good!' } } >
< Form.Item name = "Bamboo" rules = { [ { required : true } ] } >
< input / >
< / Form.Item >
2022-09-28 12:01:20 +08:00
< Form.Item >
< Button htmlType = "submit" > Submit < / Button >
< / Form.Item >
2021-06-30 11:32:34 +08:00
< / Form > ,
) ;
2022-09-28 12:01:20 +08:00
await userEvent . click ( screen . getByRole ( 'button' ) ) ;
await expect ( screen . findByRole ( 'alert' ) ) . resolves . toHaveTextContent ( 'Bamboo is good!' ) ;
2021-06-30 11:32:34 +08:00
} ) ;
2020-09-10 21:00:21 +08:00
it ( '`messageVariables` support validate' , async ( ) = > {
2022-09-28 12:01:20 +08:00
render (
2020-09-10 21:00:21 +08:00
// eslint-disable-next-line no-template-curly-in-string
< Form validateMessages = { { required : '${label} is good!' } } >
< Form.Item name = "test" messageVariables = { { label : 'Bamboo' } } rules = { [ { required : true } ] } >
< input / >
< / Form.Item >
2022-09-28 12:01:20 +08:00
< Form.Item >
< Button htmlType = "submit" > Submit < / Button >
< / Form.Item >
2020-09-10 21:00:21 +08:00
< / Form > ,
) ;
2022-09-28 12:01:20 +08:00
await userEvent . click ( screen . getByRole ( 'button' ) ) ;
await expect ( screen . findByRole ( 'alert' ) ) . resolves . toHaveTextContent ( 'Bamboo is good!' ) ;
2020-09-10 21:00:21 +08:00
} ) ;
2020-07-21 22:29:31 +08:00
it ( 'validation message should has alert role' , async ( ) = > {
// https://github.com/ant-design/ant-design/issues/25711
2022-09-28 12:01:20 +08:00
render (
2020-07-21 22:29:31 +08:00
// eslint-disable-next-line no-template-curly-in-string
< Form validateMessages = { { required : 'name is good!' } } >
< Form.Item name = "test" rules = { [ { required : true } ] } >
< input / >
< / Form.Item >
2022-09-28 12:01:20 +08:00
< Form.Item >
< Button htmlType = "submit" > Submit < / Button >
< / Form.Item >
2020-07-21 22:29:31 +08:00
< / Form > ,
) ;
2022-09-28 12:01:20 +08:00
await userEvent . click ( screen . getByRole ( 'button' ) ) ;
await expect ( screen . findByRole ( 'alert' ) ) . resolves . toHaveTextContent ( 'name is good!' ) ;
2020-07-21 22:29:31 +08:00
} ) ;
2022-09-28 12:01:20 +08:00
it ( 'return same form instance' , async ( ) = > {
2020-03-06 12:07:55 +08:00
const instances = new Set ( ) ;
2022-09-28 12:01:20 +08:00
const App : React.FC = ( ) = > {
2020-03-06 12:07:55 +08:00
const [ form ] = Form . useForm ( ) ;
instances . add ( form ) ;
const [ , forceUpdate ] = React . useState ( { } ) ;
return (
< button
type = "button"
onClick = { ( ) = > {
forceUpdate ( { } ) ;
} }
>
Refresh
< / button >
) ;
} ;
2022-09-28 12:01:20 +08:00
pureRender ( < App / > ) ;
2020-03-06 12:07:55 +08:00
for ( let i = 0 ; i < 5 ; i += 1 ) {
2022-09-28 12:01:20 +08:00
// eslint-disable-next-line no-await-in-loop
await userEvent . click ( screen . getByRole ( 'button' ) ) ;
2020-03-06 12:07:55 +08:00
}
2022-09-28 12:01:20 +08:00
expect ( instances . size ) . toBe ( 1 ) ;
2020-03-06 12:07:55 +08:00
} ) ;
2020-03-08 16:28:33 +08:00
2022-09-28 12:01:20 +08:00
it ( 'should avoid re-render' , async ( ) = > {
2020-03-08 16:28:33 +08:00
let renderTimes = 0 ;
2022-09-28 12:01:20 +08:00
const MyInput : React.FC < { value? : string } > = ( { value = '' , . . . props } ) = > {
2020-03-08 16:28:33 +08:00
renderTimes += 1 ;
return < input value = { value } { ...props } / > ;
} ;
2022-09-28 12:01:20 +08:00
const Demo : React.FC = ( ) = > (
2020-03-08 16:28:33 +08:00
< Form >
2022-09-28 12:01:20 +08:00
< Form.Item name = "username" label = "username" rules = { [ { required : true } ] } >
2020-03-08 16:28:33 +08:00
< MyInput / >
< / Form.Item >
< / Form >
) ;
2022-09-28 12:01:20 +08:00
pureRender ( < Demo / > ) ;
2020-03-08 16:28:33 +08:00
renderTimes = 0 ;
2022-09-28 12:01:20 +08:00
jest . clearAllMocks ( ) ;
fireEvent . change ( screen . getByLabelText ( 'username' ) , { target : { value : 'a' } } ) ;
2020-03-08 16:28:33 +08:00
expect ( renderTimes ) . toEqual ( 1 ) ;
2022-09-28 12:01:20 +08:00
expect ( screen . getByLabelText ( 'username' ) ) . toHaveValue ( 'a' ) ;
2020-03-08 16:28:33 +08:00
} ) ;
2020-03-24 22:23:40 +08:00
2022-09-28 12:01:20 +08:00
it ( 'should warning with `defaultValue`' , ( ) = > {
render (
2020-03-24 22:23:40 +08:00
< Form >
< Form.Item name = "light" >
< input defaultValue = "should warning" / >
< / Form.Item >
< / Form > ,
) ;
expect ( errorSpy ) . toHaveBeenCalledWith (
'Warning: [antd: Form.Item] `defaultValue` will not work on controlled Field. You should use `initialValues` of Form instead.' ,
) ;
} ) ;
2020-04-08 18:47:59 +08:00
2022-09-28 12:01:20 +08:00
it ( 'should remove Field and also reset error' , async ( ) = > {
const Demo : React.FC < { showA? : boolean } > = ( { showA } ) = > (
2022-04-18 21:02:11 +08:00
< Form >
{ showA ? (
< Form.Item name = "a" help = "error" >
< input / >
< / Form.Item >
) : (
< Form.Item name = "b" >
< input / >
< / Form.Item >
) }
< / Form >
) ;
2020-04-08 18:47:59 +08:00
2022-09-28 12:01:20 +08:00
const { rerender } = render ( < Demo showA / > ) ;
await expect ( screen . findByRole ( 'alert' ) ) . resolves . toBeInTheDocument ( ) ;
2020-04-08 18:47:59 +08:00
2022-09-28 12:01:20 +08:00
rerender ( < Demo showA = { false } / > ) ;
expect ( screen . queryByRole ( 'alert' ) ) . not . toBeInTheDocument ( ) ;
2020-04-08 18:47:59 +08:00
} ) ;
2020-05-04 12:13:44 +08:00
2020-07-08 23:43:26 +08:00
it ( 'no warning of initialValue & getValueProps & preserve' , ( ) = > {
2022-04-06 11:07:15 +08:00
render (
2020-05-04 12:13:44 +08:00
< Form >
2022-09-28 12:01:20 +08:00
< Form.Item initialValue = "bamboo" getValueProps = { ( ) = > ( { } ) } preserve = { false } >
2020-05-04 12:13:44 +08:00
< Input / >
< / Form.Item >
< / Form > ,
) ;
expect ( errorSpy ) . not . toHaveBeenCalled ( ) ;
} ) ;
2020-06-11 22:25:58 +08:00
2022-09-28 12:01:20 +08:00
it ( 'should customize id when pass with id' , ( ) = > {
render (
2020-06-11 22:25:58 +08:00
< Form >
< Form.Item name = "light" >
< Input id = "bamboo" / >
< / Form.Item >
< / Form > ,
) ;
2022-09-28 12:01:20 +08:00
expect ( screen . getByRole ( 'textbox' ) ) . toHaveAttribute ( 'id' , 'bamboo' ) ;
2020-06-11 22:25:58 +08:00
} ) ;
2020-06-14 14:15:17 +08:00
2022-09-28 12:01:20 +08:00
it ( 'should trigger validate when onBlur when pass validateTrigger onBlur' , async ( ) = > {
render (
2020-06-14 14:15:17 +08:00
< Form validateTrigger = "onBlur" >
2022-09-28 12:01:20 +08:00
< Form.Item name = "light" label = "light" rules = { [ { len : 3 } ] } >
2020-06-14 14:15:17 +08:00
< Input / >
< / Form.Item >
< / Form > ,
) ;
2022-09-28 12:01:20 +08:00
// type a invalidate value, not trigger validation
await userEvent . type ( screen . getByRole ( 'textbox' ) , '7777' ) ;
expect ( screen . queryByRole ( 'alert' ) ) . not . toBeInTheDocument ( ) ;
// tab(onBlur) the input field, trigger and see the alert
fireEvent . blur ( screen . getByRole ( 'textbox' ) ) ;
await expect ( screen . findByRole ( 'alert' ) ) . resolves . toBeInTheDocument ( ) ;
2020-06-14 14:15:17 +08:00
} ) ;
2020-06-19 14:03:16 +08:00
2020-08-05 10:08:57 +08:00
describe ( 'Form item hidden' , ( ) = > {
it ( 'should work' , ( ) = > {
2022-09-28 12:01:20 +08:00
const { container } = render (
2020-08-05 10:08:57 +08:00
< Form >
< Form.Item name = "light" hidden >
< Input / >
< / Form.Item >
< / Form > ,
) ;
2022-09-28 12:01:20 +08:00
expect ( container . firstChild ) . toMatchSnapshot ( ) ;
2020-08-05 10:08:57 +08:00
} ) ;
it ( 'noStyle should not work when hidden' , ( ) = > {
2022-09-28 12:01:20 +08:00
const { container } = render (
2020-08-05 10:08:57 +08:00
< Form >
< Form.Item name = "light" hidden noStyle >
< Input / >
< / Form.Item >
< / Form > ,
) ;
2022-09-28 12:01:20 +08:00
expect ( container . firstChild ) . toMatchSnapshot ( ) ;
2020-08-05 10:08:57 +08:00
} ) ;
2020-06-19 14:03:16 +08:00
} ) ;
2020-08-21 12:58:14 +08:00
it ( 'legacy hideRequiredMark' , ( ) = > {
2022-09-28 12:01:20 +08:00
render (
< Form hideRequiredMark role = "form" >
< Form.Item name = "light" label = "light" required >
2020-08-21 12:58:14 +08:00
< Input / >
< / Form.Item >
< / Form > ,
) ;
2022-09-28 12:01:20 +08:00
expect ( screen . getByRole ( 'form' ) ) . toHaveClass ( 'ant-form-hide-required-mark' ) ;
2020-08-21 12:58:14 +08:00
} ) ;
2020-09-17 17:11:45 +08:00
2022-04-29 20:48:10 +08:00
it ( 'form should support disabled' , ( ) = > {
2022-09-28 12:01:20 +08:00
const App : React.FC = ( ) = > (
2022-07-01 21:29:31 +08:00
< Form labelCol = { { span : 4 } } wrapperCol = { { span : 14 } } layout = "horizontal" disabled >
< Form.Item label = "Form disabled" name = "disabled" valuePropName = "checked" >
< Checkbox > disabled < / Checkbox >
< / Form.Item >
< Form.Item label = "Radio" >
< Radio.Group >
2022-09-28 12:01:20 +08:00
< Radio value = "apple" > Apple < / Radio >
< Radio value = "pear" > Pear < / Radio >
2022-07-01 21:29:31 +08:00
< / Radio.Group >
< / Form.Item >
< Form.Item label = "Input" >
< Input / >
< / Form.Item >
< Form.Item label = "Select" >
< Select >
< Select.Option value = "demo" > Demo < / Select.Option >
< / Select >
< / Form.Item >
< Form.Item label = "TreeSelect" >
< TreeSelect
treeData = { [
{
title : 'Light' ,
value : 'light' ,
children : [ { title : 'Bamboo' , value : 'bamboo' } ] ,
} ,
] }
/ >
< / Form.Item >
< Form.Item label = "Cascader" >
< Cascader
options = { [
{
value : 'zhejiang' ,
label : 'Zhejiang' ,
2022-09-28 12:01:20 +08:00
children : [ { value : 'hangzhou' , label : 'Hangzhou' } ] ,
2022-07-01 21:29:31 +08:00
} ,
] }
/ >
< / Form.Item >
< Form.Item label = "DatePicker" >
< DatePicker / >
< / Form.Item >
< Form.Item label = "RangePicker" >
< RangePicker / >
< / Form.Item >
< Form.Item label = "InputNumber" >
< InputNumber / >
< / Form.Item >
< Form.Item label = "TextArea" >
< TextArea rows = { 4 } / >
< / Form.Item >
< Form.Item label = "Switch" valuePropName = "checked" >
< Switch / >
< / Form.Item >
2022-07-18 14:21:52 +08:00
< Form.Item label = "Upload" valuePropName = "fileList" >
< Upload / >
< / Form.Item >
2022-07-01 21:29:31 +08:00
< Form.Item label = "Button" >
< Button > Button < / Button >
< / Form.Item >
< / Form >
) ;
2022-09-28 12:01:20 +08:00
const { container } = render ( < App / > ) ;
expect ( container . firstChild ) . toMatchSnapshot ( ) ;
2022-04-29 20:48:10 +08:00
} ) ;
2020-11-20 21:43:43 +08:00
it ( '_internalItemRender api test' , ( ) = > {
2022-09-28 12:01:20 +08:00
render (
2020-11-20 21:43:43 +08:00
< Form >
< Form.Item
name = "light"
2022-09-28 12:01:20 +08:00
// @ts-ignore
2020-11-20 21:43:43 +08:00
_internalItemRender = { {
mark : 'pro_table_render' ,
2022-09-28 12:01:20 +08:00
render : ( _ : any , doms : any ) = > (
< div >
< h1 > warning title < / h1 >
2020-12-09 17:12:32 +08:00
{ doms . input }
{ doms . errorList }
{ doms . extra }
< / div >
) ,
2020-11-20 21:43:43 +08:00
} }
>
< input defaultValue = "should warning" / >
< / Form.Item >
< / Form > ,
) ;
2022-09-28 12:01:20 +08:00
expect ( screen . getByRole ( 'heading' ) ) . toHaveTextContent ( /warning title/i ) ;
2021-12-12 14:58:49 +08:00
} ) ;
it ( 'Form Item element id will auto add form_item prefix if form name is empty and item name is in the black list' , async ( ) = > {
const mockFn = jest . spyOn ( Util , 'getFieldId' ) ;
const itemName = 'parentNode' ;
// mock getFieldId old logic,if form name is empty ,and item name is parentNode,will get parentNode
mockFn . mockImplementation ( ( ) = > itemName ) ;
const { Option } = Select ;
2022-09-28 12:01:20 +08:00
const Demo : React.FC = ( ) = > {
2021-12-12 14:58:49 +08:00
const [ open , setOpen ] = useState ( false ) ;
return (
< >
< Form >
2022-09-28 12:01:20 +08:00
< Form.Item name = { itemName } label = { itemName } >
2021-12-12 14:58:49 +08:00
< Select
className = "form_item_parentNode"
defaultValue = "lucy"
open = { open }
style = { { width : 120 } }
>
< Option value = "jack" > Jack < / Option >
< Option value = "lucy" > Lucy < / Option >
< Option value = "Yiminghe" > yiminghe < / Option >
< / Select >
< / Form.Item >
< / Form >
< button
type = "button"
onClick = { ( ) = > {
setOpen ( true ) ;
} }
>
{ open ? 'show' : 'hidden' }
< / button >
< / >
) ;
} ;
2022-09-28 12:01:20 +08:00
const { rerender } = render ( < Demo / > ) ;
2021-12-12 14:58:49 +08:00
expect ( mockFn ) . toHaveBeenCalled ( ) ;
2022-09-28 12:01:20 +08:00
expect ( ( Util . getFieldId as ( ) = > string ) ( ) ) . toBe ( itemName ) ;
2021-12-12 14:58:49 +08:00
// make sure input id is parentNode
2022-09-28 12:01:20 +08:00
expect ( screen . getByLabelText ( itemName ) ) . toHaveAccessibleName ( itemName ) ;
await userEvent . click ( screen . getByRole ( 'button' ) ) ;
expect ( screen . getByRole ( 'button' ) ) . toHaveTextContent ( 'show' ) ;
2021-12-12 14:58:49 +08:00
mockFn . mockRestore ( ) ;
2022-09-28 12:01:20 +08:00
rerender ( < Demo / > ) ;
expect ( screen . getByLabelText ( itemName ) ) . toBeInTheDocument ( ) ;
2020-11-20 21:43:43 +08:00
} ) ;
2020-09-17 17:11:45 +08:00
describe ( 'tooltip' , ( ) = > {
2022-09-28 12:01:20 +08:00
it ( 'ReactNode' , async ( ) = > {
render (
2020-09-17 17:11:45 +08:00
< Form >
< Form.Item label = "light" tooltip = { < span > Bamboo < / span > } >
< Input / >
< / Form.Item >
< / Form > ,
) ;
2022-09-28 12:01:20 +08:00
await userEvent . hover ( screen . getByRole ( 'img' , { name : 'question-circle' } ) ) ;
await expect ( screen . findByRole ( 'tooltip' ) ) . resolves . toMatchInlineSnapshot ( `
< div
class = "ant-tooltip-inner"
role = "tooltip"
>
< span >
Bamboo
< / span >
< / div >
` );
2020-09-17 17:11:45 +08:00
} ) ;
2022-09-28 12:01:20 +08:00
it ( 'config tooltip should show when hover on icon' , async ( ) = > {
render (
2020-09-17 17:11:45 +08:00
< Form >
< Form.Item label = "light" tooltip = { { title : 'Bamboo' } } >
< Input / >
< / Form.Item >
< / Form > ,
) ;
2022-09-28 12:01:20 +08:00
await userEvent . hover ( screen . getByRole ( 'img' , { name : 'question-circle' } ) ) ;
await expect ( screen . findByRole ( 'tooltip' ) ) . resolves . toHaveTextContent ( 'Bamboo' ) ;
2020-09-17 17:11:45 +08:00
} ) ;
} ) ;
2021-09-15 17:13:51 +08:00
it ( 'warningOnly validate' , async ( ) = > {
2022-04-06 11:07:15 +08:00
const { container } = render (
2021-09-15 17:13:51 +08:00
< Form >
< Form.Item >
2022-04-06 11:07:15 +08:00
< Form.Item
name = "test"
2022-09-28 12:01:20 +08:00
label = "test"
2022-04-06 11:07:15 +08:00
initialValue = "bamboo"
rules = { [ { required : true , warningOnly : true } ] }
>
2021-09-15 17:13:51 +08:00
< Input / >
< / Form.Item >
< / Form.Item >
< / Form > ,
) ;
2022-09-28 12:01:20 +08:00
await userEvent . type ( screen . getByLabelText ( 'test' ) , 'test' ) ;
await userEvent . clear ( screen . getByLabelText ( 'test' ) ) ;
await sleep ( 1000 ) ;
2022-04-06 11:07:15 +08:00
expect ( container . querySelectorAll ( '.ant-form-item-with-help' ) . length ) . toBeTruthy ( ) ;
expect ( container . querySelectorAll ( '.ant-form-item-has-warning' ) . length ) . toBeTruthy ( ) ;
2021-09-15 17:13:51 +08:00
} ) ;
it ( 'not warning when remove on validate' , async ( ) = > {
jest . useFakeTimers ( ) ;
2022-09-28 12:01:20 +08:00
let rejectFn : ( reason? : any ) = > void = jest . fn ( ) ;
2021-09-15 17:13:51 +08:00
2022-04-06 11:07:15 +08:00
const { container , unmount } = render (
2021-09-15 17:13:51 +08:00
< Form >
< Form.Item >
< Form.Item
noStyle
name = "test"
2022-04-06 11:07:15 +08:00
initialValue = "bamboo"
2021-09-15 17:13:51 +08:00
rules = { [
{
validator : ( ) = >
new Promise ( ( _ , reject ) = > {
rejectFn = reject ;
} ) ,
} ,
] }
>
< Input / >
< / Form.Item >
< / Form.Item >
< / Form > ,
) ;
2022-04-06 11:07:15 +08:00
await change ( container , 0 , '' , true ) ;
2021-09-15 17:13:51 +08:00
2022-04-06 11:07:15 +08:00
unmount ( ) ;
2021-09-15 17:13:51 +08:00
// Delay validate failed
rejectFn ( new Error ( 'delay failed' ) ) ;
expect ( errorSpy ) . not . toHaveBeenCalled ( ) ;
jest . useRealTimers ( ) ;
} ) ;
2021-11-11 17:51:33 +08:00
describe ( 'form colon' , ( ) = > {
it ( 'default colon' , ( ) = > {
2022-09-28 12:01:20 +08:00
render (
2021-11-11 17:51:33 +08:00
< Form >
2022-09-28 12:01:20 +08:00
< Form.Item label = "姓名" name = "姓名" >
2021-11-11 17:51:33 +08:00
< input / >
< / Form.Item >
< / Form > ,
) ;
2022-09-28 12:01:20 +08:00
expect ( screen . getByText ( '姓名' ) ) . not . toHaveClass ( 'ant-form-item-no-colon' ) ;
2021-11-11 17:51:33 +08:00
} ) ;
it ( 'set Form.Item colon false' , ( ) = > {
2022-09-28 12:01:20 +08:00
render (
2021-11-11 17:51:33 +08:00
< Form colon >
2022-09-28 12:01:20 +08:00
< Form.Item colon = { false } label = "姓名" name = "姓名" >
2021-11-11 17:51:33 +08:00
< Input / >
< / Form.Item >
< / Form > ,
) ;
2022-09-28 12:01:20 +08:00
expect ( screen . getByText ( '姓名' ) ) . toHaveClass ( 'ant-form-item-no-colon' ) ;
2021-11-11 17:51:33 +08:00
} ) ;
it ( 'set Form colon false' , ( ) = > {
2022-09-28 12:01:20 +08:00
render (
2021-11-11 17:51:33 +08:00
< Form colon = { false } >
2022-09-28 12:01:20 +08:00
< Form.Item label = "姓名" name = "姓名" >
2021-11-11 17:51:33 +08:00
< Input / >
< / Form.Item >
< / Form > ,
) ;
2022-09-28 12:01:20 +08:00
expect ( screen . getByText ( '姓名' ) ) . toHaveClass ( 'ant-form-item-no-colon' ) ;
2021-11-11 17:51:33 +08:00
} ) ;
} ) ;
2022-04-15 15:51:09 +08:00
it ( 'useFormInstance' , ( ) = > {
let formInstance ;
let subFormInstance ;
const Sub = ( ) = > {
const formSub = Form . useFormInstance ( ) ;
subFormInstance = formSub ;
return null ;
} ;
2022-09-28 12:01:20 +08:00
const Demo : React.FC = ( ) = > {
2022-04-15 15:51:09 +08:00
const [ form ] = Form . useForm ( ) ;
formInstance = form ;
return (
< Form form = { form } >
< Sub / >
< / Form >
) ;
} ;
render ( < Demo / > ) ;
expect ( subFormInstance ) . toBe ( formInstance ) ;
} ) ;
2022-06-06 23:39:00 +08:00
it ( 'noStyle should not affect status' , ( ) = > {
2022-09-28 12:01:20 +08:00
const Demo : React.FC = ( ) = > (
2022-06-06 23:39:00 +08:00
< Form >
< Form.Item validateStatus = "error" noStyle >
< Select className = "custom-select" / >
< / Form.Item >
< Form.Item validateStatus = "error" >
< Form.Item noStyle >
< Select className = "custom-select-b" / >
< / Form.Item >
< / Form.Item >
< Form.Item validateStatus = "error" >
< Form.Item noStyle validateStatus = "warning" >
< Select className = "custom-select-c" / >
< / Form.Item >
< / Form.Item >
2022-06-15 00:09:31 +08:00
< Form.Item noStyle >
< Form.Item validateStatus = "warning" >
< Select className = "custom-select-d" / >
< / Form.Item >
< / Form.Item >
2022-06-06 23:39:00 +08:00
< / Form >
) ;
const { container } = render ( < Demo / > ) ;
2022-06-15 00:09:31 +08:00
expect ( container . querySelector ( '.custom-select' ) ? . className ) . not . toContain ( 'status-error' ) ;
expect ( container . querySelector ( '.custom-select' ) ? . className ) . not . toContain ( 'in-form-item' ) ;
2022-06-06 23:39:00 +08:00
expect ( container . querySelector ( '.custom-select-b' ) ? . className ) . toContain ( 'status-error' ) ;
2022-06-15 00:09:31 +08:00
expect ( container . querySelector ( '.custom-select-b' ) ? . className ) . toContain ( 'in-form-item' ) ;
expect ( container . querySelector ( '.custom-select-c' ) ? . className ) . toContain ( 'status-error' ) ;
expect ( container . querySelector ( '.custom-select-c' ) ? . className ) . toContain ( 'in-form-item' ) ;
expect ( container . querySelector ( '.custom-select-d' ) ? . className ) . toContain ( 'status-warning' ) ;
expect ( container . querySelector ( '.custom-select-d' ) ? . className ) . toContain ( 'in-form-item' ) ;
2022-06-06 23:39:00 +08:00
} ) ;
it ( 'should not affect Popup children style' , ( ) = > {
2022-09-28 12:01:20 +08:00
const Demo : React.FC = ( ) = > (
2022-06-06 23:39:00 +08:00
< Form >
2022-09-28 12:01:20 +08:00
< Form.Item labelCol = { 4 as ColProps } validateStatus = "error" >
< Modal visible >
2022-06-06 23:39:00 +08:00
< Select className = "modal-select" / >
< / Modal >
< / Form.Item >
< Form.Item validateStatus = "error" >
2022-09-28 12:01:20 +08:00
< Drawer visible >
2022-06-06 23:39:00 +08:00
< Select className = "drawer-select" / >
< / Drawer >
< / Form.Item >
< / Form >
) ;
const { container } = render ( < Demo / > , { container : document.body } ) ;
expect ( container . querySelector ( '.modal-select' ) ? . className ) . not . toContain ( 'in-form-item' ) ;
expect ( container . querySelector ( '.modal-select' ) ? . className ) . not . toContain ( 'status-error' ) ;
expect ( container . querySelector ( '.drawer-select' ) ? . className ) . not . toContain ( 'in-form-item' ) ;
expect ( container . querySelector ( '.drawer-select' ) ? . className ) . not . toContain ( 'status-error' ) ;
} ) ;
2022-07-14 11:01:39 +08:00
it ( 'Form.Item.useStatus should work' , async ( ) = > {
2022-09-28 12:01:20 +08:00
jest . useFakeTimers ( ) ;
2022-07-14 11:01:39 +08:00
const {
Item : { useStatus } ,
} = Form ;
2022-09-28 12:01:20 +08:00
const CustomInput : React.FC < { className? : string ; value? : React.ReactNode } > = ( {
className ,
value ,
} ) = > {
2022-07-14 11:01:39 +08:00
const { status } = useStatus ( ) ;
return < div className = { classNames ( className , ` custom-input-status- ${ status } ` ) } > { value } < / div > ;
} ;
2022-09-28 12:01:20 +08:00
const Demo : React.FC = ( ) = > {
2022-07-14 11:01:39 +08:00
const [ form ] = Form . useForm ( ) ;
return (
< Form form = { form } name = "my-form" >
< Form.Item name = "required" rules = { [ { required : true } ] } >
< CustomInput className = "custom-input-required" value = "" / >
< / Form.Item >
< Form.Item name = "warning" validateStatus = "warning" >
< CustomInput className = "custom-input-warning" / >
< / Form.Item >
< Form.Item name = "normal" >
< CustomInput className = "custom-input" / >
< / Form.Item >
< CustomInput className = "custom-input-wrong" / >
< Button onClick = { ( ) = > form . submit ( ) } className = "submit-button" >
Submit
< / Button >
< / Form >
) ;
} ;
const { container } = render ( < Demo / > ) ;
expect ( container . querySelector ( '.custom-input-required' ) ? . classList ) . toContain (
'custom-input-status-' ,
) ;
expect ( container . querySelector ( '.custom-input-warning' ) ? . classList ) . toContain (
'custom-input-status-warning' ,
) ;
expect ( container . querySelector ( '.custom-input' ) ? . classList ) . toContain ( 'custom-input-status-' ) ;
expect ( container . querySelector ( '.custom-input-wrong' ) ? . classList ) . toContain (
'custom-input-status-undefined' ,
) ;
expect ( errorSpy ) . toHaveBeenCalledWith (
expect . stringContaining ( 'Form.Item.useStatus should be used under Form.Item component.' ) ,
) ;
2022-09-28 12:01:20 +08:00
fireEvent . click ( container . querySelector ( '.submit-button' ) ! ) ;
await waitFakeTimer ( ) ;
2022-07-14 11:01:39 +08:00
expect ( container . querySelector ( '.custom-input-required' ) ? . classList ) . toContain (
'custom-input-status-error' ,
) ;
2022-09-28 12:01:20 +08:00
jest . clearAllTimers ( ) ;
jest . useRealTimers ( ) ;
2022-07-14 11:01:39 +08:00
} ) ;
2022-07-19 16:01:31 +08:00
it ( 'item customize margin' , async ( ) = > {
2022-09-28 12:01:20 +08:00
const computeSpy = jest
. spyOn ( window , 'getComputedStyle' )
. mockImplementation ( ( ) = > ( { marginBottom : 24 } as unknown as CSSStyleDeclaration ) ) ;
2022-07-19 16:01:31 +08:00
const { container } = render (
< Form >
< Form.Item name = "required" initialValue = "bamboo" rules = { [ { required : true } ] } >
< Input / >
< / Form.Item >
< / Form > ,
) ;
2022-09-28 12:01:20 +08:00
fireEvent . change ( container . querySelector ( 'input' ) ! , { target : { value : '' } } ) ;
2022-07-19 16:01:31 +08:00
await sleep ( 0 ) ;
computeSpy . mockRestore ( ) ;
expect ( container . querySelector ( '.ant-form-item-margin-offset' ) ) . toHaveStyle ( {
marginBottom : - 24 ,
} ) ;
} ) ;
2022-09-20 16:48:59 +08:00
it ( 'form child components should be given priority to own disabled props when it in a disabled form' , ( ) = > {
const props = {
name : 'file' ,
action : 'https://www.mocky.io/v2/5cc8019d300000980a055e76' ,
headers : {
authorization : 'authorization-text' ,
} ,
capture : true ,
} ;
const renderComps = disabled = > [
< Button key = "Button" disabled = { disabled } type = "primary" htmlType = "submit" >
test
< / Button > ,
< Cascader key = "Cascader" disabled = { disabled } options = { [ ] } / > ,
< Checkbox key = "Checkbox" disabled = { disabled } / > ,
< Checkbox.Group
key = "CheckboxGroup"
disabled = { disabled }
options = { [
{ label : 'male' , value : 0 } ,
{ label : 'female' , value : 1 } ,
] }
/ > ,
< InputNumber key = "InputNumber" disabled = { disabled } / > ,
< Input key = "Input" disabled = { disabled } / > ,
< Select key = "Select" disabled = { disabled } / > ,
< Switch key = "Switch" disabled = { disabled } / > ,
< TreeSelect key = "TreeSelect" disabled = { disabled } / > ,
< Upload key = "Upload" { ...props } disabled = { disabled } >
< Button disabled = { disabled } > Click to Upload < / Button >
< / Upload > ,
< DatePicker key = "DatePicker" disabled = { disabled } / > ,
< DatePicker.RangePicker key = "DatePicker.RangePicker" disabled = { disabled } / > ,
< DatePicker.MonthPicker key = "DatePicker.MonthPicker" disabled = { disabled } / > ,
< DatePicker.QuarterPicker key = "DatePicker.QuarterPicker" disabled = { disabled } / > ,
< DatePicker.WeekPicker key = "DatePicker.WeekPicker" disabled = { disabled } / > ,
< DatePicker.YearPicker key = "DatePicker.YearPicker" disabled = { disabled } / > ,
< DatePicker.TimePicker key = "DatePicker.TimePicker" disabled = { disabled } / > ,
] ;
const App = ( ) = > < Form disabled > { renderComps ( false ) } < / Form > ;
const wrapper = render ( < App / > ) ;
expect ( wrapper . container . querySelectorAll ( '[disabled]' ) . length ) . toBe ( 0 ) ;
const App2 = ( ) = > < Form disabled > { renderComps ( ) } < / Form > ;
const wrapper2 = render ( < App2 / > ) ;
// 时间范围组件中会有两个 input 框,因此虽然上述只有 18 个组件,但,实际有 19 个 带有 disabled 属性的表单组件
expect ( wrapper2 . container . querySelectorAll ( '[disabled]' ) . length ) . toBe ( 19 ) ;
const App3 = ( ) = > < Form disabled > { renderComps ( true ) } < / Form > ;
const wrapper3 = render ( < App3 / > ) ;
expect ( wrapper3 . container . querySelectorAll ( '[disabled]' ) . length ) . toBe ( 19 ) ;
const App4 = ( ) = > < Form > { renderComps ( true ) } < / Form > ;
const wrapper4 = render ( < App4 / > ) ;
expect ( wrapper4 . container . querySelectorAll ( '[disabled]' ) . length ) . toBe ( 19 ) ;
const App5 = ( ) = > < Form > { renderComps ( ) } < / Form > ;
const wrapper5 = render ( < App5 / > ) ;
expect ( wrapper5 . container . querySelectorAll ( '[disabled]' ) . length ) . toBe ( 0 ) ;
} ) ;
2019-07-03 20:14:39 +08:00
} ) ;