import React from 'react' ; import { Observable , of , Observer } from 'rxjs' ; import { mapTo , delay , withLatestFrom , combineLatest , map } from 'rxjs/operators' ; import { create , act } from 'react-test-renderer' ; import * as Sinon from 'sinon' ; import { find } from './find' ; import { useEventCallback } from '../use-event-callback' ; describe ( 'useEventCallback specs' , ( ) => { function createFixture < T > ( factory , initialValue ? ) { return function Fixture ( ) { const [ callback , value ] = useEventCallback ( factory , initialValue ) ; return ( < > < h1 > { value } < /h1> < button onClick = { callback } > click me < /button> < />); } ; } it ( 'should generate callback' , ( ) => { const Fixture = createFixture ( ( ) => of ( 1 ) ) ; const fixtureNode = < Fixture />; const testRenderer = create ( fixtureNode ) ; act ( ( ) => testRenderer . update ( fixtureNode ) ) ; const button = find ( testRenderer . root , 'button' ) ; expect ( button . props . onClick . name ) . toBe ( 'eventCallback' ) ; } ) ; it ( 'should render value' , ( ) => { const value = 1 ; const Fixture = createFixture ( ( ) => of ( value ) ) ; const fixtureNode = < Fixture />; const testRenderer = create ( fixtureNode ) ; expect ( find ( testRenderer . root , 'h1' ) . children ) . toEqual ( [ ] ) ; act ( ( ) => testRenderer . update ( fixtureNode ) ) ; expect ( find ( testRenderer . root , 'h1' ) . children ) . toEqual ( [ `${ value }` ] ) ; } ) ; it ( 'should trigger handle async callback' , ( ) => { const timer = Sinon . useFakeTimers ( ) ; const timeToDelay = 200 ; const value = 1 ; const Fixture = createFixture ( ( event$ ) => event$ . pipe ( mapTo ( value ) , delay ( timeToDelay ) ) ) ; const fixtureNode = < Fixture />; const testRenderer = create ( fixtureNode ) ; act ( ( ) => testRenderer . update ( fixtureNode ) ) ; const button = find ( testRenderer . root , 'button' ) ; button . props . onClick ( ) ; timer . tick ( timeToDelay ) ; act ( ( ) => testRenderer . update ( fixtureNode ) ) ; expect ( find ( testRenderer . root , 'h1' ) . children ) . toEqual ( [ `${ value }` ] ) ; timer . restore ( ) ; } ) ; it ( 'should handle the initial value' , ( ) => { const timer = Sinon . useFakeTimers ( ) ; const initialValue = 1000 ; const value = 1 ; const timeToDelay = 200 ; const Fixture = createFixture ( ( event$ ) => event$ . pipe ( mapTo ( value ) , delay ( timeToDelay ) ) , initialValue ) ; const fixtureNode = < Fixture />; const testRenderer = create ( fixtureNode ) ; expect ( find ( testRenderer . root , 'h1' ) . children ) . toEqual ( [ `${ initialValue }` ] ) ; act ( ( ) => testRenderer . update ( fixtureNode ) ) ; const button = find ( testRenderer . root , 'button' ) ; button . props . onClick ( ) ; timer . tick ( timeToDelay ) ; act ( ( ) => testRenderer . update ( fixtureNode ) ) ; expect ( find ( testRenderer . root , 'h1' ) . children ) . toEqual ( [ `${ value }` ] ) ; timer . restore ( ) ; } ) ; it ( 'should handle the state changed' , ( ) => { const timer = Sinon . useFakeTimers ( ) ; const initialValue = 1000 ; const value = 1 ; const timeToDelay = 200 ; const factory = ( event$ , state$ ) => event$ . pipe ( withLatestFrom ( state$ ) , map ( ( [ , state ] ) => { return state + value ; } ) , delay ( timeToDelay ) ) ; function Fixture ( ) { const [ clickCallback , stateValue ] = useEventCallback ( factory , initialValue ) ; return ( < > < h1 > { stateValue } < /h1> < button onClick = { clickCallback } > click me < /button> < />); } const fixtureNode = < Fixture />; const testRenderer = create ( fixtureNode ) ; expect ( find ( testRenderer . root , 'h1' ) . children ) . toEqual ( [ `${ initialValue }` ] ) ; act ( ( ) => testRenderer . update ( fixtureNode ) ) ; const button = find ( testRenderer . root , 'button' ) ; button . props . onClick ( ) ; timer . tick ( timeToDelay ) ; act ( ( ) => testRenderer . update ( fixtureNode ) ) ; expect ( find ( testRenderer . root , 'h1' ) . children ) . toEqual ( [ `${ initialValue + value }` ] ) ; button . props . onClick ( ) ; timer . tick ( timeToDelay ) ; act ( ( ) => testRenderer . update ( fixtureNode ) ) ; expect ( find ( testRenderer . root , 'h1' ) . children ) . toEqual ( [ `${ initialValue + value * 2 }` ] ) ; timer . restore ( ) ; } ) ; it ( 'should handle the inputs changed' , ( ) => { const timer = Sinon . useFakeTimers ( ) ; const initialValue = 1000 ; const value = 1 ; const timeToDelay = 200 ; const factory = ( event$ , _state$ , inputs$ ) : Observable < number > => event$ . pipe ( combineLatest ( inputs$ ) , map ( ( [ , [ count ] ] ) => { return value + count ; } ) , delay ( timeToDelay ) ) ; function Fixture ( props ) { const [ clickCallback , stateValue ] = useEventCallback ( factory , initialValue , [ props . count ] ) ; return ( < > < h1 > { stateValue } < /h1> < button onClick = { clickCallback } > click me < /button> < />); } const fixtureNode = < Fixture count = { 1 } />; const testRenderer = create ( fixtureNode ) ; expect ( find ( testRenderer . root , 'h1' ) . children ) . toEqual ( [ `${ initialValue }` ] ) ; act ( ( ) => testRenderer . update ( fixtureNode ) ) ; const button = find ( testRenderer . root , 'button' ) ; button . props . onClick ( ) ; timer . tick ( timeToDelay ) ; act ( ( ) => testRenderer . update ( fixtureNode ) ) ; expect ( find ( testRenderer . root , 'h1' ) . children ) . toEqual ( [ `${ value + 1 }` ] ) ; act ( ( ) => testRenderer . update ( < Fixture count = { 4 } / > ) ) ; button . props . onClick ( ) ; timer . tick ( timeToDelay ) ; act ( ( ) => testRenderer . update ( < Fixture count = { 4 } / > ) ) ; timer . tick ( timeToDelay ) ; expect ( find ( testRenderer . root , 'h1' ) . children ) . toEqual ( [ `${ value + 4 }` ] ) ; timer . restore ( ) ; } ) ; it ( 'should call teardown logic after unmount' , ( ) => { const spy = Sinon . spy ( ) ; const Fixture = createFixture ( ( ) => new Observable ( ( observer ) => { const timerId = setTimeout ( ( ) => { observer . next ( 1 ) ; observer . complete ( ) ; } , 1000 ) ; return ( ) => { spy ( ) ; clearTimeout ( timerId ) ; } ; } ) ) ; const fixtureNode = < Fixture />; const testRenderer = create ( fixtureNode ) ; act ( ( ) => { testRenderer . unmount ( ) ; } ) ; expect ( spy . callCount ) . toBe ( 1 ) ; } ) ; } ) ;