navigation-stack 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/README.md +20 -12
- package/lib/cjs/NavigationStack.js +17 -2
- package/lib/cjs/debug.js +12 -0
- package/lib/cjs/getLocationFromInternalLocation.js +2 -2
- package/lib/cjs/navigationBlockers.js +3 -0
- package/lib/cjs/scroll-position/ScrollPositionAutoSaver.js +13 -2
- package/lib/cjs/scroll-position/ScrollPositionRestoration.js +29 -5
- package/lib/cjs/scroll-position/ScrollPositionSaver.js +8 -2
- package/lib/cjs/session/Session.js +6 -0
- package/lib/cjs/session/navigation/operation/operations.js +4 -4
- package/lib/esm/NavigationStack.js +17 -2
- package/lib/esm/debug.js +7 -0
- package/lib/esm/getLocationFromInternalLocation.js +2 -2
- package/lib/esm/navigationBlockers.js +3 -0
- package/lib/esm/scroll-position/ScrollPositionAutoSaver.js +13 -2
- package/lib/esm/scroll-position/ScrollPositionRestoration.js +29 -5
- package/lib/esm/scroll-position/ScrollPositionSaver.js +8 -2
- package/lib/esm/session/Session.js +6 -0
- package/lib/esm/session/navigation/operation/operations.js +4 -4
- package/lib/index.d.ts +10 -9
- package/lib/scroll-position/index.d.ts +3 -3
- package/package.json +1 -1
- package/src/NavigationStack.js +18 -2
- package/src/debug.js +8 -0
- package/src/getLocationFromInternalLocation.js +2 -2
- package/src/navigationBlockers.js +3 -0
- package/src/scroll-position/ScrollPositionAutoSaver.js +19 -2
- package/src/scroll-position/ScrollPositionRestoration.js +92 -47
- package/src/scroll-position/ScrollPositionSaver.js +21 -1
- package/src/session/Session.js +22 -0
- package/src/session/navigation/operation/operations.js +4 -4
- package/test/NavigationStack.test.js +14 -14
- package/test/middlewareTestUtil.js +1 -1
- package/test/redux/locationReducer.test.js +1 -1
- package/test/redux/middleware/createNonProgrammaticNavigationBlockerMiddleware.test.js +5 -5
- package/test/redux/middleware/createProgrammaticNavigationBlockerMiddleware.test.js +2 -2
- package/test/redux/middleware/navigationOperationMiddleware.test.js +2 -2
- package/test/scroll-position/ScrollPositionRestoration.test.js +73 -56
- package/test/scroll-position/addScrollableContainer.js +5 -2
- package/test/scroll-position/{addScrollableContainerWithHyperlink.js → addScrollableContainerWithAnchors.js} +8 -2
- package/test/scroll-position/createApp.js +20 -0
- package/test/scroll-position/withScrollableContainerAtIndexPageWithDisabledAutomaticScrollPositionRestoration.js +72 -0
- package/test/session/InMemorySession.test.js +28 -28
- package/test/session/ServerSession.test.js +1 -1
- package/test/session/WebBrowserSession.test.js +17 -17
- package/types/index.d.ts +10 -9
- package/types/scroll-position/index.d.ts +3 -3
- package/test/scroll-position/withScrollableContainerAtIndexPage.js +0 -62
|
@@ -2,12 +2,12 @@ import { offset, scrollLeft, scrollTop } from 'dom-helpers';
|
|
|
2
2
|
import sinon from 'sinon';
|
|
3
3
|
|
|
4
4
|
import addScrollableContainer from './addScrollableContainer';
|
|
5
|
-
import
|
|
5
|
+
import addScrollableContainerWithAnchors from './addScrollableContainerWithAnchors';
|
|
6
6
|
import createApp from './createApp';
|
|
7
7
|
import delay from './delay';
|
|
8
8
|
import { setEventListener, triggerEvent } from './mockPageLifecycle';
|
|
9
9
|
import runApp from './runApp';
|
|
10
|
-
import
|
|
10
|
+
import withScrollableContainerAtIndexPageWithDisabledAutomaticScrollPositionRestoration from './withScrollableContainerAtIndexPageWithDisabledAutomaticScrollPositionRestoration';
|
|
11
11
|
import PageLifecycle from '../../src/session/lifecycle/page-lifecycle/PageLifecycleInstance';
|
|
12
12
|
|
|
13
13
|
describe('ScrollPositionRestoration', () => {
|
|
@@ -59,13 +59,14 @@ describe('ScrollPositionRestoration', () => {
|
|
|
59
59
|
|
|
60
60
|
describe('default behavior', () => {
|
|
61
61
|
it('should emulate browser scroll behavior', (done) => {
|
|
62
|
-
const app =
|
|
62
|
+
const app = addScrollableContainerWithAnchors(createApp());
|
|
63
63
|
const child1 = document.getElementById('child1');
|
|
64
64
|
const child2 = document.getElementById('child2-id');
|
|
65
65
|
|
|
66
66
|
unlisten = runApp(app, [
|
|
67
67
|
() => {
|
|
68
|
-
// This will be ignored
|
|
68
|
+
// This scroll will be ignored (overwritten by a subsequent scroll),
|
|
69
|
+
// but it will test the "throttle scroll events" code.
|
|
69
70
|
scrollTop(window, 10000);
|
|
70
71
|
|
|
71
72
|
setTimeout(() => {
|
|
@@ -113,7 +114,7 @@ describe('ScrollPositionRestoration', () => {
|
|
|
113
114
|
configurable: true,
|
|
114
115
|
});
|
|
115
116
|
|
|
116
|
-
const app =
|
|
117
|
+
const app = addScrollableContainerWithAnchors(createApp());
|
|
117
118
|
|
|
118
119
|
unlisten = runApp(app, [
|
|
119
120
|
() => {
|
|
@@ -130,7 +131,7 @@ describe('ScrollPositionRestoration', () => {
|
|
|
130
131
|
|
|
131
132
|
describe('custom behavior', () => {
|
|
132
133
|
it('should allow scroll suppression', (done) => {
|
|
133
|
-
const app =
|
|
134
|
+
const app = addScrollableContainerWithAnchors(
|
|
134
135
|
createApp({
|
|
135
136
|
shouldUpdatePageScrollPositionForLocation: (
|
|
136
137
|
location,
|
|
@@ -167,7 +168,7 @@ describe('ScrollPositionRestoration', () => {
|
|
|
167
168
|
});
|
|
168
169
|
|
|
169
170
|
it('should ignore scroll events when `disableSavingScrollPosition()` is used', (done) => {
|
|
170
|
-
const app =
|
|
171
|
+
const app = addScrollableContainerWithAnchors(createApp());
|
|
171
172
|
|
|
172
173
|
unlisten = runApp(app, [
|
|
173
174
|
() => {
|
|
@@ -205,7 +206,7 @@ describe('ScrollPositionRestoration', () => {
|
|
|
205
206
|
});
|
|
206
207
|
|
|
207
208
|
it('should allow custom position', (done) => {
|
|
208
|
-
const app =
|
|
209
|
+
const app = addScrollableContainerWithAnchors(
|
|
209
210
|
createApp({
|
|
210
211
|
getPageScrollPositionForLocation: () => [10, 20],
|
|
211
212
|
}),
|
|
@@ -230,47 +231,59 @@ describe('ScrollPositionRestoration', () => {
|
|
|
230
231
|
]);
|
|
231
232
|
});
|
|
232
233
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
234
|
+
// This test case was disabled because `ScrollPositionRestoration` doesn't save
|
|
235
|
+
// scroll position on page load. It only saves scroll position on actual scroll events.
|
|
236
|
+
// If there were no scroll events, the scroll position doesn't get saved.
|
|
237
|
+
// The rationale is that when there were no scroll events, the scroll position
|
|
238
|
+
// is gonna be either a default one or a custom one specified by passing a custom
|
|
239
|
+
// `getPageScrollPositionForLocation()` function. In the latter case, the custom
|
|
240
|
+
// `getPageScrollPositionForLocation()` function is responsible to return a correct scroll position
|
|
241
|
+
// every time it gets called rather than just return a correct scroll position once,
|
|
242
|
+
// save it immediately and then restore it when returning to the page.
|
|
243
|
+
//
|
|
244
|
+
// it('should save scroll position even if no scroll events are dispatched', (done) => {
|
|
245
|
+
// let customInitialPageScrollPosition;
|
|
246
|
+
//
|
|
247
|
+
// const app = addScrollableContainerWithAnchors(
|
|
248
|
+
// createApp({
|
|
249
|
+
// // eslint-disable-next-line no-unused-vars
|
|
250
|
+
// getPageScrollPositionForLocation(location, prevLocation) {
|
|
251
|
+
// // Only when navigated via `.goTo()`. Ignore `.goBack()` navigation.
|
|
252
|
+
// if (prevLocation && location.index > prevLocation.index) {
|
|
253
|
+
// return [10, 20];
|
|
254
|
+
// }
|
|
255
|
+
// return undefined;
|
|
256
|
+
// },
|
|
257
|
+
// }),
|
|
258
|
+
// );
|
|
259
|
+
//
|
|
260
|
+
// unlisten = runApp(app, [
|
|
261
|
+
// () => {
|
|
262
|
+
// app.goTo('/detail');
|
|
263
|
+
// },
|
|
264
|
+
// () => {
|
|
265
|
+
// app.goTo('/');
|
|
266
|
+
// },
|
|
267
|
+
// () => {
|
|
268
|
+
// app.goTo('/detail');
|
|
269
|
+
// },
|
|
270
|
+
// () => {
|
|
271
|
+
// if (customInitialPageScrollPosition === 123) {
|
|
272
|
+
// customInitialPageScrollPosition = 456;
|
|
273
|
+
// }
|
|
274
|
+
// app.goBack();
|
|
275
|
+
// },
|
|
276
|
+
// () => {
|
|
277
|
+
// // Here, it said "expected 9.966666221618652 to equal 10".
|
|
278
|
+
// // Using `.to.be.closeTo()` here instead of `.to.equal()` to work around this browser issue.
|
|
279
|
+
// expect(scrollLeft(window)).to.be.closeTo(10, 0.5);
|
|
280
|
+
// // Here, it said "expected 19.933332443237305 to equal 20".
|
|
281
|
+
// // Using `.to.be.closeTo()` here instead of `.to.equal()` to work around this browser issue.
|
|
282
|
+
// expect(scrollTop(window)).to.be.closeTo(20, 0.5);
|
|
283
|
+
// done();
|
|
284
|
+
// },
|
|
285
|
+
// ]);
|
|
286
|
+
// });
|
|
274
287
|
});
|
|
275
288
|
|
|
276
289
|
describe('scrollable container', () => {
|
|
@@ -308,12 +321,13 @@ describe('ScrollPositionRestoration', () => {
|
|
|
308
321
|
]);
|
|
309
322
|
});
|
|
310
323
|
|
|
311
|
-
it('should restore scroll
|
|
312
|
-
const { container, ...app } =
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
324
|
+
it('should automatically restore a previously-saved scroll position when adding a scrollable container', (done) => {
|
|
325
|
+
const { container, ...app } =
|
|
326
|
+
withScrollableContainerAtIndexPageWithDisabledAutomaticScrollPositionRestoration(
|
|
327
|
+
createApp({
|
|
328
|
+
shouldUpdatePageScrollPositionForLocation: () => false,
|
|
329
|
+
}),
|
|
330
|
+
);
|
|
317
331
|
|
|
318
332
|
unlisten = runApp(app, [
|
|
319
333
|
() => {
|
|
@@ -325,6 +339,8 @@ describe('ScrollPositionRestoration', () => {
|
|
|
325
339
|
() => {
|
|
326
340
|
expect(container.scrollHeight).to.equal(100);
|
|
327
341
|
expect(scrollTop(container)).to.equal(0);
|
|
342
|
+
// This `scrollTop()` won't trigger a "scroll" event
|
|
343
|
+
// because the container height is only `100` so it's not scrollable.
|
|
328
344
|
scrollTop(container, 5000);
|
|
329
345
|
delay(() => {
|
|
330
346
|
app.goBack();
|
|
@@ -357,6 +373,7 @@ describe('ScrollPositionRestoration', () => {
|
|
|
357
373
|
|
|
358
374
|
const app2 = addScrollableContainer(
|
|
359
375
|
createApp({
|
|
376
|
+
// Restore the data of the session of `app1`.
|
|
360
377
|
sessionKey: app1.getSessionKey(),
|
|
361
378
|
shouldUpdatePageScrollPositionForLocation: () => false,
|
|
362
379
|
}),
|
|
@@ -377,7 +394,7 @@ describe('ScrollPositionRestoration', () => {
|
|
|
377
394
|
|
|
378
395
|
it('should ignore scroll events when `disableSavingScrollPosition` is used', (done) => {
|
|
379
396
|
const app = addScrollableContainer(
|
|
380
|
-
|
|
397
|
+
addScrollableContainerWithAnchors(createApp()),
|
|
381
398
|
);
|
|
382
399
|
|
|
383
400
|
unlisten = runApp(app, [
|
|
@@ -16,14 +16,17 @@ export default function addScrollableContainer(app) {
|
|
|
16
16
|
// Add the scrollable container to the website.
|
|
17
17
|
document.body.appendChild(container);
|
|
18
18
|
|
|
19
|
-
// This function will only be called once, so no need to guard.
|
|
20
19
|
function listen(listener) {
|
|
21
20
|
const unlisten = app.listen(listener);
|
|
22
21
|
|
|
23
|
-
app.registerScrollableContainer(
|
|
22
|
+
const unregisterScrollableContainer = app.registerScrollableContainer(
|
|
23
|
+
'container',
|
|
24
|
+
container,
|
|
25
|
+
);
|
|
24
26
|
|
|
25
27
|
return () => {
|
|
26
28
|
unlisten();
|
|
29
|
+
unregisterScrollableContainer();
|
|
27
30
|
document.body.removeChild(container);
|
|
28
31
|
};
|
|
29
32
|
}
|
|
@@ -7,27 +7,33 @@
|
|
|
7
7
|
// * When the current location is "/", the scrollable container size is 20000x20000.
|
|
8
8
|
// * At any other location, the scrollable container size is 10000x10000.
|
|
9
9
|
//
|
|
10
|
-
export default function
|
|
10
|
+
export default function addScrollableContainerWithAnchors(app) {
|
|
11
11
|
const container = document.createElement('div');
|
|
12
12
|
document.body.appendChild(container);
|
|
13
13
|
|
|
14
14
|
const child1 = document.createElement('div');
|
|
15
|
+
// In HTML, an "anchor" is matched either by `id` attribute value
|
|
16
|
+
// or by `name` attribute value.
|
|
15
17
|
child1.id = 'child1';
|
|
16
18
|
child1.style.height = '100px';
|
|
17
19
|
container.appendChild(child1);
|
|
18
20
|
|
|
19
21
|
const child2 = document.createElement('a');
|
|
20
22
|
child2.id = 'child2-id';
|
|
23
|
+
// In HTML, an "anchor" is matched either by `id` attribute value
|
|
24
|
+
// or by `name` attribute value.
|
|
25
|
+
// Here, it tests the correctness of scrolling to an anchor called "child2".
|
|
26
|
+
// The `id` attribute value is different so that it doesn't interfere.
|
|
21
27
|
child2.name = 'child2';
|
|
22
28
|
child2.style.height = '100px';
|
|
23
29
|
child2.appendChild(document.createTextNode('link'));
|
|
24
30
|
container.appendChild(child2);
|
|
25
31
|
|
|
26
|
-
// This function will only be called once, so no need to guard.
|
|
27
32
|
function listen(listener) {
|
|
28
33
|
const unlisten = app.listen((location) => {
|
|
29
34
|
listener(location);
|
|
30
35
|
|
|
36
|
+
// Scrollable container has different height on different pages.
|
|
31
37
|
if (location.pathname === '/') {
|
|
32
38
|
container.style.height = '20000px';
|
|
33
39
|
container.style.width = '20000px';
|
|
@@ -10,6 +10,7 @@ export default function createApp({
|
|
|
10
10
|
} = {}) {
|
|
11
11
|
let currentLocation = null;
|
|
12
12
|
|
|
13
|
+
let locationRenderedListeners = [];
|
|
13
14
|
let listeners = [];
|
|
14
15
|
let scrollPositionRestoration = null;
|
|
15
16
|
let navigationStack = null;
|
|
@@ -24,6 +25,22 @@ export default function createApp({
|
|
|
24
25
|
});
|
|
25
26
|
|
|
26
27
|
scrollPositionRestoration.locationRendered(location);
|
|
28
|
+
|
|
29
|
+
for (const locationRenderedListener of locationRenderedListeners) {
|
|
30
|
+
locationRenderedListener(location);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Adds a "location rendered" listener.
|
|
35
|
+
function whenRenderedLocation(listener) {
|
|
36
|
+
locationRenderedListeners.push(listener);
|
|
37
|
+
|
|
38
|
+
// Returns a "remove listener" function.
|
|
39
|
+
return () => {
|
|
40
|
+
locationRenderedListeners = locationRenderedListeners.filter(
|
|
41
|
+
(_) => _ !== listener,
|
|
42
|
+
);
|
|
43
|
+
};
|
|
27
44
|
}
|
|
28
45
|
|
|
29
46
|
// Removes a "location change" event listener.
|
|
@@ -77,6 +94,8 @@ export default function createApp({
|
|
|
77
94
|
return scrollPositionRestoration.addScrollableContainer(key, element, {
|
|
78
95
|
_shouldUpdateScrollPositionForLocation:
|
|
79
96
|
options && options.shouldUpdateScrollPositionForLocation,
|
|
97
|
+
_getScrollPositionForLocation:
|
|
98
|
+
options && options.getScrollPositionForLocation,
|
|
80
99
|
});
|
|
81
100
|
}
|
|
82
101
|
|
|
@@ -108,5 +127,6 @@ export default function createApp({
|
|
|
108
127
|
disableSavingScrollPosition,
|
|
109
128
|
enableSavingScrollPosition,
|
|
110
129
|
getSessionKey: () => session.key,
|
|
130
|
+
whenRenderedLocation,
|
|
111
131
|
};
|
|
112
132
|
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
// Adds a 100x100 scrollable container on the website.
|
|
2
|
+
//
|
|
3
|
+
// * When the current location is "/", it renders a large amount of content
|
|
4
|
+
// (20000x20000 to be specific) inside the container, making it scrollable.
|
|
5
|
+
//
|
|
6
|
+
// * At any other location, it doesn't render anything in the container.
|
|
7
|
+
//
|
|
8
|
+
export default function withScrollableContainerAtIndexPageWithDisabledAutomaticScrollPositionRestoration(
|
|
9
|
+
app,
|
|
10
|
+
) {
|
|
11
|
+
const container = document.createElement('div');
|
|
12
|
+
container.style.height = '100px';
|
|
13
|
+
container.style.width = '100px';
|
|
14
|
+
container.style.overflow = 'hidden';
|
|
15
|
+
document.body.appendChild(container);
|
|
16
|
+
|
|
17
|
+
let scrollableContainerContentElement;
|
|
18
|
+
let unregisterScrollableContainer;
|
|
19
|
+
|
|
20
|
+
function listen(listener) {
|
|
21
|
+
function shouldUpdateScrollableContainerScrollPositionForLocation(
|
|
22
|
+
location,
|
|
23
|
+
prevLocation,
|
|
24
|
+
) {
|
|
25
|
+
// Disable the automatic scroll position restoration on "back" navigation
|
|
26
|
+
// to check that it automatically restores scroll position when calling
|
|
27
|
+
// `ScrollPositionRestoration.addScrollableContainer()` method.
|
|
28
|
+
if (prevLocation && location.index < prevLocation.index) {
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
return true;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
app.whenRenderedLocation((location) => {
|
|
35
|
+
if (location.pathname === '/') {
|
|
36
|
+
unregisterScrollableContainer = app.registerScrollableContainer(
|
|
37
|
+
'container',
|
|
38
|
+
container,
|
|
39
|
+
{
|
|
40
|
+
shouldUpdateScrollPositionForLocation:
|
|
41
|
+
shouldUpdateScrollableContainerScrollPositionForLocation,
|
|
42
|
+
},
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
const unlisten = app.listen((location) => {
|
|
48
|
+
listener(location);
|
|
49
|
+
|
|
50
|
+
if (location.pathname === '/') {
|
|
51
|
+
scrollableContainerContentElement = document.createElement('div');
|
|
52
|
+
scrollableContainerContentElement.style.height = '20000px';
|
|
53
|
+
scrollableContainerContentElement.style.width = '20000px';
|
|
54
|
+
container.appendChild(scrollableContainerContentElement);
|
|
55
|
+
} else {
|
|
56
|
+
unregisterScrollableContainer();
|
|
57
|
+
container.removeChild(scrollableContainerContentElement);
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
return () => {
|
|
62
|
+
unlisten();
|
|
63
|
+
document.body.removeChild(container);
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return {
|
|
68
|
+
...app,
|
|
69
|
+
container,
|
|
70
|
+
listen,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
@@ -13,7 +13,7 @@ describe('InMemorySession', () => {
|
|
|
13
13
|
session.start(parseInputLocation('/initial?bar=baz#qux'));
|
|
14
14
|
|
|
15
15
|
expect(location).to.deep.include({
|
|
16
|
-
operation: '
|
|
16
|
+
operation: 'init',
|
|
17
17
|
pathname: '/initial',
|
|
18
18
|
search: '?bar=baz',
|
|
19
19
|
query: {
|
|
@@ -42,21 +42,21 @@ describe('InMemorySession', () => {
|
|
|
42
42
|
|
|
43
43
|
expect(listener).to.have.been.calledOnce();
|
|
44
44
|
expect(listener.firstCall.args[0]).to.deep.include({
|
|
45
|
-
operation: '
|
|
45
|
+
operation: 'init',
|
|
46
46
|
pathname: '/initial',
|
|
47
47
|
index: 0,
|
|
48
48
|
delta: 0,
|
|
49
49
|
});
|
|
50
50
|
listener.resetHistory();
|
|
51
51
|
|
|
52
|
-
session.navigate('
|
|
52
|
+
session.navigate('push', {
|
|
53
53
|
pathname: '/new',
|
|
54
54
|
});
|
|
55
55
|
|
|
56
56
|
const newLocation = location;
|
|
57
57
|
|
|
58
58
|
expect(newLocation).to.deep.include({
|
|
59
|
-
operation: '
|
|
59
|
+
operation: 'push',
|
|
60
60
|
pathname: '/new',
|
|
61
61
|
index: 1,
|
|
62
62
|
delta: 1,
|
|
@@ -65,17 +65,17 @@ describe('InMemorySession', () => {
|
|
|
65
65
|
|
|
66
66
|
expect(listener).to.have.been.calledOnce();
|
|
67
67
|
expect(listener.firstCall.args[0]).to.deep.include({
|
|
68
|
-
operation: '
|
|
68
|
+
operation: 'push',
|
|
69
69
|
pathname: '/new',
|
|
70
70
|
index: 1,
|
|
71
71
|
delta: 1,
|
|
72
72
|
});
|
|
73
73
|
listener.resetHistory();
|
|
74
74
|
|
|
75
|
-
session.navigate('
|
|
75
|
+
session.navigate('push', { pathname: '/new-2' });
|
|
76
76
|
|
|
77
77
|
expect(location).to.include({
|
|
78
|
-
operation: '
|
|
78
|
+
operation: 'push',
|
|
79
79
|
pathname: '/new-2',
|
|
80
80
|
index: 2,
|
|
81
81
|
delta: 1,
|
|
@@ -83,17 +83,17 @@ describe('InMemorySession', () => {
|
|
|
83
83
|
|
|
84
84
|
expect(listener).to.have.been.calledOnce();
|
|
85
85
|
expect(listener.firstCall.args[0]).to.deep.include({
|
|
86
|
-
operation: '
|
|
86
|
+
operation: 'push',
|
|
87
87
|
pathname: '/new-2',
|
|
88
88
|
index: 2,
|
|
89
89
|
delta: 1,
|
|
90
90
|
});
|
|
91
91
|
listener.resetHistory();
|
|
92
92
|
|
|
93
|
-
session.navigate('
|
|
93
|
+
session.navigate('replace', { pathname: '/new-3' });
|
|
94
94
|
|
|
95
95
|
expect(location).to.include({
|
|
96
|
-
operation: '
|
|
96
|
+
operation: 'replace',
|
|
97
97
|
pathname: '/new-3',
|
|
98
98
|
index: 2,
|
|
99
99
|
delta: 0,
|
|
@@ -101,7 +101,7 @@ describe('InMemorySession', () => {
|
|
|
101
101
|
|
|
102
102
|
expect(listener).to.have.been.calledOnce();
|
|
103
103
|
expect(listener.firstCall.args[0]).to.deep.include({
|
|
104
|
-
operation: '
|
|
104
|
+
operation: 'replace',
|
|
105
105
|
pathname: '/new-3',
|
|
106
106
|
index: 2,
|
|
107
107
|
delta: 0,
|
|
@@ -112,7 +112,7 @@ describe('InMemorySession', () => {
|
|
|
112
112
|
|
|
113
113
|
expect(listener).to.have.been.calledOnce();
|
|
114
114
|
expect(listener.firstCall.args[0]).to.deep.include({
|
|
115
|
-
operation: '
|
|
115
|
+
operation: 'shift',
|
|
116
116
|
pathname: '/new',
|
|
117
117
|
key: newLocation.key,
|
|
118
118
|
index: 1,
|
|
@@ -126,8 +126,8 @@ describe('InMemorySession', () => {
|
|
|
126
126
|
it('should support subscribing and unsubscribing', () => {
|
|
127
127
|
const session = new InMemorySession();
|
|
128
128
|
session.start(parseInputLocation('/initial'));
|
|
129
|
-
session.navigate('
|
|
130
|
-
session.navigate('
|
|
129
|
+
session.navigate('push', { pathname: '/new' });
|
|
130
|
+
session.navigate('push', { pathname: '/new-2' });
|
|
131
131
|
|
|
132
132
|
const listener = sinon.spy();
|
|
133
133
|
const unsubscribe = session.subscribe(listener);
|
|
@@ -136,7 +136,7 @@ describe('InMemorySession', () => {
|
|
|
136
136
|
|
|
137
137
|
expect(listener).to.have.been.calledOnce();
|
|
138
138
|
expect(listener.firstCall.args[0]).to.include({
|
|
139
|
-
operation: '
|
|
139
|
+
operation: 'shift',
|
|
140
140
|
pathname: '/new',
|
|
141
141
|
});
|
|
142
142
|
listener.resetHistory();
|
|
@@ -153,8 +153,8 @@ describe('InMemorySession', () => {
|
|
|
153
153
|
it('should respect stack bounds', () => {
|
|
154
154
|
const session = new InMemorySession();
|
|
155
155
|
session.start(parseInputLocation('/initial'));
|
|
156
|
-
session.navigate('
|
|
157
|
-
session.navigate('
|
|
156
|
+
session.navigate('push', { pathname: '/new' });
|
|
157
|
+
session.navigate('push', { pathname: '/new-2' });
|
|
158
158
|
|
|
159
159
|
const listener = sinon.spy();
|
|
160
160
|
session.subscribe(listener);
|
|
@@ -169,7 +169,7 @@ describe('InMemorySession', () => {
|
|
|
169
169
|
|
|
170
170
|
expect(listener).to.have.been.calledOnce();
|
|
171
171
|
expect(listener.firstCall.args[0]).to.include({
|
|
172
|
-
operation: '
|
|
172
|
+
operation: 'shift',
|
|
173
173
|
pathname: '/initial',
|
|
174
174
|
delta: -2,
|
|
175
175
|
});
|
|
@@ -189,7 +189,7 @@ describe('InMemorySession', () => {
|
|
|
189
189
|
|
|
190
190
|
expect(listener).to.have.been.calledOnce();
|
|
191
191
|
expect(listener.firstCall.args[0]).to.include({
|
|
192
|
-
operation: '
|
|
192
|
+
operation: 'shift',
|
|
193
193
|
pathname: '/new-2',
|
|
194
194
|
delta: 2,
|
|
195
195
|
});
|
|
@@ -207,10 +207,10 @@ describe('InMemorySession', () => {
|
|
|
207
207
|
it('should not reset forward entries on replace', () => {
|
|
208
208
|
const session = new InMemorySession();
|
|
209
209
|
session.start(parseInputLocation('/initial'));
|
|
210
|
-
session.navigate('
|
|
211
|
-
session.navigate('
|
|
210
|
+
session.navigate('push', { pathname: '/new' });
|
|
211
|
+
session.navigate('push', { pathname: '/new-2' });
|
|
212
212
|
session.shift(-2);
|
|
213
|
-
session.navigate('
|
|
213
|
+
session.navigate('replace', { pathname: '/new-3' });
|
|
214
214
|
|
|
215
215
|
const listener = sinon.spy();
|
|
216
216
|
session.subscribe(listener);
|
|
@@ -219,7 +219,7 @@ describe('InMemorySession', () => {
|
|
|
219
219
|
|
|
220
220
|
expect(listener).to.have.been.calledOnce();
|
|
221
221
|
expect(listener.firstCall.args[0]).to.include({
|
|
222
|
-
operation: '
|
|
222
|
+
operation: 'shift',
|
|
223
223
|
pathname: '/new',
|
|
224
224
|
delta: 1,
|
|
225
225
|
});
|
|
@@ -231,10 +231,10 @@ describe('InMemorySession', () => {
|
|
|
231
231
|
const session = new InMemorySession();
|
|
232
232
|
|
|
233
233
|
session.start(parseInputLocation('/initial'));
|
|
234
|
-
session.navigate('
|
|
235
|
-
session.navigate('
|
|
234
|
+
session.navigate('push', { pathname: '/new' });
|
|
235
|
+
session.navigate('push', { pathname: '/new-2' });
|
|
236
236
|
session.shift(-2);
|
|
237
|
-
session.navigate('
|
|
237
|
+
session.navigate('push', { pathname: '/new-3' });
|
|
238
238
|
|
|
239
239
|
const listener = sinon.spy();
|
|
240
240
|
session.subscribe(listener);
|
|
@@ -297,8 +297,8 @@ describe('InMemorySession', () => {
|
|
|
297
297
|
// pathname: '/initial',
|
|
298
298
|
// });
|
|
299
299
|
//
|
|
300
|
-
// session1._navigation.navigate('
|
|
301
|
-
// session1._navigation.navigate('
|
|
300
|
+
// session1._navigation.navigate('push', { pathname: '/new' });
|
|
301
|
+
// session1._navigation.navigate('push', { pathname: '/new-2' });
|
|
302
302
|
// session1._navigation.shift(-1);
|
|
303
303
|
//
|
|
304
304
|
// const session2 = new InMemorySession({ save, load });
|