Skip to content

Commit

Permalink
test: add test for state hooks
Browse files Browse the repository at this point in the history
  • Loading branch information
streamich authored Aug 2, 2019
2 parents a53c8c5 + 716a7d4 commit 4a87d63
Show file tree
Hide file tree
Showing 25 changed files with 1,123 additions and 308 deletions.
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@
"@storybook/addon-notes": "5.1.10",
"@storybook/addon-options": "5.1.10",
"@storybook/react": "5.1.10",
"@testing-library/react-hooks": "^1.1.0",
"@types/jest": "24.0.16",
"@types/react": "16.8.23",
"babel-core": "6.26.3",
Expand All @@ -87,8 +88,8 @@
"prettier": "1.17.1",
"react": "16.8.6",
"react-dom": "16.8.6",
"react-hooks-testing-library": "0.4.1",
"react-spring": "8.0.27",
"react-test-renderer": "^16.8.6",
"rebound": "0.1.0",
"redux-logger": "3.0.6",
"redux-thunk": "2.3.0",
Expand Down
45 changes: 45 additions & 0 deletions src/__tests__/createMemo.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { renderHook } from '@testing-library/react-hooks';
import createMemo from '../createMemo';

const getDouble = jest.fn((n: number): number => n * 2);

it('should init memo hook', () => {
const useMemoGetDouble = createMemo(getDouble);

expect(useMemoGetDouble).toBeInstanceOf(Function);
});

describe('when using created memo hook', () => {
let useMemoGetDouble;

beforeEach(() => {
useMemoGetDouble = createMemo(getDouble);
});

it.each([[1], [3], [5]])('should return same result as original function for argument %d', (val: number) => {
const { result } = renderHook(() => useMemoGetDouble(val));
expect(result.current).toBe(getDouble(val));
});

it('should NOT call original function for same arguments', () => {
let initialValue = 5;
expect(getDouble).not.toHaveBeenCalled();

// it's called first time calculating for argument 5
const { rerender } = renderHook(() => useMemoGetDouble(initialValue));
expect(getDouble).toHaveBeenCalled();

getDouble.mockClear();

// it's NOT called second time calculating for argument 5
rerender();
expect(getDouble).not.toHaveBeenCalled();

getDouble.mockClear();

// it's called again calculating for different argument
initialValue = 7;
rerender();
expect(getDouble).toHaveBeenCalled();
});
});
112 changes: 112 additions & 0 deletions src/__tests__/createReducer.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import { act, renderHook } from '@testing-library/react-hooks';
import createReducer from '../createReducer';
import logger from 'redux-logger';
import thunk from 'redux-thunk';

it('should init reducer hook function', () => {
const useSomeReducer = createReducer();
expect(useSomeReducer).toBeInstanceOf(Function);
});

/**
* This test suite implements the special demo in storybook that creates a
* reducer with thunk and logger for using a simple counter
*/
describe('when using created reducer hook', () => {
const initialCount = 1;
let originalLog;
let originalGroup;
const mockLog = jest.fn();
const mockGroup = jest.fn();

function reducer(state, action) {
switch (action.type) {
case 'increment':
return { count: state.count + 1 };
case 'decrement':
return { count: state.count - 1 };
case 'reset':
return { count: action.payload };
default:
throw new Error();
}
}

// Action creator to increment count, wait a second and then reset
const addAndReset = () => {
return dispatch => {
dispatch({ type: 'increment' });

setTimeout(() => {
dispatch({ type: 'reset', payload: initialCount });
}, 1000);
};
};

const setUp = () => {
const useThunkReducer = createReducer(thunk, logger);
return renderHook(() => useThunkReducer(reducer, { count: initialCount }));
};

beforeAll(() => {
originalLog = console.log;
originalGroup = console.group;
console.log = mockLog;
console.group = mockGroup;
});

beforeEach(() => {
jest.useFakeTimers();
});

afterAll(() => {
console.log = originalLog;
console.group = originalGroup;
});

it('should init state and dispatcher', () => {
const { result } = setUp();
const [state, dispatch] = result.current;

expect(state).toEqual({ count: 1 });
expect(dispatch).toBeInstanceOf(Function);
});

it.each`
actionType | expectedCount | payload
${'increment'} | ${2} | ${undefined}
${'decrement'} | ${0} | ${undefined}
${'reset'} | ${1} | ${1}
`('should handle "$actionType" action', ({ actionType, expectedCount, payload }) => {
const { result } = setUp();
const [, dispatch] = result.current;
expect(mockLog).not.toHaveBeenCalled();

act(() => {
dispatch({ type: actionType, payload });
});

expect(result.current[0]).toEqual({ count: expectedCount });
expect(mockLog).toHaveBeenCalled();
});

it('should handle async action with several middlewares', () => {
const { result } = setUp();
const [, dispatch] = result.current;
expect(mockLog).not.toHaveBeenCalled();

act(() => {
dispatch(addAndReset());
});

expect(result.current[0]).toEqual({ count: 2 });
expect(mockLog).toHaveBeenCalled();

// fast-forward until all timers have been executed
act(() => {
jest.runAllTimers();
});

expect(result.current[0]).toEqual({ count: 1 });
});
});
8 changes: 3 additions & 5 deletions src/__tests__/useAsync.test.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
import { renderHook } from '@testing-library/react-hooks';
import { useCallback } from 'react';
import { cleanup, renderHook } from 'react-hooks-testing-library';
import useAsync from '../useAsync';

