@data-client/core 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/README.md +71 -0
- package/dist/index.js +1643 -0
- package/dist/index.umd.min.js +1 -0
- package/dist/next.js +461 -0
- package/dist/package.json +1 -0
- package/legacy/actionTypes.js +12 -0
- package/legacy/compatibleActions.js +2 -0
- package/legacy/controller/BaseController.js +289 -0
- package/legacy/controller/Controller.js +20 -0
- package/legacy/controller/createFetch.js +42 -0
- package/legacy/controller/createInvalidate.js +12 -0
- package/legacy/controller/createInvalidateAll.js +8 -0
- package/legacy/controller/createOptimistic.js +33 -0
- package/legacy/controller/createReceive.js +36 -0
- package/legacy/controller/createReset.js +8 -0
- package/legacy/controller/createSubscription.js +30 -0
- package/legacy/controller/types.js +2 -0
- package/legacy/endpoint/index.js +2 -0
- package/legacy/endpoint/shapes.js +2 -0
- package/legacy/endpoint/types.js +2 -0
- package/legacy/fsa.js +2 -0
- package/legacy/index.js +22 -0
- package/legacy/internal.js +4 -0
- package/legacy/legacyActions.js +2 -0
- package/legacy/manager/ConnectionListener.js +2 -0
- package/legacy/manager/DefaultConnectionListener.js +40 -0
- package/legacy/manager/DevtoolsManager.js +73 -0
- package/legacy/manager/LogoutManager.js +34 -0
- package/legacy/manager/NetworkManager.js +291 -0
- package/legacy/manager/PollingSubscription.js +159 -0
- package/legacy/manager/SubscriptionManager.js +117 -0
- package/legacy/manager/applyManager.js +23 -0
- package/legacy/manager/devtoolsTypes.js +2 -0
- package/legacy/manager/index.js +6 -0
- package/legacy/middlewareTypes.js +2 -0
- package/legacy/newActions.js +2 -0
- package/legacy/next/Controller.js +24 -0
- package/legacy/next/index.js +3 -0
- package/legacy/previousActions.js +2 -0
- package/legacy/state/RIC.js +3 -0
- package/legacy/state/applyUpdatersToResults.js +4 -0
- package/legacy/state/legacy-actions/createFetch.js +62 -0
- package/legacy/state/legacy-actions/createReceive.js +37 -0
- package/legacy/state/legacy-actions/createReceiveError.js +28 -0
- package/legacy/state/legacy-actions/index.js +4 -0
- package/legacy/state/reducer/createReducer.js +54 -0
- package/legacy/state/reducer/fetchReducer.js +32 -0
- package/legacy/state/reducer/invalidateReducer.js +28 -0
- package/legacy/state/reducer/setReducer.js +113 -0
- package/legacy/state/reducerInstance.js +9 -0
- package/legacy/state/selectMeta.js +4 -0
- package/legacy/types.js +8 -0
- package/lib/actionTypes.d.ts +11 -0
- package/lib/actionTypes.d.ts.map +1 -0
- package/lib/actionTypes.js +12 -0
- package/lib/compatibleActions.d.ts +47 -0
- package/lib/compatibleActions.d.ts.map +1 -0
- package/lib/compatibleActions.js +2 -0
- package/lib/controller/BaseController.d.ts +128 -0
- package/lib/controller/BaseController.d.ts.map +1 -0
- package/lib/controller/BaseController.js +289 -0
- package/lib/controller/Controller.d.ts +14 -0
- package/lib/controller/Controller.d.ts.map +1 -0
- package/lib/controller/Controller.js +20 -0
- package/lib/controller/createFetch.d.ts +12 -0
- package/lib/controller/createFetch.d.ts.map +1 -0
- package/lib/controller/createFetch.js +42 -0
- package/lib/controller/createInvalidate.d.ts +6 -0
- package/lib/controller/createInvalidate.d.ts.map +1 -0
- package/lib/controller/createInvalidate.js +12 -0
- package/lib/controller/createInvalidateAll.d.ts +3 -0
- package/lib/controller/createInvalidateAll.d.ts.map +1 -0
- package/lib/controller/createInvalidateAll.js +8 -0
- package/lib/controller/createOptimistic.d.ts +10 -0
- package/lib/controller/createOptimistic.d.ts.map +1 -0
- package/lib/controller/createOptimistic.js +33 -0
- package/lib/controller/createReceive.d.ts +20 -0
- package/lib/controller/createReceive.d.ts.map +1 -0
- package/lib/controller/createReceive.js +36 -0
- package/lib/controller/createReset.d.ts +3 -0
- package/lib/controller/createReset.d.ts.map +1 -0
- package/lib/controller/createReset.js +8 -0
- package/lib/controller/createSubscription.d.ts +9 -0
- package/lib/controller/createSubscription.d.ts.map +1 -0
- package/lib/controller/createSubscription.js +30 -0
- package/lib/controller/types.d.ts +6 -0
- package/lib/controller/types.d.ts.map +1 -0
- package/lib/controller/types.js +2 -0
- package/lib/endpoint/index.d.ts +3 -0
- package/lib/endpoint/index.d.ts.map +1 -0
- package/lib/endpoint/index.js +2 -0
- package/lib/endpoint/shapes.d.ts +25 -0
- package/lib/endpoint/shapes.d.ts.map +1 -0
- package/lib/endpoint/shapes.js +2 -0
- package/lib/endpoint/types.d.ts +45 -0
- package/lib/endpoint/types.d.ts.map +1 -0
- package/lib/endpoint/types.js +2 -0
- package/lib/fsa.d.ts +41 -0
- package/lib/fsa.d.ts.map +1 -0
- package/lib/fsa.js +2 -0
- package/lib/index.d.ts +19 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +22 -0
- package/lib/internal.d.ts +4 -0
- package/lib/internal.d.ts.map +1 -0
- package/lib/internal.js +4 -0
- package/lib/legacyActions.d.ts +92 -0
- package/lib/legacyActions.d.ts.map +1 -0
- package/lib/legacyActions.js +2 -0
- package/lib/manager/ConnectionListener.d.ts +8 -0
- package/lib/manager/ConnectionListener.d.ts.map +1 -0
- package/lib/manager/ConnectionListener.js +2 -0
- package/lib/manager/DefaultConnectionListener.d.ts +20 -0
- package/lib/manager/DefaultConnectionListener.d.ts.map +1 -0
- package/lib/manager/DefaultConnectionListener.js +40 -0
- package/lib/manager/DevtoolsManager.d.ts +24 -0
- package/lib/manager/DevtoolsManager.d.ts.map +1 -0
- package/lib/manager/DevtoolsManager.js +74 -0
- package/lib/manager/LogoutManager.d.ts +25 -0
- package/lib/manager/LogoutManager.d.ts.map +1 -0
- package/lib/manager/LogoutManager.js +34 -0
- package/lib/manager/NetworkManager.d.ts +82 -0
- package/lib/manager/NetworkManager.d.ts.map +1 -0
- package/lib/manager/NetworkManager.js +295 -0
- package/lib/manager/PollingSubscription.d.ts +45 -0
- package/lib/manager/PollingSubscription.d.ts.map +1 -0
- package/lib/manager/PollingSubscription.js +159 -0
- package/lib/manager/SubscriptionManager.d.ts +55 -0
- package/lib/manager/SubscriptionManager.d.ts.map +1 -0
- package/lib/manager/SubscriptionManager.js +117 -0
- package/lib/manager/applyManager.d.ts +10 -0
- package/lib/manager/applyManager.d.ts.map +1 -0
- package/lib/manager/applyManager.js +23 -0
- package/lib/manager/devtoolsTypes.d.ts +205 -0
- package/lib/manager/devtoolsTypes.d.ts.map +1 -0
- package/lib/manager/devtoolsTypes.js +2 -0
- package/lib/manager/index.d.ts +8 -0
- package/lib/manager/index.d.ts.map +1 -0
- package/lib/manager/index.js +6 -0
- package/lib/middlewareTypes.d.ts +18 -0
- package/lib/middlewareTypes.d.ts.map +1 -0
- package/lib/middlewareTypes.js +2 -0
- package/lib/newActions.d.ts +85 -0
- package/lib/newActions.d.ts.map +1 -0
- package/lib/newActions.js +2 -0
- package/lib/next/Controller.d.ts +14 -0
- package/lib/next/Controller.d.ts.map +1 -0
- package/lib/next/Controller.js +24 -0
- package/lib/next/index.d.ts +3 -0
- package/lib/next/index.d.ts.map +1 -0
- package/lib/next/index.js +3 -0
- package/lib/previousActions.d.ts +91 -0
- package/lib/previousActions.d.ts.map +1 -0
- package/lib/previousActions.js +2 -0
- package/lib/state/RIC.d.ts +2 -0
- package/lib/state/RIC.js +3 -0
- package/lib/state/applyUpdatersToResults.d.ts +13 -0
- package/lib/state/applyUpdatersToResults.d.ts.map +1 -0
- package/lib/state/applyUpdatersToResults.js +7 -0
- package/lib/state/legacy-actions/createFetch.d.ts +19 -0
- package/lib/state/legacy-actions/createFetch.d.ts.map +1 -0
- package/lib/state/legacy-actions/createFetch.js +62 -0
- package/lib/state/legacy-actions/createReceive.d.ts +14 -0
- package/lib/state/legacy-actions/createReceive.d.ts.map +1 -0
- package/lib/state/legacy-actions/createReceive.js +37 -0
- package/lib/state/legacy-actions/createReceiveError.d.ts +9 -0
- package/lib/state/legacy-actions/createReceiveError.d.ts.map +1 -0
- package/lib/state/legacy-actions/createReceiveError.js +28 -0
- package/lib/state/legacy-actions/index.d.ts +4 -0
- package/lib/state/legacy-actions/index.d.ts.map +1 -0
- package/lib/state/legacy-actions/index.js +4 -0
- package/lib/state/reducer/createReducer.d.ts +7 -0
- package/lib/state/reducer/createReducer.d.ts.map +1 -0
- package/lib/state/reducer/createReducer.js +55 -0
- package/lib/state/reducer/fetchReducer.d.ts +4 -0
- package/lib/state/reducer/fetchReducer.d.ts.map +1 -0
- package/lib/state/reducer/fetchReducer.js +34 -0
- package/lib/state/reducer/invalidateReducer.d.ts +37 -0
- package/lib/state/reducer/invalidateReducer.d.ts.map +1 -0
- package/lib/state/reducer/invalidateReducer.js +34 -0
- package/lib/state/reducer/setReducer.d.ts +40 -0
- package/lib/state/reducer/setReducer.d.ts.map +1 -0
- package/lib/state/reducer/setReducer.js +119 -0
- package/lib/state/reducerInstance.d.ts +7 -0
- package/lib/state/reducerInstance.d.ts.map +1 -0
- package/lib/state/reducerInstance.js +9 -0
- package/lib/state/selectMeta.d.ts +3 -0
- package/lib/state/selectMeta.d.ts.map +1 -0
- package/lib/state/selectMeta.js +4 -0
- package/lib/types.d.ts +71 -0
- package/lib/types.d.ts.map +1 -0
- package/lib/types.js +8 -0
- package/node.mjs +1 -0
- package/package.json +127 -0
- package/src/actionTypes.ts +11 -0
- package/src/compatibleActions.ts +96 -0
- package/src/controller/BaseController.ts +508 -0
- package/src/controller/Controller.ts +32 -0
- package/src/controller/__tests__/Controller.ts +29 -0
- package/src/controller/__tests__/__snapshots__/getResponse.ts.snap +35 -0
- package/src/controller/__tests__/getResponse.ts +182 -0
- package/src/controller/createFetch.ts +54 -0
- package/src/controller/createInvalidate.ts +16 -0
- package/src/controller/createInvalidateAll.ts +11 -0
- package/src/controller/createOptimistic.ts +47 -0
- package/src/controller/createReceive.ts +85 -0
- package/src/controller/createReset.ts +9 -0
- package/src/controller/createSubscription.ts +39 -0
- package/src/controller/types.ts +22 -0
- package/src/endpoint/index.ts +14 -0
- package/src/endpoint/shapes.ts +53 -0
- package/src/endpoint/types.ts +72 -0
- package/src/fsa.ts +99 -0
- package/src/index.ts +61 -0
- package/src/internal.ts +3 -0
- package/src/legacyActions.ts +163 -0
- package/src/manager/ConnectionListener.ts +7 -0
- package/src/manager/DefaultConnectionListener.ts +54 -0
- package/src/manager/DevtoolsManager.ts +99 -0
- package/src/manager/LogoutManager.ts +57 -0
- package/src/manager/NetworkManager.ts +346 -0
- package/src/manager/PollingSubscription.ts +190 -0
- package/src/manager/SubscriptionManager.ts +156 -0
- package/src/manager/__tests__/__snapshots__/pollingSubscription-endpoint.ts.snap +49 -0
- package/src/manager/__tests__/__snapshots__/pollingSubscription.ts.snap +43 -0
- package/src/manager/__tests__/logoutManager.ts +112 -0
- package/src/manager/__tests__/manager.ts +44 -0
- package/src/manager/__tests__/networkManager-legacy.ts +394 -0
- package/src/manager/__tests__/networkManager.ts +426 -0
- package/src/manager/__tests__/pollingSubscription-endpoint.ts +423 -0
- package/src/manager/__tests__/pollingSubscription.ts +313 -0
- package/src/manager/__tests__/subscriptionManager.ts +208 -0
- package/src/manager/applyManager.ts +33 -0
- package/src/manager/devtoolsTypes.ts +210 -0
- package/src/manager/index.ts +7 -0
- package/src/middlewareTypes.ts +49 -0
- package/src/newActions.ts +140 -0
- package/src/next/Controller.ts +39 -0
- package/src/next/index.ts +2 -0
- package/src/package.json +1 -0
- package/src/previousActions.ts +159 -0
- package/src/state/RIC.d.ts +2 -0
- package/src/state/RIC.js +5 -0
- package/src/state/__tests__/RIC.web.ts +16 -0
- package/src/state/__tests__/__snapshots__/reducer.ts.snap +56 -0
- package/src/state/__tests__/applyUpdatersToResults.ts +40 -0
- package/src/state/__tests__/reducer.ts +868 -0
- package/src/state/applyUpdatersToResults.ts +29 -0
- package/src/state/legacy-actions/createFetch.ts +95 -0
- package/src/state/legacy-actions/createReceive.ts +68 -0
- package/src/state/legacy-actions/createReceiveError.ts +43 -0
- package/src/state/legacy-actions/index.ts +3 -0
- package/src/state/reducer/createReducer.ts +80 -0
- package/src/state/reducer/fetchReducer.ts +48 -0
- package/src/state/reducer/invalidateReducer.ts +39 -0
- package/src/state/reducer/setReducer.ts +157 -0
- package/src/state/reducerInstance.ts +14 -0
- package/src/state/selectMeta.ts +8 -0
- package/src/types.ts +125 -0
- package/ts3.4/actionTypes.d.ts +11 -0
- package/ts3.4/compatibleActions.d.ts +47 -0
- package/ts3.4/controller/BaseController.d.ts +170 -0
- package/ts3.4/controller/Controller.d.ts +14 -0
- package/ts3.4/controller/createFetch.d.ts +14 -0
- package/ts3.4/controller/createInvalidate.d.ts +8 -0
- package/ts3.4/controller/createInvalidateAll.d.ts +3 -0
- package/ts3.4/controller/createOptimistic.d.ts +12 -0
- package/ts3.4/controller/createReceive.d.ts +24 -0
- package/ts3.4/controller/createReset.d.ts +3 -0
- package/ts3.4/controller/createSubscription.d.ts +13 -0
- package/ts3.4/controller/types.d.ts +6 -0
- package/ts3.4/endpoint/index.d.ts +3 -0
- package/ts3.4/endpoint/shapes.d.ts +25 -0
- package/ts3.4/endpoint/types.d.ts +45 -0
- package/ts3.4/fsa.d.ts +41 -0
- package/ts3.4/index.d.ts +22 -0
- package/ts3.4/internal.d.ts +4 -0
- package/ts3.4/legacyActions.d.ts +95 -0
- package/ts3.4/manager/ConnectionListener.d.ts +8 -0
- package/ts3.4/manager/DefaultConnectionListener.d.ts +20 -0
- package/ts3.4/manager/DevtoolsManager.d.ts +24 -0
- package/ts3.4/manager/LogoutManager.d.ts +25 -0
- package/ts3.4/manager/NetworkManager.d.ts +82 -0
- package/ts3.4/manager/PollingSubscription.d.ts +45 -0
- package/ts3.4/manager/SubscriptionManager.d.ts +55 -0
- package/ts3.4/manager/applyManager.d.ts +10 -0
- package/ts3.4/manager/devtoolsTypes.d.ts +205 -0
- package/ts3.4/manager/index.d.ts +8 -0
- package/ts3.4/middlewareTypes.d.ts +18 -0
- package/ts3.4/newActions.d.ts +88 -0
- package/ts3.4/next/Controller.d.ts +14 -0
- package/ts3.4/next/index.d.ts +3 -0
- package/ts3.4/previousActions.d.ts +94 -0
- package/ts3.4/state/RIC.d.ts +2 -0
- package/ts3.4/state/applyUpdatersToResults.d.ts +13 -0
- package/ts3.4/state/legacy-actions/createFetch.d.ts +19 -0
- package/ts3.4/state/legacy-actions/createReceive.d.ts +14 -0
- package/ts3.4/state/legacy-actions/createReceiveError.d.ts +9 -0
- package/ts3.4/state/legacy-actions/index.d.ts +4 -0
- package/ts3.4/state/reducer/createReducer.d.ts +7 -0
- package/ts3.4/state/reducer/fetchReducer.d.ts +4 -0
- package/ts3.4/state/reducer/invalidateReducer.d.ts +37 -0
- package/ts3.4/state/reducer/setReducer.d.ts +40 -0
- package/ts3.4/state/reducerInstance.d.ts +7 -0
- package/ts3.4/state/selectMeta.d.ts +3 -0
- package/ts3.4/types.d.ts +73 -0
- package/typescript.svg +8 -0
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
import { PollingArticleResource } from '__tests__/legacy-3';
|
|
2
|
+
|
|
3
|
+
import { initialState } from '../../state/reducer/createReducer';
|
|
4
|
+
import ConnectionListener from '../ConnectionListener';
|
|
5
|
+
import DefaultConnectionListener from '../DefaultConnectionListener';
|
|
6
|
+
import PollingSubscription from '../PollingSubscription';
|
|
7
|
+
|
|
8
|
+
class MockConnectionListener implements ConnectionListener {
|
|
9
|
+
declare online: boolean;
|
|
10
|
+
declare onlineHandlers: (() => void)[];
|
|
11
|
+
declare offlineHandlers: (() => void)[];
|
|
12
|
+
|
|
13
|
+
constructor(online: boolean) {
|
|
14
|
+
this.online = online;
|
|
15
|
+
this.onlineHandlers = [];
|
|
16
|
+
this.offlineHandlers = [];
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
isOnline() {
|
|
20
|
+
return this.online;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
addOnlineListener(handler: () => void) {
|
|
24
|
+
this.onlineHandlers.push(handler);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
removeOnlineListener(handler: () => void) {
|
|
28
|
+
this.onlineHandlers = this.onlineHandlers.filter(h => h !== handler);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
addOfflineListener(handler: () => void) {
|
|
32
|
+
this.offlineHandlers.push(handler);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
removeOfflineListener(handler: () => void) {
|
|
36
|
+
this.offlineHandlers = this.offlineHandlers.filter(h => h !== handler);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
trigger(event: 'offline' | 'online') {
|
|
40
|
+
switch (event) {
|
|
41
|
+
case 'offline':
|
|
42
|
+
this.online = false;
|
|
43
|
+
this.offlineHandlers.forEach(t => t());
|
|
44
|
+
break;
|
|
45
|
+
case 'online':
|
|
46
|
+
this.online = true;
|
|
47
|
+
this.onlineHandlers.forEach(t => t());
|
|
48
|
+
break;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
reset() {
|
|
53
|
+
this.offlineHandlers = [];
|
|
54
|
+
this.onlineHandlers = [];
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function onError(e: any) {
|
|
59
|
+
e.preventDefault();
|
|
60
|
+
}
|
|
61
|
+
beforeEach(() => {
|
|
62
|
+
if (typeof addEventListener === 'function')
|
|
63
|
+
addEventListener('error', onError);
|
|
64
|
+
});
|
|
65
|
+
afterEach(() => {
|
|
66
|
+
if (typeof removeEventListener === 'function')
|
|
67
|
+
removeEventListener('error', onError);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
describe('PollingSubscription', () => {
|
|
71
|
+
const dispatch = jest.fn();
|
|
72
|
+
const a = () => Promise.resolve({ id: 5, title: 'hi' });
|
|
73
|
+
const fetch = jest.fn(a);
|
|
74
|
+
let sub: PollingSubscription;
|
|
75
|
+
|
|
76
|
+
beforeAll(() => {
|
|
77
|
+
jest.useFakeTimers();
|
|
78
|
+
sub = new PollingSubscription(
|
|
79
|
+
{
|
|
80
|
+
key: 'test.com',
|
|
81
|
+
schema: PollingArticleResource,
|
|
82
|
+
fetch,
|
|
83
|
+
frequency: 5000,
|
|
84
|
+
getState: () => initialState,
|
|
85
|
+
},
|
|
86
|
+
dispatch,
|
|
87
|
+
);
|
|
88
|
+
});
|
|
89
|
+
afterAll(() => {
|
|
90
|
+
jest.useRealTimers();
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
afterAll(() => {
|
|
94
|
+
sub.cleanup();
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it('should throw on undefined frequency in construction', () => {
|
|
98
|
+
expect(
|
|
99
|
+
() =>
|
|
100
|
+
new PollingSubscription(
|
|
101
|
+
{
|
|
102
|
+
key: 'test.com',
|
|
103
|
+
schema: PollingArticleResource,
|
|
104
|
+
fetch,
|
|
105
|
+
getState: () => initialState,
|
|
106
|
+
},
|
|
107
|
+
dispatch,
|
|
108
|
+
),
|
|
109
|
+
).toThrow();
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
it('should call immediately', () => {
|
|
113
|
+
jest.advanceTimersByTime(1);
|
|
114
|
+
expect(dispatch.mock.calls.length).toBe(1);
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it('should call after period', () => {
|
|
118
|
+
dispatch.mockReset();
|
|
119
|
+
jest.advanceTimersByTime(5000);
|
|
120
|
+
expect(dispatch.mock.calls.length).toBe(1);
|
|
121
|
+
dispatch.mock.calls[0].forEach((element: any) => {
|
|
122
|
+
delete element?.meta?.createdAt;
|
|
123
|
+
});
|
|
124
|
+
expect(dispatch.mock.calls[0]).toMatchSnapshot();
|
|
125
|
+
jest.advanceTimersByTime(5000);
|
|
126
|
+
expect(dispatch.mock.calls.length).toBe(2);
|
|
127
|
+
dispatch.mock.calls[1].forEach((element: any) => {
|
|
128
|
+
delete element?.meta?.createdAt;
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
expect(dispatch.mock.calls[1]).toMatchSnapshot();
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
describe('add()', () => {
|
|
135
|
+
it('should take smaller frequency when another is added', () => {
|
|
136
|
+
sub.add(1000);
|
|
137
|
+
jest.advanceTimersByTime(1000 * 4);
|
|
138
|
+
expect(dispatch.mock.calls.length).toBe(2 + 4);
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
it('should not change frequency if same is added', () => {
|
|
142
|
+
dispatch.mockClear();
|
|
143
|
+
sub.add(1000);
|
|
144
|
+
jest.advanceTimersByTime(1000 * 13);
|
|
145
|
+
expect(dispatch.mock.calls.length).toBe(13);
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
it('should not change frequency if larger is added', () => {
|
|
149
|
+
dispatch.mockClear();
|
|
150
|
+
sub.add(7000);
|
|
151
|
+
sub.add(8000);
|
|
152
|
+
jest.advanceTimersByTime(1000 * 13);
|
|
153
|
+
expect(dispatch.mock.calls.length).toBe(13);
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
it('should do nothing if frequency is undefined', () => {
|
|
157
|
+
dispatch.mockClear();
|
|
158
|
+
sub.add(undefined);
|
|
159
|
+
jest.advanceTimersByTime(1000 * 13);
|
|
160
|
+
expect(dispatch.mock.calls.length).toBe(13);
|
|
161
|
+
});
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
describe('remove()', () => {
|
|
165
|
+
it('should not change frequency if only partially removed', () => {
|
|
166
|
+
dispatch.mockClear();
|
|
167
|
+
sub.remove(1000);
|
|
168
|
+
jest.advanceTimersByTime(1000 * 13);
|
|
169
|
+
expect(dispatch.mock.calls.length).toBe(13);
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
it('should not change frequency if only larger removed', () => {
|
|
173
|
+
dispatch.mockClear();
|
|
174
|
+
sub.remove(7000);
|
|
175
|
+
jest.advanceTimersByTime(1000 * 13);
|
|
176
|
+
expect(dispatch.mock.calls.length).toBe(13);
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
it('should go back to fastest if smallest frequency is removed completely', () => {
|
|
180
|
+
sub.remove(1000);
|
|
181
|
+
jest.advanceTimersByTime(1000);
|
|
182
|
+
dispatch.mockClear();
|
|
183
|
+
jest.advanceTimersByTime(5000 * 13);
|
|
184
|
+
expect(dispatch.mock.calls.length).toBe(13);
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
it('should do nothing if frequency is not registered', () => {
|
|
188
|
+
const oldError = console.error;
|
|
189
|
+
const spy = (console.error = jest.fn());
|
|
190
|
+
|
|
191
|
+
sub.remove(1000);
|
|
192
|
+
dispatch.mockClear();
|
|
193
|
+
jest.advanceTimersByTime(5000 * 13);
|
|
194
|
+
expect(dispatch.mock.calls.length).toBe(13);
|
|
195
|
+
|
|
196
|
+
expect(spy.mock.calls[0]).toMatchInlineSnapshot(`
|
|
197
|
+
[
|
|
198
|
+
"Mismatched remove: 1000 is not subscribed for test.com",
|
|
199
|
+
]
|
|
200
|
+
`);
|
|
201
|
+
console.error = oldError;
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
it('should do nothing if frequency is undefined', () => {
|
|
205
|
+
sub.remove(undefined);
|
|
206
|
+
dispatch.mockClear();
|
|
207
|
+
jest.advanceTimersByTime(5000 * 13);
|
|
208
|
+
expect(dispatch.mock.calls.length).toBe(13);
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
it('should return false until completely empty, then return true', () => {
|
|
212
|
+
const sub = new PollingSubscription(
|
|
213
|
+
{
|
|
214
|
+
key: 'test.com',
|
|
215
|
+
schema: PollingArticleResource,
|
|
216
|
+
fetch,
|
|
217
|
+
frequency: 5000,
|
|
218
|
+
getState: () => initialState,
|
|
219
|
+
},
|
|
220
|
+
dispatch,
|
|
221
|
+
);
|
|
222
|
+
sub.add(5000);
|
|
223
|
+
sub.add(7000);
|
|
224
|
+
expect(sub.remove(5000)).toBe(false);
|
|
225
|
+
expect(sub.remove(5000)).toBe(false);
|
|
226
|
+
expect(sub.remove(7000)).toBe(true);
|
|
227
|
+
});
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
describe('cleanup()', () => {
|
|
231
|
+
it('should stop all timers', () => {
|
|
232
|
+
dispatch.mockClear();
|
|
233
|
+
sub.cleanup();
|
|
234
|
+
jest.advanceTimersByTime(5000 * 13);
|
|
235
|
+
expect(dispatch.mock.calls.length).toBe(0);
|
|
236
|
+
});
|
|
237
|
+
it('should be idempotent', () => {
|
|
238
|
+
sub.cleanup();
|
|
239
|
+
});
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
describe('offline support', () => {
|
|
243
|
+
function createMocks(listener: ConnectionListener) {
|
|
244
|
+
const dispatch = jest.fn();
|
|
245
|
+
const a = () => Promise.resolve({ id: 5, title: 'hi' });
|
|
246
|
+
const fetch = jest.fn(a);
|
|
247
|
+
|
|
248
|
+
const pollingSubscription = new PollingSubscription(
|
|
249
|
+
{
|
|
250
|
+
key: 'test.com',
|
|
251
|
+
schema: PollingArticleResource,
|
|
252
|
+
fetch,
|
|
253
|
+
frequency: 5000,
|
|
254
|
+
getState: () => initialState,
|
|
255
|
+
},
|
|
256
|
+
dispatch,
|
|
257
|
+
listener,
|
|
258
|
+
);
|
|
259
|
+
jest.advanceTimersByTime(1);
|
|
260
|
+
return { dispatch, fetch, pollingSubscription };
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
it('should not dispatch when offline', () => {
|
|
264
|
+
jest.spyOn(navigator, 'onLine', 'get').mockReturnValue(false);
|
|
265
|
+
const listener = new DefaultConnectionListener();
|
|
266
|
+
const offlineSpy = jest.spyOn(listener, 'addOfflineListener');
|
|
267
|
+
const onlineSpy = jest.spyOn(listener, 'addOnlineListener');
|
|
268
|
+
const { dispatch } = createMocks(listener);
|
|
269
|
+
jest.advanceTimersByTime(50000);
|
|
270
|
+
jest.spyOn(navigator, 'onLine', 'get').mockReturnValue(true);
|
|
271
|
+
expect(dispatch.mock.calls.length).toBe(0);
|
|
272
|
+
expect(offlineSpy.mock.calls.length).toBe(0);
|
|
273
|
+
expect(onlineSpy.mock.calls.length).toBe(1);
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
it('should not dispatch when onLine is undefined (default to true)', () => {
|
|
277
|
+
jest.spyOn(navigator, 'onLine', 'get').mockReturnValue(undefined as any);
|
|
278
|
+
const listener = new DefaultConnectionListener();
|
|
279
|
+
const offlineSpy = jest.spyOn(listener, 'addOfflineListener');
|
|
280
|
+
const onlineSpy = jest.spyOn(listener, 'addOnlineListener');
|
|
281
|
+
const { dispatch } = createMocks(listener);
|
|
282
|
+
expect(dispatch.mock.calls.length).toBe(1);
|
|
283
|
+
expect(offlineSpy.mock.calls.length).toBe(1);
|
|
284
|
+
expect(onlineSpy.mock.calls.length).toBe(0);
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
it('should immediately start fetching when online', () => {
|
|
288
|
+
const listener = new MockConnectionListener(false);
|
|
289
|
+
const { dispatch } = createMocks(listener);
|
|
290
|
+
expect(dispatch.mock.calls.length).toBe(0);
|
|
291
|
+
|
|
292
|
+
listener.trigger('online');
|
|
293
|
+
jest.advanceTimersByTime(1);
|
|
294
|
+
expect(dispatch.mock.calls.length).toBe(1);
|
|
295
|
+
jest.advanceTimersByTime(5000);
|
|
296
|
+
expect(dispatch.mock.calls.length).toBe(2);
|
|
297
|
+
expect(listener.offlineHandlers.length).toBe(1);
|
|
298
|
+
expect(listener.onlineHandlers.length).toBe(0);
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
it('should stop dispatching when offline again', () => {
|
|
302
|
+
const listener = new MockConnectionListener(true);
|
|
303
|
+
const { dispatch } = createMocks(listener);
|
|
304
|
+
expect(dispatch.mock.calls.length).toBe(1);
|
|
305
|
+
|
|
306
|
+
listener.trigger('offline');
|
|
307
|
+
jest.advanceTimersByTime(50000);
|
|
308
|
+
expect(dispatch.mock.calls.length).toBe(1);
|
|
309
|
+
expect(listener.offlineHandlers.length).toBe(0);
|
|
310
|
+
expect(listener.onlineHandlers.length).toBe(1);
|
|
311
|
+
});
|
|
312
|
+
});
|
|
313
|
+
});
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import { Article, PollingArticleResource } from '__tests__/new';
|
|
2
|
+
|
|
3
|
+
import { actionTypes, Controller, initialState } from '../..';
|
|
4
|
+
import { legacyActions } from '../../types';
|
|
5
|
+
import SubscriptionManager, { Subscription } from '../SubscriptionManager.js';
|
|
6
|
+
|
|
7
|
+
type SubscribeAction = legacyActions.SubscribeAction;
|
|
8
|
+
|
|
9
|
+
type UnsubscribeAction = legacyActions.UnsubscribeAction;
|
|
10
|
+
|
|
11
|
+
const { UNSUBSCRIBE_TYPE, SUBSCRIBE_TYPE, RECEIVE_TYPE } = actionTypes;
|
|
12
|
+
|
|
13
|
+
function onError(e: any) {
|
|
14
|
+
e.preventDefault();
|
|
15
|
+
}
|
|
16
|
+
beforeEach(() => {
|
|
17
|
+
if (typeof addEventListener === 'function')
|
|
18
|
+
addEventListener('error', onError);
|
|
19
|
+
});
|
|
20
|
+
afterEach(() => {
|
|
21
|
+
if (typeof removeEventListener === 'function')
|
|
22
|
+
removeEventListener('error', onError);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
describe('SubscriptionManager', () => {
|
|
26
|
+
class TestSubscription implements Subscription {
|
|
27
|
+
add = jest.fn();
|
|
28
|
+
remove = jest.fn(() => true);
|
|
29
|
+
cleanup = jest.fn();
|
|
30
|
+
}
|
|
31
|
+
const manager = new SubscriptionManager(TestSubscription);
|
|
32
|
+
const getState = () => initialState;
|
|
33
|
+
|
|
34
|
+
describe('getMiddleware()', () => {
|
|
35
|
+
it('should return the same value every call', () => {
|
|
36
|
+
const a = manager.getMiddleware();
|
|
37
|
+
expect(a).toBe(manager.getMiddleware());
|
|
38
|
+
expect(a).toBe(manager.getMiddleware());
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
describe('cleanup()', () => {
|
|
43
|
+
it('should cleanup all Subcription members', () => {
|
|
44
|
+
const sub = new TestSubscription();
|
|
45
|
+
(manager as any).subscriptions[
|
|
46
|
+
PollingArticleResource.get.key({ id: 5 })
|
|
47
|
+
] = sub;
|
|
48
|
+
manager.cleanup();
|
|
49
|
+
expect(sub.cleanup.mock.calls.length).toBe(1);
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
describe('middleware', () => {
|
|
54
|
+
function createSubscribeAction(
|
|
55
|
+
payload: Record<string, any>,
|
|
56
|
+
reject = false,
|
|
57
|
+
): SubscribeAction {
|
|
58
|
+
const fetch = reject
|
|
59
|
+
? () => Promise.reject(new Error('Failed'))
|
|
60
|
+
: () => Promise.resolve(payload);
|
|
61
|
+
return {
|
|
62
|
+
type: SUBSCRIBE_TYPE,
|
|
63
|
+
meta: {
|
|
64
|
+
schema: Article,
|
|
65
|
+
key: PollingArticleResource.get.key({ id: payload.id }),
|
|
66
|
+
fetch,
|
|
67
|
+
options: { pollFrequency: 1000 },
|
|
68
|
+
},
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
function createUnsubscribeAction(
|
|
72
|
+
payload: Record<string, any>,
|
|
73
|
+
): UnsubscribeAction {
|
|
74
|
+
return {
|
|
75
|
+
type: UNSUBSCRIBE_TYPE,
|
|
76
|
+
meta: {
|
|
77
|
+
key: PollingArticleResource.get.key({ id: payload.id }),
|
|
78
|
+
options: { pollFrequency: 1000 },
|
|
79
|
+
},
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const manager = new SubscriptionManager(TestSubscription);
|
|
84
|
+
const middleware = manager.getMiddleware();
|
|
85
|
+
const next = jest.fn();
|
|
86
|
+
const dispatch = () => Promise.resolve();
|
|
87
|
+
const controller = new Controller({ dispatch, getState });
|
|
88
|
+
const API: Controller & { controller: Controller } = Object.create(
|
|
89
|
+
controller,
|
|
90
|
+
{
|
|
91
|
+
controller: { value: controller },
|
|
92
|
+
},
|
|
93
|
+
);
|
|
94
|
+
it('subscribe should add a subscription', () => {
|
|
95
|
+
const action = createSubscribeAction({ id: 5 });
|
|
96
|
+
middleware(API)(next)(action);
|
|
97
|
+
|
|
98
|
+
expect(next).not.toHaveBeenCalled();
|
|
99
|
+
expect((manager as any).subscriptions[action.meta.key]).toBeDefined();
|
|
100
|
+
});
|
|
101
|
+
it('subscribe should add a subscription (no frequency)', () => {
|
|
102
|
+
const action = createSubscribeAction({ id: 597 });
|
|
103
|
+
delete action.meta.options;
|
|
104
|
+
middleware(API)(next)(action);
|
|
105
|
+
|
|
106
|
+
expect(next).not.toHaveBeenCalled();
|
|
107
|
+
expect((manager as any).subscriptions[action.meta.key]).toBeDefined();
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
it('subscribe with same should call subscription.add', () => {
|
|
111
|
+
const action = createSubscribeAction({ id: 5, title: 'four' });
|
|
112
|
+
middleware(API)(next)(action);
|
|
113
|
+
|
|
114
|
+
expect(
|
|
115
|
+
(manager as any).subscriptions[action.meta.key].add.mock.calls.length,
|
|
116
|
+
).toBe(1);
|
|
117
|
+
middleware(API)(next)(action);
|
|
118
|
+
expect(
|
|
119
|
+
(manager as any).subscriptions[action.meta.key].add.mock.calls.length,
|
|
120
|
+
).toBe(2);
|
|
121
|
+
});
|
|
122
|
+
it('subscribe with another should create another', () => {
|
|
123
|
+
const action = createSubscribeAction({ id: 7, title: 'four cakes' });
|
|
124
|
+
middleware(API)(next)(action);
|
|
125
|
+
|
|
126
|
+
expect((manager as any).subscriptions[action.meta.key]).toBeDefined();
|
|
127
|
+
expect(
|
|
128
|
+
(manager as any).subscriptions[action.meta.key].add.mock.calls.length,
|
|
129
|
+
).toBe(0);
|
|
130
|
+
});
|
|
131
|
+
it('subscribe with another should not call previous', () => {
|
|
132
|
+
expect(
|
|
133
|
+
(manager as any).subscriptions[
|
|
134
|
+
PollingArticleResource.get.key({ id: 5 })
|
|
135
|
+
].add.mock.calls.length,
|
|
136
|
+
).toBe(2);
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
it('unsubscribe should delete when remove returns true', () => {
|
|
140
|
+
const action = createUnsubscribeAction({ id: 7, title: 'four cakes' });
|
|
141
|
+
(manager as any).subscriptions[action.meta.key].remove.mockImplementation(
|
|
142
|
+
() => true,
|
|
143
|
+
);
|
|
144
|
+
|
|
145
|
+
middleware(API)(next)(action);
|
|
146
|
+
|
|
147
|
+
expect((manager as any).subscriptions[action.meta.key]).not.toBeDefined();
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
it('unsubscribe should delete when remove returns true (no frequency)', () => {
|
|
151
|
+
middleware(API)(next)(
|
|
152
|
+
createSubscribeAction({ id: 50, title: 'four cakes' }),
|
|
153
|
+
);
|
|
154
|
+
|
|
155
|
+
const action = createUnsubscribeAction({ id: 50, title: 'four cakes' });
|
|
156
|
+
delete action.meta.options;
|
|
157
|
+
(manager as any).subscriptions[action.meta.key].remove.mockImplementation(
|
|
158
|
+
() => true,
|
|
159
|
+
);
|
|
160
|
+
|
|
161
|
+
middleware(API)(next)(action);
|
|
162
|
+
|
|
163
|
+
expect((manager as any).subscriptions[action.meta.key]).not.toBeDefined();
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
it('unsubscribe should not delete when remove returns false', () => {
|
|
167
|
+
const action = createUnsubscribeAction({ id: 5, title: 'four cakes' });
|
|
168
|
+
(manager as any).subscriptions[action.meta.key].remove.mockImplementation(
|
|
169
|
+
() => false,
|
|
170
|
+
);
|
|
171
|
+
|
|
172
|
+
middleware(API)(next)(action);
|
|
173
|
+
|
|
174
|
+
expect((manager as any).subscriptions[action.meta.key]).toBeDefined();
|
|
175
|
+
expect(
|
|
176
|
+
(manager as any).subscriptions[action.meta.key].remove.mock.calls
|
|
177
|
+
.length,
|
|
178
|
+
).toBe(1);
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
it('unsubscribe should do nothing when there was no subscription already', () => {
|
|
182
|
+
const oldError = console.error;
|
|
183
|
+
const spy = (console.error = jest.fn());
|
|
184
|
+
|
|
185
|
+
const action = createUnsubscribeAction({ id: 25 });
|
|
186
|
+
|
|
187
|
+
middleware(API)(next)(action);
|
|
188
|
+
|
|
189
|
+
expect((manager as any).subscriptions[action.meta.key]).not.toBeDefined();
|
|
190
|
+
|
|
191
|
+
expect(spy.mock.calls[0]).toMatchInlineSnapshot(`
|
|
192
|
+
[
|
|
193
|
+
"Mismatched unsubscribe: GET http://test.com/article/25 is not subscribed",
|
|
194
|
+
]
|
|
195
|
+
`);
|
|
196
|
+
console.error = oldError;
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
it('should let other actions pass through', () => {
|
|
200
|
+
const action = { type: RECEIVE_TYPE };
|
|
201
|
+
next.mockReset();
|
|
202
|
+
|
|
203
|
+
middleware(API)(next)(action as any);
|
|
204
|
+
|
|
205
|
+
expect(next.mock.calls.length).toBe(1);
|
|
206
|
+
});
|
|
207
|
+
});
|
|
208
|
+
});
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type Controller from '../controller/Controller.js';
|
|
2
|
+
import type { Reducer, Dispatch, ReducerState } from '../middlewareTypes.js';
|
|
3
|
+
import { Manager } from '../types.js';
|
|
4
|
+
|
|
5
|
+
export default function applyManager(
|
|
6
|
+
managers: Manager[],
|
|
7
|
+
controller: Controller,
|
|
8
|
+
): Middleware[] {
|
|
9
|
+
return managers.map(manager => {
|
|
10
|
+
const middleware = manager.getMiddleware();
|
|
11
|
+
return ({ dispatch, getState }) => {
|
|
12
|
+
(controller as any).dispatch = dispatch;
|
|
13
|
+
(controller as any).getState = getState;
|
|
14
|
+
// this is needed for backwards compatibility as we added 'controller' prop previously
|
|
15
|
+
const API = Object.create(controller, {
|
|
16
|
+
controller: { value: controller },
|
|
17
|
+
});
|
|
18
|
+
// controller is a superset of the middleware API
|
|
19
|
+
return middleware(API);
|
|
20
|
+
};
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/* These should be compatible with redux */
|
|
25
|
+
export interface MiddlewareAPI<
|
|
26
|
+
R extends Reducer<any, any> = Reducer<any, any>,
|
|
27
|
+
> {
|
|
28
|
+
getState: () => ReducerState<R>;
|
|
29
|
+
dispatch: Dispatch<R>;
|
|
30
|
+
}
|
|
31
|
+
export type Middleware = <R extends Reducer<any, any>>({
|
|
32
|
+
dispatch,
|
|
33
|
+
}: MiddlewareAPI<R>) => (next: Dispatch<R>) => Dispatch<R>;
|