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..bc86e212684 100644 --- a/packages/@ember/-internals/routing/lib/services/router.ts +++ b/packages/@ember/-internals/routing/lib/services/router.ts @@ -1,8 +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. @@ -148,7 +150,7 @@ export default class RouterService extends Service { } } -RouterService.reopen({ +RouterService.reopen(Evented, { /** Name of the current route. @@ -251,3 +253,80 @@ RouterService.reopen({ */ 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 + // */ + }); +} diff --git a/packages/@ember/-internals/routing/lib/system/route.ts b/packages/@ember/-internals/routing/lib/system/route.ts index 52750bd7b91..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'; @@ -329,8 +330,8 @@ class Route extends EmberObject implements IRoute { /** @private - @method _reset - @since 1.7.0 + @method _internalReset + @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 3534b9353fc..574b9a38b8e 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'; @@ -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 @@ -21,11 +26,53 @@ import { MatchCallback } from 'route-recognizer'; import Router, { InternalRouteInfo, InternalTransition, + logAbort, Transition, + TransitionError, TransitionState, } 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() { @@ -237,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); } @@ -245,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); } @@ -257,6 +328,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; } @@ -399,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 @@ -508,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. @@ -1449,7 +1477,7 @@ export function triggerEvent( routeInfos: PrivateRouteInfo[], ignoreFailure: boolean, name: string, - args: unknown[] + args: any[] ) { if (!routeInfos) { if (ignoreFailure) { @@ -1750,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. @@ -1793,4 +1865,7 @@ EmberRouter.reopen(Evented, { }), }); +if (EMBER_ROUTING_ROUTER_SERVICE && ROUTER_EVENTS) { + EmberRouter.reopen(ROUTER_EVENT_DEPRECATIONS); +} export default EmberRouter; 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/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/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/events_test.js b/packages/ember/tests/routing/router_service_test/events_test.js new file mode 100644 index 00000000000..f3d1f2f7586 --- /dev/null +++ b/packages/ember/tests/routing/router_service_test/events_test.js @@ -0,0 +1,747 @@ +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 events are 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 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', () => {}); + }, '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', () => {}); + }); + }); + } + } + ); + + 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.'); + } + } + ); +} 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); + }); + } + } +); 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"