diff --git a/packages/ember-routing/lib/services/router.js b/packages/ember-routing/lib/services/router.js index 17c54cd8873..4dddb018893 100644 --- a/packages/ember-routing/lib/services/router.js +++ b/packages/ember-routing/lib/services/router.js @@ -42,7 +42,28 @@ const RouterService = Service.extend({ @public */ transitionTo() { - this.router.transitionTo(...arguments); + return this.router.transitionTo(...arguments); + }, + + /** + Transition into another route while replacing the current URL, if possible. + The route may be either a single route or route path: + + See [Route.replaceWith](http://emberjs.com/api/classes/Ember.Route.html#method_replaceWith) for more info. + + @method replaceWith + @category ember-routing-router-service + @param {String} name the name of the route or a URL + @param {...Object} models the model(s) or identifier(s) to be used while + transitioning to the route. + @param {Object} [options] optional hash with a queryParams property + containing a mapping of query parameters + @return {Transition} the transition object associated with this + attempted transition + @public + */ + replaceWith() { + return this.router.replaceWith(...arguments); } }); 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 93cd1720a2b..bae43914be2 100644 --- a/packages/ember/tests/routing/router_service_test/basic_test.js +++ b/packages/ember/tests/routing/router_service_test/basic_test.js @@ -1,317 +1,97 @@ -import Logger from 'ember-console'; -import { - Controller, - inject -} from 'ember-runtime'; +import { inject } from 'ember-runtime'; import { Component } from 'ember-glimmer'; import { Route, NoneLocation } from 'ember-routing'; import { - run, get, set } from 'ember-metal'; -import { jQuery } from 'ember-views'; import { - ApplicationTestCase, + RouterTestCase, moduleFor } from 'internal-test-helpers'; import { isFeatureEnabled } from 'ember-metal'; if (isFeatureEnabled('ember-routing-router-service')) { - moduleFor('Router Service - main', class extends ApplicationTestCase { - constructor() { - super(); - - this.router.map(function() { - this.route('parent', { path: '/' }, function() { - this.route('child'); - this.route('sister'); - this.route('brother'); - }); - this.route('dynamic', { path: '/dynamic/:post_id' }); - }); - } - + moduleFor('Router Service - main', class extends RouterTestCase { ['@test RouterService#currentRouteName is correctly set for top level route'](assert) { assert.expect(1); - let routerService; - - this.registerRoute('parent.index', Route.extend({ - routerService: inject.service('router'), - init() { - this._super(); - routerService = get(this, 'routerService'); - } - })); - return this.visit('/').then(() => { - assert.equal(routerService.get('currentRouteName'), 'parent.index'); + assert.equal(this.routerService.get('currentRouteName'), 'parent.index'); }); } ['@test RouterService#currentRouteName is correctly set for child route'](assert) { assert.expect(1); - let routerService; - - this.registerRoute('parent.child', Route.extend({ - routerService: inject.service('router'), - init() { - this._super(); - routerService = get(this, 'routerService'); - } - })); - return this.visit('/child').then(() => { - assert.equal(routerService.get('currentRouteName'), 'parent.child'); + assert.equal(this.routerService.get('currentRouteName'), 'parent.child'); }); } ['@test RouterService#currentRouteName is correctly set after transition'](assert) { assert.expect(1); - let routerService; - - this.registerRoute('parent.child', Route.extend({ - routerService: inject.service('router'), - init() { - this._super(); - routerService = get(this, 'routerService'); - }, - - afterModel() { - this.transitionTo('parent.sister'); - } - })); - - return this.visit('/child').then(() => { - assert.equal(routerService.get('currentRouteName'), 'parent.sister'); - }); + 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); - let routerService; - - this.registerRoute('parent.child', Route.extend({ - routerService: inject.service('router'), - init() { - this._super(); - routerService = get(this, 'routerService'); - } - })); - return this.visit('/child') .then(() => { - assert.equal(routerService.get('currentRouteName'), 'parent.child'); + assert.equal(this.routerService.get('currentRouteName'), 'parent.child'); return this.visit('/sister'); }) .then(() => { - assert.equal(routerService.get('currentRouteName'), 'parent.sister'); + assert.equal(this.routerService.get('currentRouteName'), 'parent.sister'); return this.visit('/brother'); }) .then(() => { - assert.equal(routerService.get('currentRouteName'), 'parent.brother'); + assert.equal(this.routerService.get('currentRouteName'), 'parent.brother'); }); } ['@test RouterService#rootURL is correctly set to the default value'](assert) { assert.expect(1); - let routerService; - - this.registerRoute('parent.index', Route.extend({ - routerService: inject.service('router'), - init() { - this._super(); - routerService = get(this, 'routerService'); - } - })); - return this.visit('/').then(() => { - assert.equal(routerService.get('rootURL'), '/'); + assert.equal(this.routerService.get('rootURL'), '/'); }); } ['@test RouterService#rootURL is correctly set to a custom value'](assert) { assert.expect(1); - let routerService; - this.registerRoute('parent.index', Route.extend({ - routerService: inject.service('router'), init() { this._super(); set(this.router, 'rootURL', '/homepage'); - routerService = get(this, 'routerService'); } })); return this.visit('/').then(() => { - assert.equal(routerService.get('rootURL'), '/homepage'); + assert.equal(this.routerService.get('rootURL'), '/homepage'); }); } ['@test RouterService#location is correctly delegated from router:main'](assert) { assert.expect(2); - let routerService; - - this.registerRoute('parent.index', Route.extend({ - routerService: inject.service('router'), - init() { - this._super(); - routerService = get(this, 'routerService'); - } - })); - return this.visit('/').then(() => { - let location = routerService.get('location'); + let location = this.routerService.get('location'); assert.ok(location); assert.ok(location instanceof NoneLocation); }); } - - ['@test RouterService#transitionTo with basic route'](assert) { - assert.expect(1); - - let routerService; - let componentInstance; - - this.registerRoute('parent.index', Route.extend({ - routerService: inject.service('router'), - init() { - this._super(); - routerService = get(this, 'routerService'); - } - })); - - this.registerTemplate('parent.index', '{{foo-bar}}'); - - this.registerComponent('foo-bar', { - ComponentClass: Component.extend({ - routerService: inject.service('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(routerService.get('currentRouteName'), 'parent.sister'); - }); - } - - ['@test RouterService#transitionTo with dynamic segment'](assert) { - assert.expect(3); - - let routerService; - let componentInstance; - let dynamicModel = { id: 1, contents: 'much dynamicism' }; - - this.registerRoute('parent.index', Route.extend({ - routerService: inject.service('router'), - init() { - this._super(); - routerService = get(this, 'routerService'); - } - })); - - this.registerTemplate('parent.index', '{{foo-bar}}'); - this.registerTemplate('dynamic', '{{model.contents}}'); - - this.registerComponent('foo-bar', { - ComponentClass: Component.extend({ - routerService: inject.service('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'); - }); - - assert.equal(routerService.get('currentRouteName'), 'dynamic'); - assert.equal(routerService.get('currentURL'), '/dynamic/1'); - this.assertText('much dynamicism'); - }); - } - - ['@test RouterService#transitionTo with dynamic segment and model hook'](assert) { - assert.expect(3); - - let routerService; - let componentInstance; - let dynamicModel = { id: 1, contents: 'much dynamicism' }; - - this.registerRoute('parent.index', Route.extend({ - routerService: inject.service('router'), - init() { - this._super(); - routerService = get(this, 'routerService'); - } - })); - - this.registerRoute('dynamic', Route.extend({ - model() { - return dynamicModel; - } - })); - - this.registerTemplate('parent.index', '{{foo-bar}}'); - this.registerTemplate('dynamic', '{{model.contents}}'); - - this.registerComponent('foo-bar', { - ComponentClass: Component.extend({ - routerService: inject.service('router'), - init() { - this._super(); - componentInstance = this; - }, - actions: { - transitionToDynamic() { - get(this, 'routerService').transitionTo('dynamic', 1); - } - } - }), - template: `foo-bar` - }); - - return this.visit('/').then(() => { - run(function() { - componentInstance.send('transitionToDynamic'); - }); - - assert.equal(routerService.get('currentRouteName'), 'dynamic'); - assert.equal(routerService.get('currentURL'), '/dynamic/1'); - this.assertText('much dynamicism'); - }); - } }); } 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 3e9b03203aa..bb3dc3cbc74 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 @@ -1,19 +1,12 @@ -import Logger from 'ember-console'; import { - Controller, inject, readOnly } from 'ember-runtime'; import { Component } from 'ember-glimmer'; import { Route, NoneLocation } from 'ember-routing'; +import { get } from 'ember-metal'; import { - run, - get, - set -} from 'ember-metal'; -import { jQuery } from 'ember-views'; -import { - ApplicationTestCase, + RouterTestCase, moduleFor } from 'internal-test-helpers'; @@ -42,22 +35,12 @@ if (isFeatureEnabled('ember-routing-router-service')) { } }); - moduleFor('Router Service - currentURL lifecycle', class extends ApplicationTestCase { + moduleFor('Router Service - currentURL', class extends RouterTestCase { constructor() { super(); results = []; - this.router.map(function() { - this.route('parent', { path: '/' }, function() { - this.route('child'); - this.route('sister'); - this.route('brother'); - this.route('stepsister'); - }); - this.route('dynamic', { path: '/dynamic/:post_id' }); - }); - ROUTE_NAMES.forEach((name) => { let routeName = `parent.${name}`; this.registerRoute(routeName, InstrumentedRoute.extend()); @@ -76,87 +59,47 @@ if (isFeatureEnabled('ember-routing-router-service')) { ['@test RouterService#currentURL is correctly set for top level route'](assert) { assert.expect(1); - let routerService; - - this.registerRoute('parent.index', Route.extend({ - routerService: inject.service('router'), - init() { - this._super(); - routerService = get(this, 'routerService'); - } - })); - return this.visit('/').then(() => { - assert.equal(routerService.get('currentURL'), '/'); + assert.equal(this.routerService.get('currentURL'), '/'); }); } ['@test RouterService#currentURL is correctly set for child route'](assert) { assert.expect(1); - let routerService; - - this.registerRoute('parent.child', Route.extend({ - routerService: inject.service('router'), - init() { - this._super(); - routerService = get(this, 'routerService'); - } - })); - return this.visit('/child').then(() => { - assert.equal(routerService.get('currentURL'), '/child'); + assert.equal(this.routerService.get('currentURL'), '/child'); }); } ['@test RouterService#currentURL is correctly set after transition'](assert) { assert.expect(1); - let routerService; - - this.registerRoute('parent.child', Route.extend({ - routerService: inject.service('router'), - init() { - this._super(); - routerService = get(this, 'routerService'); - }, - - afterModel() { - this.transitionTo('parent.sister'); - } - })); - - return this.visit('/child').then(() => { - assert.equal(routerService.get('currentURL'), '/sister'); - }); + 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); - let routerService; - - this.registerRoute('parent.child', Route.extend({ - routerService: inject.service('router'), - init() { - this._super(); - routerService = get(this, 'routerService'); - } - })); - return this.visit('/child') .then(() => { - assert.equal(routerService.get('currentURL'), '/child'); + assert.equal(this.routerService.get('currentURL'), '/child'); return this.visit('/sister'); }) .then(() => { - assert.equal(routerService.get('currentURL'), '/sister'); + assert.equal(this.routerService.get('currentURL'), '/sister'); return this.visit('/brother'); }) .then(() => { - assert.equal(routerService.get('currentURL'), '/brother'); + assert.equal(this.routerService.get('currentURL'), '/brother'); }); } diff --git a/packages/ember/tests/routing/router_service_test/replaceWith_test.js b/packages/ember/tests/routing/router_service_test/replaceWith_test.js new file mode 100644 index 00000000000..4f068793f32 --- /dev/null +++ b/packages/ember/tests/routing/router_service_test/replaceWith_test.js @@ -0,0 +1,91 @@ +import { NoneLocation } from 'ember-routing'; +import { + RouterTestCase, + moduleFor +} from 'internal-test-helpers'; +import { Transition } from 'router'; + +import { isFeatureEnabled } from 'ember-metal'; + +if (isFeatureEnabled('ember-routing-router-service')) { + moduleFor('Router Service - replaceWith', class extends RouterTestCase { + constructor() { + super(); + + let testCase = this; + testCase.state = []; + + this.application.register('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 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']); + }); + } + }); +} diff --git a/packages/ember/tests/routing/router_service_test/transitionTo_test.js b/packages/ember/tests/routing/router_service_test/transitionTo_test.js new file mode 100644 index 00000000000..9b510f94f7d --- /dev/null +++ b/packages/ember/tests/routing/router_service_test/transitionTo_test.js @@ -0,0 +1,208 @@ +import { inject } from 'ember-runtime'; +import { Component } from 'ember-glimmer'; +import { Route, NoneLocation } from 'ember-routing'; +import { + run, + get, + set +} from 'ember-metal'; +import { + RouterTestCase, + moduleFor +} from 'internal-test-helpers'; +import { Transition } from 'router'; + +import { isFeatureEnabled } from 'ember-metal'; + +if (isFeatureEnabled('ember-routing-router-service')) { + moduleFor('Router Service - transitionTo', class extends RouterTestCase { + constructor() { + super(); + + let testCase = this; + testCase.state = []; + + this.application.register('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#transitionTo returns a Transition'](assert) { + assert.expect(1); + + let transition; + + return this.visit('/') + .then(() => { + transition = this.routerService.transitionTo('parent.child'); + + assert.ok(transition instanceof 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 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.registerTemplate('parent.index', '{{foo-bar}}'); + + this.registerComponent('foo-bar', { + ComponentClass: Component.extend({ + routerService: inject.service('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 with dynamic segment'](assert) { + assert.expect(3); + + let componentInstance; + let dynamicModel = { id: 1, contents: 'much dynamicism' }; + + this.registerTemplate('parent.index', '{{foo-bar}}'); + this.registerTemplate('dynamic', '{{model.contents}}'); + + this.registerComponent('foo-bar', { + ComponentClass: Component.extend({ + routerService: inject.service('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'); + }); + + 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 and model hook'](assert) { + assert.expect(3); + + let componentInstance; + let dynamicModel = { id: 1, contents: 'much dynamicism' }; + + this.registerRoute('dynamic', Route.extend({ + model() { + return dynamicModel; + } + })); + + this.registerTemplate('parent.index', '{{foo-bar}}'); + this.registerTemplate('dynamic', '{{model.contents}}'); + + this.registerComponent('foo-bar', { + ComponentClass: Component.extend({ + routerService: inject.service('router'), + init() { + this._super(); + componentInstance = this; + }, + actions: { + transitionToDynamic() { + get(this, 'routerService').transitionTo('dynamic', 1); + } + } + }), + template: `foo-bar` + }); + + 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'); + }); + } + }); +} diff --git a/packages/internal-test-helpers/lib/index.js b/packages/internal-test-helpers/lib/index.js index 903eb511f35..513d01cf7e7 100644 --- a/packages/internal-test-helpers/lib/index.js +++ b/packages/internal-test-helpers/lib/index.js @@ -27,3 +27,4 @@ export { default as ApplicationTestCase } from './test-cases/application'; export { default as QueryParamTestCase } from './test-cases/query-param'; export { default as AbstractRenderingTestCase } from './test-cases/abstract-rendering'; export { default as RenderingTestCase } from './test-cases/rendering'; +export { default as RouterTestCase } from './test-cases/router'; diff --git a/packages/internal-test-helpers/lib/test-cases/router.js b/packages/internal-test-helpers/lib/test-cases/router.js new file mode 100644 index 00000000000..985d19ba8da --- /dev/null +++ b/packages/internal-test-helpers/lib/test-cases/router.js @@ -0,0 +1,21 @@ + +import ApplicationTestCase from './application'; + +export default class RouterTestCase extends ApplicationTestCase { + constructor() { + super(); + + this.router.map(function() { + this.route('parent', { path: '/' }, function() { + this.route('child'); + this.route('sister'); + this.route('brother'); + }); + this.route('dynamic', { path: '/dynamic/:post_id' }); + }); + } + + get routerService() { + return this.applicationInstance.lookup('service:router'); + } +}