navigo
Version:
A simple vanilla JavaScript router with a fallback for older browsers
413 lines (334 loc) • 11.7 kB
JavaScript
/* global beforeEach, afterEach */
import Navigo from '../../src';
import { getBrowser } from '../args';
var router;
var browser = getBrowser();
describe('Given the Navigo library on the page', function () {
beforeEach(function () {
window.location.hash = '';
delete window.__NAVIGO_WINDOW_LOCATION_MOCK__;
history.pushState({}, '', '');
});
afterEach(function () {
router.destroy();
window.onpopstate = window.onhashchange = null;
});
describe('when using the hash based routing', function () {
it('should handle page routing', function () {
router = new Navigo('/', true);
let handler = sinon.spy();
router.on('home', handler);
window.location.hash = 'home';
router.resolve();
expect(handler).to.be.calledOnce;
});
it('should not removing existing hashchange handlers', function (done) {
var existingHandler = sinon.spy();
window.onhashchange = existingHandler;
router = new Navigo('/', true);
router.on('/posts', function () {});
window.location.hash = 'posts';
setTimeout(function () {
expect(existingHandler).to.be.called;
done();
}, 1);
});
it('should remove event listeners on destroy', function (done) {
router = new Navigo('/', true);
router.resolve = sinon.spy();
router.on('page', function () {});
router.destroy();
window.location.hash = 'page';
setTimeout(function () {
expect(router.resolve).to.not.be.called;
done();
}, 1);
});
});
describe('when using the history API based routing', function () {
describe('and we use the navigate method', function () {
it('should handle page routing', function (done) {
router = new Navigo('/', false);
let handler = sinon.spy();
router.on('page', handler);
router.navigate('page');
setTimeout(function () {
expect(handler).to.be.calledOnce;
done();
}, 200);
});
});
it('should handle page routing', function () {
router = new Navigo('/', false);
let handler = sinon.spy();
router.on('page', handler);
history.pushState({}, '', 'page');
router.resolve();
expect(handler).to.be.calledOnce;
});
it('should not removing existing popstate handlers', function (done) {
var existingHandler = sinon.spy();
window.onpopstate = existingHandler;
router = new Navigo('/', false);
router.on('page', function () {});
history.pushState({}, '', 'page');
history.back();
setTimeout(function () {
expect(existingHandler).to.be.called;
done();
}, 500);
});
it('should remove event listeners on destroy', function (done) {
router = new Navigo('/', false);
router.resolve = sinon.spy();
router.on('page', function () {});
router.destroy();
history.pushState({}, '', 'page');
history.back();
setTimeout(function () {
expect(router.resolve).to.not.be.called;
done();
}, 1);
});
});
describe('when using the pause and resume method', function () {
it('should NOT fire a handler', function () {
router = new Navigo('/', false);
let handler = sinon.spy();
router.pause();
router.on('page', handler);
history.pushState({}, '', 'page');
router.resolve();
router.resume();
router.resolve();
expect(handler).to.be.calledOnce;
});
});
describe('and the problem described in issue #56', function () {
it('should resolve the notFound handler', function () {
var router = new Navigo('/v1');
var notFoundHandler = sinon.spy();
var modifyHandler = sinon.spy();
var defaultHandler = sinon.spy();
router
.notFound(notFoundHandler)
.on('modify', modifyHandler)
.on('modify/:name', modifyHandler)
.on('/', defaultHandler);
router.resolve('/v1/a/b/c');
router.resolve('/v1/modify');
router.resolve('/v1/modify/test');
expect(defaultHandler).to.not.be.called;
expect(notFoundHandler).to.be.called;
expect(modifyHandler).to.be.calledTwice;
});
});
describe('and the problem described in issue #57', function () {
it('should not resolve any handler', function () {
var router = new Navigo('/', false);
var defaultHandler = sinon.spy();
var handler = sinon.spy();
var notFoundHandler = sinon.spy();
router
.on('something', handler)
.on(defaultHandler)
.notFound(notFoundHandler);
window.location.hash = 'tab1';
router.resolve();
window.location.hash = 'tab2';
router.resolve();
expect(notFoundHandler).to.be.calledOnce;
expect(defaultHandler).to.not.be.calledOnce;
expect(handler).to.not.be.called;
});
});
describe('and the problem described in issue #63', function () {
it('should keep the trailing slash at the end', function () {
var router = new Navigo();
router.navigate('/something/else/');
expect(window.location.href).to.match(/\/something\/else\//);
});
});
describe('and the problem described in issue #70', function () {
it('should resolve the notFound handler', function () {
var router = new Navigo(null, true);
var notFoundHandler = sinon.spy();
var defaultHandler = sinon.spy();
window.location.hash = 'missing';
router.notFound(notFoundHandler);
router.on(defaultHandler);
router.resolve();
expect(defaultHandler).not.to.be.calledOnce;
expect(notFoundHandler).to.be.calledOnce;
});
});
describe('and the problem described in issue #74', function () {
(browser === 'PhantomJS' ? it.skip : it)('should resolve the handler', function () {
var router = new Navigo(null, true);
var handler = sinon.spy();
window.location.hash = '/محصولات/list';
router.on('/محصولات/list', handler).resolve();
expect(handler).to.be.calledOnce;
});
});
describe('and the problem described in issue #79', function () {
it('should not resolve the handler', function (done) {
var handler = sinon.spy();
router = new Navigo(null, true);
router
.on('r1', {
as: 'r1',
uses: handler,
hooks: {
before: function (done) {
done(false);
}
}
});
router.navigate('r1');
setTimeout(() => {
expect(handler).to.not.be.calledOnce;
done();
}, 100);
});
});
describe('and the problem described in issue #82', function () {
it('should accept only / and /product/xxx urls', function () {
var router = new Navigo('/');
var productHandler = sinon.spy();
var startPageHandler = sinon.spy();
var notFoundHandler = sinon.spy();
router
.on({
'/product/:id': productHandler,
'/': startPageHandler
})
.notFound(notFoundHandler);
router.resolve('/');
router.resolve('/product/AAA');
router.resolve('/foobar');
expect(startPageHandler).to.be.calledOnce;
expect(productHandler)
.to.be.calledOnce
.and.to.be.calledWith({ id: 'AAA' });
// expect(notFoundHandler).to.be.calledOnce;
});
});
describe('and the problem described in issue #61', function () {
it('should resolve the parameter as just "foo"', function () {
var router = new Navigo('/');
var handler = sinon.spy();
router.on('/:id', handler);
router.resolve('/foo#');
expect(handler).to.be.calledOnce.and.to.be.calledWith({ id: 'foo' });
});
});
describe('and the problem described in issue #87', function () {
it('should produce a url without hash', function () {
var router = new Navigo('/', true);
var handler = sinon.spy();
router.on({
'/foo': { as: 'foo', uses: handler }
});
router.navigate(router.generate('foo'));
expect(window.location.hash).to.be.equal('#/foo');
});
});
describe('and the problem described in issue #56-2', function () {
it('should fire the notFound handler', function () {
var router = new Navigo(null, false, '#!');
var notFoundHandler = sinon.spy();
var homeHandler = sinon.spy();
var postsHandler = sinon.spy();
router
.notFound(notFoundHandler)
.on({
'/': homeHandler,
'/my-posts': postsHandler
});
router.resolve();
router.resolve('asdf');
expect(homeHandler).to.be.calledOnce;
expect(notFoundHandler).to.be.calledOnce;
});
});
describe('and the problem described in issue #89', function () {
it('should fire the notFound handler', function (ready) {
var router = new Navigo(null, true);
var search = sinon.spy();
var home = sinon.spy();
router
.on('/search', search, {
before: function (done) {
document.title = 'Search';
done();
}
})
.on('/home', home, {
before: function (done) {
document.title = 'Home';
done();
}
});
router.navigate('/search');
router.resolve();
expect(document.title).to.be.equal('Search');
router.navigate('/home');
router.resolve();
expect(document.title).to.be.equal('Home');
history.back();
setTimeout(() => {
expect(search).to.be.calledTwice;
expect(home).to.be.calledOnce;
expect(document.title).to.be.equal('Search');
ready();
}, 500);
});
});
describe('and the problem described in issue #96 where we have a query parameter', function () {
it('should pass the query parameter to the handler', function () {
var handler = sinon.spy();
router = new Navigo(null, false);
window.__NAVIGO_WINDOW_LOCATION_MOCK__ = 'http://site.com?answer=42';
router.on(handler);
router.resolve();
delete window.__NAVIGO_WINDOW_LOCATION_MOCK__;
expect(handler)
.to.be.calledOnce
.and.to.be.calledWith('answer=42');
});
});
describe('and the problem described in #120', function () {
(browser === 'PhantomJS' ? it.skip : it)('should use a custom function for fetching link\'s url', function () {
var event = new MouseEvent('click', {
'view': window,
'bubbles': true,
'cancelable': true
});
var link = document.createElement('LINK');
var body = document.querySelector('body');
link.setAttribute('id', 'myLink');
link.setAttribute('href', 'overview/getting-started');
link.setAttribute('data-custom-url', 'foo/bar');
link.setAttribute('data-navigo', 'yes');
body.appendChild(link);
router = new Navigo(null, true);
router.getLinkPath = function (link) {
return link.getAttribute('data-custom-url');
};
link.dispatchEvent(event);
expect(window.location.href.split('#')[1]).to.be.equal('foo/bar');
body.removeChild(link);
});
});
describe('and the problem described in #122', function () {
it('should set a proper root', function () {
var handler = sinon.spy();
window.__NAVIGO_WINDOW_LOCATION_MOCK__ = 'http://site.com/members?test=42';
router = new Navigo('', false);
router.on('/members', handler).resolve();
expect(handler).to.be.calledOnce;
delete window.__NAVIGO_WINDOW_LOCATION_MOCK__;
});
});
});