mirror of
https://github.com/Telecominfraproject/wlan-cloud-ui.git
synced 2025-11-02 11:47:51 +00:00
switched to use Apollo
This commit is contained in:
@@ -1,28 +0,0 @@
|
|||||||
/*
|
|
||||||
* AppReducer
|
|
||||||
*
|
|
||||||
* The reducer takes care of our data. Using actions, we can
|
|
||||||
* update our application state. To add a new action,
|
|
||||||
* add it to the switch statement in the reducer function
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
import produce from 'immer';
|
|
||||||
|
|
||||||
// The initial state of the App
|
|
||||||
export const initialState = {
|
|
||||||
loading: false,
|
|
||||||
error: false,
|
|
||||||
currentUser: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
/* eslint-disable default-case, no-param-reassign */
|
|
||||||
const appReducer = (state = initialState, action) =>
|
|
||||||
produce(state, () => {
|
|
||||||
switch (action.type) {
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
export default appReducer;
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
/**
|
|
||||||
* The global state selectors
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { createSelector } from 'reselect';
|
|
||||||
import { initialState } from './reducer';
|
|
||||||
|
|
||||||
const selectGlobal = state => state.global || initialState;
|
|
||||||
|
|
||||||
const selectRouter = state => state.router;
|
|
||||||
|
|
||||||
const makeSelectCurrentUser = () =>
|
|
||||||
createSelector(
|
|
||||||
selectGlobal,
|
|
||||||
globalState => globalState.currentUser,
|
|
||||||
);
|
|
||||||
|
|
||||||
const makeSelectLoading = () =>
|
|
||||||
createSelector(
|
|
||||||
selectGlobal,
|
|
||||||
globalState => globalState.loading,
|
|
||||||
);
|
|
||||||
|
|
||||||
const makeSelectError = () =>
|
|
||||||
createSelector(
|
|
||||||
selectGlobal,
|
|
||||||
globalState => globalState.error,
|
|
||||||
);
|
|
||||||
|
|
||||||
const makeSelectLocation = () =>
|
|
||||||
createSelector(
|
|
||||||
selectRouter,
|
|
||||||
routerState => routerState.location,
|
|
||||||
);
|
|
||||||
|
|
||||||
export {
|
|
||||||
selectGlobal,
|
|
||||||
makeSelectCurrentUser,
|
|
||||||
makeSelectLoading,
|
|
||||||
makeSelectError,
|
|
||||||
makeSelectLocation,
|
|
||||||
};
|
|
||||||
@@ -1,31 +1,23 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { connect } from 'react-redux';
|
import { useLocation } from 'react-router-dom';
|
||||||
import { compose } from 'redux';
|
|
||||||
import { createStructuredSelector } from 'reselect';
|
|
||||||
|
|
||||||
import { AppLayout as Layout } from 'cu-ui';
|
import { AppLayout as Layout } from 'cu-ui';
|
||||||
|
|
||||||
import { makeSelectLocation } from 'containers/App/selectors';
|
|
||||||
|
|
||||||
import logo from 'images/logo-light.png';
|
import logo from 'images/logo-light.png';
|
||||||
import logoMobile from 'images/logoxmobile.jpg';
|
import logoMobile from 'images/logoxmobile.jpg';
|
||||||
|
|
||||||
const MasterLayout = ({ children, locationState }) => (
|
const MasterLayout = ({ children }) => {
|
||||||
<Layout logo={logo} logoMobile={logoMobile} onLogout={() => {}} locationState={locationState}>
|
const location = useLocation();
|
||||||
{children}
|
return (
|
||||||
</Layout>
|
<Layout company="ConnectUs" logo={logo} logoMobile={logoMobile} onLogout={() => {}} locationState={location}>
|
||||||
);
|
{children}
|
||||||
|
</Layout>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
MasterLayout.propTypes = {
|
MasterLayout.propTypes = {
|
||||||
children: PropTypes.node.isRequired,
|
children: PropTypes.node.isRequired,
|
||||||
locationState: PropTypes.instanceOf(Object).isRequired,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const mapStateToProps = createStructuredSelector({
|
export default MasterLayout;
|
||||||
locationState: makeSelectLocation(),
|
|
||||||
});
|
|
||||||
|
|
||||||
const withConnect = connect(mapStateToProps);
|
|
||||||
|
|
||||||
export default compose(withConnect)(MasterLayout);
|
|
||||||
|
|||||||
22
app/index.js
22
app/index.js
@@ -1,27 +1,27 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import ReactDOM from 'react-dom';
|
import ReactDOM from 'react-dom';
|
||||||
import { Provider } from 'react-redux';
|
import { BrowserRouter as Router } from 'react-router-dom';
|
||||||
import { ConnectedRouter } from 'connected-react-router';
|
import ApolloClient from 'apollo-boost';
|
||||||
|
import { ApolloProvider } from '@apollo/react-hooks';
|
||||||
|
|
||||||
import 'styles/antd.less';
|
import 'styles/antd.less';
|
||||||
import 'styles/index.scss';
|
import 'styles/index.scss';
|
||||||
|
|
||||||
import App from 'containers/App';
|
import App from 'containers/App';
|
||||||
import configureStore from 'store';
|
|
||||||
import history from 'utils/history';
|
|
||||||
|
|
||||||
// Create redux store with history
|
|
||||||
const initialState = {};
|
|
||||||
const store = configureStore(initialState, history);
|
|
||||||
const MOUNT_NODE = document.getElementById('root');
|
const MOUNT_NODE = document.getElementById('root');
|
||||||
|
|
||||||
|
const client = new ApolloClient({
|
||||||
|
// uri: "https://48p1r2roz4.sse.codesandbox.io"
|
||||||
|
});
|
||||||
|
|
||||||
const render = () => {
|
const render = () => {
|
||||||
ReactDOM.render(
|
ReactDOM.render(
|
||||||
<Provider store={store}>
|
<ApolloProvider client={client}>
|
||||||
<ConnectedRouter history={history}>
|
<Router>
|
||||||
<App />
|
<App />
|
||||||
</ConnectedRouter>
|
</Router>
|
||||||
</Provider>,
|
</ApolloProvider>,
|
||||||
MOUNT_NODE
|
MOUNT_NODE
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,23 +0,0 @@
|
|||||||
/**
|
|
||||||
* Combine all reducers in this file and export the combined reducers.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { combineReducers } from 'redux';
|
|
||||||
import { connectRouter } from 'connected-react-router';
|
|
||||||
|
|
||||||
import globalReducer from 'containers/App/reducer';
|
|
||||||
import history from 'utils/history';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Merges the main reducer with the router state and dynamically injected reducers
|
|
||||||
*/
|
|
||||||
|
|
||||||
export default function createReducer(injectedReducers = {}) {
|
|
||||||
const rootReducer = combineReducers({
|
|
||||||
global: globalReducer,
|
|
||||||
router: connectRouter(history),
|
|
||||||
...injectedReducers,
|
|
||||||
});
|
|
||||||
|
|
||||||
return rootReducer;
|
|
||||||
}
|
|
||||||
55
app/store.js
55
app/store.js
@@ -1,55 +0,0 @@
|
|||||||
/**
|
|
||||||
* Create the store with dynamic reducers
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { createStore, applyMiddleware, compose } from 'redux';
|
|
||||||
import { routerMiddleware } from 'connected-react-router';
|
|
||||||
import createSagaMiddleware from 'redux-saga';
|
|
||||||
import createReducer from './reducers';
|
|
||||||
|
|
||||||
export default function configureStore(initialState = {}, history) {
|
|
||||||
let composeEnhancers = compose;
|
|
||||||
const reduxSagaMonitorOptions = {};
|
|
||||||
|
|
||||||
// If Redux Dev Tools and Saga Dev Tools Extensions are installed, enable them
|
|
||||||
/* istanbul ignore next */
|
|
||||||
if (process.env.NODE_ENV !== 'production' && typeof window === 'object') {
|
|
||||||
/* eslint-disable no-underscore-dangle */
|
|
||||||
if (window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__)
|
|
||||||
composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({});
|
|
||||||
|
|
||||||
// NOTE: Uncomment the code below to restore support for Redux Saga
|
|
||||||
// Dev Tools once it supports redux-saga version 1.x.x
|
|
||||||
// if (window.__SAGA_MONITOR_EXTENSION__)
|
|
||||||
// reduxSagaMonitorOptions = {
|
|
||||||
// sagaMonitor: window.__SAGA_MONITOR_EXTENSION__,
|
|
||||||
// };
|
|
||||||
/* eslint-enable */
|
|
||||||
}
|
|
||||||
|
|
||||||
const sagaMiddleware = createSagaMiddleware(reduxSagaMonitorOptions);
|
|
||||||
|
|
||||||
// Create the store with two middlewares
|
|
||||||
// 1. sagaMiddleware: Makes redux-sagas work
|
|
||||||
// 2. routerMiddleware: Syncs the location/URL path to the state
|
|
||||||
const middlewares = [sagaMiddleware, routerMiddleware(history)];
|
|
||||||
|
|
||||||
const enhancers = [applyMiddleware(...middlewares)];
|
|
||||||
|
|
||||||
const store = createStore(createReducer(), initialState, composeEnhancers(...enhancers));
|
|
||||||
|
|
||||||
// Extensions
|
|
||||||
store.runSaga = sagaMiddleware.run;
|
|
||||||
store.injectedReducers = {}; // Reducer registry
|
|
||||||
store.injectedSagas = {}; // Saga registry
|
|
||||||
|
|
||||||
// Make reducers hot reloadable, see http://mxs.is/googmo
|
|
||||||
/* istanbul ignore next */
|
|
||||||
if (module.hot) {
|
|
||||||
module.hot.accept('./reducers', () => {
|
|
||||||
store.replaceReducer(createReducer(store.injectedReducers));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return store;
|
|
||||||
}
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
/**
|
|
||||||
* Test store addons
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { browserHistory } from 'react-router-dom';
|
|
||||||
import configureStore from '../configureStore';
|
|
||||||
|
|
||||||
describe('configureStore', () => {
|
|
||||||
let store;
|
|
||||||
|
|
||||||
beforeAll(() => {
|
|
||||||
store = configureStore({}, browserHistory);
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('injectedReducers', () => {
|
|
||||||
it('should contain an object for reducers', () => {
|
|
||||||
expect(typeof store.injectedReducers).toBe('object');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('injectedSagas', () => {
|
|
||||||
it('should contain an object for sagas', () => {
|
|
||||||
expect(typeof store.injectedSagas).toBe('object');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('runSaga', () => {
|
|
||||||
it('should contain a hook for `sagaMiddleware.run`', () => {
|
|
||||||
expect(typeof store.runSaga).toBe('function');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('configureStore params', () => {
|
|
||||||
it('should call window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__', () => {
|
|
||||||
/* eslint-disable no-underscore-dangle */
|
|
||||||
const compose = jest.fn();
|
|
||||||
window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ = () => compose;
|
|
||||||
configureStore(undefined, browserHistory);
|
|
||||||
expect(compose).toHaveBeenCalled();
|
|
||||||
/* eslint-enable */
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
import { conformsTo, isFunction, isObject } from 'lodash';
|
|
||||||
import invariant from 'invariant';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Validate the shape of redux store
|
|
||||||
*/
|
|
||||||
export default function checkStore(store) {
|
|
||||||
const shape = {
|
|
||||||
dispatch: isFunction,
|
|
||||||
subscribe: isFunction,
|
|
||||||
getState: isFunction,
|
|
||||||
replaceReducer: isFunction,
|
|
||||||
runSaga: isFunction,
|
|
||||||
injectedReducers: isObject,
|
|
||||||
injectedSagas: isObject,
|
|
||||||
};
|
|
||||||
invariant(
|
|
||||||
conformsTo(store, shape),
|
|
||||||
'(app/utils...) injectors: Expected a valid redux store',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
export const RESTART_ON_REMOUNT = '@@saga-injector/restart-on-remount';
|
|
||||||
export const DAEMON = '@@saga-injector/daemon';
|
|
||||||
export const ONCE_TILL_UNMOUNT = '@@saga-injector/once-till-unmount';
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
import { createBrowserHistory } from 'history';
|
|
||||||
|
|
||||||
const history = createBrowserHistory();
|
|
||||||
export default history;
|
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import hoistNonReactStatics from 'hoist-non-react-statics';
|
|
||||||
import { ReactReduxContext } from 'react-redux';
|
|
||||||
|
|
||||||
import getInjectors from './reducerInjectors';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Dynamically injects a reducer
|
|
||||||
*
|
|
||||||
* @param {string} key A key of the reducer
|
|
||||||
* @param {function} reducer A reducer that will be injected
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
export default ({ key, reducer }) => WrappedComponent => {
|
|
||||||
class ReducerInjector extends React.Component {
|
|
||||||
constructor(props, context) {
|
|
||||||
super(props, context);
|
|
||||||
|
|
||||||
getInjectors(context.store).injectReducer(key, reducer);
|
|
||||||
}
|
|
||||||
|
|
||||||
static WrappedComponent = WrappedComponent;
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return <WrappedComponent {...this.props} />;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ReducerInjector.displayName = `withReducer(${WrappedComponent.displayName ||
|
|
||||||
WrappedComponent.name ||
|
|
||||||
'Component'})`;
|
|
||||||
ReducerInjector.contextType = ReactReduxContext;
|
|
||||||
|
|
||||||
return hoistNonReactStatics(ReducerInjector, WrappedComponent);
|
|
||||||
};
|
|
||||||
|
|
||||||
const useInjectReducer = ({ key, reducer }) => {
|
|
||||||
const context = React.useContext(ReactReduxContext);
|
|
||||||
React.useEffect(() => {
|
|
||||||
getInjectors(context.store).injectReducer(key, reducer);
|
|
||||||
}, []);
|
|
||||||
};
|
|
||||||
|
|
||||||
export { useInjectReducer };
|
|
||||||
@@ -1,61 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import hoistNonReactStatics from 'hoist-non-react-statics';
|
|
||||||
import { ReactReduxContext } from 'react-redux';
|
|
||||||
|
|
||||||
import getInjectors from './sagaInjectors';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Dynamically injects a saga, passes component's props as saga arguments
|
|
||||||
*
|
|
||||||
* @param {string} key A key of the saga
|
|
||||||
* @param {function} saga A root saga that will be injected
|
|
||||||
* @param {string} [mode] By default (constants.DAEMON) the saga will be started
|
|
||||||
* on component mount and never canceled or started again. Another two options:
|
|
||||||
* - constants.RESTART_ON_REMOUNT — the saga will be started on component mount and
|
|
||||||
* cancelled with `task.cancel()` on component unmount for improved performance,
|
|
||||||
* - constants.ONCE_TILL_UNMOUNT — behaves like 'RESTART_ON_REMOUNT' but never runs it again.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
export default ({ key, saga, mode }) => WrappedComponent => {
|
|
||||||
class InjectSaga extends React.Component {
|
|
||||||
constructor(props, context) {
|
|
||||||
super(props, context);
|
|
||||||
|
|
||||||
this.injectors = getInjectors(context.store);
|
|
||||||
|
|
||||||
this.injectors.injectSaga(key, { saga, mode }, this.props);
|
|
||||||
}
|
|
||||||
|
|
||||||
componentWillUnmount() {
|
|
||||||
this.injectors.ejectSaga(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
static WrappedComponent = WrappedComponent;
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return <WrappedComponent {...this.props} />;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
InjectSaga.contextType = ReactReduxContext;
|
|
||||||
|
|
||||||
InjectSaga.displayName = `withSaga(${WrappedComponent.displayName ||
|
|
||||||
WrappedComponent.name ||
|
|
||||||
'Component'})`;
|
|
||||||
|
|
||||||
return hoistNonReactStatics(InjectSaga, WrappedComponent);
|
|
||||||
};
|
|
||||||
|
|
||||||
const useInjectSaga = ({ key, saga, mode }) => {
|
|
||||||
const context = React.useContext(ReactReduxContext);
|
|
||||||
React.useEffect(() => {
|
|
||||||
const injectors = getInjectors(context.store);
|
|
||||||
injectors.injectSaga(key, { saga, mode });
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
injectors.ejectSaga(key);
|
|
||||||
};
|
|
||||||
}, []);
|
|
||||||
};
|
|
||||||
|
|
||||||
export { useInjectSaga };
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
import invariant from 'invariant';
|
|
||||||
import { isEmpty, isFunction, isString } from 'lodash';
|
|
||||||
|
|
||||||
import checkStore from './checkStore';
|
|
||||||
import createReducer from '../reducers';
|
|
||||||
|
|
||||||
export function injectReducerFactory(store, isValid) {
|
|
||||||
return function injectReducer(key, reducer) {
|
|
||||||
if (!isValid) checkStore(store);
|
|
||||||
|
|
||||||
invariant(
|
|
||||||
isString(key) && !isEmpty(key) && isFunction(reducer),
|
|
||||||
'(app/utils...) injectReducer: Expected `reducer` to be a reducer function',
|
|
||||||
);
|
|
||||||
|
|
||||||
// Check `store.injectedReducers[key] === reducer` for hot reloading when a key is the same but a reducer is different
|
|
||||||
if (
|
|
||||||
Reflect.has(store.injectedReducers, key) &&
|
|
||||||
store.injectedReducers[key] === reducer
|
|
||||||
)
|
|
||||||
return;
|
|
||||||
|
|
||||||
store.injectedReducers[key] = reducer; // eslint-disable-line no-param-reassign
|
|
||||||
store.replaceReducer(createReducer(store.injectedReducers));
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function getInjectors(store) {
|
|
||||||
checkStore(store);
|
|
||||||
|
|
||||||
return {
|
|
||||||
injectReducer: injectReducerFactory(store, true),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,91 +0,0 @@
|
|||||||
import invariant from 'invariant';
|
|
||||||
import { isEmpty, isFunction, isString, conformsTo } from 'lodash';
|
|
||||||
|
|
||||||
import checkStore from './checkStore';
|
|
||||||
import { DAEMON, ONCE_TILL_UNMOUNT, RESTART_ON_REMOUNT } from './constants';
|
|
||||||
|
|
||||||
const allowedModes = [RESTART_ON_REMOUNT, DAEMON, ONCE_TILL_UNMOUNT];
|
|
||||||
|
|
||||||
const checkKey = key =>
|
|
||||||
invariant(
|
|
||||||
isString(key) && !isEmpty(key),
|
|
||||||
'(app/utils...) injectSaga: Expected `key` to be a non empty string',
|
|
||||||
);
|
|
||||||
|
|
||||||
const checkDescriptor = descriptor => {
|
|
||||||
const shape = {
|
|
||||||
saga: isFunction,
|
|
||||||
mode: mode => isString(mode) && allowedModes.includes(mode),
|
|
||||||
};
|
|
||||||
invariant(
|
|
||||||
conformsTo(descriptor, shape),
|
|
||||||
'(app/utils...) injectSaga: Expected a valid saga descriptor',
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export function injectSagaFactory(store, isValid) {
|
|
||||||
return function injectSaga(key, descriptor = {}, args) {
|
|
||||||
if (!isValid) checkStore(store);
|
|
||||||
|
|
||||||
const newDescriptor = {
|
|
||||||
...descriptor,
|
|
||||||
mode: descriptor.mode || DAEMON,
|
|
||||||
};
|
|
||||||
const { saga, mode } = newDescriptor;
|
|
||||||
|
|
||||||
checkKey(key);
|
|
||||||
checkDescriptor(newDescriptor);
|
|
||||||
|
|
||||||
let hasSaga = Reflect.has(store.injectedSagas, key);
|
|
||||||
|
|
||||||
if (process.env.NODE_ENV !== 'production') {
|
|
||||||
const oldDescriptor = store.injectedSagas[key];
|
|
||||||
// enable hot reloading of daemon and once-till-unmount sagas
|
|
||||||
if (hasSaga && oldDescriptor.saga !== saga) {
|
|
||||||
oldDescriptor.task.cancel();
|
|
||||||
hasSaga = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
!hasSaga ||
|
|
||||||
(hasSaga && mode !== DAEMON && mode !== ONCE_TILL_UNMOUNT)
|
|
||||||
) {
|
|
||||||
/* eslint-disable no-param-reassign */
|
|
||||||
store.injectedSagas[key] = {
|
|
||||||
...newDescriptor,
|
|
||||||
task: store.runSaga(saga, args),
|
|
||||||
};
|
|
||||||
/* eslint-enable no-param-reassign */
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function ejectSagaFactory(store, isValid) {
|
|
||||||
return function ejectSaga(key) {
|
|
||||||
if (!isValid) checkStore(store);
|
|
||||||
|
|
||||||
checkKey(key);
|
|
||||||
|
|
||||||
if (Reflect.has(store.injectedSagas, key)) {
|
|
||||||
const descriptor = store.injectedSagas[key];
|
|
||||||
if (descriptor.mode && descriptor.mode !== DAEMON) {
|
|
||||||
descriptor.task.cancel();
|
|
||||||
// Clean up in production; in development we need `descriptor.saga` for hot reloading
|
|
||||||
if (process.env.NODE_ENV === 'production') {
|
|
||||||
// Need some value to be able to detect `ONCE_TILL_UNMOUNT` sagas in `injectSaga`
|
|
||||||
store.injectedSagas[key] = 'done'; // eslint-disable-line no-param-reassign
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function getInjectors(store) {
|
|
||||||
checkStore(store);
|
|
||||||
|
|
||||||
return {
|
|
||||||
injectSaga: injectSagaFactory(store, true),
|
|
||||||
ejectSaga: ejectSagaFactory(store, true),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
/**
|
|
||||||
* Test injectors
|
|
||||||
*/
|
|
||||||
|
|
||||||
import checkStore from '../checkStore';
|
|
||||||
|
|
||||||
describe('checkStore', () => {
|
|
||||||
let store;
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
store = {
|
|
||||||
dispatch: () => {},
|
|
||||||
subscribe: () => {},
|
|
||||||
getState: () => {},
|
|
||||||
replaceReducer: () => {},
|
|
||||||
runSaga: () => {},
|
|
||||||
injectedReducers: {},
|
|
||||||
injectedSagas: {},
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not throw if passed valid store shape', () => {
|
|
||||||
expect(() => checkStore(store)).not.toThrow();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should throw if passed invalid store shape', () => {
|
|
||||||
expect(() => checkStore({})).toThrow();
|
|
||||||
expect(() => checkStore({ ...store, injectedSagas: null })).toThrow();
|
|
||||||
expect(() => checkStore({ ...store, injectedReducers: null })).toThrow();
|
|
||||||
expect(() => checkStore({ ...store, runSaga: null })).toThrow();
|
|
||||||
expect(() => checkStore({ ...store, replaceReducer: null })).toThrow();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,98 +0,0 @@
|
|||||||
/**
|
|
||||||
* Test injectors
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { memoryHistory } from 'react-router-dom';
|
|
||||||
import React from 'react';
|
|
||||||
import { Provider } from 'react-redux';
|
|
||||||
import renderer from 'react-test-renderer';
|
|
||||||
import { render } from 'react-testing-library';
|
|
||||||
|
|
||||||
import configureStore from '../../configureStore';
|
|
||||||
import injectReducer, { useInjectReducer } from '../injectReducer';
|
|
||||||
import * as reducerInjectors from '../reducerInjectors';
|
|
||||||
|
|
||||||
// Fixtures
|
|
||||||
const Component = () => null;
|
|
||||||
|
|
||||||
const reducer = s => s;
|
|
||||||
|
|
||||||
describe('injectReducer decorator', () => {
|
|
||||||
let store;
|
|
||||||
let injectors;
|
|
||||||
let ComponentWithReducer;
|
|
||||||
|
|
||||||
beforeAll(() => {
|
|
||||||
reducerInjectors.default = jest.fn().mockImplementation(() => injectors);
|
|
||||||
});
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
store = configureStore({}, memoryHistory);
|
|
||||||
injectors = {
|
|
||||||
injectReducer: jest.fn(),
|
|
||||||
};
|
|
||||||
ComponentWithReducer = injectReducer({ key: 'test', reducer })(Component);
|
|
||||||
reducerInjectors.default.mockClear();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should inject a given reducer', () => {
|
|
||||||
renderer.create(
|
|
||||||
<Provider store={store}>
|
|
||||||
<ComponentWithReducer />
|
|
||||||
</Provider>,
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(injectors.injectReducer).toHaveBeenCalledTimes(1);
|
|
||||||
expect(injectors.injectReducer).toHaveBeenCalledWith('test', reducer);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should set a correct display name', () => {
|
|
||||||
expect(ComponentWithReducer.displayName).toBe('withReducer(Component)');
|
|
||||||
expect(
|
|
||||||
injectReducer({ key: 'test', reducer })(() => null).displayName,
|
|
||||||
).toBe('withReducer(Component)');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should propagate props', () => {
|
|
||||||
const props = { testProp: 'test' };
|
|
||||||
const renderedComponent = renderer.create(
|
|
||||||
<Provider store={store}>
|
|
||||||
<ComponentWithReducer {...props} />
|
|
||||||
</Provider>,
|
|
||||||
);
|
|
||||||
const {
|
|
||||||
props: { children },
|
|
||||||
} = renderedComponent.getInstance();
|
|
||||||
|
|
||||||
expect(children.props).toEqual(props);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('useInjectReducer hook', () => {
|
|
||||||
let store;
|
|
||||||
let injectors;
|
|
||||||
let ComponentWithReducer;
|
|
||||||
|
|
||||||
beforeAll(() => {
|
|
||||||
injectors = {
|
|
||||||
injectReducer: jest.fn(),
|
|
||||||
};
|
|
||||||
reducerInjectors.default = jest.fn().mockImplementation(() => injectors);
|
|
||||||
store = configureStore({}, memoryHistory);
|
|
||||||
ComponentWithReducer = () => {
|
|
||||||
useInjectReducer({ key: 'test', reducer });
|
|
||||||
return null;
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should inject a given reducer', () => {
|
|
||||||
render(
|
|
||||||
<Provider store={store}>
|
|
||||||
<ComponentWithReducer />
|
|
||||||
</Provider>,
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(injectors.injectReducer).toHaveBeenCalledTimes(1);
|
|
||||||
expect(injectors.injectReducer).toHaveBeenCalledWith('test', reducer);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,149 +0,0 @@
|
|||||||
/**
|
|
||||||
* Test injectors
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { memoryHistory } from 'react-router-dom';
|
|
||||||
import { put } from 'redux-saga/effects';
|
|
||||||
import renderer from 'react-test-renderer';
|
|
||||||
import { render } from 'react-testing-library';
|
|
||||||
import React from 'react';
|
|
||||||
import { Provider } from 'react-redux';
|
|
||||||
|
|
||||||
import configureStore from '../../configureStore';
|
|
||||||
import injectSaga, { useInjectSaga } from '../injectSaga';
|
|
||||||
import * as sagaInjectors from '../sagaInjectors';
|
|
||||||
|
|
||||||
// Fixtures
|
|
||||||
const Component = () => null;
|
|
||||||
|
|
||||||
function* testSaga() {
|
|
||||||
yield put({ type: 'TEST', payload: 'yup' });
|
|
||||||
}
|
|
||||||
|
|
||||||
describe('injectSaga decorator', () => {
|
|
||||||
let store;
|
|
||||||
let injectors;
|
|
||||||
let ComponentWithSaga;
|
|
||||||
|
|
||||||
beforeAll(() => {
|
|
||||||
sagaInjectors.default = jest.fn().mockImplementation(() => injectors);
|
|
||||||
});
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
store = configureStore({}, memoryHistory);
|
|
||||||
injectors = {
|
|
||||||
injectSaga: jest.fn(),
|
|
||||||
ejectSaga: jest.fn(),
|
|
||||||
};
|
|
||||||
ComponentWithSaga = injectSaga({
|
|
||||||
key: 'test',
|
|
||||||
saga: testSaga,
|
|
||||||
mode: 'testMode',
|
|
||||||
})(Component);
|
|
||||||
sagaInjectors.default.mockClear();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should inject given saga, mode, and props', () => {
|
|
||||||
const props = { test: 'test' };
|
|
||||||
renderer.create(
|
|
||||||
<Provider store={store}>
|
|
||||||
<ComponentWithSaga {...props} />
|
|
||||||
</Provider>,
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(injectors.injectSaga).toHaveBeenCalledTimes(1);
|
|
||||||
expect(injectors.injectSaga).toHaveBeenCalledWith(
|
|
||||||
'test',
|
|
||||||
{ saga: testSaga, mode: 'testMode' },
|
|
||||||
props,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should eject on unmount with a correct saga key', () => {
|
|
||||||
const props = { test: 'test' };
|
|
||||||
const renderedComponent = renderer.create(
|
|
||||||
<Provider store={store}>
|
|
||||||
<ComponentWithSaga {...props} />
|
|
||||||
</Provider>,
|
|
||||||
);
|
|
||||||
renderedComponent.unmount();
|
|
||||||
|
|
||||||
expect(injectors.ejectSaga).toHaveBeenCalledTimes(1);
|
|
||||||
expect(injectors.ejectSaga).toHaveBeenCalledWith('test');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should set a correct display name', () => {
|
|
||||||
expect(ComponentWithSaga.displayName).toBe('withSaga(Component)');
|
|
||||||
expect(
|
|
||||||
injectSaga({ key: 'test', saga: testSaga })(() => null).displayName,
|
|
||||||
).toBe('withSaga(Component)');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should propagate props', () => {
|
|
||||||
const props = { testProp: 'test' };
|
|
||||||
const renderedComponent = renderer.create(
|
|
||||||
<Provider store={store}>
|
|
||||||
<ComponentWithSaga {...props} />
|
|
||||||
</Provider>,
|
|
||||||
);
|
|
||||||
const {
|
|
||||||
props: { children },
|
|
||||||
} = renderedComponent.getInstance();
|
|
||||||
expect(children.props).toEqual(props);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('useInjectSaga hook', () => {
|
|
||||||
let store;
|
|
||||||
let injectors;
|
|
||||||
let ComponentWithSaga;
|
|
||||||
|
|
||||||
beforeAll(() => {
|
|
||||||
sagaInjectors.default = jest.fn().mockImplementation(() => injectors);
|
|
||||||
});
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
store = configureStore({}, memoryHistory);
|
|
||||||
injectors = {
|
|
||||||
injectSaga: jest.fn(),
|
|
||||||
ejectSaga: jest.fn(),
|
|
||||||
};
|
|
||||||
ComponentWithSaga = () => {
|
|
||||||
useInjectSaga({
|
|
||||||
key: 'test',
|
|
||||||
saga: testSaga,
|
|
||||||
mode: 'testMode',
|
|
||||||
});
|
|
||||||
return null;
|
|
||||||
};
|
|
||||||
sagaInjectors.default.mockClear();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should inject given saga and mode', () => {
|
|
||||||
const props = { test: 'test' };
|
|
||||||
render(
|
|
||||||
<Provider store={store}>
|
|
||||||
<ComponentWithSaga {...props} />
|
|
||||||
</Provider>,
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(injectors.injectSaga).toHaveBeenCalledTimes(1);
|
|
||||||
expect(injectors.injectSaga).toHaveBeenCalledWith('test', {
|
|
||||||
saga: testSaga,
|
|
||||||
mode: 'testMode',
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should eject on unmount with a correct saga key', () => {
|
|
||||||
const props = { test: 'test' };
|
|
||||||
const { unmount } = render(
|
|
||||||
<Provider store={store}>
|
|
||||||
<ComponentWithSaga {...props} />
|
|
||||||
</Provider>,
|
|
||||||
);
|
|
||||||
unmount();
|
|
||||||
|
|
||||||
expect(injectors.ejectSaga).toHaveBeenCalledTimes(1);
|
|
||||||
expect(injectors.ejectSaga).toHaveBeenCalledWith('test');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,100 +0,0 @@
|
|||||||
/**
|
|
||||||
* Test injectors
|
|
||||||
*/
|
|
||||||
|
|
||||||
import produce from 'immer';
|
|
||||||
import { memoryHistory } from 'react-router-dom';
|
|
||||||
import identity from 'lodash/identity';
|
|
||||||
|
|
||||||
import configureStore from '../../configureStore';
|
|
||||||
|
|
||||||
import getInjectors, { injectReducerFactory } from '../reducerInjectors';
|
|
||||||
|
|
||||||
// Fixtures
|
|
||||||
|
|
||||||
const initialState = { reduced: 'soon' };
|
|
||||||
|
|
||||||
/* eslint-disable default-case, no-param-reassign */
|
|
||||||
const reducer = (state = initialState, action) =>
|
|
||||||
produce(state, draft => {
|
|
||||||
switch (action.type) {
|
|
||||||
case 'TEST':
|
|
||||||
draft.reduced = action.payload;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('reducer injectors', () => {
|
|
||||||
let store;
|
|
||||||
let injectReducer;
|
|
||||||
|
|
||||||
describe('getInjectors', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
store = configureStore({}, memoryHistory);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should return injectors', () => {
|
|
||||||
expect(getInjectors(store)).toEqual(
|
|
||||||
expect.objectContaining({
|
|
||||||
injectReducer: expect.any(Function),
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should throw if passed invalid store shape', () => {
|
|
||||||
Reflect.deleteProperty(store, 'dispatch');
|
|
||||||
|
|
||||||
expect(() => getInjectors(store)).toThrow();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('injectReducer helper', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
store = configureStore({}, memoryHistory);
|
|
||||||
injectReducer = injectReducerFactory(store, true);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should check a store if the second argument is falsy', () => {
|
|
||||||
const inject = injectReducerFactory({});
|
|
||||||
|
|
||||||
expect(() => inject('test', reducer)).toThrow();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('it should not check a store if the second argument is true', () => {
|
|
||||||
Reflect.deleteProperty(store, 'dispatch');
|
|
||||||
|
|
||||||
expect(() => injectReducer('test', reducer)).not.toThrow();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should validate a reducer and reducer's key", () => {
|
|
||||||
expect(() => injectReducer('', reducer)).toThrow();
|
|
||||||
expect(() => injectReducer(1, reducer)).toThrow();
|
|
||||||
expect(() => injectReducer(1, 1)).toThrow();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('given a store, it should provide a function to inject a reducer', () => {
|
|
||||||
injectReducer('test', reducer);
|
|
||||||
|
|
||||||
const actual = store.getState().test;
|
|
||||||
const expected = initialState;
|
|
||||||
|
|
||||||
expect(actual).toEqual(expected);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not assign reducer if already existing', () => {
|
|
||||||
store.replaceReducer = jest.fn();
|
|
||||||
injectReducer('test', reducer);
|
|
||||||
injectReducer('test', reducer);
|
|
||||||
|
|
||||||
expect(store.replaceReducer).toHaveBeenCalledTimes(1);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should assign reducer if different implementation for hot reloading', () => {
|
|
||||||
store.replaceReducer = jest.fn();
|
|
||||||
injectReducer('test', reducer);
|
|
||||||
injectReducer('test', identity);
|
|
||||||
|
|
||||||
expect(store.replaceReducer).toHaveBeenCalledTimes(2);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,80 +0,0 @@
|
|||||||
/**
|
|
||||||
* Test the request function
|
|
||||||
*/
|
|
||||||
|
|
||||||
import 'whatwg-fetch';
|
|
||||||
import request from '../request';
|
|
||||||
|
|
||||||
describe('request', () => {
|
|
||||||
// Before each test, stub the fetch function
|
|
||||||
beforeEach(() => {
|
|
||||||
window.fetch = jest.fn();
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('stubbing successful response', () => {
|
|
||||||
// Before each test, pretend we got a successful response
|
|
||||||
beforeEach(() => {
|
|
||||||
const res = new Response('{"hello":"world"}', {
|
|
||||||
status: 200,
|
|
||||||
headers: {
|
|
||||||
'Content-type': 'application/json',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
window.fetch.mockReturnValue(Promise.resolve(res));
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should format the response correctly', done => {
|
|
||||||
request('/thisurliscorrect')
|
|
||||||
.catch(done)
|
|
||||||
.then(json => {
|
|
||||||
expect(json.hello).toBe('world');
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('stubbing 204 response', () => {
|
|
||||||
// Before each test, pretend we got a successful response
|
|
||||||
beforeEach(() => {
|
|
||||||
const res = new Response('', {
|
|
||||||
status: 204,
|
|
||||||
statusText: 'No Content',
|
|
||||||
});
|
|
||||||
|
|
||||||
window.fetch.mockReturnValue(Promise.resolve(res));
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should return null on 204 response', done => {
|
|
||||||
request('/thisurliscorrect')
|
|
||||||
.catch(done)
|
|
||||||
.then(json => {
|
|
||||||
expect(json).toBeNull();
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('stubbing error response', () => {
|
|
||||||
// Before each test, pretend we got an unsuccessful response
|
|
||||||
beforeEach(() => {
|
|
||||||
const res = new Response('', {
|
|
||||||
status: 404,
|
|
||||||
statusText: 'Not Found',
|
|
||||||
headers: {
|
|
||||||
'Content-type': 'application/json',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
window.fetch.mockReturnValue(Promise.resolve(res));
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should catch errors', done => {
|
|
||||||
request('/thisdoesntexist').catch(err => {
|
|
||||||
expect(err.response.status).toBe(404);
|
|
||||||
expect(err.response.statusText).toBe('Not Found');
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,231 +0,0 @@
|
|||||||
/**
|
|
||||||
* Test injectors
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { memoryHistory } from 'react-router-dom';
|
|
||||||
import { put } from 'redux-saga/effects';
|
|
||||||
|
|
||||||
import configureStore from '../../configureStore';
|
|
||||||
import getInjectors, {
|
|
||||||
injectSagaFactory,
|
|
||||||
ejectSagaFactory,
|
|
||||||
} from '../sagaInjectors';
|
|
||||||
import { DAEMON, ONCE_TILL_UNMOUNT, RESTART_ON_REMOUNT } from '../constants';
|
|
||||||
|
|
||||||
function* testSaga() {
|
|
||||||
yield put({ type: 'TEST', payload: 'yup' });
|
|
||||||
}
|
|
||||||
|
|
||||||
describe('injectors', () => {
|
|
||||||
const originalNodeEnv = process.env.NODE_ENV;
|
|
||||||
let store;
|
|
||||||
let injectSaga;
|
|
||||||
let ejectSaga;
|
|
||||||
|
|
||||||
describe('getInjectors', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
store = configureStore({}, memoryHistory);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should return injectors', () => {
|
|
||||||
expect(getInjectors(store)).toEqual(
|
|
||||||
expect.objectContaining({
|
|
||||||
injectSaga: expect.any(Function),
|
|
||||||
ejectSaga: expect.any(Function),
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should throw if passed invalid store shape', () => {
|
|
||||||
Reflect.deleteProperty(store, 'dispatch');
|
|
||||||
|
|
||||||
expect(() => getInjectors(store)).toThrow();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('ejectSaga helper', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
store = configureStore({}, memoryHistory);
|
|
||||||
injectSaga = injectSagaFactory(store, true);
|
|
||||||
ejectSaga = ejectSagaFactory(store, true);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should check a store if the second argument is falsy', () => {
|
|
||||||
const eject = ejectSagaFactory({});
|
|
||||||
|
|
||||||
expect(() => eject('test')).toThrow();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not check a store if the second argument is true', () => {
|
|
||||||
Reflect.deleteProperty(store, 'dispatch');
|
|
||||||
injectSaga('test', { saga: testSaga });
|
|
||||||
|
|
||||||
expect(() => ejectSaga('test')).not.toThrow();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should validate saga's key", () => {
|
|
||||||
expect(() => ejectSaga('')).toThrow();
|
|
||||||
expect(() => ejectSaga(1)).toThrow();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should cancel a saga in RESTART_ON_REMOUNT mode', () => {
|
|
||||||
const cancel = jest.fn();
|
|
||||||
store.injectedSagas.test = { task: { cancel }, mode: RESTART_ON_REMOUNT };
|
|
||||||
ejectSaga('test');
|
|
||||||
|
|
||||||
expect(cancel).toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not cancel a daemon saga', () => {
|
|
||||||
const cancel = jest.fn();
|
|
||||||
store.injectedSagas.test = { task: { cancel }, mode: DAEMON };
|
|
||||||
ejectSaga('test');
|
|
||||||
|
|
||||||
expect(cancel).not.toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should ignore saga that was not previously injected', () => {
|
|
||||||
expect(() => ejectSaga('test')).not.toThrow();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should remove non daemon saga's descriptor in production", () => {
|
|
||||||
process.env.NODE_ENV = 'production';
|
|
||||||
injectSaga('test', { saga: testSaga, mode: RESTART_ON_REMOUNT });
|
|
||||||
injectSaga('test1', { saga: testSaga, mode: ONCE_TILL_UNMOUNT });
|
|
||||||
|
|
||||||
ejectSaga('test');
|
|
||||||
ejectSaga('test1');
|
|
||||||
|
|
||||||
expect(store.injectedSagas.test).toBe('done');
|
|
||||||
expect(store.injectedSagas.test1).toBe('done');
|
|
||||||
process.env.NODE_ENV = originalNodeEnv;
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should not remove daemon saga's descriptor in production", () => {
|
|
||||||
process.env.NODE_ENV = 'production';
|
|
||||||
injectSaga('test', { saga: testSaga, mode: DAEMON });
|
|
||||||
ejectSaga('test');
|
|
||||||
|
|
||||||
expect(store.injectedSagas.test.saga).toBe(testSaga);
|
|
||||||
process.env.NODE_ENV = originalNodeEnv;
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should not remove daemon saga's descriptor in development", () => {
|
|
||||||
injectSaga('test', { saga: testSaga, mode: DAEMON });
|
|
||||||
ejectSaga('test');
|
|
||||||
|
|
||||||
expect(store.injectedSagas.test.saga).toBe(testSaga);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('injectSaga helper', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
store = configureStore({}, memoryHistory);
|
|
||||||
injectSaga = injectSagaFactory(store, true);
|
|
||||||
ejectSaga = ejectSagaFactory(store, true);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should check a store if the second argument is falsy', () => {
|
|
||||||
const inject = injectSagaFactory({});
|
|
||||||
|
|
||||||
expect(() => inject('test', testSaga)).toThrow();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('it should not check a store if the second argument is true', () => {
|
|
||||||
Reflect.deleteProperty(store, 'dispatch');
|
|
||||||
|
|
||||||
expect(() => injectSaga('test', { saga: testSaga })).not.toThrow();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should validate saga's key", () => {
|
|
||||||
expect(() => injectSaga('', { saga: testSaga })).toThrow();
|
|
||||||
expect(() => injectSaga(1, { saga: testSaga })).toThrow();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should validate saga's descriptor", () => {
|
|
||||||
expect(() => injectSaga('test')).toThrow();
|
|
||||||
expect(() => injectSaga('test', { saga: 1 })).toThrow();
|
|
||||||
expect(() =>
|
|
||||||
injectSaga('test', { saga: testSaga, mode: 'testMode' }),
|
|
||||||
).toThrow();
|
|
||||||
expect(() => injectSaga('test', { saga: testSaga, mode: 1 })).toThrow();
|
|
||||||
expect(() =>
|
|
||||||
injectSaga('test', { saga: testSaga, mode: RESTART_ON_REMOUNT }),
|
|
||||||
).not.toThrow();
|
|
||||||
expect(() =>
|
|
||||||
injectSaga('test', { saga: testSaga, mode: DAEMON }),
|
|
||||||
).not.toThrow();
|
|
||||||
expect(() =>
|
|
||||||
injectSaga('test', { saga: testSaga, mode: ONCE_TILL_UNMOUNT }),
|
|
||||||
).not.toThrow();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should pass args to saga.run', () => {
|
|
||||||
const args = {};
|
|
||||||
store.runSaga = jest.fn();
|
|
||||||
injectSaga('test', { saga: testSaga }, args);
|
|
||||||
|
|
||||||
expect(store.runSaga).toHaveBeenCalledWith(testSaga, args);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not start daemon and once-till-unmount sagas if were started before', () => {
|
|
||||||
store.runSaga = jest.fn();
|
|
||||||
|
|
||||||
injectSaga('test1', { saga: testSaga, mode: DAEMON });
|
|
||||||
injectSaga('test1', { saga: testSaga, mode: DAEMON });
|
|
||||||
injectSaga('test2', { saga: testSaga, mode: ONCE_TILL_UNMOUNT });
|
|
||||||
injectSaga('test2', { saga: testSaga, mode: ONCE_TILL_UNMOUNT });
|
|
||||||
|
|
||||||
expect(store.runSaga).toHaveBeenCalledTimes(2);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should start any saga that was not started before', () => {
|
|
||||||
store.runSaga = jest.fn();
|
|
||||||
|
|
||||||
injectSaga('test1', { saga: testSaga });
|
|
||||||
injectSaga('test2', { saga: testSaga, mode: DAEMON });
|
|
||||||
injectSaga('test3', { saga: testSaga, mode: ONCE_TILL_UNMOUNT });
|
|
||||||
|
|
||||||
expect(store.runSaga).toHaveBeenCalledTimes(3);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should restart a saga if different implementation for hot reloading', () => {
|
|
||||||
const cancel = jest.fn();
|
|
||||||
store.injectedSagas.test = { saga: testSaga, task: { cancel } };
|
|
||||||
store.runSaga = jest.fn();
|
|
||||||
|
|
||||||
function* testSaga1() {
|
|
||||||
yield put({ type: 'TEST', payload: 'yup' });
|
|
||||||
}
|
|
||||||
|
|
||||||
injectSaga('test', { saga: testSaga1 });
|
|
||||||
|
|
||||||
expect(cancel).toHaveBeenCalledTimes(1);
|
|
||||||
expect(store.runSaga).toHaveBeenCalledWith(testSaga1, undefined);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not cancel saga if different implementation in production', () => {
|
|
||||||
process.env.NODE_ENV = 'production';
|
|
||||||
const cancel = jest.fn();
|
|
||||||
store.injectedSagas.test = {
|
|
||||||
saga: testSaga,
|
|
||||||
task: { cancel },
|
|
||||||
mode: RESTART_ON_REMOUNT,
|
|
||||||
};
|
|
||||||
|
|
||||||
function* testSaga1() {
|
|
||||||
yield put({ type: 'TEST', payload: 'yup' });
|
|
||||||
}
|
|
||||||
|
|
||||||
injectSaga('test', { saga: testSaga1, mode: DAEMON });
|
|
||||||
|
|
||||||
expect(cancel).toHaveBeenCalledTimes(0);
|
|
||||||
process.env.NODE_ENV = originalNodeEnv;
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should save an entire descriptor in the saga registry', () => {
|
|
||||||
injectSaga('test', { saga: testSaga, foo: 'bar' });
|
|
||||||
expect(store.injectedSagas.test.foo).toBe('bar');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -23,7 +23,6 @@
|
|||||||
"clean-webpack-plugin": "^3.0.0",
|
"clean-webpack-plugin": "^3.0.0",
|
||||||
"cu-ui": "git+https://sean_macfarlane@bitbucket.org/connectustechnologies/connectus-wlan-ui-workspace.git",
|
"cu-ui": "git+https://sean_macfarlane@bitbucket.org/connectustechnologies/connectus-wlan-ui-workspace.git",
|
||||||
"graphql": "^14.6.0",
|
"graphql": "^14.6.0",
|
||||||
"history": "^4.10.1",
|
|
||||||
"prop-types": "^15.7.2",
|
"prop-types": "^15.7.2",
|
||||||
"react": "^16.13.0",
|
"react": "^16.13.0",
|
||||||
"react-dom": "^16.13.0",
|
"react-dom": "^16.13.0",
|
||||||
|
|||||||
Reference in New Issue
Block a user