(globalStateManager, name, reducer),
27 | );
28 | }
29 |
30 | // Return a function that will remove these reducers.
31 | return (): boolean => {
32 | let success = true;
33 | for (const removeReducer of removeReducers) {
34 | success = success && removeReducer();
35 | }
36 | return success;
37 | };
38 | };
39 |
--------------------------------------------------------------------------------
/src/components.ts:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import { Reducers, State } from '../default';
3 | import Callback from '../types/callback';
4 | import { DispatcherMap } from '../types/dispatchers';
5 | import NewGlobalState from '../types/new-global-state';
6 | import {
7 | ReactNDispatch,
8 | ReactNGlobal,
9 | ReactNGlobalCallback,
10 | ReactNSetGlobal,
11 | } from './methods';
12 | import bindLifecycleMethods from './utils/bind-lifecycle-methods';
13 |
14 |
15 |
16 | // TODO -- https://github.com/CharlesStover/reactn/issues/14
17 | const isComponentDidMount = false;
18 | const isComponentDidUpdate = false;
19 | const isSetGlobalCallback = false;
20 |
21 | const ReactPureComponent = React.PureComponent || React.Component;
22 |
23 |
24 |
25 | export class ReactNComponent<
26 | P extends {} = {},
27 | S extends {} = {},
28 | G extends {} = State,
29 | R extends {} = Reducers,
30 | SS = any,
31 | > extends React.Component {
32 |
33 | public constructor(props: Readonly
, context?: any) {
34 | super(props, context);
35 | this._globalCallback = this._globalCallback.bind(this);
36 | bindLifecycleMethods(this);
37 | }
38 |
39 | public get dispatch(): Readonly> {
40 | return ReactNDispatch();
41 | }
42 |
43 | public get global(): Readonly {
44 | return ReactNGlobal(this);
45 | }
46 |
47 | public setGlobal(
48 | newGlobalState: NewGlobalState,
49 | callback: Callback | null = null
50 | ): Promise> {
51 | return ReactNSetGlobal(
52 | newGlobalState, callback,
53 | !isComponentDidMount &&
54 | !isComponentDidUpdate &&
55 | !isSetGlobalCallback,
56 | );
57 | }
58 |
59 | public _globalCallback(): void {
60 | return ReactNGlobalCallback(this);
61 | }
62 | };
63 |
64 | export class ReactNPureComponent<
65 | P extends {} = {},
66 | S extends {} = {},
67 | G extends {} = State,
68 | R extends {} = Reducers,
69 | SS = any,
70 | > extends ReactPureComponent {
71 |
72 | public constructor(props: Readonly
, context?: any) {
73 | super(props, context);
74 | this._globalCallback = this._globalCallback.bind(this);
75 | bindLifecycleMethods(this);
76 | }
77 |
78 | public get dispatch(): Readonly> {
79 | return ReactNDispatch();
80 | }
81 |
82 | public get global(): Readonly {
83 | return ReactNGlobal(this);
84 | }
85 |
86 | public setGlobal(
87 | newGlobalState: NewGlobalState,
88 | callback: Callback | null = null
89 | ): Promise> {
90 | return ReactNSetGlobal(
91 | newGlobalState, callback,
92 | !isComponentDidMount &&
93 | !isComponentDidUpdate &&
94 | !isSetGlobalCallback,
95 | );
96 | }
97 |
98 | public _globalCallback(): void {
99 | return ReactNGlobalCallback(this);
100 | }
101 | };
102 |
--------------------------------------------------------------------------------
/src/context.ts:
--------------------------------------------------------------------------------
1 | import { Context, createContext } from 'react';
2 | import defaultGlobalStateManager from './default-global-state-manager';
3 | import GlobalStateManager from './global-state-manager';
4 |
5 | type AnyGSM = GlobalStateManager;
6 |
7 | interface TrueContext extends Context {
8 | _currentValue: T;
9 | _currentValue2: T;
10 | }
11 |
12 | const getContext = (): TrueContext => {
13 | if (typeof createContext === 'function') {
14 | return createContext(defaultGlobalStateManager) as TrueContext;
15 | }
16 | return null;
17 | };
18 |
19 | export default getContext();
20 |
--------------------------------------------------------------------------------
/src/default-global-state-manager.ts:
--------------------------------------------------------------------------------
1 | import GlobalStateManager from './global-state-manager';
2 |
3 | type RSA = Record;
4 |
5 | export default new GlobalStateManager();
6 |
--------------------------------------------------------------------------------
/src/get-dispatch.ts:
--------------------------------------------------------------------------------
1 | import { Reducers, State } from '../default';
2 | import Dispatchers from '../types/dispatchers';
3 | import GlobalStateManager from './global-state-manager';
4 |
5 |
6 |
7 | export default function _getDispatch<
8 | G extends {} = State,
9 | R extends {} = Reducers,
10 | >(
11 | globalStateManager: GlobalStateManager,
12 | ): Dispatchers {
13 | return globalStateManager.dispatchers;
14 | };
15 |
--------------------------------------------------------------------------------
/src/get-global.ts:
--------------------------------------------------------------------------------
1 | import { State } from '../default';
2 | import GlobalStateManager from './global-state-manager';
3 |
4 |
5 |
6 | export default function _getGlobal(
7 | globalStateManager: GlobalStateManager,
8 | ): G {
9 | return globalStateManager.state;
10 | };
11 |
--------------------------------------------------------------------------------
/src/remove-callback.ts:
--------------------------------------------------------------------------------
1 | import { State } from '../default';
2 | import Callback from '../types/callback';
3 | import GlobalStateManager from './global-state-manager';
4 |
5 |
6 |
7 | export default function _removeCallback(
8 | globalStateManager: GlobalStateManager,
9 | callback: Callback,
10 | ): boolean {
11 | return globalStateManager.removeCallback(callback);
12 | };
13 |
--------------------------------------------------------------------------------
/src/reset-global.ts:
--------------------------------------------------------------------------------
1 | import GlobalStateManager from './global-state-manager';
2 |
3 |
4 |
5 | export default function _resetGlobal(
6 | globalStateManager: GlobalStateManager,
7 | ): void {
8 | return globalStateManager.reset();
9 | };
10 |
--------------------------------------------------------------------------------
/src/set-global.ts:
--------------------------------------------------------------------------------
1 | import { Reducers, State } from '../default';
2 | import Callback from '../types/callback';
3 | import NewGlobalState from '../types/new-global-state';
4 | import GlobalStateManager from './global-state-manager';
5 |
6 |
7 |
8 | export default function _setGlobal<
9 | G extends {} = State,
10 | R extends {} = Reducers,
11 | >(
12 | globalStateManager: GlobalStateManager,
13 | newGlobalState: NewGlobalState,
14 | callback: Callback = null,
15 | ): Promise {
16 |
17 | // If there is no callback, update then return the state.
18 | if (callback === null) {
19 | return globalStateManager.set(newGlobalState)
20 | .then((): G =>
21 | globalStateManager.state,
22 | );
23 | }
24 |
25 | // If there is a callback, update the state, update the state with the
26 | // callback, then return the state.
27 | return globalStateManager.set(newGlobalState)
28 | .then((stateChange: Partial): Promise =>
29 | _setGlobal(
30 | globalStateManager,
31 | callback(
32 | globalStateManager.state,
33 | globalStateManager.dispatcherMap,
34 | stateChange,
35 | ),
36 | )
37 | )
38 | .then((): G =>
39 | globalStateManager.state,
40 | );
41 | };
42 |
--------------------------------------------------------------------------------
/src/use-global.ts:
--------------------------------------------------------------------------------
1 | import { useCallback, useContext, useEffect } from 'react';
2 | import useForceUpdate from 'use-force-update';
3 | import { State } from '../default';
4 | import Callback from '../types/callback';
5 | import NewGlobalState from '../types/new-global-state';
6 | import UseGlobal, { GlobalTuple, StateTuple } from '../types/use-global';
7 | import Context from './context';
8 | import defaultGlobalStateManager from './default-global-state-manager';
9 | import GlobalStateManager from './global-state-manager';
10 | import setGlobal from './set-global';
11 | import REACT_HOOKS_ERROR from './utils/react-hooks-error';
12 |
13 | type VoidFunction = () => void;
14 |
15 | // useGlobal()
16 | export default function _useGlobal(
17 | overrideGlobalStateManager: GlobalStateManager | null,
18 | ): GlobalTuple;
19 |
20 | // useGlobal('property')
21 | export default function _useGlobal<
22 | G extends {} = State,
23 | Property extends keyof G = keyof G
24 | >(
25 | overrideGlobalStateManager: GlobalStateManager | null,
26 | property: Property,
27 | ): StateTuple;
28 |
29 | // Implementation
30 | export default function _useGlobal<
31 | G extends {} = State,
32 | Property extends keyof G = keyof G
33 | >(
34 | overrideGlobalStateManager: GlobalStateManager | null,
35 | property?: Property,
36 | ): UseGlobal {
37 | // Require hooks.
38 | if (!useContext) {
39 | throw REACT_HOOKS_ERROR;
40 | }
41 |
42 | const globalStateManager: GlobalStateManager =
43 | overrideGlobalStateManager ||
44 | (useContext(Context) as GlobalStateManager) ||
45 | (defaultGlobalStateManager as GlobalStateManager);
46 |
47 | const forceUpdate = useForceUpdate();
48 | const removeForceUpdateListener = (): void => {
49 | globalStateManager.removePropertyListener(forceUpdate);
50 | };
51 |
52 | // Return the entire global state.
53 | if (typeof property === 'undefined') {
54 | // If this component ever updates or unmounts, remove the force update
55 | // listener.
56 | useEffect((): VoidFunction => removeForceUpdateListener, []);
57 |
58 | const globalStateSetter = useCallback(
59 | (
60 | newGlobalState: NewGlobalState,
61 | callback: Callback | null = null,
62 | ): Promise => setGlobal(globalStateManager, newGlobalState, callback),
63 | [],
64 | );
65 |
66 | return [globalStateManager.spyState(forceUpdate), globalStateSetter];
67 | }
68 |
69 | useEffect(
70 | (): VoidFunction => {
71 | // We add the listener as an effect, so that there are not race conditions
72 | // between subscribing and unsubscribing.
73 | // Subscribing outside of useEffect via `spyState()[property]` will
74 | // cause the re-render subscription to occur before the unmount
75 | // unsubscription occurs. As a result, the unmount unsubscription
76 | // removes the re-rendered subscription.
77 | globalStateManager.addPropertyListener(property, forceUpdate);
78 |
79 | // If this component ever updates or unmounts, remove the force update
80 | // listener.
81 | return removeForceUpdateListener;
82 | },
83 | );
84 |
85 | const globalPropertySetter = useCallback(
86 | (value: G[Property], callback: Callback | null = null): Promise => {
87 | const newGlobalState: Partial = Object.create(null);
88 | newGlobalState[property] = value;
89 | return setGlobal(globalStateManager, newGlobalState, callback);
90 | },
91 | [],
92 | );
93 |
94 | // Return both getter and setter.
95 | return [globalStateManager.state[property], globalPropertySetter];
96 | }
97 |
--------------------------------------------------------------------------------
/src/utils/bind-lifecycle-methods.ts:
--------------------------------------------------------------------------------
1 | import { Reducers, State } from '../../default';
2 | import { ReactNComponent, ReactNPureComponent } from '../../types/component';
3 | import React = require('react');
4 | import {
5 | ReactNComponentWillUnmount,
6 | ReactNComponentWillUpdate,
7 | ReactNShouldComponentUpdate,
8 | } from '../methods';
9 | import {
10 | // componentWillUnmountInstance,
11 | componentWillUnmountPrototype,
12 | } from './component-will-unmount';
13 | import {
14 | // componentWillUpdateInstance,
15 | componentWillUpdatePrototype,
16 | } from './component-will-update';
17 | import {
18 | // shouldComponentUpdateInstance,
19 | shouldComponentUpdatePrototype,
20 | } from './should-component-update';
21 |
22 |
23 |
24 | export default function bindLifecycleMethods<
25 | P extends {} = {},
26 | S extends {} = {},
27 | G extends {} = State,
28 | R extends {} = Reducers,
29 | SS = any,
30 | >(
31 | that: ReactNComponent | ReactNPureComponent
,
32 | ): void {
33 |
34 | if (
35 | // !componentWillUnmountInstance(that) &&
36 | !componentWillUnmountPrototype(that)
37 | ) {
38 |
39 | // Warning: If componentWillUnmount is defined in the constructor (or as an
40 | // arrow function), this will be overridden.
41 | that.componentWillUnmount = (): void => {
42 | ReactNComponentWillUnmount(that);
43 | };
44 | }
45 |
46 | const [ rVerMaj, rVerMin ] = React.version.split('.').map((v): number => parseInt(v));
47 | const isPureComponent = React.PureComponent && (that instanceof React.PureComponent);
48 | const isUsingOldReact = rVerMaj < 16 || (rVerMaj === 16 && rVerMin < 3);
49 |
50 | if (
51 | // !componentWillUpdateInstance(that) &&
52 | isUsingOldReact && !componentWillUpdatePrototype(that)
53 | ) {
54 | // This will fire if using an Old React Version
55 | // Warning: If componentWillUpdate is defined in the constructor (or as an
56 | // arrow function), this will be overridden.
57 | that.componentWillUpdate = (): void => {
58 | ReactNComponentWillUpdate(that);
59 | };
60 | }
61 |
62 | if (
63 | // !componentWillUpdateInstance(that) &&
64 | !isUsingOldReact && isPureComponent && !componentWillUpdatePrototype(that)
65 | ) {
66 | // This will fire if using New React Versions and component is Pure.
67 | // TODO: Firgure out a way to move out UNSAFE methods as can't use shouldComponentUpdate on Pure Components.
68 | // Warning: If UNSAFE_componentWillUpdate is defined in the constructor (or as an
69 | // arrow function), this will be overridden.
70 | that.UNSAFE_componentWillUpdate = (): void => {
71 | ReactNComponentWillUpdate(that);
72 | };
73 | }
74 |
75 | if (
76 | // !shouldComponentUpdateInstance(that) &&
77 | !isUsingOldReact && !isPureComponent && !shouldComponentUpdatePrototype(that)
78 | ) {
79 | // This will fire if using New React Versions and regular components
80 | // Warning: If shouldComponentUpdate is defined in the constructor (or as an
81 | // arrow function), this will be overridden.
82 | that.shouldComponentUpdate = (): boolean => {
83 | ReactNShouldComponentUpdate(that);
84 | return true; // If shouldComponentUpdate is not defined it defaults to true
85 | };
86 | }
87 | };
88 |
--------------------------------------------------------------------------------
/src/utils/component-will-unmount.ts:
--------------------------------------------------------------------------------
1 | import { Reducers, State } from '../../default';
2 | import { ReactNComponent, ReactNPureComponent } from '../../types/component';
3 | import { ReactNComponentWillUnmount } from '../methods';
4 |
5 |
6 |
7 | // type VoidFunction = () => void;
8 |
9 |
10 |
11 | // this.componentWillUnmount on instance
12 | /*
13 | export const componentWillUnmountInstance = <
14 | P extends {} = {},
15 | S extends {} = {},
16 | G extends {} = State,
17 | R extends {} = Reducers,
18 | SS = any,
19 | >(
20 | that: ReactNComponent
| ReactNPureComponent
,
21 | ): boolean => {
22 | if (Object.prototype.hasOwnProperty.call(that, 'componentWillUnmount')) {
23 | const instanceCwu: VoidFunction = that.componentWillUnmount;
24 | that.componentWillUnmount = (): void => {
25 | ReactNComponentWillUnmount(that);
26 | instanceCwu();
27 | };
28 | return true;
29 | }
30 | return false;
31 | };
32 | */
33 |
34 | // this.componentWillUnmount on prototype
35 | export const componentWillUnmountPrototype = <
36 | P extends {} = {},
37 | S extends {} = {},
38 | G extends {} = State,
39 | R extends {} = Reducers,
40 | SS = any,
41 | >(
42 | that: ReactNComponent
| ReactNPureComponent
,
43 | ): boolean => {
44 | const proto: ReactNComponent | ReactNPureComponent =
45 | Object.getPrototypeOf(that);
46 | if (Object.prototype.hasOwnProperty.call(proto, 'componentWillUnmount')) {
47 | that.componentWillUnmount = (): void => {
48 | ReactNComponentWillUnmount(that);
49 | proto.componentWillUnmount.bind(that)();
50 | };
51 | return true;
52 | }
53 | return false;
54 | };
55 |
--------------------------------------------------------------------------------
/src/utils/component-will-update.ts:
--------------------------------------------------------------------------------
1 | import { Reducers, State } from '../../default';
2 | import { ReactNComponent, ReactNPureComponent } from '../../types/component';
3 | import { ReactNComponentWillUpdate } from '../methods';
4 | import React = require('react');
5 |
6 |
7 | /*
8 | type ComponentWillUpdate
=
9 | (nextProps: P, nextState: S, context: any) => void;
10 | */
11 |
12 |
13 |
14 | // this.componentWillUpdate on instance
15 | /*
16 | export const componentWillUpdateInstance = <
17 | P extends {} = {},
18 | S extends {} = {},
19 | G extends {} = State,
20 | R extends {} = Reducers,
21 | SS = any,
22 | >(
23 | that: ReactNComponent
| ReactNPureComponent
,
24 | ): boolean => {
25 | if (Object.prototype.hasOwnProperty.call(that, 'componentWillUpdate')) {
26 | const instanceCwu: ComponentWillUpdate
= that.componentWillUpdate;
27 | that.componentWillUpdate = (...args: [ P, S, any ]): void => {
28 | ReactNComponentWillUpdate(that);
29 | instanceCwu(...args);
30 | };
31 | return true;
32 | }
33 | return false;
34 | };
35 | */
36 |
37 | // this.componentWillUpdate on prototype
38 | export const componentWillUpdatePrototype = <
39 | P extends {} = {},
40 | S extends {} = {},
41 | G extends {} = State,
42 | R extends {} = Reducers,
43 | SS = any,
44 | >(
45 | that: ReactNComponent
| ReactNPureComponent
,
46 | ): boolean => {
47 | const proto: ReactNComponent | ReactNPureComponent =
48 | Object.getPrototypeOf(that);
49 | const [ rVerMaj, rVerMin ] = React.version.split('.').map((v): number => parseInt(v));
50 | if (Object.prototype.hasOwnProperty.call(proto, 'componentWillUpdate')
51 | && ((rVerMaj < 16 || (rVerMaj === 16 && rVerMin < 3)))) { // Using old react version
52 | that.componentWillUpdate = (...args: [ P, S, any ]): void => {
53 | ReactNComponentWillUpdate(that);
54 | proto.componentWillUpdate.bind(that)(...args);
55 | };
56 | return true;
57 | }
58 | return false;
59 | };
60 |
--------------------------------------------------------------------------------
/src/utils/get-global-state-manager.ts:
--------------------------------------------------------------------------------
1 | import { Reducers, State } from '../../default';
2 | import Context from '../context';
3 | import defaultGlobalStateManager from '../default-global-state-manager';
4 | import GlobalStateManager from '../global-state-manager';
5 |
6 | export default function getGlobalStateManager<
7 | G extends {} = State,
8 | R extends {} = Reducers,
9 | >(): GlobalStateManager {
10 | return (
11 | Context &&
12 | (
13 | (Context._currentValue2 as GlobalStateManager) ||
14 | (Context._currentValue as GlobalStateManager)
15 | )
16 | ) ||
17 | (defaultGlobalStateManager as GlobalStateManager);
18 | }
19 |
--------------------------------------------------------------------------------
/src/utils/is-property-reducer.ts:
--------------------------------------------------------------------------------
1 | import { Reducers, State } from '../../default';
2 | import Reducer, { PropertyReducer } from '../../types/reducer';
3 |
4 | // Used to accurately identify the reducer as a property reducer if a property
5 | // is specified.
6 | export default function isPropertyReducer<
7 | G extends {} = State,
8 | R extends {} = Reducers,
9 | P extends keyof G = keyof G,
10 | A extends any[] = any[],
11 | >(
12 | _reducer: Reducer | PropertyReducer,
13 | property?: keyof G,
14 | ): _reducer is PropertyReducer {
15 | return typeof property !== 'undefined';
16 | };
17 |
--------------------------------------------------------------------------------
/src/utils/object-get-listener.ts:
--------------------------------------------------------------------------------
1 | // Return an object that executes a read listener.
2 |
3 | export default function objectGetListener(
4 | obj: Shape,
5 | listener: Function,
6 | ): Shape {
7 | return (Object.keys(obj) as (keyof Shape)[]).reduce(
8 | (accumulator: Partial, key: keyof Shape): Partial => {
9 | Object.defineProperty(accumulator, key, {
10 | configurable: false,
11 | enumerable: true,
12 | get: (): Shape[keyof Shape] => {
13 | listener(key);
14 | return obj[key];
15 | }
16 | });
17 | return accumulator;
18 | },
19 | Object.create(null)
20 | );
21 | };
22 |
--------------------------------------------------------------------------------
/src/utils/react-context-error.ts:
--------------------------------------------------------------------------------
1 | export default new Error(
2 | 'The installed version of React does not support Context. ' +
3 | 'Upgrade to React v16.3.0 or later.'
4 | );
5 |
--------------------------------------------------------------------------------
/src/utils/react-hooks-error.ts:
--------------------------------------------------------------------------------
1 | export default new Error(
2 | 'The installed version of React does not support Hooks. ' +
3 | 'Upgrade to React v16.8.0 or later.'
4 | );
5 |
--------------------------------------------------------------------------------
/src/utils/should-component-update.ts:
--------------------------------------------------------------------------------
1 | import { Reducers, State } from '../../default';
2 | import { ReactNComponent, ReactNPureComponent } from '../../types/component';
3 | import { ReactNShouldComponentUpdate } from '../methods';
4 | import React = require('react');
5 |
6 |
7 | /*
8 | type ShouldComponentUpdate =
9 | (nextProps: P, nextState: S, context: any) => void;
10 | */
11 |
12 |
13 |
14 | // this.shouldComponentUpdate on instance
15 | /*
16 | export const shouldComponentUpdateInstance = <
17 | P extends {} = {},
18 | S extends {} = {},
19 | G extends {} = State,
20 | R extends {} = Reducers,
21 | SS = any,
22 | >(
23 | that: ReactNComponent
| ReactNPureComponent
,
24 | ): boolean => {
25 | if (Object.prototype.hasOwnProperty.call(that, 'shouldComponentUpdate')) {
26 | const instanceCwu: ShouldComponentUpdate
= that.shouldComponentUpdate;
27 | that.shouldComponentUpdate = (...args: [ P, S, any ]): void => {
28 | ReactNShouldComponentUpdate(that);
29 | instanceCwu(...args);
30 | };
31 | return true;
32 | }
33 | return false;
34 | };
35 | */
36 |
37 | // this.shouldComponentUpdate on prototype
38 | export const shouldComponentUpdatePrototype = <
39 | P extends {} = {},
40 | S extends {} = {},
41 | G extends {} = State,
42 | R extends {} = Reducers,
43 | SS = any,
44 | >(
45 | that: ReactNComponent
| ReactNPureComponent
,
46 | ): boolean => {
47 |
48 | const proto: ReactNComponent | ReactNPureComponent =
49 | Object.getPrototypeOf(that);
50 | const [ rVerMaj, rVerMin ] = React.version.split('.').map((v): number => parseInt(v));
51 | if (Object.prototype.hasOwnProperty.call(proto, 'shouldComponentUpdate')
52 | && ((rVerMaj > 16 || (rVerMaj === 16 && rVerMin >= 3)))) {
53 | that.shouldComponentUpdate = (...args: [ P, S, any ]): boolean => {
54 | ReactNShouldComponentUpdate(that);
55 | return proto.shouldComponentUpdate.bind(that)(...args); // Returns outcome of shouldComponentUpdate method
56 | };
57 | return true;
58 | }
59 | return false;
60 | };
61 |
--------------------------------------------------------------------------------
/src/with-init.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import { Reducers, State } from '../default';
3 | import { ReactNComponentClass } from '../types/component-class';
4 | import NewGlobalState from '../types/new-global-state';
5 | import WithInit, { WithInitState } from '../types/with-init';
6 | import addReducers from './add-reducers';
7 | import { ReactNComponent } from './components';
8 | import defaultGlobalStateManager from './default-global-state-manager';
9 |
10 | export default function _withInit<
11 | G extends {} = State,
12 | R extends {} = Reducers,
13 | P extends {} = {},
14 | >(
15 | initialGlobal: NewGlobalState | null = null,
16 | initialReducers: null | R = null,
17 | ): WithInit {
18 | return function ReactNWithInit(
19 | Component: React.ComponentType
| string,
20 | FallbackComponent: null | React.ComponentType
| string = null,
21 | ): ReactNComponentClass
{
22 | return class ReactNWithInitHoc extends ReactNComponent
{
23 |
24 | state: WithInitState = {
25 | global: !Boolean(initialGlobal),
26 | reducers: !Boolean(initialReducers),
27 | };
28 |
29 | public componentDidMount(): void {
30 |
31 | // If there is an initial global state, set it.
32 | if (initialGlobal) {
33 | this.setGlobal(
34 | initialGlobal,
35 | (): void => {
36 | this.setState({ global: true });
37 | },
38 | );
39 | }
40 |
41 | // If there are initial reducers, add them.
42 | if (initialReducers) {
43 |
44 | // Reducers only need to worry about being added to the default
45 | // global state manager. Providers will have their reducers set
46 | // when they are created.
47 | addReducers(defaultGlobalStateManager, initialReducers);
48 | this.setState({ reducers: true });
49 | }
50 | }
51 | render() {
52 | if (
53 | !this.state.global ||
54 | !this.state.reducers
55 | ) {
56 | if (FallbackComponent) {
57 | return ;
58 | }
59 | return null;
60 | }
61 |
62 | return ;
63 | }
64 | };
65 | };
66 | }
67 |
--------------------------------------------------------------------------------
/tests/add-callback.test.ts:
--------------------------------------------------------------------------------
1 | import addCallback from '../src/add-callback';
2 | import GlobalStateManager from '../src/global-state-manager';
3 | import Callback from '../types/callback';
4 | import { G, INITIAL_STATE } from './utils/initial';
5 | import spyOn from './utils/spy-on-global-state-manager';
6 |
7 |
8 |
9 | const CALLBACK: Callback = (): null => null;
10 |
11 |
12 |
13 | describe.only('addCallback', (): void => {
14 |
15 | let globalStateManager: GlobalStateManager;
16 | const spy = spyOn('addCallback', 'removeCallback');
17 |
18 | beforeEach((): void => {
19 | globalStateManager = new GlobalStateManager(INITIAL_STATE);
20 | });
21 |
22 |
23 |
24 | it('should be a function with 2 arguments', (): void => {
25 | expect(addCallback).toBeInstanceOf(Function);
26 | expect(addCallback).toHaveLength(2);
27 | });
28 |
29 | it('should call GlobalStateManager.addCallback', (): void => {
30 | addCallback(globalStateManager, CALLBACK);
31 | expect(spy.addCallback).toHaveBeenCalledTimes(1);
32 | expect(spy.addCallback).toHaveBeenCalledWith(CALLBACK);
33 | });
34 |
35 |
36 |
37 | describe('returned remove callback function', (): void => {
38 |
39 | let removeCallback: () => boolean;
40 | beforeEach((): void => {
41 | removeCallback = addCallback(globalStateManager, CALLBACK);
42 | });
43 |
44 | it('should be a function with no arguments', (): void => {
45 | expect(removeCallback).toBeInstanceOf(Function);
46 | expect(removeCallback).toHaveLength(0);
47 | });
48 |
49 | it('should call GlobalStateManager.removeCallback', (): void => {
50 | removeCallback();
51 | expect(spy.removeCallback).toHaveBeenCalledTimes(1);
52 | expect(spy.removeCallback).toHaveBeenCalledWith(CALLBACK);
53 | });
54 | });
55 |
56 | });
57 |
--------------------------------------------------------------------------------
/tests/add-reducer.test.ts:
--------------------------------------------------------------------------------
1 | import addReducer from '../src/add-reducer';
2 | import GlobalStateManager from '../src/global-state-manager';
3 | import spyOn from './utils/spy-on-global-state-manager';
4 |
5 |
6 |
7 | const REDUCER = (_globalState, _dispatch, _one, _two, _three): void => { };
8 |
9 | const REDUCER_NAME = 'reducerName';
10 |
11 |
12 |
13 | describe('addReducer', (): void => {
14 |
15 | let globalStateManager: GlobalStateManager<{}, {}>;
16 | const spy = spyOn('addReducer', 'removeDispatcher');
17 |
18 | beforeEach((): void => {
19 | globalStateManager = new GlobalStateManager<{}, {}>();
20 | });
21 |
22 |
23 |
24 | it('should be a function with 3 arguments', (): void => {
25 | expect(addReducer).toBeInstanceOf(Function);
26 | expect(addReducer).toHaveLength(3);
27 | });
28 |
29 | it('should call GlobalStateManager.addReducer', (): void => {
30 | addReducer(globalStateManager, REDUCER_NAME, REDUCER);
31 | expect(spy.addReducer).toHaveBeenCalledTimes(1);
32 | expect(spy.addReducer).toHaveBeenCalledWith(REDUCER_NAME, REDUCER);
33 | });
34 |
35 |
36 |
37 | describe('returned remove reducer function', (): void => {
38 |
39 | let removeReducer: () => boolean;
40 | beforeEach((): void => {
41 | removeReducer = addReducer(globalStateManager, REDUCER_NAME, REDUCER);
42 | });
43 |
44 | it('should be a function with no arguments', (): void => {
45 | expect(removeReducer).toBeInstanceOf(Function);
46 | expect(removeReducer).toHaveLength(0);
47 | });
48 |
49 | it('should call GlobalStateManager.removeDispatcher', (): void => {
50 | removeReducer();
51 | expect(spy.removeDispatcher).toHaveBeenCalledTimes(1);
52 | expect(spy.removeDispatcher).toHaveBeenCalledWith(REDUCER_NAME);
53 | });
54 | });
55 |
56 | });
57 |
--------------------------------------------------------------------------------
/tests/add-reducers.test.ts:
--------------------------------------------------------------------------------
1 | import addReducers from '../src/add-reducers';
2 | import GlobalStateManager from '../src/global-state-manager';
3 | import { G, INITIAL_REDUCERS, INITIAL_STATE } from './utils/initial';
4 | import spyOn from './utils/spy-on-global-state-manager';
5 |
6 |
7 |
8 | const REDUCER_NAMES = Object.keys(INITIAL_REDUCERS);
9 |
10 | const REDUCERS = Object.entries(INITIAL_REDUCERS);
11 |
12 |
13 |
14 | describe('addReducers', (): void => {
15 |
16 | let globalStateManager: GlobalStateManager;
17 | const spy = spyOn('addReducer', 'removeDispatcher');
18 |
19 | beforeEach((): void => {
20 | globalStateManager = new GlobalStateManager(INITIAL_STATE);
21 | });
22 |
23 |
24 |
25 | it('should be a function with 2 arguments', (): void => {
26 | expect(addReducers).toBeInstanceOf(Function);
27 | expect(addReducers).toHaveLength(2);
28 | });
29 |
30 | it(
31 | 'should call GlobalStateManager.addReducer for each reducer',
32 | (): void => {
33 | addReducers(globalStateManager, INITIAL_REDUCERS);
34 | expect(spy.addReducer).toHaveBeenCalledTimes(REDUCERS.length);
35 | for (const [ name, reducer ] of REDUCERS) {
36 | expect(spy.addReducer).toHaveBeenCalledWith(name, reducer);
37 | }
38 | }
39 | );
40 |
41 |
42 |
43 | describe('returned remove reducers function', (): void => {
44 |
45 | let removeReducers: () => boolean;
46 | beforeEach((): void => {
47 | removeReducers = addReducers(globalStateManager, INITIAL_REDUCERS);
48 | });
49 |
50 | it('should be a function with no arguments', (): void => {
51 | expect(removeReducers).toBeInstanceOf(Function);
52 | expect(removeReducers).toHaveLength(0);
53 | });
54 |
55 | it(
56 | 'should call GlobalStateManager.removeDispatcher for each reducer',
57 | (): void => {
58 | removeReducers();
59 | expect(spy.removeDispatcher).toHaveBeenCalledTimes(REDUCERS.length);
60 | for (const reducerName of REDUCER_NAMES) {
61 | expect(spy.removeDispatcher).toHaveBeenCalledWith(reducerName);
62 | }
63 | }
64 | );
65 |
66 | it('should return true', (): void => {
67 | expect(removeReducers()).toBe(true);
68 | });
69 | });
70 |
71 | });
72 |
--------------------------------------------------------------------------------
/tests/components/component-will-unmount.tsx:
--------------------------------------------------------------------------------
1 | import { render } from '@testing-library/react';
2 | import React, { ComponentClass } from '../../build';
3 | import spyOn from '../utils/spy-on-global-state-manager';
4 |
5 |
6 |
7 | export default (
8 | source: string,
9 | TestComponent: ComponentClass,
10 | spyCwu: jest.Mock,
11 | ): void => {
12 |
13 | const spy = spyOn('removePropertyListener');
14 |
15 | it(`should maintain componentWillUpdate behavior on ${source}`, (): void => {
16 | expect(spyCwu).not.toHaveBeenCalled();
17 | const { unmount } = render();
18 | expect(spyCwu).not.toHaveBeenCalled();
19 | unmount();
20 | expect(spyCwu).toHaveBeenCalledTimes(1);
21 | });
22 |
23 | it(`should unsubscribe when unmounting on ${source}`, (): void => {
24 | expect(spy.removePropertyListener).not.toHaveBeenCalled();
25 | const { unmount } = render();
26 | expect(spy.removePropertyListener).not.toHaveBeenCalled();
27 | unmount();
28 | expect(spy.removePropertyListener).toHaveBeenCalledTimes(1);
29 | /*
30 | TODO: Determine how to get the INSTANCE.
31 | expect(spy.removePropertyListener).toHaveBeenCalledWith(
32 | INSTANCE._globalCallback
33 | );
34 | */
35 | });
36 | };
37 |
--------------------------------------------------------------------------------
/tests/components/component-will-update.tsx:
--------------------------------------------------------------------------------
1 | import { render } from '@testing-library/react';
2 | import React, { ComponentClass } from '../../build';
3 | import { G, R } from '../utils/initial';
4 | import spyOn from '../utils/spy-on-global-state-manager';
5 | import Props from './props';
6 |
7 |
8 |
9 | export default (
10 | source: string,
11 | TestComponent: ComponentClass,
12 | spyCwu: jest.Mock,
13 | ): void => {
14 |
15 | const spy = spyOn('removePropertyListener');
16 |
17 | it(`should maintain componentWillUpdate behavior on ${source}`, (): void => {
18 | const { rerender } = render();
19 | rerender();
20 | expect(spyCwu).toHaveBeenCalledTimes(1);
21 | });
22 |
23 | it(`should unsubscribe when updating on ${source}`, (): void => {
24 | const { rerender } = render();
25 | expect(spy.removePropertyListener).not.toHaveBeenCalled();
26 | rerender();
27 | expect(spy.removePropertyListener).toHaveBeenCalledTimes(1);
28 | /*
29 | TODO: Determine how to get the INSTANCE.
30 | expect(spy.removePropertyListener).toHaveBeenCalledWith(
31 | INSTANCE._globalCallback
32 | );
33 | */
34 | });
35 | };
36 |
--------------------------------------------------------------------------------
/tests/components/mount.tsx:
--------------------------------------------------------------------------------
1 | import { render } from '@testing-library/react';
2 | import React, { ComponentClass } from '../../build';
3 | import { G, R } from '../utils/initial';
4 |
5 |
6 |
7 | export default (TestComponent: ComponentClass<{}, {}, G, R>): void => {
8 |
9 | it('should mount without error', (): void => {
10 | render();
11 | });
12 | };
13 |
--------------------------------------------------------------------------------
/tests/components/props.ts:
--------------------------------------------------------------------------------
1 | export default interface Props {
2 | a?: boolean;
3 | b?: number;
4 | c?: string;
5 | }
6 |
--------------------------------------------------------------------------------
/tests/context.test.ts:
--------------------------------------------------------------------------------
1 | import { Context, createContext } from 'react';
2 | import ReactNContext from '../src/context';
3 | import defaultGlobalStateManager from '../src/default-global-state-manager';
4 | import { hasContext } from './utils/react-version';
5 |
6 |
7 |
8 | describe('ReactN Context', (): void => {
9 |
10 | // If Context is not supported,
11 | if (!hasContext) {
12 | it('should require Context', (): void => {
13 | expect(ReactNContext).toBe(null);
14 | });
15 | return;
16 | }
17 |
18 |
19 |
20 | it('should be a React Context', (): void => {
21 | const reactContext: Context = createContext(null);
22 | const ReactContextPrototype = Object.getPrototypeOf(reactContext);
23 | const ReactNContextPrototype = Object.getPrototypeOf(ReactNContext);
24 | expect(ReactNContextPrototype).toBe(ReactContextPrototype);
25 | });
26 |
27 | it('should default to the default global state manager', (): void => {
28 | expect(ReactNContext._currentValue)
29 | .toStrictEqual(defaultGlobalStateManager);
30 |
31 | // React 16.3 does not have a _currentValue2.
32 | if (ReactNContext._currentValue2) {
33 | expect(ReactNContext._currentValue2)
34 | .toStrictEqual(defaultGlobalStateManager);
35 | }
36 | });
37 | });
38 |
--------------------------------------------------------------------------------
/tests/context/use-dispatch-function.test.ts:
--------------------------------------------------------------------------------
1 | import ReactN = require('../../src/index');
2 | import createProvider from '../../src/create-provider';
3 | import defaultGlobalStateManager from '../../src/default-global-state-manager';
4 | import Dispatcher from '../../types/dispatcher';
5 | import ReactNProvider from '../../types/provider';
6 | import Reducer from '../../types/reducer';
7 | import HookTest from '../utils/hook-test';
8 | import { G, INITIAL_REDUCERS, INITIAL_STATE, R } from '../utils/initial';
9 | import { hasHooks } from '../utils/react-version';
10 | import spyOn from '../utils/spy-on-global-state-manager';
11 |
12 |
13 |
14 | type A = string[];
15 |
16 | type AppendReducer = Reducer>;
17 |
18 | type P = [ AppendReducer ];
19 |
20 | type V = Dispatcher;
21 |
22 |
23 |
24 | const ARGS: string[] = [ 'te', 'st' ];
25 |
26 | const EMPTY_STATE: {} = Object.create(null);
27 |
28 | const REDUCER: AppendReducer = INITIAL_REDUCERS.append;
29 |
30 |
31 |
32 |
33 |
34 | describe('Context useDispatch(Function)', (): void => {
35 |
36 | // If Context is not supported,
37 | if (!hasHooks) {
38 | it.todo('should require hooks');
39 | return;
40 | }
41 |
42 |
43 |
44 | const Provider: ReactNProvider =
45 | createProvider(INITIAL_STATE, INITIAL_REDUCERS);
46 |
47 | const spy = spyOn('set');
48 |
49 | const STATE_CHANGE: Partial =
50 | REDUCER(INITIAL_STATE, Provider.dispatcherMap, ...ARGS);
51 |
52 | const NEW_STATE: G = {
53 | ...INITIAL_STATE,
54 | ...STATE_CHANGE,
55 | };
56 |
57 |
58 |
59 | let dispatch: Dispatcher;
60 | let testUseDispatch: HookTest;
61 | beforeEach((): void => {
62 | testUseDispatch =
63 | new HookTest
(
64 | (reducer: AppendReducer): V => ReactN.useDispatch(reducer),
65 | )
66 | .addParent(Provider);
67 | testUseDispatch.render(REDUCER);
68 | dispatch = testUseDispatch.value;
69 | });
70 |
71 | afterEach((): void => {
72 | Provider.reset();
73 | });
74 |
75 |
76 |
77 | it('should call GlobalStateManager.set', async (): Promise => {
78 | await dispatch(...ARGS);
79 | expect(spy.set).toHaveBeenCalledTimes(1);
80 | expect(spy.set).toHaveBeenCalledWith(STATE_CHANGE, REDUCER.name, ARGS);
81 | });
82 |
83 | it('should update the Context global state', async (): Promise => {
84 | await dispatch(...ARGS);
85 | expect(Provider.global).toEqual(NEW_STATE);
86 | });
87 |
88 | it('should not update the default global state', async (): Promise => {
89 | await dispatch(...ARGS);
90 | expect(defaultGlobalStateManager.state).toStrictEqual(EMPTY_STATE);
91 | });
92 |
93 | });
94 |
--------------------------------------------------------------------------------
/tests/context/use-dispatch-string.test.ts:
--------------------------------------------------------------------------------
1 | import ReactN = require('../../src/index');
2 | import createProvider from '../../src/create-provider';
3 | import defaultGlobalStateManager from '../../src/default-global-state-manager';
4 | import Dispatcher, { ExtractArguments } from '../../types/dispatcher';
5 | import ReactNProvider from '../../types/provider';
6 | import HookTest from '../utils/hook-test';
7 | import { G, INITIAL_REDUCERS, INITIAL_STATE, R } from '../utils/initial';
8 | import { hasHooks } from '../utils/react-version';
9 | import spyOn from '../utils/spy-on-global-state-manager';
10 |
11 |
12 |
13 | type A = ExtractArguments;
14 |
15 | type P = [ keyof R ];
16 |
17 | type V = Dispatcher;
18 |
19 |
20 |
21 | const ARGS: A = [ 'te', 'st' ];
22 |
23 | const EMPTY_STATE: {} = Object.create(null);
24 |
25 | const NAME: keyof R = 'append';
26 |
27 |
28 |
29 | describe('Context useDispatch(string)', (): void => {
30 |
31 | // If Hooks are not supported,
32 | if (!hasHooks) {
33 | it.todo('should require hooks');
34 | return;
35 | }
36 |
37 |
38 |
39 | const Provider: ReactNProvider = createProvider(
40 | INITIAL_STATE,
41 | INITIAL_REDUCERS,
42 | );
43 |
44 | const spy = spyOn('set');
45 |
46 | const STATE_CHANGE: Partial = INITIAL_REDUCERS[NAME](
47 | INITIAL_STATE,
48 | Provider.dispatcherMap,
49 | ...ARGS,
50 | );
51 |
52 | const NEW_STATE: G = {
53 | ...INITIAL_STATE,
54 | ...STATE_CHANGE,
55 | };
56 |
57 |
58 |
59 | let dispatch: Dispatcher;
60 | let testUseDispatch: HookTest;
61 | beforeEach((): void => {
62 | testUseDispatch =
63 | new HookTest
(
64 | (reducer: keyof R): V => ReactN.useDispatch(reducer)
65 | )
66 | .addParent(Provider);
67 | testUseDispatch.render(NAME);
68 | dispatch = testUseDispatch.value;
69 | });
70 |
71 | afterEach((): void => {
72 | Provider.reset();
73 | });
74 |
75 |
76 |
77 | it('should call GlobalStateManager.set', async (): Promise => {
78 | await dispatch(...ARGS);
79 | expect(spy.set).toHaveBeenCalledTimes(1);
80 | expect(spy.set).toHaveBeenCalledWith(STATE_CHANGE, NAME, ARGS);
81 | });
82 |
83 | it('should update the Context global state', async (): Promise => {
84 | await dispatch(...ARGS);
85 | expect(Provider.global).toEqual(NEW_STATE);
86 | });
87 |
88 | it('should not update the default global state', async (): Promise => {
89 | await dispatch(...ARGS);
90 | expect(defaultGlobalStateManager.state).toStrictEqual(EMPTY_STATE);
91 | });
92 |
93 | });
94 |
--------------------------------------------------------------------------------
/tests/context/use-dispatch-undefined.test.ts:
--------------------------------------------------------------------------------
1 | import ReactN = require('../../src/index');
2 | import createProvider from '../../src/create-provider';
3 | import defaultGlobalStateManager from '../../src/default-global-state-manager';
4 | import Dispatcher, { ExtractArguments } from '../../types/dispatcher';
5 | import DispatchFunction from '../../types/dispatch-function';
6 | import Dispatchers from '../../types/dispatchers';
7 | import ReactNProvider from '../../types/provider';
8 | import HookTest from '../utils/hook-test';
9 | import { G, INITIAL_REDUCERS, INITIAL_STATE, R } from '../utils/initial';
10 | import { hasHooks } from '../utils/react-version';
11 | import spyOn from '../utils/spy-on-global-state-manager';
12 |
13 |
14 |
15 | type A = ExtractArguments;
16 |
17 | type P = [ ];
18 |
19 | type V = DispatchFunction & Dispatchers;
20 |
21 |
22 |
23 | const ARGS: A = [ 'te', 'st' ];
24 |
25 | const EMPTY_STATE: {} = Object.create(null);
26 |
27 | const REDUCER: keyof R = 'append';
28 |
29 | const REDUCER_NAMES: string[] = Object.keys(INITIAL_REDUCERS);
30 |
31 | REDUCER_NAMES.sort();
32 |
33 |
34 |
35 | describe('Context useDispatch()', (): void => {
36 |
37 | // If Context is not supported,
38 | if (!hasHooks) {
39 | it.todo('should require hooks');
40 | return;
41 | }
42 |
43 |
44 |
45 | const Provider: ReactNProvider = createProvider(
46 | INITIAL_STATE,
47 | INITIAL_REDUCERS,
48 | );
49 |
50 | const spy = spyOn('set');
51 |
52 | const STATE_CHANGE: Partial = INITIAL_REDUCERS[REDUCER](
53 | INITIAL_STATE,
54 | Provider.dispatcherMap,
55 | ...ARGS,
56 | );
57 |
58 | const NEW_STATE: G = {
59 | ...INITIAL_STATE,
60 | ...STATE_CHANGE,
61 | };
62 |
63 |
64 |
65 | let dispatchers: Dispatchers;
66 | let testUseDispatch: HookTest;
67 | beforeEach((): void => {
68 | testUseDispatch =
69 | new HookTest
(
70 | (): V => ReactN.useDispatch()
71 | )
72 | .addParent(Provider);
73 | testUseDispatch.render();
74 | dispatchers = testUseDispatch.value;
75 | });
76 |
77 | afterEach((): void => {
78 | Provider.reset();
79 | });
80 |
81 |
82 | it('should return a function', (): void => {
83 | expect(dispatchers).toBeInstanceOf(Function);
84 | });
85 |
86 | it('should return the global dispatch object', async (): Promise => {
87 | const DISPATCHER_NAMES: string[] = Object.keys(dispatchers);
88 | DISPATCHER_NAMES.sort();
89 | expect(DISPATCHER_NAMES).toEqual(REDUCER_NAMES);
90 | });
91 |
92 | describe('the returned dispatch object values', (): void => {
93 |
94 | let dispatch: Dispatcher;
95 | beforeEach((): void => {
96 | dispatch = dispatchers[REDUCER];
97 | });
98 |
99 | it('should call GlobalStateManager.set', async (): Promise => {
100 | await dispatch(...ARGS);
101 | expect(spy.set).toHaveBeenCalledTimes(1);
102 | expect(spy.set).toHaveBeenCalledWith(STATE_CHANGE, REDUCER, ARGS);
103 | });
104 |
105 | it('should update the Context global state', async (): Promise => {
106 | await dispatch(...ARGS);
107 | expect(Provider.global).toEqual(NEW_STATE);
108 | });
109 |
110 | it('should not update the default global state', async (): Promise => {
111 | await dispatch(...ARGS);
112 | expect(defaultGlobalStateManager.state).toStrictEqual(EMPTY_STATE);
113 | });
114 | });
115 |
116 | });
117 |
--------------------------------------------------------------------------------
/tests/create-provider.test.ts:
--------------------------------------------------------------------------------
1 | import { Component } from 'react';
2 | import createProvider from '../src/create-provider';
3 | import REACT_CONTEXT_ERROR from '../src/utils/react-context-error';
4 | import ReactNProvider from '../types/provider';
5 | import { G, INITIAL_REDUCERS, INITIAL_STATE, R } from './utils/initial';
6 | import { hasContext } from './utils/react-version';
7 |
8 |
9 |
10 | const EMPTY_OBJECT: {} = Object.create(null);
11 |
12 | const INITIAL_REDUCERS_KEYS: string[] = Object.keys(INITIAL_REDUCERS);
13 |
14 | INITIAL_REDUCERS_KEYS.sort();
15 |
16 |
17 |
18 | describe('createProvider', (): void => {
19 |
20 | it('should be a function with 2 arguments', (): void => {
21 | expect(createProvider).toBeInstanceOf(Function);
22 | expect(createProvider).toHaveLength(2);
23 | });
24 |
25 |
26 |
27 | // If Context is not supported,
28 | if (!hasContext) {
29 | return;
30 | }
31 |
32 |
33 |
34 | describe('return value', (): void => {
35 |
36 | describe('with no parameters', (): void => {
37 |
38 | let Provider: ReactNProvider<{}, {}>;
39 | beforeEach((): void => {
40 | Provider = createProvider();
41 | });
42 |
43 | it('should be a React Component', (): void => {
44 | expect(Object.getPrototypeOf(Provider)).toBe(Component);
45 | });
46 |
47 | it('should have an empty state', (): void => {
48 | expect(Provider.global).toStrictEqual(EMPTY_OBJECT);
49 | });
50 |
51 | it('should have empty dispatchers', (): void => {
52 | expect(Provider.dispatch).toStrictEqual(EMPTY_OBJECT);
53 | });
54 | });
55 |
56 |
57 |
58 | describe('with an initial state', (): void => {
59 |
60 | let Provider: ReactNProvider;
61 | beforeEach((): void => {
62 | Provider = createProvider(INITIAL_STATE);
63 | });
64 |
65 | it('should be a React Component', (): void => {
66 | expect(Object.getPrototypeOf(Provider)).toStrictEqual(Component);
67 | });
68 |
69 | it('should have a state', (): void => {
70 | expect(Provider.global).toEqual(INITIAL_STATE);
71 | });
72 |
73 | it('should have empty dispatchers', (): void => {
74 | expect(Provider.dispatch).toStrictEqual(EMPTY_OBJECT);
75 | });
76 | });
77 |
78 |
79 |
80 | describe('with an initial state and reducers', (): void => {
81 |
82 | let Provider: ReactNProvider;
83 | beforeEach((): void => {
84 | Provider = createProvider(INITIAL_STATE, INITIAL_REDUCERS);
85 | });
86 |
87 | it('should be a React Component', (): void => {
88 | expect(Object.getPrototypeOf(Provider)).toStrictEqual(Component);
89 | });
90 |
91 | it('should have a state', (): void => {
92 | expect(Provider.global).toEqual(INITIAL_STATE);
93 | });
94 |
95 | it('should have dispatchers', (): void => {
96 | const dispatchKeys: string[] = Object.keys(Provider.dispatch);
97 | dispatchKeys.sort();
98 | expect(dispatchKeys).toStrictEqual(INITIAL_REDUCERS_KEYS);
99 | });
100 | });
101 |
102 | });
103 |
104 | });
105 |
--------------------------------------------------------------------------------
/tests/decorator.test.ts:
--------------------------------------------------------------------------------
1 | import decorator from '../build/index';
2 | import commonjs = require('../build/index');
3 |
4 | describe('@reactn decorator', (): void => {
5 |
6 | it('should be the CommonJS export and the default export', (): void => {
7 | expect(commonjs).toBe(decorator);
8 | });
9 |
10 | it('should be a function with 1 argument', (): void => {
11 | expect(decorator).toBeInstanceOf(Function);
12 | expect(decorator).toHaveLength(1);
13 | });
14 |
15 | it.skip('should do more', (): void => {
16 | });
17 | });
18 |
--------------------------------------------------------------------------------
/tests/default-global-state-manager.test.ts:
--------------------------------------------------------------------------------
1 | import defaultGlobalStateManager from '../src/default-global-state-manager';
2 | import GlobalStateManager from '../src/global-state-manager';
3 |
4 |
5 |
6 | const EMPTY_OBJECT: {} = Object.create(null);
7 |
8 |
9 |
10 | describe('Default GlobalStateManager', (): void => {
11 |
12 | it('should be a GlobalStateManager', (): void => {
13 | expect(defaultGlobalStateManager).toBeInstanceOf(GlobalStateManager);
14 | });
15 |
16 | it('should have an empty state', (): void => {
17 | expect(defaultGlobalStateManager.state).toStrictEqual(EMPTY_OBJECT);
18 | });
19 |
20 | it('should not have dispatchers', (): void => {
21 | expect(defaultGlobalStateManager.dispatchers).toStrictEqual(EMPTY_OBJECT);
22 | });
23 | });
24 |
--------------------------------------------------------------------------------
/tests/get-dispatch.test.ts:
--------------------------------------------------------------------------------
1 | import addReducer from '../src/add-reducer';
2 | import getDispatch from '../src/get-dispatch';
3 | import GlobalStateManager from '../src/global-state-manager';
4 | import Dispatchers from '../types/dispatchers';
5 | import { G, INITIAL_REDUCERS, INITIAL_STATE, R } from './utils/initial';
6 | import spyOn from './utils/spy-on-global-state-manager';
7 |
8 |
9 |
10 | const REDUCER_NAMES: string[] = Object.keys(INITIAL_REDUCERS);
11 |
12 |
13 |
14 | describe('getDispatch', (): void => {
15 |
16 | let dispatchers: Dispatchers;
17 | let globalStateManager: GlobalStateManager;
18 | const spy = spyOn('dispatchers');
19 |
20 | beforeEach((): void => {
21 | globalStateManager = new GlobalStateManager(
22 | INITIAL_STATE,
23 | INITIAL_REDUCERS,
24 | );
25 | dispatchers = getDispatch(globalStateManager);
26 | });
27 |
28 |
29 |
30 | it('should be a function with 1 argument', (): void => {
31 | expect(getDispatch).toBeInstanceOf(Function);
32 | expect(getDispatch).toHaveLength(1);
33 | });
34 |
35 | it('should call GlobalStateManager.dispatchers', (): void => {
36 | expect(spy.dispatchers).toHaveBeenCalledTimes(1);
37 | expect(spy.dispatchers).toHaveBeenCalledWith();
38 | });
39 |
40 | it('should be an object of functions', (): void => {
41 | // Provider.dispatch extends Object.create(null), so it has no prototype.
42 | expect(dispatchers).toEqual(expect.any(Object));
43 | for (const dispatcher of Object.values(dispatchers)) {
44 | expect(dispatcher).toBeInstanceOf(Function);
45 | }
46 | });
47 |
48 | it('should share keys with reducers', (): void => {
49 | const DISPATCHER_NAMES: string[] = Object.keys(dispatchers);
50 | DISPATCHER_NAMES.sort();
51 | expect(DISPATCHER_NAMES).toStrictEqual(REDUCER_NAMES);
52 | });
53 |
54 | it('should update when reducers update', (): void => {
55 | const REDUCER = (): void => { };
56 | const REDUCER_NAME = 'REDUCER_NAME';
57 |
58 | expect(dispatchers[REDUCER_NAME]).toBeUndefined();
59 | addReducer(globalStateManager, REDUCER_NAME, REDUCER);
60 | dispatchers = getDispatch(globalStateManager);
61 | expect(dispatchers[REDUCER_NAME]).toBeInstanceOf(Function);
62 | });
63 | });
64 |
--------------------------------------------------------------------------------
/tests/get-global.test.ts:
--------------------------------------------------------------------------------
1 | import getGlobal from '../src/get-global';
2 | import GlobalStateManager from '../src/global-state-manager';
3 | import { G, INITIAL_STATE } from './utils/initial';
4 | import spyOn from './utils/spy-on-global-state-manager';
5 |
6 |
7 |
8 | describe('getGlobal', (): void => {
9 |
10 | let globalStateManager: GlobalStateManager;
11 | const spy = spyOn('state');
12 |
13 | beforeEach((): void => {
14 | globalStateManager = new GlobalStateManager(INITIAL_STATE);
15 | });
16 |
17 |
18 |
19 | it('should be a function with 1 argument', (): void => {
20 | expect(getGlobal).toBeInstanceOf(Function);
21 | expect(getGlobal).toHaveLength(1);
22 | });
23 |
24 | it('should call GlobalStateManager.state', (): void => {
25 | getGlobal(globalStateManager);
26 | expect(spy.state).toHaveBeenCalledTimes(1);
27 | expect(spy.state).toHaveBeenCalledWith();
28 | });
29 |
30 | it('should return a copy of the state', (): void => {
31 | const state: G = getGlobal(globalStateManager);
32 | expect(state).toEqual(INITIAL_STATE);
33 | expect(state).not.toBe(INITIAL_STATE);
34 | });
35 | });
36 |
--------------------------------------------------------------------------------
/tests/global-state-manager/add-callback.test.ts:
--------------------------------------------------------------------------------
1 | import GlobalStateManager from '../../src/global-state-manager';
2 | import Callback from '../../types/callback';
3 |
4 |
5 |
6 | const CALLBACK: Callback<{}> = (): void => { };
7 |
8 |
9 |
10 | describe('GlobalStateManager.addCallback', (): void => {
11 |
12 | let globalStateManager: GlobalStateManager<{}>;
13 | beforeEach((): void => {
14 | globalStateManager = new GlobalStateManager<{}>({});
15 | });
16 |
17 |
18 |
19 | it('should be a function with 1 argument', (): void => {
20 | expect(globalStateManager.addCallback).toBeInstanceOf(Function);
21 | expect(globalStateManager.addCallback).toHaveLength(1);
22 | });
23 |
24 | it('should add a callback', (): void => {
25 | expect(globalStateManager.hasCallback(CALLBACK)).toBe(false);
26 | globalStateManager.addCallback(CALLBACK);
27 | expect(globalStateManager.hasCallback(CALLBACK)).toBe(true);
28 | });
29 |
30 |
31 |
32 | describe('return value', (): void => {
33 |
34 | let removeCallback: () => boolean;
35 | beforeEach((): void => {
36 | removeCallback = globalStateManager.addCallback(CALLBACK);
37 | });
38 |
39 | it('should be a function with no arguments', (): void => {
40 | expect(removeCallback).toBeInstanceOf(Function);
41 | expect(removeCallback).toHaveLength(0);
42 | });
43 |
44 | it('should remove the callback', (): void => {
45 | expect(globalStateManager.hasCallback(CALLBACK)).toBe(true);
46 | removeCallback();
47 | expect(globalStateManager.hasCallback(CALLBACK)).toBe(false);
48 | });
49 | });
50 |
51 | });
52 |
--------------------------------------------------------------------------------
/tests/global-state-manager/add-middleware.test.ts:
--------------------------------------------------------------------------------
1 | import GlobalStateManager from '../../src/global-state-manager';
2 | import Middleware from '../../types/middleware';
3 | import { G, INITIAL_STATE } from '../utils/initial';
4 |
5 |
6 |
7 | const MIDDLEWARE: Middleware = (state: G): G => state;
8 | const MIDDLEWARE_CREATOR = (): Middleware => MIDDLEWARE;
9 |
10 |
11 |
12 | describe('GlobalStateManager.addMiddleware', (): void => {
13 |
14 | let globalStateManager: GlobalStateManager;
15 | beforeEach((): void => {
16 | globalStateManager = new GlobalStateManager(INITIAL_STATE);
17 | });
18 |
19 |
20 |
21 | it('should be a function with 1 argument', (): void => {
22 | expect(globalStateManager.addMiddleware).toBeInstanceOf(Function);
23 | expect(globalStateManager.addMiddleware).toHaveLength(1);
24 | });
25 |
26 | it('should add a middleware', (): void => {
27 | expect(globalStateManager.hasMiddleware(MIDDLEWARE)).toBe(false);
28 | globalStateManager.addMiddleware(MIDDLEWARE_CREATOR);
29 | expect(globalStateManager.hasMiddleware(MIDDLEWARE)).toBe(true);
30 | });
31 |
32 |
33 |
34 | describe('return value', (): void => {
35 |
36 | let removeMiddleware: () => boolean;
37 | beforeEach((): void => {
38 | removeMiddleware = globalStateManager.addMiddleware(MIDDLEWARE_CREATOR);
39 | });
40 |
41 | it('should be a function with no arguments', (): void => {
42 | expect(removeMiddleware).toBeInstanceOf(Function);
43 | expect(removeMiddleware).toHaveLength(0);
44 | });
45 |
46 | it('should remove the middleware', (): void => {
47 | expect(globalStateManager.hasMiddleware(MIDDLEWARE)).toBe(true);
48 | removeMiddleware();
49 | expect(globalStateManager.hasMiddleware(MIDDLEWARE)).toBe(false);
50 | });
51 | });
52 |
53 | });
54 |
--------------------------------------------------------------------------------
/tests/global-state-manager/add-property-listener.test.ts:
--------------------------------------------------------------------------------
1 | import GlobalStateManager, {
2 | PropertyListener,
3 | } from '../../src/global-state-manager';
4 | import { G, INITIAL_STATE } from '../utils/initial';
5 |
6 |
7 |
8 | const PROPERTY: keyof G = 'x';
9 |
10 | const PROPERTY_LISTENER: PropertyListener = (): void => { };
11 |
12 |
13 |
14 | describe('GlobalStateManager.addPropertyListener', (): void => {
15 |
16 | let globalStateManager: GlobalStateManager;
17 | beforeEach((): void => {
18 | globalStateManager = new GlobalStateManager(INITIAL_STATE);
19 | });
20 |
21 |
22 |
23 | it('should be a function with 2 arguments', (): void => {
24 | expect(globalStateManager.addPropertyListener).toBeInstanceOf(Function);
25 | expect(globalStateManager.addPropertyListener).toHaveLength(2);
26 | });
27 |
28 | it('should not return anything', (): void => {
29 | expect(globalStateManager.addPropertyListener(PROPERTY, PROPERTY_LISTENER))
30 | .toBeUndefined();
31 | });
32 |
33 |
34 |
35 | describe('properties without listeners', (): void => {
36 |
37 | it('should add a property listener', (): void => {
38 | expect(globalStateManager.hasPropertyListener(PROPERTY_LISTENER))
39 | .toBe(false);
40 | globalStateManager.addPropertyListener(PROPERTY, PROPERTY_LISTENER);
41 | expect(globalStateManager.hasPropertyListener(PROPERTY_LISTENER))
42 | .toBe(true);
43 | });
44 |
45 | it('should not return anything', (): void => {
46 | const value: void =
47 | globalStateManager.addPropertyListener(PROPERTY, PROPERTY_LISTENER);
48 | expect(value).toBeUndefined();
49 | });
50 | });
51 |
52 |
53 |
54 | describe('properties with listeners', (): void => {
55 |
56 | const PROPERTY_LISTENER2 = (): void => { };
57 |
58 | it('should add a property listener', (): void => {
59 | globalStateManager.addPropertyListener(PROPERTY, PROPERTY_LISTENER);
60 | expect(globalStateManager.hasPropertyListener(PROPERTY_LISTENER))
61 | .toBe(true);
62 | expect(globalStateManager.hasPropertyListener(PROPERTY_LISTENER2))
63 | .toBe(false);
64 | globalStateManager.addPropertyListener(PROPERTY, PROPERTY_LISTENER2);
65 | expect(globalStateManager.hasPropertyListener(PROPERTY_LISTENER2))
66 | .toBe(true);
67 | });
68 |
69 | it('should not return anything', (): void => {
70 | globalStateManager.addPropertyListener(PROPERTY, PROPERTY_LISTENER);
71 | const value: void =
72 | globalStateManager.addPropertyListener(PROPERTY, PROPERTY_LISTENER2);
73 | expect(value).toBeUndefined();
74 | });
75 | });
76 |
77 | });
78 |
--------------------------------------------------------------------------------
/tests/global-state-manager/add-reducer.test.ts:
--------------------------------------------------------------------------------
1 | import GlobalStateManager from '../../src/global-state-manager';
2 |
3 |
4 |
5 | const REDUCER = (): null => null;
6 |
7 | const REDUCER_NAME = 'mockReducer';
8 |
9 |
10 |
11 | describe('GlobalStateManager.addReducer', (): void => {
12 |
13 | let globalStateManager: GlobalStateManager<{}, {}>;
14 | beforeEach((): void => {
15 | globalStateManager = new GlobalStateManager<{}, {}>();
16 | });
17 |
18 |
19 |
20 | it('should be a function with 2 arguments', (): void => {
21 | expect(globalStateManager.addReducer).toBeInstanceOf(Function);
22 | expect(globalStateManager.addReducer).toHaveLength(2);
23 | });
24 |
25 | it('should add a reducer', (): void => {
26 | expect(globalStateManager.hasDispatcher(REDUCER_NAME)).toBe(false);
27 | globalStateManager.addReducer(REDUCER_NAME, REDUCER);
28 | expect(globalStateManager.hasDispatcher(REDUCER_NAME)).toBe(true);
29 | });
30 |
31 |
32 |
33 | describe('return value', (): void => {
34 |
35 | let removeDispatcher: () => boolean;
36 | beforeEach((): void => {
37 | removeDispatcher = globalStateManager.addReducer(REDUCER_NAME, REDUCER);
38 | });
39 |
40 | it('should be a function with no arguments', (): void => {
41 | expect(removeDispatcher).toBeInstanceOf(Function);
42 | expect(removeDispatcher).toHaveLength(0);
43 | });
44 |
45 | it('should remove the callback', (): void => {
46 | expect(globalStateManager.hasDispatcher(REDUCER_NAME)).toBe(true);
47 | removeDispatcher();
48 | expect(globalStateManager.hasDispatcher(REDUCER_NAME)).toBe(false);
49 | });
50 | });
51 |
52 | });
53 |
--------------------------------------------------------------------------------
/tests/global-state-manager/clear-queue.test.ts:
--------------------------------------------------------------------------------
1 | import GlobalStateManager from '../../src/global-state-manager';
2 | import { G, INITIAL_STATE } from '../utils/initial';
3 |
4 |
5 |
6 | describe('GlobalStateManager.clearQueue', (): void => {
7 |
8 | let globalStateManager: GlobalStateManager;
9 | beforeEach((): void => {
10 | globalStateManager = new GlobalStateManager(INITIAL_STATE);
11 | });
12 |
13 |
14 |
15 | it('should be a function with no arguments', (): void => {
16 | expect(globalStateManager.clearQueue).toBeInstanceOf(Function);
17 | expect(globalStateManager.clearQueue).toHaveLength(0);
18 | });
19 |
20 | it('should not return anything', (): void => {
21 | expect(globalStateManager.clearQueue()).toBeUndefined();
22 | });
23 |
24 | it('should clear the queue', (): void => {
25 | globalStateManager.enqueue('x', true);
26 | expect(globalStateManager.queue.size).toBe(1);
27 | globalStateManager.clearQueue();
28 | expect(globalStateManager.queue.size).toBe(0);
29 | });
30 | });
31 |
--------------------------------------------------------------------------------
/tests/global-state-manager/constructor.test.ts:
--------------------------------------------------------------------------------
1 | import GlobalStateManager from '../../src/global-state-manager';
2 | import { G, INITIAL_REDUCERS, INITIAL_STATE, R } from '../utils/initial';
3 |
4 |
5 |
6 | const EMPTY_OBJECT: {} = Object.create(null);
7 |
8 | const INITIAL_REDUCERS_KEYS: string[] = Object.keys(INITIAL_REDUCERS);
9 |
10 | INITIAL_REDUCERS_KEYS.sort();
11 |
12 |
13 |
14 | describe('GlobalStateManager.constructor', (): void => {
15 |
16 | it('should initialize with an empty state object', (): void => {
17 | const globalStateManager: GlobalStateManager = new GlobalStateManager();
18 | expect(globalStateManager.state).toStrictEqual(EMPTY_OBJECT);
19 | });
20 |
21 | it('should initialize with an empty dispatchers object', (): void => {
22 | const globalStateManager: GlobalStateManager = new GlobalStateManager();
23 | expect(globalStateManager.dispatchers).toStrictEqual(EMPTY_OBJECT);
24 | });
25 |
26 | it('should support an initial state', (): void => {
27 | const globalStateManager: GlobalStateManager =
28 | new GlobalStateManager(INITIAL_STATE);
29 | expect(globalStateManager.state).toEqual(INITIAL_STATE);
30 | });
31 |
32 | it('should support initial dispatchers', (): void => {
33 | const globalStateManager: GlobalStateManager =
34 | new GlobalStateManager(INITIAL_STATE, INITIAL_REDUCERS);
35 | const dispatchers: string[] = Object.keys(globalStateManager.dispatchers);
36 | dispatchers.sort();
37 | expect(dispatchers).toStrictEqual(INITIAL_REDUCERS_KEYS);
38 | });
39 | });
40 |
--------------------------------------------------------------------------------
/tests/global-state-manager/dispatcher-map.test.ts:
--------------------------------------------------------------------------------
1 | import GlobalStateManager from '../../src/global-state-manager';
2 | import { G, INITIAL_REDUCERS, INITIAL_STATE, R } from '../utils/initial';
3 |
4 | const STATE_CHANGE: Partial = {
5 | x: true,
6 | };
7 |
8 | const NEW_STATE: G = {
9 | ...INITIAL_STATE,
10 | ...STATE_CHANGE,
11 | };
12 |
13 | describe('GlobalStateManager.dispatcherMap', (): void => {
14 |
15 | let globalStateManager: GlobalStateManager;
16 | beforeEach((): void => {
17 | globalStateManager = new GlobalStateManager(INITIAL_STATE, INITIAL_REDUCERS);
18 | });
19 |
20 |
21 |
22 | it('should be a function with 1 argument', (): void => {
23 | expect(globalStateManager.dispatcherMap).toBeInstanceOf(Function);
24 | expect(globalStateManager.dispatcherMap).toHaveLength(1);
25 | });
26 |
27 | it('should update and return the global state', async (): Promise => {
28 | expect(globalStateManager.state).not.toEqual(NEW_STATE);
29 | const newGlobalState: G = await globalStateManager.dispatcherMap(STATE_CHANGE);
30 | expect(newGlobalState).toEqual(NEW_STATE);
31 | expect(globalStateManager.state).toEqual(NEW_STATE);
32 | });
33 |
34 | it('should have a property for each reducer', (): void => {
35 | for (const reducer of Object.keys(INITIAL_REDUCERS)) {
36 | expect(globalStateManager.dispatcherMap).toHaveProperty(reducer);
37 | expect(globalStateManager.dispatcherMap[reducer]).toBeInstanceOf(Function);
38 | }
39 | });
40 | });
41 |
--------------------------------------------------------------------------------
/tests/global-state-manager/enqueue.test.ts:
--------------------------------------------------------------------------------
1 | import GlobalStateManager from '../../src/global-state-manager';
2 | import { G, INITIAL_STATE } from '../utils/initial';
3 |
4 |
5 |
6 | describe('GlobalStateManager.enqueue', (): void => {
7 |
8 | let globalStateManager: GlobalStateManager;
9 | beforeEach((): void => {
10 | globalStateManager = new GlobalStateManager(INITIAL_STATE);
11 | });
12 |
13 |
14 |
15 | it('should be a function with 2 arguments', (): void => {
16 | expect(globalStateManager.enqueue).toBeInstanceOf(Function);
17 | expect(globalStateManager.enqueue).toHaveLength(2);
18 | });
19 |
20 | it('should not return anything', (): void => {
21 | expect(globalStateManager.enqueue('x', true)).toBeUndefined();
22 | });
23 |
24 | it('should update the queue', (): void => {
25 | expect(globalStateManager.queue.get('x')).toBeUndefined();
26 | globalStateManager.enqueue('x', true);
27 | expect(globalStateManager.queue.size).toBe(1);
28 | expect(globalStateManager.queue.get('x')).toBe(true);
29 | });
30 | });
31 |
--------------------------------------------------------------------------------
/tests/global-state-manager/flush.test.ts:
--------------------------------------------------------------------------------
1 | import GlobalStateManager from '../../src/global-state-manager';
2 | import { G, INITIAL_STATE } from '../utils/initial';
3 |
4 |
5 |
6 | describe('GlobalStateManager.flush', (): void => {
7 |
8 | let globalStateManager: GlobalStateManager;
9 | beforeEach((): void => {
10 | globalStateManager = new GlobalStateManager(INITIAL_STATE);
11 | });
12 |
13 |
14 |
15 | it('should be a function with 2 arguments', (): void => {
16 | expect(globalStateManager.flush).toBeInstanceOf(Function);
17 | expect(globalStateManager.flush).toHaveLength(2);
18 | });
19 |
20 | it('should return an object', (): void => {
21 | expect(typeof globalStateManager.flush()).toBe('object');
22 | });
23 |
24 | it('should return the state change', (): void => {
25 | globalStateManager.enqueue('x', true);
26 | const stateChange: Partial = globalStateManager.flush();
27 | expect(stateChange.x).toBe(true);
28 | expect(stateChange.y).toBeUndefined();
29 | expect(stateChange.z).toBeUndefined();
30 | });
31 |
32 | it('should clear the queue', (): void => {
33 | globalStateManager.enqueue('x', true);
34 | expect(globalStateManager.queue.size).toBe(1);
35 | globalStateManager.flush();
36 | expect(globalStateManager.queue.size).toBe(0);
37 | });
38 |
39 | it('should write the queue to the state', (): void => {
40 | expect(globalStateManager.state.x).toBe(false);
41 | globalStateManager.enqueue('x', true);
42 | globalStateManager.flush();
43 | expect(globalStateManager.state.x).toBe(true);
44 | });
45 | });
46 |
--------------------------------------------------------------------------------
/tests/global-state-manager/get-dispatcher.test.ts:
--------------------------------------------------------------------------------
1 | import GlobalStateManager from '../../src/global-state-manager';
2 | import Dispatcher from '../../types/dispatcher';
3 | import { G, INITIAL_REDUCERS, INITIAL_STATE, R } from '../utils/initial';
4 |
5 |
6 |
7 | describe('GlobalStateManager.getDispatcher', (): void => {
8 |
9 | let globalStateManager: GlobalStateManager;
10 | beforeEach((): void => {
11 | globalStateManager = new GlobalStateManager(
12 | INITIAL_STATE,
13 | INITIAL_REDUCERS,
14 | );
15 | });
16 |
17 |
18 |
19 | it('should be a function with 1 argument', (): void => {
20 | expect(globalStateManager.getDispatcher).toBeInstanceOf(Function);
21 | expect(globalStateManager.getDispatcher).toHaveLength(1);
22 | });
23 |
24 | it('should return a dispatcher if one exists', (): void => {
25 | const dispatcher: Dispatcher =
26 | globalStateManager.getDispatcher('append');
27 | expect(dispatcher).toBeInstanceOf(Function);
28 | });
29 |
30 | it('should throw an error if the dispatcher does not exist', (): void => {
31 | expect(() => {
32 | // @ts-ignore: Deliberately throwing an error.
33 | globalStateManager.getDispatcher('ANY');
34 | }).toThrow();
35 | });
36 | });
37 |
--------------------------------------------------------------------------------
/tests/global-state-manager/remove-callback.test.ts:
--------------------------------------------------------------------------------
1 | import GlobalStateManager from '../../src/global-state-manager';
2 | import Callback from '../../types/callback';
3 |
4 |
5 |
6 | const CALLBACK: Callback = (): void => { };
7 |
8 |
9 |
10 | describe('GlobalStateManager.removeCallback', (): void => {
11 |
12 | let globalStateManager: GlobalStateManager;
13 | beforeEach((): void => {
14 | globalStateManager = new GlobalStateManager();
15 | });
16 |
17 |
18 |
19 | it('should be a function with 1 argument', (): void => {
20 | expect(globalStateManager.removeCallback).toBeInstanceOf(Function);
21 | expect(globalStateManager.removeCallback).toHaveLength(1);
22 | });
23 |
24 | it('remove a callback', (): void => {
25 | globalStateManager.addCallback(CALLBACK);
26 | expect(globalStateManager.hasCallback(CALLBACK)).toBe(true);
27 | globalStateManager.removeCallback(CALLBACK);
28 | expect(globalStateManager.hasCallback(CALLBACK)).toBe(false);
29 | });
30 |
31 |
32 |
33 | describe('return value', (): void => {
34 |
35 | it('should be true if the callback existed', (): void => {
36 | globalStateManager.addCallback(CALLBACK);
37 | const removed: boolean = globalStateManager.removeCallback(CALLBACK);
38 | expect(removed).toBe(true);
39 | });
40 |
41 | it('should be false if the callback did not exist', (): void => {
42 | const removed: boolean = globalStateManager.removeCallback(CALLBACK);
43 | expect(removed).toBe(false);
44 | });
45 | });
46 |
47 | });
48 |
--------------------------------------------------------------------------------
/tests/global-state-manager/remove-dispatcher.test.ts:
--------------------------------------------------------------------------------
1 | import GlobalStateManager from '../../src/global-state-manager';
2 | import { G, INITIAL_REDUCERS, INITIAL_STATE, R } from '../utils/initial';
3 |
4 |
5 |
6 | const DISPATCHER_NAME: keyof R = 'increment';
7 |
8 |
9 |
10 | describe('GlobalStateManager.removeDispatcher', (): void => {
11 |
12 | let globalStateManager: GlobalStateManager;
13 | beforeEach((): void => {
14 | globalStateManager = new GlobalStateManager(
15 | INITIAL_STATE,
16 | INITIAL_REDUCERS,
17 | );
18 | });
19 |
20 |
21 |
22 | it('should be a function with 1 argument', (): void => {
23 | expect(globalStateManager.removeDispatcher).toBeInstanceOf(Function);
24 | expect(globalStateManager.removeDispatcher).toHaveLength(1);
25 | });
26 |
27 | it('remove a dispatcher', (): void => {
28 | expect(globalStateManager.hasDispatcher(DISPATCHER_NAME)).toBe(true);
29 | globalStateManager.removeDispatcher(DISPATCHER_NAME);
30 | expect(globalStateManager.hasDispatcher(DISPATCHER_NAME)).toBe(false);
31 | });
32 |
33 |
34 |
35 | describe('return value', (): void => {
36 |
37 | it('should be true if the dispatcher existed', (): void => {
38 | const removed: boolean =
39 | globalStateManager.removeDispatcher(DISPATCHER_NAME);
40 | expect(removed).toBe(true);
41 | });
42 |
43 | it('should be false if the dispatcher did not exist', (): void => {
44 | const FAKE_DISPATCHER_NAME: string = 'mockDispatcher';
45 | const removed: boolean =
46 | globalStateManager.removeDispatcher(FAKE_DISPATCHER_NAME);
47 | expect(removed).toBe(false);
48 | });
49 | });
50 |
51 | });
52 |
--------------------------------------------------------------------------------
/tests/global-state-manager/remove-middleware.test.ts:
--------------------------------------------------------------------------------
1 | import GlobalStateManager from '../../src/global-state-manager';
2 | import Middleware from '../../types/middleware';
3 | import { G } from '../utils/initial';
4 |
5 |
6 |
7 | const MIDDLEWARE: Middleware = (state: G): G => state;
8 | const MIDDLEWARE_CREATOR = (): Middleware => MIDDLEWARE;
9 |
10 |
11 |
12 | describe('GlobalStateManager.removeMiddleware', (): void => {
13 |
14 | let globalStateManager: GlobalStateManager;
15 | beforeEach((): void => {
16 | globalStateManager = new GlobalStateManager();
17 | });
18 |
19 |
20 |
21 | it('should be a function with 1 argument', (): void => {
22 | expect(globalStateManager.removeMiddleware).toBeInstanceOf(Function);
23 | expect(globalStateManager.removeMiddleware).toHaveLength(1);
24 | });
25 |
26 | it('remove a middleware', (): void => {
27 | globalStateManager.addMiddleware(MIDDLEWARE_CREATOR);
28 | expect(globalStateManager.hasMiddleware(MIDDLEWARE)).toBe(true);
29 | globalStateManager.removeMiddleware(MIDDLEWARE);
30 | expect(globalStateManager.hasMiddleware(MIDDLEWARE)).toBe(false);
31 | });
32 |
33 |
34 |
35 | describe('return value', (): void => {
36 |
37 | it('should be true if the middleware existed', (): void => {
38 | globalStateManager.addMiddleware(MIDDLEWARE_CREATOR);
39 | const removed: boolean = globalStateManager.removeMiddleware(MIDDLEWARE);
40 | expect(removed).toBe(true);
41 | });
42 |
43 | it('should be false if the middleware did not exist', (): void => {
44 | const removed: boolean = globalStateManager.removeMiddleware(MIDDLEWARE);
45 | expect(removed).toBe(false);
46 | });
47 | });
48 |
49 | });
50 |
--------------------------------------------------------------------------------
/tests/global-state-manager/remove-property-listener.test.ts:
--------------------------------------------------------------------------------
1 | import GlobalStateManager from '../../src/global-state-manager';
2 | import { G, INITIAL_STATE } from '../utils/initial';
3 |
4 |
5 |
6 | const PROPERTY: keyof G = 'x';
7 |
8 | const PROPERTY2: keyof G = 'y';
9 |
10 | const PROPERTY_LISTENER = (): void => { };
11 |
12 |
13 |
14 | describe('GlobalStateManager.removePropertyListener', (): void => {
15 |
16 | let globalStateManager: GlobalStateManager;
17 | beforeEach((): void => {
18 | globalStateManager = new GlobalStateManager(INITIAL_STATE);
19 | });
20 |
21 |
22 |
23 | it('should be a function with 1 argument', (): void => {
24 | expect(globalStateManager.removePropertyListener).toBeInstanceOf(Function);
25 | expect(globalStateManager.removePropertyListener).toHaveLength(1);
26 | });
27 |
28 | it('should remove a property listener', (): void => {
29 | globalStateManager.addPropertyListener(PROPERTY, PROPERTY_LISTENER);
30 | expect(globalStateManager.hasPropertyListener(PROPERTY_LISTENER))
31 | .toBe(true);
32 | globalStateManager.removePropertyListener(PROPERTY_LISTENER);
33 | expect(globalStateManager.hasPropertyListener(PROPERTY_LISTENER))
34 | .toBe(false);
35 | });
36 |
37 | it('should remove more than 1 property listener', (): void => {
38 | globalStateManager.addPropertyListener(PROPERTY, PROPERTY_LISTENER);
39 | globalStateManager.addPropertyListener(PROPERTY2, PROPERTY_LISTENER);
40 | expect(globalStateManager.hasPropertyListener(PROPERTY_LISTENER))
41 | .toBe(true);
42 | globalStateManager.removePropertyListener(PROPERTY_LISTENER);
43 | expect(globalStateManager.hasPropertyListener(PROPERTY_LISTENER))
44 | .toBe(false);
45 | });
46 |
47 |
48 |
49 | describe('return value', (): void => {
50 |
51 | it('should be true if the property listener existed', (): void => {
52 | globalStateManager.addPropertyListener(PROPERTY, PROPERTY_LISTENER);
53 | const removed: boolean =
54 | globalStateManager.removePropertyListener(PROPERTY_LISTENER);
55 | expect(removed).toBe(true);
56 | });
57 |
58 | it('should be false if the property listener did not exist', (): void => {
59 | const removed: boolean =
60 | globalStateManager.removePropertyListener(PROPERTY_LISTENER);
61 | expect(removed).toBe(false);
62 | });
63 | });
64 |
65 | });
66 |
--------------------------------------------------------------------------------
/tests/global-state-manager/reset.test.ts:
--------------------------------------------------------------------------------
1 | import GlobalStateManager from '../../src/global-state-manager';
2 | import { G, INITIAL_REDUCERS, INITIAL_STATE, R } from '../utils/initial';
3 |
4 |
5 |
6 | describe('GlobalStateManager.reset', (): void => {
7 |
8 | let globalStateManager: GlobalStateManager;
9 | beforeEach((): void => {
10 | globalStateManager = new GlobalStateManager(
11 | INITIAL_STATE,
12 | INITIAL_REDUCERS,
13 | );
14 | });
15 |
16 |
17 |
18 | it('should be a function with no arguments', (): void => {
19 | expect(globalStateManager.reset).toBeInstanceOf(Function);
20 | expect(globalStateManager.reset).toHaveLength(0);
21 | });
22 |
23 | it('should remove callbacks', (): void => {
24 | const CALLBACK = (): void => { };
25 | globalStateManager.addCallback(CALLBACK);
26 | expect(globalStateManager.hasCallback(CALLBACK)).toBe(true);
27 | globalStateManager.reset();
28 | expect(globalStateManager.hasCallback(CALLBACK)).toBe(false);
29 | });
30 |
31 | it('should reset dispatchers', (): void => {
32 | const REDUCER = (): null => null;
33 | const REDUCER_NAME = 'reducerName';
34 |
35 | globalStateManager.addReducer(REDUCER_NAME, REDUCER);
36 | expect(globalStateManager.hasDispatcher('increment')).toBe(true);
37 | expect(globalStateManager.hasDispatcher(REDUCER_NAME)).toBe(true);
38 | globalStateManager.reset();
39 | expect(globalStateManager.hasDispatcher('increment')).toBe(true);
40 | expect(globalStateManager.hasDispatcher(REDUCER_NAME)).toBe(false);
41 | });
42 |
43 | it('should remove property listeners', (): void => {
44 | const PROPERTY: keyof G = 'x';
45 | const PROPERTY_LISTENER = (): void => { };
46 | globalStateManager.addPropertyListener(PROPERTY, PROPERTY_LISTENER);
47 | expect(globalStateManager.hasPropertyListener(PROPERTY_LISTENER))
48 | .toBe(true);
49 | globalStateManager.reset();
50 | expect(globalStateManager.hasPropertyListener(PROPERTY_LISTENER))
51 | .toBe(false);
52 | });
53 |
54 | it('should empty the queue', (): void => {
55 | globalStateManager.enqueue('x', true);
56 | expect(globalStateManager.queue.size).toBe(1);
57 | globalStateManager.reset();
58 | expect(globalStateManager.queue.size).toBe(0);
59 | });
60 |
61 | it('should reset the state', async (): Promise => {
62 | await globalStateManager.set({
63 | x: true,
64 | y: 1,
65 | z: 'any',
66 | });
67 | expect(globalStateManager.state).not.toEqual(INITIAL_STATE);
68 | globalStateManager.reset();
69 | expect(globalStateManager.state).toEqual(INITIAL_STATE);
70 | });
71 |
72 | it('should not return anything', (): void => {
73 | expect(globalStateManager.reset()).toBeUndefined();
74 | });
75 | });
76 |
--------------------------------------------------------------------------------
/tests/global-state-manager/set-function.test.ts:
--------------------------------------------------------------------------------
1 | import GlobalStateManager from '../../src/global-state-manager';
2 | import { G, INITIAL_STATE } from '../utils/initial';
3 | import spyOn from '../utils/spy-on-global-state-manager';
4 |
5 |
6 |
7 | type A = [ boolean, number, string ];
8 |
9 |
10 |
11 | const FUNC = (gs: G): Partial => ({
12 | x: !gs.x,
13 | });
14 |
15 | const REDUCER_ARGS: A = [ true, 1, 'str' ];
16 |
17 | const REDUCER_NAME: string = 'reducerName';
18 |
19 | const STATE_CHANGE: Partial = Object.assign(Object.create(null), {
20 | x: true,
21 | });
22 |
23 |
24 |
25 | describe('GlobalStateManager.setFunction', (): void => {
26 |
27 | let globalStateManager: GlobalStateManager;
28 | const spy = spyOn('set');
29 | beforeEach((): void => {
30 | globalStateManager = new GlobalStateManager(INITIAL_STATE);
31 | });
32 |
33 |
34 |
35 | it('should be a function with 3 arguments', (): void => {
36 | expect(globalStateManager.setFunction).toBeInstanceOf(Function);
37 | expect(globalStateManager.setFunction).toHaveLength(3);
38 | });
39 |
40 | it('should call set', async (): Promise => {
41 | const set: Promise> =
42 | globalStateManager.setFunction(FUNC, REDUCER_NAME, REDUCER_ARGS);
43 | expect(spy.set).toHaveBeenCalledTimes(1);
44 | expect(spy.set)
45 | .toHaveBeenCalledWith(STATE_CHANGE, REDUCER_NAME, REDUCER_ARGS);
46 | await set;
47 | });
48 |
49 |
50 |
51 | describe('return value', (): void => {
52 |
53 | it('should be a Promise', async (): Promise => {
54 | const set: Promise> =
55 | globalStateManager.setFunction(FUNC, REDUCER_NAME, REDUCER_ARGS);
56 | expect(set).toBeInstanceOf(Promise);
57 | await set;
58 | });
59 |
60 | it('should resolve to the state change', async (): Promise => {
61 | const set: Promise> =
62 | globalStateManager.setFunction(FUNC, REDUCER_NAME, REDUCER_ARGS);
63 | const stateChange: Partial = await set;
64 | expect(stateChange).toStrictEqual(STATE_CHANGE);
65 | });
66 | });
67 |
68 | });
69 |
--------------------------------------------------------------------------------
/tests/global-state-manager/set-object.test.ts:
--------------------------------------------------------------------------------
1 | import GlobalStateManager from '../../src/global-state-manager';
2 | import { G, INITIAL_STATE } from '../utils/initial';
3 | import spyOn from '../utils/spy-on-global-state-manager';
4 |
5 |
6 |
7 | type A = [ boolean, number, string ];
8 |
9 |
10 |
11 | const REDUCER_ARGS: A = [ true, 1, 'str' ];
12 |
13 | const REDUCER_NAME: string = 'reducerName';
14 |
15 | const STATE_CHANGE: Partial = {
16 | x: true,
17 | };
18 |
19 |
20 |
21 | describe('GlobalStateManager.setObject', (): void => {
22 |
23 | let globalStateManager: GlobalStateManager;
24 | const spy = spyOn('enqueue', 'flush');
25 | beforeEach((): void => {
26 | globalStateManager = new GlobalStateManager(INITIAL_STATE);
27 | });
28 |
29 |
30 |
31 | it('should be a function with 3 arguments', (): void => {
32 | expect(globalStateManager.setObject).toBeInstanceOf(Function);
33 | expect(globalStateManager.setObject).toHaveLength(3);
34 | });
35 |
36 | it('should call enqueue and flush', async (): Promise => {
37 | await globalStateManager.setObject(
38 | STATE_CHANGE,
39 | REDUCER_NAME,
40 | REDUCER_ARGS
41 | );
42 | expect(spy.enqueue).toHaveBeenCalledTimes(1);
43 | expect(spy.enqueue).toHaveBeenCalledWith('x', STATE_CHANGE.x);
44 |
45 | expect(spy.flush).toHaveBeenCalledTimes(1);
46 | expect(spy.flush).toHaveBeenCalledWith(REDUCER_NAME, REDUCER_ARGS);
47 | });
48 |
49 |
50 |
51 | describe('return value', (): void => {
52 |
53 | it('should be a Promise', async (): Promise => {
54 | const set: Promise> =
55 | globalStateManager.setObject(STATE_CHANGE, REDUCER_NAME, REDUCER_ARGS);
56 | expect(set).toBeInstanceOf(Promise);
57 | await set;
58 | });
59 |
60 | it('should resolve to the state change', async (): Promise => {
61 | const set: Promise> =
62 | globalStateManager.setObject(STATE_CHANGE, REDUCER_NAME, REDUCER_ARGS);
63 | const stateChange: Partial = await set;
64 | expect(stateChange).toEqual(STATE_CHANGE);
65 | });
66 | });
67 |
68 | });
69 |
--------------------------------------------------------------------------------
/tests/global-state-manager/set-promise.test.ts:
--------------------------------------------------------------------------------
1 | import GlobalStateManager from '../../src/global-state-manager';
2 | import { G, INITIAL_STATE } from '../utils/initial';
3 | import spyOn from '../utils/spy-on-global-state-manager';
4 |
5 |
6 |
7 | type A = [ boolean, number, string ];
8 |
9 |
10 |
11 | const REDUCER_ARGS: A = [ true, 1, 'str' ];
12 |
13 | const REDUCER_NAME: string = 'reducerName';
14 |
15 | const STATE_CHANGE: Partial = {
16 | x: true,
17 | };
18 |
19 | const PROMISE: Promise> = Promise.resolve(STATE_CHANGE);
20 |
21 |
22 |
23 | describe('GlobalStateManager.setPromise', (): void => {
24 |
25 | let globalStateManager: GlobalStateManager;
26 | const spy = spyOn('set');
27 | beforeEach((): void => {
28 | globalStateManager = new GlobalStateManager(INITIAL_STATE);
29 | });
30 |
31 |
32 |
33 | it('should be a function with 3 arguments', (): void => {
34 | expect(globalStateManager.setPromise).toBeInstanceOf(Function);
35 | expect(globalStateManager.setPromise).toHaveLength(3);
36 | });
37 |
38 | it('should call set', async (): Promise => {
39 | await globalStateManager.setPromise(PROMISE, REDUCER_NAME, REDUCER_ARGS);
40 | expect(spy.set).toHaveBeenCalledTimes(1);
41 | expect(spy.set)
42 | .toHaveBeenCalledWith(STATE_CHANGE, REDUCER_NAME, REDUCER_ARGS);
43 | });
44 |
45 |
46 |
47 | describe('return value', (): void => {
48 |
49 | it('should be a Promise', async (): Promise => {
50 | const set: Promise> =
51 | globalStateManager.setPromise(PROMISE, REDUCER_NAME, REDUCER_ARGS);
52 | expect(set).toBeInstanceOf(Promise);
53 | await set;
54 | });
55 |
56 | it('should resolve to the state change', async (): Promise => {
57 | const set: Promise> =
58 | globalStateManager.setPromise(PROMISE, REDUCER_NAME, REDUCER_ARGS);
59 | const stateChange: Partial = await set;
60 | expect(stateChange).toEqual(STATE_CHANGE);
61 | });
62 | });
63 |
64 | });
65 |
--------------------------------------------------------------------------------
/tests/global-state-manager/spy-state.test.ts:
--------------------------------------------------------------------------------
1 | import GlobalStateManager from '../../src/global-state-manager';
2 | import { G, INITIAL_STATE } from '../utils/initial';
3 | import spyOn from '../utils/spy-on-global-state-manager';
4 |
5 |
6 |
7 | const MOCK_STATE: Partial = {
8 | x: true,
9 | y: 1,
10 | };
11 |
12 | const PROPERTY: keyof G = 'x';
13 |
14 | const PROPERTY_LISTENER = (): void => { };
15 |
16 |
17 |
18 | describe('GlobalStateManager.spyState', (): void => {
19 |
20 | let globalStateManager: GlobalStateManager;
21 | const spy = spyOn('addPropertyListener');
22 | beforeEach((): void => {
23 | globalStateManager = new GlobalStateManager(INITIAL_STATE);
24 | });
25 |
26 |
27 |
28 | it('should be a function with 1 argument', (): void => {
29 | expect(globalStateManager.spyState).toBeInstanceOf(Function);
30 | expect(globalStateManager.spyState).toHaveLength(1);
31 | });
32 |
33 | it('should return the current state', async (): Promise => {
34 | await globalStateManager.set(MOCK_STATE);
35 | expect(globalStateManager.spyState(PROPERTY_LISTENER)).toEqual({
36 | ...INITIAL_STATE,
37 | ...MOCK_STATE,
38 | });
39 | });
40 |
41 | it('should add a property listener when accessed', (): void => {
42 | expect(globalStateManager.hasPropertyListener(PROPERTY_LISTENER))
43 | .toBe(false);
44 | globalStateManager.spyState(PROPERTY_LISTENER)[PROPERTY];
45 | expect(spy.addPropertyListener).toHaveBeenCalledTimes(1);
46 | expect(spy.addPropertyListener)
47 | .toHaveBeenCalledWith(PROPERTY, PROPERTY_LISTENER);
48 | });
49 | });
50 |
--------------------------------------------------------------------------------
/tests/index.js:
--------------------------------------------------------------------------------
1 | /* eslint @typescript-eslint/no-var-requires: 0 */
2 | const { execSync } = require('child_process');
3 |
4 | const supportedVersions = [
5 | /*
6 | '~0.1',
7 | '~0.2',
8 | '~0.3',
9 | '~0.5',
10 | '~0.6',
11 | '~0.7',
12 | '~0.8',
13 | '~0.9',
14 | '~0.10',
15 | '~0.11',
16 | '~0.12',
17 | '~0.13',
18 | */
19 | // '~0.14', // ReactDOM
20 | // '~15.0',
21 | // '~15.3', // PureComponent
22 | // '~16.0',
23 | // '~16.3', // Context
24 | // '~16.8', // Hooks
25 | // '~16.9', // async act
26 | 'latest',
27 | ];
28 |
29 | const header = str => {
30 | console.log();
31 | console.log();
32 | console.log();
33 | const bar = '-'.repeat(str.length);
34 | console.log(`/-${bar}-\\`);
35 | console.log(`| ${str} |`);
36 | console.log(`\\-${bar}-/`);
37 | };
38 |
39 | for (const supportedVersion of supportedVersions) {
40 | header(`Installing react@${supportedVersion}.`);
41 | execSync(
42 | `yarn add react@${supportedVersion} react-dom@${supportedVersion} ` +
43 | '--dev --no-lockfile',
44 | {
45 | stdio: 'inherit',
46 | },
47 | );
48 |
49 | const { version } = require('react/package.json');
50 | delete require.cache[require.resolve('react/package.json')];
51 | header(`Testing react@${version}.`);
52 | let jestOptions = '';
53 | if (supportedVersion === 'latest') {
54 | jestOptions = ' --coverage';
55 | }
56 | execSync(`jest${jestOptions}`, {
57 | stdio: 'inherit',
58 | });
59 | }
60 |
--------------------------------------------------------------------------------
/tests/methods/component-will-unmount/component-will-unmount.TODO.tsx:
--------------------------------------------------------------------------------
1 | // import unmock from './utils/mock-component-will-unmount';
2 | import { ReactNComponent, ReactNPureComponent } from '../../../src/components';
3 | // import testPrototype from './utils/test-prototype';
4 | import testUndefined from './utils/test-undefined';
5 |
6 |
7 |
8 | describe('componentWillUnmount', (): void => {
9 |
10 | /*
11 | afterAll((): void => {
12 | unmock();
13 | });
14 | */
15 |
16 | describe('undefined componentWillUnmount', (): void => {
17 | it('will fire on Components', testUndefined(ReactNComponent));
18 | it('will fire on PureComponents', testUndefined(ReactNPureComponent));
19 | });
20 |
21 | /*
22 | This works, but the jest.mocks for the tested helper functions are not
23 | binding correctly.
24 | describe('prototype componentWillUnmount', (): void => {
25 | it('will fire on Components', testPrototype(ReactNComponent));
26 | it('will fire on PureComponents', testPrototype(ReactNPureComponent));
27 | });
28 | */
29 |
30 | /*
31 | describe('instance componentWillUnmount', (): void => {
32 | it('will fire on Components', testInstance(ReactNComponent));
33 | it('will fire on PureComponents', testInstance(ReactNPureComponent));
34 | });
35 | */
36 | });
37 |
--------------------------------------------------------------------------------
/tests/methods/component-will-unmount/utils/mock-component-will-unmount.ts:
--------------------------------------------------------------------------------
1 | import * as methods from '../../../../src/methods';
2 | import * as componentWillUnmount from '../../../../src/utils/component-will-unmount';
3 |
4 |
5 |
6 | jest.mock('../../../../src/methods', () => ({
7 | ...methods,
8 | ReactNComponentWillUnmount: jest.fn(methods.ReactNComponentWillUnmount),
9 | }));
10 |
11 | jest.mock('../../../../src/utils/component-will-unmount', () => ({
12 | ...componentWillUnmount,
13 | componentWillUnmountInstance:
14 | jest.fn(componentWillUnmount.componentWillUnmountInstance),
15 | componentWillUnmountPrototype:
16 | jest.fn(componentWillUnmount.componentWillUnmountPrototype),
17 | }));
18 |
19 | export default function unmock() {
20 | jest.unmock('../../../../src/methods');
21 | jest.unmock('../../../../src/utils/component-will-unmount');
22 | }
23 |
--------------------------------------------------------------------------------
/tests/methods/component-will-unmount/utils/test-instance.tsx:
--------------------------------------------------------------------------------
1 | import { render } from '@testing-library/react';
2 | import * as React from 'react';
3 | import { ReactNComponent } from '../../../../src/components';
4 | import { ReactNComponentWillUnmount } from '../../../../src/methods';
5 |
6 |
7 |
8 | type VoidFunction = () => void;
9 |
10 |
11 |
12 | export default function testUndefined(
13 | Component: typeof ReactNComponent,
14 | ): VoidFunction {
15 | return (): void => {
16 |
17 | const mockComponentWillUnmount: VoidFunction =
18 | jest.fn((): void => { });
19 |
20 | class TestComponent extends Component {
21 |
22 | componentWillUnmount = mockComponentWillUnmount;
23 |
24 | render() {
25 | return null;
26 | }
27 | }
28 |
29 | const testComponent = render()
30 | testComponent.unmount();
31 |
32 | expect(ReactNComponentWillUnmount).toHaveBeenCalledTimes(1);
33 | // expect(ReactNComponentWillUnmount).toHaveBeenCalledWith()
34 | // expect(componentWillUnmountInstance).toHaveBeenCalledTimes(1);
35 | // expect(componentWillUnmountInstance).toHaveBeenCalledWith();
36 | expect(mockComponentWillUnmount).toHaveBeenCalledTimes(1);
37 | expect(mockComponentWillUnmount).toHaveBeenCalledWith();
38 | };
39 | };
40 |
--------------------------------------------------------------------------------
/tests/methods/component-will-unmount/utils/test-prototype.tsx:
--------------------------------------------------------------------------------
1 | import { render } from '@testing-library/react';
2 | import * as React from 'react';
3 | import { ReactNComponent } from '../../../../src/components';
4 | import { ReactNComponentWillUnmount } from '../../../../src/methods';
5 | import {
6 | componentWillUnmountPrototype,
7 | } from '../../../../src/utils/component-will-unmount';
8 |
9 |
10 |
11 | type VoidFunction = () => void;
12 |
13 |
14 |
15 | export default function testUndefined(
16 | Component: typeof ReactNComponent,
17 | ): VoidFunction {
18 | return (): void => {
19 |
20 | const mockComponentWillUnmount: VoidFunction = jest.fn();
21 |
22 | class TestComponent extends Component {
23 | componentWillUnmount() {
24 | mockComponentWillUnmount();
25 | }
26 | render() {
27 | return null;
28 | }
29 | }
30 |
31 | const { unmount } = render()
32 | unmount();
33 |
34 | expect(ReactNComponentWillUnmount).toHaveBeenCalledTimes(1);
35 | // expect(ReactNComponentWillUnmount).toHaveBeenCalledWith();
36 | expect(componentWillUnmountPrototype).toHaveBeenCalledTimes(1);
37 | // expect(componentWillUnmountPrototype).toHaveBeenCalledWith();
38 | expect(mockComponentWillUnmount).toHaveBeenCalledTimes(1);
39 | };
40 | };
41 |
--------------------------------------------------------------------------------
/tests/methods/component-will-unmount/utils/test-undefined.tsx:
--------------------------------------------------------------------------------
1 | import { render } from '@testing-library/react';
2 | import * as React from 'react';
3 | import { ReactNComponent } from '../../../../src/components';
4 | import { ReactNComponentWillUnmount } from '../../../../src/methods';
5 |
6 |
7 |
8 | type VoidFunction = () => void;
9 |
10 |
11 |
12 | export default function testUndefined(
13 | Component: typeof ReactNComponent,
14 | ): VoidFunction {
15 | return (): void => {
16 |
17 | class TestComponent extends Component {
18 | render() {
19 | return null;
20 | }
21 | }
22 |
23 | const testComponent = render()
24 | testComponent.unmount();
25 |
26 | expect(ReactNComponentWillUnmount).toHaveBeenCalledTimes(1);
27 | // expect(ReactNComponentWillUnmount).toHaveBeenCalledWith();
28 | };
29 | };
30 |
--------------------------------------------------------------------------------
/tests/methods/component-will-update/component-will-update.TODO.tsx:
--------------------------------------------------------------------------------
1 | // import unmock from './utils/mock-component-will-update';
2 | import { ReactNComponent, ReactNPureComponent } from '../../../src/components';
3 | // import testPrototype from './utils/test-prototype';
4 | import testUndefined from './utils/test-undefined';
5 |
6 |
7 |
8 | describe('componentWillUpdate', (): void => {
9 |
10 | /*
11 | afterAll((): void => {
12 | unmock();
13 | });
14 | */
15 |
16 | describe('undefined componentWillUpdate', (): void => {
17 | it('will fire on Components', testUndefined(ReactNComponent));
18 | it('will fire on PureComponents', testUndefined(ReactNPureComponent));
19 | });
20 |
21 |
22 | /*
23 | This works, but the jest.mocks for the tested helper functions are not
24 | binding correctly.
25 | describe('prototype componentWillUpdate', (): void => {
26 | it('will fire on Components', testPrototype(ReactNComponent));
27 | it('will fire on PureComponents', testPrototype(ReactNPureComponent));
28 | });
29 | */
30 |
31 | /*
32 | describe('instance componentWillUpdate', (): void => {
33 | it('will fire on Components', testInstance(ReactNComponent));
34 | it('will fire on PureComponents', testInstance(ReactNPureComponent));
35 | });
36 | */
37 | });
38 |
--------------------------------------------------------------------------------
/tests/methods/component-will-update/utils/mock-component-will-update.ts:
--------------------------------------------------------------------------------
1 | import * as methods from '../../../../src/methods';
2 | import * as componentWillUpdate from '../../../../src/utils/component-will-update';
3 |
4 |
5 |
6 | jest.mock('../../../../src/methods', () => ({
7 | ...methods,
8 | ReactNComponentWillUpdate: jest.fn(methods.ReactNComponentWillUpdate),
9 | }));
10 |
11 | jest.mock('../../../../src/utils/component-will-update', () => ({
12 | ...componentWillUpdate,
13 | componentWillUpdateInstance:
14 | jest.fn(componentWillUpdate.componentWillUpdateInstance),
15 | componentWillUpdatePrototype:
16 | jest.fn(componentWillUpdate.componentWillUpdatePrototype),
17 | }));
18 |
19 | export default function unmock() {
20 | jest.unmock('../../../../src/methods');
21 | jest.unmock('../../../../src/utils/component-will-update');
22 | }
23 |
--------------------------------------------------------------------------------
/tests/methods/component-will-update/utils/test-instance.tsx:
--------------------------------------------------------------------------------
1 | import { render } from '@testing-library/react';
2 | import * as React from 'react';
3 | import { ReactNComponent } from '../../../../src/components';
4 | import { ReactNComponentWillUpdate } from '../../../../src/methods';
5 |
6 |
7 |
8 | interface P {
9 | n: number;
10 | }
11 |
12 | type VoidFunction = () => void;
13 |
14 |
15 |
16 | export default function testUndefined(
17 | Component: typeof ReactNComponent,
18 | ): VoidFunction {
19 | return (): void => {
20 |
21 | const mockComponentWillUpdate: VoidFunction =
22 | jest.fn((): void => { });
23 |
24 | class TestComponent extends Component {
25 |
26 | componentWillUpdate = mockComponentWillUpdate;
27 |
28 | render() {
29 | return null;
30 | }
31 | }
32 |
33 | const testComponent = render()
34 | testComponent.rerender();
35 |
36 | expect(ReactNComponentWillUpdate).toHaveBeenCalledTimes(1);
37 | // expect(ReactNComponentWillUpdate).toHaveBeenCalledWith();
38 | // expect(componentWillUpdateInstance).toHaveBeenCalledTimes(1);
39 | // expect(componentWillUpdateInstance).toHaveBeenCalledWith();
40 | expect(mockComponentWillUpdate).toHaveBeenCalledTimes(1);
41 | expect(mockComponentWillUpdate).toHaveBeenCalledWith();
42 | };
43 | };
44 |
--------------------------------------------------------------------------------
/tests/methods/component-will-update/utils/test-prototype.tsx:
--------------------------------------------------------------------------------
1 | import { render } from '@testing-library/react';
2 | import * as React from 'react';
3 | import { ReactNComponent } from '../../../../src/components';
4 | import { ReactNComponentWillUpdate } from '../../../../src/methods';
5 | import {
6 | componentWillUpdatePrototype,
7 | } from '../../../../src/utils/component-will-update';
8 |
9 |
10 |
11 | interface P {
12 | n: number;
13 | }
14 |
15 | type VoidFunction = () => void;
16 |
17 |
18 |
19 | export default function testUndefined(
20 | Component: typeof ReactNComponent,
21 | ): VoidFunction {
22 | return (): void => {
23 |
24 | const mockComponentWillUpdate: VoidFunction = jest.fn();
25 |
26 | class TestComponent extends Component
{
27 | componentWillUpdate() {
28 | mockComponentWillUpdate();
29 | }
30 | render() {
31 | return null;
32 | }
33 | }
34 |
35 | const testComponent = render();
36 | testComponent.rerender();
37 |
38 | expect(ReactNComponentWillUpdate).toHaveBeenCalledTimes(1);
39 | // expect(ReactNComponentWillUpdate).toHaveBeenCalledWith();
40 | expect(componentWillUpdatePrototype).toHaveBeenCalledTimes(1);
41 | // expect(componentWillUpdatePrototype).toHaveBeenCalledWith();
42 | expect(mockComponentWillUpdate).toHaveBeenCalledTimes(1);
43 | };
44 | };
45 |
--------------------------------------------------------------------------------
/tests/methods/component-will-update/utils/test-undefined.tsx:
--------------------------------------------------------------------------------
1 | import { render } from '@testing-library/react';
2 | import * as React from 'react';
3 | import { ReactNComponent } from '../../../../src/components';
4 | import { ReactNComponentWillUpdate } from '../../../../src/methods';
5 |
6 |
7 |
8 | interface P {
9 | n: number;
10 | }
11 |
12 | type VoidFunction = () => void;
13 |
14 |
15 |
16 | export default function testUndefined(
17 | Component: typeof ReactNComponent,
18 | ): VoidFunction {
19 | return (): void => {
20 |
21 | class TestComponent extends Component
{
22 | render() {
23 | return null;
24 | }
25 | }
26 |
27 | const testComponent = render()
28 | testComponent.rerender();
29 |
30 | expect(ReactNComponentWillUpdate).toHaveBeenCalledTimes(1);
31 | // expect(ReactNComponentWillUpdate).toHaveBeenCalledWith();
32 | };
33 | };
34 |
--------------------------------------------------------------------------------
/tests/object-get-listener.test.ts:
--------------------------------------------------------------------------------
1 | import objectGetListener from '../src/utils/object-get-listener';
2 |
3 | interface Shape {
4 | [key: string]: number;
5 | }
6 |
7 | describe('objectGetListener', (): void => {
8 |
9 | it('should return the same keys and values', (): void => {
10 |
11 | // Original object.
12 | const obj1: Shape = {
13 | x: 1,
14 | y: 2,
15 | z: 3,
16 | };
17 |
18 | // Subscribed object.
19 | const obj2 = objectGetListener(obj1, (): void => { });
20 |
21 | // Expect the shape to be the same.
22 | expect(obj1).toEqual(obj2);
23 | });
24 |
25 | it('should subscribe to the object properties\' get', (): void => {
26 |
27 | // Listener records which properties were read.
28 | const read = new Set();
29 | const listener = (key: string): void => {
30 | read.add(key);
31 | };
32 |
33 | // Create the objects.
34 | const obj: Shape = {
35 | x: 1,
36 | y: 2,
37 | z: 3,
38 | };
39 | const obj2 = objectGetListener(obj, listener);
40 | expect(obj2.y).toBe(2);
41 |
42 | // Expect only Y to be read.
43 | expect(read.has('x')).toBe(false);
44 | expect(read.has('y')).toBe(true);
45 | expect(read.has('z')).toBe(false);
46 | });
47 | });
48 |
--------------------------------------------------------------------------------
/tests/provider/add-callback.test.ts:
--------------------------------------------------------------------------------
1 | import createProvider from '../../src/create-provider';
2 | import ReactNProvider from '../../types/provider';
3 | import { hasContext } from '../utils/react-version';
4 | import spyOn from '../utils/spy-on-global-state-manager';
5 | import itShouldRequireContext from './utils/it-should-require-context';
6 |
7 |
8 |
9 | const CALLBACK = (): void => { };
10 |
11 |
12 |
13 | describe('Provider.addCallback', (): void => {
14 |
15 | // If Context is not supported,
16 | if (!hasContext) {
17 | itShouldRequireContext();
18 | return;
19 | }
20 |
21 |
22 |
23 | const spy = spyOn('addCallback', 'removeCallback');
24 |
25 | let Provider: ReactNProvider<{}>;
26 | beforeEach((): void => {
27 | Provider = createProvider();
28 | });
29 |
30 | it('should be a function with 1 argument', (): void => {
31 | expect(Provider.addCallback).toBeInstanceOf(Function);
32 | expect(Provider.addCallback).toHaveLength(1);
33 | });
34 |
35 | it('should call GlobalStateManager.addCallback', (): void => {
36 | Provider.addCallback(CALLBACK);
37 | expect(spy.addCallback).toHaveBeenCalledTimes(1);
38 | expect(spy.addCallback).toHaveBeenCalledWith(CALLBACK);
39 | });
40 |
41 | describe('return value', (): void => {
42 |
43 | let removeCallback: () => boolean;
44 |
45 | beforeEach((): void => {
46 | removeCallback = Provider.addCallback(CALLBACK);
47 | });
48 |
49 | it('should be a function with no arguments', (): void => {
50 | expect(removeCallback).toBeInstanceOf(Function);
51 | expect(removeCallback).toHaveLength(0);
52 | });
53 |
54 | it('should call GlobalStateManager.removeCallback', (): void => {
55 | removeCallback();
56 | expect(spy.removeCallback).toHaveBeenCalledTimes(1);
57 | expect(spy.removeCallback).toHaveBeenCalledWith(CALLBACK);
58 | });
59 |
60 | it('should return true', (): void => {
61 | expect(removeCallback()).toBe(true);
62 | });
63 | });
64 | });
65 |
--------------------------------------------------------------------------------
/tests/provider/add-reducer.test.ts:
--------------------------------------------------------------------------------
1 | import createProvider from '../../src/create-provider';
2 | import ReactNProvider from '../../types/provider';
3 | import { hasContext } from '../utils/react-version';
4 | import spyOn from '../utils/spy-on-global-state-manager';
5 | import itShouldRequireContext from './utils/it-should-require-context';
6 |
7 |
8 |
9 | const REDUCER = (): void => {};
10 |
11 | const REDUCER_NAME = 'REDUCER_NAME';
12 |
13 |
14 |
15 | describe('Provider.addReducer', (): void => {
16 |
17 | // If Context is not supported,
18 | if (!hasContext) {
19 | itShouldRequireContext();
20 | return;
21 | }
22 |
23 |
24 |
25 | const spy = spyOn('addReducer', 'removeDispatcher');
26 |
27 | let Provider: ReactNProvider<{}>;
28 | beforeEach((): void => {
29 | Provider = createProvider();
30 | });
31 |
32 |
33 |
34 | it('should be a function with 2 arguments', (): void => {
35 | expect(Provider.addReducer).toBeInstanceOf(Function);
36 | expect(Provider.addReducer).toHaveLength(2);
37 | });
38 |
39 | it('should call GlobalStateManager.addReducer', (): void => {
40 | Provider.addReducer(REDUCER_NAME, REDUCER);
41 | expect(spy.addReducer).toHaveBeenCalledTimes(1);
42 | expect(spy.addReducer).toHaveBeenCalledWith(REDUCER_NAME, REDUCER);
43 | });
44 |
45 |
46 |
47 | describe('return value', (): void => {
48 |
49 | let removeReducer: () => boolean;
50 | beforeEach((): void => {
51 | removeReducer = Provider.addReducer(REDUCER_NAME, REDUCER);
52 | });
53 |
54 | it('should be a function with no arguments', (): void => {
55 | expect(removeReducer).toBeInstanceOf(Function);
56 | expect(removeReducer).toHaveLength(0);
57 | });
58 |
59 | it('should call GlobalStateManager.removeDispatcher', (): void => {
60 | removeReducer();
61 | expect(spy.removeDispatcher).toHaveBeenCalledTimes(1);
62 | expect(spy.removeDispatcher).toHaveBeenCalledWith(REDUCER_NAME);
63 | });
64 |
65 | it('should return true', (): void => {
66 | expect(removeReducer()).toBe(true);
67 | });
68 | });
69 |
70 | });
71 |
--------------------------------------------------------------------------------
/tests/provider/add-reducers.test.ts:
--------------------------------------------------------------------------------
1 | import createProvider from '../../src/create-provider';
2 | import ReactNProvider from '../../types/provider';
3 | import Reducer from '../../types/reducer';
4 | import { G, INITIAL_REDUCERS, INITIAL_STATE } from '../utils/initial';
5 | import { hasContext } from '../utils/react-version';
6 | import spyOn from '../utils/spy-on-global-state-manager';
7 | import itShouldRequireContext from './utils/it-should-require-context';
8 |
9 |
10 |
11 | const REDUCER_NAMES: string[] = Object.keys(INITIAL_REDUCERS);
12 |
13 | const REDUCERS: [ string, Reducer ][] = Object.entries(INITIAL_REDUCERS);
14 |
15 |
16 |
17 | describe('Provider.addReducers', (): void => {
18 |
19 | // If Context is not supported,
20 | if (!hasContext) {
21 | itShouldRequireContext();
22 | return;
23 | }
24 |
25 |
26 |
27 | const spy = spyOn('addReducer', 'removeDispatcher');
28 |
29 | let Provider: ReactNProvider;
30 | beforeEach((): void => {
31 | Provider = createProvider(INITIAL_STATE);
32 | });
33 |
34 |
35 |
36 | it('should be a function with 1 arguments', (): void => {
37 | expect(Provider.addReducers).toBeInstanceOf(Function);
38 | expect(Provider.addReducers).toHaveLength(1);
39 | });
40 |
41 | it(
42 | 'should call GlobalStateManager.addReducer for each reducer',
43 | (): void => {
44 | Provider.addReducers(INITIAL_REDUCERS);
45 | expect(spy.addReducer).toHaveBeenCalledTimes(REDUCERS.length);
46 | for (const [ name, reducer ] of REDUCERS) {
47 | expect(spy.addReducer).toHaveBeenCalledWith(name, reducer);
48 | }
49 | }
50 | );
51 |
52 |
53 |
54 | describe('return remove reducers function', (): void => {
55 |
56 | let removeReducers: () => boolean;
57 | beforeEach((): void => {
58 | removeReducers = Provider.addReducers(INITIAL_REDUCERS);
59 | });
60 |
61 |
62 |
63 | it('should be a function with no arguments', (): void => {
64 | expect(removeReducers).toBeInstanceOf(Function);
65 | expect(removeReducers).toHaveLength(0);
66 | });
67 |
68 | it(
69 | 'should call GlobalStateManager.removeDispatcher for each reducer',
70 | (): void => {
71 | removeReducers();
72 | expect(spy.removeDispatcher).toHaveBeenCalledTimes(REDUCER_NAMES.length);
73 | for (const reducerName of REDUCER_NAMES) {
74 | expect(spy.removeDispatcher).toHaveBeenCalledWith(reducerName);
75 | }
76 | }
77 | );
78 |
79 | it('should return true', (): void => {
80 | expect(removeReducers()).toBe(true);
81 | });
82 | });
83 |
84 | });
85 |
--------------------------------------------------------------------------------
/tests/provider/dispatch.test.ts:
--------------------------------------------------------------------------------
1 | import createProvider from '../../src/create-provider';
2 | import ReactNProvider from '../../types/provider';
3 | import { G, INITIAL_REDUCERS, INITIAL_STATE, R } from '../utils/initial';
4 | import { hasContext } from '../utils/react-version';
5 | import itShouldRequireContext from './utils/it-should-require-context';
6 |
7 |
8 |
9 | const INITIAL_REDUCERS_KEYS: string[] = Object.keys(INITIAL_REDUCERS);
10 |
11 | INITIAL_REDUCERS_KEYS.sort();
12 |
13 |
14 |
15 | describe('Provider.dispatch', (): void => {
16 |
17 | // If Context is not supported,
18 | if (!hasContext) {
19 | itShouldRequireContext();
20 | return;
21 | }
22 |
23 |
24 |
25 | let Provider: ReactNProvider;
26 | beforeEach((): void => {
27 | Provider = createProvider(INITIAL_STATE, INITIAL_REDUCERS);
28 | });
29 |
30 |
31 |
32 | it('should be an object of functions', (): void => {
33 | // Provider.dispatch extends Object.create(null), so it has no prototype.
34 | expect(Provider.dispatch).toEqual(expect.any(Object));
35 | for (const dispatcher of Object.values(Provider.dispatch)) {
36 | expect(dispatcher).toBeInstanceOf(Function);
37 | }
38 | });
39 |
40 | it('should not have a setter', (): void => {
41 | expect((): void => {
42 | // @ts-ignore: Deliberately throwing an error.
43 | Provider.dispatch = true;
44 | }).toThrow();
45 | });
46 |
47 | it('should share keys with reducers', (): void => {
48 | const dispatchKeys: string[] = Object.keys(Provider.dispatch);
49 | dispatchKeys.sort();
50 | expect(dispatchKeys).toStrictEqual(INITIAL_REDUCERS_KEYS);
51 | });
52 |
53 | it('should update when reducers update', (): void => {
54 | const REDUCER = (): void => { };
55 | const REDUCER_NAME = 'REDUCER_NAME';
56 |
57 | expect(Provider.dispatch[REDUCER_NAME]).toBeUndefined();
58 | Provider.addReducer(REDUCER_NAME, REDUCER);
59 | expect(Provider.dispatch[REDUCER_NAME]).toBeInstanceOf(Function);
60 | });
61 | });
62 |
--------------------------------------------------------------------------------
/tests/provider/get-dispatch.test.ts:
--------------------------------------------------------------------------------
1 | import createProvider from '../../src/create-provider';
2 | import ReactNProvider from '../../types/provider';
3 | import { G, INITIAL_REDUCERS, INITIAL_STATE, R } from '../utils/initial';
4 | import { hasContext } from '../utils/react-version';
5 | import itShouldRequireContext from './utils/it-should-require-context';
6 |
7 |
8 |
9 | describe('Provider.getDispatch', (): void => {
10 |
11 | // If Context is not supported,
12 | if (!hasContext) {
13 | itShouldRequireContext();
14 | return;
15 | }
16 |
17 |
18 |
19 | let Provider: ReactNProvider;
20 | beforeEach((): void => {
21 | Provider = createProvider(INITIAL_STATE, INITIAL_REDUCERS);
22 | });
23 |
24 |
25 |
26 | it('should be a function with no arguments', (): void => {
27 | expect(Provider.getDispatch).toBeInstanceOf(Function);
28 | expect(Provider.getDispatch).toHaveLength(0);
29 | });
30 |
31 | it('should return dispatch', (): void => {
32 | expect(Provider.getDispatch()).toStrictEqual(Provider.dispatch);
33 | });
34 | });
35 |
--------------------------------------------------------------------------------
/tests/provider/get-global.test.ts:
--------------------------------------------------------------------------------
1 | import createProvider from '../../src/create-provider';
2 | import ReactNProvider from '../../types/provider';
3 | import { G, INITIAL_STATE } from '../utils/initial';
4 | import { hasContext } from '../utils/react-version';
5 | import itShouldRequireContext from './utils/it-should-require-context';
6 |
7 |
8 |
9 | describe('Provider.getGlobal', (): void => {
10 |
11 | // If Context is not supported,
12 | if (!hasContext) {
13 | itShouldRequireContext();
14 | return;
15 | }
16 |
17 |
18 |
19 | let Provider: ReactNProvider;
20 | beforeEach((): void => {
21 | Provider = createProvider(INITIAL_STATE);
22 | });
23 |
24 |
25 |
26 | it('should be a function with no arguments', (): void => {
27 | expect(Provider.getGlobal).toBeInstanceOf(Function);
28 | expect(Provider.getGlobal).toHaveLength(0);
29 | });
30 |
31 | it('should return global', (): void => {
32 | expect(Provider.getGlobal()).toStrictEqual(Provider.global);
33 | });
34 | });
35 |
--------------------------------------------------------------------------------
/tests/provider/global.test.ts:
--------------------------------------------------------------------------------
1 | import createProvider from '../../src/create-provider';
2 | import ReactNProvider from '../../types/provider';
3 | import { G, INITIAL_STATE } from '../utils/initial';
4 | import { hasContext } from '../utils/react-version';
5 | import spyOn from '../utils/spy-on-global-state-manager';
6 | import itShouldRequireContext from './utils/it-should-require-context';
7 |
8 |
9 |
10 | describe('Provider.global', (): void => {
11 |
12 | // If Context is not supported,
13 | if (!hasContext) {
14 | itShouldRequireContext();
15 | return;
16 | }
17 |
18 |
19 |
20 | let Provider: ReactNProvider;
21 | const spy = spyOn('state');
22 | beforeEach((): void => {
23 | Provider = createProvider(INITIAL_STATE);
24 | });
25 |
26 |
27 |
28 | it('should be an object', (): void => {
29 | // Provider.global extends Object.create(null), so it has no prototype.
30 | expect(Provider.global).toEqual(expect.any(Object));
31 | });
32 |
33 | it('should not have a setter', (): void => {
34 | expect((): void => {
35 | // @ts-ignore: Deliberately throwing an error.
36 | Provider.global = true;
37 | }).toThrow();
38 | });
39 |
40 | it('should call GlobalStateManager.state', (): void => {
41 | Provider.global;
42 | expect(spy.state).toHaveBeenCalledTimes(1);
43 | expect(spy.state).toHaveBeenCalledWith();
44 | });
45 |
46 | it('should return a copy of the state', (): void => {
47 | const state: G = Provider.global;
48 | expect(state).toEqual(INITIAL_STATE);
49 | expect(state).not.toBe(INITIAL_STATE);
50 | });
51 |
52 | it('should update when the state updates', async (): Promise => {
53 | const STRING = 'create-provider.test.global';
54 |
55 | expect(Provider.global.z).not.toBe(STRING);
56 | await Provider.setGlobal({
57 | z: STRING,
58 | });
59 | expect(Provider.global.z).toBe(STRING);
60 | });
61 | });
62 |
--------------------------------------------------------------------------------
/tests/provider/remove-callback.test.ts:
--------------------------------------------------------------------------------
1 | import createProvider from '../../src/create-provider';
2 | import ReactNProvider from '../../types/provider';
3 | import { hasContext } from '../utils/react-version';
4 | import spyOn from '../utils/spy-on-global-state-manager';
5 | import itShouldRequireContext from './utils/it-should-require-context';
6 |
7 |
8 |
9 | const CALLBACK = (): void => { };
10 |
11 |
12 |
13 | describe('Provider.removeCallback', (): void => {
14 |
15 | // If Context is not supported,
16 | if (!hasContext) {
17 | itShouldRequireContext();
18 | return;
19 | }
20 |
21 |
22 |
23 | let Provider: ReactNProvider;
24 | const spy = spyOn('removeCallback');
25 | beforeEach((): void => {
26 | Provider = createProvider();
27 | });
28 |
29 |
30 |
31 | it('should be a function with 1 argument', (): void => {
32 | expect(Provider.removeCallback).toBeInstanceOf(Function);
33 | expect(Provider.removeCallback).toHaveLength(1);
34 | });
35 |
36 | it('should call GlobalStateManager.removeCallback', () => {
37 | Provider.removeCallback(CALLBACK);
38 | expect(spy.removeCallback).toHaveBeenCalledTimes(1);
39 | expect(spy.removeCallback).toHaveBeenCalledWith(CALLBACK);
40 | });
41 |
42 | it('should return true if the callback existed', (): void => {
43 | Provider.addCallback(CALLBACK);
44 | expect(Provider.removeCallback(CALLBACK)).toBe(true);
45 | });
46 |
47 | it('should return false if the callback did not exist', (): void => {
48 | expect(Provider.removeCallback(CALLBACK)).toBe(false);
49 | });
50 | });
51 |
--------------------------------------------------------------------------------
/tests/provider/reset.test.ts:
--------------------------------------------------------------------------------
1 | import createProvider from '../../src/create-provider';
2 | import ReactNProvider from '../../types/provider';
3 | import { hasContext } from '../utils/react-version';
4 | import spyOn from '../utils/spy-on-global-state-manager';
5 | import itShouldRequireContext from './utils/it-should-require-context';
6 |
7 |
8 |
9 | describe('Provider.reset', (): void => {
10 |
11 | // If Context is not supported,
12 | if (!hasContext) {
13 | itShouldRequireContext();
14 | return;
15 | }
16 |
17 |
18 |
19 | let Provider: ReactNProvider;
20 | const spy = spyOn('reset');
21 | beforeEach((): void => {
22 | Provider = createProvider();
23 | });
24 |
25 |
26 |
27 | it('should be a function with no arguments', (): void => {
28 | expect(Provider.reset).toBeInstanceOf(Function);
29 | expect(Provider.reset).toHaveLength(0);
30 | });
31 |
32 | it('should call GlobalStateManager.reset', (): void => {
33 | Provider.reset();
34 | expect(spy.reset).toHaveBeenCalledTimes(1);
35 | expect(spy.reset).toHaveBeenCalledWith();
36 | });
37 |
38 | it('should return undefined', (): void => {
39 | expect(Provider.reset()).toBeUndefined();
40 | });
41 | });
42 |
--------------------------------------------------------------------------------
/tests/provider/set-global.test.ts:
--------------------------------------------------------------------------------
1 | import createProvider from '../../src/create-provider';
2 | import DispatchFunction from '../../types/dispatch-function';
3 | import Dispatchers from '../../types/dispatchers';
4 | import ReactNProvider from '../../types/provider';
5 | import { G, INITIAL_REDUCERS, INITIAL_STATE, R } from '../utils/initial';
6 | import { hasContext } from '../utils/react-version';
7 | import spyOn from '../utils/spy-on-global-state-manager';
8 | import itShouldRequireContext from './utils/it-should-require-context';
9 |
10 |
11 |
12 | const STATE_CHANGE: Partial = {
13 | x: !INITIAL_STATE.x,
14 | };
15 |
16 | const NEW_STATE: G = {
17 | ...INITIAL_STATE,
18 | ...STATE_CHANGE,
19 | };
20 |
21 |
22 |
23 | describe('Provider.setGlobal', (): void => {
24 |
25 | // If Context is not supported,
26 | if (!hasContext) {
27 | itShouldRequireContext();
28 | return;
29 | }
30 |
31 |
32 |
33 | let Provider: ReactNProvider;
34 | beforeEach((): void => {
35 | Provider = createProvider(INITIAL_STATE, INITIAL_REDUCERS);
36 | });
37 |
38 |
39 |
40 | it('should be a function with 2 arguments', (): void => {
41 | expect(Provider.setGlobal).toBeInstanceOf(Function);
42 | expect(Provider.setGlobal).toHaveLength(2);
43 | });
44 |
45 | it(
46 | 'should execute a callback with the new state',
47 | async (): Promise => {
48 | const CALLBACK: jest.Mock & Dispatchers]> = jest.fn();
49 | await Provider.setGlobal(STATE_CHANGE, CALLBACK);
50 | expect(CALLBACK).toHaveBeenCalledTimes(1);
51 | expect(CALLBACK)
52 | .toHaveBeenCalledWith(NEW_STATE, expect.anything(), STATE_CHANGE);
53 | expect(CALLBACK.mock.calls[0][1].toString())
54 | .toBe(Provider.dispatcherMap.toString());
55 | }
56 | );
57 |
58 |
59 |
60 | describe('GlobalStateManager.set', (): void => {
61 |
62 | const spy = spyOn('set');
63 |
64 | it('should be called without a callback', async (): Promise => {
65 | await Provider.setGlobal(STATE_CHANGE);
66 | expect(spy.set).toHaveBeenCalledTimes(1);
67 | expect(spy.set).toHaveBeenCalledWith(STATE_CHANGE);
68 | });
69 |
70 | it('should be called with a callback', async (): Promise => {
71 | const NOOP = (): void => { };
72 | await Provider.setGlobal(STATE_CHANGE, NOOP);
73 | expect(spy.set).toHaveBeenCalledTimes(2);
74 | expect(spy.set).toHaveBeenNthCalledWith(1, STATE_CHANGE);
75 | expect(spy.set).toHaveBeenNthCalledWith(2, undefined);
76 | });
77 | });
78 |
79 |
80 |
81 | describe('return value', (): void => {
82 |
83 | it(
84 | 'should be a Promise of the new global state if there was a callback',
85 | async (): Promise => {
86 | const set: Promise = Provider.setGlobal(STATE_CHANGE);
87 | expect(set).toBeInstanceOf(Promise);
88 | const value: G = await set;
89 | expect(value).toEqual(NEW_STATE);
90 | }
91 | );
92 |
93 | it(
94 | 'should be a Promise of the new global state ' +
95 | 'if there was not a callback',
96 | async (): Promise => {
97 | const NOOP = (): void => { };
98 | const set: Promise = Provider.setGlobal(STATE_CHANGE, NOOP);
99 | expect(set).toBeInstanceOf(Promise);
100 | const value: G = await set;
101 | expect(value).toEqual(NEW_STATE);
102 | }
103 | );
104 | });
105 |
106 | });
107 |
--------------------------------------------------------------------------------
/tests/provider/use-dispatch.ts:
--------------------------------------------------------------------------------
1 | import createProvider from '../../src/create-provider';
2 | import ReactNProvider from '../../types/provider';
3 | import { G, INITIAL_REDUCERS, INITIAL_STATE, R } from '../utils/initial';
4 | import { hasContext } from '../utils/react-version';
5 | import itShouldRequireContext from './utils/it-should-require-context';
6 |
7 |
8 |
9 | describe('Provider.useDispatch', (): void => {
10 |
11 | // If Context is not supported,
12 | if (!hasContext) {
13 | itShouldRequireContext();
14 | return;
15 | }
16 |
17 |
18 |
19 | let Provider: ReactNProvider;
20 | beforeEach((): void => {
21 | Provider = createProvider(INITIAL_STATE, INITIAL_REDUCERS);
22 | });
23 |
24 |
25 |
26 | it('should be a function with 2 arguments', (): void => {
27 | expect(Provider.useDispatch).toBeInstanceOf(Function);
28 | expect(Provider.useDispatch).toHaveLength(2);
29 | });
30 |
31 | it.skip('should call the useDispatch helper function', (): void => {
32 | });
33 | });
34 |
--------------------------------------------------------------------------------
/tests/provider/use-global.test.ts:
--------------------------------------------------------------------------------
1 | import createProvider from '../../src/create-provider';
2 | import ReactNProvider from '../../types/provider';
3 | import { G, INITIAL_REDUCERS, INITIAL_STATE, R } from '../utils/initial';
4 | import { hasContext } from '../utils/react-version';
5 | import itShouldRequireContext from './utils/it-should-require-context';
6 |
7 |
8 |
9 | describe('Provider.useGlobal', (): void => {
10 |
11 | // If Context is not supported,
12 | if (!hasContext) {
13 | itShouldRequireContext();
14 | return;
15 | }
16 |
17 |
18 |
19 | let Provider: ReactNProvider;
20 | beforeEach((): void => {
21 | Provider = createProvider(INITIAL_STATE, INITIAL_REDUCERS);
22 | });
23 |
24 |
25 |
26 | it('should be a function with 1 arguments', (): void => {
27 | expect(Provider.useGlobal).toBeInstanceOf(Function);
28 | expect(Provider.useGlobal).toHaveLength(1);
29 | });
30 |
31 | it.skip('should call the useGlobal helper function', (): void => {
32 | });
33 | });
34 |
--------------------------------------------------------------------------------
/tests/provider/utils/it-should-require-context.ts:
--------------------------------------------------------------------------------
1 | import createProvider from '../../../src/create-provider';
2 | import REACT_CONTEXT_ERROR from '../../../src/utils/react-context-error';
3 |
4 | export default function itShouldRequireContext(): void {
5 | it('should require Context', (): void => {
6 | expect((): void => {
7 | createProvider();
8 | }).toThrowError(REACT_CONTEXT_ERROR);
9 | });
10 | }
11 |
--------------------------------------------------------------------------------
/tests/provider/with-global.test.tsx:
--------------------------------------------------------------------------------
1 | import { render } from '@testing-library/react';
2 | import * as React from 'react';
3 | import createProvider from '../../src/create-provider';
4 | import Dispatcher, { ExtractArguments } from '../../types/dispatcher';
5 | import Dispatchers from '../../types/dispatchers';
6 | import ReactNProvider from '../../types/provider';
7 | import { Getter, Setter } from '../../types/with-global';
8 | import { G, INITIAL_REDUCERS, INITIAL_STATE, R } from '../utils/initial';
9 | import { hasContext } from '../utils/react-version';
10 | import itShouldRequireContext from './utils/it-should-require-context';
11 |
12 |
13 |
14 | interface DispatchProps {
15 | increment: Dispatcher>;
16 | }
17 |
18 | interface GlobalProps {
19 | z: string;
20 | }
21 |
22 | type Props = Partial;
23 |
24 |
25 |
26 | const DISPATCH_INNER_TEXT: string = 'function';
27 |
28 | const EMPTY_GETTER: Getter = (): null => null;
29 |
30 | const GLOBAL_INNER_TEXT: string = `undefined ${INITIAL_STATE.z}`;
31 |
32 | const mapDispatchToProps: Setter =
33 | (_setGlobal, { increment }: Dispatchers): DispatchProps => ({
34 | increment,
35 | });
36 |
37 | const mapGlobalToProps: Getter =
38 | ({ z }: G): GlobalProps => ({
39 | z,
40 | });
41 |
42 | const TestComponent = ({ increment, z }: Props): JSX.Element => {
43 | return {typeof increment} {z};
44 | };
45 |
46 |
47 |
48 | describe('Provider.withGlobal', (): void => {
49 |
50 | // If Context is not supported,
51 | if (!hasContext) {
52 | itShouldRequireContext();
53 | return;
54 | }
55 |
56 |
57 |
58 | let Provider: ReactNProvider;
59 | beforeEach((): void => {
60 | Provider = createProvider(INITIAL_STATE, INITIAL_REDUCERS);
61 | });
62 |
63 |
64 |
65 | it('should be a function with 2 arguments', (): void => {
66 | expect(Provider.withGlobal).toBeInstanceOf(Function);
67 | expect(Provider.withGlobal).toHaveLength(2);
68 | });
69 |
70 | it('should map global to props', (): void => {
71 | const WrappedTestComponent: React.ComponentType<{}> =
72 | Provider.withGlobal(mapGlobalToProps)(TestComponent);
73 | const { getByTestId } = render();
74 | // @ts-ignore:
75 | // Argument of type '"test"' is not assignable to parameter of type
76 | // 'MatcherOptions'.
77 | // Property 'toHaveTextContent' does not exist on type
78 | // 'Matchers'.
79 | expect(getByTestId('test')).toHaveTextContent(GLOBAL_INNER_TEXT);
80 | });
81 |
82 | it('should map dispatch to props', (): void => {
83 | const WrappedTestComponent: React.ComponentType<{}> =
84 | Provider.withGlobal(EMPTY_GETTER, mapDispatchToProps)(TestComponent);
85 | const { getByTestId } = render();
86 | // @ts-ignore:
87 | // Argument of type '"test"' is not assignable to parameter of type
88 | // 'MatcherOptions'.
89 | // Property 'toHaveTextContent' does not exist on type
90 | // 'Matchers'.
91 | expect(getByTestId('test')).toHaveTextContent(DISPATCH_INNER_TEXT);
92 | });
93 | });
94 |
--------------------------------------------------------------------------------
/tests/remove-callback.test.ts:
--------------------------------------------------------------------------------
1 | import addCallback from '../src/add-callback';
2 | import GlobalStateManager from '../src/global-state-manager';
3 | import removeCallback from '../src/remove-callback';
4 | import Callback from '../types/callback';
5 | import { G, INITIAL_STATE } from './utils/initial';
6 | import spyOn from './utils/spy-on-global-state-manager';
7 |
8 |
9 |
10 | const CALLBACK: Callback = (): void => { };
11 |
12 |
13 |
14 | describe('removeCallback', (): void => {
15 |
16 | let globalStateManager: GlobalStateManager;
17 | const spy = spyOn('removeCallback');
18 |
19 | beforeEach((): void => {
20 | globalStateManager = new GlobalStateManager(INITIAL_STATE);
21 | });
22 |
23 |
24 |
25 | it('should be a function with 2 arguments', (): void => {
26 | expect(removeCallback).toBeInstanceOf(Function);
27 | expect(removeCallback).toHaveLength(2);
28 | });
29 |
30 | it('should call GlobalStateManager.removeCallback', (): void => {
31 | removeCallback(globalStateManager, CALLBACK);
32 | expect(spy.removeCallback).toHaveBeenCalledTimes(1);
33 | expect(spy.removeCallback).toHaveBeenCalledWith(CALLBACK);
34 | });
35 |
36 | describe('return value', (): void => {
37 |
38 | it('should be true if the callback existed', (): void => {
39 | addCallback(globalStateManager, CALLBACK);
40 | expect(removeCallback(globalStateManager, CALLBACK)).toBe(true);
41 | });
42 |
43 | it('should be false if the callback did not exist', (): void => {
44 | expect(removeCallback(globalStateManager, CALLBACK)).toBe(false);
45 | });
46 | });
47 | });
48 |
--------------------------------------------------------------------------------
/tests/reset-global.test.ts:
--------------------------------------------------------------------------------
1 | import GlobalStateManager from '../src/global-state-manager';
2 | import resetGlobal from '../src/reset-global';
3 | import spyOn from './utils/spy-on-global-state-manager';
4 |
5 | describe('resetGlobal', (): void => {
6 |
7 | let globalStateManager: GlobalStateManager;
8 | const spy = spyOn('reset');
9 | beforeEach((): void => {
10 | globalStateManager = new GlobalStateManager();
11 | });
12 |
13 |
14 |
15 | it('should be a function with 1 argument', (): void => {
16 | expect(resetGlobal).toBeInstanceOf(Function);
17 | expect(resetGlobal).toHaveLength(1);
18 | });
19 |
20 | it('should call GlobalStateManager.reset', (): void => {
21 | resetGlobal(globalStateManager);
22 | expect(spy.reset).toHaveBeenCalledTimes(1);
23 | expect(spy.reset).toHaveBeenCalledWith();
24 | });
25 |
26 | it('should return undefined', (): void => {
27 | expect(resetGlobal(globalStateManager)).toBeUndefined();
28 | });
29 | });
30 |
--------------------------------------------------------------------------------
/tests/set-global.test.ts:
--------------------------------------------------------------------------------
1 | import GlobalStateManager from '../src/global-state-manager';
2 | import setGlobal from '../src/set-global';
3 | import DispatchFunction from '../types/dispatch-function';
4 | import Dispatchers from '../types/dispatchers';
5 | import { G, INITIAL_REDUCERS, INITIAL_STATE, R } from './utils/initial';
6 | import spyOn from './utils/spy-on-global-state-manager';
7 |
8 |
9 |
10 | const CALLBACK: jest.Mock & Dispatchers]> = jest.fn();
11 |
12 | const STATE_CHANGE: Partial = {
13 | x: true,
14 | };
15 |
16 | const NEW_STATE: G = {
17 | ...INITIAL_STATE,
18 | ...STATE_CHANGE,
19 | };
20 |
21 |
22 |
23 | describe('setGlobal', (): void => {
24 |
25 | let globalStateManager: GlobalStateManager;
26 | beforeEach((): void => {
27 | globalStateManager =
28 | new GlobalStateManager(INITIAL_STATE, INITIAL_REDUCERS);
29 | });
30 |
31 |
32 |
33 | it('should be a function with 3 arguments', (): void => {
34 | expect(setGlobal).toBeInstanceOf(Function);
35 | expect(setGlobal).toHaveLength(3);
36 | });
37 |
38 | it(
39 | 'should return a Promise of the new state if there is no callback',
40 | async (): Promise => {
41 | const p = setGlobal(globalStateManager, STATE_CHANGE);
42 | expect(p).toBeInstanceOf(Promise);
43 | const newGlobalState: G = await p;
44 | expect(newGlobalState).toStrictEqual(globalStateManager.state);
45 | }
46 | );
47 |
48 | it(
49 | 'should return a Promise of the new state if there is a callback',
50 | async (): Promise