import { useState } from 'react' ; import { RenderHookResult } from '@testing-library/react-hooks' ; import useStateValidator , { StateValidator , UseStateValidatorReturn } from 'react-use/esm/useStateValidator' ; import renderHook from './renderHook' ; import act from './act' ; import timeout from './timeout' ; interface Mock extends jest . Mock { } describe ( 'useStateValidator' , ( ) => { it ( 'should be defined' , ( ) => { expect ( useStateValidator ) . toBeDefined ( ) ; } ) ; function getHook ( fn = jest . fn ( ( state ) : [ boolean ] => [ ! ! ( state % 2 ) ] ) ) { return [ fn , renderHook ( ( ) => { const [ state , setState ] = useState ( 1 ) ; return [ setState , useStateValidator ( state , fn ) ] ; } ) , ] ; } it ( 'should return an array of two elements' , async ( ) => { const [ , hook ] = getHook ( ) ; await timeout ( ) ; const res = hook . result . current [ 1 ] ; expect ( Array . isArray ( res ) ) . toBe ( true ) ; expect ( res [ 0 ] ) . toEqual ( [ true ] ) ; expect ( typeof res [ 1 ] ) . toBe ( 'function' ) ; } ) ; it ( 'first element should represent current validity state' , async ( ) => { const [ , hook ] = getHook ( ) ; await timeout ( ) ; let [ setState , [ validity ] ] = hook . result . current ; expect ( validity ) . toEqual ( [ true ] ) ; act ( ( ) => setState ( 3 ) ) ; await timeout ( ) ; [ setState , [ validity ] ] = hook . result . current ; expect ( validity ) . toEqual ( [ true ] ) ; act ( ( ) => setState ( 4 ) ) ; await timeout ( ) ; [ setState , [ validity ] ] = hook . result . current ; expect ( validity ) . toEqual ( [ false ] ) ; } ) ; it ( 'second element should re-call validation' , ( ) => { const [ spy , hook ] = getHook ( ) ; const [ , [ , revalidate ] ] = hook . result . current ; expect ( spy ) . toHaveBeenCalledTimes ( 1 ) ; act ( ( ) => revalidate ( ) ) ; expect ( spy ) . toHaveBeenCalledTimes ( 2 ) ; } ) ; it ( 'validator have to be called on init plus on each state update' , async ( ) => { const [ spy , hook ] = getHook ( jest . fn ( ) ) ; const [ setState ] = hook . result . current ; expect ( spy ) . toHaveBeenCalledTimes ( 1 ) ; act ( ( ) => setState ( 4 ) ) ; await timeout ( ) ; expect ( spy ) . toHaveBeenCalledTimes ( 2 ) ; act ( ( ) => setState ( ( prevState ) => prevState + 1 ) ) ; await timeout ( ) ; expect ( spy ) . toHaveBeenCalledTimes ( 3 ) ; } ) ; it ( 'should pass to validator one parameter - current state' , async ( ) => { const [ spy , hook ] = getHook ( jest . fn ( ) ) ; const [ setState ] = hook . result . current ; act ( ( ) => setState ( 4 ) ) ; await timeout ( ) ; act ( ( ) => setState ( 5 ) ) ; await timeout ( ) ; expect ( ( spy as Mock ) . mock . calls [ 0 ] . length ) . toBe ( 1 ) ; expect ( ( spy as Mock ) . mock . calls [ 0 ] . length ) . toBe ( 1 ) ; expect ( ( spy as Mock ) . mock . calls [ 0 ] [ 0 ] ) . toBe ( 1 ) ; expect ( ( spy as Mock ) . mock . calls [ 1 ] . length ) . toBe ( 1 ) ; expect ( ( spy as Mock ) . mock . calls [ 1 ] [ 0 ] ) . toBe ( 4 ) ; expect ( ( spy as Mock ) . mock . calls [ 2 ] . length ) . toBe ( 1 ) ; expect ( ( spy as Mock ) . mock . calls [ 2 ] [ 0 ] ) . toBe ( 5 ) ; } ) ; it ( 'if validator expects 2nd parameter it should pass a validity setter there' , async ( ) => { const [ spy , hook ] = getHook ( ( jest . fn ( ( state , setValidity ) : void => { setValidity ( [ state % 2 === 0 ] ) ; } ) as unknown ) as StateValidator < [ boolean ] , number > ) ; await timeout ( ) ; let [ setState , [ [ isValid ] ] ] = hook . result . current ; expect ( ( spy as Mock ) . mock . calls [ 0 ] . length ) . toBe ( 2 ) ; expect ( typeof ( spy as Mock ) . mock . calls [ 0 ] [ 1 ] ) . toBe ( 'function' ) ; expect ( isValid ) . toBe ( false ) ; act ( ( ) => setState ( ( prevState ) => prevState + 1 ) ) ; await timeout ( ) ; [ setState , [ [ isValid ] ] ] = hook . result . current ; expect ( isValid ) . toBe ( true ) ; act ( ( ) => setState ( 5 ) ) ; await timeout ( ) ; [ setState , [ [ isValid ] ] ] = hook . result . current ; expect ( isValid ) . toBe ( false ) ; } ) ; } ) ;