navigation-stack 0.3.0 → 0.4.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 (253) hide show
  1. package/README.md +603 -163
  2. package/data-storage/package.json +6 -0
  3. package/karma.conf.cjs +21 -4
  4. package/lib/cjs/NavigationStack.js +73 -0
  5. package/lib/cjs/data-storage/DataStorage.js +71 -0
  6. package/lib/cjs/data-storage/LocationDataStorage.js +29 -0
  7. package/lib/cjs/data-storage/index.js +9 -0
  8. package/lib/cjs/environment/InMemoryEnvironment.js +15 -0
  9. package/lib/cjs/environment/WebBrowserEnvironment.js +15 -0
  10. package/lib/cjs/environment/data-storage/InMemoryDataStorage.js +27 -0
  11. package/lib/cjs/environment/data-storage/WebBrowserDataStorage.js +21 -0
  12. package/lib/cjs/environment/scroll-position/InMemoryScrollPosition.js +44 -0
  13. package/lib/cjs/environment/scroll-position/WebBrowserScrollPosition.js +60 -0
  14. package/lib/cjs/getLocationFromInternalLocation.js +14 -0
  15. package/lib/cjs/index.js +20 -16
  16. package/lib/cjs/navigationBlockers.js +25 -23
  17. package/lib/cjs/{normalizeInputLocation.js → parseInputLocation.js} +25 -9
  18. package/lib/cjs/{ActionTypes.js → redux/ActionTypes.js} +1 -1
  19. package/lib/cjs/redux/ActionTypesInternal.js +8 -0
  20. package/lib/cjs/{Actions.js → redux/Actions.js} +5 -4
  21. package/lib/cjs/redux/createMiddlewares.js +60 -0
  22. package/lib/cjs/redux/index.js +13 -0
  23. package/lib/cjs/redux/internalLocationReducer.js +14 -0
  24. package/lib/cjs/redux/middleware/createAddInputLocationBasePathMiddleware.js +32 -0
  25. package/lib/cjs/redux/middleware/createNonProgrammaticNavigationBlockerMiddleware.js +113 -0
  26. package/lib/cjs/redux/middleware/createProgrammaticNavigationBlockerMiddleware.js +94 -0
  27. package/lib/cjs/redux/middleware/createRemoveOutputLocationBasePathMiddleware.js +30 -0
  28. package/lib/cjs/redux/middleware/createUpdateInternalLocationMiddleware.js +73 -0
  29. package/lib/cjs/{middleware/navigationActionMiddleware.js → redux/middleware/navigationOperationMiddleware.js} +11 -8
  30. package/lib/cjs/{middleware/normalizeInputLocationMiddleware.js → redux/middleware/parseInputLocationMiddleware.js} +6 -4
  31. package/lib/cjs/redux/middleware/updateLocationMiddleware.js +34 -0
  32. package/lib/cjs/scroll-position/PageScrollPositionSetter.js +97 -0
  33. package/lib/cjs/scroll-position/ScrollPositionAutoSaver.js +130 -0
  34. package/lib/cjs/scroll-position/ScrollPositionRestoration.js +383 -0
  35. package/lib/cjs/scroll-position/ScrollPositionSaver.js +81 -0
  36. package/lib/cjs/scroll-position/ScrollPositionSetter.js +16 -0
  37. package/lib/cjs/scroll-position/constants.js +5 -0
  38. package/lib/cjs/scroll-position/index.js +7 -0
  39. package/lib/cjs/scroll-position/scheduleNextTick.js +11 -0
  40. package/lib/cjs/session/InMemorySession.js +22 -0
  41. package/lib/cjs/session/ServerSideRenderSession.js +17 -0
  42. package/lib/cjs/session/Session.js +196 -0
  43. package/lib/cjs/session/WebBrowserSession.js +20 -0
  44. package/lib/cjs/session/key/createSessionKey.js +23 -0
  45. package/lib/cjs/session/lifecycle/InMemorySessionLifecycle.js +19 -0
  46. package/lib/cjs/session/lifecycle/WebBrowserSessionLifecycle.js +128 -0
  47. package/lib/cjs/session/lifecycle/page-lifecycle/PageLifecycle.js +269 -0
  48. package/lib/cjs/session/lifecycle/page-lifecycle/PageLifecycleInstance.js +8 -0
  49. package/lib/cjs/session/lifecycle/page-lifecycle/supportsConstructableEventTarget.js +33 -0
  50. package/lib/cjs/session/navigation/InMemoryNavigation.js +104 -0
  51. package/lib/cjs/session/navigation/ServerSideNavigation.js +61 -0
  52. package/lib/cjs/session/navigation/WebBrowserNavigation.js +221 -0
  53. package/lib/cjs/session/navigation/error/NavigationOutOfBoundsError.js +12 -0
  54. package/lib/cjs/session/navigation/error/ServerSideNavigationError.js +21 -0
  55. package/lib/cjs/session/navigation/operation/operations.js +11 -0
  56. package/lib/cjs/session/subscription/Subscription.js +81 -0
  57. package/lib/data-storage/index.d.ts +35 -0
  58. package/lib/esm/NavigationStack.js +66 -0
  59. package/lib/esm/data-storage/DataStorage.js +65 -0
  60. package/lib/esm/data-storage/LocationDataStorage.js +22 -0
  61. package/lib/esm/data-storage/index.js +2 -0
  62. package/lib/esm/environment/InMemoryEnvironment.js +8 -0
  63. package/lib/esm/environment/WebBrowserEnvironment.js +8 -0
  64. package/lib/esm/environment/data-storage/InMemoryDataStorage.js +21 -0
  65. package/lib/esm/environment/data-storage/WebBrowserDataStorage.js +15 -0
  66. package/lib/esm/environment/scroll-position/InMemoryScrollPosition.js +38 -0
  67. package/lib/esm/environment/scroll-position/WebBrowserScrollPosition.js +54 -0
  68. package/lib/esm/getLocationFromInternalLocation.js +9 -0
  69. package/lib/esm/index.js +10 -8
  70. package/lib/esm/navigationBlockers.js +25 -23
  71. package/lib/esm/{normalizeInputLocation.js → parseInputLocation.js} +24 -8
  72. package/lib/esm/{ActionTypes.js → redux/ActionTypes.js} +1 -1
  73. package/lib/esm/redux/ActionTypesInternal.js +3 -0
  74. package/lib/esm/{Actions.js → redux/Actions.js} +5 -4
  75. package/lib/esm/redux/createMiddlewares.js +54 -0
  76. package/lib/esm/redux/index.js +4 -0
  77. package/lib/esm/redux/internalLocationReducer.js +8 -0
  78. package/lib/esm/redux/middleware/createAddInputLocationBasePathMiddleware.js +27 -0
  79. package/lib/esm/redux/middleware/createNonProgrammaticNavigationBlockerMiddleware.js +108 -0
  80. package/lib/esm/redux/middleware/createProgrammaticNavigationBlockerMiddleware.js +88 -0
  81. package/lib/esm/redux/middleware/createRemoveOutputLocationBasePathMiddleware.js +25 -0
  82. package/lib/esm/redux/middleware/createUpdateInternalLocationMiddleware.js +68 -0
  83. package/lib/esm/{middleware/navigationActionMiddleware.js → redux/middleware/navigationOperationMiddleware.js} +10 -7
  84. package/lib/esm/{middleware/normalizeInputLocationMiddleware.js → redux/middleware/parseInputLocationMiddleware.js} +5 -3
  85. package/lib/esm/redux/middleware/updateLocationMiddleware.js +28 -0
  86. package/lib/esm/scroll-position/PageScrollPositionSetter.js +91 -0
  87. package/lib/esm/scroll-position/ScrollPositionAutoSaver.js +123 -0
  88. package/lib/esm/scroll-position/ScrollPositionRestoration.js +376 -0
  89. package/lib/esm/scroll-position/ScrollPositionSaver.js +74 -0
  90. package/lib/esm/scroll-position/ScrollPositionSetter.js +10 -0
  91. package/lib/esm/scroll-position/constants.js +1 -0
  92. package/lib/esm/scroll-position/index.js +1 -0
  93. package/lib/esm/scroll-position/scheduleNextTick.js +6 -0
  94. package/lib/esm/session/InMemorySession.js +15 -0
  95. package/lib/esm/session/ServerSideRenderSession.js +11 -0
  96. package/lib/esm/session/Session.js +189 -0
  97. package/lib/esm/session/WebBrowserSession.js +13 -0
  98. package/lib/esm/session/key/createSessionKey.js +18 -0
  99. package/lib/esm/session/lifecycle/InMemorySessionLifecycle.js +13 -0
  100. package/lib/esm/session/lifecycle/WebBrowserSessionLifecycle.js +120 -0
  101. package/lib/esm/session/lifecycle/page-lifecycle/PageLifecycle.js +263 -0
  102. package/lib/esm/session/lifecycle/page-lifecycle/PageLifecycleInstance.js +2 -0
  103. package/lib/esm/session/lifecycle/page-lifecycle/supportsConstructableEventTarget.js +30 -0
  104. package/lib/esm/session/navigation/InMemoryNavigation.js +97 -0
  105. package/lib/esm/session/navigation/ServerSideNavigation.js +54 -0
  106. package/lib/esm/session/navigation/WebBrowserNavigation.js +213 -0
  107. package/lib/esm/session/navigation/error/NavigationOutOfBoundsError.js +6 -0
  108. package/lib/esm/session/navigation/error/ServerSideNavigationError.js +14 -0
  109. package/lib/esm/session/navigation/operation/operations.js +6 -0
  110. package/lib/esm/session/subscription/Subscription.js +75 -0
  111. package/lib/index.d.ts +178 -157
  112. package/lib/redux/index.d.ts +90 -0
  113. package/lib/scroll-position/index.d.ts +107 -0
  114. package/package.json +9 -5
  115. package/redux/package.json +6 -0
  116. package/scroll-position/package.json +6 -0
  117. package/src/NavigationStack.js +84 -0
  118. package/src/data-storage/DataStorage.js +69 -0
  119. package/src/data-storage/LocationDataStorage.js +23 -0
  120. package/src/data-storage/index.js +2 -0
  121. package/src/environment/InMemoryEnvironment.js +9 -0
  122. package/src/environment/WebBrowserEnvironment.js +9 -0
  123. package/src/environment/data-storage/InMemoryDataStorage.js +23 -0
  124. package/src/environment/data-storage/WebBrowserDataStorage.js +17 -0
  125. package/src/environment/scroll-position/InMemoryScrollPosition.js +45 -0
  126. package/src/environment/scroll-position/WebBrowserScrollPosition.js +72 -0
  127. package/src/getLocationFromInternalLocation.js +7 -0
  128. package/src/index.js +10 -8
  129. package/src/navigationBlockers.js +28 -27
  130. package/src/{normalizeInputLocation.js → parseInputLocation.js} +23 -8
  131. package/src/{ActionTypes.js → redux/ActionTypes.js} +1 -1
  132. package/src/redux/ActionTypesInternal.js +3 -0
  133. package/src/{Actions.js → redux/Actions.js} +4 -3
  134. package/src/redux/createMiddlewares.js +65 -0
  135. package/src/redux/index.js +4 -0
  136. package/src/redux/internalLocationReducer.js +9 -0
  137. package/src/redux/middleware/createAddInputLocationBasePathMiddleware.js +27 -0
  138. package/src/redux/middleware/createNonProgrammaticNavigationBlockerMiddleware.js +119 -0
  139. package/src/redux/middleware/createProgrammaticNavigationBlockerMiddleware.js +94 -0
  140. package/src/redux/middleware/createRemoveOutputLocationBasePathMiddleware.js +26 -0
  141. package/src/redux/middleware/createUpdateInternalLocationMiddleware.js +72 -0
  142. package/src/{middleware/navigationActionMiddleware.js → redux/middleware/navigationOperationMiddleware.js} +10 -3
  143. package/src/{middleware/normalizeInputLocationMiddleware.js → redux/middleware/parseInputLocationMiddleware.js} +5 -3
  144. package/src/redux/middleware/updateLocationMiddleware.js +28 -0
  145. package/src/scroll-position/PageScrollPositionSetter.js +110 -0
  146. package/src/scroll-position/ScrollPositionAutoSaver.js +151 -0
  147. package/src/scroll-position/ScrollPositionRestoration.js +506 -0
  148. package/src/scroll-position/ScrollPositionSaver.js +100 -0
  149. package/src/scroll-position/ScrollPositionSetter.js +16 -0
  150. package/src/scroll-position/constants.js +1 -0
  151. package/src/scroll-position/index.js +1 -0
  152. package/src/scroll-position/scheduleNextTick.js +6 -0
  153. package/src/session/InMemorySession.js +13 -0
  154. package/src/session/ServerSideRenderSession.js +9 -0
  155. package/src/session/Session.js +216 -0
  156. package/src/session/WebBrowserSession.js +13 -0
  157. package/src/session/key/createSessionKey.js +18 -0
  158. package/src/session/lifecycle/InMemorySessionLifecycle.js +13 -0
  159. package/src/session/lifecycle/WebBrowserSessionLifecycle.js +126 -0
  160. package/src/session/lifecycle/page-lifecycle/PageLifecycle.js +291 -0
  161. package/src/session/lifecycle/page-lifecycle/PageLifecycleInstance.js +3 -0
  162. package/src/session/lifecycle/page-lifecycle/supportsConstructableEventTarget.js +32 -0
  163. package/src/session/navigation/InMemoryNavigation.js +78 -0
  164. package/src/session/navigation/ServerSideNavigation.js +43 -0
  165. package/src/session/navigation/WebBrowserNavigation.js +224 -0
  166. package/src/session/navigation/error/NavigationOutOfBoundsError.js +7 -0
  167. package/src/session/navigation/error/ServerSideNavigationError.js +18 -0
  168. package/src/session/navigation/operation/operations.js +6 -0
  169. package/src/session/subscription/Subscription.js +76 -0
  170. package/test/NavigationStack.test.js +296 -0
  171. package/test/{LocationDataStorage.test.js → data-storage/LocationDataStorage.test.js} +3 -3
  172. package/test/data-storage/index.test.js +8 -0
  173. package/test/index.js +12 -0
  174. package/test/index.test.js +8 -7
  175. package/test/{helpers.js → middlewareTestUtil.js} +9 -12
  176. package/test/{normalizeInputLocation.test.js → parseInputLocationMiddleware.test.js} +9 -9
  177. package/test/{Action.test.js → redux/Action.test.js} +7 -6
  178. package/test/{ActionTypes.test.js → redux/ActionTypes.test.js} +2 -2
  179. package/test/redux/createMiddlewares.test.js +96 -0
  180. package/test/redux/index.test.js +10 -0
  181. package/test/{locationReducer.test.js → redux/locationReducer.test.js} +4 -7
  182. package/test/redux/middleware/createAddInputLocationBasePathMiddleware.test.js +40 -0
  183. package/test/redux/middleware/createNonProgrammaticNavigationBlockerMiddleware.test.js +264 -0
  184. package/test/redux/middleware/createProgrammaticNavigationBlockerMiddleware.test.js +312 -0
  185. package/test/redux/middleware/createRemoveOutputLocationBasePathMiddleware.test.js +51 -0
  186. package/test/{middleware/navigationActionMiddleware.test.js → redux/middleware/navigationOperationMiddleware.test.js} +16 -12
  187. package/test/{middleware/normalizeInputLocationMiddleware.test.js → redux/middleware/parseInputLocationMiddleware.test.js} +4 -4
  188. package/test/scroll-position/ScrollPositionRestoration.test.js +418 -0
  189. package/test/scroll-position/addScrollableContainer.js +36 -0
  190. package/test/scroll-position/addScrollableContainerWithHyperlink.js +50 -0
  191. package/test/scroll-position/createApp.js +112 -0
  192. package/test/scroll-position/delay.js +9 -0
  193. package/test/scroll-position/mockPageLifecycle.js +17 -0
  194. package/test/scroll-position/runApp.js +24 -0
  195. package/test/scroll-position/withScrollableContainerAtIndexPage.js +62 -0
  196. package/test/session/InMemorySession.test.js +348 -0
  197. package/test/session/ServerSession.test.js +17 -9
  198. package/test/session/WebBrowserSession.test.js +265 -0
  199. package/test/testUtil.js +3 -0
  200. package/types/data-storage/index.d.ts +35 -0
  201. package/types/index.d.ts +178 -157
  202. package/types/redux/index.d.ts +90 -0
  203. package/types/scroll-position/index.d.ts +107 -0
  204. package/types/tsconfig.json +1 -1
  205. package/lib/cjs/LocationDataStorage.js +0 -60
  206. package/lib/cjs/addBeforeLocationChangeListener.js +0 -7
  207. package/lib/cjs/beforeLocationChangeListeners.js +0 -51
  208. package/lib/cjs/createMiddlewares.js +0 -47
  209. package/lib/cjs/middleware/createBasePathMiddleware.js +0 -24
  210. package/lib/cjs/middleware/createBeforeLocationChangeListenerMiddleware.js +0 -39
  211. package/lib/cjs/middleware/createLocationMiddleware.js +0 -56
  212. package/lib/cjs/middleware/createNavigationBlockerMiddleware.js +0 -161
  213. package/lib/cjs/middleware/createTransformLocationMiddleware.js +0 -38
  214. package/lib/cjs/onlyAllowedOnClientSide.js +0 -10
  215. package/lib/cjs/session/BrowserSession.js +0 -235
  216. package/lib/cjs/session/MemorySession.js +0 -223
  217. package/lib/cjs/session/ServerSession.js +0 -65
  218. package/lib/esm/LocationDataStorage.js +0 -53
  219. package/lib/esm/addBeforeLocationChangeListener.js +0 -2
  220. package/lib/esm/beforeLocationChangeListeners.js +0 -44
  221. package/lib/esm/createMiddlewares.js +0 -41
  222. package/lib/esm/middleware/createBasePathMiddleware.js +0 -19
  223. package/lib/esm/middleware/createBeforeLocationChangeListenerMiddleware.js +0 -34
  224. package/lib/esm/middleware/createLocationMiddleware.js +0 -50
  225. package/lib/esm/middleware/createNavigationBlockerMiddleware.js +0 -156
  226. package/lib/esm/middleware/createTransformLocationMiddleware.js +0 -33
  227. package/lib/esm/onlyAllowedOnClientSide.js +0 -5
  228. package/lib/esm/session/BrowserSession.js +0 -229
  229. package/lib/esm/session/MemorySession.js +0 -217
  230. package/lib/esm/session/ServerSession.js +0 -58
  231. package/src/LocationDataStorage.js +0 -59
  232. package/src/addBeforeLocationChangeListener.js +0 -2
  233. package/src/beforeLocationChangeListeners.js +0 -54
  234. package/src/createMiddlewares.js +0 -45
  235. package/src/middleware/createBasePathMiddleware.js +0 -20
  236. package/src/middleware/createBeforeLocationChangeListenerMiddleware.js +0 -40
  237. package/src/middleware/createLocationMiddleware.js +0 -55
  238. package/src/middleware/createNavigationBlockerMiddleware.js +0 -168
  239. package/src/middleware/createTransformLocationMiddleware.js +0 -29
  240. package/src/onlyAllowedOnClientSide.js +0 -5
  241. package/src/session/BrowserSession.js +0 -235
  242. package/src/session/MemorySession.js +0 -219
  243. package/src/session/ServerSession.js +0 -67
  244. package/test/createMiddlewares.test.js +0 -62
  245. package/test/middleware/createBasePathMiddleware.test.js +0 -67
  246. package/test/middleware/createBeforeLocationChangeListenerMiddleware.test.js +0 -141
  247. package/test/middleware/createNavigationBlockerMiddleware.test.js +0 -471
  248. package/test/middleware/createTransformLocationMiddleware.test.js +0 -44
  249. package/test/session/BrowserSession.test.js +0 -182
  250. package/test/session/MemorySession.test.js +0 -244
  251. /package/lib/cjs/{locationReducer.js → redux/locationReducer.js} +0 -0
  252. /package/lib/esm/{locationReducer.js → redux/locationReducer.js} +0 -0
  253. /package/src/{locationReducer.js → redux/locationReducer.js} +0 -0
