navigation-stack 0.5.2 → 0.6.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 (210) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/README.md +144 -282
  3. package/karma.conf.cjs +1 -1
  4. package/lib/cjs/NavigationStack.js +138 -49
  5. package/lib/cjs/data-storage/DataStorage.js +7 -6
  6. package/lib/cjs/environment/InMemoryEnvironment.js +6 -0
  7. package/lib/cjs/{session/ServerSideRenderSession.js → environment/ServerSideRenderEnvironment.js} +5 -6
  8. package/lib/cjs/environment/WebBrowserEnvironment.js +6 -0
  9. package/lib/cjs/environment/log/InMemoryLog.js +23 -0
  10. package/lib/cjs/environment/log/WebBrowserLog.js +22 -0
  11. package/lib/cjs/{session → environment}/navigation/InMemoryNavigation.js +16 -5
  12. package/lib/cjs/{session → environment}/navigation/ServerSideNavigation.js +16 -7
  13. package/lib/cjs/{session → environment}/navigation/WebBrowserNavigation.js +48 -8
  14. package/lib/cjs/{session/navigation/error/ServerSideNavigationError.js → environment/navigation/error/ServerSideRedirectError.js} +2 -2
  15. package/lib/cjs/environment/scroll-position/WebBrowserScrollPosition.js +15 -0
  16. package/lib/cjs/getLocationBaseFromLocation.js +14 -0
  17. package/lib/cjs/getLocationUrl.js +3 -5
  18. package/lib/cjs/index.js +10 -16
  19. package/lib/cjs/navigationBlockers.js +34 -32
  20. package/lib/cjs/navigationBlockersEvaluation.js +150 -0
  21. package/lib/cjs/parseInputLocation.js +10 -3
  22. package/lib/cjs/parseQueryFromSearch.js +3 -6
  23. package/lib/cjs/parseQueryString.js +77 -0
  24. package/lib/cjs/scroll-position/ScrollPositionAutoSaver.js +7 -6
  25. package/lib/cjs/scroll-position/ScrollPositionRestoration.js +31 -27
  26. package/lib/cjs/scroll-position/ScrollPositionSaver.js +6 -4
  27. package/lib/cjs/session/Session.js +61 -26
  28. package/lib/cjs/session/subscription/Subscription.js +36 -18
  29. package/lib/cjs/stringifyQuery.js +66 -0
  30. package/lib/cjs/stringifyQueryAsSearch.js +14 -0
  31. package/lib/esm/NavigationStack.js +138 -49
  32. package/lib/esm/data-storage/DataStorage.js +7 -6
  33. package/lib/esm/environment/InMemoryEnvironment.js +6 -0
  34. package/lib/esm/environment/ServerSideRenderEnvironment.js +10 -0
  35. package/lib/esm/environment/WebBrowserEnvironment.js +6 -0
  36. package/lib/esm/environment/log/InMemoryLog.js +17 -0
  37. package/lib/esm/environment/log/WebBrowserLog.js +16 -0
  38. package/lib/esm/{session → environment}/navigation/InMemoryNavigation.js +16 -5
  39. package/lib/esm/{session → environment}/navigation/ServerSideNavigation.js +16 -7
  40. package/lib/esm/{session → environment}/navigation/WebBrowserNavigation.js +48 -8
  41. package/lib/esm/{session/navigation/error/ServerSideNavigationError.js → environment/navigation/error/ServerSideRedirectError.js} +1 -1
  42. package/lib/esm/environment/scroll-position/WebBrowserScrollPosition.js +15 -0
  43. package/lib/esm/getLocationBaseFromLocation.js +9 -0
  44. package/lib/esm/getLocationUrl.js +2 -5
  45. package/lib/esm/index.js +5 -8
  46. package/lib/esm/navigationBlockers.js +34 -32
  47. package/lib/esm/navigationBlockersEvaluation.js +145 -0
  48. package/lib/esm/parseInputLocation.js +9 -3
  49. package/lib/esm/parseQueryFromSearch.js +2 -6
  50. package/lib/esm/parseQueryString.js +72 -0
  51. package/lib/esm/scroll-position/ScrollPositionAutoSaver.js +7 -6
  52. package/lib/esm/scroll-position/ScrollPositionRestoration.js +31 -27
  53. package/lib/esm/scroll-position/ScrollPositionSaver.js +6 -4
  54. package/lib/esm/session/Session.js +61 -26
  55. package/lib/esm/session/subscription/Subscription.js +36 -18
  56. package/lib/esm/stringifyQuery.js +61 -0
  57. package/lib/esm/stringifyQueryAsSearch.js +8 -0
  58. package/lib/index.d.ts +180 -34
  59. package/package.json +4 -7
  60. package/src/NavigationStack.js +166 -56
  61. package/src/data-storage/DataStorage.js +9 -6
  62. package/src/environment/InMemoryEnvironment.js +6 -0
  63. package/src/environment/ServerSideRenderEnvironment.js +10 -0
  64. package/src/environment/WebBrowserEnvironment.js +6 -0
  65. package/src/environment/log/InMemoryLog.js +20 -0
  66. package/src/environment/log/WebBrowserLog.js +18 -0
  67. package/src/{session → environment}/navigation/InMemoryNavigation.js +16 -5
  68. package/src/{session → environment}/navigation/ServerSideNavigation.js +16 -7
  69. package/src/{session → environment}/navigation/WebBrowserNavigation.js +48 -8
  70. package/src/{session/navigation/error/ServerSideNavigationError.js → environment/navigation/error/ServerSideRedirectError.js} +1 -1
  71. package/src/environment/scroll-position/WebBrowserScrollPosition.js +15 -0
  72. package/src/getLocationBaseFromLocation.js +7 -0
  73. package/src/getLocationUrl.js +2 -5
  74. package/src/index.js +10 -13
  75. package/src/navigationBlockers.js +55 -34
  76. package/src/navigationBlockersEvaluation.js +161 -0
  77. package/src/parseInputLocation.js +10 -3
  78. package/src/parseQueryFromSearch.js +2 -6
  79. package/src/parseQueryString.js +81 -0
  80. package/src/scroll-position/ScrollPositionAutoSaver.js +10 -6
  81. package/src/scroll-position/ScrollPositionRestoration.js +36 -30
  82. package/src/scroll-position/ScrollPositionSaver.js +6 -4
  83. package/src/scroll-position/index.js +1 -1
  84. package/src/session/Session.js +68 -24
  85. package/src/session/subscription/Subscription.js +36 -11
  86. package/src/stringifyQuery.js +71 -0
  87. package/src/stringifyQueryAsSearch.js +9 -0
  88. package/test/NavigationStack.addBasePath.test.js +50 -0
  89. package/test/{redux/middleware/createNonProgrammaticNavigationBlockerMiddleware.test.js → NavigationStack.blockNonProgrammaticNavigationIfRequired.test.js} +51 -63
  90. package/test/{redux/middleware/createProgrammaticNavigationBlockerMiddleware.test.js → NavigationStack.blockProgrammaticNavigationIfRequired.test.js} +98 -78
  91. package/test/NavigationStack.general.test.js +68 -0
  92. package/test/NavigationStack.parseInputLocation.test.js +52 -0
  93. package/test/NavigationStack.removeBasePath.test.js +69 -0
  94. package/test/NavigationStack.test.js +97 -29
  95. package/test/data-storage/LocationDataStorage.test.js +3 -2
  96. package/test/index.js +7 -31
  97. package/test/index.test.js +4 -5
  98. package/test/parseQueryFromSearch.test.js +19 -0
  99. package/test/parseQueryString.test.js +18 -0
  100. package/test/scroll-position/ScrollPositionRestoration.test.js +34 -13
  101. package/test/scroll-position/createApp.js +8 -8
  102. package/test/scroll-position/withScrollableContainerAtIndexPageWithDisabledAutomaticScrollPositionRestoration.js +4 -4
  103. package/test/session/{InMemorySession.test.js → Session.InMemoryEnvironment.test.js} +10 -9
  104. package/test/session/{ServerSession.test.js → Session.ServerSideRenderEnvironment.test.js} +5 -4
  105. package/test/session/{WebBrowserSession.test.js → Session.WebBrowserEnvironment.test.js} +63 -13
  106. package/test/shouldWarn.js +44 -0
  107. package/test/stringifyQuery.test.js +65 -0
  108. package/types/index.d.ts +180 -34
  109. package/types/tsconfig.json +0 -1
  110. package/data-storage/package.json +0 -7
  111. package/lib/cjs/createSearchFromQuery.js +0 -13
  112. package/lib/cjs/debug.js +0 -12
  113. package/lib/cjs/redux/ActionTypes.js +0 -14
  114. package/lib/cjs/redux/ActionTypesInternal.js +0 -8
  115. package/lib/cjs/redux/Actions.js +0 -28
  116. package/lib/cjs/redux/createMiddlewares.js +0 -60
  117. package/lib/cjs/redux/index.js +0 -13
  118. package/lib/cjs/redux/internalLocationReducer.js +0 -14
  119. package/lib/cjs/redux/locationReducer.js +0 -13
  120. package/lib/cjs/redux/middleware/createAddInputLocationBasePathMiddleware.js +0 -32
  121. package/lib/cjs/redux/middleware/createNonProgrammaticNavigationBlockerMiddleware.js +0 -113
  122. package/lib/cjs/redux/middleware/createProgrammaticNavigationBlockerMiddleware.js +0 -94
  123. package/lib/cjs/redux/middleware/createRemoveOutputLocationBasePathMiddleware.js +0 -30
  124. package/lib/cjs/redux/middleware/createUpdateInternalLocationMiddleware.js +0 -73
  125. package/lib/cjs/redux/middleware/navigationOperationMiddleware.js +0 -40
  126. package/lib/cjs/redux/middleware/parseInputLocationMiddleware.js +0 -29
  127. package/lib/cjs/redux/middleware/updateLocationMiddleware.js +0 -34
  128. package/lib/cjs/session/InMemorySession.js +0 -22
  129. package/lib/cjs/session/WebBrowserSession.js +0 -20
  130. package/lib/data-storage/index.d.ts +0 -35
  131. package/lib/esm/createSearchFromQuery.js +0 -8
  132. package/lib/esm/debug.js +0 -7
  133. package/lib/esm/redux/ActionTypes.js +0 -9
  134. package/lib/esm/redux/ActionTypesInternal.js +0 -3
  135. package/lib/esm/redux/Actions.js +0 -22
  136. package/lib/esm/redux/createMiddlewares.js +0 -54
  137. package/lib/esm/redux/index.js +0 -4
  138. package/lib/esm/redux/internalLocationReducer.js +0 -8
  139. package/lib/esm/redux/locationReducer.js +0 -7
  140. package/lib/esm/redux/middleware/createAddInputLocationBasePathMiddleware.js +0 -27
  141. package/lib/esm/redux/middleware/createNonProgrammaticNavigationBlockerMiddleware.js +0 -108
  142. package/lib/esm/redux/middleware/createProgrammaticNavigationBlockerMiddleware.js +0 -88
  143. package/lib/esm/redux/middleware/createRemoveOutputLocationBasePathMiddleware.js +0 -25
  144. package/lib/esm/redux/middleware/createUpdateInternalLocationMiddleware.js +0 -68
  145. package/lib/esm/redux/middleware/navigationOperationMiddleware.js +0 -35
  146. package/lib/esm/redux/middleware/parseInputLocationMiddleware.js +0 -24
  147. package/lib/esm/redux/middleware/updateLocationMiddleware.js +0 -28
  148. package/lib/esm/session/InMemorySession.js +0 -15
  149. package/lib/esm/session/ServerSideRenderSession.js +0 -11
  150. package/lib/esm/session/WebBrowserSession.js +0 -13
  151. package/lib/redux/index.d.ts +0 -90
  152. package/lib/scroll-position/index.d.ts +0 -107
  153. package/redux/package.json +0 -7
  154. package/scroll-position/package.json +0 -7
  155. package/src/createSearchFromQuery.js +0 -9
  156. package/src/debug.js +0 -8
  157. package/src/redux/ActionTypes.js +0 -9
  158. package/src/redux/ActionTypesInternal.js +0 -3
  159. package/src/redux/Actions.js +0 -27
  160. package/src/redux/createMiddlewares.js +0 -65
  161. package/src/redux/index.js +0 -4
  162. package/src/redux/internalLocationReducer.js +0 -9
  163. package/src/redux/locationReducer.js +0 -8
  164. package/src/redux/middleware/createAddInputLocationBasePathMiddleware.js +0 -27
  165. package/src/redux/middleware/createNonProgrammaticNavigationBlockerMiddleware.js +0 -119
  166. package/src/redux/middleware/createProgrammaticNavigationBlockerMiddleware.js +0 -94
  167. package/src/redux/middleware/createRemoveOutputLocationBasePathMiddleware.js +0 -26
  168. package/src/redux/middleware/createUpdateInternalLocationMiddleware.js +0 -72
  169. package/src/redux/middleware/navigationOperationMiddleware.js +0 -34
  170. package/src/redux/middleware/parseInputLocationMiddleware.js +0 -23
  171. package/src/redux/middleware/updateLocationMiddleware.js +0 -28
  172. package/src/session/InMemorySession.js +0 -13
  173. package/src/session/ServerSideRenderSession.js +0 -9
  174. package/src/session/WebBrowserSession.js +0 -13
  175. package/test/middlewareTestUtil.js +0 -31
  176. package/test/redux/Action.test.js +0 -73
  177. package/test/redux/ActionTypes.test.js +0 -13
  178. package/test/redux/createMiddlewares.test.js +0 -96
  179. package/test/redux/index.test.js +0 -10
  180. package/test/redux/locationReducer.test.js +0 -39
  181. package/test/redux/middleware/createAddInputLocationBasePathMiddleware.test.js +0 -40
  182. package/test/redux/middleware/createRemoveOutputLocationBasePathMiddleware.test.js +0 -51
  183. package/test/redux/middleware/navigationOperationMiddleware.test.js +0 -78
  184. package/test/redux/middleware/parseInputLocationMiddleware.test.js +0 -62
  185. package/test/testUtil.js +0 -3
  186. package/types/data-storage/index.d.ts +0 -35
  187. package/types/redux/index.d.ts +0 -90
  188. package/types/scroll-position/index.d.ts +0 -107
  189. /package/lib/cjs/{session → environment}/lifecycle/InMemorySessionLifecycle.js +0 -0
  190. /package/lib/cjs/{session → environment}/lifecycle/WebBrowserSessionLifecycle.js +0 -0
  191. /package/lib/cjs/{session → environment}/lifecycle/page-lifecycle/PageLifecycle.js +0 -0
  192. /package/lib/cjs/{session → environment}/lifecycle/page-lifecycle/PageLifecycleInstance.js +0 -0
  193. /package/lib/cjs/{session → environment}/lifecycle/page-lifecycle/supportsConstructableEventTarget.js +0 -0
  194. /package/lib/cjs/{session → environment}/navigation/error/NavigationOutOfBoundsError.js +0 -0
  195. /package/lib/cjs/{session → environment}/navigation/operation/operations.js +0 -0
  196. /package/lib/esm/{session → environment}/lifecycle/InMemorySessionLifecycle.js +0 -0
  197. /package/lib/esm/{session → environment}/lifecycle/WebBrowserSessionLifecycle.js +0 -0
  198. /package/lib/esm/{session → environment}/lifecycle/page-lifecycle/PageLifecycle.js +0 -0
  199. /package/lib/esm/{session → environment}/lifecycle/page-lifecycle/PageLifecycleInstance.js +0 -0
  200. /package/lib/esm/{session → environment}/lifecycle/page-lifecycle/supportsConstructableEventTarget.js +0 -0
  201. /package/lib/esm/{session → environment}/navigation/error/NavigationOutOfBoundsError.js +0 -0
  202. /package/lib/esm/{session → environment}/navigation/operation/operations.js +0 -0
  203. /package/src/{session → environment}/lifecycle/InMemorySessionLifecycle.js +0 -0
  204. /package/src/{session → environment}/lifecycle/WebBrowserSessionLifecycle.js +0 -0
  205. /package/src/{session → environment}/lifecycle/page-lifecycle/PageLifecycle.js +0 -0
  206. /package/src/{session → environment}/lifecycle/page-lifecycle/PageLifecycleInstance.js +0 -0
  207. /package/src/{session → environment}/lifecycle/page-lifecycle/supportsConstructableEventTarget.js +0 -0
  208. /package/src/{session → environment}/navigation/error/NavigationOutOfBoundsError.js +0 -0
  209. /package/src/{session → environment}/navigation/operation/operations.js +0 -0
  210. /package/test/{parseInputLocationMiddleware.test.js → parseInputLocation.test.js} +0 -0
