@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.
Files changed (268) hide show
  1. package/components/AppBar/index.js +84 -0
  2. package/components/AppBar/styles.js +27 -0
  3. package/components/BookDetail/index.js +268 -0
  4. package/components/BookDetail/styles.js +124 -0
  5. package/components/ComponentJobsFilter/index.js +188 -0
  6. package/components/ComponentJobsFilter/styles.js +12 -0
  7. package/components/ConnectivityWarning/index.js +65 -0
  8. package/components/ConnectivityWarning/styles.js +19 -0
  9. package/components/ContactDetail/index.js +232 -0
  10. package/components/ContactDetail/styles.js +32 -0
  11. package/components/CourseDetail/index.js +357 -0
  12. package/components/CourseDetail/styles.js +59 -0
  13. package/components/CourseDetailDialog/index.js +169 -0
  14. package/components/CourseDetailDialog/styles.js +116 -0
  15. package/components/CourseInfo/index.js +219 -0
  16. package/components/CourseInfo/styles.js +40 -0
  17. package/components/DevelopmentDialog/index.js +214 -0
  18. package/components/DevelopmentDialog/styles.js +10 -0
  19. package/components/EventCodeInput/index.js +149 -0
  20. package/components/EventCodeInput/styles.js +108 -0
  21. package/components/FlexMenuEntry/index.js +84 -0
  22. package/components/FlexMenuEntry/styles.js +27 -0
  23. package/components/MainMenuEntry/index.js +88 -0
  24. package/components/MainMenuEntry/styles.js +28 -0
  25. package/components/MealItem/index.js +87 -0
  26. package/components/MealItem/styles.js +84 -0
  27. package/components/MensaMenu/index.js +305 -0
  28. package/components/MensaMenu/styles.js +94 -0
  29. package/components/MensaSlider/index.js +188 -0
  30. package/components/MensaSlider/styles.js +53 -0
  31. package/components/Modal/index.js +106 -0
  32. package/components/Modal/styles.js +8 -0
  33. package/components/NewsDetail/index.js +377 -0
  34. package/components/NewsDetail/styles.js +77 -0
  35. package/components/NewsList/index.js +120 -0
  36. package/components/NewsList/styles.js +19 -0
  37. package/components/NewsListItem/index.js +89 -0
  38. package/components/NewsListItem/styles.js +32 -0
  39. package/components/OtherCourses/index.js +152 -0
  40. package/components/OtherCourses/styles.js +10 -0
  41. package/components/PtsDeparture/index.js +140 -0
  42. package/components/PtsDeparture/styles.js +7 -0
  43. package/components/PtsStation/index.js +186 -0
  44. package/components/PtsStation/styles.js +47 -0
  45. package/components/QuickLinks/index.js +127 -0
  46. package/components/QuickLinks/styles.js +45 -0
  47. package/components/RoomDetail/index.js +281 -0
  48. package/components/RoomDetail/styles.js +56 -0
  49. package/components/ScaledImage/index.js +92 -0
  50. package/components/SearchResults/index.js +362 -0
  51. package/components/SearchResults/styles.js +59 -0
  52. package/components/SettingSection/index.js +54 -0
  53. package/components/SettingSection/styles.js +15 -0
  54. package/components/SettingsDialog/index.js +52 -0
  55. package/components/SettingsDialog/styles.js +12 -0
  56. package/components/SettingsDialogRadio/index.js +66 -0
  57. package/components/SettingsDialogRadio/styles.js +7 -0
  58. package/components/SettingsDialogSelect/index.js +73 -0
  59. package/components/SettingsDialogSelect/styles.js +7 -0
  60. package/components/TimetableCodeInput/index.js +205 -0
  61. package/components/TimetableCodeInput/styles.js +28 -0
  62. package/components/TimetableDay/index.js +252 -0
  63. package/components/TimetableDay/styles.js +103 -0
  64. package/components/TimetableEvent/index.js +163 -0
  65. package/components/TimetableEvent/styles.js +108 -0
  66. package/components/TimetableList/index.js +180 -0
  67. package/components/TimetableList/styles.js +106 -0
  68. package/components/TimetableMonth/index.js +156 -0
  69. package/components/TimetableMonth/styles.js +29 -0
  70. package/components/TimetableWeek/index.js +230 -0
  71. package/components/TimetableWeek/styles.js +58 -0
  72. package/components/TopNews/index.js +282 -0
  73. package/components/TopNews/styles.js +125 -0
  74. package/components/TopNewsHtwk/index.js +279 -0
  75. package/components/TopNewsHtwk/styles.js +142 -0
  76. package/components/WebView/index.js +108 -0
  77. package/components/WebView/styles.js +11 -0
  78. package/context/context-callmanager/TucCallManager.js +74 -0
  79. package/context/context-callmanager/index.js +128 -0
  80. package/context/context-canteen/AsistServerApiProvider.js +126 -0
  81. package/context/context-canteen/CollektorVersion2ApiProvider.js +100 -0
  82. package/context/context-canteen/index.js +692 -0
  83. package/context/context-connectivity/index.js +85 -0
  84. package/context/context-event/AsistServerClient.js +82 -0
  85. package/context/context-event/index.js +205 -0
  86. package/context/context-flex-menu/AsistServerApiProvider.js +0 -0
  87. package/context/context-flex-menu/CollectorVersion2ApiProvider.js +32 -0
  88. package/context/context-flex-menu/index.js +160 -0
  89. package/context/context-info-dialog/CollektorVersion2ApiProvider.js +53 -0
  90. package/context/context-info-dialog/ProviderErrors.js +22 -0
  91. package/context/context-info-dialog/index.js +190 -0
  92. package/context/context-mails/MailService.js +147 -0
  93. package/context/context-mails/TucUnseenApiProvider.js +65 -0
  94. package/context/context-mails/index.js +131 -0
  95. package/context/context-news/CollektorVersion2ApiProvider.js +68 -0
  96. package/context/context-news/index.js +155 -0
  97. package/context/context-public-transport-ticket/CollektorVersion2ApiProvider.js +45 -0
  98. package/context/context-public-transport-ticket/ProviderErrors.js +11 -0
  99. package/context/context-public-transport-ticket/index.js +175 -0
  100. package/context/context-timetable/CollectorVersion2ApiProvider.js +102 -0
  101. package/context/context-timetable/ProviderErrors.js +22 -0
  102. package/context/context-timetable/TimetableService.js +357 -0
  103. package/context/context-timetable/index.js +215 -0
  104. package/context/context-user/index.js +586 -0
  105. package/index.js +178 -0
  106. package/libraries/base-api-provider/hooks.js +24 -0
  107. package/libraries/base-api-provider/http-api-provider.js +168 -0
  108. package/libraries/base-api-provider/index.js +20 -0
  109. package/libraries/core/api/canteens.js +88 -0
  110. package/libraries/core/api/corona.js +45 -0
  111. package/libraries/core/api/courses.js +54 -0
  112. package/libraries/core/api/device.js +98 -0
  113. package/libraries/core/api/feed.js +144 -0
  114. package/libraries/core/api/feedback.js +123 -0
  115. package/libraries/core/api/jobs.js +48 -0
  116. package/libraries/core/api/library.js +67 -0
  117. package/libraries/core/api/lostfound.js +77 -0
  118. package/libraries/core/api/occupancy.js +44 -0
  119. package/libraries/core/api/officehours.js +25 -0
  120. package/libraries/core/api/poi.js +25 -0
  121. package/libraries/core/api/pts.js +68 -0
  122. package/libraries/core/api/rooms.js +31 -0
  123. package/libraries/core/api/survey.js +121 -0
  124. package/libraries/core/api/telephone.js +32 -0
  125. package/libraries/core/api/tucrooms.js +31 -0
  126. package/libraries/core/constants/actions.js +75 -0
  127. package/libraries/core/constants/api.js +146 -0
  128. package/libraries/core/constants/enums.js +5 -0
  129. package/libraries/core/helper/date.js +9 -0
  130. package/libraries/core/helper/format.helper.js +2 -0
  131. package/libraries/core/index.js +60 -0
  132. package/libraries/core/redux/actions/notifications.js +6 -0
  133. package/libraries/core/redux/actions/setting.js +75 -0
  134. package/libraries/core/redux/actions/state.js +74 -0
  135. package/libraries/core/redux/asyncDispatch.js +27 -0
  136. package/libraries/core/redux/reducers/api.js +190 -0
  137. package/libraries/core/redux/reducers/index.js +32 -0
  138. package/libraries/core/redux/reducers/notifications.js +16 -0
  139. package/libraries/core/redux/reducers/setting.js +99 -0
  140. package/libraries/core/redux/reducers/state.js +122 -0
  141. package/libraries/core/redux/selectors.js +123 -0
  142. package/libraries/core/redux/store.js +40 -0
  143. package/libraries/core/services/data.js +96 -0
  144. package/libraries/core/services/root-navigation.js +36 -0
  145. package/libraries/core/services/search.js +175 -0
  146. package/libraries/core-plugins/enums/replaceTypes.js +53 -0
  147. package/libraries/core-plugins/index.js +21 -0
  148. package/libraries/core-plugins/redux/actions/plugin.js +13 -0
  149. package/libraries/core-plugins/redux/reducers/plugin.js +210 -0
  150. package/libraries/icons-openasist/index.js +143 -0
  151. package/libraries/notifications/index.js +268 -0
  152. package/libraries/notifications/services/subscriber.service.js +123 -0
  153. package/libraries/react-native-webview-autoheight/README.md +47 -0
  154. package/libraries/react-native-webview-autoheight/index.js +140 -0
  155. package/libraries/react-native-webview-autoheight/package.json +37 -0
  156. package/libraries/react-native-webview-autoheight/utils.js +268 -0
  157. package/libraries/redux-offline/.eslintignore +1 -0
  158. package/libraries/redux-offline/.github/ISSUE_TEMPLATE/redux-offline-core-issues-template.md +25 -0
  159. package/libraries/redux-offline/.prettierrc +6 -0
  160. package/libraries/redux-offline/LICENSE +21 -0
  161. package/libraries/redux-offline/README.md +133 -0
  162. package/libraries/redux-offline/flow-typed/npm/redux-logger_vx.x.x.js +109 -0
  163. package/libraries/redux-offline/flow-typed/npm/redux_v4.x.x.js +100 -0
  164. package/libraries/redux-offline/lib/actions.js +46 -0
  165. package/libraries/redux-offline/lib/config.js +15 -0
  166. package/libraries/redux-offline/lib/constants.js +17 -0
  167. package/libraries/redux-offline/lib/defaults/defaultCommit.js +11 -0
  168. package/libraries/redux-offline/lib/defaults/defaultRollback.js +11 -0
  169. package/libraries/redux-offline/lib/defaults/detectNetwork.js +33 -0
  170. package/libraries/redux-offline/lib/defaults/detectNetwork.native.js +211 -0
  171. package/libraries/redux-offline/lib/defaults/discard.js +18 -0
  172. package/libraries/redux-offline/lib/defaults/effect.js +87 -0
  173. package/libraries/redux-offline/lib/defaults/index.js +32 -0
  174. package/libraries/redux-offline/lib/defaults/offlineStateLens.js +24 -0
  175. package/libraries/redux-offline/lib/defaults/persist.js +9 -0
  176. package/libraries/redux-offline/lib/defaults/persist.native.js +17 -0
  177. package/libraries/redux-offline/lib/defaults/persistAutoRehydrate.js +8 -0
  178. package/libraries/redux-offline/lib/defaults/queue.js +27 -0
  179. package/libraries/redux-offline/lib/defaults/retry.js +29 -0
  180. package/libraries/redux-offline/lib/index.js +115 -0
  181. package/libraries/redux-offline/lib/middleware.js +56 -0
  182. package/libraries/redux-offline/lib/offlineActionTracker.js +43 -0
  183. package/libraries/redux-offline/lib/send.js +112 -0
  184. package/libraries/redux-offline/lib/types.js +3 -0
  185. package/libraries/redux-offline/lib/updater.js +111 -0
  186. package/libraries/redux-offline/package.json +69 -0
  187. package/libraries/redux-offline/src/__tests__/defaults/discard.js +22 -0
  188. package/libraries/redux-offline/src/__tests__/defaults/effect.js +118 -0
  189. package/libraries/redux-offline/src/__tests__/index.js +116 -0
  190. package/libraries/redux-offline/src/__tests__/middleware.js +185 -0
  191. package/libraries/redux-offline/src/__tests__/offlineActionTracker.js +54 -0
  192. package/libraries/redux-offline/src/__tests__/send.js +208 -0
  193. package/libraries/redux-offline/src/actions.js +36 -0
  194. package/libraries/redux-offline/src/config.js +9 -0
  195. package/libraries/redux-offline/src/constants.js +18 -0
  196. package/libraries/redux-offline/src/defaults/defaultCommit.js +7 -0
  197. package/libraries/redux-offline/src/defaults/defaultRollback.js +7 -0
  198. package/libraries/redux-offline/src/defaults/detectNetwork.js +16 -0
  199. package/libraries/redux-offline/src/defaults/detectNetwork.native.js +148 -0
  200. package/libraries/redux-offline/src/defaults/discard.js +19 -0
  201. package/libraries/redux-offline/src/defaults/effect.js +86 -0
  202. package/libraries/redux-offline/src/defaults/index.js +25 -0
  203. package/libraries/redux-offline/src/defaults/offlineStateLens.js +11 -0
  204. package/libraries/redux-offline/src/defaults/persist.js +4 -0
  205. package/libraries/redux-offline/src/defaults/persist.native.js +9 -0
  206. package/libraries/redux-offline/src/defaults/persistAutoRehydrate.js +4 -0
  207. package/libraries/redux-offline/src/defaults/queue.js +20 -0
  208. package/libraries/redux-offline/src/defaults/retry.js +18 -0
  209. package/libraries/redux-offline/src/index.js +138 -0
  210. package/libraries/redux-offline/src/middleware.js +52 -0
  211. package/libraries/redux-offline/src/offlineActionTracker.js +40 -0
  212. package/libraries/redux-offline/src/send.js +111 -0
  213. package/libraries/redux-offline/src/types.js +127 -0
  214. package/libraries/redux-offline/src/updater.js +141 -0
  215. package/libraries/redux-offline/typings.d.ts +150 -0
  216. package/libraries/redux-offline/yarn.lock +6460 -0
  217. package/libraries/stored-state/index.js +155 -0
  218. package/package.json +68 -0
  219. package/views/Callmanager/index.js +148 -0
  220. package/views/Callmanager/styles.js +62 -0
  221. package/views/Canteens/index.js +383 -0
  222. package/views/Canteens/styles.js +89 -0
  223. package/views/Dashboard/index.js +176 -0
  224. package/views/Dashboard/styles.js +16 -0
  225. package/views/DashboardHtwk/index.js +197 -0
  226. package/views/DashboardHtwk/styles.js +24 -0
  227. package/views/Event/index.js +373 -0
  228. package/views/Event/styles.js +63 -0
  229. package/views/FeedList/index.js +165 -0
  230. package/views/FeedList/styles.js +15 -0
  231. package/views/FeedNews/index.js +243 -0
  232. package/views/FeedNews/styles.js +32 -0
  233. package/views/Flexmenu/index.js +185 -0
  234. package/views/Flexmenu/styles.js +62 -0
  235. package/views/FlexmenuWebview/index.js +167 -0
  236. package/views/FlexmenuWebview/styles.js +16 -0
  237. package/views/Howhy/index.js +151 -0
  238. package/views/Howhy/styles.js +11 -0
  239. package/views/Jobs/index.js +217 -0
  240. package/views/Jobs/styles.js +41 -0
  241. package/views/MainMenu/index.js +283 -0
  242. package/views/MainMenu/styles.js +36 -0
  243. package/views/NewsTabbar/index.js +164 -0
  244. package/views/NewsTabbar/styles.js +27 -0
  245. package/views/Opal/index.js +136 -0
  246. package/views/Opal/styles.js +11 -0
  247. package/views/PublicTransportTicket/index.js +173 -0
  248. package/views/PublicTransportTicket/styles.js +17 -0
  249. package/views/Search/index.js +186 -0
  250. package/views/Search/styles.js +67 -0
  251. package/views/SettingsAccessibility/index.js +148 -0
  252. package/views/SettingsAccessibility/styles.js +13 -0
  253. package/views/SettingsAppInfo/index.js +86 -0
  254. package/views/SettingsAppInfo/styles.js +18 -0
  255. package/views/SettingsCanteens/index.js +249 -0
  256. package/views/SettingsCanteens/styles.js +33 -0
  257. package/views/SettingsGeneral/index.js +173 -0
  258. package/views/SettingsGeneral/styles.js +28 -0
  259. package/views/SettingsNotifications/index.js +204 -0
  260. package/views/SettingsNotifications/styles.js +38 -0
  261. package/views/SettingsUser/index.js +170 -0
  262. package/views/SettingsUser/styles.js +17 -0
  263. package/views/TimetableCalendar/index.js +387 -0
  264. package/views/TimetableCalendar/styles.js +121 -0
  265. package/views/TimetableList/index.js +402 -0
  266. package/views/TimetableList/styles.js +55 -0
  267. package/views/Webviews/index.js +158 -0
  268. 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
+ });