Skip to content

Commit

Permalink
Add withStrict test util
Browse files Browse the repository at this point in the history
  • Loading branch information
albertogasparin committed Aug 17, 2024
1 parent c2de77f commit bbd9b87
Show file tree
Hide file tree
Showing 4 changed files with 31 additions and 45 deletions.
28 changes: 13 additions & 15 deletions src/__tests__/integration.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import React, { Fragment, memo, useEffect } from 'react';
import { render, act, configure } from '@testing-library/react';

import { withStrict } from './utils';
import { createStore, defaultRegistry } from '../store';
import { createContainer } from '../components/container';
import { createSubscriber } from '../components/subscriber';
Expand Down Expand Up @@ -49,7 +50,6 @@ describe('Integration', () => {
});

it('should get closer storeState with scope id if matching', () => {
configure({ reactStrictMode: false });
const Container = createContainer(Store);
const Subscriber = createSubscriber(Store);
const children1 = jest.fn((s, a) => {
Expand Down Expand Up @@ -81,15 +81,14 @@ describe('Integration', () => {
expect.any(Object)
);

expect(children2).toHaveBeenCalledTimes(1);
expect(children2).toHaveBeenCalledTimes(withStrict(1));
expect(children2).toHaveBeenCalledWith(
{ todos: [], loading: false },
expectActions
);
});

it('should share scoped state across multiple subscribers', async () => {
configure({ reactStrictMode: false });
const Container = createContainer(Store, {
onInit:
() =>
Expand Down Expand Up @@ -118,7 +117,7 @@ describe('Integration', () => {
{ loading: true, todos: [] },
expectActions,
]);
expect(children1.mock.calls[1]).toEqual([
expect(children1.mock.calls[withStrict(1)]).toEqual([
{ loading: false, todos: ['todo'] },
expectActions,
]);
Expand All @@ -127,14 +126,13 @@ describe('Integration', () => {
{ loading: true, todos: [] },
expectActions,
]);
expect(children2.mock.calls[1]).toEqual([
expect(children2.mock.calls[withStrict(1)]).toEqual([
{ loading: false, todos: ['todo'] },
expectActions,
]);
});

it('should update all subscribers on scope change', async () => {
configure({ reactStrictMode: false });
const Container = createContainer(Store, {
onInit:
() =>
Expand Down Expand Up @@ -168,28 +166,29 @@ describe('Integration', () => {

// 1. { loading: true, todos: [] };
// 2. { loading: false, todos: ['todo1'] };
expect(children1).toHaveBeenCalledTimes(2);
expect(children2).toHaveBeenCalledTimes(2);
expect(children1).toHaveBeenCalledTimes(withStrict(2));
expect(children2).toHaveBeenCalledTimes(withStrict(2));

rerender(<App scopeId="B" />);

await actTick();

const state2 = { loading: true, todos: [] };
const call2 = 2;
const call2 = withStrict(2);
expect(children1.mock.calls[call2]).toEqual([state2, expectActions]);
expect(children2.mock.calls[call2]).toEqual([state2, expectActions]);

await actTick();

const state3 = { loading: false, todos: ['todoB'] };
const call3 = 3;
const call3 = withStrict(3);
expect(children1.mock.calls[call3]).toEqual([state3, expectActions]);
expect(children2.mock.calls[call3]).toEqual([state3, expectActions]);
});

it('should call the listeners in the correct register order', async () => {
configure({ reactStrictMode: false });

const Container = createContainer(Store, {});
const Subscriber = createSubscriber(Store);
const useHook = createHook(Store);
Expand Down Expand Up @@ -253,6 +252,7 @@ describe('Integration', () => {

it('should call the listeners in the correct register order after scope change', async () => {
configure({ reactStrictMode: false });

const Container = createContainer(Store, {});
const Subscriber = createSubscriber(Store);
const useHook = createHook(Store);
Expand Down Expand Up @@ -326,6 +326,7 @@ describe('Integration', () => {

it('should not re-render components if selector returns same value', async () => {
configure({ reactStrictMode: false });

const opts = { selector: (s) => ({ l: s.loading }) };
const Subscriber = createSubscriber(Store, opts);
const useHook = createHook(Store, opts);
Expand Down Expand Up @@ -366,7 +367,6 @@ describe('Integration', () => {
});

it('should not render-loop if state/action returns shallow equal value', async () => {
configure({ reactStrictMode: false });
const useHook = createHook(Store);
const calls = [];

Expand All @@ -381,11 +381,10 @@ describe('Integration', () => {

render(<HookWrapper />);

expect(calls).toHaveLength(2);
expect(calls).toHaveLength(withStrict(2));
});

it('should not re-compute selector if no arguments are passed', async () => {
configure({ reactStrictMode: false });
const selector = jest.fn((s) => s);
const useHook = createHook(Store, { selector });

Expand All @@ -400,7 +399,6 @@ describe('Integration', () => {
});

it('should re-compute selector if arguments are passed', async () => {
configure({ reactStrictMode: false });
const selector = jest.fn((s) => s);
const useHook = createHook(Store, { selector });

Expand All @@ -411,7 +409,7 @@ describe('Integration', () => {
};
render(<HookWrapper />);

expect(selector).toHaveBeenCalledTimes(2);
expect(selector).toHaveBeenCalledTimes(withStrict(2));
});

it('should capture all contained stores', async () => {
Expand Down
6 changes: 6 additions & 0 deletions src/__tests__/utils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { getConfig } from '@testing-library/react';

export function withStrict(n) {
const { reactStrictMode } = getConfig();
return reactStrictMode ? n * 2 : n;
}
9 changes: 3 additions & 6 deletions src/components/__tests__/container.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import React from 'react';
import { render, act, configure } from '@testing-library/react';

import { StoreMock } from '../../__tests__/mocks';
import { withStrict } from '../../__tests__/utils';
import { defaultRegistry } from '../../store/registry';
import { createStore } from '../../store';
import { createContainer } from '../container';
Expand Down Expand Up @@ -213,8 +214,6 @@ describe('Container', () => {
});

it('should call Container onInit for every first render if override', () => {
configure({ reactStrictMode: false });

const Subscriber = createSubscriber(Store);
const renderPropChildren = jest.fn().mockReturnValue(null);
const children = <Subscriber>{renderPropChildren}</Subscriber>;
Expand All @@ -225,7 +224,7 @@ describe('Container', () => {
</>
);

expect(mockOnContainerInitInner).toHaveBeenCalledTimes(2);
expect(mockOnContainerInitInner).toHaveBeenCalledTimes(withStrict(2));
});

it('should call Container onInit only on first render if global and containedBy', () => {
Expand All @@ -252,8 +251,6 @@ describe('Container', () => {
});

it('should call Container onInit when global and override even with no subscriber children', () => {
configure({ reactStrictMode: false });

const Subscriber = createSubscriber(Store);
mockOnContainerInitInner.mockImplementationOnce(({ setState }) =>
setState({ count: 1 })
Expand All @@ -266,7 +263,7 @@ describe('Container', () => {
</>
);

expect(mockOnContainerInitInner).toHaveBeenCalledTimes(1);
expect(mockOnContainerInitInner).toHaveBeenCalledTimes(withStrict(1));
expect(renderPropChildren).toHaveBeenCalledWith(
{ count: 1 },
expect.any(Object)
Expand Down
33 changes: 9 additions & 24 deletions src/components/__tests__/hook.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import React from 'react';
import { render, act, configure } from '@testing-library/react';

import { StoreMock, storeStateMock } from '../../__tests__/mocks';
import { withStrict } from '../../__tests__/utils';
import { createHook, createActionsHook, createStateHook } from '../hook';
import { defaultRegistry } from '../../store/registry';

Expand Down Expand Up @@ -65,17 +66,13 @@ describe('Hook', () => {
});

it('should render children with store data and actions', () => {
configure({ reactStrictMode: false });

const { getRender, children } = setup();
getRender();
expect(children).toHaveBeenCalledTimes(1);
expect(children).toHaveBeenCalledTimes(withStrict(1));
expect(children).toHaveBeenCalledWith([{ count: 0 }, actions]);
});

it('should update when store calls update listener', () => {
configure({ reactStrictMode: false });

const { getRender, children } = setup();
storeStateMock.getState.mockReturnValue({ count: 1 });
getRender();
Expand All @@ -86,14 +83,12 @@ describe('Hook', () => {
const update = storeStateMock.subscribe.mock.calls[0][0];
act(() => update(newState));

expect(children).toHaveBeenCalledTimes(2);
expect(children).toHaveBeenCalledTimes(withStrict(2));
expect(children).toHaveBeenCalledWith([{ count: 1 }, actions]);
expect(children).toHaveBeenLastCalledWith([{ count: 2 }, actions]);
});

it('should avoid re-render children when just rendered from parent update', () => {
configure({ reactStrictMode: false });

const { getElement, children } = setup();
const App = () => getElement();

Expand All @@ -106,7 +101,7 @@ describe('Hook', () => {
act(() => update(newState));

expect(storeStateMock.getState).toHaveBeenCalled();
expect(children).toHaveBeenCalledTimes(2);
expect(children).toHaveBeenCalledTimes(withStrict(2));
expect(children).toHaveBeenCalledWith([newState, actions]);
});

Expand Down Expand Up @@ -177,8 +172,6 @@ describe('Hook', () => {
});

it('should update on state change if selector output is not shallow equal', () => {
configure({ reactStrictMode: false });

const selector = jest.fn().mockImplementation(() => ({ foo: [1] }));
const { getRender, children } = setup({ selector });
getRender();
Expand All @@ -187,7 +180,7 @@ describe('Hook', () => {
const update = storeStateMock.subscribe.mock.calls[0][0];
act(() => update(newState));

expect(children).toHaveBeenCalledTimes(2);
expect(children).toHaveBeenCalledTimes(withStrict(2));
});

it('should not update on state change if selector output is shallow equal', () => {
Expand All @@ -207,20 +200,16 @@ describe('Hook', () => {
});

it('should not recompute selector if state & props are equal', () => {
configure({ reactStrictMode: false });

const selector = jest.fn().mockReturnValue({ foo: 1 });
const { getRender, getElement } = setup({ props: { bar: 1 }, selector });
const { rerender } = getRender();
rerender(getElement({ bar: 1 }));

// ensure memoisation works
expect(selector).toHaveBeenCalledTimes(1);
expect(selector).toHaveBeenCalledTimes(withStrict(1));
});

it('should not update on state change if selector is null', () => {
configure({ reactStrictMode: false });

const selector = null;
const { getRender, children } = setup({ selector });
getRender();
Expand All @@ -230,7 +219,7 @@ describe('Hook', () => {
const update = storeStateMock.subscribe.mock.calls[0][0];
act(() => update(storeStateMock.getState(), storeStateMock));

expect(children).toHaveBeenCalledTimes(1);
expect(children).toHaveBeenCalledTimes(withStrict(1));
expect(children).toHaveBeenCalledWith([undefined, actions]);
});

Expand All @@ -253,22 +242,18 @@ describe('Hook', () => {

describe('createActionsHook', () => {
it('should render children with just actions', () => {
configure({ reactStrictMode: false });

const { getRender, children } = setup({ creator: createActionsHook });
getRender();
expect(children).toHaveBeenCalledTimes(1);
expect(children).toHaveBeenCalledTimes(withStrict(1));
expect(children).toHaveBeenCalledWith(actions);
});
});

describe('createStateHook', () => {
it('should render children with just store data', () => {
configure({ reactStrictMode: false });

const { getRender, children } = setup({ creator: createStateHook });
getRender();
expect(children).toHaveBeenCalledTimes(1);
expect(children).toHaveBeenCalledTimes(withStrict(1));
expect(children).toHaveBeenCalledWith({ count: 0 });
});
});
Expand Down

0 comments on commit bbd9b87

Please sign in to comment.