afterEach(cleanup);

// NOTE: these tests cause console errors.
// maybe we should test in a real environment instead
// of a fake one?
Expand All @@ -17,7 +15,7 @@ describe('useAsync', () => {
let callCount = 0;

const resolver = async () => {
return new Promise((resolve, reject) => {
return new Promise(resolve => {
callCount++;

const wait = setTimeout(() => {
Expand Down Expand Up @@ -58,7 +56,7 @@ describe('useAsync', () => {
let callCount = 0;

const rejection = async () => {
return new Promise((resolve, reject) => {
return new Promise((_, reject) => {
callCount++;

const wait = setTimeout(() => {
Expand Down
4 changes: 1 addition & 3 deletions src/__tests__/useAsyncFn.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,9 @@
// does not automatically invoke the function
// and it can take arguments.

import { cleanup, renderHook } from 'react-hooks-testing-library';
import { renderHook } from '@testing-library/react-hooks';
import useAsyncFn, { AsyncState } from '../useAsyncFn';

afterEach(cleanup);

type AdderFn = (a: number, b: number) => Promise<number>;

describe('useAsyncFn', () => {
Expand Down
6 changes: 6 additions & 0 deletions src/__tests__/useBoolean.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import useBoolean from '../useBoolean';
import useToggle from '../useToggle';

it('should be an alias for useToggle ', () => {
expect(useBoolean).toBe(useToggle);
});
128 changes: 128 additions & 0 deletions src/__tests__/useCounter.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
import { act, renderHook } from '@testing-library/react-hooks';
import useCounter from '../useCounter';

const setUp = (initialValue?: number) => renderHook(() => useCounter(initialValue));

it('should init counter and utils', () => {
const { result } = setUp(5);

expect(result.current[0]).toBe(5);
expect(result.current[1]).toStrictEqual({
inc: expect.any(Function),
dec: expect.any(Function),
get: expect.any(Function),
set: expect.any(Function),
reset: expect.any(Function),
});
});

it('should init counter to 0 if not initial value received', () => {
const { result } = setUp();

expect(result.current[0]).toBe(0);
});

it('should init counter to negative number', () => {
const { result } = setUp(-2);

expect(result.current[0]).toBe(-2);
});

it('should get current counter', () => {
const { result } = setUp(5);
const { get } = result.current[1];

expect(get()).toBe(5);
});

it('should increment by 1 if not value received', () => {
const { result } = setUp(5);
const { get, inc } = result.current[1];

act(() => inc());

expect(result.current[0]).toBe(6);
expect(get()).toBe(6);
});

it('should increment by value received', () => {
const { result } = setUp(5);
const { get, inc } = result.current[1];

act(() => inc(9));

expect(result.current[0]).toBe(14);
expect(get()).toBe(14);
});

it('should decrement by 1 if not value received', () => {
const { result } = setUp(5);
const { get, dec } = result.current[1];

act(() => dec());

expect(result.current[0]).toBe(4);
expect(get()).toBe(4);
});

it('should decrement by value received', () => {
const { result } = setUp(5);
const { get, dec } = result.current[1];

act(() => dec(9));

expect(result.current[0]).toBe(-4);
expect(get()).toBe(-4);
});

it('should set to value received', () => {
const { result } = setUp(5);
const { get, set } = result.current[1];

act(() => set(17));

expect(result.current[0]).toBe(17);
expect(get()).toBe(17);
});

it('should reset to original value', () => {
const { result } = setUp(5);
const { get, set, reset } = result.current[1];

// set different value than initial one...
act(() => set(17));
expect(result.current[0]).toBe(17);

// ... and reset it to initial one
act(() => reset());
expect(result.current[0]).toBe(5);
expect(get()).toBe(5);
});

it('should reset and set new original value', () => {
const { result } = setUp(5);
const { get, set, reset } = result.current[1];

// set different value than initial one...
act(() => set(17));
expect(result.current[0]).toBe(17);

// ... now reset and set it to different than initial one...
act(() => reset(8));
expect(result.current[0]).toBe(8);

// ... and set different value than initial one again...
act(() => set(32));
expect(result.current[0]).toBe(32);

// ... and reset it to new initial value
act(() => reset());
expect(result.current[0]).toBe(8);
expect(get()).toBe(8);
});

it.todo('should log an error if initial value is other than a number');
it.todo('should log an error if increment value is other than a number');
it.todo('should log an error if increment value is a negative number');
it.todo('should log an error if decrement value is other than a number');
it.todo('should log an error if decrement value is a negative number');
Loading

0 comments on commit 4a87d63

Please sign in to comment.