@@ -1,42 +1,34 @@
1
1
  import delay from 'delay';
2
2
  import pDefer from 'p-defer';
3
- import { applyMiddleware, createStore } from 'redux';
4
3
 
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
- import { shouldWarn } from '../../testUtil';
4
+ import shouldWarn from './shouldWarn';
5
+ import NavigationStack from '../src/NavigationStack';
6
+ import addNavigationBlockerOriginal from '../src/addNavigationBlocker';
7
+ import InMemoryEnvironment from '../src/environment/InMemoryEnvironment';
11
8
 
12
- describe('createProgrammaticNavigationBlockerMiddleware', () => {
9
+ describe('NavigationStack (blockProgrammaticNavigationIfRequired)', () => {
13
10
  // const sandbox = sinon.createSandbox();
14
11
 
15
12
  let session;
16
- let store;
13
+ let navigationStack;
17
14
 
18
15
  function addNavigationBlocker(blocker) {
19
16
  return addNavigationBlockerOriginal(session, blocker);
20
17
  }
21
18
 
22
19
  beforeEach(() => {
23
- session = new InMemorySession();
24
-
25
- store = createStore(
26
- internalLocationReducer,
27
- applyMiddleware(
28
- ...createMiddlewares(session, {
29
- _internalLocationReducer: true,
30
- }),
31
- ),
32
- );
33
- store.dispatch(Actions.init('/initial'));
34
-
35
- sinon.spy(session.lifecycle, 'addTerminationBlocker');
20
+ navigationStack = new NavigationStack(InMemoryEnvironment);
21
+
22
+ // eslint-disable-next-line no-underscore-dangle
23
+ session = navigationStack._session;
24
+
25
+ navigationStack.init('/initial');
26
+
27
+ sinon.spy(session.environment.lifecycle, 'addTerminationBlocker');
36
28
  });
37
29
 
38
30
  afterEach(() => {
39
- store.dispatch(Actions.stop());
31
+ navigationStack.stop();
40
32
 
41
33
  // sandbox.restore();
42
34
  });
@@ -46,8 +38,8 @@ describe('createProgrammaticNavigationBlockerMiddleware', () => {
46
38
  const blocker = sinon.stub().returns(true);
47
39
  addNavigationBlocker(blocker);
48
40
 
49
- store.dispatch(Actions.push('/new'));
50
- expect(store.getState().pathname).to.equal('/initial');
41
+ navigationStack.push('/new');
42
+ expect(navigationStack.current().pathname).to.equal('/initial');
51
43
 
52
44
  expect(blocker).to.have.been.calledOnce();
53
45
 
@@ -60,8 +52,8 @@ describe('createProgrammaticNavigationBlockerMiddleware', () => {
60
52
  it('should allow navigation when blocker returns `undefined`', () => {
61
53
  addNavigationBlocker(() => undefined);
62
54
 
63
- store.dispatch(Actions.push('/new'));
64
- expect(store.getState().pathname).to.equal('/new');
55
+ navigationStack.push('/new');
56
+ expect(navigationStack.current().pathname).to.equal('/new');
65
57
  });
66
58
 
67
59
  it("should fall through when first blocker doesn't return `true`", () => {
@@ -71,8 +63,8 @@ describe('createProgrammaticNavigationBlockerMiddleware', () => {
71
63
  addNavigationBlocker(blocker1);
72
64
  addNavigationBlocker(blocker2);
73
65
 
74
- store.dispatch(Actions.push('/new'));
75
- expect(store.getState().pathname).to.equal('/initial');
66
+ navigationStack.push('/new');
67
+ expect(navigationStack.current().pathname).to.equal('/initial');
76
68
 
77
69
  expect(blocker1).to.have.been.calledOnce();
78
70
  expect(blocker2).to.have.been.calledOnce();
@@ -85,8 +77,8 @@ describe('createProgrammaticNavigationBlockerMiddleware', () => {
85
77
  addNavigationBlocker(blocker1);
86
78
  addNavigationBlocker(blocker2);
87
79
 
88
- store.dispatch(Actions.push('/new'));
89
- expect(store.getState().pathname).to.equal('/initial');
80
+ navigationStack.push('/new');
81
+ expect(navigationStack.current().pathname).to.equal('/initial');
90
82
 
91
83
  expect(blocker1).to.have.been.calledOnce();
92
84
  expect(blocker2).not.to.have.been.called();
@@ -103,8 +95,8 @@ describe('createProgrammaticNavigationBlockerMiddleware', () => {
103
95
 
104
96
  addNavigationBlocker(syncBlocker);
105
97
 
106
- store.dispatch(Actions.push('/new'));
107
- expect(store.getState().pathname).to.equal('/new');
98
+ navigationStack.push('/new');
99
+ expect(navigationStack.current().pathname).to.equal('/new');
108
100
  });
109
101
 
110
102
  // it('should show a confirmation dialog and allow navigation on string', () => {
@@ -112,8 +104,8 @@ describe('createProgrammaticNavigationBlockerMiddleware', () => {
112
104
  //
113
105
  // addNavigationBlocker(({ pathname }) => pathname);
114
106
  //
115
- // store.dispatch(Actions.push('/new'));
116
- // expect(store.getState().pathname).to.equal('/new');
107
+ // navigationStack.push('/new'));
108
+ // expect(navigationStack.current().pathname).to.equal('/new');
117
109
  //
118
110
  // expect(window.confirm)
119
111
  // .to.have.been.calledOnce()
@@ -125,8 +117,8 @@ describe('createProgrammaticNavigationBlockerMiddleware', () => {
125
117
  //
126
118
  // addNavigationBlocker(({ pathname }) => pathname);
127
119
  //
128
- // store.dispatch(Actions.push('/new'));
129
- // expect(store.getState().pathname).to.equal('/initial');
120
+ // navigationStack.push('/new'));
121
+ // expect(navigationStack.current().pathname).to.equal('/initial');
130
122
  //
131
123
  // expect(window.confirm)
132
124
  // .to.have.been.calledOnce()
@@ -137,26 +129,26 @@ describe('createProgrammaticNavigationBlockerMiddleware', () => {
137
129
  const navigationBlockerDeferred = pDefer();
138
130
  addNavigationBlocker(() => navigationBlockerDeferred.promise);
139
131
 
140
- store.dispatch(Actions.push('/new'));
141
- expect(store.getState().pathname).to.equal('/initial');
132
+ navigationStack.push('/new');
133
+ expect(navigationStack.current().pathname).to.equal('/initial');
142
134
 
143
135
  navigationBlockerDeferred.resolve(undefined);
144
136
  await delay(10);
145
137
 
146
- expect(store.getState().pathname).to.equal('/new');
138
+ expect(navigationStack.current().pathname).to.equal('/new');
147
139
  });
148
140
 
149
141
  it('should block navigation when blocker returns `true` (async)', async () => {
150
142
  const navigationBlockerDeferred = pDefer();
151
143
  addNavigationBlocker(() => navigationBlockerDeferred.promise);
152
144
 
153
- store.dispatch(Actions.push('/new'));
154
- expect(store.getState().pathname).to.equal('/initial');
145
+ navigationStack.push('/new');
146
+ expect(navigationStack.current().pathname).to.equal('/initial');
155
147
 
156
148
  navigationBlockerDeferred.resolve(true);
157
149
  await delay(10);
158
150
 
159
- expect(store.getState().pathname).to.equal('/initial');
151
+ expect(navigationStack.current().pathname).to.equal('/initial');
160
152
  });
161
153
 
162
154
  it('should allow chaining async blockers', async () => {
@@ -166,18 +158,18 @@ describe('createProgrammaticNavigationBlockerMiddleware', () => {
166
158
  addNavigationBlocker(() => navigationBlockerDeferred1.promise);
167
159
  addNavigationBlocker(() => navigationBlockerDeferred2.promise);
168
160
 
169
- store.dispatch(Actions.push('/new'));
170
- expect(store.getState().pathname).to.equal('/initial');
161
+ navigationStack.push('/new');
162
+ expect(navigationStack.current().pathname).to.equal('/initial');
171
163
 
172
164
  navigationBlockerDeferred1.resolve(undefined);
173
165
  await delay(10);
174
166
 
175
- expect(store.getState().pathname).to.equal('/initial');
167
+ expect(navigationStack.current().pathname).to.equal('/initial');
176
168
 
177
169
  navigationBlockerDeferred2.resolve(undefined);
178
170
  await delay(10);
179
171
 
180
- expect(store.getState().pathname).to.equal('/new');
172
+ expect(navigationStack.current().pathname).to.equal('/new');
181
173
  });
182
174
 
183
175
  it('should warn on and ignore async blockers that throw an error', async () => {
@@ -192,24 +184,24 @@ describe('createProgrammaticNavigationBlockerMiddleware', () => {
192
184
 
193
185
  addNavigationBlocker(asyncBlocker);
194
186
 
195
- store.dispatch(Actions.push('/new'));
196
- expect(store.getState().pathname).to.equal('/initial');
187
+ navigationStack.push('/new');
188
+ expect(navigationStack.current().pathname).to.equal('/initial');
197
189
 
198
190
  await delay(10);
199
191
 
200
- expect(store.getState().pathname).to.equal('/new');
192
+ expect(navigationStack.current().pathname).to.equal('/new');
201
193
  });
202
194
 
203
195
  it('should allow removing blockers', () => {
204
196
  const removeNavigationBlocker = addNavigationBlocker(() => true);
205
197
 
206
- store.dispatch(Actions.push('/new'));
207
- expect(store.getState().pathname).to.equal('/initial');
198
+ navigationStack.push('/new');
199
+ expect(navigationStack.current().pathname).to.equal('/initial');
208
200
 
209
201
  removeNavigationBlocker();
210
202
 
211
- store.dispatch(Actions.push('/new'));
212
- expect(store.getState().pathname).to.equal('/new');
203
+ navigationStack.push('/new');
204
+ expect(navigationStack.current().pathname).to.equal('/new');
213
205
  });
214
206
  });
215
207
  });
@@ -218,43 +210,46 @@ describe('addTerminationBlocker', () => {
218
210
  // const sandbox = sinon.createSandbox();
219
211
 
220
212
  let session;
221
- let store;
213
+ let navigationStack;
222
214
 
223
215
  function addNavigationBlocker(blocker) {
224
216
  return addNavigationBlockerOriginal(session, blocker);
225
217
  }
226
218
 
227
219
  beforeEach(() => {
228
- session = new InMemorySession();
220
+ navigationStack = new NavigationStack(InMemoryEnvironment);
221
+
222
+ // eslint-disable-next-line no-underscore-dangle
223
+ session = navigationStack._session;
229
224
 
230
- store = createStore(
231
- internalLocationReducer,
232
- applyMiddleware(...createMiddlewares(session)),
233
- );
234
- store.dispatch(Actions.init('/initial'));
225
+ navigationStack.init('/initial');
235
226
 
236
- sinon.spy(session.lifecycle, 'addTerminationBlocker');
227
+ sinon.spy(session.environment.lifecycle, 'addTerminationBlocker');
237
228
 
238
229
  // sandbox.stub(window, 'addEventListener');
239
230
  // sandbox.stub(window, 'removeEventListener');
240
231
  });
241
232
 
242
233
  afterEach(() => {
243
- if (store) {
244
- store.dispatch(Actions.stop());
234
+ if (navigationStack) {
235
+ navigationStack.stop();
245
236
  }
246
237
 
247
238
  // sandbox.restore();
248
239
  });
249
240
 
250
241
  it('should add/remove event blocker', () => {
251
- expect(session.lifecycle.addTerminationBlocker).not.to.have.been.called();
242
+ expect(
243
+ session.environment.lifecycle.addTerminationBlocker,
244
+ ).not.to.have.been.called();
252
245
  // expect(window.addEventListener).not.to.have.been.called();
253
246
 
254
247
  const removeNavigationBlocker1 = addNavigationBlocker(() => null, {
255
248
  beforeUnload: true,
256
249
  });
257
- expect(session.lifecycle.addTerminationBlocker).to.have.been.calledOnce();
250
+ expect(
251
+ session.environment.lifecycle.addTerminationBlocker,
252
+ ).to.have.been.calledOnce();
258
253
  // expect(window.addEventListener)
259
254
  // .to.have.been.calledOnce()
260
255
  // .and.to.have.been.called.with('beforeunload');
@@ -262,7 +257,9 @@ describe('addTerminationBlocker', () => {
262
257
  const removeNavigationBlocker2 = addNavigationBlocker(() => null, {
263
258
  beforeUnload: true,
264
259
  });
265
- expect(session.lifecycle.addTerminationBlocker).to.have.been.calledOnce();
260
+ expect(
261
+ session.environment.lifecycle.addTerminationBlocker,
262
+ ).to.have.been.calledOnce();
266
263
  // expect(window.addEventListener)
267
264
  // .to.have.been.calledOnce()
268
265
  // .and.to.have.been.called.with('beforeunload');
@@ -282,6 +279,39 @@ describe('addTerminationBlocker', () => {
282
279
  expect(removeTerminationBlocker).to.have.been.calledOnce();
283
280
  });
284
281
 
282
+ // it('should not add a global "before destroy" listener when no `beforeTermination` blocker has been added', () => {
283
+ // const removeNavigationBlocker = addNavigationBlocker(() => null);
284
+ // expect(window.addEventListener).not.to.have.been.called();
285
+ //
286
+ // removeNavigationBlocker();
287
+ // expect(window.removeEventListener).not.to.have.been.called();
288
+ // });
289
+ });
290
+
291
+ describe('NavigationStack (blockProgrammaticNavigationIfRequired) (stop)', () => {
292
+ let session;
293
+ let navigationStack;
294
+
295
+ function addNavigationBlocker(blocker) {
296
+ return addNavigationBlockerOriginal(session, blocker);
297
+ }
298
+
299
+ beforeEach(() => {
300
+ navigationStack = new NavigationStack(InMemoryEnvironment);
301
+
302
+ // eslint-disable-next-line no-underscore-dangle
303
+ session = navigationStack._session;
304
+
305
+ navigationStack.init('/initial');
306
+
307
+ sinon.spy(session.environment.lifecycle, 'addTerminationBlocker');
308
+ });
309
+
310
+ afterEach(() => {
311
+ // Don't call `navigationStack.stop()` here because it was already stopped in the test case.
312
+ // navigationStack.stop();
313
+ });
314
+
285
315
  it('should remove event blocker on stop', () => {
286
316
  addNavigationBlocker(() => null);
287
317
 
@@ -292,21 +322,11 @@ describe('addTerminationBlocker', () => {
292
322
  // expect(window.removeEventListener).not.to.have.been.called();
293
323
  expect(removeTerminationBlocker).not.to.have.been.called();
294
324
 
295
- store.dispatch(Actions.stop());
296
- // Prevent `store.dispatch(Actions.stop())` in `afterEach()`.
297
- store = undefined;
325
+ navigationStack.stop();
298
326
 
299
327
  // expect(window.removeEventListener)
300
328
  // .to.have.been.calledOnce()
301
329
  // .and.to.have.been.called.with('beforeunload');
302
330
  expect(removeTerminationBlocker).to.have.been.calledOnce();
303
331
  });
304
-
305
- // it('should not add a global "before destroy" listener when no `beforeTermination` blocker has been added', () => {
306
- // const removeNavigationBlocker = addNavigationBlocker(() => null);
307
- // expect(window.addEventListener).not.to.have.been.called();
308
- //
309
- // removeNavigationBlocker();
310
- // expect(window.removeEventListener).not.to.have.been.called();
311
- // });
312
332
  });
@@ -0,0 +1,68 @@
1
+ import NavigationStack from '../src/NavigationStack';
2
+ import InMemoryEnvironment from '../src/environment/InMemoryEnvironment';
3
+
4
+ describe('NavigationStack (general)', () => {
5
+ let navigationStack;
6
+
7
+ beforeEach(() => {
8
+ navigationStack = new NavigationStack(InMemoryEnvironment);
9
+ navigationStack.init('/initial');
10
+ });
11
+
12
+ afterEach(() => {
13
+ navigationStack.stop();
14
+ });
15
+
16
+ it('should support `push` and `shift` navigation actions', () => {
17
+ navigationStack.push('/new');
18
+ expect(navigationStack.current()).to.include({
19
+ pathname: '/new',
20
+ index: 1,
21
+ });
22
+
23
+ navigationStack.shift(-1);
24
+ expect(navigationStack.current()).to.include({
25
+ pathname: '/initial',
26
+ index: 0,
27
+ });
28
+
29
+ navigationStack.shift(+1);
30
+ expect(navigationStack.current()).to.include({
31
+ pathname: '/new',
32
+ index: 1,
33
+ });
34
+ });
35
+
36
+ it('should support `replace` navigation action', () => {
37
+ navigationStack.replace('/new');
38
+ expect(navigationStack.current()).to.include({
39
+ pathname: '/new',
40
+ index: 0,
41
+ });
42
+ });
43
+ });
44
+
45
+ describe('NavigationStack (basePath)', () => {
46
+ it('should support `basePath` option', () => {
47
+ const navigationStack = new NavigationStack(InMemoryEnvironment, {
48
+ basePath: '/base',
49
+ });
50
+
51
+ // eslint-disable-next-line no-underscore-dangle
52
+ const session = navigationStack._session;
53
+
54
+ navigationStack.init('/initial');
55
+
56
+ navigationStack.push('/new');
57
+
58
+ // eslint-disable-next-line no-underscore-dangle
59
+ expect(session._subscription._latest.pathname).to.equal('/base/new');
60
+
61
+ expect(navigationStack.current()).to.include({
62
+ pathname: '/new',
63
+ index: 1,
64
+ });
65
+
66
+ navigationStack.stop();
67
+ });
68
+ });
@@ -0,0 +1,52 @@
1
+ import NavigationStack from '../src/NavigationStack';
2
+ import InMemoryEnvironment from '../src/environment/InMemoryEnvironment';
3
+
4
+ describe('NavigationStack (parseInputLocation)', () => {
5
+ let navigationStack;
6
+
7
+ beforeEach(() => {
8
+ navigationStack = new NavigationStack(InMemoryEnvironment);
9
+
10
+ // should transform input location of `.init()` method (`string` to `object`)
11
+ navigationStack.init('/foo?bar=baz#qux');
12
+
13
+ expect(navigationStack.current()).to.deep.include({
14
+ pathname: '/foo',
15
+ search: '?bar=baz',
16
+ query: {
17
+ bar: 'baz',
18
+ },
19
+ hash: '#qux',
20
+ });
21
+ });
22
+
23
+ afterEach(() => {
24
+ navigationStack.stop();
25
+ });
26
+
27
+ it('should transform input location of `.push()` method (`string` to `object`)', () => {
28
+ navigationStack.push('/foo?bar=baz#qux');
29
+
30
+ expect(navigationStack.current()).to.deep.include({
31
+ pathname: '/foo',
32
+ search: '?bar=baz',
33
+ query: {
34
+ bar: 'baz',
35
+ },
36
+ hash: '#qux',
37
+ });
38
+ });
39
+
40
+ it('should transform input location of `.replace()` method (`string` to `object`)', () => {
41
+ navigationStack.replace('/foo?bar=baz#qux');
42
+
43
+ expect(navigationStack.current()).to.deep.include({
44
+ pathname: '/foo',
45
+ search: '?bar=baz',
46
+ query: {
47
+ bar: 'baz',
48
+ },
49
+ hash: '#qux',
50
+ });
51
+ });
52
+ });
@@ -0,0 +1,69 @@
1
+ import NavigationStack from '../src/NavigationStack';
2
+ import InMemoryEnvironment from '../src/environment/InMemoryEnvironment';
3
+ import parseInputLocation from '../src/parseInputLocation';
4
+
5
+ describe('NavigationStack (removeBasePath)', () => {
6
+ it('should strip `basePath` from `location.pathname`', () => {
7
+ const navigationStack = new NavigationStack(InMemoryEnvironment, {
8
+ basePath: '/base',
9
+ });
10
+ // eslint-disable-next-line no-underscore-dangle
11
+ const session = navigationStack._session;
12
+ session.start(parseInputLocation('/base/path'));
13
+ expect(navigationStack.current().pathname).to.equal('/path');
14
+ navigationStack.stop();
15
+ });
16
+
17
+ it('should strip `basePath` (with a trailing slash) from `location.pathname`', () => {
18
+ const navigationStack = new NavigationStack(InMemoryEnvironment, {
19
+ basePath: '/base/',
20
+ });
21
+ // eslint-disable-next-line no-underscore-dangle
22
+ const session = navigationStack._session;
23
+ session.start(parseInputLocation('/base/path'));
24
+ expect(navigationStack.current().pathname).to.equal('/path');
25
+ navigationStack.stop();
26
+ });
27
+
28
+ it('should not strip `basePath` from `location.pathname` when it does not contain the `basePath`', () => {
29
+ const navigationStack = new NavigationStack(InMemoryEnvironment, {
30
+ basePath: '/base',
31
+ });
32
+ // eslint-disable-next-line no-underscore-dangle
33
+ const session = navigationStack._session;
34
+ session.start(parseInputLocation('/path'));
35
+ expect(navigationStack.current().pathname).to.equal('/path');
36
+ navigationStack.stop();
37
+ });
38
+
39
+ it('should not strip `basePath` (with a trailing slash) from `location.pathname` when it does not contain the `basePath`', () => {
40
+ const navigationStack = new NavigationStack(InMemoryEnvironment, {
41
+ basePath: '/base/',
42
+ });
43
+ // eslint-disable-next-line no-underscore-dangle
44
+ const session = navigationStack._session;
45
+ session.start(parseInputLocation('/path'));
46
+ expect(navigationStack.current().pathname).to.equal('/path');
47
+ navigationStack.stop();
48
+ });
49
+
50
+ it('should not modify `location.pathname` when no `basePath` was specified', () => {
51
+ const navigationStack = new NavigationStack(InMemoryEnvironment);
52
+ // eslint-disable-next-line no-underscore-dangle
53
+ const session = navigationStack._session;
54
+ session.start(parseInputLocation('/path'));
55
+ expect(navigationStack.current().pathname).to.equal('/path');
56
+ navigationStack.stop();
57
+ });
58
+
59
+ it('should not modify `location.pathname` when `basePath: "/"` was specified', () => {
60
+ const navigationStack = new NavigationStack(InMemoryEnvironment, {
61
+ basePath: '/',
62
+ });
63
+ // eslint-disable-next-line no-underscore-dangle
64
+ const session = navigationStack._session;
65
+ session.start(parseInputLocation('/path'));
66
+ expect(navigationStack.current().pathname).to.equal('/path');
67
+ navigationStack.stop();
68
+ });
69
+ });