import { useRef } from 'react' ; import useList , { ListActions } from 'react-use/esm/useList' ; import renderHook from './renderHook' ; import act from './act' ; import timeout from './timeout' ; describe ( 'useList' , ( ) => { it ( 'should be defined' , ( ) => { expect ( useList ) . toBeDefined ( ) ; } ) ; function getHook < T > ( initialArray ? ) { return renderHook ( ( props ) : [ number , [ T [ ] , ListActions < T > ] ] => { const counter = useRef ( 0 ) ; return [ ++ counter . current , useList ( props ) ] ; } , { initialProps : initialArray } ) ; } it ( 'should init with 1st parameter and actions' , ( ) => { const hook = getHook ( [ 1 , 2 , 3 ] ) ; const [ , [ list , actions ] ] = hook . result . current ; expect ( list ) . toEqual ( [ 1 , 2 , 3 ] ) ; expect ( actions ) . toStrictEqual ( { set : expect . any ( Function ) , push : expect . any ( Function ) , updateAt : expect . any ( Function ) , insertAt : expect . any ( Function ) , update : expect . any ( Function ) , updateFirst : expect . any ( Function ) , upsert : expect . any ( Function ) , sort : expect . any ( Function ) , filter : expect . any ( Function ) , removeAt : expect . any ( Function ) , remove : expect . any ( Function ) , clear : expect . any ( Function ) , reset : expect . any ( Function ) , } ) ; } ) ; it ( 'should return the same actions object each render' , ( ) => { const hook = getHook ( [ 1 , 2 , 3 ] ) ; const [ , [ , actions ] ] = hook . result . current ; act ( ( ) => { actions . set ( [ 1 , 2 , 3 , 4 ] ) ; } ) ; expect ( actions ) . toBe ( hook . result . current [ 1 ] [ 1 ] ) ; } ) ; it ( 'should default with empty array' , ( ) => { expect ( getHook ( ) . result . current [ 1 ] [ 0 ] ) . toEqual ( [ ] ) ; } ) ; describe ( 'set()' , ( ) => { it ( 'should reset list with given array and cause re-render' , async ( ) => { const hook = getHook ( [ 1 , 2 , 3 ] ) ; const [ , [ , { set } ] ] = hook . result . current ; expect ( hook . result . current [ 0 ] ) . toBe ( 1 ) ; act ( ( ) => { set ( [ 1 , 2 , 3 , 4 ] ) ; } ) ; await timeout ( ) ; expect ( hook . result . current [ 1 ] [ 0 ] ) . toEqual ( [ 1 , 2 , 3 , 4 ] ) ; expect ( hook . result . current [ 0 ] ) . toBe ( 2 ) ; act ( ( ) => { set ( [ 1 , 2 , 3 , 4 , 5 ] ) ; } ) ; await timeout ( ) ; expect ( hook . result . current [ 1 ] [ 0 ] ) . toEqual ( [ 1 , 2 , 3 , 4 , 5 ] ) ; expect ( hook . result . current [ 0 ] ) . toBe ( 3 ) ; } ) ; } ) ; describe ( 'push()' , ( ) => { it ( 'should add arbitrary amount of items to the end and cause re-render' , async ( ) => { const hook = getHook ( [ 1 , 2 , 3 ] ) ; const [ , [ , { push } ] ] = hook . result . current ; expect ( hook . result . current [ 0 ] ) . toBe ( 1 ) ; act ( ( ) => { push ( 1 , 2 , 3 , 4 ) ; } ) ; await timeout ( ) ; expect ( hook . result . current [ 1 ] [ 0 ] ) . toEqual ( [ 1 , 2 , 3 , 1 , 2 , 3 , 4 ] ) ; expect ( hook . result . current [ 0 ] ) . toBe ( 2 ) ; } ) ; it ( 'should not do anything if called with no parameters' , ( ) => { const hook = getHook ( [ 1 , 2 , 3 ] ) ; const [ , [ list , { push } ] ] = hook . result . current ; expect ( hook . result . current [ 0 ] ) . toBe ( 1 ) ; act ( ( ) => { push ( ) ; } ) ; expect ( hook . result . current [ 0 ] ) . toBe ( 1 ) ; expect ( list ) . toBe ( hook . result . current [ 1 ] [ 0 ] ) ; } ) ; } ) ; describe ( 'updateAt()' , ( ) => { it ( 'should replace item at given index with given value and cause re-render' , async ( ) => { const hook = getHook ( [ 1 , 2 , 3 ] ) ; const [ , [ , { updateAt } ] ] = hook . result . current ; expect ( hook . result . current [ 0 ] ) . toBe ( 1 ) ; act ( ( ) => { updateAt ( 1 , 5 ) ; } ) ; await timeout ( ) ; expect ( hook . result . current [ 0 ] ) . toBe ( 2 ) ; expect ( hook . result . current [ 1 ] [ 0 ] ) . toEqual ( [ 1 , 5 , 3 ] ) ; } ) ; it ( 'should work fine if target index is out of array length' , async ( ) => { const hook = getHook ( [ 1 , 2 , 3 ] ) ; const [ , [ , { updateAt } ] ] = hook . result . current ; expect ( hook . result . current [ 0 ] ) . toBe ( 1 ) ; act ( ( ) => { updateAt ( 5 , 6 ) ; } ) ; await timeout ( ) ; expect ( hook . result . current [ 0 ] ) . toBe ( 2 ) ; expect ( hook . result . current [ 1 ] [ 0 ] ) . toEqual ( [ 1 , 2 , 3 , undefined , undefined , 6 ] ) ; } ) ; } ) ; describe ( 'insertAt()' , ( ) => { it ( 'should insert item at given index shifting all the right elements and cause re-render' , async ( ) => { const hook = getHook ( [ 1 , 2 , 3 ] ) ; const [ , [ , { insertAt } ] ] = hook . result . current ; expect ( hook . result . current [ 0 ] ) . toBe ( 1 ) ; act ( ( ) => { insertAt ( 1 , 5 ) ; } ) ; await timeout ( ) ; expect ( hook . result . current [ 0 ] ) . toBe ( 2 ) ; expect ( hook . result . current [ 1 ] [ 0 ] ) . toEqual ( [ 1 , 5 , 2 , 3 ] ) ; } ) ; it ( 'should work if index is out of array length' , async ( ) => { const hook = getHook ( [ 1 , 2 , 3 ] ) ; const [ , [ , { insertAt } ] ] = hook . result . current ; expect ( hook . result . current [ 0 ] ) . toBe ( 1 ) ; act ( ( ) => { insertAt ( 5 , 6 ) ; } ) ; await timeout ( ) ; expect ( hook . result . current [ 0 ] ) . toBe ( 2 ) ; expect ( hook . result . current [ 1 ] [ 0 ] ) . toEqual ( [ 1 , 2 , 3 , undefined , undefined , 6 ] ) ; } ) ; } ) ; describe ( 'update()' , ( ) => { it ( 'should replace all items that matches the predicate and cause re-render' , async ( ) => { const hook = getHook ( [ 1 , 2 , 3 ] ) ; const [ , [ , { update } ] ] = hook . result . current ; expect ( hook . result . current [ 0 ] ) . toBe ( 1 ) ; act ( ( ) => { update ( ( a , _ ) => a % 2 === 1 , 0 ) ; } ) ; await timeout ( ) ; expect ( hook . result . current [ 0 ] ) . toBe ( 2 ) ; expect ( hook . result . current [ 1 ] [ 0 ] ) . toEqual ( [ 0 , 2 , 0 ] ) ; } ) ; it ( 'should pass two parameters to the predicate, iterated element and new one' , ( ) => { const hook = getHook ( [ 1 , 2 , 3 ] ) ; const [ , [ , { update } ] ] = hook . result . current ; const spy = jest . fn ( ) ; act ( ( ) => { update ( spy , 0 ) ; } ) ; expect ( spy . mock . calls [ 0 ] [ 0 ] ) . toBe ( 1 ) ; expect ( spy . mock . calls [ 0 ] [ 1 ] ) . toBe ( 0 ) ; } ) ; } ) ; describe ( 'updateFirst()' , ( ) => { it ( 'should replace first items that matches the predicate and cause re-render' , async ( ) => { const hook = getHook ( [ 1 , 2 , 3 ] ) ; const [ , [ , { updateFirst } ] ] = hook . result . current ; expect ( hook . result . current [ 0 ] ) . toBe ( 1 ) ; act ( ( ) => { updateFirst ( ( a , _ ) => a % 2 === 1 , 0 ) ; } ) ; await timeout ( ) ; expect ( hook . result . current [ 0 ] ) . toBe ( 2 ) ; expect ( hook . result . current [ 1 ] [ 0 ] ) . toEqual ( [ 0 , 2 , 3 ] ) ; } ) ; it ( 'should pass two parameters to the predicate, iterated element and new one' , ( ) => { const hook = getHook ( [ 1 , 2 , 3 ] ) ; const [ , [ , { updateFirst } ] ] = hook . result . current ; const spy = jest . fn ( ) ; act ( ( ) => { updateFirst ( spy , 0 ) ; } ) ; expect ( spy . mock . calls [ 0 ] . length ) . toBe ( 2 ) ; expect ( spy . mock . calls [ 0 ] [ 0 ] ) . toBe ( 1 ) ; expect ( spy . mock . calls [ 0 ] [ 1 ] ) . toBe ( 0 ) ; } ) ; } ) ; describe ( 'upsert()' , ( ) => { it ( 'should replace first item that matches the predicate and cause re-render' , async ( ) => { const hook = getHook ( [ 1 , 2 , 3 ] ) ; const [ , [ , { upsert } ] ] = hook . result . current ; expect ( hook . result . current [ 0 ] ) . toBe ( 1 ) ; act ( ( ) => { upsert ( ( a , _ ) => a === 1 , 0 ) ; } ) ; await timeout ( ) ; expect ( hook . result . current [ 0 ] ) . toBe ( 2 ) ; expect ( hook . result . current [ 1 ] [ 0 ] ) . toEqual ( [ 0 , 2 , 3 ] ) ; } ) ; it ( 'otherwise should push it to the list and cause re-render' , async ( ) => { const hook = getHook ( [ 1 , 2 , 3 ] ) ; const [ , [ , { upsert } ] ] = hook . result . current ; expect ( hook . result . current [ 0 ] ) . toBe ( 1 ) ; act ( ( ) => { upsert ( ( a , _ ) => a === 5 , 0 ) ; } ) ; await timeout ( ) ; expect ( hook . result . current [ 0 ] ) . toBe ( 2 ) ; expect ( hook . result . current [ 1 ] [ 0 ] ) . toEqual ( [ 1 , 2 , 3 , 0 ] ) ; } ) ; it ( 'should pass two parameters to the predicate, iterated element and new one' , ( ) => { const hook = getHook ( [ 1 , 2 , 3 ] ) ; const [ , [ , { upsert } ] ] = hook . result . current ; const spy = jest . fn ( ) ; act ( ( ) => { upsert ( spy , 0 ) ; } ) ; expect ( spy . mock . calls [ 0 ] . length ) . toBe ( 2 ) ; expect ( spy . mock . calls [ 0 ] [ 0 ] ) . toBe ( 1 ) ; expect ( spy . mock . calls [ 0 ] [ 1 ] ) . toBe ( 0 ) ; } ) ; } ) ; describe ( 'sort()' , ( ) => { it ( 'should sort the list with given comparator and cause re-render' , async ( ) => { const hook = getHook ( [ 1 , 2 , 3 ] ) ; const [ , [ , { sort } ] ] = hook . result . current ; expect ( hook . result . current [ 0 ] ) . toBe ( 1 ) ; act ( ( ) => { sort ( ( a , b ) => ( a === b ? 0 : a < b ? 1 : - 1 ) ) ; } ) ; await timeout ( ) ; expect ( hook . result . current [ 0 ] ) . toBe ( 2 ) ; expect ( hook . result . current [ 1 ] [ 0 ] ) . toEqual ( [ 3 , 2 , 1 ] ) ; } ) ; it ( 'should use default array`s sorting function of called without parameters' , async ( ) => { const hook = getHook ( [ 2 , 3 , 1 ] ) ; const [ , [ , { sort } ] ] = hook . result . current ; expect ( hook . result . current [ 0 ] ) . toBe ( 1 ) ; act ( ( ) => { sort ( ) ; } ) ; await timeout ( ) ; expect ( hook . result . current [ 0 ] ) . toBe ( 2 ) ; expect ( hook . result . current [ 1 ] [ 0 ] ) . toEqual ( [ 1 , 2 , 3 ] ) ; } ) ; } ) ; describe ( 'filter()' , ( ) => { it ( 'should filter the list with given predicate and cause re-render' , async ( ) => { const hook = getHook ( [ 1 , 2 , 3 ] ) ; const [ , [ , { filter } ] ] = hook . result . current ; expect ( hook . result . current [ 0 ] ) . toBe ( 1 ) ; act ( ( ) => { filter ( ( val ) => val % 2 === 1 ) ; } ) ; await timeout ( ) ; expect ( hook . result . current [ 0 ] ) . toBe ( 2 ) ; expect ( hook . result . current [ 1 ] [ 0 ] ) . toEqual ( [ 1 , 3 ] ) ; } ) ; it ( 'should pass three parameters to the predicate, iterated element, it`s index and filtered array' , ( ) => { const hook = getHook ( [ 1 , 2 , 3 ] ) ; const [ , [ list , { filter } ] ] = hook . result . current ; const spy = jest . fn ( ( _ , _2 , _3 ) => false ) ; act ( ( ) => { filter ( spy ) ; } ) ; expect ( spy . mock . calls [ 0 ] . length ) . toBe ( 3 ) ; expect ( spy . mock . calls [ 0 ] [ 0 ] ) . toBe ( 1 ) ; expect ( spy . mock . calls [ 0 ] [ 1 ] ) . toBe ( 0 ) ; expect ( spy . mock . calls [ 0 ] [ 2 ] ) . toEqual ( list ) ; } ) ; } ) ; describe ( 'removeAt()' , ( ) => { it ( 'should remove item at given index and cause re-render' , async ( ) => { const hook = getHook ( [ 1 , 2 , 3 ] ) ; const [ , [ , { removeAt } ] ] = hook . result . current ; expect ( hook . result . current [ 0 ] ) . toBe ( 1 ) ; act ( ( ) => { removeAt ( 1 ) ; } ) ; await timeout ( ) ; expect ( hook . result . current [ 0 ] ) . toBe ( 2 ) ; expect ( hook . result . current [ 1 ] [ 0 ] ) . toEqual ( [ 1 , 3 ] ) ; } ) ; it ( 'should do nothing if index is out of array length, although it should cause re-render' , async ( ) => { const hook = getHook ( [ 1 , 2 , 3 ] ) ; const [ , [ , { removeAt } ] ] = hook . result . current ; expect ( hook . result . current [ 0 ] ) . toBe ( 1 ) ; act ( ( ) => { removeAt ( 5 ) ; } ) ; await timeout ( ) ; expect ( hook . result . current [ 0 ] ) . toBe ( 2 ) ; expect ( hook . result . current [ 1 ] [ 0 ] ) . toEqual ( [ 1 , 2 , 3 ] ) ; } ) ; } ) ; describe ( 'remove()' , ( ) => { it ( 'should be a ref to removeAt' , ( ) => { const hook = getHook ( [ 1 , 2 , 3 ] ) ; const [ , [ , { remove , removeAt } ] ] = hook . result . current ; expect ( remove ) . toBe ( removeAt ) ; } ) ; } ) ; describe ( 'clear()' , ( ) => { it ( 'should clear the list and cause re-render' , async ( ) => { const hook = getHook ( [ 1 , 2 , 3 ] ) ; const [ , [ , { clear } ] ] = hook . result . current ; expect ( hook . result . current [ 0 ] ) . toBe ( 1 ) ; act ( ( ) => { clear ( ) ; } ) ; await timeout ( ) ; expect ( hook . result . current [ 0 ] ) . toBe ( 2 ) ; expect ( hook . result . current [ 1 ] [ 0 ] ) . toEqual ( [ ] ) ; } ) ; } ) ; describe ( 'reset()' , ( ) => { it ( 'should reset list to initial values and cause re-render' , async ( ) => { const hook = getHook ( [ 1 , 2 , 3 ] ) ; const [ , [ , { set , reset } ] ] = hook . result . current ; act ( ( ) => { set ( [ 1 , 2 , 3 , 4 , 6 , 7 , 8 ] ) ; } ) ; await timeout ( ) ; expect ( hook . result . current [ 1 ] [ 0 ] ) . toEqual ( [ 1 , 2 , 3 , 4 , 6 , 7 , 8 ] ) ; expect ( hook . result . current [ 0 ] ) . toBe ( 2 ) ; act ( ( ) => { reset ( ) ; } ) ; await timeout ( ) ; expect ( hook . result . current [ 0 ] ) . toBe ( 3 ) ; expect ( hook . result . current [ 1 ] [ 0 ] ) . toEqual ( [ 1 , 2 , 3 ] ) ; } ) ; } ) ; } ) ;