From c5a29135021ba553475fdf9ad472a97979fd3ae1 Mon Sep 17 00:00:00 2001 From: Chad Hietala Date: Mon, 1 Oct 2018 13:26:25 -0400 Subject: [PATCH 1/4] [Feature Router Service] routeWillChange & routeDidChange --- package.json | 2 +- .../-internals/routing/lib/services/router.ts | 14 +- .../-internals/routing/lib/system/route.ts | 2 +- .../-internals/routing/lib/system/router.ts | 76 +- packages/@ember/deprecated-features/index.ts | 1 + .../router_service_test/events_test.js | 663 ++++++++++++++++++ .../lib/test-cases/router.js | 3 + yarn.lock | 7 +- 8 files changed, 759 insertions(+), 9 deletions(-) create mode 100644 packages/ember/tests/routing/router_service_test/events_test.js diff --git a/package.json b/package.json index ed5900f0e6d..fee4b90d213 100644 --- a/package.json +++ b/package.json @@ -136,7 +136,7 @@ "puppeteer": "^1.3.0", "qunit": "^2.5.0", "route-recognizer": "^0.3.4", - "router_js": "^5.0.3", + "router_js": "^5.1.1", "rsvp": "^4.8.2", "semver": "^5.5.0", "serve-static": "^1.12.2", diff --git a/packages/@ember/-internals/routing/lib/services/router.ts b/packages/@ember/-internals/routing/lib/services/router.ts index cc7440cad75..d58308a1fcd 100644 --- a/packages/@ember/-internals/routing/lib/services/router.ts +++ b/packages/@ember/-internals/routing/lib/services/router.ts @@ -1,5 +1,7 @@ +import { Evented } from '@ember/-internals/runtime'; import { readOnly } from '@ember/object/computed'; import Service from '@ember/service'; +import { Transition } from 'router_js'; import EmberRouter from '../system/router'; import { extractRouteArgs, resemblesURL, shallowEqual } from '../utils'; @@ -148,7 +150,17 @@ export default class RouterService extends Service { } } -RouterService.reopen({ +RouterService.reopen(Evented, { + init() { + this._super(...arguments); + this._router.on('routeWillChange', (transition: Transition) => { + this.trigger('routeWillChange', transition); + }); + + this._router.on('routeDidChange', (transition: Transition) => { + this.trigger('routeDidChange', transition); + }); + }, /** Name of the current route. diff --git a/packages/@ember/-internals/routing/lib/system/route.ts b/packages/@ember/-internals/routing/lib/system/route.ts index 52750bd7b91..42d77d3a9f9 100644 --- a/packages/@ember/-internals/routing/lib/system/route.ts +++ b/packages/@ember/-internals/routing/lib/system/route.ts @@ -329,7 +329,7 @@ class Route extends EmberObject implements IRoute { /** @private - @method _reset + @method _internalReset @since 1.7.0 */ _internalReset(isExiting: boolean, transition: Transition) { diff --git a/packages/@ember/-internals/routing/lib/system/router.ts b/packages/@ember/-internals/routing/lib/system/router.ts index 3534b9353fc..157bc107307 100644 --- a/packages/@ember/-internals/routing/lib/system/router.ts +++ b/packages/@ember/-internals/routing/lib/system/router.ts @@ -3,7 +3,7 @@ import { getOwner, Owner } from '@ember/-internals/owner'; import { A as emberA, Evented, Object as EmberObject, typeOf } from '@ember/-internals/runtime'; import { EMBER_ROUTING_ROUTER_SERVICE } from '@ember/canary-features'; import { assert, deprecate, info } from '@ember/debug'; -import { HANDLER_INFOS } from '@ember/deprecated-features'; +import { HANDLER_INFOS, ROUTER_EVENTS } from '@ember/deprecated-features'; import EmberError from '@ember/error'; import { assign } from '@ember/polyfills'; import { cancel, once, run, scheduleOnce } from '@ember/runloop'; @@ -21,7 +21,9 @@ import { MatchCallback } from 'route-recognizer'; import Router, { InternalRouteInfo, InternalTransition, + logAbort, Transition, + TransitionError, TransitionState, } from 'router_js'; import { EngineRouteInfo } from './engines'; @@ -257,6 +259,43 @@ class EmberRouter extends EmberObject { return triggerEvent.bind(router)(routeInfos, ignoreFailure, name, args); } + routeWillChange(transition: Transition) { + if (EMBER_ROUTING_ROUTER_SERVICE) { + router.trigger('routeWillChange', transition); + } + } + + routeDidChange(transition: Transition) { + if (EMBER_ROUTING_ROUTER_SERVICE) { + router.trigger('routeDidChange', transition); + } + } + + transitionDidError(error: TransitionError, transition: Transition) { + if (error.wasAborted || transition.isAborted) { + // If the error was a transition erorr or the transition aborted + // log the abort. + return logAbort(transition); + } else { + // Otherwise trigger the "error" event to attempt an intermediate + // transition into an error substate + transition.trigger(false, 'error', error.error, transition, error.route); + if (router._isErrorHandled(error.error)) { + // If we handled the error with a substate just roll the state back on + // the transition and send the "routeDidChange" event for landing on + // the error substate and return the error. + transition.rollback(); + this.routeDidChange(transition); + return error.error; + } else { + // If it was not handled, abort the transition completely and return + // the error. + transition.abort(); + return error.error; + } + } + } + _triggerWillChangeContext() { return router; } @@ -1449,7 +1488,7 @@ export function triggerEvent( routeInfos: PrivateRouteInfo[], ignoreFailure: boolean, name: string, - args: unknown[] + args: any[] ) { if (!routeInfos) { if (ignoreFailure) { @@ -1793,4 +1832,37 @@ EmberRouter.reopen(Evented, { }), }); +if (EMBER_ROUTING_ROUTER_SERVICE) { + if (ROUTER_EVENTS) { + EmberRouter.reopen({ + on(name: string) { + this._super(...arguments); + let hasDidTransition = name === 'didTransition'; + let hasWillTransition = name === 'willTransition'; + + if (hasDidTransition) { + deprecate( + 'You attempted to listen to the "didTransition" event which is deprecated. Please inject the router service and listen to the "routeDidChange" event.', + false, + { + id: 'deprecate-router-events', + until: '3.9.0', + } + ); + } + + if (hasWillTransition) { + deprecate( + 'You attempted to listen to the "willTransition" event which is deprecated. Please inject the router service and listen to the "routeWillChange" event.', + false, + { + id: 'deprecate-router-events', + until: '3.9.0', + } + ); + } + }, + }); + } +} export default EmberRouter; diff --git a/packages/@ember/deprecated-features/index.ts b/packages/@ember/deprecated-features/index.ts index a25689b422f..7751e68b1e7 100644 --- a/packages/@ember/deprecated-features/index.ts +++ b/packages/@ember/deprecated-features/index.ts @@ -13,3 +13,4 @@ export const MAP = !!'3.3.0-beta.1'; export const ORDERED_SET = !!'3.3.0-beta.1'; export const MERGE = !!'3.6.0-beta.1'; export const HANDLER_INFOS = !!'3.9.0'; +export const ROUTER_EVENTS = !!'3.9.0'; diff --git a/packages/ember/tests/routing/router_service_test/events_test.js b/packages/ember/tests/routing/router_service_test/events_test.js new file mode 100644 index 00000000000..d2434178a2c --- /dev/null +++ b/packages/ember/tests/routing/router_service_test/events_test.js @@ -0,0 +1,663 @@ +import { RouterTestCase, moduleFor } from 'internal-test-helpers'; +import { EMBER_ROUTING_ROUTER_SERVICE } from '@ember/canary-features'; +import { inject as service } from '@ember/service'; +import { Route } from '@ember/-internals/routing'; + +if (EMBER_ROUTING_ROUTER_SERVICE) { + moduleFor( + 'Router Service - events', + class extends RouterTestCase { + '@test initial render'(assert) { + assert.expect(8); + this.add( + `route:application`, + Route.extend({ + router: service('router'), + init() { + this._super(...arguments); + this.router.on('routeWillChange', transition => { + assert.ok(transition); + assert.equal(transition.from, undefined); + assert.equal(transition.to.name, 'parent.index'); + assert.equal(transition.to.localName, 'index'); + }); + + this.router.on('routeDidChange', transition => { + assert.ok(transition); + assert.equal(transition.from, undefined); + assert.equal(transition.to.name, 'parent.index'); + assert.equal(transition.to.localName, 'index'); + }); + }, + }) + ); + return this.visit('/'); + } + + '@test subsequent visits'(assert) { + assert.expect(20); + let toParent = true; + + this.add( + `route:application`, + Route.extend({ + router: service('router'), + init() { + this._super(...arguments); + this.router.on('routeWillChange', transition => { + if (toParent) { + assert.equal(transition.from, undefined); + assert.equal(transition.to.name, 'parent.child'); + assert.equal(transition.to.localName, 'child'); + assert.equal(transition.to.parent.name, 'parent', 'parent node'); + assert.equal( + transition.to.parent.child, + transition.to, + 'parents child node is the `to`' + ); + assert.equal(transition.to.parent.parent.name, 'application', 'top level'); + assert.equal(transition.to.parent.parent.parent, null, 'top level'); + } else { + assert.notEqual(transition.from, undefined); + assert.equal(transition.from.name, 'parent.child'); + assert.equal(transition.from.localName, 'child'); + assert.equal(transition.to.localName, 'sister'); + assert.equal(transition.to.name, 'parent.sister'); + } + }); + + this.router.on('routeDidChange', transition => { + if (toParent) { + assert.equal(transition.from, undefined); + assert.equal(transition.to.name, 'parent.child'); + assert.equal(transition.to.localName, 'child'); + } else { + assert.notEqual(transition.from, undefined); + assert.equal(transition.from.name, 'parent.child'); + assert.equal(transition.from.localName, 'child'); + assert.equal(transition.to.localName, 'sister'); + assert.equal(transition.to.name, 'parent.sister'); + } + }); + }, + }) + ); + return this.visit('/child').then(() => { + toParent = false; + return this.routerService.transitionTo('parent.sister'); + }); + } + + '@test redirection with `transitionTo`'(assert) { + assert.expect(8); + let toChild = false; + let toSister = false; + + this.add( + `route:parent`, + Route.extend({ + model() { + this.transitionTo('parent.child'); + }, + }) + ); + + this.add( + `route:parent.child`, + Route.extend({ + model() { + this.transitionTo('parent.sister'); + }, + }) + ); + + this.add( + `route:application`, + Route.extend({ + router: service('router'), + init() { + this._super(...arguments); + + this.router.on('routeWillChange', transition => { + assert.equal(transition.from, undefined, 'initial'); + if (toChild) { + if (toSister) { + assert.equal(transition.to.name, 'parent.sister', 'going to /sister'); + } else { + assert.equal(transition.to.name, 'parent.child', 'going to /child'); + toSister = true; + } + } else { + // Going to `/` + assert.equal(transition.to.name, 'parent.index', 'going to /'); + toChild = true; + } + }); + + this.router.on('routeDidChange', transition => { + assert.equal(transition.from, undefined, 'initial'); + assert.equal(transition.to.name, 'parent.sister', 'landed on /sister'); + }); + }, + }) + ); + return this.visit('/'); + } + + '@test redirection with `replaceWith`'(assert) { + assert.expect(8); + let toChild = false; + let toSister = false; + + this.add( + `route:parent`, + Route.extend({ + model() { + this.replaceWith('parent.child'); + }, + }) + ); + + this.add( + `route:parent.child`, + Route.extend({ + model() { + this.replaceWith('parent.sister'); + }, + }) + ); + + this.add( + `route:application`, + Route.extend({ + router: service('router'), + init() { + this._super(...arguments); + + this.router.on('routeWillChange', transition => { + assert.equal(transition.from, undefined, 'initial'); + if (toChild) { + if (toSister) { + assert.equal(transition.to.name, 'parent.sister', 'going to /sister'); + } else { + assert.equal(transition.to.name, 'parent.child', 'going to /child'); + toSister = true; + } + } else { + // Going to `/` + assert.equal(transition.to.name, 'parent.index', 'going to /'); + toChild = true; + } + }); + + this.router.on('routeDidChange', transition => { + assert.equal(transition.from, undefined, 'initial'); + assert.equal(transition.to.name, 'parent.sister', 'landed on /sister'); + }); + }, + }) + ); + return this.visit('/'); + } + + '@test nested redirection with `transitionTo`'(assert) { + assert.expect(11); + let toChild = false; + let toSister = false; + + this.add( + `route:parent.child`, + Route.extend({ + model() { + this.transitionTo('parent.sister'); + }, + }) + ); + + this.add( + `route:application`, + Route.extend({ + router: service('router'), + init() { + this._super(...arguments); + + this.router.on('routeWillChange', transition => { + if (toChild) { + assert.equal(transition.from.name, 'parent.index'); + if (toSister) { + assert.equal(transition.to.name, 'parent.sister', 'going to /sister'); + } else { + assert.equal(transition.to.name, 'parent.child', 'going to /child'); + toSister = true; + } + } else { + // Going to `/` + assert.equal(transition.to.name, 'parent.index', 'going to /'); + assert.equal(transition.from, undefined, 'initial'); + } + }); + + this.router.on('routeDidChange', transition => { + if (toSister) { + assert.equal(transition.from.name, 'parent.index', 'initial'); + assert.equal(transition.to.name, 'parent.sister', 'landed on /sister'); + } else { + assert.equal(transition.from, undefined, 'initial'); + assert.equal(transition.to.name, 'parent.index', 'landed on /'); + } + }); + }, + }) + ); + return this.visit('/').then(() => { + toChild = true; + return this.routerService.transitionTo('/child').catch(e => { + assert.equal(e.name, 'TransitionAborted', 'Transition aborted'); + }); + }); + } + + '@test nested redirection with `replaceWith`'(assert) { + assert.expect(11); + let toChild = false; + let toSister = false; + + this.add( + `route:parent.child`, + Route.extend({ + model() { + this.replaceWith('parent.sister'); + }, + }) + ); + + this.add( + `route:application`, + Route.extend({ + router: service('router'), + init() { + this._super(...arguments); + + this.router.on('routeWillChange', transition => { + if (toChild) { + assert.equal(transition.from.name, 'parent.index'); + if (toSister) { + assert.equal(transition.to.name, 'parent.sister', 'going to /sister'); + } else { + assert.equal(transition.to.name, 'parent.child', 'going to /child'); + toSister = true; + } + } else { + // Going to `/` + assert.equal(transition.to.name, 'parent.index', 'going to /'); + assert.equal(transition.from, undefined, 'initial'); + } + }); + + this.router.on('routeDidChange', transition => { + if (toSister) { + assert.equal(transition.from.name, 'parent.index', 'initial'); + assert.equal(transition.to.name, 'parent.sister', 'landed on /sister'); + } else { + assert.equal(transition.from, undefined, 'initial'); + assert.equal(transition.to.name, 'parent.index', 'landed on /'); + } + }); + }, + }) + ); + return this.visit('/').then(() => { + toChild = true; + return this.routerService.transitionTo('/child').catch(e => { + assert.equal(e.name, 'TransitionAborted', 'Transition aborted'); + }); + }); + } + + '@test aborted transition'(assert) { + assert.expect(11); + let didAbort = false; + let toChild = false; + + this.add( + `route:parent.child`, + Route.extend({ + model(_model, transition) { + didAbort = true; + transition.abort(); + }, + }) + ); + + this.add( + `route:application`, + Route.extend({ + router: service('router'), + init() { + this._super(...arguments); + + this.router.on('routeWillChange', transition => { + if (didAbort) { + assert.equal(transition.to.name, 'parent.index', 'transition aborted'); + assert.equal(transition.from.name, 'parent.index', 'transition aborted'); + } else if (toChild) { + assert.equal(transition.from.name, 'parent.index', 'from /'); + assert.equal(transition.to.name, 'parent.child', 'to /child'); + } else { + assert.equal(transition.to.name, 'parent.index', 'going to /'); + assert.equal(transition.from, undefined, 'initial'); + } + }); + + this.router.on('routeDidChange', transition => { + if (didAbort) { + assert.equal(transition.to.name, 'parent.index', 'landed on /'); + assert.equal(transition.from.name, 'parent.index', 'initial'); + } else { + assert.equal(transition.to.name, 'parent.index', 'transition aborted'); + assert.equal(transition.from, undefined, 'transition aborted'); + } + }); + }, + }) + ); + return this.visit('/').then(() => { + toChild = true; + return this.routerService.transitionTo('/child').catch(e => { + assert.equal(e.name, 'TransitionAborted', 'Transition aborted'); + }); + }); + } + + '@test query param transitions'(assert) { + assert.expect(9); + let initial = true; + let addQP = false; + let removeQP = false; + + this.add( + `route:application`, + Route.extend({ + router: service('router'), + init() { + this._super(...arguments); + + this.router.on('routeWillChange', transition => { + assert.equal(transition.to.name, 'parent.index'); + if (initial) { + assert.deepEqual(transition.to.queryParams, { a: 'true' }); + } else if (addQP) { + assert.deepEqual(transition.to.queryParams, { a: 'false', b: 'b' }); + } else if (removeQP) { + assert.deepEqual(transition.to.queryParams, { a: 'false' }); + } else { + assert.ok(false, 'never'); + } + }); + + this.router.on('routeDidChange', transition => { + if (initial) { + assert.deepEqual(transition.to.queryParams, { a: 'true' }); + } else if (addQP) { + assert.deepEqual(transition.to.queryParams, { a: 'false', b: 'b' }); + } else if (removeQP) { + assert.deepEqual(transition.to.queryParams, { a: 'false' }); + } else { + assert.ok(false, 'never'); + } + }); + }, + }) + ); + + return this.visit('/?a=true') + .then(() => { + addQP = true; + initial = false; + return this.routerService.transitionTo('/?a=false&b=b'); + }) + .then(() => { + removeQP = true; + addQP = false; + return this.routerService.transitionTo('/?a=false'); + }); + } + + '@test query param redirects with `transitionTo`'(assert) { + assert.expect(6); + let toSister = false; + + this.add( + `route:parent.child`, + Route.extend({ + model() { + toSister = true; + this.transitionTo('/sister?a=a'); + }, + }) + ); + + this.add( + `route:application`, + Route.extend({ + router: service('router'), + init() { + this._super(...arguments); + + this.router.on('routeWillChange', transition => { + if (toSister) { + assert.equal(transition.to.name, 'parent.sister'); + assert.deepEqual(transition.to.queryParams, { a: 'a' }); + } else { + assert.equal(transition.to.name, 'parent.child'); + assert.deepEqual(transition.to.queryParams, {}); + } + }); + + this.router.on('routeDidChange', transition => { + assert.equal(transition.to.name, 'parent.sister'); + assert.deepEqual(transition.to.queryParams, { a: 'a' }); + }); + }, + }) + ); + + return this.visit('/child'); + } + '@test query param redirects with `replaceWith`'(assert) { + assert.expect(6); + let toSister = false; + + this.add( + `route:parent.child`, + Route.extend({ + model() { + toSister = true; + this.replaceWith('/sister?a=a'); + }, + }) + ); + + this.add( + `route:application`, + Route.extend({ + router: service('router'), + init() { + this._super(...arguments); + + this.router.on('routeWillChange', transition => { + if (toSister) { + assert.equal(transition.to.name, 'parent.sister'); + assert.deepEqual(transition.to.queryParams, { a: 'a' }); + } else { + assert.equal(transition.to.name, 'parent.child'); + assert.deepEqual(transition.to.queryParams, {}); + } + }); + + this.router.on('routeDidChange', transition => { + assert.equal(transition.to.name, 'parent.sister'); + assert.deepEqual(transition.to.queryParams, { a: 'a' }); + }); + }, + }) + ); + + return this.visit('/child'); + } + + '@test params'(assert) { + assert.expect(14); + + let inital = true; + + this.add( + 'route:dynamic', + Route.extend({ + model(params) { + if (inital) { + assert.deepEqual(params, { dynamic_id: '123' }); + } else { + assert.deepEqual(params, { dynamic_id: '1' }); + } + return params; + }, + }) + ); + + this.add( + `route:application`, + Route.extend({ + router: service('router'), + init() { + this._super(...arguments); + + this.router.on('routeWillChange', transition => { + assert.equal(transition.to.name, 'dynamic'); + if (inital) { + assert.deepEqual(transition.to.paramNames, ['dynamic_id']); + assert.deepEqual(transition.to.params, { dynamic_id: '123' }); + } else { + assert.deepEqual(transition.to.paramNames, ['dynamic_id']); + assert.deepEqual(transition.to.params, { dynamic_id: '1' }); + } + }); + + this.router.on('routeDidChange', transition => { + assert.equal(transition.to.name, 'dynamic'); + assert.deepEqual(transition.to.paramNames, ['dynamic_id']); + if (inital) { + assert.deepEqual(transition.to.params, { dynamic_id: '123' }); + } else { + assert.deepEqual(transition.to.params, { dynamic_id: '1' }); + } + }); + }, + }) + ); + + return this.visit('/dynamic/123').then(() => { + inital = false; + return this.routerService.transitionTo('dynamic', 1); + }); + } + + '@test nested params'(assert) { + assert.expect(24); + let initial = true; + + this.add( + 'route:dynamicWithChild', + Route.extend({ + model(params) { + if (initial) { + assert.deepEqual(params, { dynamic_id: '123' }); + } else { + assert.deepEqual(params, { dynamic_id: '456' }); + } + return params; + }, + }) + ); + + this.add( + 'route:dynamicWithChild.child', + Route.extend({ + model(params) { + assert.deepEqual(params, { child_id: '456' }); + return params; + }, + }) + ); + + this.add( + `route:application`, + Route.extend({ + router: service('router'), + init() { + this._super(...arguments); + + this.router.on('routeWillChange', transition => { + assert.equal(transition.to.name, 'dynamicWithChild.child'); + assert.deepEqual(transition.to.paramNames, ['child_id']); + assert.deepEqual(transition.to.params, { child_id: '456' }); + assert.deepEqual(transition.to.parent.paramNames, ['dynamic_id']); + if (initial) { + assert.deepEqual(transition.to.parent.params, { dynamic_id: '123' }); + } else { + assert.deepEqual(transition.to.parent.params, { dynamic_id: '456' }); + } + }); + + this.router.on('routeDidChange', transition => { + assert.equal(transition.to.name, 'dynamicWithChild.child'); + assert.deepEqual(transition.to.paramNames, ['child_id']); + assert.deepEqual(transition.to.params, { child_id: '456' }); + assert.deepEqual(transition.to.parent.paramNames, ['dynamic_id']); + if (initial) { + assert.deepEqual(transition.to.parent.params, { dynamic_id: '123' }); + } else { + assert.deepEqual(transition.to.parent.params, { dynamic_id: '456' }); + } + }); + }, + }) + ); + + return this.visit('/dynamic-with-child/123/456').then(() => { + initial = false; + return this.routerService.transitionTo('/dynamic-with-child/456/456'); + }); + } + } + ); + + moduleFor( + 'Router Service - deprecated events', + class extends RouterTestCase { + '@test willTransition is deprecated'() { + return this.visit('/').then(() => { + expectDeprecation(() => { + this.routerService['_router'].on('willTransition', () => {}); + }, 'You attempted to listen to the "willTransition" event which is deprecated. Please inject the router service and listen to the "routeWillChange" event.'); + }); + } + + '@test didTransition is deprecated'() { + return this.visit('/').then(() => { + expectDeprecation(() => { + this.routerService['_router'].on('didTransition', () => {}); + }, 'You attempted to listen to the "didTransition" event which is deprecated. Please inject the router service and listen to the "routeDidChange" event.'); + }); + } + + '@test other events are not deprecated'() { + return this.visit('/').then(() => { + expectNoDeprecation(() => { + this.routerService['_router'].on('wat', () => {}); + }); + }); + } + } + ); +} diff --git a/packages/internal-test-helpers/lib/test-cases/router.js b/packages/internal-test-helpers/lib/test-cases/router.js index b71e3152e5b..05d888e3beb 100644 --- a/packages/internal-test-helpers/lib/test-cases/router.js +++ b/packages/internal-test-helpers/lib/test-cases/router.js @@ -11,6 +11,9 @@ export default class RouterTestCase extends ApplicationTestCase { this.route('brother'); }); this.route('dynamic', { path: '/dynamic/:dynamic_id' }); + this.route('dynamicWithChild', { path: '/dynamic-with-child/:dynamic_id' }, function() { + this.route('child', { path: '/:child_id' }); + }); }); } diff --git a/yarn.lock b/yarn.lock index 00ef93de0d9..634bf9239a4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7385,10 +7385,9 @@ route-recognizer@^0.3.4: resolved "https://registry.yarnpkg.com/route-recognizer/-/route-recognizer-0.3.4.tgz#39ab1ffbce1c59e6d2bdca416f0932611e4f3ca3" integrity sha512-2+MhsfPhvauN1O8KaXpXAOfR/fwe8dnUXVM+xw7yt40lJRfPVQxV6yryZm0cgRvAj5fMF/mdRZbL2ptwbs5i2g== -router_js@^5.0.3: - version "5.0.3" - resolved "https://registry.yarnpkg.com/router_js/-/router_js-5.0.3.tgz#38dda09088f9b2bc764ada05eaab628d829f1c17" - integrity sha512-t7VWv5XPq8XUHcCAVXwKu+LIbwO2VooeKKsqoVjO5PZL0gHDmNwBdAncIvqLVTI3wyIgq05hlHyiRF8L1YxL+w== +router_js@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/router_js/-/router_js-5.1.1.tgz#3a285264132040ea4aa4a6ce2d9b7a05d40176fb" dependencies: "@types/node" "^10.5.5" From d8ee5524036703d377a50fa5709bc4634b90c300 Mon Sep 17 00:00:00 2001 From: Chad Hietala Date: Tue, 2 Oct 2018 16:49:29 -0400 Subject: [PATCH 2/4] add docs and more deprecations --- .../-internals/routing/lib/services/router.ts | 83 ++++++- .../-internals/routing/lib/system/route.ts | 40 ++- .../-internals/routing/lib/system/router.ts | 229 +++++++++--------- .../router_service_test/events_test.js | 88 ++++++- 4 files changed, 313 insertions(+), 127 deletions(-) diff --git a/packages/@ember/-internals/routing/lib/services/router.ts b/packages/@ember/-internals/routing/lib/services/router.ts index d58308a1fcd..869d8c5ba99 100644 --- a/packages/@ember/-internals/routing/lib/services/router.ts +++ b/packages/@ember/-internals/routing/lib/services/router.ts @@ -38,6 +38,79 @@ import { extractRouteArgs, resemblesURL, shallowEqual } from '../utils'; export default class RouterService extends Service { _router!: EmberRouter; + init() { + this._super(...arguments); + this._router.on('routeWillChange', (transition: Transition) => { + this.trigger('routeWillChange', transition); + }); + + this._router.on('routeDidChange', (transition: Transition) => { + this.trigger('routeDidChange', transition); + }); + } + + /** + The `routeWillChange` event is fired at the beginning of any + attempted transition with a `Transition` object as the sole + argument. This action can be used for aborting, redirecting, + or decorating the transition from the currently active routes. + + A good example is preventing navigation when a form is + half-filled out: + + ```app/routes/contact-form.js + import {inject as service} from '@ember/service'; + + export default Route.extend({ + router: service('router'), + init() { + this._super(...arguments); + this.router.on('routeWillUpdate', (transition) => { + if (!transition.to.find(route => route.name === this.routeName)) { + alert("Please save or cancel your changes."); + transition.abort(); + } + }) + } + }); + ``` + + The `routeWillChange` event fires whenever a new route is chosen as the desired target of a transition. This includes `transitionTo`, `replaceWith`, all redirection for any reason including error handling, and abort. Aborting implies changing the desired target back to where you already were. Once a transition has completed, `routeDidChange` fires. + + @event routeWillChange + @param {Transition} transition + @public + */ + + /** + The `routeDidChange` event only fires once a transition has settled. + This includes aborts and error substates. Like the `routeWillChange` event + it recieves a Transition as the sole argument. + + A good example is sending some analytics when the route has transitioned: + + ```app/routes/contact-form.js + import {inject as service} from '@ember/service'; + + export default Route.extend({ + router: service('router'), + init() { + this._super(...arguments); + this.router.on('routeDidUpdate', (transition) => { + ga.send('pageView', { + current: transition.to.name, + from: transition.from.name + }); + }) + } + }); + ``` + + @event routeDidChange + @param {Transition} transition + @public + */ + /** Transition the application into another route. The route may be either a single route or route path: @@ -151,16 +224,6 @@ export default class RouterService extends Service { } RouterService.reopen(Evented, { - init() { - this._super(...arguments); - this._router.on('routeWillChange', (transition: Transition) => { - this.trigger('routeWillChange', transition); - }); - - this._router.on('routeDidChange', (transition: Transition) => { - this.trigger('routeDidChange', transition); - }); - }, /** Name of the current route. diff --git a/packages/@ember/-internals/routing/lib/system/route.ts b/packages/@ember/-internals/routing/lib/system/route.ts index 42d77d3a9f9..fdce44eb433 100644 --- a/packages/@ember/-internals/routing/lib/system/route.ts +++ b/packages/@ember/-internals/routing/lib/system/route.ts @@ -7,8 +7,9 @@ import { Object as EmberObject, typeOf, } from '@ember/-internals/runtime'; +import { EMBER_ROUTING_ROUTER_SERVICE } from '@ember/canary-features'; import { assert, deprecate, info, isTesting } from '@ember/debug'; -import { ROUTER_ROUTER } from '@ember/deprecated-features'; +import { ROUTER_EVENTS, ROUTER_ROUTER } from '@ember/deprecated-features'; import { assign } from '@ember/polyfills'; import { once } from '@ember/runloop'; import { classify } from '@ember/string'; @@ -330,7 +331,7 @@ class Route extends EmberObject implements IRoute { @private @method _internalReset - @since 1.7.0 + @since 3.6.0 */ _internalReset(isExiting: boolean, transition: Transition) { let controller = this.controller; @@ -2511,4 +2512,39 @@ Route.reopen(ActionHandler, Evented, { }, }); +export let ROUTER_EVENT_DEPRECATIONS: any; +if (EMBER_ROUTING_ROUTER_SERVICE && ROUTER_EVENTS) { + ROUTER_EVENT_DEPRECATIONS = { + on(name: string) { + this._super(...arguments); + let hasDidTransition = name === 'didTransition'; + let hasWillTransition = name === 'willTransition'; + + if (hasDidTransition) { + deprecate( + 'You attempted to listen to the "didTransition" event which is deprecated. Please inject the router service and listen to the "routeDidChange" event.', + false, + { + id: 'deprecate-router-events', + until: '4.0.0', + } + ); + } + + if (hasWillTransition) { + deprecate( + 'You attempted to listen to the "willTransition" event which is deprecated. Please inject the router service and listen to the "routeWillChange" event.', + false, + { + id: 'deprecate-router-events', + until: '4.0.0', + } + ); + } + }, + }; + + Route.reopen(ROUTER_EVENT_DEPRECATIONS); +} + export default Route; diff --git a/packages/@ember/-internals/routing/lib/system/router.ts b/packages/@ember/-internals/routing/lib/system/router.ts index 157bc107307..574b9a38b8e 100644 --- a/packages/@ember/-internals/routing/lib/system/router.ts +++ b/packages/@ember/-internals/routing/lib/system/router.ts @@ -11,7 +11,12 @@ import { DEBUG } from '@glimmer/env'; import EmberLocation, { EmberLocation as IEmberLocation } from '../location/api'; import { calculateCacheKey, extractRouteArgs, getActiveTargetName, resemblesURL } from '../utils'; import EmberRouterDSL from './dsl'; -import Route, { defaultSerialize, hasDefaultSerialize, RenderOptions } from './route'; +import Route, { + defaultSerialize, + hasDefaultSerialize, + RenderOptions, + ROUTER_EVENT_DEPRECATIONS, +} from './route'; import RouterState from './router_state'; /** @module @ember/routing @@ -28,6 +33,46 @@ import Router, { } from 'router_js'; import { EngineRouteInfo } from './engines'; +function defaultDidTransition(this: EmberRouter, infos: PrivateRouteInfo[]) { + updatePaths(this); + + this._cancelSlowTransitionTimer(); + + this.notifyPropertyChange('url'); + this.set('currentState', this.targetState); + + // Put this in the runloop so url will be accurate. Seems + // less surprising than didTransition being out of sync. + once(this, this.trigger, 'didTransition'); + + if (DEBUG) { + if (get(this, 'namespace').LOG_TRANSITIONS) { + // eslint-disable-next-line no-console + console.log(`Transitioned into '${EmberRouter._routePath(infos)}'`); + } + } +} + +function defaultWillTransition( + this: EmberRouter, + oldInfos: PrivateRouteInfo[], + newInfos: PrivateRouteInfo[], + transition: Transition +) { + once(this, this.trigger, 'willTransition', transition); + + if (DEBUG) { + if (get(this, 'namespace').LOG_TRANSITIONS) { + // eslint-disable-next-line no-console + console.log( + `Preparing to transition from '${EmberRouter._routePath( + oldInfos + )}' to '${EmberRouter._routePath(newInfos)}'` + ); + } + } +} + if (HANDLER_INFOS) { Object.defineProperty(InternalRouteInfo.prototype, 'handler', { get() { @@ -239,6 +284,18 @@ class EmberRouter extends EmberObject { } didTransition(infos: PrivateRouteInfo[]) { + if (EMBER_ROUTING_ROUTER_SERVICE && ROUTER_EVENTS) { + if (router.didTransition !== defaultDidTransition) { + deprecate( + 'You attempted to override the "didTransition" method which is deprecated. Please inject the router service and listen to the "routeDidChange" event.', + false, + { + id: 'deprecate-router-events', + until: '4.0.0', + } + ); + } + } router.didTransition(infos); } @@ -247,6 +304,18 @@ class EmberRouter extends EmberObject { newInfos: PrivateRouteInfo[], transition: Transition ) { + if (EMBER_ROUTING_ROUTER_SERVICE && ROUTER_EVENTS) { + if (router.willTransition !== defaultWillTransition) { + deprecate( + 'You attempted to override the "willTransition" method which is deprecated. Please inject the router service and listen to the "routeWillChange" event.', + false, + { + id: 'deprecate-router-events', + until: '4.0.0', + } + ); + } + } router.willTransition(oldInfos, newInfos, transition); } @@ -438,57 +507,6 @@ class EmberRouter extends EmberObject { return true; } - /** - Handles updating the paths and notifying any listeners of the URL - change. - - Triggers the router level `didTransition` hook. - - For example, to notify google analytics when the route changes, - you could use this hook. (Note: requires also including GA scripts, etc.) - - ```javascript - import config from './config/environment'; - import EmberRouter from '@ember/routing/router'; - - let Router = EmberRouter.extend({ - location: config.locationType, - - didTransition: function() { - this._super(...arguments); - - return ga('send', 'pageview', { - 'page': this.get('url'), - 'title': this.get('url') - }); - } - }); - ``` - - @method didTransition - @public - @since 1.2.0 - */ - didTransition(infos: PrivateRouteInfo[]) { - updatePaths(this); - - this._cancelSlowTransitionTimer(); - - this.notifyPropertyChange('url'); - this.set('currentState', this.targetState); - - // Put this in the runloop so url will be accurate. Seems - // less surprising than didTransition being out of sync. - once(this, this.trigger, 'didTransition'); - - if (DEBUG) { - if (get(this, 'namespace').LOG_TRANSITIONS) { - // eslint-disable-next-line no-console - console.log(`Transitioned into '${EmberRouter._routePath(infos)}'`); - } - } - } - _setOutlets() { // This is triggered async during Route#willDestroy. // If the router is also being destroyed we do not want to @@ -547,35 +565,6 @@ class EmberRouter extends EmberObject { } } - /** - Handles notifying any listeners of an impending URL - change. - - Triggers the router level `willTransition` hook. - - @method willTransition - @public - @since 1.11.0 - */ - willTransition( - oldInfos: PrivateRouteInfo[], - newInfos: PrivateRouteInfo[], - transition: Transition - ) { - once(this, this.trigger, 'willTransition', transition); - - if (DEBUG) { - if (get(this, 'namespace').LOG_TRANSITIONS) { - // eslint-disable-next-line no-console - console.log( - `Preparing to transition from '${EmberRouter._routePath( - oldInfos - )}' to '${EmberRouter._routePath(newInfos)}'` - ); - } - } - } - handleURL(url: string) { // Until we have an ember-idiomatic way of accessing #hashes, we need to // remove it because router.js doesn't know how to handle it. @@ -1789,6 +1778,50 @@ function representEmptyRoute( } EmberRouter.reopen(Evented, { + /** + Handles updating the paths and notifying any listeners of the URL + change. + + Triggers the router level `didTransition` hook. + + For example, to notify google analytics when the route changes, + you could use this hook. (Note: requires also including GA scripts, etc.) + + ```javascript + import config from './config/environment'; + import EmberRouter from '@ember/routing/router'; + + let Router = EmberRouter.extend({ + location: config.locationType, + + didTransition: function() { + this._super(...arguments); + + return ga('send', 'pageview', { + 'page': this.get('url'), + 'title': this.get('url') + }); + } + }); + ``` + + @method didTransition + @public + @since 1.2.0 + */ + didTransition: defaultDidTransition, + + /** + Handles notifying any listeners of an impending URL + change. + + Triggers the router level `willTransition` hook. + + @method willTransition + @public + @since 1.11.0 + */ + willTransition: defaultWillTransition, /** Represents the URL of the root of the application, often '/'. This prefix is assumed on all routes defined on this router. @@ -1832,37 +1865,7 @@ EmberRouter.reopen(Evented, { }), }); -if (EMBER_ROUTING_ROUTER_SERVICE) { - if (ROUTER_EVENTS) { - EmberRouter.reopen({ - on(name: string) { - this._super(...arguments); - let hasDidTransition = name === 'didTransition'; - let hasWillTransition = name === 'willTransition'; - - if (hasDidTransition) { - deprecate( - 'You attempted to listen to the "didTransition" event which is deprecated. Please inject the router service and listen to the "routeDidChange" event.', - false, - { - id: 'deprecate-router-events', - until: '3.9.0', - } - ); - } - - if (hasWillTransition) { - deprecate( - 'You attempted to listen to the "willTransition" event which is deprecated. Please inject the router service and listen to the "routeWillChange" event.', - false, - { - id: 'deprecate-router-events', - until: '3.9.0', - } - ); - } - }, - }); - } +if (EMBER_ROUTING_ROUTER_SERVICE && ROUTER_EVENTS) { + EmberRouter.reopen(ROUTER_EVENT_DEPRECATIONS); } export default EmberRouter; diff --git a/packages/ember/tests/routing/router_service_test/events_test.js b/packages/ember/tests/routing/router_service_test/events_test.js index d2434178a2c..f3d1f2f7586 100644 --- a/packages/ember/tests/routing/router_service_test/events_test.js +++ b/packages/ember/tests/routing/router_service_test/events_test.js @@ -635,7 +635,7 @@ if (EMBER_ROUTING_ROUTER_SERVICE) { moduleFor( 'Router Service - deprecated events', class extends RouterTestCase { - '@test willTransition is deprecated'() { + '@test willTransition events are deprecated'() { return this.visit('/').then(() => { expectDeprecation(() => { this.routerService['_router'].on('willTransition', () => {}); @@ -643,7 +643,52 @@ if (EMBER_ROUTING_ROUTER_SERVICE) { }); } - '@test didTransition is deprecated'() { + '@test willTransition events are deprecated on routes'() { + this.add( + 'route:application', + Route.extend({ + init() { + this._super(...arguments); + this.on('willTransition', () => {}); + }, + }) + ); + expectDeprecation(() => { + return this.visit('/'); + }, 'You attempted to listen to the "willTransition" event which is deprecated. Please inject the router service and listen to the "routeWillChange" event.'); + } + + '@test didTransition events are deprecated on routes'() { + this.add( + 'route:application', + Route.extend({ + init() { + this._super(...arguments); + this.on('didTransition', () => {}); + }, + }) + ); + expectDeprecation(() => { + return this.visit('/'); + }, 'You attempted to listen to the "didTransition" event which is deprecated. Please inject the router service and listen to the "routeDidChange" event.'); + } + + '@test other events are not deprecated on routes'() { + this.add( + 'route:application', + Route.extend({ + init() { + this._super(...arguments); + this.on('fixx', () => {}); + }, + }) + ); + expectNoDeprecation(() => { + return this.visit('/'); + }); + } + + '@test didTransition events are deprecated'() { return this.visit('/').then(() => { expectDeprecation(() => { this.routerService['_router'].on('didTransition', () => {}); @@ -660,4 +705,43 @@ if (EMBER_ROUTING_ROUTER_SERVICE) { } } ); + + moduleFor( + 'Router Service: deprecated willTransition hook', + class extends RouterTestCase { + get routerOptions() { + return { + willTransition() { + this._super(...arguments); + // Overrides + }, + }; + } + + '@test willTransition hook is deprecated'() { + expectDeprecation(() => { + return this.visit('/'); + }, 'You attempted to override the "willTransition" method which is deprecated. Please inject the router service and listen to the "routeWillChange" event.'); + } + } + ); + moduleFor( + 'Router Service: deprecated didTransition hook', + class extends RouterTestCase { + get routerOptions() { + return { + didTransition() { + this._super(...arguments); + // Overrides + }, + }; + } + + '@test didTransition hook is deprecated'() { + expectDeprecation(() => { + return this.visit('/'); + }, 'You attempted to override the "didTransition" method which is deprecated. Please inject the router service and listen to the "routeDidChange" event.'); + } + } + ); } From 0f95c777fe507940f2922e6d256125677d5bb882 Mon Sep 17 00:00:00 2001 From: Chad Hietala Date: Wed, 3 Oct 2018 09:41:54 -0400 Subject: [PATCH 3/4] Remove old Router Service FF This removes the FF around all the router service features that were go'd earlier this year. It also reuses the same flag to guard the new features that we are adding. --- .../@ember/application/lib/application.js | 7 +- packages/@ember/canary-features/index.ts | 2 +- .../tests/routing/decoupled_basic_test.js | 54 +- .../routing/deprecated_handler_infos_test.js | 8 +- .../routing/router_service_test/basic_test.js | 158 +++-- .../currenturl_lifecycle_test.js | 262 ++++--- .../router_service_test/isActive_test.js | 214 +++--- .../router_service_test/replaceWith_test.js | 270 ++++---- .../router_service_test/transitionTo_test.js | 644 +++++++++--------- .../router_service_test/urlFor_test.js | 474 +++++++------ 10 files changed, 1043 insertions(+), 1050 deletions(-) diff --git a/packages/@ember/application/lib/application.js b/packages/@ember/application/lib/application.js index b7cbe6ea168..2f59927c35f 100644 --- a/packages/@ember/application/lib/application.js +++ b/packages/@ember/application/lib/application.js @@ -30,7 +30,6 @@ import Engine from '@ember/engine'; import { privatize as P } from '@ember/-internals/container'; import { setupApplicationRegistry } from '@ember/-internals/glimmer'; import { RouterService } from '@ember/-internals/routing'; -import { EMBER_ROUTING_ROUTER_SERVICE } from '@ember/canary-features'; let librariesRegistered = false; @@ -1124,10 +1123,8 @@ function commonSetupRegistry(registry) { }, }); - if (EMBER_ROUTING_ROUTER_SERVICE) { - registry.register('service:router', RouterService); - registry.injection('service:router', '_router', 'router:main'); - } + registry.register('service:router', RouterService); + registry.injection('service:router', '_router', 'router:main'); } function registerLibraries() { diff --git a/packages/@ember/canary-features/index.ts b/packages/@ember/canary-features/index.ts index 07627d4a9ed..dc5ee4698b7 100644 --- a/packages/@ember/canary-features/index.ts +++ b/packages/@ember/canary-features/index.ts @@ -10,7 +10,7 @@ export const DEFAULT_FEATURES = { EMBER_LIBRARIES_ISREGISTERED: null, EMBER_IMPROVED_INSTRUMENTATION: null, EMBER_GLIMMER_NAMED_ARGUMENTS: true, - EMBER_ROUTING_ROUTER_SERVICE: true, + EMBER_ROUTING_ROUTER_SERVICE: null, EMBER_ENGINES_MOUNT_PARAMS: true, EMBER_MODULE_UNIFICATION: null, GLIMMER_CUSTOM_COMPONENT_MANAGER: true, diff --git a/packages/ember/tests/routing/decoupled_basic_test.js b/packages/ember/tests/routing/decoupled_basic_test.js index 6b083268997..a65e437f152 100644 --- a/packages/ember/tests/routing/decoupled_basic_test.js +++ b/packages/ember/tests/routing/decoupled_basic_test.js @@ -12,6 +12,7 @@ import { getTextOf } from 'internal-test-helpers'; import { Component } from '@ember/-internals/glimmer'; import Engine from '@ember/engine'; import { InternalTransition as Transition } from 'router_js'; +import { EMBER_ROUTING_ROUTER_SERVICE } from '@ember/canary-features'; let originalConsoleError; @@ -2750,22 +2751,37 @@ moduleFor( ['@test Router `willTransition` hook passes in cancellable transition'](assert) { // Should hit willTransition 3 times, once for the initial route, and then 2 more times // for the two handleURL calls below - assert.expect(5); + if (EMBER_ROUTING_ROUTER_SERVICE) { + assert.expect(7); + + this.router.reopen({ + init() { + this._super(...arguments); + this.on('routeWillChange', transition => { + assert.ok(true, 'routeWillChange was called'); + if (transition.intent && transition.intent.url !== '/') { + transition.abort(); + } + }); + }, + }); + } else { + assert.expect(5); + this.router.reopen({ + willTransition(_, _2, transition) { + assert.ok(true, 'willTransition was called'); + if (transition.intent.url !== '/') { + transition.abort(); + } + }, + }); + } this.router.map(function() { this.route('nork'); this.route('about'); }); - this.router.reopen({ - willTransition(_, _2, transition) { - assert.ok(true, 'willTransition was called'); - if (transition.intent.url !== '/') { - transition.abort(); - } - }, - }); - this.add( 'route:loading', Route.extend({ @@ -2922,13 +2938,17 @@ moduleFor( this.router.map(function() { this.route('nork'); }); - - this.router.reopen({ - didTransition() { - this._super(...arguments); - assert.ok(true, 'reopened didTransition was called'); - }, - }); + if (EMBER_ROUTING_ROUTER_SERVICE) { + assert.ok(true, 'no longer a valid test'); + return; + } else { + this.router.reopen({ + didTransition() { + this._super(...arguments); + assert.ok(true, 'reopened didTransition was called'); + }, + }); + } return this.visit('/'); } diff --git a/packages/ember/tests/routing/deprecated_handler_infos_test.js b/packages/ember/tests/routing/deprecated_handler_infos_test.js index 8bbc61121da..a650619edd6 100644 --- a/packages/ember/tests/routing/deprecated_handler_infos_test.js +++ b/packages/ember/tests/routing/deprecated_handler_infos_test.js @@ -60,9 +60,11 @@ if (EMBER_ROUTING_ROUTER_SERVICE) { '@test handlerInfos are deprecated and associated private apis'(assert) { let done = assert.async(); - return this.visit('/parent').then(() => { - done(); - }); + expectDeprecation(() => { + return this.visit('/parent').then(() => { + done(); + }); + }, /You attempted to override the \"(willTransition|didTransition)\" method which is deprecated. Please inject the router service and listen to the \"(routeWillChange|routeDidChange)\" event\./); } } ); diff --git a/packages/ember/tests/routing/router_service_test/basic_test.js b/packages/ember/tests/routing/router_service_test/basic_test.js index d1e05a1a710..1fa50a116ae 100644 --- a/packages/ember/tests/routing/router_service_test/basic_test.js +++ b/packages/ember/tests/routing/router_service_test/basic_test.js @@ -2,94 +2,90 @@ import { Route, NoneLocation } from '@ember/-internals/routing'; import { set } from '@ember/-internals/metal'; import { RouterTestCase, moduleFor } from 'internal-test-helpers'; -import { EMBER_ROUTING_ROUTER_SERVICE } from '@ember/canary-features'; +moduleFor( + 'Router Service - main', + class extends RouterTestCase { + ['@test RouterService#currentRouteName is correctly set for top level route'](assert) { + assert.expect(1); + + return this.visit('/').then(() => { + assert.equal(this.routerService.get('currentRouteName'), 'parent.index'); + }); + } + + ['@test RouterService#currentRouteName is correctly set for child route'](assert) { + assert.expect(1); + + return this.visit('/child').then(() => { + assert.equal(this.routerService.get('currentRouteName'), 'parent.child'); + }); + } -if (EMBER_ROUTING_ROUTER_SERVICE) { - moduleFor( - 'Router Service - main', - class extends RouterTestCase { - ['@test RouterService#currentRouteName is correctly set for top level route'](assert) { - assert.expect(1); + ['@test RouterService#currentRouteName is correctly set after transition'](assert) { + assert.expect(1); - return this.visit('/').then(() => { - assert.equal(this.routerService.get('currentRouteName'), 'parent.index'); + return this.visit('/child') + .then(() => { + return this.routerService.transitionTo('parent.sister'); + }) + .then(() => { + assert.equal(this.routerService.get('currentRouteName'), 'parent.sister'); }); - } + } - ['@test RouterService#currentRouteName is correctly set for child route'](assert) { - assert.expect(1); + ['@test RouterService#currentRouteName is correctly set on each transition'](assert) { + assert.expect(3); - return this.visit('/child').then(() => { + return this.visit('/child') + .then(() => { assert.equal(this.routerService.get('currentRouteName'), 'parent.child'); - }); - } - - ['@test RouterService#currentRouteName is correctly set after transition'](assert) { - assert.expect(1); - - return this.visit('/child') - .then(() => { - return this.routerService.transitionTo('parent.sister'); - }) - .then(() => { - assert.equal(this.routerService.get('currentRouteName'), 'parent.sister'); - }); - } - - ['@test RouterService#currentRouteName is correctly set on each transition'](assert) { - assert.expect(3); - - return this.visit('/child') - .then(() => { - assert.equal(this.routerService.get('currentRouteName'), 'parent.child'); - - return this.visit('/sister'); - }) - .then(() => { - assert.equal(this.routerService.get('currentRouteName'), 'parent.sister'); - - return this.visit('/brother'); - }) - .then(() => { - assert.equal(this.routerService.get('currentRouteName'), 'parent.brother'); - }); - } - - ['@test RouterService#rootURL is correctly set to the default value'](assert) { - assert.expect(1); - - return this.visit('/').then(() => { - assert.equal(this.routerService.get('rootURL'), '/'); - }); - } - - ['@test RouterService#rootURL is correctly set to a custom value'](assert) { - assert.expect(1); - - this.add( - 'route:parent.index', - Route.extend({ - init() { - this._super(); - set(this._router, 'rootURL', '/homepage'); - }, - }) - ); - - return this.visit('/').then(() => { - assert.equal(this.routerService.get('rootURL'), '/homepage'); - }); - } - ['@test RouterService#location is correctly delegated from router:main'](assert) { - assert.expect(2); + return this.visit('/sister'); + }) + .then(() => { + assert.equal(this.routerService.get('currentRouteName'), 'parent.sister'); - return this.visit('/').then(() => { - let location = this.routerService.get('location'); - assert.ok(location); - assert.ok(location instanceof NoneLocation); + return this.visit('/brother'); + }) + .then(() => { + assert.equal(this.routerService.get('currentRouteName'), 'parent.brother'); }); - } } - ); -} + + ['@test RouterService#rootURL is correctly set to the default value'](assert) { + assert.expect(1); + + return this.visit('/').then(() => { + assert.equal(this.routerService.get('rootURL'), '/'); + }); + } + + ['@test RouterService#rootURL is correctly set to a custom value'](assert) { + assert.expect(1); + + this.add( + 'route:parent.index', + Route.extend({ + init() { + this._super(); + set(this._router, 'rootURL', '/homepage'); + }, + }) + ); + + return this.visit('/').then(() => { + assert.equal(this.routerService.get('rootURL'), '/homepage'); + }); + } + + ['@test RouterService#location is correctly delegated from router:main'](assert) { + assert.expect(2); + + return this.visit('/').then(() => { + let location = this.routerService.get('location'); + assert.ok(location); + assert.ok(location instanceof NoneLocation); + }); + } + } +); diff --git a/packages/ember/tests/routing/router_service_test/currenturl_lifecycle_test.js b/packages/ember/tests/routing/router_service_test/currenturl_lifecycle_test.js index 89acdb19b9b..5fa5a355d60 100644 --- a/packages/ember/tests/routing/router_service_test/currenturl_lifecycle_test.js +++ b/packages/ember/tests/routing/router_service_test/currenturl_lifecycle_test.js @@ -5,145 +5,141 @@ import { Route } from '@ember/-internals/routing'; import { get } from '@ember/-internals/metal'; import { RouterTestCase, moduleFor } from 'internal-test-helpers'; -import { EMBER_ROUTING_ROUTER_SERVICE } from '@ember/canary-features'; - -if (EMBER_ROUTING_ROUTER_SERVICE) { - let results = []; - let ROUTE_NAMES = ['index', 'child', 'sister', 'brother']; - - let InstrumentedRoute = Route.extend({ - routerService: injectService('router'), - - beforeModel() { - let service = get(this, 'routerService'); - results.push([service.get('currentRouteName'), 'beforeModel', service.get('currentURL')]); - }, - - model() { - let service = get(this, 'routerService'); - results.push([service.get('currentRouteName'), 'model', service.get('currentURL')]); - }, - - afterModel() { - let service = get(this, 'routerService'); - results.push([service.get('currentRouteName'), 'afterModel', service.get('currentURL')]); - }, - }); - - moduleFor( - 'Router Service - currentURL', - class extends RouterTestCase { - constructor() { - super(...arguments); - - results = []; - - ROUTE_NAMES.forEach(name => { - let routeName = `parent.${name}`; - this.add(`route:${routeName}`, InstrumentedRoute.extend()); - this.addTemplate(routeName, '{{current-url}}'); - }); +let results = []; +let ROUTE_NAMES = ['index', 'child', 'sister', 'brother']; + +let InstrumentedRoute = Route.extend({ + routerService: injectService('router'), + + beforeModel() { + let service = get(this, 'routerService'); + results.push([service.get('currentRouteName'), 'beforeModel', service.get('currentURL')]); + }, + + model() { + let service = get(this, 'routerService'); + results.push([service.get('currentRouteName'), 'model', service.get('currentURL')]); + }, + + afterModel() { + let service = get(this, 'routerService'); + results.push([service.get('currentRouteName'), 'afterModel', service.get('currentURL')]); + }, +}); + +moduleFor( + 'Router Service - currentURL', + class extends RouterTestCase { + constructor() { + super(...arguments); + + results = []; + + ROUTE_NAMES.forEach(name => { + let routeName = `parent.${name}`; + this.add(`route:${routeName}`, InstrumentedRoute.extend()); + this.addTemplate(routeName, '{{current-url}}'); + }); + + this.addComponent('current-url', { + ComponentClass: Component.extend({ + routerService: injectService('router'), + currentURL: readOnly('routerService.currentURL'), + }), + template: '{{currentURL}}', + }); + } - this.addComponent('current-url', { - ComponentClass: Component.extend({ - routerService: injectService('router'), - currentURL: readOnly('routerService.currentURL'), - }), - template: '{{currentURL}}', - }); - } + ['@test RouterService#currentURL is correctly set for top level route'](assert) { + assert.expect(1); + + return this.visit('/').then(() => { + assert.equal(this.routerService.get('currentURL'), '/'); + }); + } - ['@test RouterService#currentURL is correctly set for top level route'](assert) { - assert.expect(1); + ['@test RouterService#currentURL is correctly set for child route'](assert) { + assert.expect(1); - return this.visit('/').then(() => { - assert.equal(this.routerService.get('currentURL'), '/'); + return this.visit('/child').then(() => { + assert.equal(this.routerService.get('currentURL'), '/child'); + }); + } + + ['@test RouterService#currentURL is correctly set after transition'](assert) { + assert.expect(1); + + return this.visit('/child') + .then(() => { + return this.routerService.transitionTo('parent.sister'); + }) + .then(() => { + assert.equal(this.routerService.get('currentURL'), '/sister'); }); - } + } - ['@test RouterService#currentURL is correctly set for child route'](assert) { - assert.expect(1); + ['@test RouterService#currentURL is correctly set on each transition'](assert) { + assert.expect(3); - return this.visit('/child').then(() => { + return this.visit('/child') + .then(() => { assert.equal(this.routerService.get('currentURL'), '/child'); + + return this.visit('/sister'); + }) + .then(() => { + assert.equal(this.routerService.get('currentURL'), '/sister'); + + return this.visit('/brother'); + }) + .then(() => { + assert.equal(this.routerService.get('currentURL'), '/brother'); + }); + } + + ['@test RouterService#currentURL is not set during lifecycle hooks'](assert) { + assert.expect(2); + + return this.visit('/') + .then(() => { + assert.deepEqual(results, [ + [null, 'beforeModel', null], + [null, 'model', null], + [null, 'afterModel', null], + ]); + + results = []; + + return this.visit('/child'); + }) + .then(() => { + assert.deepEqual(results, [ + ['parent.index', 'beforeModel', '/'], + ['parent.index', 'model', '/'], + ['parent.index', 'afterModel', '/'], + ]); + }); + } + + ['@test RouterService#currentURL is correctly set with component after consecutive visits']( + assert + ) { + assert.expect(3); + + return this.visit('/') + .then(() => { + this.assertText('/'); + + return this.visit('/child'); + }) + .then(() => { + this.assertText('/child'); + + return this.visit('/'); + }) + .then(() => { + this.assertText('/'); }); - } - - ['@test RouterService#currentURL is correctly set after transition'](assert) { - assert.expect(1); - - return this.visit('/child') - .then(() => { - return this.routerService.transitionTo('parent.sister'); - }) - .then(() => { - assert.equal(this.routerService.get('currentURL'), '/sister'); - }); - } - - ['@test RouterService#currentURL is correctly set on each transition'](assert) { - assert.expect(3); - - return this.visit('/child') - .then(() => { - assert.equal(this.routerService.get('currentURL'), '/child'); - - return this.visit('/sister'); - }) - .then(() => { - assert.equal(this.routerService.get('currentURL'), '/sister'); - - return this.visit('/brother'); - }) - .then(() => { - assert.equal(this.routerService.get('currentURL'), '/brother'); - }); - } - - ['@test RouterService#currentURL is not set during lifecycle hooks'](assert) { - assert.expect(2); - - return this.visit('/') - .then(() => { - assert.deepEqual(results, [ - [null, 'beforeModel', null], - [null, 'model', null], - [null, 'afterModel', null], - ]); - - results = []; - - return this.visit('/child'); - }) - .then(() => { - assert.deepEqual(results, [ - ['parent.index', 'beforeModel', '/'], - ['parent.index', 'model', '/'], - ['parent.index', 'afterModel', '/'], - ]); - }); - } - - ['@test RouterService#currentURL is correctly set with component after consecutive visits']( - assert - ) { - assert.expect(3); - - return this.visit('/') - .then(() => { - this.assertText('/'); - - return this.visit('/child'); - }) - .then(() => { - this.assertText('/child'); - - return this.visit('/'); - }) - .then(() => { - this.assertText('/'); - }); - } } - ); -} + } +); diff --git a/packages/ember/tests/routing/router_service_test/isActive_test.js b/packages/ember/tests/routing/router_service_test/isActive_test.js index 1b1c203dde3..0fec62fcaa2 100644 --- a/packages/ember/tests/routing/router_service_test/isActive_test.js +++ b/packages/ember/tests/routing/router_service_test/isActive_test.js @@ -1,113 +1,109 @@ import Controller from '@ember/controller'; import { RouterTestCase, moduleFor } from 'internal-test-helpers'; -import { EMBER_ROUTING_ROUTER_SERVICE } from '@ember/canary-features'; - -if (EMBER_ROUTING_ROUTER_SERVICE) { - moduleFor( - 'Router Service - isActive', - class extends RouterTestCase { - ['@test RouterService#isActive returns true for simple route'](assert) { - assert.expect(1); - - return this.visit('/') - .then(() => { - return this.routerService.transitionTo('parent.child'); - }) - .then(() => { - return this.routerService.transitionTo('parent.sister'); - }) - .then(() => { - assert.ok(this.routerService.isActive('parent.sister')); - }); - } - - ['@test RouterService#isActive returns true for simple route with dynamic segments'](assert) { - assert.expect(1); - - let dynamicModel = { id: 1 }; - - return this.visit('/') - .then(() => { - return this.routerService.transitionTo('dynamic', dynamicModel); - }) - .then(() => { - assert.ok(this.routerService.isActive('dynamic', dynamicModel)); - }); - } - - ['@test RouterService#isActive does not eagerly instantiate controller for query params']( - assert - ) { - assert.expect(1); - - let queryParams = this.buildQueryParams({ sort: 'ASC' }); - - this.add( - 'controller:parent.sister', - Controller.extend({ - queryParams: ['sort'], - sort: 'ASC', - - init() { - assert.ok(false, 'should never create'); - this._super(...arguments); - }, - }) - ); - - return this.visit('/') - .then(() => { - return this.routerService.transitionTo('parent.brother'); - }) - .then(() => { - assert.notOk(this.routerService.isActive('parent.sister', queryParams)); - }); - } - - ['@test RouterService#isActive is correct for simple route with basic query params'](assert) { - assert.expect(2); - - let queryParams = this.buildQueryParams({ sort: 'ASC' }); - - this.add( - 'controller:parent.child', - Controller.extend({ - queryParams: ['sort'], - sort: 'ASC', - }) - ); - - return this.visit('/') - .then(() => { - return this.routerService.transitionTo('parent.child', queryParams); - }) - .then(() => { - assert.ok(this.routerService.isActive('parent.child', queryParams)); - assert.notOk( - this.routerService.isActive('parent.child', this.buildQueryParams({ sort: 'DESC' })) - ); - }); - } - - ['@test RouterService#isActive for simple route with array as query params'](assert) { - assert.expect(1); - - let queryParams = this.buildQueryParams({ sort: ['ascending'] }); - - return this.visit('/') - .then(() => { - return this.routerService.transitionTo('parent.child', queryParams); - }) - .then(() => { - assert.notOk( - this.routerService.isActive( - 'parent.child', - this.buildQueryParams({ sort: 'descending' }) - ) - ); - }); - } +moduleFor( + 'Router Service - isActive', + class extends RouterTestCase { + ['@test RouterService#isActive returns true for simple route'](assert) { + assert.expect(1); + + return this.visit('/') + .then(() => { + return this.routerService.transitionTo('parent.child'); + }) + .then(() => { + return this.routerService.transitionTo('parent.sister'); + }) + .then(() => { + assert.ok(this.routerService.isActive('parent.sister')); + }); } - ); -} + + ['@test RouterService#isActive returns true for simple route with dynamic segments'](assert) { + assert.expect(1); + + let dynamicModel = { id: 1 }; + + return this.visit('/') + .then(() => { + return this.routerService.transitionTo('dynamic', dynamicModel); + }) + .then(() => { + assert.ok(this.routerService.isActive('dynamic', dynamicModel)); + }); + } + + ['@test RouterService#isActive does not eagerly instantiate controller for query params']( + assert + ) { + assert.expect(1); + + let queryParams = this.buildQueryParams({ sort: 'ASC' }); + + this.add( + 'controller:parent.sister', + Controller.extend({ + queryParams: ['sort'], + sort: 'ASC', + + init() { + assert.ok(false, 'should never create'); + this._super(...arguments); + }, + }) + ); + + return this.visit('/') + .then(() => { + return this.routerService.transitionTo('parent.brother'); + }) + .then(() => { + assert.notOk(this.routerService.isActive('parent.sister', queryParams)); + }); + } + + ['@test RouterService#isActive is correct for simple route with basic query params'](assert) { + assert.expect(2); + + let queryParams = this.buildQueryParams({ sort: 'ASC' }); + + this.add( + 'controller:parent.child', + Controller.extend({ + queryParams: ['sort'], + sort: 'ASC', + }) + ); + + return this.visit('/') + .then(() => { + return this.routerService.transitionTo('parent.child', queryParams); + }) + .then(() => { + assert.ok(this.routerService.isActive('parent.child', queryParams)); + assert.notOk( + this.routerService.isActive('parent.child', this.buildQueryParams({ sort: 'DESC' })) + ); + }); + } + + ['@test RouterService#isActive for simple route with array as query params'](assert) { + assert.expect(1); + + let queryParams = this.buildQueryParams({ sort: ['ascending'] }); + + return this.visit('/') + .then(() => { + return this.routerService.transitionTo('parent.child', queryParams); + }) + .then(() => { + assert.notOk( + this.routerService.isActive( + 'parent.child', + this.buildQueryParams({ sort: 'descending' }) + ) + ); + }); + } + } +); diff --git a/packages/ember/tests/routing/router_service_test/replaceWith_test.js b/packages/ember/tests/routing/router_service_test/replaceWith_test.js index 8589b431644..a1ca0bca866 100644 --- a/packages/ember/tests/routing/router_service_test/replaceWith_test.js +++ b/packages/ember/tests/routing/router_service_test/replaceWith_test.js @@ -3,142 +3,138 @@ import { RouterTestCase, moduleFor } from 'internal-test-helpers'; import { InternalTransition as Transition } from 'router_js'; import Controller from '@ember/controller'; -import { EMBER_ROUTING_ROUTER_SERVICE } from '@ember/canary-features'; - -if (EMBER_ROUTING_ROUTER_SERVICE) { - moduleFor( - 'Router Service - replaceWith', - class extends RouterTestCase { - constructor() { - super(...arguments); - - let testCase = this; - testCase.state = []; - - this.add( - 'location:test', - NoneLocation.extend({ - setURL(path) { - testCase.state.push(path); - this.set('path', path); - }, - - replaceURL(path) { - testCase.state.splice(testCase.state.length - 1, 1, path); - this.set('path', path); - }, - }) - ); - } - - get routerOptions() { - return { - location: 'test', - }; - } - - ['@test RouterService#replaceWith returns a Transition'](assert) { - assert.expect(1); - - let transition; - - return this.visit('/').then(() => { - transition = this.routerService.replaceWith('parent.child'); - - assert.ok(transition instanceof Transition); - - return transition; +moduleFor( + 'Router Service - replaceWith', + class extends RouterTestCase { + constructor() { + super(...arguments); + + let testCase = this; + testCase.state = []; + + this.add( + 'location:test', + NoneLocation.extend({ + setURL(path) { + testCase.state.push(path); + this.set('path', path); + }, + + replaceURL(path) { + testCase.state.splice(testCase.state.length - 1, 1, path); + this.set('path', path); + }, + }) + ); + } + + get routerOptions() { + return { + location: 'test', + }; + } + + ['@test RouterService#replaceWith returns a Transition'](assert) { + assert.expect(1); + + let transition; + + return this.visit('/').then(() => { + transition = this.routerService.replaceWith('parent.child'); + + assert.ok(transition instanceof Transition); + + return transition; + }); + } + + ['@test RouterService#replaceWith with basic route replaces location'](assert) { + assert.expect(1); + + return this.visit('/') + .then(() => { + return this.routerService.transitionTo('parent.child'); + }) + .then(() => { + return this.routerService.transitionTo('parent.sister'); + }) + .then(() => { + return this.routerService.replaceWith('parent.brother'); + }) + .then(() => { + assert.deepEqual(this.state, ['/', '/child', '/brother']); + }); + } + + ['@test RouterService#replaceWith with basic route using URLs replaces location'](assert) { + assert.expect(1); + + return this.visit('/') + .then(() => { + return this.routerService.transitionTo('/child'); + }) + .then(() => { + return this.routerService.transitionTo('/sister'); + }) + .then(() => { + return this.routerService.replaceWith('/brother'); + }) + .then(() => { + assert.deepEqual(this.state, ['/', '/child', '/brother']); + }); + } + + ['@test RouterService#replaceWith transitioning back to previously visited route replaces location']( + assert + ) { + assert.expect(1); + + return this.visit('/') + .then(() => { + return this.routerService.transitionTo('parent.child'); + }) + .then(() => { + return this.routerService.transitionTo('parent.sister'); + }) + .then(() => { + return this.routerService.transitionTo('parent.brother'); + }) + .then(() => { + return this.routerService.replaceWith('parent.sister'); + }) + .then(() => { + assert.deepEqual(this.state, ['/', '/child', '/sister', '/sister']); + }); + } + + ['@test RouterService#replaceWith with basic query params does not remove query param defaults']( + assert + ) { + assert.expect(1); + + this.add( + 'controller:parent.child', + Controller.extend({ + queryParams: ['sort'], + sort: 'ASC', + }) + ); + + let queryParams = this.buildQueryParams({ sort: 'ASC' }); + + return this.visit('/') + .then(() => { + return this.routerService.transitionTo('parent.brother'); + }) + .then(() => { + return this.routerService.replaceWith('parent.sister'); + }) + .then(() => { + return this.routerService.replaceWith('parent.child', queryParams); + }) + .then(() => { + assert.deepEqual(this.state, ['/', '/child?sort=ASC']); }); - } - - ['@test RouterService#replaceWith with basic route replaces location'](assert) { - assert.expect(1); - - return this.visit('/') - .then(() => { - return this.routerService.transitionTo('parent.child'); - }) - .then(() => { - return this.routerService.transitionTo('parent.sister'); - }) - .then(() => { - return this.routerService.replaceWith('parent.brother'); - }) - .then(() => { - assert.deepEqual(this.state, ['/', '/child', '/brother']); - }); - } - - ['@test RouterService#replaceWith with basic route using URLs replaces location'](assert) { - assert.expect(1); - - return this.visit('/') - .then(() => { - return this.routerService.transitionTo('/child'); - }) - .then(() => { - return this.routerService.transitionTo('/sister'); - }) - .then(() => { - return this.routerService.replaceWith('/brother'); - }) - .then(() => { - assert.deepEqual(this.state, ['/', '/child', '/brother']); - }); - } - - ['@test RouterService#replaceWith transitioning back to previously visited route replaces location']( - assert - ) { - assert.expect(1); - - return this.visit('/') - .then(() => { - return this.routerService.transitionTo('parent.child'); - }) - .then(() => { - return this.routerService.transitionTo('parent.sister'); - }) - .then(() => { - return this.routerService.transitionTo('parent.brother'); - }) - .then(() => { - return this.routerService.replaceWith('parent.sister'); - }) - .then(() => { - assert.deepEqual(this.state, ['/', '/child', '/sister', '/sister']); - }); - } - - ['@test RouterService#replaceWith with basic query params does not remove query param defaults']( - assert - ) { - assert.expect(1); - - this.add( - 'controller:parent.child', - Controller.extend({ - queryParams: ['sort'], - sort: 'ASC', - }) - ); - - let queryParams = this.buildQueryParams({ sort: 'ASC' }); - - return this.visit('/') - .then(() => { - return this.routerService.transitionTo('parent.brother'); - }) - .then(() => { - return this.routerService.replaceWith('parent.sister'); - }) - .then(() => { - return this.routerService.replaceWith('parent.child', queryParams); - }) - .then(() => { - assert.deepEqual(this.state, ['/', '/child?sort=ASC']); - }); - } } - ); -} + } +); diff --git a/packages/ember/tests/routing/router_service_test/transitionTo_test.js b/packages/ember/tests/routing/router_service_test/transitionTo_test.js index 2a023d8530c..4be05d10a45 100644 --- a/packages/ember/tests/routing/router_service_test/transitionTo_test.js +++ b/packages/ember/tests/routing/router_service_test/transitionTo_test.js @@ -7,364 +7,360 @@ import { get } from '@ember/-internals/metal'; import { RouterTestCase, moduleFor } from 'internal-test-helpers'; import { InternalTransition as Transition } from 'router_js'; -import { EMBER_ROUTING_ROUTER_SERVICE } from '@ember/canary-features'; - -if (EMBER_ROUTING_ROUTER_SERVICE) { - moduleFor( - 'Router Service - transitionTo', - class extends RouterTestCase { - constructor() { - super(...arguments); - - let testCase = this; - testCase.state = []; - - this.add( - 'location:test', - NoneLocation.extend({ - setURL(path) { - testCase.state.push(path); - this.set('path', path); - }, +moduleFor( + 'Router Service - transitionTo', + class extends RouterTestCase { + constructor() { + super(...arguments); + + let testCase = this; + testCase.state = []; + + this.add( + 'location:test', + NoneLocation.extend({ + setURL(path) { + testCase.state.push(path); + this.set('path', path); + }, + + replaceURL(path) { + testCase.state.splice(testCase.state.length - 1, 1, path); + this.set('path', path); + }, + }) + ); + } - replaceURL(path) { - testCase.state.splice(testCase.state.length - 1, 1, path); - this.set('path', path); - }, - }) - ); - } + get routerOptions() { + return { + location: 'test', + }; + } - get routerOptions() { - return { - location: 'test', - }; - } + ['@test RouterService#transitionTo returns a Transition'](assert) { + assert.expect(1); - ['@test RouterService#transitionTo returns a Transition'](assert) { - assert.expect(1); + let transition; - let transition; + return this.visit('/').then(() => { + transition = this.routerService.transitionTo('parent.child'); - return this.visit('/').then(() => { - transition = this.routerService.transitionTo('parent.child'); + assert.ok(transition instanceof Transition); - assert.ok(transition instanceof Transition); + return transition; + }); + } - return transition; + ['@test RouterService#transitionTo with basic route updates location'](assert) { + assert.expect(1); + + return this.visit('/') + .then(() => { + return this.routerService.transitionTo('parent.child'); + }) + .then(() => { + return this.routerService.transitionTo('parent.sister'); + }) + .then(() => { + return this.routerService.transitionTo('parent.brother'); + }) + .then(() => { + assert.deepEqual(this.state, ['/', '/child', '/sister', '/brother']); }); - } - - ['@test RouterService#transitionTo with basic route updates location'](assert) { - assert.expect(1); - - return this.visit('/') - .then(() => { - return this.routerService.transitionTo('parent.child'); - }) - .then(() => { - return this.routerService.transitionTo('parent.sister'); - }) - .then(() => { - return this.routerService.transitionTo('parent.brother'); - }) - .then(() => { - assert.deepEqual(this.state, ['/', '/child', '/sister', '/brother']); - }); - } - - ['@test RouterService#transitionTo transitioning back to previously visited route updates location']( - assert - ) { - assert.expect(1); - - return this.visit('/') - .then(() => { - return this.routerService.transitionTo('parent.child'); - }) - .then(() => { - return this.routerService.transitionTo('parent.sister'); - }) - .then(() => { - return this.routerService.transitionTo('parent.brother'); - }) - .then(() => { - return this.routerService.transitionTo('parent.sister'); - }) - .then(() => { - assert.deepEqual(this.state, ['/', '/child', '/sister', '/brother', '/sister']); - }); - } - - ['@test RouterService#transitionTo with basic route'](assert) { - assert.expect(1); - - let componentInstance; - - this.addTemplate('parent.index', '{{foo-bar}}'); - - this.addComponent('foo-bar', { - ComponentClass: Component.extend({ - routerService: injectService('router'), - init() { - this._super(); - componentInstance = this; - }, - actions: { - transitionToSister() { - get(this, 'routerService').transitionTo('parent.sister'); - }, - }, - }), - template: `foo-bar`, - }); - - return this.visit('/').then(() => { - run(function() { - componentInstance.send('transitionToSister'); - }); + } - assert.equal(this.routerService.get('currentRouteName'), 'parent.sister'); + ['@test RouterService#transitionTo transitioning back to previously visited route updates location']( + assert + ) { + assert.expect(1); + + return this.visit('/') + .then(() => { + return this.routerService.transitionTo('parent.child'); + }) + .then(() => { + return this.routerService.transitionTo('parent.sister'); + }) + .then(() => { + return this.routerService.transitionTo('parent.brother'); + }) + .then(() => { + return this.routerService.transitionTo('parent.sister'); + }) + .then(() => { + assert.deepEqual(this.state, ['/', '/child', '/sister', '/brother', '/sister']); }); - } + } - ['@test RouterService#transitionTo with basic route using URL'](assert) { - assert.expect(1); + ['@test RouterService#transitionTo with basic route'](assert) { + assert.expect(1); - let componentInstance; + let componentInstance; - this.addTemplate('parent.index', '{{foo-bar}}'); + this.addTemplate('parent.index', '{{foo-bar}}'); - this.addComponent('foo-bar', { - ComponentClass: Component.extend({ - routerService: injectService('router'), - init() { - this._super(); - componentInstance = this; + this.addComponent('foo-bar', { + ComponentClass: Component.extend({ + routerService: injectService('router'), + init() { + this._super(); + componentInstance = this; + }, + actions: { + transitionToSister() { + get(this, 'routerService').transitionTo('parent.sister'); }, - actions: { - transitionToSister() { - get(this, 'routerService').transitionTo('/sister'); - }, - }, - }), - template: `foo-bar`, + }, + }), + template: `foo-bar`, + }); + + return this.visit('/').then(() => { + run(function() { + componentInstance.send('transitionToSister'); }); - return this.visit('/').then(() => { - run(function() { - componentInstance.send('transitionToSister'); - }); - - assert.equal(this.routerService.get('currentRouteName'), 'parent.sister'); - }); - } + assert.equal(this.routerService.get('currentRouteName'), 'parent.sister'); + }); + } - ['@test RouterService#transitionTo with dynamic segment'](assert) { - assert.expect(3); + ['@test RouterService#transitionTo with basic route using URL'](assert) { + assert.expect(1); - let componentInstance; - let dynamicModel = { id: 1, contents: 'much dynamicism' }; + let componentInstance; - this.addTemplate('parent.index', '{{foo-bar}}'); - this.addTemplate('dynamic', '{{model.contents}}'); + this.addTemplate('parent.index', '{{foo-bar}}'); - this.addComponent('foo-bar', { - ComponentClass: Component.extend({ - routerService: injectService('router'), - init() { - this._super(); - componentInstance = this; + this.addComponent('foo-bar', { + ComponentClass: Component.extend({ + routerService: injectService('router'), + init() { + this._super(); + componentInstance = this; + }, + actions: { + transitionToSister() { + get(this, 'routerService').transitionTo('/sister'); }, - actions: { - transitionToDynamic() { - get(this, 'routerService').transitionTo('dynamic', dynamicModel); - }, - }, - }), - template: `foo-bar`, + }, + }), + template: `foo-bar`, + }); + + return this.visit('/').then(() => { + run(function() { + componentInstance.send('transitionToSister'); }); - return this.visit('/').then(() => { - run(function() { - componentInstance.send('transitionToDynamic'); - }); + assert.equal(this.routerService.get('currentRouteName'), 'parent.sister'); + }); + } - assert.equal(this.routerService.get('currentRouteName'), 'dynamic'); - assert.equal(this.routerService.get('currentURL'), '/dynamic/1'); - this.assertText('much dynamicism'); + ['@test RouterService#transitionTo with dynamic segment'](assert) { + assert.expect(3); + + let componentInstance; + let dynamicModel = { id: 1, contents: 'much dynamicism' }; + + this.addTemplate('parent.index', '{{foo-bar}}'); + this.addTemplate('dynamic', '{{model.contents}}'); + + this.addComponent('foo-bar', { + ComponentClass: Component.extend({ + routerService: injectService('router'), + init() { + this._super(); + componentInstance = this; + }, + actions: { + transitionToDynamic() { + get(this, 'routerService').transitionTo('dynamic', dynamicModel); + }, + }, + }), + template: `foo-bar`, + }); + + return this.visit('/').then(() => { + run(function() { + componentInstance.send('transitionToDynamic'); }); - } - - ['@test RouterService#transitionTo with dynamic segment and model hook'](assert) { - assert.expect(3); - let componentInstance; - let dynamicModel = { id: 1, contents: 'much dynamicism' }; + assert.equal(this.routerService.get('currentRouteName'), 'dynamic'); + assert.equal(this.routerService.get('currentURL'), '/dynamic/1'); + this.assertText('much dynamicism'); + }); + } - this.add( - 'route:dynamic', - Route.extend({ - model() { - return dynamicModel; - }, - }) - ); - - this.addTemplate('parent.index', '{{foo-bar}}'); - this.addTemplate('dynamic', '{{model.contents}}'); - - this.addComponent('foo-bar', { - ComponentClass: Component.extend({ - routerService: injectService('router'), - init() { - this._super(); - componentInstance = this; + ['@test RouterService#transitionTo with dynamic segment and model hook'](assert) { + assert.expect(3); + + let componentInstance; + let dynamicModel = { id: 1, contents: 'much dynamicism' }; + + this.add( + 'route:dynamic', + Route.extend({ + model() { + return dynamicModel; + }, + }) + ); + + this.addTemplate('parent.index', '{{foo-bar}}'); + this.addTemplate('dynamic', '{{model.contents}}'); + + this.addComponent('foo-bar', { + ComponentClass: Component.extend({ + routerService: injectService('router'), + init() { + this._super(); + componentInstance = this; + }, + actions: { + transitionToDynamic() { + get(this, 'routerService').transitionTo('dynamic', 1); }, - actions: { - transitionToDynamic() { - get(this, 'routerService').transitionTo('dynamic', 1); - }, - }, - }), - template: `foo-bar`, + }, + }), + template: `foo-bar`, + }); + + return this.visit('/').then(() => { + run(function() { + componentInstance.send('transitionToDynamic'); }); - return this.visit('/').then(() => { - run(function() { - componentInstance.send('transitionToDynamic'); - }); + assert.equal(this.routerService.get('currentRouteName'), 'dynamic'); + assert.equal(this.routerService.get('currentURL'), '/dynamic/1'); + this.assertText('much dynamicism'); + }); + } - assert.equal(this.routerService.get('currentRouteName'), 'dynamic'); - assert.equal(this.routerService.get('currentURL'), '/dynamic/1'); - this.assertText('much dynamicism'); + ['@test RouterService#transitionTo with basic query params does not remove query param defaults']( + assert + ) { + assert.expect(1); + + this.add( + 'controller:parent.child', + Controller.extend({ + queryParams: ['sort'], + sort: 'ASC', + }) + ); + + let queryParams = this.buildQueryParams({ sort: 'ASC' }); + + return this.visit('/') + .then(() => { + return this.routerService.transitionTo('parent.child', queryParams); + }) + .then(() => { + assert.equal(this.routerService.get('currentURL'), '/child?sort=ASC'); }); - } - - ['@test RouterService#transitionTo with basic query params does not remove query param defaults']( - assert - ) { - assert.expect(1); - - this.add( - 'controller:parent.child', - Controller.extend({ - queryParams: ['sort'], - sort: 'ASC', - }) - ); - - let queryParams = this.buildQueryParams({ sort: 'ASC' }); - - return this.visit('/') - .then(() => { - return this.routerService.transitionTo('parent.child', queryParams); - }) - .then(() => { - assert.equal(this.routerService.get('currentURL'), '/child?sort=ASC'); - }); - } - - ['@test RouterService#transitionTo passing only queryParams works'](assert) { - assert.expect(2); - - this.add( - 'controller:parent.child', - Controller.extend({ - queryParams: ['sort'], - }) - ); - - let queryParams = this.buildQueryParams({ sort: 'DESC' }); - - return this.visit('/') - .then(() => { - return this.routerService.transitionTo('parent.child'); - }) - .then(() => { - assert.equal(this.routerService.get('currentURL'), '/child'); - }) - .then(() => { - return this.routerService.transitionTo(queryParams); - }) - .then(() => { - assert.equal(this.routerService.get('currentURL'), '/child?sort=DESC'); - }); - } - - ['@test RouterService#transitionTo with unspecified query params'](assert) { - assert.expect(1); - - this.add( - 'controller:parent.child', - Controller.extend({ - queryParams: ['sort', 'page', 'category', 'extra'], - sort: 'ASC', - page: null, - category: undefined, - }) - ); - - let queryParams = this.buildQueryParams({ sort: 'ASC' }); - - return this.visit('/') - .then(() => { - return this.routerService.transitionTo('parent.child', queryParams); - }) - .then(() => { - assert.equal(this.routerService.get('currentURL'), '/child?sort=ASC'); - }); - } - - ['@test RouterService#transitionTo with aliased query params uses the original provided key']( - assert - ) { - assert.expect(1); - - this.add( - 'controller:parent.child', - Controller.extend({ - queryParams: { - cont_sort: 'url_sort', - }, - cont_sort: 'ASC', - }) - ); - - let queryParams = this.buildQueryParams({ url_sort: 'ASC' }); - - return this.visit('/') - .then(() => { - return this.routerService.transitionTo('parent.child', queryParams); - }) - .then(() => { - assert.equal(this.routerService.get('currentURL'), '/child?url_sort=ASC'); - }); - } - - ['@test RouterService#transitionTo with aliased query params uses the original provided key when controller property name']( - assert - ) { - assert.expect(1); - - this.add( - 'controller:parent.child', - Controller.extend({ - queryParams: { - cont_sort: 'url_sort', - }, - cont_sort: 'ASC', - }) - ); + } + + ['@test RouterService#transitionTo passing only queryParams works'](assert) { + assert.expect(2); + + this.add( + 'controller:parent.child', + Controller.extend({ + queryParams: ['sort'], + }) + ); + + let queryParams = this.buildQueryParams({ sort: 'DESC' }); + + return this.visit('/') + .then(() => { + return this.routerService.transitionTo('parent.child'); + }) + .then(() => { + assert.equal(this.routerService.get('currentURL'), '/child'); + }) + .then(() => { + return this.routerService.transitionTo(queryParams); + }) + .then(() => { + assert.equal(this.routerService.get('currentURL'), '/child?sort=DESC'); + }); + } - let queryParams = this.buildQueryParams({ cont_sort: 'ASC' }); + ['@test RouterService#transitionTo with unspecified query params'](assert) { + assert.expect(1); + + this.add( + 'controller:parent.child', + Controller.extend({ + queryParams: ['sort', 'page', 'category', 'extra'], + sort: 'ASC', + page: null, + category: undefined, + }) + ); + + let queryParams = this.buildQueryParams({ sort: 'ASC' }); + + return this.visit('/') + .then(() => { + return this.routerService.transitionTo('parent.child', queryParams); + }) + .then(() => { + assert.equal(this.routerService.get('currentURL'), '/child?sort=ASC'); + }); + } - return this.visit('/').then(() => { - expectAssertion(() => { - return this.routerService.transitionTo('parent.child', queryParams); - }, 'You passed the `cont_sort` query parameter during a transition into parent.child, please update to url_sort'); + ['@test RouterService#transitionTo with aliased query params uses the original provided key']( + assert + ) { + assert.expect(1); + + this.add( + 'controller:parent.child', + Controller.extend({ + queryParams: { + cont_sort: 'url_sort', + }, + cont_sort: 'ASC', + }) + ); + + let queryParams = this.buildQueryParams({ url_sort: 'ASC' }); + + return this.visit('/') + .then(() => { + return this.routerService.transitionTo('parent.child', queryParams); + }) + .then(() => { + assert.equal(this.routerService.get('currentURL'), '/child?url_sort=ASC'); }); - } } - ); -} + + ['@test RouterService#transitionTo with aliased query params uses the original provided key when controller property name']( + assert + ) { + assert.expect(1); + + this.add( + 'controller:parent.child', + Controller.extend({ + queryParams: { + cont_sort: 'url_sort', + }, + cont_sort: 'ASC', + }) + ); + + let queryParams = this.buildQueryParams({ cont_sort: 'ASC' }); + + return this.visit('/').then(() => { + expectAssertion(() => { + return this.routerService.transitionTo('parent.child', queryParams); + }, 'You passed the `cont_sort` query parameter during a transition into parent.child, please update to url_sort'); + }); + } + } +); diff --git a/packages/ember/tests/routing/router_service_test/urlFor_test.js b/packages/ember/tests/routing/router_service_test/urlFor_test.js index abbda06df48..7eef0f561d8 100644 --- a/packages/ember/tests/routing/router_service_test/urlFor_test.js +++ b/packages/ember/tests/routing/router_service_test/urlFor_test.js @@ -4,8 +4,6 @@ import { Route } from '@ember/-internals/routing'; import { get } from '@ember/-internals/metal'; import { RouterTestCase, moduleFor } from 'internal-test-helpers'; -import { EMBER_ROUTING_ROUTER_SERVICE } from '@ember/canary-features'; - function setupController(app, name) { let controllerName = `${capitalize(name)}Controller`; @@ -16,292 +14,288 @@ function setupController(app, name) { }); } -if (EMBER_ROUTING_ROUTER_SERVICE) { - moduleFor( - 'Router Service - urlFor', - class extends RouterTestCase { - ['@test RouterService#urlFor returns URL for simple route'](assert) { - assert.expect(1); +moduleFor( + 'Router Service - urlFor', + class extends RouterTestCase { + ['@test RouterService#urlFor returns URL for simple route'](assert) { + assert.expect(1); - return this.visit('/').then(() => { - let expectedURL = this.routerService.urlFor('parent.child'); + return this.visit('/').then(() => { + let expectedURL = this.routerService.urlFor('parent.child'); - assert.equal('/child', expectedURL); - }); - } + assert.equal('/child', expectedURL); + }); + } - ['@test RouterService#urlFor returns URL for simple route with dynamic segments'](assert) { - assert.expect(1); + ['@test RouterService#urlFor returns URL for simple route with dynamic segments'](assert) { + assert.expect(1); - setupController(this.application, 'dynamic'); + setupController(this.application, 'dynamic'); - let dynamicModel = { id: 1, contents: 'much dynamicism' }; + let dynamicModel = { id: 1, contents: 'much dynamicism' }; - return this.visit('/').then(() => { - let expectedURL = this.routerService.urlFor('dynamic', dynamicModel); + return this.visit('/').then(() => { + let expectedURL = this.routerService.urlFor('dynamic', dynamicModel); - assert.equal('/dynamic/1', expectedURL); - }); - } + assert.equal('/dynamic/1', expectedURL); + }); + } - ['@test RouterService#urlFor returns URL for simple route with basic query params'](assert) { - assert.expect(1); + ['@test RouterService#urlFor returns URL for simple route with basic query params'](assert) { + assert.expect(1); - let queryParams = this.buildQueryParams({ foo: 'bar' }); + let queryParams = this.buildQueryParams({ foo: 'bar' }); - return this.visit('/').then(() => { - let expectedURL = this.routerService.urlFor('parent.child', queryParams); + return this.visit('/').then(() => { + let expectedURL = this.routerService.urlFor('parent.child', queryParams); - assert.equal('/child?foo=bar', expectedURL); - }); - } - - ['@test RouterService#urlFor returns URL for simple route with basic query params and default value']( - assert - ) { - assert.expect(1); - - this.add( - 'controller:parent.child', - Controller.extend({ - queryParams: ['sort'], - sort: 'ASC', - }) - ); + assert.equal('/child?foo=bar', expectedURL); + }); + } - let queryParams = this.buildQueryParams({ sort: 'ASC' }); + ['@test RouterService#urlFor returns URL for simple route with basic query params and default value']( + assert + ) { + assert.expect(1); - return this.visit('/').then(() => { - let expectedURL = this.routerService.urlFor('parent.child', queryParams); + this.add( + 'controller:parent.child', + Controller.extend({ + queryParams: ['sort'], + sort: 'ASC', + }) + ); - assert.equal('/child?sort=ASC', expectedURL); - }); - } - - ['@test RouterService#urlFor returns URL for simple route with basic query params and default value with stickyness']( - assert - ) { - assert.expect(2); - - this.add( - 'controller:parent.child', - Controller.extend({ - queryParams: ['sort', 'foo'], - sort: 'ASC', - }) - ); + let queryParams = this.buildQueryParams({ sort: 'ASC' }); - return this.visit('/child/?sort=DESC').then(() => { - let controller = this.applicationInstance.lookup('controller:parent.child'); - assert.equal(get(controller, 'sort'), 'DESC', 'sticky is set'); + return this.visit('/').then(() => { + let expectedURL = this.routerService.urlFor('parent.child', queryParams); - let queryParams = this.buildQueryParams({ foo: 'derp' }); - let actual = this.routerService.urlFor('parent.child', queryParams); + assert.equal('/child?sort=ASC', expectedURL); + }); + } - assert.equal(actual, '/child?foo=derp', 'does not use "stickiness"'); - }); - } + ['@test RouterService#urlFor returns URL for simple route with basic query params and default value with stickyness']( + assert + ) { + assert.expect(2); - ['@test RouterService#urlFor returns URL for simple route with array as query params']( - assert - ) { - assert.expect(1); + this.add( + 'controller:parent.child', + Controller.extend({ + queryParams: ['sort', 'foo'], + sort: 'ASC', + }) + ); - let queryParams = this.buildQueryParams({ - selectedItems: ['a', 'b', 'c'], - }); + return this.visit('/child/?sort=DESC').then(() => { + let controller = this.applicationInstance.lookup('controller:parent.child'); + assert.equal(get(controller, 'sort'), 'DESC', 'sticky is set'); - return this.visit('/').then(() => { - let expectedURL = this.routerService.urlFor('parent.child', queryParams); + let queryParams = this.buildQueryParams({ foo: 'derp' }); + let actual = this.routerService.urlFor('parent.child', queryParams); - assert.equal('/child?selectedItems[]=a&selectedItems[]=b&selectedItems[]=c', expectedURL); - }); - } + assert.equal(actual, '/child?foo=derp', 'does not use "stickiness"'); + }); + } - ['@test RouterService#urlFor returns URL for simple route with null query params'](assert) { - assert.expect(1); + ['@test RouterService#urlFor returns URL for simple route with array as query params'](assert) { + assert.expect(1); - let queryParams = this.buildQueryParams({ foo: null }); + let queryParams = this.buildQueryParams({ + selectedItems: ['a', 'b', 'c'], + }); - return this.visit('/').then(() => { - let expectedURL = this.routerService.urlFor('parent.child', queryParams); + return this.visit('/').then(() => { + let expectedURL = this.routerService.urlFor('parent.child', queryParams); - assert.equal('/child', expectedURL); - }); - } + assert.equal('/child?selectedItems[]=a&selectedItems[]=b&selectedItems[]=c', expectedURL); + }); + } - ['@test RouterService#urlFor returns URL for simple route with undefined query params']( - assert - ) { - assert.expect(1); + ['@test RouterService#urlFor returns URL for simple route with null query params'](assert) { + assert.expect(1); - let queryParams = this.buildQueryParams({ foo: undefined }); + let queryParams = this.buildQueryParams({ foo: null }); - return this.visit('/').then(() => { - let expectedURL = this.routerService.urlFor('parent.child', queryParams); + return this.visit('/').then(() => { + let expectedURL = this.routerService.urlFor('parent.child', queryParams); - assert.equal('/child', expectedURL); - }); - } + assert.equal('/child', expectedURL); + }); + } - ['@test RouterService#urlFor returns URL for simple route with dynamic segments and basic query params']( - assert - ) { - assert.expect(1); + ['@test RouterService#urlFor returns URL for simple route with undefined query params']( + assert + ) { + assert.expect(1); - let queryParams = this.buildQueryParams({ foo: 'bar' }); + let queryParams = this.buildQueryParams({ foo: undefined }); - return this.visit('/').then(() => { - let expectedURL = this.routerService.urlFor('dynamic', { id: 1 }, queryParams); + return this.visit('/').then(() => { + let expectedURL = this.routerService.urlFor('parent.child', queryParams); - assert.equal('/dynamic/1?foo=bar', expectedURL); - }); - } + assert.equal('/child', expectedURL); + }); + } - ['@test RouterService#urlFor returns URL for simple route with dynamic segments and array as query params']( - assert - ) { - assert.expect(1); + ['@test RouterService#urlFor returns URL for simple route with dynamic segments and basic query params']( + assert + ) { + assert.expect(1); - let queryParams = this.buildQueryParams({ - selectedItems: ['a', 'b', 'c'], - }); + let queryParams = this.buildQueryParams({ foo: 'bar' }); - return this.visit('/').then(() => { - let expectedURL = this.routerService.urlFor('dynamic', { id: 1 }, queryParams); + return this.visit('/').then(() => { + let expectedURL = this.routerService.urlFor('dynamic', { id: 1 }, queryParams); - assert.equal( - '/dynamic/1?selectedItems[]=a&selectedItems[]=b&selectedItems[]=c', - expectedURL - ); - }); - } + assert.equal('/dynamic/1?foo=bar', expectedURL); + }); + } - ['@test RouterService#urlFor returns URL for simple route with dynamic segments and null query params']( - assert - ) { - assert.expect(1); + ['@test RouterService#urlFor returns URL for simple route with dynamic segments and array as query params']( + assert + ) { + assert.expect(1); - let queryParams = this.buildQueryParams({ foo: null }); + let queryParams = this.buildQueryParams({ + selectedItems: ['a', 'b', 'c'], + }); - return this.visit('/').then(() => { - let expectedURL = this.routerService.urlFor('dynamic', { id: 1 }, queryParams); + return this.visit('/').then(() => { + let expectedURL = this.routerService.urlFor('dynamic', { id: 1 }, queryParams); - assert.equal('/dynamic/1', expectedURL); - }); - } + assert.equal( + '/dynamic/1?selectedItems[]=a&selectedItems[]=b&selectedItems[]=c', + expectedURL + ); + }); + } + + ['@test RouterService#urlFor returns URL for simple route with dynamic segments and null query params']( + assert + ) { + assert.expect(1); + + let queryParams = this.buildQueryParams({ foo: null }); + + return this.visit('/').then(() => { + let expectedURL = this.routerService.urlFor('dynamic', { id: 1 }, queryParams); + + assert.equal('/dynamic/1', expectedURL); + }); + } + + ['@test RouterService#urlFor returns URL for simple route with dynamic segments and undefined query params']( + assert + ) { + assert.expect(1); + + let queryParams = this.buildQueryParams({ foo: undefined }); + + return this.visit('/').then(() => { + let expectedURL = this.routerService.urlFor('dynamic', { id: 1 }, queryParams); + + assert.equal('/dynamic/1', expectedURL); + }); + } - ['@test RouterService#urlFor returns URL for simple route with dynamic segments and undefined query params']( - assert - ) { - assert.expect(1); + ['@test RouterService#urlFor correctly transitions to route via generated path'](assert) { + assert.expect(1); - let queryParams = this.buildQueryParams({ foo: undefined }); + let expectedURL; - return this.visit('/').then(() => { - let expectedURL = this.routerService.urlFor('dynamic', { id: 1 }, queryParams); + return this.visit('/') + .then(() => { + expectedURL = this.routerService.urlFor('parent.child'); - assert.equal('/dynamic/1', expectedURL); + return this.routerService.transitionTo(expectedURL); + }) + .then(() => { + assert.equal(expectedURL, this.routerService.get('currentURL')); }); - } - - ['@test RouterService#urlFor correctly transitions to route via generated path'](assert) { - assert.expect(1); - - let expectedURL; - - return this.visit('/') - .then(() => { - expectedURL = this.routerService.urlFor('parent.child'); - - return this.routerService.transitionTo(expectedURL); - }) - .then(() => { - assert.equal(expectedURL, this.routerService.get('currentURL')); - }); - } - - ['@test RouterService#urlFor correctly transitions to route via generated path with dynamic segments']( - assert - ) { - assert.expect(1); - - let expectedURL; - let dynamicModel = { id: 1 }; - - this.add( - 'route:dynamic', - Route.extend({ - model() { - return dynamicModel; - }, - }) - ); + } - return this.visit('/') - .then(() => { - expectedURL = this.routerService.urlFor('dynamic', dynamicModel); - - return this.routerService.transitionTo(expectedURL); - }) - .then(() => { - assert.equal(expectedURL, this.routerService.get('currentURL')); - }); - } - - ['@test RouterService#urlFor correctly transitions to route via generated path with query params']( - assert - ) { - assert.expect(1); - - let expectedURL; - let actualURL; - let queryParams = this.buildQueryParams({ foo: 'bar' }); - - return this.visit('/') - .then(() => { - expectedURL = this.routerService.urlFor('parent.child', queryParams); - - return this.routerService.transitionTo(expectedURL); - }) - .then(() => { - actualURL = `${this.routerService.get('currentURL')}?foo=bar`; - - assert.equal(expectedURL, actualURL); - }); - } - - ['@test RouterService#urlFor correctly transitions to route via generated path with dynamic segments and query params']( - assert - ) { - assert.expect(1); - - let expectedURL; - let actualURL; - let queryParams = this.buildQueryParams({ foo: 'bar' }); - let dynamicModel = { id: 1 }; - - this.add( - 'route:dynamic', - Route.extend({ - model() { - return dynamicModel; - }, - }) - ); + ['@test RouterService#urlFor correctly transitions to route via generated path with dynamic segments']( + assert + ) { + assert.expect(1); + + let expectedURL; + let dynamicModel = { id: 1 }; + + this.add( + 'route:dynamic', + Route.extend({ + model() { + return dynamicModel; + }, + }) + ); + + return this.visit('/') + .then(() => { + expectedURL = this.routerService.urlFor('dynamic', dynamicModel); + + return this.routerService.transitionTo(expectedURL); + }) + .then(() => { + assert.equal(expectedURL, this.routerService.get('currentURL')); + }); + } + + ['@test RouterService#urlFor correctly transitions to route via generated path with query params']( + assert + ) { + assert.expect(1); - return this.visit('/') - .then(() => { - expectedURL = this.routerService.urlFor('dynamic', dynamicModel, queryParams); + let expectedURL; + let actualURL; + let queryParams = this.buildQueryParams({ foo: 'bar' }); - return this.routerService.transitionTo(expectedURL); - }) - .then(() => { - actualURL = `${this.routerService.get('currentURL')}?foo=bar`; + return this.visit('/') + .then(() => { + expectedURL = this.routerService.urlFor('parent.child', queryParams); - assert.equal(expectedURL, actualURL); - }); - } + return this.routerService.transitionTo(expectedURL); + }) + .then(() => { + actualURL = `${this.routerService.get('currentURL')}?foo=bar`; + + assert.equal(expectedURL, actualURL); + }); } - ); -} + + ['@test RouterService#urlFor correctly transitions to route via generated path with dynamic segments and query params']( + assert + ) { + assert.expect(1); + + let expectedURL; + let actualURL; + let queryParams = this.buildQueryParams({ foo: 'bar' }); + let dynamicModel = { id: 1 }; + + this.add( + 'route:dynamic', + Route.extend({ + model() { + return dynamicModel; + }, + }) + ); + + return this.visit('/') + .then(() => { + expectedURL = this.routerService.urlFor('dynamic', dynamicModel, queryParams); + + return this.routerService.transitionTo(expectedURL); + }) + .then(() => { + actualURL = `${this.routerService.get('currentURL')}?foo=bar`; + + assert.equal(expectedURL, actualURL); + }); + } + } +); From 411f7c415650874e4e75e7402b29a31afd2dc6f2 Mon Sep 17 00:00:00 2001 From: Chad Hietala Date: Wed, 3 Oct 2018 10:00:05 -0400 Subject: [PATCH 4/4] Move router service invents into a reopen --- .../-internals/routing/lib/services/router.ts | 152 +++++++++--------- 1 file changed, 78 insertions(+), 74 deletions(-) diff --git a/packages/@ember/-internals/routing/lib/services/router.ts b/packages/@ember/-internals/routing/lib/services/router.ts index 869d8c5ba99..bc86e212684 100644 --- a/packages/@ember/-internals/routing/lib/services/router.ts +++ b/packages/@ember/-internals/routing/lib/services/router.ts @@ -1,10 +1,10 @@ import { Evented } from '@ember/-internals/runtime'; +import { EMBER_ROUTING_ROUTER_SERVICE } from '@ember/canary-features'; import { readOnly } from '@ember/object/computed'; import Service from '@ember/service'; import { Transition } from 'router_js'; import EmberRouter from '../system/router'; import { extractRouteArgs, resemblesURL, shallowEqual } from '../utils'; - /** The Router service is the public API that provides access to the router. @@ -38,79 +38,6 @@ import { extractRouteArgs, resemblesURL, shallowEqual } from '../utils'; export default class RouterService extends Service { _router!: EmberRouter; - init() { - this._super(...arguments); - this._router.on('routeWillChange', (transition: Transition) => { - this.trigger('routeWillChange', transition); - }); - - this._router.on('routeDidChange', (transition: Transition) => { - this.trigger('routeDidChange', transition); - }); - } - - /** - The `routeWillChange` event is fired at the beginning of any - attempted transition with a `Transition` object as the sole - argument. This action can be used for aborting, redirecting, - or decorating the transition from the currently active routes. - - A good example is preventing navigation when a form is - half-filled out: - - ```app/routes/contact-form.js - import {inject as service} from '@ember/service'; - - export default Route.extend({ - router: service('router'), - init() { - this._super(...arguments); - this.router.on('routeWillUpdate', (transition) => { - if (!transition.to.find(route => route.name === this.routeName)) { - alert("Please save or cancel your changes."); - transition.abort(); - } - }) - } - }); - ``` - - The `routeWillChange` event fires whenever a new route is chosen as the desired target of a transition. This includes `transitionTo`, `replaceWith`, all redirection for any reason including error handling, and abort. Aborting implies changing the desired target back to where you already were. Once a transition has completed, `routeDidChange` fires. - - @event routeWillChange - @param {Transition} transition - @public - */ - - /** - The `routeDidChange` event only fires once a transition has settled. - This includes aborts and error substates. Like the `routeWillChange` event - it recieves a Transition as the sole argument. - - A good example is sending some analytics when the route has transitioned: - - ```app/routes/contact-form.js - import {inject as service} from '@ember/service'; - - export default Route.extend({ - router: service('router'), - init() { - this._super(...arguments); - this.router.on('routeDidUpdate', (transition) => { - ga.send('pageView', { - current: transition.to.name, - from: transition.from.name - }); - }) - } - }); - ``` - - @event routeDidChange - @param {Transition} transition - @public - */ - /** Transition the application into another route. The route may be either a single route or route path: @@ -326,3 +253,80 @@ RouterService.reopen(Evented, { */ rootURL: readOnly('_router.rootURL'), }); + +if (EMBER_ROUTING_ROUTER_SERVICE) { + RouterService.reopen(Evented, { + init() { + this._super(...arguments); + this._router.on('routeWillChange', (transition: Transition) => { + this.trigger('routeWillChange', transition); + }); + + this._router.on('routeDidChange', (transition: Transition) => { + this.trigger('routeDidChange', transition); + }); + }, + // Uncomment this when we go the feature + // /** + // The `routeWillChange` event is fired at the beginning of any + // attempted transition with a `Transition` object as the sole + // argument. This action can be used for aborting, redirecting, + // or decorating the transition from the currently active routes. + + // A good example is preventing navigation when a form is + // half-filled out: + + // ```app/routes/contact-form.js + // import {inject as service} from '@ember/service'; + + // export default Route.extend({ + // router: service('router'), + // init() { + // this._super(...arguments); + // this.router.on('routeWillUpdate', (transition) => { + // if (!transition.to.find(route => route.name === this.routeName)) { + // alert("Please save or cancel your changes."); + // transition.abort(); + // } + // }) + // } + // }); + // ``` + + // The `routeWillChange` event fires whenever a new route is chosen as the desired target of a transition. This includes `transitionTo`, `replaceWith`, all redirection for any reason including error handling, and abort. Aborting implies changing the desired target back to where you already were. Once a transition has completed, `routeDidChange` fires. + + // @event routeWillChange + // @param {Transition} transition + // @public + // */ + + // /** + // The `routeDidChange` event only fires once a transition has settled. + // This includes aborts and error substates. Like the `routeWillChange` event + // it recieves a Transition as the sole argument. + + // A good example is sending some analytics when the route has transitioned: + + // ```app/routes/contact-form.js + // import {inject as service} from '@ember/service'; + + // export default Route.extend({ + // router: service('router'), + // init() { + // this._super(...arguments); + // this.router.on('routeDidUpdate', (transition) => { + // ga.send('pageView', { + // current: transition.to.name, + // from: transition.from.name + // }); + // }) + // } + // }); + // ``` + + // @event routeDidChange + // @param {Transition} transition + // @public + // */ + }); +}