@olea-bps/base 1.0.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/components/AppBar/index.js +84 -0
- package/components/AppBar/styles.js +27 -0
- package/components/BookDetail/index.js +268 -0
- package/components/BookDetail/styles.js +124 -0
- package/components/ComponentJobsFilter/index.js +188 -0
- package/components/ComponentJobsFilter/styles.js +12 -0
- package/components/ConnectivityWarning/index.js +65 -0
- package/components/ConnectivityWarning/styles.js +19 -0
- package/components/ContactDetail/index.js +232 -0
- package/components/ContactDetail/styles.js +32 -0
- package/components/CourseDetail/index.js +357 -0
- package/components/CourseDetail/styles.js +59 -0
- package/components/CourseDetailDialog/index.js +169 -0
- package/components/CourseDetailDialog/styles.js +116 -0
- package/components/CourseInfo/index.js +219 -0
- package/components/CourseInfo/styles.js +40 -0
- package/components/DevelopmentDialog/index.js +214 -0
- package/components/DevelopmentDialog/styles.js +10 -0
- package/components/EventCodeInput/index.js +149 -0
- package/components/EventCodeInput/styles.js +108 -0
- package/components/FlexMenuEntry/index.js +84 -0
- package/components/FlexMenuEntry/styles.js +27 -0
- package/components/MainMenuEntry/index.js +88 -0
- package/components/MainMenuEntry/styles.js +28 -0
- package/components/MealItem/index.js +87 -0
- package/components/MealItem/styles.js +84 -0
- package/components/MensaMenu/index.js +305 -0
- package/components/MensaMenu/styles.js +94 -0
- package/components/MensaSlider/index.js +188 -0
- package/components/MensaSlider/styles.js +53 -0
- package/components/Modal/index.js +106 -0
- package/components/Modal/styles.js +8 -0
- package/components/NewsDetail/index.js +377 -0
- package/components/NewsDetail/styles.js +77 -0
- package/components/NewsList/index.js +120 -0
- package/components/NewsList/styles.js +19 -0
- package/components/NewsListItem/index.js +89 -0
- package/components/NewsListItem/styles.js +32 -0
- package/components/OtherCourses/index.js +152 -0
- package/components/OtherCourses/styles.js +10 -0
- package/components/PtsDeparture/index.js +140 -0
- package/components/PtsDeparture/styles.js +7 -0
- package/components/PtsStation/index.js +186 -0
- package/components/PtsStation/styles.js +47 -0
- package/components/QuickLinks/index.js +127 -0
- package/components/QuickLinks/styles.js +45 -0
- package/components/RoomDetail/index.js +281 -0
- package/components/RoomDetail/styles.js +56 -0
- package/components/ScaledImage/index.js +92 -0
- package/components/SearchResults/index.js +362 -0
- package/components/SearchResults/styles.js +59 -0
- package/components/SettingSection/index.js +54 -0
- package/components/SettingSection/styles.js +15 -0
- package/components/SettingsDialog/index.js +52 -0
- package/components/SettingsDialog/styles.js +12 -0
- package/components/SettingsDialogRadio/index.js +66 -0
- package/components/SettingsDialogRadio/styles.js +7 -0
- package/components/SettingsDialogSelect/index.js +73 -0
- package/components/SettingsDialogSelect/styles.js +7 -0
- package/components/TimetableCodeInput/index.js +205 -0
- package/components/TimetableCodeInput/styles.js +28 -0
- package/components/TimetableDay/index.js +252 -0
- package/components/TimetableDay/styles.js +103 -0
- package/components/TimetableEvent/index.js +163 -0
- package/components/TimetableEvent/styles.js +108 -0
- package/components/TimetableList/index.js +180 -0
- package/components/TimetableList/styles.js +106 -0
- package/components/TimetableMonth/index.js +156 -0
- package/components/TimetableMonth/styles.js +29 -0
- package/components/TimetableWeek/index.js +230 -0
- package/components/TimetableWeek/styles.js +58 -0
- package/components/TopNews/index.js +282 -0
- package/components/TopNews/styles.js +125 -0
- package/components/TopNewsHtwk/index.js +279 -0
- package/components/TopNewsHtwk/styles.js +142 -0
- package/components/WebView/index.js +108 -0
- package/components/WebView/styles.js +11 -0
- package/context/context-callmanager/TucCallManager.js +74 -0
- package/context/context-callmanager/index.js +128 -0
- package/context/context-canteen/AsistServerApiProvider.js +126 -0
- package/context/context-canteen/CollektorVersion2ApiProvider.js +100 -0
- package/context/context-canteen/index.js +692 -0
- package/context/context-connectivity/index.js +85 -0
- package/context/context-event/AsistServerClient.js +82 -0
- package/context/context-event/index.js +205 -0
- package/context/context-flex-menu/AsistServerApiProvider.js +0 -0
- package/context/context-flex-menu/CollectorVersion2ApiProvider.js +32 -0
- package/context/context-flex-menu/index.js +160 -0
- package/context/context-info-dialog/CollektorVersion2ApiProvider.js +53 -0
- package/context/context-info-dialog/ProviderErrors.js +22 -0
- package/context/context-info-dialog/index.js +190 -0
- package/context/context-mails/MailService.js +147 -0
- package/context/context-mails/TucUnseenApiProvider.js +65 -0
- package/context/context-mails/index.js +131 -0
- package/context/context-news/CollektorVersion2ApiProvider.js +68 -0
- package/context/context-news/index.js +155 -0
- package/context/context-public-transport-ticket/CollektorVersion2ApiProvider.js +45 -0
- package/context/context-public-transport-ticket/ProviderErrors.js +11 -0
- package/context/context-public-transport-ticket/index.js +175 -0
- package/context/context-timetable/CollectorVersion2ApiProvider.js +102 -0
- package/context/context-timetable/ProviderErrors.js +22 -0
- package/context/context-timetable/TimetableService.js +357 -0
- package/context/context-timetable/index.js +215 -0
- package/context/context-user/index.js +586 -0
- package/index.js +178 -0
- package/libraries/base-api-provider/hooks.js +24 -0
- package/libraries/base-api-provider/http-api-provider.js +168 -0
- package/libraries/base-api-provider/index.js +20 -0
- package/libraries/core/api/canteens.js +88 -0
- package/libraries/core/api/corona.js +45 -0
- package/libraries/core/api/courses.js +54 -0
- package/libraries/core/api/device.js +98 -0
- package/libraries/core/api/feed.js +144 -0
- package/libraries/core/api/feedback.js +123 -0
- package/libraries/core/api/jobs.js +48 -0
- package/libraries/core/api/library.js +67 -0
- package/libraries/core/api/lostfound.js +77 -0
- package/libraries/core/api/occupancy.js +44 -0
- package/libraries/core/api/officehours.js +25 -0
- package/libraries/core/api/poi.js +25 -0
- package/libraries/core/api/pts.js +68 -0
- package/libraries/core/api/rooms.js +31 -0
- package/libraries/core/api/survey.js +121 -0
- package/libraries/core/api/telephone.js +32 -0
- package/libraries/core/api/tucrooms.js +31 -0
- package/libraries/core/constants/actions.js +75 -0
- package/libraries/core/constants/api.js +146 -0
- package/libraries/core/constants/enums.js +5 -0
- package/libraries/core/helper/date.js +9 -0
- package/libraries/core/helper/format.helper.js +2 -0
- package/libraries/core/index.js +60 -0
- package/libraries/core/redux/actions/notifications.js +6 -0
- package/libraries/core/redux/actions/setting.js +75 -0
- package/libraries/core/redux/actions/state.js +74 -0
- package/libraries/core/redux/asyncDispatch.js +27 -0
- package/libraries/core/redux/reducers/api.js +190 -0
- package/libraries/core/redux/reducers/index.js +32 -0
- package/libraries/core/redux/reducers/notifications.js +16 -0
- package/libraries/core/redux/reducers/setting.js +99 -0
- package/libraries/core/redux/reducers/state.js +122 -0
- package/libraries/core/redux/selectors.js +123 -0
- package/libraries/core/redux/store.js +40 -0
- package/libraries/core/services/data.js +96 -0
- package/libraries/core/services/root-navigation.js +36 -0
- package/libraries/core/services/search.js +175 -0
- package/libraries/core-plugins/enums/replaceTypes.js +53 -0
- package/libraries/core-plugins/index.js +21 -0
- package/libraries/core-plugins/redux/actions/plugin.js +13 -0
- package/libraries/core-plugins/redux/reducers/plugin.js +210 -0
- package/libraries/icons-openasist/index.js +143 -0
- package/libraries/notifications/index.js +268 -0
- package/libraries/notifications/services/subscriber.service.js +123 -0
- package/libraries/react-native-webview-autoheight/README.md +47 -0
- package/libraries/react-native-webview-autoheight/index.js +140 -0
- package/libraries/react-native-webview-autoheight/package.json +37 -0
- package/libraries/react-native-webview-autoheight/utils.js +268 -0
- package/libraries/redux-offline/.eslintignore +1 -0
- package/libraries/redux-offline/.github/ISSUE_TEMPLATE/redux-offline-core-issues-template.md +25 -0
- package/libraries/redux-offline/.prettierrc +6 -0
- package/libraries/redux-offline/LICENSE +21 -0
- package/libraries/redux-offline/README.md +133 -0
- package/libraries/redux-offline/flow-typed/npm/redux-logger_vx.x.x.js +109 -0
- package/libraries/redux-offline/flow-typed/npm/redux_v4.x.x.js +100 -0
- package/libraries/redux-offline/lib/actions.js +46 -0
- package/libraries/redux-offline/lib/config.js +15 -0
- package/libraries/redux-offline/lib/constants.js +17 -0
- package/libraries/redux-offline/lib/defaults/defaultCommit.js +11 -0
- package/libraries/redux-offline/lib/defaults/defaultRollback.js +11 -0
- package/libraries/redux-offline/lib/defaults/detectNetwork.js +33 -0
- package/libraries/redux-offline/lib/defaults/detectNetwork.native.js +211 -0
- package/libraries/redux-offline/lib/defaults/discard.js +18 -0
- package/libraries/redux-offline/lib/defaults/effect.js +87 -0
- package/libraries/redux-offline/lib/defaults/index.js +32 -0
- package/libraries/redux-offline/lib/defaults/offlineStateLens.js +24 -0
- package/libraries/redux-offline/lib/defaults/persist.js +9 -0
- package/libraries/redux-offline/lib/defaults/persist.native.js +17 -0
- package/libraries/redux-offline/lib/defaults/persistAutoRehydrate.js +8 -0
- package/libraries/redux-offline/lib/defaults/queue.js +27 -0
- package/libraries/redux-offline/lib/defaults/retry.js +29 -0
- package/libraries/redux-offline/lib/index.js +115 -0
- package/libraries/redux-offline/lib/middleware.js +56 -0
- package/libraries/redux-offline/lib/offlineActionTracker.js +43 -0
- package/libraries/redux-offline/lib/send.js +112 -0
- package/libraries/redux-offline/lib/types.js +3 -0
- package/libraries/redux-offline/lib/updater.js +111 -0
- package/libraries/redux-offline/package.json +69 -0
- package/libraries/redux-offline/src/__tests__/defaults/discard.js +22 -0
- package/libraries/redux-offline/src/__tests__/defaults/effect.js +118 -0
- package/libraries/redux-offline/src/__tests__/index.js +116 -0
- package/libraries/redux-offline/src/__tests__/middleware.js +185 -0
- package/libraries/redux-offline/src/__tests__/offlineActionTracker.js +54 -0
- package/libraries/redux-offline/src/__tests__/send.js +208 -0
- package/libraries/redux-offline/src/actions.js +36 -0
- package/libraries/redux-offline/src/config.js +9 -0
- package/libraries/redux-offline/src/constants.js +18 -0
- package/libraries/redux-offline/src/defaults/defaultCommit.js +7 -0
- package/libraries/redux-offline/src/defaults/defaultRollback.js +7 -0
- package/libraries/redux-offline/src/defaults/detectNetwork.js +16 -0
- package/libraries/redux-offline/src/defaults/detectNetwork.native.js +148 -0
- package/libraries/redux-offline/src/defaults/discard.js +19 -0
- package/libraries/redux-offline/src/defaults/effect.js +86 -0
- package/libraries/redux-offline/src/defaults/index.js +25 -0
- package/libraries/redux-offline/src/defaults/offlineStateLens.js +11 -0
- package/libraries/redux-offline/src/defaults/persist.js +4 -0
- package/libraries/redux-offline/src/defaults/persist.native.js +9 -0
- package/libraries/redux-offline/src/defaults/persistAutoRehydrate.js +4 -0
- package/libraries/redux-offline/src/defaults/queue.js +20 -0
- package/libraries/redux-offline/src/defaults/retry.js +18 -0
- package/libraries/redux-offline/src/index.js +138 -0
- package/libraries/redux-offline/src/middleware.js +52 -0
- package/libraries/redux-offline/src/offlineActionTracker.js +40 -0
- package/libraries/redux-offline/src/send.js +111 -0
- package/libraries/redux-offline/src/types.js +127 -0
- package/libraries/redux-offline/src/updater.js +141 -0
- package/libraries/redux-offline/typings.d.ts +150 -0
- package/libraries/redux-offline/yarn.lock +6460 -0
- package/libraries/stored-state/index.js +155 -0
- package/package.json +68 -0
- package/views/Callmanager/index.js +148 -0
- package/views/Callmanager/styles.js +62 -0
- package/views/Canteens/index.js +383 -0
- package/views/Canteens/styles.js +89 -0
- package/views/Dashboard/index.js +176 -0
- package/views/Dashboard/styles.js +16 -0
- package/views/DashboardHtwk/index.js +197 -0
- package/views/DashboardHtwk/styles.js +24 -0
- package/views/Event/index.js +373 -0
- package/views/Event/styles.js +63 -0
- package/views/FeedList/index.js +165 -0
- package/views/FeedList/styles.js +15 -0
- package/views/FeedNews/index.js +243 -0
- package/views/FeedNews/styles.js +32 -0
- package/views/Flexmenu/index.js +185 -0
- package/views/Flexmenu/styles.js +62 -0
- package/views/FlexmenuWebview/index.js +167 -0
- package/views/FlexmenuWebview/styles.js +16 -0
- package/views/Howhy/index.js +151 -0
- package/views/Howhy/styles.js +11 -0
- package/views/Jobs/index.js +217 -0
- package/views/Jobs/styles.js +41 -0
- package/views/MainMenu/index.js +283 -0
- package/views/MainMenu/styles.js +36 -0
- package/views/NewsTabbar/index.js +164 -0
- package/views/NewsTabbar/styles.js +27 -0
- package/views/Opal/index.js +136 -0
- package/views/Opal/styles.js +11 -0
- package/views/PublicTransportTicket/index.js +173 -0
- package/views/PublicTransportTicket/styles.js +17 -0
- package/views/Search/index.js +186 -0
- package/views/Search/styles.js +67 -0
- package/views/SettingsAccessibility/index.js +148 -0
- package/views/SettingsAccessibility/styles.js +13 -0
- package/views/SettingsAppInfo/index.js +86 -0
- package/views/SettingsAppInfo/styles.js +18 -0
- package/views/SettingsCanteens/index.js +249 -0
- package/views/SettingsCanteens/styles.js +33 -0
- package/views/SettingsGeneral/index.js +173 -0
- package/views/SettingsGeneral/styles.js +28 -0
- package/views/SettingsNotifications/index.js +204 -0
- package/views/SettingsNotifications/styles.js +38 -0
- package/views/SettingsUser/index.js +170 -0
- package/views/SettingsUser/styles.js +17 -0
- package/views/TimetableCalendar/index.js +387 -0
- package/views/TimetableCalendar/styles.js +121 -0
- package/views/TimetableList/index.js +402 -0
- package/views/TimetableList/styles.js +55 -0
- package/views/Webviews/index.js +158 -0
- package/views/Webviews/styles.js +11 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import discard from '../../defaults/discard';
|
|
2
|
+
|
|
3
|
+
test('discards non-http error', () => {
|
|
4
|
+
const error = { message: 'Non-http error' };
|
|
5
|
+
const action = { type: 'DISCARD', meta: { offline: { effect: {} } } };
|
|
6
|
+
|
|
7
|
+
expect(discard(error, action)).toEqual(true);
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
test('discards http 4xx errors', () => {
|
|
11
|
+
const error = { status: 404 };
|
|
12
|
+
const action = { type: 'DISCARD', meta: { offline: { effect: {} } } };
|
|
13
|
+
|
|
14
|
+
expect(discard(error, action)).toEqual(true);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
test('does not discard http 5xx errors', () => {
|
|
18
|
+
const error = { status: 500 };
|
|
19
|
+
const action = { type: 'DISCARD', meta: { offline: { effect: {} } } };
|
|
20
|
+
|
|
21
|
+
expect(discard(error, action)).toEqual(false);
|
|
22
|
+
});
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import effectReconciler, { getHeaders } from '../../defaults/effect';
|
|
2
|
+
|
|
3
|
+
function fetch(body) {
|
|
4
|
+
return Promise.resolve({
|
|
5
|
+
ok: true,
|
|
6
|
+
headers: { get: jest.fn(() => 'application/json') },
|
|
7
|
+
text: jest.fn(() => Promise.resolve(body))
|
|
8
|
+
});
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
let globalFetch;
|
|
12
|
+
|
|
13
|
+
beforeAll(() => {
|
|
14
|
+
globalFetch = global.fetch;
|
|
15
|
+
});
|
|
16
|
+
afterAll(() => {
|
|
17
|
+
global.fetch = globalFetch;
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
test('effector accept JSON stringified object', () => {
|
|
21
|
+
const body = {
|
|
22
|
+
email: 'email@example.com',
|
|
23
|
+
password: 'p4ssw0rd'
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
global.fetch = jest.fn((url, options) => {
|
|
27
|
+
expect(options.headers['content-type']).toEqual('application/json');
|
|
28
|
+
expect(JSON.parse(options.body)).toEqual(body);
|
|
29
|
+
|
|
30
|
+
return fetch('');
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
return effectReconciler({ body: JSON.stringify(body) }).then(body2 => {
|
|
34
|
+
expect(body2).toEqual(null);
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
test('effector accept JSON object', () => {
|
|
39
|
+
const json = {
|
|
40
|
+
email: 'email@example.com',
|
|
41
|
+
password: 'p4ssw0rd'
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
global.fetch = jest.fn((url, options) => {
|
|
45
|
+
expect(options.headers['content-type']).toEqual('application/json');
|
|
46
|
+
expect(JSON.parse(options.body)).toEqual(json);
|
|
47
|
+
|
|
48
|
+
return fetch('');
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
return effectReconciler({ json }).then(body2 => {
|
|
52
|
+
expect(body2).toEqual(null);
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
test('effector rejects invalid JSON object', () => {
|
|
57
|
+
const circularObject = {};
|
|
58
|
+
circularObject.self = circularObject;
|
|
59
|
+
|
|
60
|
+
return effectReconciler({ json: circularObject }).catch(error => {
|
|
61
|
+
expect(error).toBeInstanceOf(TypeError);
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
test('effector receive JSON and response objects', () => {
|
|
66
|
+
const body = { id: 1234 };
|
|
67
|
+
|
|
68
|
+
global.fetch = jest.fn(() => fetch(JSON.stringify(body)));
|
|
69
|
+
|
|
70
|
+
return effectReconciler({}).then(body2 => {
|
|
71
|
+
expect(body2).toEqual(body);
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
test('effector accepts content-type and Content-Type headers', () => {
|
|
76
|
+
const otherHeaders = { 'other-one': 'other-one', 'other-two': 'other-two' };
|
|
77
|
+
const formUrlEncoded = 'application/x-www-form-urlencoded';
|
|
78
|
+
|
|
79
|
+
const noHeaders = undefined;
|
|
80
|
+
const capitalizedHeaders = {
|
|
81
|
+
'Content-Type': formUrlEncoded,
|
|
82
|
+
...otherHeaders
|
|
83
|
+
};
|
|
84
|
+
const lowerCasedHeaders = { 'content-type': formUrlEncoded, ...otherHeaders };
|
|
85
|
+
|
|
86
|
+
expect(getHeaders(noHeaders)).toEqual({ 'content-type': 'application/json' });
|
|
87
|
+
expect(getHeaders(capitalizedHeaders)).toEqual({
|
|
88
|
+
'content-type': formUrlEncoded,
|
|
89
|
+
...otherHeaders
|
|
90
|
+
});
|
|
91
|
+
expect(getHeaders(lowerCasedHeaders)).toEqual({
|
|
92
|
+
'content-type': formUrlEncoded,
|
|
93
|
+
...otherHeaders
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
test('effector receives object as multipart/form-data', () => {
|
|
98
|
+
const body = new FormData();
|
|
99
|
+
body.append('id', 1234);
|
|
100
|
+
body.append('name', 'john');
|
|
101
|
+
body.forEach((value, key) => {
|
|
102
|
+
body[key] = value;
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
global.fetch = jest.fn((url, options) => {
|
|
106
|
+
expect(options.headers['content-type']).toEqual('multipart/form-data');
|
|
107
|
+
expect(options.body).toBeInstanceOf(FormData);
|
|
108
|
+
expect(options.body.get('id')).toBe('1234');
|
|
109
|
+
return fetch('');
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
return effectReconciler({
|
|
113
|
+
body,
|
|
114
|
+
headers: { 'content-type': 'multipart/form-data' }
|
|
115
|
+
}).then(body2 => {
|
|
116
|
+
expect(body2).toEqual(null);
|
|
117
|
+
});
|
|
118
|
+
});
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { applyMiddleware, combineReducers, compose, createStore } from "redux";
|
|
2
|
+
import { KEY_PREFIX } from "redux-persist/lib/constants"
|
|
3
|
+
import { AsyncNodeStorage } from "redux-persist-node-storage";
|
|
4
|
+
import instrument from "redux-devtools-instrument";
|
|
5
|
+
import { createOffline, offline } from "../index";
|
|
6
|
+
import { applyDefaults } from "../config";
|
|
7
|
+
import { networkStatusChanged } from "../actions";
|
|
8
|
+
|
|
9
|
+
const storageKey = `${KEY_PREFIX}offline`;
|
|
10
|
+
const defaultReducer = (state = {}) => state;
|
|
11
|
+
const noop = () => {};
|
|
12
|
+
|
|
13
|
+
let defaultConfig;
|
|
14
|
+
beforeEach(() => {
|
|
15
|
+
const storage = new AsyncNodeStorage("/tmp/storageDir");
|
|
16
|
+
defaultConfig = applyDefaults({
|
|
17
|
+
effect: jest.fn(() => Promise.resolve()),
|
|
18
|
+
persistOptions: { storage }
|
|
19
|
+
});
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
test("offline() creates storeEnhancer", () => {
|
|
23
|
+
const storeEnhancer = offline(defaultConfig);
|
|
24
|
+
|
|
25
|
+
const store = storeEnhancer(createStore)(defaultReducer);
|
|
26
|
+
expect(store.dispatch).toEqual(expect.any(Function));
|
|
27
|
+
expect(store.getState).toEqual(expect.any(Function));
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
test("createOffline() creates storeEnhancer", () => {
|
|
31
|
+
const { middleware, enhanceReducer, enhanceStore } =
|
|
32
|
+
createOffline(defaultConfig);
|
|
33
|
+
const reducer = enhanceReducer(defaultReducer);
|
|
34
|
+
const store = createStore(reducer, compose(
|
|
35
|
+
applyMiddleware(middleware),
|
|
36
|
+
enhanceStore
|
|
37
|
+
));
|
|
38
|
+
expect(store.dispatch).toEqual(expect.any(Function));
|
|
39
|
+
expect(store.getState).toEqual(expect.any(Function));
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
// see https://github.com/redux-offline/redux-offline/issues/31
|
|
43
|
+
test("supports HMR by overriding `replaceReducer()`", () => {
|
|
44
|
+
const store = offline(defaultConfig)(createStore)(defaultReducer);
|
|
45
|
+
store.replaceReducer(combineReducers({
|
|
46
|
+
data: defaultReducer
|
|
47
|
+
}));
|
|
48
|
+
store.dispatch({ type: "SOME_ACTION" });
|
|
49
|
+
expect(store.getState()).toHaveProperty("offline");
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
test("createOffline() supports HMR", () => {
|
|
53
|
+
const { middleware, enhanceReducer, enhanceStore } =
|
|
54
|
+
createOffline(defaultConfig);
|
|
55
|
+
const reducer = enhanceReducer(defaultReducer);
|
|
56
|
+
const store = createStore(reducer, compose(
|
|
57
|
+
applyMiddleware(middleware),
|
|
58
|
+
enhanceStore
|
|
59
|
+
));
|
|
60
|
+
store.replaceReducer(combineReducers({
|
|
61
|
+
data: defaultReducer
|
|
62
|
+
}));
|
|
63
|
+
store.dispatch({ type: "SOME_ACTION" });
|
|
64
|
+
expect(store.getState()).toHaveProperty("offline");
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
// see https://github.com/redux-offline/redux-offline/issues/4
|
|
68
|
+
test("restores offline outbox when rehydrates", done => {
|
|
69
|
+
const actions = [{
|
|
70
|
+
type: "SOME_OFFLINE_ACTION",
|
|
71
|
+
meta: { offline: { effect: {} } }
|
|
72
|
+
}];
|
|
73
|
+
defaultConfig.persistOptions.storage.setItem(
|
|
74
|
+
storageKey,
|
|
75
|
+
JSON.stringify({ outbox: actions }),
|
|
76
|
+
noop
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
const store = offline({
|
|
80
|
+
...defaultConfig,
|
|
81
|
+
persistCallback() {
|
|
82
|
+
const { offline: { outbox } } = store.getState();
|
|
83
|
+
expect(outbox).toEqual(actions);
|
|
84
|
+
done();
|
|
85
|
+
}
|
|
86
|
+
})(createStore)(defaultReducer);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
// see https://github.com/jevakallio/redux-offline/pull/91
|
|
90
|
+
test("works with devtools store enhancer", () => {
|
|
91
|
+
const monitorReducer = state => state;
|
|
92
|
+
const store = createStore(
|
|
93
|
+
defaultReducer,
|
|
94
|
+
compose(offline(defaultConfig), instrument(monitorReducer))
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
expect(() => {
|
|
98
|
+
store.dispatch({ type: "SOME_ACTION" });
|
|
99
|
+
}).not.toThrow();
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
// there were some reports that this might not be working correctly
|
|
103
|
+
test("coming online processes outbox", () => {
|
|
104
|
+
const { middleware, enhanceReducer } = createOffline(defaultConfig);
|
|
105
|
+
const reducer = enhanceReducer(defaultReducer);
|
|
106
|
+
const store = createStore(reducer, applyMiddleware(middleware));
|
|
107
|
+
|
|
108
|
+
expect(store.getState().offline.online).toBe(false);
|
|
109
|
+
const action = { type: "REQUEST", meta: { offline: { effect: {} } } };
|
|
110
|
+
store.dispatch(action);
|
|
111
|
+
expect(defaultConfig.effect).not.toBeCalled();
|
|
112
|
+
|
|
113
|
+
store.dispatch(networkStatusChanged(true));
|
|
114
|
+
expect(store.getState().offline.online).toBe(true);
|
|
115
|
+
expect(defaultConfig.effect).toBeCalled();
|
|
116
|
+
});
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import { createOfflineMiddleware } from '../middleware';
|
|
2
|
+
import { completeRetry, scheduleRetry } from '../actions';
|
|
3
|
+
import defaultConfig from '../defaults';
|
|
4
|
+
import { OFFLINE_SEND } from '../constants';
|
|
5
|
+
import send from '../send';
|
|
6
|
+
import offlineActionTracker from '../offlineActionTracker';
|
|
7
|
+
|
|
8
|
+
const offlineAction = {
|
|
9
|
+
type: 'OFFLINE_ACTION_REQUEST',
|
|
10
|
+
meta: {
|
|
11
|
+
offline: {
|
|
12
|
+
effect: { url: '/api/endpoint', method: 'POST' },
|
|
13
|
+
commit: { type: 'OFFLINE_ACTION_COMMIT' },
|
|
14
|
+
rollback: { type: 'OFFLINE_ACTION_ROLLBACK' }
|
|
15
|
+
},
|
|
16
|
+
transaction: 0
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const defaultOfflineState = {
|
|
21
|
+
busy: false,
|
|
22
|
+
lastTransaction: 0,
|
|
23
|
+
online: true,
|
|
24
|
+
outbox: [offlineAction],
|
|
25
|
+
receipts: [],
|
|
26
|
+
retryToken: 0,
|
|
27
|
+
retryCount: 0,
|
|
28
|
+
retryScheduled: false,
|
|
29
|
+
netInfo: {
|
|
30
|
+
isConnectionExpensive: null,
|
|
31
|
+
reach: 'NONE'
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
function setup(offlineState = {}) {
|
|
36
|
+
const state = {
|
|
37
|
+
offline: { ...defaultOfflineState, ...offlineState }
|
|
38
|
+
};
|
|
39
|
+
return {
|
|
40
|
+
config: {
|
|
41
|
+
...defaultConfig,
|
|
42
|
+
rehydrate: false,
|
|
43
|
+
persist: null,
|
|
44
|
+
detectNetwork: null,
|
|
45
|
+
batch: jest.fn(outbox => outbox.slice(0, 1)),
|
|
46
|
+
effect: jest.fn(),
|
|
47
|
+
retry: jest.fn(),
|
|
48
|
+
discard: jest.fn(),
|
|
49
|
+
offlineActionTracker: offlineActionTracker.withoutPromises
|
|
50
|
+
},
|
|
51
|
+
store: {
|
|
52
|
+
getState: jest.fn(() => state),
|
|
53
|
+
dispatch: jest.fn()
|
|
54
|
+
},
|
|
55
|
+
next: jest.fn(action => ({ actions: [action] })),
|
|
56
|
+
action: { type: 'NOT_OFFLINE_ACTION' }
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// NOTE: there is not currently an action creator for this
|
|
61
|
+
function offlineSend() {
|
|
62
|
+
return { type: OFFLINE_SEND };
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
jest.mock('../send', () => jest.fn(() => Promise.resolve()));
|
|
66
|
+
beforeEach(send.mockClear);
|
|
67
|
+
|
|
68
|
+
test('creates middleware', () => {
|
|
69
|
+
const { config, store, next, action } = setup();
|
|
70
|
+
const middleware = createOfflineMiddleware(config);
|
|
71
|
+
|
|
72
|
+
const result = middleware(store)(next)(action);
|
|
73
|
+
expect(next).toBeCalled();
|
|
74
|
+
expect(result).toEqual(next(action));
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
describe('on any action', () => {
|
|
78
|
+
it('processes outbox when idle', () => {
|
|
79
|
+
const { config, store, next, action } = setup();
|
|
80
|
+
createOfflineMiddleware(config)(store)(next)(action);
|
|
81
|
+
expect(send).toBeCalled();
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it('does not process outbox when busy', () => {
|
|
85
|
+
const { config, store, next, action } = setup({ busy: true });
|
|
86
|
+
createOfflineMiddleware(config)(store)(next)(action);
|
|
87
|
+
expect(send).not.toBeCalled();
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it('does not process outbox when retry scheduled', () => {
|
|
91
|
+
const { config, store, next, action } = setup({ retryScheduled: true });
|
|
92
|
+
createOfflineMiddleware(config)(store)(next)(action);
|
|
93
|
+
expect(send).not.toBeCalled();
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it('does not process outbox when offline', () => {
|
|
97
|
+
const { config, store, next, action } = setup({ online: false });
|
|
98
|
+
createOfflineMiddleware(config)(store)(next)(action);
|
|
99
|
+
expect(send).not.toBeCalled();
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
// TODO: test for double dispatch
|
|
104
|
+
describe('on OFFLINE_SEND', () => {
|
|
105
|
+
it('processes outbox when idle', () => {
|
|
106
|
+
const { config, store, next } = setup();
|
|
107
|
+
createOfflineMiddleware(config)(store)(next)(offlineSend());
|
|
108
|
+
expect(send).toBeCalled();
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it('does not process outbox when busy', () => {
|
|
112
|
+
const { config, store, next } = setup({ busy: true });
|
|
113
|
+
createOfflineMiddleware(config)(store)(next)(offlineSend());
|
|
114
|
+
expect(send).not.toBeCalled();
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it('processes outbox when retry scheduled', () => {
|
|
118
|
+
const { config, store, next } = setup({ retryScheduled: true });
|
|
119
|
+
createOfflineMiddleware(config)(store)(next)(offlineSend());
|
|
120
|
+
expect(send).toBeCalled();
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
it('processes outbox when offline', () => {
|
|
124
|
+
const { config, store, next } = setup({ online: false });
|
|
125
|
+
createOfflineMiddleware(config)(store)(next)(offlineSend());
|
|
126
|
+
expect(send).toBeCalled();
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
// TODO: wrapping `setTimeout()` in a promise in `after()` is pointless
|
|
131
|
+
describe('on OFFLINE_SCHEDULE_RETRY', () => {
|
|
132
|
+
jest.useFakeTimers();
|
|
133
|
+
const delay = 15000;
|
|
134
|
+
|
|
135
|
+
test('dispatches COMPLETE_RETRY after delay', () => {
|
|
136
|
+
const { config, store, next } = setup();
|
|
137
|
+
createOfflineMiddleware(config)(store)(next)(scheduleRetry(delay));
|
|
138
|
+
jest.runTimersToTime(delay);
|
|
139
|
+
|
|
140
|
+
expect.assertions(1);
|
|
141
|
+
const nextAction = store.getState().offline.outbox[0];
|
|
142
|
+
return Promise.resolve().then(() =>
|
|
143
|
+
expect(store.dispatch).toBeCalledWith(completeRetry(nextAction)));
|
|
144
|
+
});
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
test('offlineActionTracker without promises', () => {
|
|
148
|
+
const { config, store, next } = setup();
|
|
149
|
+
|
|
150
|
+
const value = createOfflineMiddleware(config)(store)(next)(offlineAction);
|
|
151
|
+
expect(value).not.toHaveProperty('then');
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
describe('offlineActionTracker integration', () => {
|
|
155
|
+
let config, store, next;
|
|
156
|
+
beforeEach(() => {
|
|
157
|
+
({ config, store, next } = setup())
|
|
158
|
+
config = {
|
|
159
|
+
...config,
|
|
160
|
+
offlineActionTracker: offlineActionTracker.withPromises
|
|
161
|
+
};
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
test('returns a promise that can be resolved', () => {
|
|
165
|
+
const promise = createOfflineMiddleware(config)(store)(next)(offlineAction);
|
|
166
|
+
|
|
167
|
+
const transaction = 0;
|
|
168
|
+
const data = { some: "data" };
|
|
169
|
+
config.offlineActionTracker.resolveAction(transaction, data);
|
|
170
|
+
|
|
171
|
+
expect.assertions(1);
|
|
172
|
+
return promise.then(value => expect(value).toEqual(data));
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
test('returns a promise that can be rejected', () => {
|
|
176
|
+
const promise = createOfflineMiddleware(config)(store)(next)(offlineAction);
|
|
177
|
+
|
|
178
|
+
const transaction = 0;
|
|
179
|
+
const data = { some: 'data' };
|
|
180
|
+
config.offlineActionTracker.rejectAction(transaction, data);
|
|
181
|
+
|
|
182
|
+
expect.assertions(1);
|
|
183
|
+
return promise.catch(error => expect(error).toEqual(data));
|
|
184
|
+
});
|
|
185
|
+
});
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import offlineActionTracker from '../offlineActionTracker.js';
|
|
2
|
+
const {
|
|
3
|
+
registerAction,
|
|
4
|
+
resolveAction,
|
|
5
|
+
rejectAction
|
|
6
|
+
} = offlineActionTracker.withPromises;
|
|
7
|
+
|
|
8
|
+
test('resolves first action with correct transaction', () => {
|
|
9
|
+
const transaction = 0;
|
|
10
|
+
const promise = registerAction(transaction);
|
|
11
|
+
|
|
12
|
+
const data = { some: "data" };
|
|
13
|
+
resolveAction(transaction, data);
|
|
14
|
+
|
|
15
|
+
expect.assertions(1);
|
|
16
|
+
return promise.then(value => expect(value).toEqual(data));
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
test('rejects first action with correct transaction', () => {
|
|
20
|
+
const transaction = 0;
|
|
21
|
+
const promise = registerAction(transaction);
|
|
22
|
+
|
|
23
|
+
const data = { some: "data" };
|
|
24
|
+
rejectAction(transaction, data);
|
|
25
|
+
|
|
26
|
+
expect.assertions(1);
|
|
27
|
+
return promise.catch(error => expect(error).toEqual(data));
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
test('does not resolve first action with incorrect transaction', () => {
|
|
31
|
+
const transaction = 0;
|
|
32
|
+
const promise = registerAction(transaction);
|
|
33
|
+
|
|
34
|
+
const incorrectData = { incorrect: "data" };
|
|
35
|
+
resolveAction(transaction + 1, incorrectData);
|
|
36
|
+
|
|
37
|
+
const correctData = { some: "data" };
|
|
38
|
+
resolveAction(transaction, correctData);
|
|
39
|
+
|
|
40
|
+
expect.assertions(1);
|
|
41
|
+
return promise.then(value => expect(value).toEqual(correctData));
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
test('resolves second action with correct transaction', () => {
|
|
45
|
+
const array = [];
|
|
46
|
+
|
|
47
|
+
registerAction(0).then(() => array.push(0));
|
|
48
|
+
const promise = registerAction(1).then(() => array.push(1));
|
|
49
|
+
resolveAction(1);
|
|
50
|
+
resolveAction(0);
|
|
51
|
+
|
|
52
|
+
expect.assertions(1);
|
|
53
|
+
return promise.then(() => expect(array).toEqual([1, 0]));
|
|
54
|
+
});
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import send from '../send';
|
|
2
|
+
import { busy, scheduleRetry } from '../actions';
|
|
3
|
+
import defaultCommitAction from '../defaults/defaultCommit';
|
|
4
|
+
import defaultRollbackAction from '../defaults/defaultRollback';
|
|
5
|
+
import offlineActionTracker from '../offlineActionTracker';
|
|
6
|
+
|
|
7
|
+
const DELAY = 1000;
|
|
8
|
+
const completedMeta = {
|
|
9
|
+
meta: expect.objectContaining({ completed: expect.any(Boolean) })
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
function setup(partialConfig) {
|
|
13
|
+
const defaultConfig = {
|
|
14
|
+
effect: jest.fn(() => Promise.resolve()),
|
|
15
|
+
discard: () => false,
|
|
16
|
+
retry: () => DELAY,
|
|
17
|
+
defaultCommit: defaultCommitAction,
|
|
18
|
+
defaultRollback: defaultRollbackAction,
|
|
19
|
+
offlineActionTracker: offlineActionTracker.withoutPromises
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
return {
|
|
23
|
+
action: {
|
|
24
|
+
type: 'REQUEST',
|
|
25
|
+
meta: {
|
|
26
|
+
offline: {
|
|
27
|
+
effect: { url: '/api/resource', method: 'get' },
|
|
28
|
+
commit: { type: 'COMMIT' },
|
|
29
|
+
rollback: { type: 'ROLLBACK' }
|
|
30
|
+
},
|
|
31
|
+
transaction: 0
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
config: { ...defaultConfig, ...partialConfig },
|
|
35
|
+
dispatch: jest.fn()
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
test('dispatches busy action', () => {
|
|
40
|
+
const { action, config, dispatch } = setup();
|
|
41
|
+
const promise = send(action, dispatch, config);
|
|
42
|
+
|
|
43
|
+
expect.assertions(2);
|
|
44
|
+
return promise.then(() => {
|
|
45
|
+
expect(dispatch).toBeCalledWith(busy(true));
|
|
46
|
+
expect(dispatch).toHaveBeenLastCalledWith(busy(false));
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
test('requests resource using effects reconciler', () => {
|
|
51
|
+
const { action, config, dispatch } = setup();
|
|
52
|
+
send(action, dispatch, config);
|
|
53
|
+
expect(config.effect).toBeCalledWith(action.meta.offline.effect, action);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
describe('when request succeeds', () => {
|
|
57
|
+
test('dispatches complete action', () => {
|
|
58
|
+
const effect = () => Promise.resolve();
|
|
59
|
+
const { action, config, dispatch } = setup({ effect });
|
|
60
|
+
const promise = send(action, dispatch, config);
|
|
61
|
+
|
|
62
|
+
const { commit } = action.meta.offline;
|
|
63
|
+
expect.assertions(2);
|
|
64
|
+
return promise.then(() => {
|
|
65
|
+
expect(dispatch).toBeCalledWith(expect.objectContaining(commit));
|
|
66
|
+
expect(dispatch).toBeCalledWith(expect.objectContaining(completedMeta));
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
describe('when request fails', () => {
|
|
72
|
+
test('dispatches schedule retry action', () => {
|
|
73
|
+
const effect = () => Promise.reject();
|
|
74
|
+
const { action, config, dispatch } = setup({ effect });
|
|
75
|
+
const promise = send(action, dispatch, config);
|
|
76
|
+
|
|
77
|
+
expect.assertions(1);
|
|
78
|
+
return promise.then(() => {
|
|
79
|
+
expect(dispatch).toBeCalledWith(scheduleRetry(DELAY));
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
test('dispatches complete action on discard', () => {
|
|
84
|
+
const effect = () => Promise.reject();
|
|
85
|
+
const discard = () => true;
|
|
86
|
+
const { action, config, dispatch } = setup({ effect, discard });
|
|
87
|
+
const promise = send(action, dispatch, config);
|
|
88
|
+
|
|
89
|
+
const { rollback } = action.meta.offline;
|
|
90
|
+
expect.assertions(2);
|
|
91
|
+
return promise.then(() => {
|
|
92
|
+
expect(dispatch).toBeCalledWith(expect.objectContaining(rollback));
|
|
93
|
+
expect(dispatch).toBeCalledWith(expect.objectContaining(completedMeta));
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
test('dispatches complete action with promised discard', () => {
|
|
98
|
+
const effect = () => Promise.reject();
|
|
99
|
+
const discard = () => Promise.resolve(true);
|
|
100
|
+
const { action, config, dispatch } = setup({ effect, discard });
|
|
101
|
+
const promise = send(action, dispatch, config);
|
|
102
|
+
|
|
103
|
+
const { rollback } = action.meta.offline;
|
|
104
|
+
expect.assertions(2);
|
|
105
|
+
return promise.then(() => {
|
|
106
|
+
expect(dispatch).toBeCalledWith(expect.objectContaining(rollback));
|
|
107
|
+
expect(dispatch).toBeCalledWith(expect.objectContaining(completedMeta));
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
test('dispatches complete action when discard throw an exception', () => {
|
|
112
|
+
const effect = () => Promise.reject();
|
|
113
|
+
const discard = () => {throw new Error};
|
|
114
|
+
const { action, config, dispatch } = setup({ effect, discard });
|
|
115
|
+
const promise = send(action, dispatch, config);
|
|
116
|
+
|
|
117
|
+
const { rollback } = action.meta.offline;
|
|
118
|
+
expect.assertions(2);
|
|
119
|
+
return promise.then(() => {
|
|
120
|
+
expect(dispatch).toBeCalledWith(expect.objectContaining(rollback));
|
|
121
|
+
expect(dispatch).toBeCalledWith(expect.objectContaining(completedMeta));
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
describe('when request succeeds and commit is undefined', () => {
|
|
127
|
+
test('dispatches default commit action', () => {
|
|
128
|
+
const effect = () => Promise.resolve();
|
|
129
|
+
|
|
130
|
+
const action = {
|
|
131
|
+
type: 'REQUEST',
|
|
132
|
+
meta: {
|
|
133
|
+
offline: {
|
|
134
|
+
effect: { type: 'MOCK' },
|
|
135
|
+
},
|
|
136
|
+
},
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
const { config, dispatch } = setup({ effect });
|
|
140
|
+
|
|
141
|
+
const promise = send(action, dispatch, config)
|
|
142
|
+
|
|
143
|
+
return promise.then(() => {
|
|
144
|
+
expect(dispatch).toBeCalledWith(expect.objectContaining(defaultCommitAction));
|
|
145
|
+
expect(dispatch).toBeCalledWith(expect.objectContaining(completedMeta));
|
|
146
|
+
});
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
describe('when request is to be discarded and rollback is undefined', () => {
|
|
151
|
+
test('dispatches default rollback action', () => {
|
|
152
|
+
const effect = () => Promise.reject();
|
|
153
|
+
const discard = () => true;
|
|
154
|
+
|
|
155
|
+
const action = {
|
|
156
|
+
type: 'REQUEST',
|
|
157
|
+
meta: {
|
|
158
|
+
offline: {
|
|
159
|
+
effect: { type: 'MOCK' },
|
|
160
|
+
},
|
|
161
|
+
},
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
const { config, dispatch } = setup({ effect, discard });
|
|
165
|
+
|
|
166
|
+
const promise = send(action, dispatch, config)
|
|
167
|
+
|
|
168
|
+
return promise.then(() => {
|
|
169
|
+
expect(dispatch).toBeCalledWith(expect.objectContaining(defaultRollbackAction));
|
|
170
|
+
expect(dispatch).toBeCalledWith(expect.objectContaining(completedMeta));
|
|
171
|
+
});
|
|
172
|
+
});
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
describe('offlineActionTracker', () => {
|
|
176
|
+
let trackerMock;
|
|
177
|
+
beforeEach(() => {
|
|
178
|
+
trackerMock = {
|
|
179
|
+
registerAction: () => {},
|
|
180
|
+
resolveAction: jest.fn(),
|
|
181
|
+
rejectAction: jest.fn()
|
|
182
|
+
}
|
|
183
|
+
})
|
|
184
|
+
test('resolves action on successful complete', () => {
|
|
185
|
+
const effect = () => Promise.resolve();
|
|
186
|
+
const { action, config, dispatch } = setup({ effect });
|
|
187
|
+
const promise = send(action, dispatch, {
|
|
188
|
+
...config,
|
|
189
|
+
offlineActionTracker: trackerMock
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
expect.assertions(1);
|
|
193
|
+
return promise.then(() => expect(trackerMock.resolveAction).toBeCalled());
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
test('rejects action on failed complete', () => {
|
|
197
|
+
const effect = () => Promise.reject(new Error());
|
|
198
|
+
const discard = () => true;
|
|
199
|
+
const { action, config, dispatch } = setup({ effect, discard });
|
|
200
|
+
const promise = send(action, dispatch, {
|
|
201
|
+
...config,
|
|
202
|
+
offlineActionTracker: trackerMock
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
expect.assertions(1);
|
|
206
|
+
return promise.then(() => expect(trackerMock.rejectAction).toBeCalled());
|
|
207
|
+
});
|
|
208
|
+
});
|