react-use/tests/useStateList.test.ts

251 lines
6.8 KiB
TypeScript

import { act, renderHook } from '@testing-library/react-hooks';
import useStateList from '../src/useStateList';
describe('useStateList', () => {
it('should be defined', () => {
expect(useStateList).toBeDefined();
});
function getHook(list: any[] = ['a', 'b', 'c']) {
return renderHook(({ states }) => useStateList(states), { initialProps: { states: list } });
}
it('should return an object containing `state`, `next` and `prev`', () => {
const res = getHook().result.current;
expect(res).toStrictEqual({
state: expect.any(String),
currentIndex: expect.any(Number),
prev: expect.any(Function),
next: expect.any(Function),
setStateAt: expect.any(Function),
setState: expect.any(Function),
isFirst: expect.any(Boolean),
isLast: expect.any(Boolean),
});
});
it('should return the first state on init', () => {
expect(getHook().result.current.state).toBe('a');
});
it('should return isFirst on init', () => {
expect(getHook().result.current.isFirst).toBe(true);
});
it('should return isLast when on last state', () => {
const hook = getHook();
act(() => hook.result.current.setStateAt(2));
expect(hook.result.current.isLast).toBe(true);
});
describe('setState()', () => {
it('should set state value if it exists in states list', () => {
const hook = getHook();
expect(hook.result.current.state).toBe('a');
act(() => hook.result.current.setState('c'));
expect(hook.result.current.state).toBe('c');
});
it('should throw if required state not exists', () => {
const hook = getHook();
expect(hook.result.current.state).toBe('a');
expect(() => hook.result.current.setState('d')).toThrow(
`State 'd' is not a valid state (does not exist in state list)`
);
});
it('should do nothing on unmounted component', () => {
const hook = getHook();
expect(hook.result.current.state).toBe('a');
hook.unmount();
expect(() => hook.result.current.setState('c')).not.toThrow(Error);
expect(hook.result.current.state).toBe('a');
});
});
describe('setStateAt()', () => {
it('should set state by it`s index in states list', () => {
const hook = getHook();
expect(hook.result.current.state).toBe('a');
act(() => hook.result.current.setStateAt(2));
expect(hook.result.current.state).toBe('c');
act(() => hook.result.current.setStateAt(1));
expect(hook.result.current.state).toBe('b');
});
it('should cyclically travel through the right border', () => {
const hook = getHook();
expect(hook.result.current.state).toBe('a');
act(() => hook.result.current.setStateAt(5));
expect(hook.result.current.state).toBe('c');
act(() => hook.result.current.setStateAt(9));
expect(hook.result.current.state).toBe('a');
act(() => hook.result.current.setStateAt(10));
expect(hook.result.current.state).toBe('b');
});
it('should cyclically travel through the left border', () => {
const hook = getHook();
expect(hook.result.current.state).toBe('a');
act(() => hook.result.current.setStateAt(-1));
expect(hook.result.current.state).toBe('c');
act(() => hook.result.current.setStateAt(-2));
expect(hook.result.current.state).toBe('b');
act(() => hook.result.current.setStateAt(-17));
expect(hook.result.current.state).toBe('b');
});
});
describe('next()', () => {
it('should switch states forward and cause re-render', () => {
const hook = getHook();
expect(hook.result.current.state).toBe('a');
act(() => {
hook.result.current.next();
});
expect(hook.result.current.state).toBe('b');
act(() => {
hook.result.current.next();
});
expect(hook.result.current.state).toBe('c');
});
it('on overflow should switch to first element (should be cycled)', () => {
const hook = getHook();
expect(hook.result.current.state).toBe('a');
act(() => {
hook.result.current.next();
hook.result.current.next();
hook.result.current.next();
});
expect(hook.result.current.state).toBe('a');
});
});
describe('prev()', () => {
it('on overflow should switch to last element (should be cycled)', () => {
const hook = getHook();
expect(hook.result.current.state).toBe('a');
act(() => {
hook.result.current.prev();
});
expect(hook.result.current.state).toBe('c');
});
it('should switch states backward and cause re-render', () => {
const hook = getHook();
expect(hook.result.current.state).toBe('a');
act(() => {
hook.result.current.prev();
});
expect(hook.result.current.state).toBe('c');
act(() => {
hook.result.current.prev();
});
expect(hook.result.current.state).toBe('b');
act(() => {
hook.result.current.prev();
});
expect(hook.result.current.state).toBe('a');
});
});
describe('with empty states list', () => {
it('should have `undefined` state', () => {
expect(getHook([]).result.current.state).toBe(undefined);
});
it('should do nothing on next() call', () => {
const hook = getHook([]);
act(() => {
hook.result.current.next();
});
expect(hook.result.current.state).toBe(undefined);
});
it('should do nothing on prev() call', () => {
const hook = getHook([]);
act(() => {
hook.result.current.prev();
});
expect(hook.result.current.state).toBe(undefined);
});
});
describe('on state list shrink', () => {
it('should set last element as state if index was beyond new last element', () => {
const hook = getHook();
act(() => {
hook.result.current.prev();
});
expect(hook.result.current.state).toBe('c');
hook.rerender({ states: ['a', 'b'] });
expect(hook.result.current.state).toBe('b');
});
it('should so nothing if current index within new range', () => {
const hook = getHook();
act(() => {
hook.result.current.prev();
});
expect(hook.result.current.state).toBe('c');
hook.rerender({ states: ['a', 'b', 'c', 'd'] });
expect(hook.result.current.state).toBe('c');
});
});
describe('ou unmounted component', () => {
it('next() should not do anything', () => {
const hook = getHook();
const { next } = hook.result.current;
hook.unmount();
act(() => next());
expect(hook.result.current.state).toBe('a');
});
it('prev() should not do anything', () => {
const hook = getHook();
const { prev } = hook.result.current;
hook.unmount();
act(() => prev());
expect(hook.result.current.state).toBe('a');
});
});
});