@@ -0,0 +1,96 @@
1
+ import { applyMiddleware, createStore } from 'redux';
2
+
3
+ import Actions from '../../src/redux/Actions';
4
+ import createMiddlewares from '../../src/redux/createMiddlewares';
5
+ import internalLocationReducer from '../../src/redux/internalLocationReducer';
6
+ import InMemorySession from '../../src/session/InMemorySession';
7
+
8
+ describe('createMiddlewares', () => {
9
+ let store;
10
+
11
+ beforeEach(() => {
12
+ store = createStore(
13
+ internalLocationReducer,
14
+ applyMiddleware(
15
+ ...createMiddlewares(new InMemorySession(), {
16
+ _internalLocationReducer: true,
17
+ }),
18
+ ),
19
+ );
20
+ store.dispatch(Actions.init('/initial'));
21
+ });
22
+
23
+ afterEach(() => {
24
+ store.dispatch(Actions.stop());
25
+ });
26
+
27
+ it('should support `push` and `shift` navigation actions', () => {
28
+ store.dispatch(Actions.push('/new'));
29
+ expect(store.getState()).to.include({
30
+ pathname: '/new',
31
+ index: 1,
32
+ });
33
+
34
+ store.dispatch(Actions.shift(-1));
35
+ expect(store.getState()).to.include({
36
+ pathname: '/initial',
37
+ index: 0,
38
+ });
39
+
40
+ store.dispatch(Actions.shift(+1));
41
+ expect(store.getState()).to.include({
42
+ pathname: '/new',
43
+ index: 1,
44
+ });
45
+ });
46
+
47
+ it('should support `replace` navigation action', () => {
48
+ store.dispatch(Actions.replace('/new'));
49
+ expect(store.getState()).to.include({
50
+ pathname: '/new',
51
+ index: 0,
52
+ });
53
+ });
54
+
55
+ it('should ignore other actions', () => {
56
+ expect(
57
+ store.dispatch({
58
+ type: 'UNKNOWN',
59
+ payload: { unknown: {} },
60
+ }),
61
+ ).to.eql({
62
+ type: 'UNKNOWN',
63
+ payload: { unknown: {} },
64
+ });
65
+ });
66
+ });
67
+
68
+ describe('createMiddlewares.basePath', () => {
69
+ it('should support `basePath`', () => {
70
+ const session = new InMemorySession();
71
+
72
+ const store = createStore(
73
+ internalLocationReducer,
74
+ applyMiddleware(
75
+ ...createMiddlewares(session, {
76
+ basePath: '/base',
77
+ _internalLocationReducer: true,
78
+ }),
79
+ ),
80
+ );
81
+
82
+ store.dispatch(Actions.init('/initial'));
83
+
84
+ store.dispatch(Actions.push('/new'));
85
+
86
+ // eslint-disable-next-line no-underscore-dangle
87
+ expect(session._subscription._latest.pathname).to.equal('/base/new');
88
+
89
+ expect(store.getState()).to.include({
90
+ pathname: '/new',
91
+ index: 1,
92
+ });
93
+
94
+ store.dispatch(Actions.stop());
95
+ });
96
+ });
@@ -0,0 +1,10 @@
1
+ import * as exports from '../../src/redux';
2
+
3
+ describe('redux', () => {
4
+ it('should export top level correctly', () => {
5
+ expect(exports.Actions).to.exist();
6
+ expect(exports.ActionTypes).to.exist();
7
+ expect(exports.createMiddlewares).to.exist();
8
+ expect(exports.locationReducer).to.exist();
9
+ });
10
+ });
@@ -1,9 +1,9 @@
1
- import ActionTypes from '../src/ActionTypes';
2
- import locationReducer from '../src/locationReducer';
1
+ import ActionTypes from '../../src/redux/ActionTypes';
2
+ import locationReducer from '../../src/redux/locationReducer';
3
3
 
