2019-07-03 20:14:39 +08:00
import React from 'react' ;
import { mount } from 'enzyme' ;
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 '..' ;
import Input from '../../input' ;
import Button from '../../button' ;
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' ;
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-12-26 15:48:52 +08:00
const delay = ( timeout = 0 ) =>
2019-07-03 20:14:39 +08:00
new Promise ( resolve => {
2019-12-26 15:48:52 +08:00
setTimeout ( resolve , timeout ) ;
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 ( ( ) => { } ) ;
async function change ( wrapper , index , value ) {
wrapper
. find ( Input )
. at ( index )
. simulate ( 'change' , { target : { value } } ) ;
2019-12-27 16:50:44 +08:00
await delay ( 50 ) ;
2019-07-03 20:14:39 +08:00
wrapper . update ( ) ;
}
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
} ) ;
describe ( 'List' , ( ) => {
function testList ( name , renderField ) {
it ( name , async ( ) => {
const wrapper = mount (
< Form >
< Form . List name = "list" >
{ ( fields , { add , remove } ) => (
2019-08-29 23:41:46 +08:00
< >
2019-07-03 20:14:39 +08:00
{ fields . map ( field => renderField ( field ) ) }
< Button className = "add" onClick = { add } >
Add
< / B u t t o n >
< Button
className = "remove"
onClick = { ( ) => {
remove ( 1 ) ;
} }
>
Remove
< / B u t t o n >
2019-08-29 23:41:46 +08:00
< / >
2019-07-03 20:14:39 +08:00
) }
< / F o r m . L i s t >
< / F o r m > ,
) ;
async function operate ( className ) {
wrapper
. find ( className )
. last ( )
. simulate ( 'click' ) ;
await delay ( ) ;
wrapper . update ( ) ;
}
await operate ( '.add' ) ;
expect ( wrapper . find ( Input ) . length ) . toBe ( 1 ) ;
await operate ( '.add' ) ;
expect ( wrapper . find ( Input ) . length ) . toBe ( 2 ) ;
await change ( wrapper , 1 , '' ) ;
2020-01-17 11:50:06 +08:00
wrapper . update ( ) ;
2019-07-03 20:14:39 +08:00
expect ( wrapper . find ( '.ant-form-item-explain' ) . length ) . toBe ( 1 ) ;
await operate ( '.remove' ) ;
2020-01-17 11:50:06 +08:00
wrapper . update ( ) ;
2019-07-03 20:14:39 +08:00
expect ( wrapper . find ( Input ) . length ) . toBe ( 1 ) ;
expect ( wrapper . find ( '.ant-form-item-explain' ) . length ) . toBe ( 0 ) ;
} ) ;
}
testList ( 'operation correctly' , field => (
< Form . Item { ... field } rules = { [ { required : true } ] } >
< Input / >
< / F o r m . I t e m >
) ) ;
2019-07-19 14:07:39 +08:00
testList ( 'nest noStyle' , field => (
2019-07-03 20:14:39 +08:00
< Form . Item key = { field . key } >
2019-07-19 14:07:39 +08:00
< Form . Item noStyle { ... field } rules = { [ { required : true } ] } >
2019-07-03 20:14:39 +08:00
< Input / >
< / F o r m . I t e m >
< / F o r m . I t e m >
) ) ;
} ) ;
2019-07-19 14:07:39 +08:00
it ( 'noStyle Form.Item' , async ( ) => {
2019-07-03 20:14:39 +08:00
const onChange = jest . fn ( ) ;
const wrapper = mount (
< Form >
< Form . Item >
< Form . Item name = "test" rules = { [ { required : true } ] } >
< Input onChange = { onChange } / >
< / F o r m . I t e m >
< / F o r m . I t e m >
< / F o r m > ,
) ;
await change ( wrapper , 0 , '' ) ;
expect ( wrapper . find ( '.ant-form-item-explain' ) . length ) . toBe ( 1 ) ;
expect ( onChange ) . toHaveBeenCalled ( ) ;
} ) ;
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 (
'Warning: [antd: Form.Item] `children` of render props only work with `shouldUpdate`.' ,
) ;
} ) ;
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-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 >
) ;
} ;
mount ( < Demo / > , { attachTo : document . body } ) ;
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
} ) ;
}
// 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
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
it ( 'should show related className when customize help' , ( ) => {
const wrapper = mount (
< Form >
< Form . Item help = "good" >
< input / >
< / F o r m . I t e m >
< / F o r m > ,
) ;
expect ( wrapper . find ( '.ant-form-item-with-help' ) . length ) . toBeTruthy ( ) ;
} ) ;
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 ( ) => {
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 ) {
await change ( wrapper , 0 , '' ) ;
expect (
wrapper
. find ( '.ant-form-item-explain' )
. first ( )
. text ( ) ,
) . toEqual ( "'name' is required" ) ;
await change ( wrapper , 0 , 'p' ) ;
expect (
wrapper
. find ( '.ant-form-item-explain' )
. first ( )
. text ( ) ,
) . toEqual ( 'not a p' ) ;
}
/* eslint-enable */
} ) ;
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-01-17 11:50:06 +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 ( ) ;
} ) ;
2019-07-03 20:14:39 +08:00
} ) ;