| <!doctype html> |
| <!-- |
| @license |
| Copyright (c) 2016 The Polymer Project Authors. All rights reserved. |
| This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt |
| The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt |
| The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt |
| Code distributed by Google as part of the polymer project is also |
| subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt |
| --> |
| <html> |
| <head> |
| <title>app-route</title> |
| |
| <script src="../../webcomponentsjs/webcomponents-lite.js"></script> |
| <script src="../../web-component-tester/browser.js"></script> |
| |
| <link rel="import" href="../../polymer/polymer.html"> |
| <link rel="import" href="../app-route.html"> |
| <link rel="import" href="./redirection.html"> |
| </head> |
| <body> |
| <test-fixture id="BasicRoute"> |
| <template> |
| <app-route pattern='/user/:username'> |
| </app-route> |
| </template> |
| </test-fixture> |
| |
| <test-fixture id="ChainedRoutes"> |
| <template is="dom-template"> |
| <app-route |
| pattern="/foo/:foo" |
| route="{{numberOneTopRoute}}" |
| data="{{fooData}}" |
| tail="{{fooRoute}}"> |
| </app-route> |
| |
| <app-route |
| pattern="/bar/:bar" |
| route="{{fooRoute}}" |
| data="{{barData}}"> |
| </app-route> |
| |
| <app-route |
| pattern="/baz/:baz" |
| route="{{fooRoute}}" |
| data="{{bazData}}"> |
| </app-route> |
| </template> |
| </test-fixture> |
| |
| <test-fixture id="Redirection"> |
| <template> |
| <redirect-app-route></redirect-app-route> |
| </template> |
| </test-fixture> |
| |
| <script> |
| 'use strict'; |
| |
| function fixtureChainedRoutes(route) { |
| var routes = fixture('ChainedRoutes', { |
| numberOneTopRoute: { |
| path: route.path || '', |
| prefix: route.prefix || '', |
| __queryParams: route.__queryParams || {} |
| } |
| }); |
| |
| return { |
| foo: routes[0], |
| bar: routes[1], |
| baz: routes[2] |
| }; |
| } |
| |
| suite('<app-route>', function () { |
| var route; |
| |
| setup(function() { |
| route = fixture('BasicRoute'); |
| |
| // This works around a bug in `dom-template` that is somehow |
| // exaserbated by the `app-route` implementation. A reduced test case |
| // is hard to come by. Track polymerelements/test-fixture#31 and remove |
| // this when that has been resolved: |
| var tmpl = document.querySelector('#ChainedRoutes').fixtureTemplates[0]; |
| tmpl._parentProps = {}; |
| }); |
| |
| test('it parses a path', function() { |
| route.route = { |
| prefix: '', |
| path: '/user/papyrus/details', |
| __queryParams: {} |
| } |
| expect(route.tail.prefix).to.be.equal('/user/papyrus'); |
| expect(route.tail.path).to.be.equal('/details'); |
| expect(route.data.username).to.be.equal('papyrus'); |
| }); |
| |
| test('it bidirectionally maps changes between tail and route', function() { |
| route.route = { |
| prefix: '', |
| path: '/user/papyrus/details', |
| __queryParams: {} |
| }; |
| |
| route.set('tail.path', '/messages'); |
| expect(route.route.path).to.be.deep.equal('/user/papyrus/messages'); |
| route.set('route.path', '/user/toriel'); |
| expect(route.tail).to.be.deep.equal({ |
| prefix: '/user/toriel', |
| path: '', |
| __queryParams: {} |
| }); |
| }); |
| |
| test('it creates data as described by pattern', function() { |
| route.route = { |
| prefix: '', |
| path: '/user/sans' |
| }; |
| |
| expect(route.data).to.be.deep.equal({username: 'sans'}); |
| expect(route.active).to.be.equal(true); |
| |
| route.pattern = '/user/:username/likes/:count'; |
| |
| // At the moment, we don't reset data when we no longer match. |
| expect(route.data).to.be.deep.equal({username: 'sans'}); |
| expect(route.active).to.be.equal(false); |
| |
| route.set('route.path', "/does/not/match"); |
| |
| expect(route.data).to.be.deep.equal({username: 'sans'}); |
| expect(route.active).to.be.equal(false); |
| |
| route.set('route.path', '/user/undyne/likes/20'); |
| expect(route.data).to.be.deep.equal({username: 'undyne', count: '20'}); |
| expect(route.active).to.be.equal(true); |
| }); |
| |
| test('changing data changes the path', function() { |
| route.route = { |
| prefix: '', |
| path: '/user/asgore' |
| }; |
| |
| expect(route.data).to.be.deep.equal({username: 'asgore'}); |
| route.data = {username: 'toriel'}; |
| expect(route.route.path).to.be.equal('/user/toriel'); |
| }); |
| |
| suite('propagating data', function() { |
| test('data is empty if no routes in the tree have matched', function() { |
| var routes = fixtureChainedRoutes({ path: '' }); |
| |
| expect(routes.foo.data).to.be.eql({}); |
| expect(routes.bar.data).to.be.eql({}); |
| expect(routes.baz.data).to.be.eql({}); |
| }); |
| |
| test('limits propagation to last matched route', function() { |
| var routes = fixtureChainedRoutes({ path: '/foo/123' }); |
| |
| expect(routes.foo.data).to.be.eql({ foo: '123' }); |
| expect(routes.bar.data).to.be.eql({}); |
| expect(routes.baz.data).to.be.eql({}); |
| }); |
| |
| test('propagates data to matching chained routes', function() { |
| var routes = fixtureChainedRoutes({ path: '/foo/123/bar/abc' }); |
| |
| expect(routes.foo.data).to.be.eql({ foo: '123' }); |
| expect(routes.bar.data).to.be.eql({ bar: 'abc' }); |
| expect(routes.baz.data).to.be.eql({}); |
| }); |
| |
| test('chained route state is untouched when deactivated', function() { |
| var routes = fixtureChainedRoutes({ path: '/foo/123/bar/abc' }); |
| |
| routes.foo.set('route.path', '/foo/321/baz/zyx'); |
| |
| expect(routes.foo.data).to.be.eql({ foo: '321' }); |
| expect(routes.bar.data).to.be.eql({ bar: 'abc' }); |
| expect(routes.baz.data).to.be.eql({ baz: 'zyx' }); |
| }); |
| |
| suite('updating the global path', function() { |
| test('happens when data changes if the route is active', function() { |
| var routes = fixtureChainedRoutes({ path: '/foo/123/bar/abc' }); |
| |
| expect(routes.bar.active).to.be.eql(true); |
| routes.bar.set('data.bar', 'cba'); |
| expect(routes.foo.route.path).to.be.eql('/foo/123/bar/cba'); |
| }); |
| |
| test('ignores changes when the route is inactive', function() { |
| var routes = fixtureChainedRoutes({ path: '/foo/123/bar/abc' }); |
| |
| expect(routes.baz.active).to.be.eql(false); |
| routes.baz.set('data.baz', 'cba'); |
| expect(routes.foo.route.path).to.be.eql('/foo/123/bar/abc'); |
| }); |
| |
| test('ignores changes after a route deactives', function() { |
| var routes = fixtureChainedRoutes({ path: '/foo/123/bar/abc' }); |
| |
| routes.foo.set('route.path', '/foo/123/baz/zyx'); |
| |
| expect(routes.bar.active).to.be.eql(false); |
| expect(routes.baz.active).to.be.eql(true); |
| routes.bar.set('data.bar', 'cba'); |
| expect(routes.foo.route.path).to.be.eql('/foo/123/baz/zyx'); |
| }); |
| }); |
| }); |
| |
| suite('propagating query params', function() { |
| test('query params are empty if no routes match', function() { |
| var routes = fixtureChainedRoutes({ path: '', __queryParams: { |
| qux: 'zot' |
| }}); |
| expect(routes.foo.queryParams).to.be.eql({}); |
| expect(routes.bar.queryParams).to.be.eql({}); |
| expect(routes.baz.queryParams).to.be.eql({}); |
| }); |
| |
| test('updates query params for all matched routes', function() { |
| var routes = fixtureChainedRoutes({ path: '/foo/123/bar/abc', __queryParams: { |
| qux: 'zot' |
| }}); |
| expect(routes.foo.queryParams).to.be.eql({ qux: 'zot' }); |
| expect(routes.bar.queryParams).to.be.eql({ qux: 'zot' }); |
| expect(routes.baz.queryParams).to.be.eql({}); |
| }); |
| |
| test('retains query params after routes deactivate', function() { |
| var routes = fixtureChainedRoutes({ path: '/foo/123/bar/abc', __queryParams: { |
| qux: 'zot' |
| }}); |
| routes.foo.set('route.path', '/foo/123/baz/xyz') |
| routes.foo.set('queryParams', { |
| qux: 'quux' |
| }); |
| expect(routes.foo.queryParams).to.be.eql({ qux: 'quux' }); |
| expect(routes.bar.queryParams).to.be.eql({ qux: 'zot' }); |
| expect(routes.baz.queryParams).to.be.eql({ qux: 'quux' }); |
| }); |
| |
| suite('updating global query params', function() { |
| test('happens when query params change on active routes', function() { |
| var routes = fixtureChainedRoutes({ path: '/foo/123/bar/abc', __queryParams: { |
| qux: 'zot' |
| }}); |
| |
| routes.bar.set('queryParams', { qux: 'quux' }); |
| |
| expect(routes.foo.queryParams).to.be.eql({ qux: 'quux' }); |
| expect(routes.bar.queryParams).to.be.eql({ qux: 'quux' }); |
| expect(routes.baz.queryParams).to.be.eql({}); |
| }); |
| |
| test('updates are ignored for routes that are inactive', function() { |
| var routes = fixtureChainedRoutes({ path: '/foo/123/bar/abc', __queryParams: { |
| qux: 'zot' |
| }}); |
| |
| routes.baz.set('queryParams', { qux: 'quux' }); |
| |
| expect(routes.foo.queryParams).to.be.eql({ qux: 'zot' }); |
| expect(routes.bar.queryParams).to.be.eql({ qux: 'zot' }); |
| expect(routes.baz.queryParams).to.be.eql({ qux: 'quux' }); |
| }); |
| |
| test('doesn\'t generate excess query-params-changed events', function() { |
| var routes = fixtureChainedRoutes({}); |
| var appRoutes = [routes.foo, routes.bar, routes.baz]; |
| var numChanges = 0; |
| for (var i = 0; i < appRoutes.length; i++) { |
| appRoutes[i].addEventListener('query-params-changed', function() { |
| numChanges++; |
| }); |
| } |
| |
| // Messing with paths but not query params shouldn't generate any |
| // change events. |
| expect(numChanges).to.be.equal(0); |
| routes.foo.set('route.path', '/foo/123/bar/456'); |
| expect(numChanges).to.be.equal(0); |
| routes.foo.set('route.path', '/foo/456/baz/789'); |
| expect(numChanges).to.be.equal(0); |
| |
| // Changing queryParams here should update foo and baz |
| routes.foo.set('route.__queryParams', {key: 'value'}); |
| expect(numChanges).to.be.equal(2); |
| // Then this should update bar |
| routes.foo.set('route.path', '/foo/123/bar/456'); |
| expect(numChanges).to.be.equal(3); |
| |
| // Changing back to baz shouldn't generate a change event. |
| routes.foo.set('route.path', '/foo/456/baz/789'); |
| expect(numChanges).to.be.equal(3); |
| |
| routes.foo.set('route.__queryParams', {}); |
| expect(numChanges).to.be.equal(5); |
| routes.foo.set('route.path', '/foo/123/bar/456'); |
| expect(numChanges).to.be.equal(6); |
| |
| }); |
| }); |
| }); |
| |
| suite('handles reentrent changes to its properties', function() { |
| var initialUrl; |
| setup(function() { |
| initialUrl = window.location.href; |
| }); |
| |
| teardown(function() { |
| window.history.replaceState({}, '', initialUrl); |
| }); |
| |
| test('changing path in response to path changing', function() { |
| var r = fixture('Redirection'); |
| r.addEventListener('route-changed', function() { |
| r.set('route.path', '/bar/baz'); |
| }); |
| r.set('route.path', '/foo'); |
| expect(window.location.pathname).to.be.equal('/bar/baz'); |
| expect(r.data).to.be.deep.equal({page: 'bar'}); |
| expect(r.route.path).to.be.equal('/bar/baz'); |
| expect(r.tail.path).to.be.equal('/baz'); |
| }); |
| |
| test('changing data wholesale in response to path changing', function() { |
| var r = fixture('Redirection'); |
| r.set('data.page', 'bar'); |
| r.addEventListener('route-changed', function(e) { |
| if (e.detail.path === 'route.path' && r.route.path === '/foo/baz') { |
| r.data = {page: 'bar'}; |
| } |
| }); |
| r.set('route.path', '/foo/baz'); |
| expect(window.location.pathname).to.be.equal('/bar'); |
| expect(r.data).to.be.deep.equal({page: 'bar'}); |
| expect(r.route.path).to.be.equal('/bar'); |
| expect(r.tail.path).to.be.equal(''); |
| }); |
| |
| test('changing a data piece in response to path changing', function() { |
| var r = fixture('Redirection'); |
| r.set('data.page', 'bar'); |
| r.addEventListener('route-changed', function(e) { |
| r.set('data.page', 'bar'); |
| }); |
| r.set('route.path', '/foo/baz'); |
| expect(window.location.pathname).to.be.equal('/bar'); |
| expect(r.data).to.be.deep.equal({page: 'bar'}); |
| expect(r.route.path).to.be.equal('/bar'); |
| expect(r.tail.path).to.be.equal(''); |
| }); |
| |
| test('changing the tail in response to path changing', function() { |
| var r = fixture('Redirection'); |
| r.addEventListener('route-changed', function() { |
| r.set('tail.path', '/bar'); |
| }); |
| r.set('route.path', '/foo'); |
| expect(window.location.pathname).to.be.equal('/foo/bar'); |
| expect(r.data).to.be.deep.equal({page: 'foo'}); |
| expect(r.route.path).to.be.equal('/foo/bar'); |
| expect(r.tail.path).to.be.equal('/bar'); |
| |
| r.set('route.path', '/foo/baz'); |
| expect(window.location.pathname).to.be.equal('/foo/bar'); |
| expect(r.data).to.be.deep.equal({page: 'foo'}); |
| expect(r.route.path).to.be.equal('/foo/bar'); |
| expect(r.tail.path).to.be.equal('/bar'); |
| }); |
| |
| test('changing the path in response to data changing', function() { |
| var r = fixture('Redirection'); |
| r.addEventListener('data-changed', function() { |
| r.set('route.path', '/bar'); |
| }); |
| r.set('data', {page: 'foo'}); |
| expect(window.location.pathname).to.be.equal('/bar'); |
| expect(r.data).to.be.deep.equal({page: 'bar'}); |
| expect(r.route.path).to.be.equal('/bar'); |
| expect(r.tail.path).to.be.equal(''); |
| }); |
| |
| test('changing data in response to data changing', function() { |
| var r = fixture('Redirection'); |
| r.addEventListener('data-changed', function() { |
| r.set('data.page', 'bar'); |
| }); |
| r.set('data', {page: 'foo'}); |
| expect(window.location.pathname).to.be.equal('/bar'); |
| expect(r.data).to.be.deep.equal({page: 'bar'}); |
| expect(r.route.path).to.be.equal('/bar'); |
| expect(r.tail.path).to.be.equal(''); |
| }); |
| |
| test('changing the data object wholesale in response to data changing', function() { |
| var r = fixture('Redirection'); |
| r.addEventListener('data-changed', function() { |
| if (r.data.page == 'foo') { |
| r.set('data', {page: 'bar'}); |
| } |
| }); |
| r.set('data', {page: 'foo'}); |
| expect(window.location.pathname).to.be.equal('/bar'); |
| expect(r.data).to.be.deep.equal({page: 'bar'}); |
| expect(r.route.path).to.be.equal('/bar'); |
| expect(r.tail.path).to.be.equal(''); |
| }); |
| |
| test('changing the tail in response to data changing', function() { |
| var r = fixture('Redirection'); |
| r.addEventListener('data-changed', function() { |
| r.set('tail.path', '/bar'); |
| }); |
| r.set('data', {page: 'foo'}); |
| expect(window.location.pathname).to.be.equal('/foo/bar'); |
| expect(r.data).to.be.deep.equal({page: 'foo'}); |
| expect(r.route.path).to.be.equal('/foo/bar'); |
| expect(r.tail.path).to.be.equal('/bar'); |
| }); |
| |
| test('changing the path in response to tail changing', function() { |
| var r = fixture('Redirection'); |
| r.set('route.path', '/foo/'); |
| r.addEventListener('tail-changed', function() { |
| r.set('route.path', '/baz' + r.tail.path); |
| }); |
| r.set('tail.path', '/bar'); |
| expect(window.location.pathname).to.be.equal('/baz/bar'); |
| expect(r.data).to.be.deep.equal({page: 'baz'}); |
| expect(r.route.path).to.be.equal('/baz/bar'); |
| expect(r.tail.path).to.be.equal('/bar'); |
| }); |
| |
| test('changing the data in response to tail changing', function() { |
| var r = fixture('Redirection'); |
| r.set('route.path', '/foo/'); |
| r.addEventListener('tail-changed', function() { |
| r.set('data.page', 'baz'); |
| }); |
| r.set('tail.path', '/bar'); |
| expect(window.location.pathname).to.be.equal('/baz'); |
| expect(r.data).to.be.deep.equal({page: 'baz'}); |
| expect(r.route.path).to.be.equal('/baz'); |
| expect(r.tail.path).to.be.equal(''); |
| }); |
| |
| test('changing the data object wholesale in response to tail changing', function() { |
| var r = fixture('Redirection'); |
| r.set('route.path', '/foo/'); |
| r.addEventListener('tail-changed', function() { |
| r.set('data', {page: 'baz'}); |
| }); |
| r.set('tail.path', '/bar'); |
| expect(window.location.pathname).to.be.equal('/baz'); |
| expect(r.data).to.be.deep.equal({page: 'baz'}); |
| expect(r.route.path).to.be.equal('/baz'); |
| expect(r.tail.path).to.be.equal(''); |
| }); |
| |
| test('changing the tail in response to tail changing', function() { |
| var r = fixture('Redirection'); |
| r.set('route.path', '/foo/'); |
| r.addEventListener('tail-changed', function() { |
| r.set('tail.path', '/baz'); |
| }); |
| r.set('tail.path', '/bar'); |
| expect(window.location.pathname).to.be.equal('/foo/baz'); |
| expect(r.data).to.be.deep.equal({page: 'foo'}); |
| expect(r.route.path).to.be.equal('/foo/baz'); |
| expect(r.tail.path).to.be.equal('/baz'); |
| }); |
| }); |
| }); |
| </script> |
| </body> |