4
4
  describe('locationReducer', () => {
5
5
  const prevState = {
6
- action: 'PUSH',
6
+ operation: 'PUSH',
7
7
  delta: 1,
8
8
  hash: '',
9
9
  index: 5,
@@ -15,16 +15,13 @@ describe('locationReducer', () => {
15
15
 
16
16
  it('should handle UPDATE', () => {
17
17
  const newLocation = {
18
- action: 'PUSH',
19
- delta: 1,
20
- hash: '#qux',
21
- index: 6,
22
18
  key: 'h0j8qq:5',
23
19
  pathname: '/foo',
24
20
  query: {
25
21
  bar: 'baz',
26
22
  },
27
23
  search: '?bar=baz',
24
+ hash: '#qux',
28
25
  };
29
26
  const action = {
30
27
  type: ActionTypes.UPDATE,
@@ -0,0 +1,40 @@
1
+ import createAddInputLocationBasePathMiddleware from '../../../src/redux/middleware/createAddInputLocationBasePathMiddleware';
2
+ import { transformInputLocationUsingMiddleware } from '../../middlewareTestUtil';
3
+
4
+ describe('createAddInputLocationBasePathMiddleware', () => {
5
+ [
6
+ ['/base', 'generic `basePath`'],
7
+ ['/base/', '`basePath` with a trailing slash'],
8
+ ].forEach(([basePath, title]) => {
9
+ describe(title, () => {
10
+ const addInputLocationBasePathMiddleware =
11
+ createAddInputLocationBasePathMiddleware(basePath);
12
+
13
+ it('should prepend `basePath` to `location.pathname` on input locations', () => {
14
+ expect(
15
+ transformInputLocationUsingMiddleware(
16
+ addInputLocationBasePathMiddleware,
17
+ { pathname: '/path' },
18
+ ),
19
+ ).to.eql({
20
+ pathname: '/base/path',
21
+ });
22
+ });
23
+ });
24
+ });
25
+
26
+ describe('No `basePath` specified', () => {
27
+ const addInputLocationBasePathMiddleware =
28
+ createAddInputLocationBasePathMiddleware('/');
29
+
30
+ it('should not modify `location.pathname` of input locations', () => {
31
+ const location = { pathname: '/path' };
32
+ expect(
33
+ transformInputLocationUsingMiddleware(
34
+ addInputLocationBasePathMiddleware,
35
+ location,
36
+ ),
37
+ ).to.equal(location);
38
+ });
39
+ });
40
+ });
@@ -0,0 +1,264 @@
1
+ import delay from 'delay';
2
+ import pDefer from 'p-defer';
3
+ import { applyMiddleware, createStore } from 'redux';
4
+
5
+ import addNavigationBlockerOriginal from '../../../src/addNavigationBlocker';
6
+ import Actions from '../../../src/redux/Actions';
7
+ import createMiddlewares from '../../../src/redux/createMiddlewares';
8
+ import internalLocationReducer from '../../../src/redux/internalLocationReducer';
9
+ import InMemorySession from '../../../src/session/InMemorySession';
10
+
11
+ describe('createNonProgrammaticNavigationBlockerMiddleware', () => {
12
+ // const sandbox = sinon.createSandbox();
13
+
14
+ let session;
15
+ let store;
16
+
17
+ function addNavigationBlocker(blocker) {
18
+ return addNavigationBlockerOriginal(session, blocker);
19
+ }
20
+
21
+ beforeEach(() => {
22
+ session = new InMemorySession();
23
+
24
+ store = createStore(
25
+ internalLocationReducer,
26
+ applyMiddleware(
27
+ ...createMiddlewares(session, {
28
+ _internalLocationReducer: true,
29
+ }),
30
+ ),
31
+ );
32
+ store.dispatch(Actions.init('/initial'));
33
+
34
+ sinon.spy(session.lifecycle, 'addTerminationBlocker');
35
+ });
36
+
37
+ afterEach(() => {
38
+ store.dispatch(Actions.stop());
39
+
40
+ // sandbox.restore();
41
+ });
42
+
43
+ describe('SHIFT navigation', () => {
44
+ beforeEach(() => {
45
+ store.dispatch(Actions.push('/new'));
46
+ });
47
+
48
+ it('should allow navigation when blocker returns `undefined`', () => {
49
+ const blocker = sinon.stub().returns(undefined);
50
+ addNavigationBlocker(blocker);
51
+
52
+ store.dispatch(Actions.shift(-1));
53
+ expect(store.getState().pathname).to.equal('/initial');
54
+
55
+ expect(blocker.firstCall.args[0]).to.include({
56
+ // operation: 'SHIFT',
57
+ pathname: '/initial',
58
+ // delta: -1,
59
+ });
60
+ });
61
+
62
+ it('should block navigation when blocker returns `true`', () => {
63
+ addNavigationBlocker(() => true);
64
+
65
+ store.dispatch(Actions.shift(-1));
66
+ expect(store.getState().pathname).to.equal('/new');
67
+ });
68
+
69
+ it('should allow navigation when blocker returns `undefined` (async)', async () => {
70
+ const navigationBlockerDeferred = pDefer();
71
+ addNavigationBlocker(() => navigationBlockerDeferred.promise);
72
+
73
+ store.dispatch(Actions.shift(-1));
74
+ expect(store.getState().pathname).to.equal('/new');
75
+
76
+ navigationBlockerDeferred.resolve(undefined);
77
+ await delay(10);
78
+
79
+ expect(store.getState().pathname).to.equal('/initial');
80
+ });
81
+
82
+ it('should block navigation when blocker returns `true` (async)', async () => {
83
+ const navigationBlockerDeferred = pDefer();
84
+ addNavigationBlocker(() => navigationBlockerDeferred.promise);
85
+
86
+ store.dispatch(Actions.shift(-1));
87
+ expect(store.getState().pathname).to.equal('/new');
88
+
89
+ navigationBlockerDeferred.resolve(true);
90
+ await delay(10);
91
+
92
+ expect(store.getState().pathname).to.equal('/new');
93
+ });
94
+
95
+ // it('should show a confirmation dialog and allow navigation on string', () => {
96
+ // sandbox.stub(window, 'confirm').returns(true);
97
+ //
98
+ // addNavigationBlocker(({ pathname }) => pathname);
99
+ //
100
+ // store.dispatch(Actions.shift(-1));
101
+ // expect(store.getState().pathname).to.equal('/initial');
102
+ //
103
+ // expect(window.confirm)
104
+ // .to.have.been.calledOnce()
105
+ // .and.to.have.been.called.with('/new');
106
+ // });
107
+
108
+ it('should ignore the initial load when blocker returns `true`', () => {
109
+ // Get rid of the old store. We'll replace it with a new one.
110
+ store.dispatch(Actions.stop());
111
+
112
+ store = createStore(
113
+ internalLocationReducer,
114
+ applyMiddleware(
115
+ ...createMiddlewares(new InMemorySession(), {
116
+ _internalLocationReducer: true,
117
+ }),
118
+ ),
119
+ );
120
+ addNavigationBlocker(() => true);
121
+
122
+ expect(store.getState()).to.be.undefined();
123
+ store.dispatch(Actions.init('/initial'));
124
+ expect(store.getState().pathname).to.equal('/initial');
125
+ });
126
+
127
+ it('should support async rewinding', async () => {
128
+ // eslint-disable-next-line no-underscore-dangle
129
+ if (session._subscription._listeners.length !== 2) {
130
+ throw new Error(
131
+ "Expected 2 listeners: 1 session's own and 1 from `updateInternalLocationMiddleware()`",
132
+ );
133
+ }
134
+
135
+ const originalListener =
136
+ // eslint-disable-next-line no-underscore-dangle
137
+ session._subscription._listeners[1].listener;
138
+
139
+ let navigationDeferred;
140
+
141
+ // Alternatively to overwriting `_listeners[1].listener`,
142
+ // it could pass the `deferred` to `createMiddlewares()` function,
143
+ // which would then pass it to `updateInternalLocationMiddleware()`'s listener.
144
+ // eslint-disable-next-line no-underscore-dangle
145
+ session._subscription._listeners[1].listener = async (...args) => {
146
+ navigationDeferred = pDefer();
147
+ await navigationDeferred.promise;
148
+
149
+ originalListener(...args);
150
+ };
151
+
152
+ const navigationBlockerDeferred = pDefer();
153
+ addNavigationBlocker(() => navigationBlockerDeferred.promise);
154
+
155
+ store.dispatch(Actions.shift(-1));
156
+
157
+ // current location was updated immediately.
158
+ // Any `.subscribe()` listeners haven't yet been called,
159
+ // so current location in Redux state hasn't been updated yet.
160
+ // That's why it uses `session._subscription._latest` here
161
+ // instead of simply reading the current location from Redux state.
162
+ // eslint-disable-next-line no-underscore-dangle
163
+ expect(session._subscription._latest.pathname).to.equal('/initial');
164
+ // navigation is waiting.
165
+ expect(store.getState().pathname).to.equal('/new');
166
+
167
+ // proceed with navigation.
168
+ navigationDeferred.resolve();
169
+ await delay(10);
170
+
171
+ // rewinded.
172
+ // eslint-disable-next-line no-underscore-dangle
173
+ expect(session._subscription._latest.pathname).to.equal('/new');
174
+ // navigation almost finished: navigation blockers are running.
175
+ expect(store.getState().pathname).to.equal('/new');
176
+
177
+ // finish navigation blockers.
178
+ navigationBlockerDeferred.resolve(undefined);
179
+ await delay(10);
180
+
181
+ // finish navigation.
182
+ navigationDeferred.resolve();
183
+ await delay(10);
184
+
185
+ // the rewind was undone.
186
+ // eslint-disable-next-line no-underscore-dangle
187
+ expect(session._subscription._latest.pathname).to.equal('/initial');
188
+ // navigation finished.
189
+ // wasn't blocked.
190
+ expect(store.getState().pathname).to.equal('/initial');
191
+ });
192
+
193
+ // it('should allow navigation without calling any blockers when `location.delta` is `null`', async () => {
194
+ // const navigationBlockerDeferred = pDefer();
195
+ // addNavigationBlocker(() => navigationBlockerDeferred.promise);
196
+ //
197
+ // // Update location with a `SHIFT` operation.
198
+ // /* eslint-disable no-underscore-dangle */
199
+ // session._currentLocationIndex = 0;
200
+ // session._navigation._triggerUpdateInternalLocationMiddlewareListener(
201
+ // session._navigation._createLocationObject({
202
+ // operation: 'SHIFT',
203
+ // delta: null,
204
+ // }),
205
+ // );
206
+ // /* eslint-enable no-underscore-dangle */
207
+ //
208
+ // navigationBlockerDeferred.resolve(true);
209
+ // await delay(10);
210
+ //
211
+ // // Without delta, we can't rewind the location change,
212
+ // // so navigation is allowed without calling any blockers.
213
+ // expect(currentNavigationLocation.pathname).to.equal('/initial');
214
+ // expect(store.getState().pathname).to.equal('/initial');
215
+ // });
216
+
217
+ // it('should allow navigation when blocker returns `undefined` and `location.delta` is `null`', async () => {
218
+ // const navigationBlockerDeferred = pDefer();
219
+ // addNavigationBlocker(() => navigationBlockerDeferred.promise);
220
+ //
221
+ // // Update location with a `SHIFT` operation.
222
+ // /* eslint-disable no-underscore-dangle */
223
+ // session._navigation._index = 0;
224
+ // session._navigation._subscriptionListener(session._navigation._createLocationObject({
225
+ // operation: 'SHIFT',
226
+ // delta: null,
227
+ // }));
228
+ // /* eslint-enable no-underscore-dangle */
229
+ //
230
+ // // Without delta, we can't rewind on the session.
231
+ // expect(currentNavigationLocation.pathname).to.equal('/initial');
232
+ // expect(store.getState().pathname).to.equal('/new');
233
+ //
234
+ // navigationBlockerDeferred.resolve(undefined);
235
+ // await delay(10);
236
+ //
237
+ // expect(currentNavigationLocation.pathname).to.equal('/initial');
238
+ // expect(store.getState().pathname).to.equal('/initial');
239
+ // });
240
+
241
+ // it('should block store update when blocker returns `true` and `location.delta` is `null`', async () => {
242
+ // const navigationBlockerDeferred = pDefer();
243
+ // addNavigationBlocker(() => navigationBlockerDeferred.promise);
244
+ //
245
+ // /* eslint-disable no-underscore-dangle */
246
+ // session._navigation._index = 0;
247
+ // session._navigation._subscriptionListener(session._navigation._createLocationObject({
248
+ // operation: 'SHIFT',
249
+ // delta: null,
250
+ // }));
251
+ // /* eslint-enable no-underscore-dangle */
252
+ //
253
+ // expect(session._navigation.getInitialLocation().pathname).to.equal('/initial');
254
+ // expect(store.getState().pathname).to.equal('/new');
255
+ //
256
+ // navigationBlockerDeferred.resolve(true);
257
+ // await delay(10);
258
+ //
259
+ // // These are out-of-sync now, but it's the best we can do.
260
+ // expect(session._navigation.getInitialLocation().pathname).to.equal('/initial');
261
+ // expect(store.getState().pathname).to.equal('/new');
262
+ // });
263
+ });
264
+ });