blob: da4af2ca78447d4d5bff057c81c2812bd1a9e897 [file] [log] [blame]
<!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>