import { Router } from './index.js'; describe('router.href builds urls', () => { const router = Router([ { name: 'home', path: '/', enter: (route, router) => {}, exit: (route, newRoute, router) => {} }, { name: 'article', path: '/article/:id', vars: { id: /[a-f0-9]{6,40}/ }, enter: (route, router) => {}, exit: (route, newRoute, router) => {} } ]); it('at the root', () => { expect(router.href('home')).toEqual('#/'); }); it('with variables', () => { expect(router.href('article', { id: 156234 })).toEqual('#/article/156234'); }); describe('but throws an error', () => { it("if the route doesn't exist", () => { expect(() => { router.href('artcle', { id: 156 }); }).toThrowError(Error, 'Invalid route artcle.'); }); it("if the vars don't match", () => { expect(() => { router.href('article', { id: 156 }); }).toThrowError(Error, 'Invalid value for route /article/:id var id: 156.'); }); }); }); describe('router on start', () => { let routeSpecArray, router; beforeEach(() => { routeSpecArray = [ { name: 'home', path: '/' }, { name: 'article', path: '/article/:id', vars: { id: /[a-f0-9]{6,40}/ } } ]; router = Router(routeSpecArray); spyOn(router, 'goto').and.callThrough(); }); afterEach(() => { if (router) { router.stop(); router = null; location.hash = ''; } }); it('listens to hashchanges', (done) => { expect(router.goto).toHaveBeenCalledTimes(0); router.start('home'); expect(router.goto).toHaveBeenCalledWith('home'); location.hash = '/article/123456'; setTimeout(() => { expect(router.goto).toHaveBeenCalledWith('#/article/123456', null, true); done(); }, 20); }); it('does not double-start', () => { expect(router.goto).toHaveBeenCalledTimes(0); router.start('home'); expect(router.goto).toHaveBeenCalledWith('home'); router.start('gravy'); expect(router.goto).toHaveBeenCalledTimes(1); }); it('throws errors for invalid routes', () => { expect(() => router.start('artcle')).toThrowError(Error, 'Invalid route artcle.'); }); }); describe('router on hashchange event', () => { let reEnterHooks, routeSpecArray, router; beforeEach(() => { reEnterHooks = { home: () => {}, article: () => {} }; routeSpecArray = [ { name: 'home', path: '/', enter: (route, router) => reEnterHooks.home, exit: (route, newRoute, router) => {} }, { name: 'article', path: '/article/:id', vars: { id: /[a-f0-9]{6,40}/ }, enter: (route, router) => reEnterHooks.article, exit: (route, newRoute, router) => {} } ]; spyOn(routeSpecArray[0], 'enter').and.callThrough(); spyOn(routeSpecArray[1], 'enter').and.callThrough(); router = Router(routeSpecArray); spyOn(router, 'goto').and.callThrough(); }); afterEach(() => { if (router) { router.stop(); router = null; location.hash = ''; } }); it('calls handlers for the new location', (done) => { expect(router.goto).toHaveBeenCalledTimes(0); router.start('home'); location.hash = '/article/123456'; expect(routeSpecArray[1].enter).toHaveBeenCalledTimes(0); setTimeout(() => { expect(router.goto).toHaveBeenCalledWith('#/article/123456', null, true); expect(routeSpecArray[1].enter).toHaveBeenCalledTimes(1); done(); }, 20); }); it('reverts if the change does not have a valid path.', (done) => { function stepA() { expect(router.goto).toHaveBeenCalledWith('home'); expect(router.current().name).toEqual('home'); expect(routeSpecArray[1].enter).toHaveBeenCalledTimes(0); location.hash = '/invalid'; expect(location.hash).toEqual('#/invalid'); setTimeout(stepB, 20); } function stepB() { expect(router.goto).toHaveBeenCalledWith('#/invalid', null, true); expect(location.hash).toEqual('#/'); done(); } router.start('home'); setTimeout(stepA, 20); }); }); describe('router.goto()', () => { let reEnterHooks, routeSpecArray, router; describe('goes to routes', () => { beforeEach(() => { reEnterHooks = { home: () => {}, article: () => {} }; routeSpecArray = [ { name: 'home', path: '/', enter: (route, router) => reEnterHooks.home, exit: (route, newRoute, router) => {} }, { name: 'article', path: '/article/:id', vars: { id: /[a-f0-9]{6,40}/ }, enter: (route, router) => reEnterHooks.article, exit: (route, newRoute, router) => {} } ]; spyOn(routeSpecArray[0], 'enter').and.callThrough(); spyOn(routeSpecArray[1], 'enter').and.callThrough(); router = Router(routeSpecArray); }); it('with vars', done => { router.goto('article', { id: 156234 }).then(() => { expect(routeSpecArray[1].enter).toHaveBeenCalled(); const current = router.current(); expect(current.name).toEqual('article'); expect(current.path).toEqual('#/article/156234'); done(); }); }); it('at the root', done => { router.goto('home').then(() => { expect(routeSpecArray[0].enter).toHaveBeenCalled(); const current = router.current(); expect(current.name).toEqual('home'); expect(current.path).toEqual('#/'); done(); }); }); it('with a url', done => { router.goto('#/article/156233').then(() => { expect(routeSpecArray[1].enter).toHaveBeenCalled(); const current = router.current(); expect(current.name).toEqual('article'); expect(current.path).toEqual('#/article/156233'); done(); }); }); }); describe('reEnters routes', () => { it('when a route is navigated with merely different vars with a reenter hook', done => { reEnterHooks = { home: () => {}, article: () => {} }; routeSpecArray = [ { name: 'home', path: '/', enter: (route, router) => reEnterHooks.home, exit: (route, newRoute, router) => {} }, { name: 'article', path: '/article/:id', vars: { id: /[a-f0-9]{6,40}/ }, enter: (route, router) => reEnterHooks.article, exit: (route, newRoute, router) => {} } ]; spyOn(routeSpecArray[0], 'enter').and.callThrough(); spyOn(routeSpecArray[1], 'enter').and.callThrough(); spyOn(routeSpecArray[1], 'exit').and.callThrough(); spyOn(reEnterHooks, 'article').and.callThrough(); router = Router(routeSpecArray); router .goto('article', { id: 156234 }) .then(() => { expect(routeSpecArray[1].enter).toHaveBeenCalled(); expect(routeSpecArray[1].exit).toHaveBeenCalledTimes(0); return router.goto('article', { id: 151234 }); }) .then(() => { expect(reEnterHooks.article).toHaveBeenCalled(); expect(routeSpecArray[1].exit).toHaveBeenCalledTimes(0); return router.goto('home'); }) .then(() => { expect(routeSpecArray[0].enter).toHaveBeenCalled(); expect(routeSpecArray[1].exit).toHaveBeenCalled(); }) .then(done); }); it('when a route is navigated with merely different vars without a reenter hook', done => { routeSpecArray = [ { name: 'home', path: '/', enter: (route, router) => {}, exit: (route, newRoute, router) => {} }, { name: 'article', path: '/article/:id', vars: { id: /[a-f0-9]{6,40}/ }, enter: (route, router) => {}, exit: (route, newRoute, router) => {} } ]; spyOn(routeSpecArray[0], 'enter').and.callThrough(); spyOn(routeSpecArray[1], 'enter').and.callThrough(); spyOn(routeSpecArray[1], 'exit').and.callThrough(); router = Router(routeSpecArray); router .goto('article', { id: 156234 }) .then(() => { expect(routeSpecArray[1].enter).toHaveBeenCalled(); expect(routeSpecArray[1].exit).toHaveBeenCalledTimes(0); return router.goto('article', { id: 151234 }); }) .then(() => { expect(routeSpecArray[1].enter).toHaveBeenCalledTimes(2); expect(routeSpecArray[1].exit).toHaveBeenCalledTimes(1); return router.goto('home'); }) .then(() => { expect(routeSpecArray[0].enter).toHaveBeenCalled(); expect(routeSpecArray[1].exit).toHaveBeenCalled(); }) .then(done); }); }); });