2021-12-12 14:58:49 +08:00
import React , { Component , useState } from 'react' ;
2019-07-03 20:14:39 +08:00
import { mount } from 'enzyme' ;
2021-06-04 14:44:41 +08:00
import { act } from 'react-dom/test-utils' ;
2019-12-23 18:33:08 +08:00
import scrollIntoView from 'scroll-into-view-if-needed' ;
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 Input from '../../input' ;
import Button from '../../button' ;
2021-12-12 14:58:49 +08:00
import Select from '../../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' ;
2020-03-27 12:07:18 +08:00
import { sleep } from '../../../tests/utils' ;
2021-12-31 16:56:21 +08:00
import ConfigProvider from '../../config-provider' ;
2022-01-13 13:50:18 +08:00
import zhCN from '../../locale/zh_CN' ;
2019-07-03 20:14:39 +08:00
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 ) ;
2019-07-04 15:00:11 +08:00
scrollIntoView . mockImplementation ( ( ) => { } ) ;
2019-07-03 20:14:39 +08:00
const errorSpy = jest . spyOn ( console , 'error' ) . mockImplementation ( ( ) => { } ) ;
2021-06-04 14:44:41 +08:00
async function change ( wrapper , index , value , executeMockTimer ) {
2020-03-24 22:23:40 +08:00
wrapper . find ( Input ) . at ( index ) . simulate ( 'change' , { target : { value } } ) ;
2020-07-28 17:54:46 +08:00
await sleep ( 200 ) ;
2021-06-04 14:44:41 +08:00
if ( executeMockTimer ) {
act ( ( ) => {
jest . runAllTimers ( ) ;
wrapper . update ( ) ;
} ) ;
await sleep ( 1 ) ;
}
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 ( ) ;
2019-07-04 15:00:11 +08:00
scrollIntoView . mockReset ( ) ;
} ) ;
2019-07-03 20:14:39 +08:00
afterEach ( ( ) => {
errorSpy . mockReset ( ) ;
} ) ;
afterAll ( ( ) => {
errorSpy . mockRestore ( ) ;
2019-07-04 15:00:11 +08:00
scrollIntoView . mockRestore ( ) ;
2019-07-03 20:14:39 +08:00
} ) ;
2020-07-21 20:51:36 +08:00
describe ( 'noStyle Form.Item' , ( ) => {
it ( 'work' , async ( ) => {
2021-06-04 14:44:41 +08:00
jest . useFakeTimers ( ) ;
2020-07-21 20:51:36 +08:00
const onChange = jest . fn ( ) ;
2019-07-03 20:14:39 +08:00
2020-07-21 20:51:36 +08:00
const wrapper = mount (
< Form >
< Form . Item >
< Form . Item name = "test" rules = { [ { required : true } ] } >
< Input onChange = { onChange } / >
< / F o r m . I t e m >
2019-07-03 20:14:39 +08:00
< / F o r m . I t e m >
2020-07-21 20:51:36 +08:00
< / F o r m > ,
) ;
2021-06-04 14:44:41 +08:00
await change ( wrapper , 0 , '' , true ) ;
2021-01-26 09:36:20 +08:00
expect ( wrapper . find ( '.ant-form-item-with-help' ) . length ) . toBeTruthy ( ) ;
2020-07-21 20:51:36 +08:00
expect ( wrapper . find ( '.ant-form-item-has-error' ) . length ) . toBeTruthy ( ) ;
expect ( onChange ) . toHaveBeenCalled ( ) ;
2021-06-04 14:44:41 +08:00
jest . useRealTimers ( ) ;
2020-07-21 20:51:36 +08:00
} ) ;
it ( 'should clean up' , async ( ) => {
2021-06-04 14:44:41 +08:00
jest . useFakeTimers ( ) ;
2020-07-21 20:51:36 +08:00
const Demo = ( ) => {
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 ( ) ;
} catch ( e ) {
// do nothing
}
} }
/ >
< / F o r m . I t e m >
< Form . Item shouldUpdate noStyle >
{ ( ) => {
const aaa = form . getFieldValue ( 'aaa' ) ;
if ( aaa === '1' ) {
return (
< Form . Item name = "bbb" rules = { [ { required : true , message : 'aaa' } ] } >
< Input / >
< / F o r m . I t e m >
) ;
}
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 / >
< / F o r m . I t e m >
< / F o r m . I t e m >
) ;
} }
< / F o r m . I t e m >
< / F o r m >
) ;
} ;
2019-07-03 20:14:39 +08:00
2020-07-21 20:51:36 +08:00
const wrapper = mount ( < Demo / > ) ;
2021-06-04 14:44:41 +08:00
await change ( wrapper , 0 , '1' , true ) ;
2020-07-21 20:51:36 +08:00
expect ( wrapper . find ( '.ant-form-item-explain' ) . text ( ) ) . toEqual ( 'aaa' ) ;
2021-06-04 14:44:41 +08:00
await change ( wrapper , 0 , '2' , true ) ;
2020-07-21 20:51:36 +08:00
expect ( wrapper . find ( '.ant-form-item-explain' ) . text ( ) ) . toEqual ( 'ccc' ) ;
2021-06-04 14:44:41 +08:00
await change ( wrapper , 0 , '1' , true ) ;
2020-07-21 20:51:36 +08:00
expect ( wrapper . find ( '.ant-form-item-explain' ) . text ( ) ) . toEqual ( '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' , ( ) => {
mount (
< Form >
< Form . Item > { ( ) => null } < / F o r m . I t e m >
< / F o r m > ,
) ;
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`" , ( ) => {
mount (
< Form >
< Form . Item shouldUpdate dependencies = { [ ] } >
{ ( ) => null }
< / F o r m . I t e m >
< / F o r m > ,
) ;
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' , ( ) => {
mount (
< Form >
< Form . Item name = "test" shouldUpdate >
{ ( ) => null }
< / F o r m . I t e m >
< / F o r m > ,
) ;
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' , ( ) => {
mount (
< Form >
< Form . Item name = "test" >
< div > one < / d i v >
< div > two < / d i v >
< / F o r m . I t e m >
< / F o r m > ,
) ;
expect ( errorSpy ) . toHaveBeenCalledWith (
'Warning: [antd: Form.Item] `children` is array of render props cannot have `name`.' ,
) ;
} ) ;
2019-07-04 15:00:11 +08:00
describe ( 'scrollToField' , ( ) => {
function test ( name , genForm ) {
it ( name , ( ) => {
let callGetForm ;
const Demo = ( ) => {
const { props , getForm } = genForm ( ) ;
callGetForm = getForm ;
return (
< Form name = "scroll" { ... props } >
< Form . Item name = "test" >
< Input / >
< / F o r m . I t e m >
< / F o r m >
) ;
} ;
2020-02-19 17:38:46 +08:00
const wrapper = mount ( < Demo / > , { attachTo : document . body } ) ;
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' ,
} ) ;
2020-02-19 17:38:46 +08:00
wrapper . unmount ( ) ;
2019-07-04 15:00:11 +08:00
} ) ;
}
// 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 ,
} ;
} ) ;
} ) ;
2019-11-13 11:48:20 +08:00
2020-02-19 17:38:46 +08:00
it ( 'scrollToFirstError' , async ( ) => {
const onFinishFailed = jest . fn ( ) ;
const wrapper = mount (
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 / >
< / F o r m . I t e m >
< / F o r m > ,
{ attachTo : document . body } ,
) ;
expect ( scrollIntoView ) . not . toHaveBeenCalled ( ) ;
wrapper . find ( 'form' ) . simulate ( 'submit' ) ;
2020-06-09 18:10:43 +08:00
await sleep ( 50 ) ;
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 ( ) ;
wrapper . unmount ( ) ;
} ) ;
2019-11-13 11:48:20 +08:00
it ( 'Form.Item should support data-*、aria-* and custom attribute' , ( ) => {
const wrapper = mount (
< Form >
< Form . Item data - text = "123" aria - hidden = "true" cccc = "bbbb" >
text
< / F o r m . I t e m >
< / F o r m > ,
) ;
expect ( wrapper . render ( ) ) . toMatchSnapshot ( ) ;
} ) ;
2019-12-11 16:08:59 +08:00
it ( 'warning when use `name` but children is not validate element' , ( ) => {
mount (
< Form >
< Form . Item name = "warning" > text < / F o r m . I t e m >
< / F o r m > ,
) ;
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
it ( 'dynamic change required' , ( ) => {
const wrapper = mount (
< Form >
< Form . Item label = "light" name = "light" valuePropName = "checked" >
< input type = "checkbox" / >
< / F o r m . I t e m >
< Form . Item
label = "bamboo"
name = "bamboo"
dependencies = { [ 'light' ] }
rules = { [ ( { getFieldValue } ) => ( { required : getFieldValue ( 'light' ) } ) ] }
>
< input / >
< / F o r m . I t e m >
< / F o r m > ,
) ;
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 ) ;
} ) ;
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' , ( ) => {
const wrapper = mount (
< Form >
< Form . Item help = "good" >
< input / >
< / F o r m . I t e m >
< / F o r m > ,
) ;
expect ( wrapper . exists ( '.ant-form-item-with-help' ) ) . toBeTruthy ( ) ;
} ) ;
it ( 'empty string' , ( ) => {
const wrapper = mount (
< Form >
< Form . Item help = "" >
< input / >
< / F o r m . I t e m >
< / F o r m > ,
) ;
2019-12-27 16:50:44 +08:00
2022-01-10 15:58:53 +08:00
expect ( wrapper . exists ( '.ant-form-item-with-help' ) ) . toBeTruthy ( ) ;
} ) ;
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 ( ) ;
2020-01-07 16:20:18 +08:00
const wrapper = mount (
< Form >
< Form . Item
name = "name"
rules = { [
{ required : true } ,
{
validator : ( _ , value ) => {
if ( value === 'p' ) {
return Promise . reject ( new Error ( 'not a p' ) ) ;
}
return Promise . resolve ( ) ;
} ,
} ,
] }
>
< Input / >
< / F o r m . I t e m >
< / F o r m > ,
) ;
/* eslint-disable no-await-in-loop */
for ( let i = 0 ; i < 3 ; i += 1 ) {
2021-06-04 14:44:41 +08:00
await change ( wrapper , 0 , '' , true ) ;
2020-03-24 22:23:40 +08:00
expect ( wrapper . find ( '.ant-form-item-explain' ) . first ( ) . text ( ) ) . toEqual ( "'name' is required" ) ;
2020-01-07 16:20:18 +08:00
2021-06-04 14:44:41 +08:00
await change ( wrapper , 0 , 'p' , true ) ;
2020-06-09 18:10:43 +08:00
await sleep ( 100 ) ;
2020-03-06 11:53:09 +08:00
wrapper . update ( ) ;
2020-03-24 22:23:40 +08:00
expect ( wrapper . find ( '.ant-form-item-explain' ) . first ( ) . text ( ) ) . 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
it ( 'should update help directly when provided' , ( ) => {
function App ( ) {
const [ message , updateMessage ] = React . useState ( '' ) ;
return (
< Form >
< Form . Item label = "hello" help = { message } >
< Input / >
< / F o r m . I t e m >
< Button onClick = { ( ) => updateMessage ( 'bamboo' ) } / >
< / F o r m >
) ;
}
const wrapper = mount ( < App / > ) ;
wrapper . find ( 'button' ) . simulate ( 'click' ) ;
2020-03-24 22:23:40 +08:00
expect ( wrapper . find ( '.ant-form-item' ) . first ( ) . hasClass ( 'ant-form-item-with-help' ) ) . toBeTruthy ( ) ;
2020-01-14 14:45:22 +08:00
expect ( wrapper . find ( '.ant-form-item-explain' ) . text ( ) ) . toEqual ( 'bamboo' ) ;
} ) ;
2020-01-17 11:50:06 +08:00
it ( 'warning when use `dependencies` but `name` is empty & children is not a render props' , ( ) => {
mount (
< Form >
< Form . Item dependencies = { [ ] } > text < / F o r m . I t e m >
< / F o r m > ,
) ;
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 ( ) => {
const shouldNotRender = jest . fn ( ) ;
const StaticInput = ( ) => {
shouldNotRender ( ) ;
return < Input / > ;
} ;
const shouldRender = jest . fn ( ) ;
const DynamicInput = ( ) => {
shouldRender ( ) ;
return < Input / > ;
} ;
const formRef = React . createRef ( ) ;
mount (
< div >
< Form ref = { formRef } >
< Form . Item >
< StaticInput / >
< / F o r m . I t e m >
< Form . Item name = "light" >
< DynamicInput / >
< / F o r m . I t e m >
< / F o r m >
< / d i v > ,
) ;
expect ( shouldNotRender ) . toHaveBeenCalledTimes ( 1 ) ;
expect ( shouldRender ) . toHaveBeenCalledTimes ( 1 ) ;
formRef . current . setFieldsValue ( { light : 'bamboo' } ) ;
await Promise . resolve ( ) ;
expect ( shouldNotRender ) . toHaveBeenCalledTimes ( 1 ) ;
expect ( shouldRender ) . toHaveBeenCalledTimes ( 2 ) ;
} ) ;
2020-01-20 16:09:59 +08:00
it ( 'empty help should also render' , ( ) => {
const wrapper = mount (
< Form . Item help = "" >
< input / >
< / F o r m . I t e m > ,
) ;
expect ( wrapper . find ( '.ant-form-item-explain' ) . length ) . toBeTruthy ( ) ;
} ) ;
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 ( ) => {
2021-06-04 14:44:41 +08:00
jest . useFakeTimers ( ) ;
2020-07-13 12:02:15 +08:00
const wrapper = mount (
< Form >
< Form . Item name = "test" help = "help" rules = { [ { required : true , message : 'message' } ] } >
< Input / >
< / F o r m . I t e m >
< / F o r m > ,
) ;
2021-06-04 14:44:41 +08:00
await change ( wrapper , 0 , '' , true ) ;
2020-07-13 12:02:15 +08:00
expect ( wrapper . find ( '.ant-form-item' ) . first ( ) . hasClass ( 'ant-form-item-has-error' ) ) . toBeTruthy ( ) ;
expect ( wrapper . find ( '.ant-form-item-explain' ) . text ( ) ) . toEqual ( 'help' ) ;
2021-06-04 14:44:41 +08:00
jest . useRealTimers ( ) ;
2020-07-13 12:02:15 +08:00
} ) ;
2020-09-18 16:53:18 +08:00
it ( 'clear validation message when ' , async ( ) => {
2021-06-04 14:44:41 +08:00
jest . useFakeTimers ( ) ;
2020-09-18 16:53:18 +08:00
const wrapper = mount (
< Form >
< Form . Item name = "username" rules = { [ { required : true , message : 'message' } ] } >
< Input / >
< / F o r m . I t e m >
< / F o r m > ,
) ;
2021-06-04 14:44:41 +08:00
await change ( wrapper , 0 , '1' , true ) ;
2020-09-18 16:53:18 +08:00
expect ( wrapper . find ( '.ant-form-item-explain' ) . length ) . toBeFalsy ( ) ;
2021-06-04 14:44:41 +08:00
await change ( wrapper , 0 , '' , true ) ;
2020-09-18 16:53:18 +08:00
expect ( wrapper . find ( '.ant-form-item-explain' ) . length ) . toBeTruthy ( ) ;
2021-06-04 14:44:41 +08:00
await change ( wrapper , 0 , '123' , true ) ;
2020-09-18 16:53:18 +08:00
await sleep ( 800 ) ;
wrapper . update ( ) ;
expect ( wrapper . find ( '.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`' , ( ) => {
const wrapper = mount (
< Form . Item label = "test" required >
< input / >
< / F o r m . I t e m > ,
) ;
expect ( wrapper . find ( '.ant-form-item-required' ) ) . toHaveLength ( 1 ) ;
} ) ;
2020-02-02 22:28:05 +08:00
it ( '0 is a validate Field' , ( ) => {
const wrapper = mount (
< Form . Item name = { 0 } >
< input / >
< / F o r m . I t e m > ,
) ;
expect ( wrapper . find ( 'Field' ) ) . toHaveLength ( 1 ) ;
} ) ;
2020-02-03 17:54:33 +08:00
it ( '`null` triggers warning and is treated as `undefined`' , ( ) => {
const wrapper = mount (
< Form . Item name = { null } >
< input / >
< / F o r m . I t e m > ,
) ;
expect ( wrapper . find ( 'Field' ) ) . toHaveLength ( 0 ) ;
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
it ( 'Component.props.onChange is null' , ( ) => {
2020-04-03 15:02:11 +08:00
// eslint-disable-next-line react/prefer-stateless-function
2020-02-18 15:38:00 +08:00
class CustomComponent extends Component {
static defaultProps = {
onChange : null ,
} ;
render ( ) {
return < input { ... this . props } / > ;
}
}
expect ( ( ) => {
const wrapper = mount (
< Form >
< Form . Item name = "custom" >
< CustomComponent / >
< / F o r m . I t e m >
< / F o r m > ,
) ;
wrapper . find ( CustomComponent ) . simulate ( 'change' , { value : '123' } ) ;
} ) . not . toThrow ( ) ;
} ) ;
2020-03-03 12:10:32 +08:00
it ( 'change `help` should not warning' , ( ) => {
const Demo = ( ) => {
const [ error , setError ] = React . useState ( null ) ;
return (
< Form >
< Form . Item
help = { error ? 'This is an error msg' : undefined }
validateStatus = { error ? 'error' : '' }
label = "Username"
name = "username"
>
< input / >
< / F o r m . I t e m >
< Form . Item >
< button type = "button" onClick = { ( ) => setError ( ! error ) } >
Trigger
< / b u t t o n >
< / F o r m . I t e m >
< / F o r m >
) ;
} ;
const wrapper = mount ( < Demo / > ) ;
wrapper . find ( 'button' ) . simulate ( 'click' ) ;
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 ( ) => {
const wrapper = mount (
// 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 / >
< / F o r m . I t e m >
< / F o r m > ,
) ;
wrapper . find ( 'form' ) . simulate ( 'submit' ) ;
2020-06-09 18:10:43 +08:00
await sleep ( 100 ) ;
2020-03-03 21:57:06 +08:00
wrapper . update ( ) ;
2020-06-09 18:10:43 +08:00
await sleep ( 100 ) ;
2020-04-08 18:47:59 +08:00
expect ( wrapper . find ( '.ant-form-item-explain' ) . first ( ) . text ( ) ) . toEqual ( '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 ( ) => {
2021-12-31 16:56:21 +08:00
const wrapper = mount (
2022-01-13 13:50:18 +08:00
< ConfigProvider locale = { zhCN } >
< ConfigProvider >
< Form >
< Form . Item name = "test" label = "Bamboo" rules = { [ { required : true } ] } >
< input / >
< / F o r m . I t e m >
< / F o r m >
< / C o n f i g P r o v i d e r >
2021-12-31 16:56:21 +08:00
< / C o n f i g P r o v i d e r > ,
) ;
wrapper . find ( 'form' ) . simulate ( 'submit' ) ;
await sleep ( 100 ) ;
wrapper . update ( ) ;
await sleep ( 100 ) ;
2022-01-13 13:50:18 +08:00
expect ( wrapper . find ( '.ant-form-item-explain' ) . first ( ) . text ( ) ) . toEqual ( '请输入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 ( ) => {
const wrapper = mount (
// eslint-disable-next-line no-template-curly-in-string
< Form validateMessages = { { required : '${label} is good!' } } >
< Form . Item name = "Bamboo" rules = { [ { required : true } ] } >
< input / >
< / F o r m . I t e m >
< / F o r m > ,
) ;
wrapper . find ( 'form' ) . simulate ( 'submit' ) ;
await sleep ( 100 ) ;
wrapper . update ( ) ;
await sleep ( 100 ) ;
expect ( wrapper . find ( '.ant-form-item-explain' ) . first ( ) . text ( ) ) . toEqual ( 'Bamboo is good!' ) ;
} ) ;
2020-09-10 21:00:21 +08:00
it ( '`messageVariables` support validate' , async ( ) => {
const wrapper = mount (
// 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 / >
< / F o r m . I t e m >
< / F o r m > ,
) ;
wrapper . find ( 'form' ) . simulate ( 'submit' ) ;
await sleep ( 100 ) ;
wrapper . update ( ) ;
await sleep ( 100 ) ;
expect ( wrapper . find ( '.ant-form-item-explain' ) . first ( ) . text ( ) ) . toEqual ( 'Bamboo is good!' ) ;
} ) ;
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
const wrapper = mount (
// eslint-disable-next-line no-template-curly-in-string
< Form validateMessages = { { required : 'name is good!' } } >
< Form . Item name = "test" rules = { [ { required : true } ] } >
< input / >
< / F o r m . I t e m >
< / F o r m > ,
) ;
wrapper . find ( 'form' ) . simulate ( 'submit' ) ;
await sleep ( 100 ) ;
wrapper . update ( ) ;
await sleep ( 100 ) ;
expect ( wrapper . find ( '.ant-form-item-explain div' ) . getDOMNode ( ) . getAttribute ( 'role' ) ) . toBe (
'alert' ,
) ;
} ) ;
2020-03-06 12:07:55 +08:00
it ( 'return same form instance' , ( ) => {
const instances = new Set ( ) ;
const App = ( ) => {
const [ form ] = Form . useForm ( ) ;
instances . add ( form ) ;
const [ , forceUpdate ] = React . useState ( { } ) ;
return (
< button
type = "button"
onClick = { ( ) => {
forceUpdate ( { } ) ;
} }
>
Refresh
< / b u t t o n >
) ;
} ;
const wrapper = mount ( < App / > ) ;
for ( let i = 0 ; i < 5 ; i += 1 ) {
wrapper . find ( 'button' ) . simulate ( 'click' ) ;
}
expect ( instances . size ) . toEqual ( 1 ) ;
} ) ;
2020-03-08 16:28:33 +08:00
it ( 'avoid re-render' , async ( ) => {
let renderTimes = 0 ;
const MyInput = ( { value = '' , ... props } ) => {
renderTimes += 1 ;
return < input value = { value } { ... props } / > ;
} ;
const Demo = ( ) => (
< Form >
< Form . Item name = "username" rules = { [ { required : true } ] } >
< MyInput / >
< / F o r m . I t e m >
< / F o r m >
) ;
const wrapper = mount ( < Demo / > ) ;
renderTimes = 0 ;
wrapper . find ( 'input' ) . simulate ( 'change' , {
target : {
value : 'a' ,
} ,
} ) ;
2020-06-09 18:10:43 +08:00
await sleep ( ) ;
2020-03-08 16:28:33 +08:00
expect ( renderTimes ) . toEqual ( 1 ) ;
expect ( wrapper . find ( 'input' ) . props ( ) . value ) . toEqual ( 'a' ) ;
} ) ;
2020-03-24 22:23:40 +08:00
it ( 'warning with `defaultValue`' , ( ) => {
mount (
< Form >
< Form . Item name = "light" >
< input defaultValue = "should warning" / >
< / F o r m . I t e m >
< / F o r m > ,
) ;
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
it ( 'Remove Field should also reset error' , async ( ) => {
class Demo extends React . Component {
state = {
showA : true ,
} ;
render ( ) {
return (
< Form >
{ this . state . showA ? (
< Form . Item name = "a" help = "error" >
< input / >
< / F o r m . I t e m >
) : (
< Form . Item name = "b" >
< input / >
< / F o r m . I t e m >
) }
< / F o r m >
) ;
}
}
const wrapper = mount ( < Demo / > ) ;
await Promise . resolve ( ) ;
expect ( wrapper . find ( '.ant-form-item' ) . last ( ) . hasClass ( 'ant-form-item-with-help' ) ) . toBeTruthy ( ) ;
wrapper . setState ( { showA : false } ) ;
await Promise . resolve ( ) ;
wrapper . update ( ) ;
expect ( wrapper . find ( '.ant-form-item' ) . last ( ) . hasClass ( 'ant-form-item-with-help' ) ) . toBeFalsy ( ) ;
} ) ;
2020-05-04 12:13:44 +08:00
2020-07-08 23:43:26 +08:00
it ( 'no warning of initialValue & getValueProps & preserve' , ( ) => {
2020-05-04 12:13:44 +08:00
mount (
< Form >
2020-07-08 23:43:26 +08:00
< Form . Item initialValue = "bamboo" getValueProps = { ( ) => null } preserve = { false } >
2020-05-04 12:13:44 +08:00
< Input / >
< / F o r m . I t e m >
< / F o r m > ,
) ;
expect ( errorSpy ) . not . toHaveBeenCalled ( ) ;
} ) ;
2020-06-11 22:25:58 +08:00
it ( 'should customize id work' , ( ) => {
const wrapper = mount (
< Form >
< Form . Item name = "light" >
< Input id = "bamboo" / >
< / F o r m . I t e m >
< / F o r m > ,
) ;
expect ( wrapper . find ( 'input' ) . prop ( 'id' ) ) . toEqual ( 'bamboo' ) ;
} ) ;
2020-06-14 14:15:17 +08:00
it ( 'Form validateTrigger' , ( ) => {
const wrapper = mount (
< Form validateTrigger = "onBlur" >
< Form . Item name = "light" >
< Input / >
< / F o r m . I t e m >
< / F o r m > ,
) ;
expect ( wrapper . find ( 'input' ) . prop ( 'onBlur' ) ) . toBeTruthy ( ) ;
} ) ;
2020-06-19 14:03:16 +08:00
2020-08-05 10:08:57 +08:00
describe ( 'Form item hidden' , ( ) => {
it ( 'should work' , ( ) => {
const wrapper = mount (
< Form >
< Form . Item name = "light" hidden >
< Input / >
< / F o r m . I t e m >
< / F o r m > ,
) ;
2021-12-04 17:06:36 +08:00
expect ( wrapper . render ( ) ) . toMatchSnapshot ( ) ;
2020-08-05 10:08:57 +08:00
} ) ;
it ( 'noStyle should not work when hidden' , ( ) => {
const wrapper = mount (
< Form >
< Form . Item name = "light" hidden noStyle >
< Input / >
< / F o r m . I t e m >
< / F o r m > ,
) ;
2021-12-04 17:06:36 +08:00
expect ( wrapper . render ( ) ) . 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' , ( ) => {
const wrapper = mount (
< Form hideRequiredMark >
< Form . Item name = "light" required >
< Input / >
< / F o r m . I t e m >
< / F o r m > ,
) ;
expect ( wrapper . find ( 'form' ) . hasClass ( 'ant-form-hide-required-mark' ) ) . toBeTruthy ( ) ;
} ) ;
2020-09-17 17:11:45 +08:00
2020-11-20 21:43:43 +08:00
it ( '_internalItemRender api test' , ( ) => {
const wrapper = mount (
< Form >
< Form . Item
name = "light"
_internalItemRender = { {
mark : 'pro_table_render' ,
2020-12-09 17:12:32 +08:00
render : ( _ , doms ) => (
2021-12-12 14:58:49 +08:00
< div id = "_test" >
2020-12-09 17:12:32 +08:00
{ doms . input }
{ doms . errorList }
{ doms . extra }
< / d i v >
) ,
2020-11-20 21:43:43 +08:00
} }
>
< input defaultValue = "should warning" / >
< / F o r m . I t e m >
< / F o r m > ,
) ;
2021-12-12 14:58:49 +08:00
expect ( wrapper . find ( '#_test' ) . exists ( ) ) . toBeTruthy ( ) ;
} ) ;
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 ;
const Demo = ( ) => {
const [ open , setOpen ] = useState ( false ) ;
return (
< >
< Form >
< Form . Item name = { itemName } >
< Select
className = "form_item_parentNode"
defaultValue = "lucy"
open = { open }
style = { { width : 120 } }
>
< Option value = "jack" > Jack < / O p t i o n >
< Option value = "lucy" > Lucy < / O p t i o n >
< Option value = "Yiminghe" > yiminghe < / O p t i o n >
< / S e l e c t >
< / F o r m . I t e m >
< / F o r m >
< button
type = "button"
onClick = { ( ) => {
setOpen ( true ) ;
} }
>
{ open ? 'show' : 'hidden' }
< / b u t t o n >
< / >
) ;
} ;
const wrapper = mount ( < Demo / > , { attachTo : document . body } ) ;
expect ( mockFn ) . toHaveBeenCalled ( ) ;
expect ( Util . getFieldId ( ) ) . toBe ( itemName ) ;
// make sure input id is parentNode
expect ( wrapper . find ( ` # ${ itemName } ` ) . exists ( ) ) . toBeTruthy ( ) ;
act ( ( ) => {
wrapper . find ( 'button' ) . simulate ( 'click' ) ;
} ) ;
expect ( wrapper . find ( 'button' ) . text ( ) ) . toBe ( 'show' ) ;
mockFn . mockRestore ( ) ;
// https://enzymejs.github.io/enzyme/docs/api/ShallowWrapper/update.html
// setProps instead of update
wrapper . setProps ( { } ) ;
expect ( wrapper . find ( ` #form_item_ ${ itemName } ` ) . exists ( ) ) . toBeTruthy ( ) ;
wrapper . unmount ( ) ;
2020-11-20 21:43:43 +08:00
} ) ;
2020-09-17 17:11:45 +08:00
describe ( 'tooltip' , ( ) => {
it ( 'ReactNode' , ( ) => {
const wrapper = mount (
< Form >
< Form . Item label = "light" tooltip = { < span > Bamboo < / s p a n > } >
< Input / >
< / F o r m . I t e m >
< / F o r m > ,
) ;
const tooltipProps = wrapper . find ( 'Tooltip' ) . props ( ) ;
expect ( tooltipProps . title ) . toEqual ( < span > Bamboo < / s p a n > ) ;
} ) ;
it ( 'config' , ( ) => {
const wrapper = mount (
< Form >
< Form . Item label = "light" tooltip = { { title : 'Bamboo' } } >
< Input / >
< / F o r m . I t e m >
< / F o r m > ,
) ;
const tooltipProps = wrapper . find ( 'Tooltip' ) . props ( ) ;
expect ( tooltipProps . title ) . toEqual ( 'Bamboo' ) ;
} ) ;
} ) ;
2021-09-15 17:13:51 +08:00
it ( 'warningOnly validate' , async ( ) => {
jest . useFakeTimers ( ) ;
const wrapper = mount (
< Form >
< Form . Item >
< Form . Item name = "test" rules = { [ { required : true , warningOnly : true } ] } >
< Input / >
< / F o r m . I t e m >
< / F o r m . I t e m >
< / F o r m > ,
) ;
await change ( wrapper , 0 , '' , true ) ;
expect ( wrapper . find ( '.ant-form-item-with-help' ) . length ) . toBeTruthy ( ) ;
expect ( wrapper . find ( '.ant-form-item-has-warning' ) . length ) . toBeTruthy ( ) ;
jest . useRealTimers ( ) ;
} ) ;
it ( 'not warning when remove on validate' , async ( ) => {
jest . useFakeTimers ( ) ;
let rejectFn = null ;
const wrapper = mount (
< Form >
< Form . Item >
< Form . Item
noStyle
name = "test"
rules = { [
{
validator : ( ) =>
new Promise ( ( _ , reject ) => {
rejectFn = reject ;
} ) ,
} ,
] }
>
< Input / >
< / F o r m . I t e m >
< / F o r m . I t e m >
< / F o r m > ,
) ;
await change ( wrapper , 0 , '' , true ) ;
wrapper . unmount ( ) ;
// 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' , ( ) => {
const wrapper = mount (
< Form >
< Form . Item label = "姓名" >
< input / >
< / F o r m . I t e m >
< / F o r m > ,
) ;
expect ( wrapper . exists ( '.ant-form-item-no-colon' ) ) . toBeFalsy ( ) ;
} ) ;
it ( 'set Form.Item colon false' , ( ) => {
const wrapper = mount (
< Form colon >
< Form . Item colon = { false } label = "姓名" >
< Input / >
< / F o r m . I t e m >
< / F o r m > ,
) ;
expect ( wrapper . find ( '.ant-form-item-no-colon' ) ) . toBeTruthy ( ) ;
} ) ;
it ( 'set Form colon false' , ( ) => {
const wrapper = mount (
< Form colon = { false } >
< Form . Item label = "姓名" >
< Input / >
< / F o r m . I t e m >
< / F o r m > ,
) ;
expect ( wrapper . find ( '.ant-form-item-no-colon' ) ) . toBeTruthy ( ) ;
} ) ;
} ) ;
2019-07-03 20:14:39 +08:00
} ) ;