navigation-stack
Version:
Handles navigation in a web browser
349 lines (284 loc) • 8.99 kB
JavaScript
import parseInputLocation from '../../src/parseInputLocation';
import InMemorySession from '../../src/session/InMemorySession';
describe('InMemorySession', () => {
it('should parse the initial location', () => {
const session = new InMemorySession();
let location;
session.subscribe((newLocation) => {
location = newLocation;
});
session.start(parseInputLocation('/initial?bar=baz#qux'));
expect(location).to.deep.include({
operation: 'init',
pathname: '/initial',
search: '?bar=baz',
query: {
bar: 'baz',
},
hash: '#qux',
index: 0,
delta: 0,
});
session.stop();
});
it('should support basic navigation', () => {
const session = new InMemorySession();
let location;
session.subscribe((newLocation) => {
location = newLocation;
});
const listener = sinon.spy();
session.subscribe(listener);
session.start(parseInputLocation('/initial'));
expect(listener).to.have.been.calledOnce();
expect(listener.firstCall.args[0]).to.deep.include({
operation: 'init',
pathname: '/initial',
index: 0,
delta: 0,
});
listener.resetHistory();
session.navigate('push', {
pathname: '/new',
});
const newLocation = location;
expect(newLocation).to.deep.include({
operation: 'push',
pathname: '/new',
index: 1,
delta: 1,
});
expect(newLocation.key).not.to.be.empty();
expect(listener).to.have.been.calledOnce();
expect(listener.firstCall.args[0]).to.deep.include({
operation: 'push',
pathname: '/new',
index: 1,
delta: 1,
});
listener.resetHistory();
session.navigate('push', { pathname: '/new-2' });
expect(location).to.include({
operation: 'push',
pathname: '/new-2',
index: 2,
delta: 1,
});
expect(listener).to.have.been.calledOnce();
expect(listener.firstCall.args[0]).to.deep.include({
operation: 'push',
pathname: '/new-2',
index: 2,
delta: 1,
});
listener.resetHistory();
session.navigate('replace', { pathname: '/new-3' });
expect(location).to.include({
operation: 'replace',
pathname: '/new-3',
index: 2,
delta: 0,
});
expect(listener).to.have.been.calledOnce();
expect(listener.firstCall.args[0]).to.deep.include({
operation: 'replace',
pathname: '/new-3',
index: 2,
delta: 0,
});
listener.resetHistory();
session.shift(-1);
expect(listener).to.have.been.calledOnce();
expect(listener.firstCall.args[0]).to.deep.include({
operation: 'shift',
pathname: '/new',
key: newLocation.key,
index: 1,
delta: -1,
});
listener.resetHistory();
session.stop();
});
it('should support subscribing and unsubscribing', () => {
const session = new InMemorySession();
session.start(parseInputLocation('/initial'));
session.navigate('push', { pathname: '/new' });
session.navigate('push', { pathname: '/new-2' });
const listener = sinon.spy();
const unsubscribe = session.subscribe(listener);
session.shift(-1);
expect(listener).to.have.been.calledOnce();
expect(listener.firstCall.args[0]).to.include({
operation: 'shift',
pathname: '/new',
});
listener.resetHistory();
unsubscribe();
session.shift(-1);
expect(listener).not.to.have.been.called();
session.stop();
});
it('should respect stack bounds', () => {
const session = new InMemorySession();
session.start(parseInputLocation('/initial'));
session.navigate('push', { pathname: '/new' });
session.navigate('push', { pathname: '/new-2' });
const listener = sinon.spy();
session.subscribe(listener);
expect(() => {
session.shift(-390);
}).to.throw('out of navigation history bounds');
expect(listener).to.not.have.been.called();
session.shift(-2);
expect(listener).to.have.been.calledOnce();
expect(listener.firstCall.args[0]).to.include({
operation: 'shift',
pathname: '/initial',
delta: -2,
});
listener.resetHistory();
expect(() => {
session.shift(-1);
}).to.throw('out of navigation history bounds');
expect(listener).not.to.have.been.called();
expect(() => {
session.shift(+22);
}).to.throw('out of navigation history bounds');
session.shift(+2);
expect(listener).to.have.been.calledOnce();
expect(listener.firstCall.args[0]).to.include({
operation: 'shift',
pathname: '/new-2',
delta: 2,
});
listener.resetHistory();
expect(() => {
session.shift(+1);
}).to.throw('out of navigation history bounds');
expect(listener).not.to.have.been.called();
session.stop();
});
it('should not reset forward entries on replace', () => {
const session = new InMemorySession();
session.start(parseInputLocation('/initial'));
session.navigate('push', { pathname: '/new' });
session.navigate('push', { pathname: '/new-2' });
session.shift(-2);
session.navigate('replace', { pathname: '/new-3' });
const listener = sinon.spy();
session.subscribe(listener);
session.shift(+1);
expect(listener).to.have.been.calledOnce();
expect(listener.firstCall.args[0]).to.include({
operation: 'shift',
pathname: '/new',
delta: 1,
});
session.stop();
});
it('should reset forward entries on push', () => {
const session = new InMemorySession();
session.start(parseInputLocation('/initial'));
session.navigate('push', { pathname: '/new' });
session.navigate('push', { pathname: '/new-2' });
session.shift(-2);
session.navigate('push', { pathname: '/new-3' });
const listener = sinon.spy();
session.subscribe(listener);
expect(() => {
session.shift(+1);
}).to.throw('out of navigation history bounds');
expect(listener).not.to.have.been.called();
session.stop();
});
it('should remove all subscriptions on stop', () => {
const session = new InMemorySession();
session.start(parseInputLocation('/initial'));
// eslint-disable-next-line no-underscore-dangle
expect(session._subscription._listeners.length).to.equal(1);
const listener = sinon.spy();
session.subscribe(listener);
// eslint-disable-next-line no-underscore-dangle
expect(session._subscription._listeners.length).to.equal(2);
session.stop();
// eslint-disable-next-line no-underscore-dangle
expect(session._subscription._listeners.length).to.equal(0);
});
// describe('persistence', () => {
// // beforeEach(() => {
// // window.sessionStorage.clear();
// // });
//
// it('should support persistence', () => {
// // function save(key, data) {
// // window.sessionStorage.setItem(key, data);
// // }
// //
// // function load(key) {
// // return window.sessionStorage.getItem(key);
// // }
//
// const storage = {};
//
// const save = (key, data) => {
// storage[key] = data;
// };
//
// const load = (key) => {
// return storage[key];
// };
//
// const session1 = new InMemorySession({ save, load });
// expect(session1.start(parseInputLocation('/initial))).to.include({
// pathname: '/initial',
// });
//
// session1._navigation.navigate('push', { pathname: '/new' });
// session1._navigation.navigate('push', { pathname: '/new-2' });
// session1._navigation.shift(-1);
//
// const session2 = new InMemorySession({ save, load });
// expect(parseInputLocation(session2.start('/initial'))).to.include({
// pathname: '/new',
// });
//
// session2._navigation.shift(+1);
// expect(parseInputLocation(session2.start())).to.include({
// pathname: '/new-2',
// });
// session1.stop();
// session2.stop();
// });
//
// it('should ignore broken session storage entry', () => {
// // function save(key, data) {
// // window.sessionStorage.setItem(key, data);
// // }
// //
// // function load(key) {
// // return window.sessionStorage.getItem(key);
// // }
//
// const storage = {};
//
// const save = (key, data) => {
// storage[key] = data;
// };
//
// const load = (key) => {
// return storage[key];
// };
//
// save(
// // `stack` should have sufficient items so that the `index` wouldn't be out of bounds.
// JSON.stringify({ stack: [], index: 2 }),
// );
//
// const session = new InMemorySession({ save, load });
// expect(session.start(parseInputLocation('/initial'))).to.include({
// pathname: '/initial',
// });
// session.stop();
// });
// });
});