/
utils.ts
122 lines (95 loc) · 3.89 KB
/
utils.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
import { configureStore, createAction, createReducer, PayloadAction } from "@reduxjs/toolkit";
import { Action, Middleware, Reducer, Store } from "redux";
import { commonState } from "../state/commonReducer";
import { StoreLogSagaMonitor, StoreSagaMonitor } from "./sagaMonitor";
import makeSagaMiddleware from 'redux-saga';
import { ExtendedStore } from "../types";
const tooLongInMs = 3000;
export const asyncDispatch = <RootState extends commonState>(store: Store<RootState>, action: Action): Promise<string> => {
const actionType = action.type;
const promiseToDispachAction = new Promise<string>((resolve, reject) => {
const promiseTimeOut = setTimeout(() => {
// Rise error if taking too long
console.log(action);
console.log(`The action ${actionType} took too much time to execute`);
throw new Error(`The action ${actionType} took too much time to execute`);
}, tooLongInMs);
const clearPromiseTimeOut = () => {
clearTimeout(promiseTimeOut);
};
// Subscribe on loading state
let actionStarted = false;
store.subscribe(() => {
const state = store.getState();
const actionLoad = !!state.loading[actionType];
const actionError = !!state.error[actionType];
// loading is true. Action started
if (actionLoad) {
actionStarted = true;
}
// loading was true, but then changed to false. Action finished.
if (actionLoad === false && actionStarted) {
clearPromiseTimeOut();
resolve('');
}
if (actionError) {
resolve('');
}
});
});
store.dispatch(action);
return promiseToDispachAction;
};
export const makeStoreCreator = <State extends commonState>(reducer: Reducer<State>, rootSaga: () => Generator<any>) => {
const makeANewStore: StoreCreator<State> = () => {
const actionHistory: any[] = [];
const sagaMonitor = new StoreSagaMonitor(actionHistory);
const sagaMiddleware = makeSagaMiddleware({sagaMonitor});
const store = configureStore({
reducer,
middleware: (getDefaultMiddleware) => getDefaultMiddleware().concat(sagaMiddleware as Middleware),
devTools: process.env.NODE_ENV !== 'production',
});
sagaMiddleware.run(rootSaga);
return {
...store,
asyncDispatch: (action: Action) => asyncDispatch(store, action),
getActionHistory: () => [...actionHistory],
getActionHistoryRepresentation: () => JSON.stringify(actionHistory, undefined, 2),
};
};
return makeANewStore;
}
export type StoreCreator<State> = () => ExtendedStore<State>;
export const setState = createAction<Partial<commonState>>('setState');
const resetState = createAction('resetState');
export const makeHocTestingStore = <State extends commonState>(store: ExtendedStore<State>): ExtendedStore<State> => {
const initialState = store.getState();
const rootReducer = createReducer(initialState, (builder) => {
builder
.addCase(setState, (state, action: PayloadAction<Partial<State>>) => {
return {...state, ...action.payload};
})
.addCase(resetState, () => {
return {...initialState};
});
});
const actionHistory: any[] = [];
const sagaMonitor = new StoreLogSagaMonitor(actionHistory);
const sagaMiddleware = makeSagaMiddleware({sagaMonitor});
const hocStore = configureStore({
reducer: rootReducer,
middleware: (getDefaultMiddleware) => getDefaultMiddleware().concat(sagaMiddleware as Middleware),
devTools: process.env.NODE_ENV !== 'production',
});
const rootSaga = function*() {
}
sagaMiddleware.run(rootSaga);
return {
...hocStore,
asyncDispatch: (action: Action) => {throw NotImplementedException()},
getActionHistory: () => [...actionHistory],
getActionHistoryRepresentation: () => JSON.stringify(actionHistory, undefined, 2),
};
}
const NotImplementedException = () => new Error('Not implemented because no logic is connected.');