navigation-stack 0.1.3 → 0.3.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/.github/workflows/main.yml +39 -39
- package/README.md +128 -27
- package/lib/cjs/{LocationStateStorage.js → LocationDataStorage.js} +5 -5
- package/lib/cjs/addBeforeLocationChangeListener.js +7 -0
- package/lib/cjs/beforeLocationChangeListeners.js +51 -0
- package/lib/cjs/createMiddlewares.js +21 -17
- package/lib/cjs/index.js +9 -9
- package/lib/cjs/middleware/createBasePathMiddleware.js +2 -2
- package/lib/cjs/middleware/createBeforeLocationChangeListenerMiddleware.js +39 -0
- package/lib/cjs/middleware/{createEnvironmentMiddleware.js → createLocationMiddleware.js} +12 -14
- package/lib/cjs/middleware/createNavigationBlockerMiddleware.js +62 -29
- package/lib/cjs/middleware/createTransformLocationMiddleware.js +2 -2
- package/lib/cjs/navigationBlockers.js +55 -47
- package/lib/cjs/normalizeInputLocation.js +1 -0
- package/lib/cjs/parseLocationUrl.js +2 -0
- package/lib/cjs/parseQueryFromSearch.js +1 -1
- package/lib/cjs/session/BrowserSession.js +235 -0
- package/lib/cjs/session/MemorySession.js +223 -0
- package/lib/cjs/{environment/ServerEnvironment.js → session/ServerSession.js} +28 -16
- package/lib/esm/{LocationStateStorage.js → LocationDataStorage.js} +4 -4
- package/lib/esm/addBeforeLocationChangeListener.js +2 -0
- package/lib/esm/beforeLocationChangeListeners.js +44 -0
- package/lib/esm/createMiddlewares.js +21 -17
- package/lib/esm/index.js +4 -4
- package/lib/esm/middleware/createBasePathMiddleware.js +2 -2
- package/lib/esm/middleware/createBeforeLocationChangeListenerMiddleware.js +34 -0
- package/lib/esm/middleware/{createEnvironmentMiddleware.js → createLocationMiddleware.js} +11 -13
- package/lib/esm/middleware/createNavigationBlockerMiddleware.js +62 -29
- package/lib/esm/middleware/createTransformLocationMiddleware.js +2 -2
- package/lib/esm/navigationBlockers.js +55 -47
- package/lib/esm/normalizeInputLocation.js +1 -0
- package/lib/esm/parseLocationUrl.js +2 -0
- package/lib/esm/parseQueryFromSearch.js +1 -1
- package/lib/esm/session/BrowserSession.js +229 -0
- package/lib/esm/session/MemorySession.js +217 -0
- package/lib/esm/{environment/ServerEnvironment.js → session/ServerSession.js} +27 -15
- package/lib/index.d.ts +66 -65
- package/package.json +2 -7
- package/src/{LocationStateStorage.js → LocationDataStorage.js} +4 -4
- package/src/addBeforeLocationChangeListener.js +2 -0
- package/src/beforeLocationChangeListeners.js +54 -0
- package/src/createMiddlewares.js +21 -17
- package/src/index.js +4 -4
- package/src/middleware/createBasePathMiddleware.js +2 -2
- package/src/middleware/createBeforeLocationChangeListenerMiddleware.js +40 -0
- package/src/middleware/{createEnvironmentMiddleware.js → createLocationMiddleware.js} +12 -14
- package/src/middleware/createNavigationBlockerMiddleware.js +68 -28
- package/src/middleware/createTransformLocationMiddleware.js +2 -2
- package/src/navigationBlockers.js +68 -49
- package/src/normalizeInputLocation.js +1 -0
- package/src/parseLocationUrl.js +2 -0
- package/src/parseQueryFromSearch.js +1 -1
- package/src/session/BrowserSession.js +235 -0
- package/src/session/MemorySession.js +219 -0
- package/src/{environment/ServerEnvironment.js → session/ServerSession.js} +28 -15
- package/test/{LocationStateStorage.test.js → LocationDataStorage.test.js} +6 -6
- package/test/createMiddlewares.test.js +2 -2
- package/test/helpers.js +1 -1
- package/test/index.test.js +3 -3
- package/test/middleware/createBasePathMiddleware.test.js +7 -7
- package/test/middleware/createBeforeLocationChangeListenerMiddleware.test.js +141 -0
- package/test/middleware/createNavigationBlockerMiddleware.test.js +100 -101
- package/test/middleware/createTransformLocationMiddleware.test.js +1 -1
- package/test/normalizeInputLocation.test.js +3 -0
- package/test/parseLocationUrl.test.js +2 -0
- package/test/{environment/BrowserEnvironment.test.js → session/BrowserSession.test.js} +38 -21
- package/test/session/MemorySession.test.js +244 -0
- package/test/session/ServerSession.test.js +23 -0
- package/types/index.d.ts +66 -65
- package/lib/cjs/environment/BrowserEnvironment.js +0 -111
- package/lib/cjs/environment/MemoryEnvironment.js +0 -150
- package/lib/esm/environment/BrowserEnvironment.js +0 -104
- package/lib/esm/environment/MemoryEnvironment.js +0 -143
- package/src/environment/BrowserEnvironment.js +0 -109
- package/src/environment/MemoryEnvironment.js +0 -151
- package/test/environment/MemoryEnvironment.test.js +0 -218
- package/test/environment/ServerEnvironment.test.js +0 -23
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { applyMiddleware, createStore } from 'redux';
|
|
2
|
+
|
|
3
|
+
import Actions from '../../src/Actions';
|
|
4
|
+
import addBeforeLocationChangeListenerOriginal from '../../src/addBeforeLocationChangeListener';
|
|
5
|
+
import createMiddlewares from '../../src/createMiddlewares';
|
|
6
|
+
import locationReducer from '../../src/locationReducer';
|
|
7
|
+
import MemorySession from '../../src/session/MemorySession';
|
|
8
|
+
import { shouldWarn } from '../helpers';
|
|
9
|
+
|
|
10
|
+
describe('createBeforeLocationChangeListenerMiddleware', () => {
|
|
11
|
+
const sandbox = sinon.createSandbox();
|
|
12
|
+
|
|
13
|
+
let session;
|
|
14
|
+
let store;
|
|
15
|
+
|
|
16
|
+
function addBeforeLocationChangeListener(listener) {
|
|
17
|
+
return addBeforeLocationChangeListenerOriginal(session, listener);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
beforeEach(() => {
|
|
21
|
+
session = new MemorySession('/foo');
|
|
22
|
+
|
|
23
|
+
store = createStore(
|
|
24
|
+
locationReducer,
|
|
25
|
+
applyMiddleware(...createMiddlewares(session)),
|
|
26
|
+
);
|
|
27
|
+
store.dispatch(Actions.init());
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
afterEach(() => {
|
|
31
|
+
store.dispatch(Actions.dispose());
|
|
32
|
+
|
|
33
|
+
sandbox.restore();
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
describe('PUSH navigations', () => {
|
|
37
|
+
it('should listen to upcoming navigation', () => {
|
|
38
|
+
const listener1 = sinon.stub();
|
|
39
|
+
const listener2 = sinon.stub();
|
|
40
|
+
|
|
41
|
+
addBeforeLocationChangeListener(listener1);
|
|
42
|
+
addBeforeLocationChangeListener(listener2);
|
|
43
|
+
|
|
44
|
+
store.dispatch(Actions.push('/bar'));
|
|
45
|
+
expect(store.getState().pathname).to.equal('/bar');
|
|
46
|
+
|
|
47
|
+
expect(listener1).to.have.been.calledOnce();
|
|
48
|
+
|
|
49
|
+
expect(listener1.firstCall.args[0]).to.include({
|
|
50
|
+
action: 'PUSH',
|
|
51
|
+
pathname: '/bar',
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
expect(listener2).to.have.been.calledOnce();
|
|
55
|
+
|
|
56
|
+
expect(listener2.firstCall.args[0]).to.include({
|
|
57
|
+
action: 'PUSH',
|
|
58
|
+
pathname: '/bar',
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it('should warn on and ignore listeners that throw', () => {
|
|
63
|
+
shouldWarn(
|
|
64
|
+
'Ignoring before location change listener `syncListener` that failed with `Error: foo`.',
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
const syncListener = () => {
|
|
68
|
+
throw new Error('foo');
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
addBeforeLocationChangeListener(syncListener);
|
|
72
|
+
|
|
73
|
+
store.dispatch(Actions.push('/bar'));
|
|
74
|
+
expect(store.getState().pathname).to.equal('/bar');
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('should allow removing listeners', () => {
|
|
78
|
+
const listener = sinon.stub();
|
|
79
|
+
|
|
80
|
+
const removeNavigationListener =
|
|
81
|
+
addBeforeLocationChangeListener(listener);
|
|
82
|
+
|
|
83
|
+
store.dispatch(Actions.push('/bar-1'));
|
|
84
|
+
expect(store.getState().pathname).to.equal('/bar-1');
|
|
85
|
+
|
|
86
|
+
expect(listener).to.have.been.calledOnce();
|
|
87
|
+
|
|
88
|
+
removeNavigationListener();
|
|
89
|
+
|
|
90
|
+
store.dispatch(Actions.push('/bar-2'));
|
|
91
|
+
expect(store.getState().pathname).to.equal('/bar-2');
|
|
92
|
+
|
|
93
|
+
expect(listener).to.have.been.calledOnce();
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
describe('SHIFT navigations', () => {
|
|
98
|
+
beforeEach(() => {
|
|
99
|
+
store.dispatch(Actions.push('/bar'));
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it('should listen to upcoming navigation', () => {
|
|
103
|
+
const listener = sinon.stub();
|
|
104
|
+
addBeforeLocationChangeListener(listener);
|
|
105
|
+
|
|
106
|
+
store.dispatch(Actions.shift(-1));
|
|
107
|
+
expect(store.getState().pathname).to.equal('/foo');
|
|
108
|
+
|
|
109
|
+
expect(listener).to.have.been.calledOnce();
|
|
110
|
+
|
|
111
|
+
expect(listener.firstCall.args[0]).to.include({
|
|
112
|
+
action: 'SHIFT',
|
|
113
|
+
pathname: '/foo',
|
|
114
|
+
delta: -1,
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
describe('INIT', () => {
|
|
120
|
+
it('should ignore to the initial load', () => {
|
|
121
|
+
// Get rid of the old store. We'll replace it with a new one.
|
|
122
|
+
store.dispatch(Actions.dispose());
|
|
123
|
+
|
|
124
|
+
store = createStore(
|
|
125
|
+
locationReducer,
|
|
126
|
+
applyMiddleware(...createMiddlewares(new MemorySession('/foo'))),
|
|
127
|
+
);
|
|
128
|
+
|
|
129
|
+
const listener = sinon.stub();
|
|
130
|
+
addBeforeLocationChangeListener(listener);
|
|
131
|
+
|
|
132
|
+
expect(listener).to.not.have.been.called();
|
|
133
|
+
|
|
134
|
+
expect(store.getState()).to.be.undefined();
|
|
135
|
+
store.dispatch(Actions.init());
|
|
136
|
+
expect(store.getState().pathname).to.equal('/foo');
|
|
137
|
+
|
|
138
|
+
expect(listener).to.not.have.been.called();
|
|
139
|
+
});
|
|
140
|
+
});
|
|
141
|
+
});
|
|
@@ -5,31 +5,31 @@ import { applyMiddleware, createStore } from 'redux';
|
|
|
5
5
|
import Actions from '../../src/Actions';
|
|
6
6
|
import addNavigationBlockerOriginal from '../../src/addNavigationBlocker';
|
|
7
7
|
import createMiddlewares from '../../src/createMiddlewares';
|
|
8
|
-
import MemoryEnvironment from '../../src/environment/MemoryEnvironment';
|
|
9
8
|
import locationReducer from '../../src/locationReducer';
|
|
9
|
+
import MemorySession from '../../src/session/MemorySession';
|
|
10
10
|
import { shouldWarn } from '../helpers';
|
|
11
11
|
|
|
12
12
|
describe('createNavigationBlockerMiddleware', () => {
|
|
13
13
|
const sandbox = sinon.createSandbox();
|
|
14
14
|
|
|
15
|
-
let
|
|
15
|
+
let session;
|
|
16
16
|
let store;
|
|
17
17
|
|
|
18
|
-
function addNavigationBlocker(
|
|
19
|
-
return addNavigationBlockerOriginal(
|
|
18
|
+
function addNavigationBlocker(blocker) {
|
|
19
|
+
return addNavigationBlockerOriginal(session, blocker);
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
beforeEach(() => {
|
|
23
|
-
|
|
23
|
+
session = new MemorySession('/foo');
|
|
24
24
|
|
|
25
25
|
store = createStore(
|
|
26
26
|
locationReducer,
|
|
27
|
-
applyMiddleware(...createMiddlewares(
|
|
27
|
+
applyMiddleware(...createMiddlewares(session)),
|
|
28
28
|
);
|
|
29
29
|
store.dispatch(Actions.init());
|
|
30
30
|
|
|
31
|
-
sinon.spy(
|
|
32
|
-
sinon.spy(
|
|
31
|
+
sinon.spy(session, 'addBeforeDestroyListener');
|
|
32
|
+
// sinon.spy(session, '_removeBeforeDestroyListener');
|
|
33
33
|
});
|
|
34
34
|
|
|
35
35
|
afterEach(() => {
|
|
@@ -40,13 +40,15 @@ describe('createNavigationBlockerMiddleware', () => {
|
|
|
40
40
|
|
|
41
41
|
describe('PUSH navigations', () => {
|
|
42
42
|
it('should block navigation when blocker returns `true`', () => {
|
|
43
|
-
const
|
|
44
|
-
addNavigationBlocker(
|
|
43
|
+
const blocker = sinon.stub().returns(true);
|
|
44
|
+
addNavigationBlocker(blocker);
|
|
45
45
|
|
|
46
46
|
store.dispatch(Actions.push('/bar'));
|
|
47
47
|
expect(store.getState().pathname).to.equal('/foo');
|
|
48
48
|
|
|
49
|
-
expect(
|
|
49
|
+
expect(blocker).to.have.been.calledOnce();
|
|
50
|
+
|
|
51
|
+
expect(blocker.firstCall.args[0]).to.include({
|
|
50
52
|
action: 'PUSH',
|
|
51
53
|
pathname: '/bar',
|
|
52
54
|
});
|
|
@@ -60,43 +62,43 @@ describe('createNavigationBlockerMiddleware', () => {
|
|
|
60
62
|
});
|
|
61
63
|
|
|
62
64
|
it("should fall through when first blocker doesn't return `true`", () => {
|
|
63
|
-
const
|
|
64
|
-
const
|
|
65
|
+
const blocker1 = sinon.stub().returns(undefined);
|
|
66
|
+
const blocker2 = sinon.stub().returns(true);
|
|
65
67
|
|
|
66
|
-
addNavigationBlocker(
|
|
67
|
-
addNavigationBlocker(
|
|
68
|
+
addNavigationBlocker(blocker1);
|
|
69
|
+
addNavigationBlocker(blocker2);
|
|
68
70
|
|
|
69
71
|
store.dispatch(Actions.push('/bar'));
|
|
70
72
|
expect(store.getState().pathname).to.equal('/foo');
|
|
71
73
|
|
|
72
|
-
expect(
|
|
73
|
-
expect(
|
|
74
|
+
expect(blocker1).to.have.been.calledOnce();
|
|
75
|
+
expect(blocker2).to.have.been.calledOnce();
|
|
74
76
|
});
|
|
75
77
|
|
|
76
78
|
it('should not fall through when first blocker returns `true`', () => {
|
|
77
|
-
const
|
|
78
|
-
const
|
|
79
|
+
const blocker1 = sinon.stub().returns(true);
|
|
80
|
+
const blocker2 = sinon.stub().returns(undefined);
|
|
79
81
|
|
|
80
|
-
addNavigationBlocker(
|
|
81
|
-
addNavigationBlocker(
|
|
82
|
+
addNavigationBlocker(blocker1);
|
|
83
|
+
addNavigationBlocker(blocker2);
|
|
82
84
|
|
|
83
85
|
store.dispatch(Actions.push('/bar'));
|
|
84
86
|
expect(store.getState().pathname).to.equal('/foo');
|
|
85
87
|
|
|
86
|
-
expect(
|
|
87
|
-
expect(
|
|
88
|
+
expect(blocker1).to.have.been.calledOnce();
|
|
89
|
+
expect(blocker2).not.to.have.been.called();
|
|
88
90
|
});
|
|
89
91
|
|
|
90
|
-
it('should warn on and ignore
|
|
92
|
+
it('should warn on and ignore blockers that throw', () => {
|
|
91
93
|
shouldWarn(
|
|
92
|
-
'Ignoring navigation blocker `
|
|
94
|
+
'Ignoring navigation blocker `syncblocker` that failed with `Error: foo`.',
|
|
93
95
|
);
|
|
94
96
|
|
|
95
|
-
const
|
|
97
|
+
const syncblocker = () => {
|
|
96
98
|
throw new Error('foo');
|
|
97
99
|
};
|
|
98
100
|
|
|
99
|
-
addNavigationBlocker(
|
|
101
|
+
addNavigationBlocker(syncblocker);
|
|
100
102
|
|
|
101
103
|
store.dispatch(Actions.push('/bar'));
|
|
102
104
|
expect(store.getState().pathname).to.equal('/bar');
|
|
@@ -177,15 +179,15 @@ describe('createNavigationBlockerMiddleware', () => {
|
|
|
177
179
|
|
|
178
180
|
it('should warn on and ignore async blockers that throw an error', async () => {
|
|
179
181
|
shouldWarn(
|
|
180
|
-
'Ignoring navigation blocker `
|
|
182
|
+
'Ignoring navigation blocker `asyncblocker` that failed with `Error: foo`.',
|
|
181
183
|
);
|
|
182
184
|
|
|
183
185
|
// eslint-disable-next-line require-await
|
|
184
|
-
const
|
|
186
|
+
const asyncblocker = async () => {
|
|
185
187
|
throw new Error('foo');
|
|
186
188
|
};
|
|
187
189
|
|
|
188
|
-
addNavigationBlocker(
|
|
190
|
+
addNavigationBlocker(asyncblocker);
|
|
189
191
|
|
|
190
192
|
store.dispatch(Actions.push('/bar'));
|
|
191
193
|
expect(store.getState().pathname).to.equal('/foo');
|
|
@@ -195,33 +197,33 @@ describe('createNavigationBlockerMiddleware', () => {
|
|
|
195
197
|
expect(store.getState().pathname).to.equal('/bar');
|
|
196
198
|
});
|
|
197
199
|
|
|
198
|
-
it('should allow removing
|
|
199
|
-
const
|
|
200
|
+
it('should allow removing blockers', () => {
|
|
201
|
+
const removeNavigationBlocker = addNavigationBlocker(() => true);
|
|
200
202
|
|
|
201
203
|
store.dispatch(Actions.push('/bar'));
|
|
202
204
|
expect(store.getState().pathname).to.equal('/foo');
|
|
203
205
|
|
|
204
|
-
|
|
206
|
+
removeNavigationBlocker();
|
|
205
207
|
|
|
206
208
|
store.dispatch(Actions.push('/bar'));
|
|
207
209
|
expect(store.getState().pathname).to.equal('/bar');
|
|
208
210
|
});
|
|
209
211
|
});
|
|
210
212
|
|
|
211
|
-
describe('
|
|
213
|
+
describe('SHIFT navigations', () => {
|
|
212
214
|
beforeEach(() => {
|
|
213
215
|
store.dispatch(Actions.push('/bar'));
|
|
214
216
|
});
|
|
215
217
|
|
|
216
218
|
it('should allow navigation when blocker returns `undefined`', () => {
|
|
217
|
-
const
|
|
218
|
-
addNavigationBlocker(
|
|
219
|
+
const blocker = sinon.stub().returns(undefined);
|
|
220
|
+
addNavigationBlocker(blocker);
|
|
219
221
|
|
|
220
222
|
store.dispatch(Actions.shift(-1));
|
|
221
223
|
expect(store.getState().pathname).to.equal('/foo');
|
|
222
224
|
|
|
223
|
-
expect(
|
|
224
|
-
action: '
|
|
225
|
+
expect(blocker.firstCall.args[0]).to.include({
|
|
226
|
+
action: 'SHIFT',
|
|
225
227
|
pathname: '/foo',
|
|
226
228
|
delta: -1,
|
|
227
229
|
});
|
|
@@ -279,7 +281,7 @@ describe('createNavigationBlockerMiddleware', () => {
|
|
|
279
281
|
|
|
280
282
|
store = createStore(
|
|
281
283
|
locationReducer,
|
|
282
|
-
applyMiddleware(...createMiddlewares(new
|
|
284
|
+
applyMiddleware(...createMiddlewares(new MemorySession('/foo'))),
|
|
283
285
|
);
|
|
284
286
|
addNavigationBlocker(() => true);
|
|
285
287
|
|
|
@@ -290,16 +292,16 @@ describe('createNavigationBlockerMiddleware', () => {
|
|
|
290
292
|
|
|
291
293
|
it('should support async rewinding', async () => {
|
|
292
294
|
// eslint-disable-next-line no-underscore-dangle
|
|
293
|
-
const
|
|
295
|
+
const blocker = session.navigation._subscriptionListener;
|
|
294
296
|
|
|
295
|
-
let
|
|
297
|
+
let sessionDeferred;
|
|
296
298
|
|
|
297
299
|
// eslint-disable-next-line no-underscore-dangle
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
await
|
|
300
|
+
session.navigation._subscriptionListener = async (location) => {
|
|
301
|
+
sessionDeferred = pDefer();
|
|
302
|
+
await sessionDeferred.promise;
|
|
301
303
|
|
|
302
|
-
|
|
304
|
+
blocker(location);
|
|
303
305
|
};
|
|
304
306
|
|
|
305
307
|
const deferred = pDefer();
|
|
@@ -307,25 +309,25 @@ describe('createNavigationBlockerMiddleware', () => {
|
|
|
307
309
|
|
|
308
310
|
store.dispatch(Actions.shift(-1));
|
|
309
311
|
|
|
310
|
-
//
|
|
311
|
-
expect(
|
|
312
|
+
// session shifted, update to store blocked.
|
|
313
|
+
expect(session.navigation.init().pathname).to.equal('/foo');
|
|
312
314
|
expect(store.getState().pathname).to.equal('/bar');
|
|
313
315
|
|
|
314
|
-
|
|
316
|
+
sessionDeferred.resolve();
|
|
315
317
|
await delay(10);
|
|
316
318
|
|
|
317
|
-
//
|
|
318
|
-
expect(
|
|
319
|
+
// session rewinded.
|
|
320
|
+
expect(session.navigation.init().pathname).to.equal('/bar');
|
|
319
321
|
expect(store.getState().pathname).to.equal('/bar');
|
|
320
322
|
|
|
321
323
|
deferred.resolve(undefined);
|
|
322
324
|
await delay(10);
|
|
323
325
|
|
|
324
|
-
|
|
326
|
+
sessionDeferred.resolve();
|
|
325
327
|
await delay(10);
|
|
326
328
|
|
|
327
|
-
//
|
|
328
|
-
expect(
|
|
329
|
+
// session re-shifted (the rewind was undone), update to store delayed.
|
|
330
|
+
expect(session.navigation.init().pathname).to.equal('/foo');
|
|
329
331
|
expect(store.getState().pathname).to.equal('/foo');
|
|
330
332
|
});
|
|
331
333
|
|
|
@@ -333,18 +335,18 @@ describe('createNavigationBlockerMiddleware', () => {
|
|
|
333
335
|
const deferred = pDefer();
|
|
334
336
|
addNavigationBlocker(() => deferred.promise);
|
|
335
337
|
|
|
336
|
-
// Update location with a `
|
|
338
|
+
// Update location with a `SHIFT` action.
|
|
337
339
|
/* eslint-disable no-underscore-dangle */
|
|
338
|
-
|
|
339
|
-
|
|
340
|
+
session.navigation._index = 0;
|
|
341
|
+
session.navigation._subscriptionListener(session.navigation.init(null));
|
|
340
342
|
/* eslint-enable no-underscore-dangle */
|
|
341
343
|
|
|
342
344
|
deferred.resolve(undefined);
|
|
343
345
|
await delay(10);
|
|
344
346
|
|
|
345
|
-
// Without delta, we can't rewind on the
|
|
347
|
+
// Without delta, we can't rewind on the session,
|
|
346
348
|
// so navigation is allowed without calling any blockers.
|
|
347
|
-
expect(
|
|
349
|
+
expect(session.navigation.init().pathname).to.equal('/foo');
|
|
348
350
|
expect(store.getState().pathname).to.equal('/foo');
|
|
349
351
|
});
|
|
350
352
|
|
|
@@ -352,20 +354,20 @@ describe('createNavigationBlockerMiddleware', () => {
|
|
|
352
354
|
// const deferred = pDefer();
|
|
353
355
|
// addNavigationBlocker(() => deferred.promise);
|
|
354
356
|
//
|
|
355
|
-
// // Update location with a `
|
|
357
|
+
// // Update location with a `SHIFT` action.
|
|
356
358
|
// /* eslint-disable no-underscore-dangle */
|
|
357
|
-
//
|
|
358
|
-
//
|
|
359
|
+
// session.navigation._index = 0;
|
|
360
|
+
// session.navigation._subscriptionListener(session.navigation.init(null));
|
|
359
361
|
// /* eslint-enable no-underscore-dangle */
|
|
360
362
|
//
|
|
361
|
-
// // Without delta, we can't rewind on the
|
|
362
|
-
// expect(
|
|
363
|
+
// // Without delta, we can't rewind on the session.
|
|
364
|
+
// expect(session.navigation.init().pathname).to.equal('/foo');
|
|
363
365
|
// expect(store.getState().pathname).to.equal('/bar');
|
|
364
366
|
//
|
|
365
367
|
// deferred.resolve(undefined);
|
|
366
368
|
// await delay(10);
|
|
367
369
|
//
|
|
368
|
-
// expect(
|
|
370
|
+
// expect(session.navigation.init().pathname).to.equal('/foo');
|
|
369
371
|
// expect(store.getState().pathname).to.equal('/foo');
|
|
370
372
|
// });
|
|
371
373
|
|
|
@@ -374,18 +376,18 @@ describe('createNavigationBlockerMiddleware', () => {
|
|
|
374
376
|
// addNavigationBlocker(() => deferred.promise);
|
|
375
377
|
//
|
|
376
378
|
// /* eslint-disable no-underscore-dangle */
|
|
377
|
-
//
|
|
378
|
-
//
|
|
379
|
+
// session.navigation._index = 0;
|
|
380
|
+
// session.navigation._subscriptionListener(session.navigation.init(null));
|
|
379
381
|
// /* eslint-enable no-underscore-dangle */
|
|
380
382
|
//
|
|
381
|
-
// expect(
|
|
383
|
+
// expect(session.navigation.init().pathname).to.equal('/foo');
|
|
382
384
|
// expect(store.getState().pathname).to.equal('/bar');
|
|
383
385
|
//
|
|
384
386
|
// deferred.resolve(true);
|
|
385
387
|
// await delay(10);
|
|
386
388
|
//
|
|
387
389
|
// // These are out-of-sync now, but it's the best we can do.
|
|
388
|
-
// expect(
|
|
390
|
+
// expect(session.navigation.init().pathname).to.equal('/foo');
|
|
389
391
|
// expect(store.getState().pathname).to.equal('/bar');
|
|
390
392
|
// });
|
|
391
393
|
});
|
|
@@ -400,73 +402,70 @@ describe('createNavigationBlockerMiddleware', () => {
|
|
|
400
402
|
|
|
401
403
|
store = createStore(
|
|
402
404
|
() => null,
|
|
403
|
-
applyMiddleware(...createMiddlewares(
|
|
405
|
+
applyMiddleware(...createMiddlewares(session)),
|
|
404
406
|
);
|
|
405
407
|
|
|
406
408
|
store.dispatch(Actions.init());
|
|
407
409
|
});
|
|
408
410
|
|
|
409
|
-
it('should manage event
|
|
410
|
-
expect(
|
|
411
|
+
it('should manage event blocker', () => {
|
|
412
|
+
expect(session.addBeforeDestroyListener).not.to.have.been.called();
|
|
411
413
|
// expect(window.addEventListener).not.to.have.been.called();
|
|
412
414
|
|
|
413
|
-
const
|
|
415
|
+
const removeNavigationBlocker1 = addNavigationBlocker(() => null, {
|
|
414
416
|
beforeUnload: true,
|
|
415
417
|
});
|
|
416
|
-
expect(
|
|
418
|
+
expect(session.addBeforeDestroyListener).to.have.been.calledOnce();
|
|
417
419
|
// expect(window.addEventListener)
|
|
418
420
|
// .to.have.been.calledOnce()
|
|
419
421
|
// .and.to.have.been.called.with('beforeunload');
|
|
420
422
|
|
|
421
|
-
const
|
|
423
|
+
const removeNavigationBlocker2 = addNavigationBlocker(() => null, {
|
|
422
424
|
beforeUnload: true,
|
|
423
425
|
});
|
|
424
|
-
expect(
|
|
426
|
+
expect(session.addBeforeDestroyListener).to.have.been.calledOnce();
|
|
425
427
|
// expect(window.addEventListener)
|
|
426
428
|
// .to.have.been.calledOnce()
|
|
427
429
|
// .and.to.have.been.called.with('beforeunload');
|
|
428
430
|
|
|
429
|
-
|
|
431
|
+
const removeBeforeDestroyListener = sinon.stub();
|
|
432
|
+
// eslint-disable-next-line no-underscore-dangle
|
|
433
|
+
session._removeBeforeDestroyListener = removeBeforeDestroyListener;
|
|
434
|
+
|
|
435
|
+
removeNavigationBlocker1();
|
|
430
436
|
// expect(window.removeEventListener).not.to.have.been.called();
|
|
431
|
-
expect(
|
|
432
|
-
// eslint-disable-next-line no-underscore-dangle
|
|
433
|
-
environment._removeBeforeDestroyListener,
|
|
434
|
-
).not.to.have.been.called();
|
|
437
|
+
expect(removeBeforeDestroyListener).not.to.have.been.called();
|
|
435
438
|
|
|
436
|
-
|
|
439
|
+
removeNavigationBlocker2();
|
|
437
440
|
// expect(window.removeEventListener)
|
|
438
441
|
// .to.have.been.calledOnce()
|
|
439
442
|
// .and.to.have.been.called.with('beforeunload');
|
|
440
|
-
expect(
|
|
441
|
-
// eslint-disable-next-line no-underscore-dangle
|
|
442
|
-
environment._removeBeforeDestroyListener,
|
|
443
|
-
).to.have.been.calledOnce();
|
|
443
|
+
expect(removeBeforeDestroyListener).to.have.been.calledOnce();
|
|
444
444
|
});
|
|
445
445
|
|
|
446
|
-
it('should remove event
|
|
446
|
+
it('should remove event blocker on dispose', () => {
|
|
447
447
|
addNavigationBlocker(() => null, { beforeUnload: true });
|
|
448
|
+
|
|
449
|
+
const removeBeforeDestroyListener = sinon.stub();
|
|
450
|
+
// eslint-disable-next-line no-underscore-dangle
|
|
451
|
+
session._removeBeforeDestroyListener = removeBeforeDestroyListener;
|
|
452
|
+
|
|
448
453
|
// expect(window.removeEventListener).not.to.have.been.called();
|
|
449
|
-
expect(
|
|
450
|
-
// eslint-disable-next-line no-underscore-dangle
|
|
451
|
-
environment._removeBeforeDestroyListener,
|
|
452
|
-
).not.to.have.been.called();
|
|
454
|
+
expect(removeBeforeDestroyListener).not.to.have.been.called();
|
|
453
455
|
|
|
454
456
|
store.dispatch(Actions.dispose());
|
|
455
457
|
// expect(window.removeEventListener)
|
|
456
458
|
// .to.have.been.calledOnce()
|
|
457
459
|
// .and.to.have.been.called.with('beforeunload');
|
|
458
|
-
expect(
|
|
459
|
-
// eslint-disable-next-line no-underscore-dangle
|
|
460
|
-
environment._removeBeforeDestroyListener,
|
|
461
|
-
).to.have.been.calledOnce();
|
|
460
|
+
expect(removeBeforeDestroyListener).to.have.been.calledOnce();
|
|
462
461
|
});
|
|
463
462
|
|
|
464
|
-
it('should not add
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
});
|
|
463
|
+
// it('should not add a global "before destroy" listener when no `beforeDestroy` blocker has been added', () => {
|
|
464
|
+
// const removeNavigationBlocker = addNavigationBlocker(() => null);
|
|
465
|
+
// expect(window.addEventListener).not.to.have.been.called();
|
|
466
|
+
//
|
|
467
|
+
// removeNavigationBlocker();
|
|
468
|
+
// expect(window.removeEventListener).not.to.have.been.called();
|
|
469
|
+
// });
|
|
471
470
|
});
|
|
472
471
|
});
|
|
@@ -4,7 +4,7 @@ import createTransformLocationMiddleware from '../../src/middleware/createTransf
|
|
|
4
4
|
describe('createTransformLocationMiddleware', () => {
|
|
5
5
|
const middleware = createTransformLocationMiddleware({
|
|
6
6
|
transformInputLocation: (locationInput) => ({ locationInput }),
|
|
7
|
-
|
|
7
|
+
transformSubscriptionLocation: (location) => ({ location }),
|
|
8
8
|
});
|
|
9
9
|
|
|
10
10
|
const dispatch = middleware()((action) => action.payload);
|
|
@@ -26,6 +26,7 @@ describe('normalizeInputLocation', () => {
|
|
|
26
26
|
).to.eql({
|
|
27
27
|
pathname: '/new/pathname',
|
|
28
28
|
search: '',
|
|
29
|
+
query: {},
|
|
29
30
|
hash: '',
|
|
30
31
|
});
|
|
31
32
|
});
|
|
@@ -34,6 +35,7 @@ describe('normalizeInputLocation', () => {
|
|
|
34
35
|
expect(normalizeInputLocation('/foo')).to.eql({
|
|
35
36
|
pathname: '/foo',
|
|
36
37
|
search: '',
|
|
38
|
+
query: {},
|
|
37
39
|
hash: '',
|
|
38
40
|
});
|
|
39
41
|
|
|
@@ -49,6 +51,7 @@ describe('normalizeInputLocation', () => {
|
|
|
49
51
|
expect(normalizeInputLocation('/foo#qux')).to.eql({
|
|
50
52
|
pathname: '/foo',
|
|
51
53
|
search: '',
|
|
54
|
+
query: {},
|
|
52
55
|
hash: '#qux',
|
|
53
56
|
});
|
|
54
57
|
|
|
@@ -16,6 +16,7 @@ describe('parseLocationUrl', () => {
|
|
|
16
16
|
expect(parseLocationUrl('/foo?#qux')).to.deep.equal({
|
|
17
17
|
pathname: '/foo',
|
|
18
18
|
search: '?',
|
|
19
|
+
query: {},
|
|
19
20
|
hash: '#qux',
|
|
20
21
|
});
|
|
21
22
|
});
|
|
@@ -24,6 +25,7 @@ describe('parseLocationUrl', () => {
|
|
|
24
25
|
expect(parseLocationUrl('/foo')).to.deep.equal({
|
|
25
26
|
pathname: '/foo',
|
|
26
27
|
search: '',
|
|
28
|
+
query: {},
|
|
27
29
|
hash: '',
|
|
28
30
|
});
|
|
29
31
|
});
|