• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1<!doctype html>
2<!--
3@license
4Copyright (c) 2016 The Polymer Project Authors. All rights reserved.
5This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
6The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
7The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
8Code distributed by Google as part of the polymer project is also
9subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
10-->
11<html>
12<head>
13  <title>app-route</title>
14
15  <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
16  <script src="../../web-component-tester/browser.js"></script>
17
18  <link rel="import" href="../../polymer/polymer.html">
19  <link rel="import" href="../app-route.html">
20  <link rel="import" href="./redirection.html">
21</head>
22<body>
23  <test-fixture id="BasicRoute">
24    <template>
25      <app-route pattern='/user/:username'>
26      </app-route>
27    </template>
28  </test-fixture>
29
30  <test-fixture id="ChainedRoutes">
31    <template is="dom-template">
32      <app-route
33          pattern="/foo/:foo"
34          route="{{numberOneTopRoute}}"
35          data="{{fooData}}"
36          tail="{{fooRoute}}">
37      </app-route>
38
39      <app-route
40          pattern="/bar/:bar"
41          route="{{fooRoute}}"
42          data="{{barData}}">
43      </app-route>
44
45      <app-route
46          pattern="/baz/:baz"
47          route="{{fooRoute}}"
48          data="{{bazData}}">
49      </app-route>
50    </template>
51  </test-fixture>
52
53  <test-fixture id="Redirection">
54    <template>
55      <redirect-app-route></redirect-app-route>
56    </template>
57  </test-fixture>
58
59<script>
60  'use strict';
61
62  function fixtureChainedRoutes(route) {
63    var routes = fixture('ChainedRoutes', {
64      numberOneTopRoute: {
65        path: route.path || '',
66        prefix: route.prefix || '',
67        __queryParams: route.__queryParams || {}
68      }
69    });
70
71    return {
72      foo: routes[0],
73      bar: routes[1],
74      baz: routes[2]
75    };
76  }
77
78  suite('<app-route>', function () {
79    var route;
80
81    setup(function() {
82      route = fixture('BasicRoute');
83
84      // This works around a bug in `dom-template` that is somehow
85      // exaserbated by the `app-route` implementation. A reduced test case
86      // is hard to come by. Track polymerelements/test-fixture#31 and remove
87      // this when that has been resolved:
88      var tmpl = document.querySelector('#ChainedRoutes').fixtureTemplates[0];
89      tmpl._parentProps = {};
90    });
91
92    test('it parses a path', function() {
93      route.route = {
94        prefix: '',
95        path: '/user/papyrus/details',
96        __queryParams: {}
97      }
98      expect(route.tail.prefix).to.be.equal('/user/papyrus');
99      expect(route.tail.path).to.be.equal('/details');
100      expect(route.data.username).to.be.equal('papyrus');
101    });
102
103    test('it bidirectionally maps changes between tail and route', function() {
104      route.route = {
105        prefix: '',
106        path: '/user/papyrus/details',
107        __queryParams: {}
108      };
109
110      route.set('tail.path', '/messages');
111      expect(route.route.path).to.be.deep.equal('/user/papyrus/messages');
112      route.set('route.path', '/user/toriel');
113      expect(route.tail).to.be.deep.equal({
114        prefix: '/user/toriel',
115        path: '',
116        __queryParams: {}
117      });
118    });
119
120    test('it creates data as described by pattern', function() {
121      route.route = {
122        prefix: '',
123        path: '/user/sans'
124      };
125
126      expect(route.data).to.be.deep.equal({username: 'sans'});
127      expect(route.active).to.be.equal(true);
128
129      route.pattern = '/user/:username/likes/:count';
130
131      // At the moment, we don't reset data when we no longer match.
132      expect(route.data).to.be.deep.equal({username: 'sans'});
133      expect(route.active).to.be.equal(false);
134
135      route.set('route.path', "/does/not/match");
136
137      expect(route.data).to.be.deep.equal({username: 'sans'});
138      expect(route.active).to.be.equal(false);
139
140      route.set('route.path', '/user/undyne/likes/20');
141      expect(route.data).to.be.deep.equal({username: 'undyne', count: '20'});
142      expect(route.active).to.be.equal(true);
143    });
144
145    test('changing data changes the path', function() {
146      route.route = {
147        prefix: '',
148        path: '/user/asgore'
149      };
150
151      expect(route.data).to.be.deep.equal({username: 'asgore'});
152      route.data = {username: 'toriel'};
153      expect(route.route.path).to.be.equal('/user/toriel');
154    });
155
156    suite('propagating data', function() {
157      test('data is empty if no routes in the tree have matched', function() {
158        var routes = fixtureChainedRoutes({ path: '' });
159
160        expect(routes.foo.data).to.be.eql({});
161        expect(routes.bar.data).to.be.eql({});
162        expect(routes.baz.data).to.be.eql({});
163      });
164
165      test('limits propagation to last matched route', function() {
166        var routes = fixtureChainedRoutes({ path: '/foo/123' });
167
168        expect(routes.foo.data).to.be.eql({ foo: '123' });
169        expect(routes.bar.data).to.be.eql({});
170        expect(routes.baz.data).to.be.eql({});
171      });
172
173      test('propagates data to matching chained routes', function() {
174        var routes = fixtureChainedRoutes({ path: '/foo/123/bar/abc' });
175
176        expect(routes.foo.data).to.be.eql({ foo: '123' });
177        expect(routes.bar.data).to.be.eql({ bar: 'abc' });
178        expect(routes.baz.data).to.be.eql({});
179      });
180
181      test('chained route state is untouched when deactivated', function() {
182        var routes = fixtureChainedRoutes({ path: '/foo/123/bar/abc' });
183
184        routes.foo.set('route.path', '/foo/321/baz/zyx');
185
186        expect(routes.foo.data).to.be.eql({ foo: '321' });
187        expect(routes.bar.data).to.be.eql({ bar: 'abc' });
188        expect(routes.baz.data).to.be.eql({ baz: 'zyx' });
189      });
190
191      suite('updating the global path', function() {
192        test('happens when data changes if the route is active', function() {
193          var routes = fixtureChainedRoutes({ path: '/foo/123/bar/abc' });
194
195          expect(routes.bar.active).to.be.eql(true);
196          routes.bar.set('data.bar', 'cba');
197          expect(routes.foo.route.path).to.be.eql('/foo/123/bar/cba');
198        });
199
200        test('ignores changes when the route is inactive', function() {
201          var routes = fixtureChainedRoutes({ path: '/foo/123/bar/abc' });
202
203          expect(routes.baz.active).to.be.eql(false);
204          routes.baz.set('data.baz', 'cba');
205          expect(routes.foo.route.path).to.be.eql('/foo/123/bar/abc');
206        });
207
208        test('ignores changes after a route deactives', function() {
209          var routes = fixtureChainedRoutes({ path: '/foo/123/bar/abc' });
210
211          routes.foo.set('route.path', '/foo/123/baz/zyx');
212
213          expect(routes.bar.active).to.be.eql(false);
214          expect(routes.baz.active).to.be.eql(true);
215          routes.bar.set('data.bar', 'cba');
216          expect(routes.foo.route.path).to.be.eql('/foo/123/baz/zyx');
217        });
218      });
219    });
220
221    suite('propagating query params', function() {
222      test('query params are empty if no routes match', function() {
223        var routes = fixtureChainedRoutes({ path: '', __queryParams: {
224          qux: 'zot'
225        }});
226        expect(routes.foo.queryParams).to.be.eql({});
227        expect(routes.bar.queryParams).to.be.eql({});
228        expect(routes.baz.queryParams).to.be.eql({});
229      });
230
231      test('updates query params for all matched routes', function() {
232        var routes = fixtureChainedRoutes({ path: '/foo/123/bar/abc', __queryParams: {
233          qux: 'zot'
234        }});
235        expect(routes.foo.queryParams).to.be.eql({ qux: 'zot' });
236        expect(routes.bar.queryParams).to.be.eql({ qux: 'zot' });
237        expect(routes.baz.queryParams).to.be.eql({});
238      });
239
240      test('retains query params after routes deactivate', function() {
241        var routes = fixtureChainedRoutes({ path: '/foo/123/bar/abc', __queryParams: {
242          qux: 'zot'
243        }});
244        routes.foo.set('route.path', '/foo/123/baz/xyz')
245        routes.foo.set('queryParams', {
246          qux: 'quux'
247        });
248        expect(routes.foo.queryParams).to.be.eql({ qux: 'quux' });
249        expect(routes.bar.queryParams).to.be.eql({ qux: 'zot' });
250        expect(routes.baz.queryParams).to.be.eql({ qux: 'quux' });
251      });
252
253      suite('updating global query params', function() {
254        test('happens when query params change on active routes', function() {
255          var routes = fixtureChainedRoutes({ path: '/foo/123/bar/abc', __queryParams: {
256            qux: 'zot'
257          }});
258
259          routes.bar.set('queryParams', { qux: 'quux' });
260
261          expect(routes.foo.queryParams).to.be.eql({ qux: 'quux' });
262          expect(routes.bar.queryParams).to.be.eql({ qux: 'quux' });
263          expect(routes.baz.queryParams).to.be.eql({});
264        });
265
266        test('updates are ignored for routes that are inactive', function() {
267          var routes = fixtureChainedRoutes({ path: '/foo/123/bar/abc', __queryParams: {
268            qux: 'zot'
269          }});
270
271          routes.baz.set('queryParams', { qux: 'quux' });
272
273          expect(routes.foo.queryParams).to.be.eql({ qux: 'zot' });
274          expect(routes.bar.queryParams).to.be.eql({ qux: 'zot' });
275          expect(routes.baz.queryParams).to.be.eql({ qux: 'quux' });
276        });
277
278        test('doesn\'t generate excess query-params-changed events', function() {
279          var routes = fixtureChainedRoutes({});
280          var appRoutes = [routes.foo, routes.bar, routes.baz];
281          var numChanges = 0;
282          for (var i = 0; i < appRoutes.length; i++) {
283            appRoutes[i].addEventListener('query-params-changed', function() {
284              numChanges++;
285            });
286          }
287
288          // Messing with paths but not query params shouldn't generate any
289          // change events.
290          expect(numChanges).to.be.equal(0);
291          routes.foo.set('route.path', '/foo/123/bar/456');
292          expect(numChanges).to.be.equal(0);
293          routes.foo.set('route.path', '/foo/456/baz/789');
294          expect(numChanges).to.be.equal(0);
295
296          // Changing queryParams here should update foo and baz
297          routes.foo.set('route.__queryParams', {key: 'value'});
298          expect(numChanges).to.be.equal(2);
299          // Then this should update bar
300          routes.foo.set('route.path', '/foo/123/bar/456');
301          expect(numChanges).to.be.equal(3);
302
303          // Changing back to baz shouldn't generate a change event.
304          routes.foo.set('route.path', '/foo/456/baz/789');
305          expect(numChanges).to.be.equal(3);
306
307          routes.foo.set('route.__queryParams', {});
308          expect(numChanges).to.be.equal(5);
309          routes.foo.set('route.path', '/foo/123/bar/456');
310          expect(numChanges).to.be.equal(6);
311
312        });
313      });
314    });
315
316    suite('handles reentrent changes to its properties', function() {
317      var initialUrl;
318      setup(function() {
319        initialUrl = window.location.href;
320      });
321
322      teardown(function() {
323        window.history.replaceState({}, '', initialUrl);
324      });
325
326      test('changing path in response to path changing', function() {
327        var r = fixture('Redirection');
328        r.addEventListener('route-changed', function() {
329          r.set('route.path', '/bar/baz');
330        });
331        r.set('route.path', '/foo');
332        expect(window.location.pathname).to.be.equal('/bar/baz');
333        expect(r.data).to.be.deep.equal({page: 'bar'});
334        expect(r.route.path).to.be.equal('/bar/baz');
335        expect(r.tail.path).to.be.equal('/baz');
336      });
337
338      test('changing data wholesale in response to path changing', function() {
339        var r = fixture('Redirection');
340        r.set('data.page', 'bar');
341        r.addEventListener('route-changed', function(e) {
342          if (e.detail.path === 'route.path' && r.route.path === '/foo/baz') {
343            r.data = {page: 'bar'};
344          }
345        });
346        r.set('route.path', '/foo/baz');
347        expect(window.location.pathname).to.be.equal('/bar');
348        expect(r.data).to.be.deep.equal({page: 'bar'});
349        expect(r.route.path).to.be.equal('/bar');
350        expect(r.tail.path).to.be.equal('');
351      });
352
353      test('changing a data piece in response to path changing', function() {
354        var r = fixture('Redirection');
355        r.set('data.page', 'bar');
356        r.addEventListener('route-changed', function(e) {
357          r.set('data.page', 'bar');
358        });
359        r.set('route.path', '/foo/baz');
360        expect(window.location.pathname).to.be.equal('/bar');
361        expect(r.data).to.be.deep.equal({page: 'bar'});
362        expect(r.route.path).to.be.equal('/bar');
363        expect(r.tail.path).to.be.equal('');
364      });
365
366      test('changing the tail in response to path changing', function() {
367        var r = fixture('Redirection');
368        r.addEventListener('route-changed', function() {
369          r.set('tail.path', '/bar');
370        });
371        r.set('route.path', '/foo');
372        expect(window.location.pathname).to.be.equal('/foo/bar');
373        expect(r.data).to.be.deep.equal({page: 'foo'});
374        expect(r.route.path).to.be.equal('/foo/bar');
375        expect(r.tail.path).to.be.equal('/bar');
376
377        r.set('route.path', '/foo/baz');
378        expect(window.location.pathname).to.be.equal('/foo/bar');
379        expect(r.data).to.be.deep.equal({page: 'foo'});
380        expect(r.route.path).to.be.equal('/foo/bar');
381        expect(r.tail.path).to.be.equal('/bar');
382      });
383
384      test('changing the path in response to data changing', function() {
385        var r = fixture('Redirection');
386        r.addEventListener('data-changed', function() {
387          r.set('route.path', '/bar');
388        });
389        r.set('data', {page: 'foo'});
390        expect(window.location.pathname).to.be.equal('/bar');
391        expect(r.data).to.be.deep.equal({page: 'bar'});
392        expect(r.route.path).to.be.equal('/bar');
393        expect(r.tail.path).to.be.equal('');
394      });
395
396      test('changing data in response to data changing', function() {
397        var r = fixture('Redirection');
398        r.addEventListener('data-changed', function() {
399          r.set('data.page', 'bar');
400        });
401        r.set('data', {page: 'foo'});
402        expect(window.location.pathname).to.be.equal('/bar');
403        expect(r.data).to.be.deep.equal({page: 'bar'});
404        expect(r.route.path).to.be.equal('/bar');
405        expect(r.tail.path).to.be.equal('');
406      });
407
408      test('changing the data object wholesale in response to data changing', function() {
409        var r = fixture('Redirection');
410        r.addEventListener('data-changed', function() {
411          if (r.data.page == 'foo') {
412            r.set('data', {page: 'bar'});
413          }
414        });
415        r.set('data', {page: 'foo'});
416        expect(window.location.pathname).to.be.equal('/bar');
417        expect(r.data).to.be.deep.equal({page: 'bar'});
418        expect(r.route.path).to.be.equal('/bar');
419        expect(r.tail.path).to.be.equal('');
420      });
421
422      test('changing the tail in response to data changing', function() {
423        var r = fixture('Redirection');
424        r.addEventListener('data-changed', function() {
425          r.set('tail.path', '/bar');
426        });
427        r.set('data', {page: 'foo'});
428        expect(window.location.pathname).to.be.equal('/foo/bar');
429        expect(r.data).to.be.deep.equal({page: 'foo'});
430        expect(r.route.path).to.be.equal('/foo/bar');
431        expect(r.tail.path).to.be.equal('/bar');
432      });
433
434      test('changing the path in response to tail changing', function() {
435        var r = fixture('Redirection');
436        r.set('route.path', '/foo/');
437        r.addEventListener('tail-changed', function() {
438          r.set('route.path', '/baz' + r.tail.path);
439        });
440        r.set('tail.path', '/bar');
441        expect(window.location.pathname).to.be.equal('/baz/bar');
442        expect(r.data).to.be.deep.equal({page: 'baz'});
443        expect(r.route.path).to.be.equal('/baz/bar');
444        expect(r.tail.path).to.be.equal('/bar');
445      });
446
447      test('changing the data in response to tail changing', function() {
448        var r = fixture('Redirection');
449        r.set('route.path', '/foo/');
450        r.addEventListener('tail-changed', function() {
451          r.set('data.page', 'baz');
452        });
453        r.set('tail.path', '/bar');
454        expect(window.location.pathname).to.be.equal('/baz');
455        expect(r.data).to.be.deep.equal({page: 'baz'});
456        expect(r.route.path).to.be.equal('/baz');
457        expect(r.tail.path).to.be.equal('');
458      });
459
460      test('changing the data object wholesale in response to tail changing', function() {
461        var r = fixture('Redirection');
462        r.set('route.path', '/foo/');
463        r.addEventListener('tail-changed', function() {
464          r.set('data', {page: 'baz'});
465        });
466        r.set('tail.path', '/bar');
467        expect(window.location.pathname).to.be.equal('/baz');
468        expect(r.data).to.be.deep.equal({page: 'baz'});
469        expect(r.route.path).to.be.equal('/baz');
470        expect(r.tail.path).to.be.equal('');
471      });
472
473      test('changing the tail in response to tail changing', function() {
474        var r = fixture('Redirection');
475        r.set('route.path', '/foo/');
476        r.addEventListener('tail-changed', function() {
477          r.set('tail.path', '/baz');
478        });
479        r.set('tail.path', '/bar');
480        expect(window.location.pathname).to.be.equal('/foo/baz');
481        expect(r.data).to.be.deep.equal({page: 'foo'});
482        expect(r.route.path).to.be.equal('/foo/baz');
483        expect(r.tail.path).to.be.equal('/baz');
484      });
485    });
486  });
487</script>
488</body>
489