Skip to content

Commit

Permalink
Merge pull request #15 from szhsin/chore/unit-test-middleware
Browse files Browse the repository at this point in the history
chore: unit test middleware
  • Loading branch information
szhsin authored Dec 29, 2022
2 parents 1bc16c7 + 97b8743 commit e9fc9b4
Show file tree
Hide file tree
Showing 4 changed files with 161 additions and 0 deletions.
42 changes: 42 additions & 0 deletions src/__tests__/middleware/applyMiddleware.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import type { Middleware } from '../../common';
import { createState } from '../../';
import { applyMiddleware } from '../../middleware';

const middleware = jest.fn();
const createMiddleware: (arg: unknown) => Middleware =
(arg) =>
({ set }) =>
(...args) => {
set(...args);
middleware(arg, ...args);
};

const middlewares = [createMiddleware(1), createMiddleware(2), createMiddleware(3)];

test('applyMiddleware from left', () => {
const state = createState({
middleware: applyMiddleware(middlewares)
});

const power = state('off');
expect(middleware).toHaveBeenCalledTimes(0);
power.set('on');
expect(middleware).toHaveBeenCalledTimes(3);
expect(middleware).toHaveBeenNthCalledWith(1, 1, 'on');
expect(middleware).toHaveBeenNthCalledWith(2, 2, 'on');
expect(middleware).toHaveBeenNthCalledWith(3, 3, 'on');
});

test('applyMiddleware from right', () => {
const state = createState({
middleware: applyMiddleware(middlewares, { fromRight: true })
});

const power = state('off');
expect(middleware).toHaveBeenCalledTimes(0);
power.set('on');
expect(middleware).toHaveBeenCalledTimes(3);
expect(middleware).toHaveBeenNthCalledWith(1, 3, 'on');
expect(middleware).toHaveBeenNthCalledWith(2, 2, 'on');
expect(middleware).toHaveBeenNthCalledWith(3, 1, 'on');
});
22 changes: 22 additions & 0 deletions src/__tests__/middleware/immer.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { createState } from '../../';
import { immer } from '../../middleware';

test('immer', () => {
const state = createState({ middleware: immer });

const listener = jest.fn();
const container = state({ deep: { nested: { value: 1 } } });
container.subscribe(listener);
expect(listener).toHaveBeenCalledTimes(0);

container.set((state) => {
state.deep.nested.value = 2;
return state;
});
expect(listener).toHaveBeenCalledTimes(1);
expect(container.get()).toEqual({ deep: { nested: { value: 2 } } });

container.set({ deep: { nested: { value: 3 } } });
expect(listener).toHaveBeenCalledTimes(2);
expect(container.get()).toEqual({ deep: { nested: { value: 3 } } });
});
38 changes: 38 additions & 0 deletions src/__tests__/middleware/persist.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { createState } from '../../';
import { persist } from '../../middleware';

test('persist should load and save data to storage', () => {
const getItem = jest
.fn<string | null, string[]>()
.mockImplementation((key) => (key === 'cat-colour' ? '"Calico"' : null));
const setItem = jest.fn();
(global.localStorage as Pick<Storage, 'getItem' | 'setItem'>) = { getItem, setItem };

const middleware = persist({ prefix: 'cat-' });
const state = createState({ middleware });

const colour = state('Ginger', null, { key: 'colour' });
const age = state(1, null, { key: 'age' });
expect(colour.get()).toBe('Ginger');
expect(age.get()).toBe(1);

middleware.hydrate();
expect(colour.get()).toBe('Calico');
expect(age.get()).toBe(1);
expect(getItem).toHaveBeenCalledWith('cat-colour');
expect(getItem).toHaveBeenCalledWith('cat-age');

age.set(2);
expect(colour.get()).toBe('Calico');
expect(age.get()).toBe(2);
expect(setItem).toHaveBeenCalledWith('cat-age', '2');

expect(getItem).toHaveBeenCalledTimes(2);
expect(setItem).toHaveBeenCalledTimes(1);
});

test('persist should warn if a key is not provided in config', () => {
const state = createState({ middleware: persist() });
expect(() => state('no key')).toThrow();
state('with key', null, { key: 'some-key' });
});
59 changes: 59 additions & 0 deletions src/__tests__/middleware/reduxDevtools.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import type {} from '@redux-devtools/extension';
import { createState } from '../../';
import { reduxDevtools } from '../../middleware';

describe('reduxDevtools', () => {
const init = jest.fn();
const send = jest.fn();
const connect = jest.fn().mockReturnValue({ init, send });

beforeAll(() => {
(global.window as {
__REDUX_DEVTOOLS_EXTENSION__?: {
connect: NonNullable<Window['__REDUX_DEVTOOLS_EXTENSION__']>['connect'];
};
}) = {
__REDUX_DEVTOOLS_EXTENSION__: { connect }
};
});

afterAll(() => {
delete global.window.__REDUX_DEVTOOLS_EXTENSION__;
});

test('should send actions', () => {
const state = createState({ middleware: reduxDevtools({ name: 'my-app' }) });
const price = state(0.99, null, { key: 'price' });
const quantity = state(1, null, { key: 'qty' });

expect(connect).toHaveBeenLastCalledWith({ name: 'my-app' });
expect(init).toHaveBeenLastCalledWith({ price: 0.99, qty: 1 });

quantity.set(2);
expect(send).toHaveBeenLastCalledWith({ type: 'SET_qty', value: 2 }, { price: 0.99, qty: 2 });

quantity.set((q) => q + 1, 'increase/qty');
expect(send).toHaveBeenLastCalledWith({ type: 'increase/qty' }, { price: 0.99, qty: 3 });

quantity.set((q) => q + 2, { type: 'increaseBy/qty', by: 2 });
expect(send).toHaveBeenLastCalledWith(
{ type: 'increaseBy/qty', by: 2 },
{ price: 0.99, qty: 5 }
);

expect(price.get()).toBe(0.99);
expect(quantity.get()).toBe(5);
});

test('should warn if a key is not provided in config', () => {
const state = createState({ middleware: reduxDevtools() });
expect(() => state('no key')).toThrow();
state('with key', null, { key: 'some-key' });
});
});

test('reduxDevtools should return original set when there is no redux devtools extension', () => {
const set = jest.fn();
// eslint-disable-next-line
expect(reduxDevtools()({ set } as any)).toBe(set);
});

0 comments on commit e9fc9b4

Please sign in to comment.