From 9a69d632b27faf0592c58698e3eb83ccced2d972 Mon Sep 17 00:00:00 2001 From: Trent Willis Date: Thu, 11 Aug 2016 13:01:09 -0700 Subject: [PATCH] [BUGFIX beta] Ensure substates properly work with resetNamespace --- packages/ember-routing/lib/system/dsl.js | 4 +- packages/ember-routing/lib/system/router.js | 20 ++++-- .../ember-routing/tests/system/dsl_test.js | 58 +++++++++++++++ .../ember/tests/routing/substates_test.js | 71 ++++++++++++++++++- 4 files changed, 143 insertions(+), 10 deletions(-) diff --git a/packages/ember-routing/lib/system/dsl.js b/packages/ember-routing/lib/system/dsl.js index 8e6d14bb8b2..1ac3b22202a 100644 --- a/packages/ember-routing/lib/system/dsl.js +++ b/packages/ember-routing/lib/system/dsl.js @@ -40,7 +40,7 @@ DSL.prototype = { if (this.enableLoadingSubstates) { createRoute(this, `${name}_loading`, { resetNamespace: options.resetNamespace }); - createRoute(this, `${name}_error`, { path: dummyErrorRoute }); + createRoute(this, `${name}_error`, { resetNamespace: options.resetNamespace, path: dummyErrorRoute }); } if (callback) { @@ -193,7 +193,7 @@ if (isEnabled('ember-application-engines')) { if (this.enableLoadingSubstates) { let dummyErrorRoute = `/_unused_dummy_error_path_route_${name}/:error`; createRoute(this, `${name}_loading`, { resetNamespace: options.resetNamespace }); - createRoute(this, `${name}_error`, { path: dummyErrorRoute }); + createRoute(this, `${name}_error`, { resetNamespace: options.resetNamespace, path: dummyErrorRoute }); } let localFullName = 'application'; diff --git a/packages/ember-routing/lib/system/router.js b/packages/ember-routing/lib/system/router.js index 53e23ff74e4..4cd22cb8ac3 100644 --- a/packages/ember-routing/lib/system/router.js +++ b/packages/ember-routing/lib/system/router.js @@ -1014,16 +1014,24 @@ function findChildRouteName(parentRoute, originatingChildRoute, name) { } } - let targetChildRouteName = originatingChildRouteName.split('.').pop(); - let namespace = parentRoute.routeName === 'application' ? '' : parentRoute.routeName + '.'; - - // First, try a named loading state, e.g. 'foo_loading' - childName = namespace + targetChildRouteName + '_' + name; + // First, try a named loading state of the route, e.g. 'foo_loading' + childName = originatingChildRouteName + '_' + name; if (routeHasBeenDefined(router, childName)) { return childName; } - // Second, try general loading state, e.g. 'loading' + // Second, try general loading state of the parent, e.g. 'loading' + let originatingChildRouteParts = originatingChildRouteName.split('.').slice(0, -1); + let namespace; + + // If there is a namespace on the route, then we use that, otherwise we use + // the parent route as the namespace. + if (originatingChildRouteParts.length) { + namespace = originatingChildRouteParts.join('.') + '.'; + } else { + namespace = parentRoute.routeName === 'application' ? '' : parentRoute.routeName + '.'; + } + childName = namespace + name; if (routeHasBeenDefined(router, childName)) { return childName; diff --git a/packages/ember-routing/tests/system/dsl_test.js b/packages/ember-routing/tests/system/dsl_test.js index 1c78bfea7f0..8762a9d0218 100644 --- a/packages/ember-routing/tests/system/dsl_test.js +++ b/packages/ember-routing/tests/system/dsl_test.js @@ -114,6 +114,33 @@ QUnit.test('should not add loading and error routes if _isRouterMapResult is fal ok(!router.router.recognizer.names['blork_error'], 'error route was not added'); }); +QUnit.test('should reset namespace of loading and error routes for routes with resetNamespace', function() { + Router.map(function() { + this.route('blork', function() { + this.route('blorp'); + this.route('bleep', { resetNamespace: true }); + }); + }); + + let router = Router.create({ + _hasModuleBasedResolver() { return true; } + }); + + router._initRouterJs(); + + ok(router.router.recognizer.names['blork.blorp'], 'nested route was created'); + ok(router.router.recognizer.names['blork.blorp_loading'], 'nested loading route was added'); + ok(router.router.recognizer.names['blork.blorp_error'], 'nested error route was added'); + + ok(router.router.recognizer.names['bleep'], 'reset route was created'); + ok(router.router.recognizer.names['bleep_loading'], 'reset loading route was added'); + ok(router.router.recognizer.names['bleep_error'], 'reset error route was added'); + + ok(!router.router.recognizer.names['blork.bleep'], 'nested reset route was not created'); + ok(!router.router.recognizer.names['blork.bleep_loading'], 'nested reset loading route was not added'); + ok(!router.router.recognizer.names['blork.bleep_error'], 'nested reset error route was not added'); +}); + if (isEnabled('ember-application-engines')) { QUnit.test('should throw an error when defining a route serializer outside an engine', function() { Router.map(function() { @@ -267,4 +294,35 @@ if (isEnabled('ember-application-engines')) { ok(!router.router.recognizer.names['chat_loading'], 'loading route was not added'); ok(!router.router.recognizer.names['chat_error'], 'error route was not added'); }); + + QUnit.test('should reset namespace of loading and error routes for mounts with resetNamespace', function() { + Router.map(function() { + this.route('news', function() { + this.mount('chat'); + this.mount('blog', { resetNamespace: true }); + }); + }); + + let engineInstance = buildOwner({ + routable: true + }); + + let router = Router.create({ + _hasModuleBasedResolver() { return true; } + }); + setOwner(router, engineInstance); + router._initRouterJs(); + + ok(router.router.recognizer.names['news.chat'], 'nested route was created'); + ok(router.router.recognizer.names['news.chat_loading'], 'nested loading route was added'); + ok(router.router.recognizer.names['news.chat_error'], 'nested error route was added'); + + ok(router.router.recognizer.names['blog'], 'reset route was created'); + ok(router.router.recognizer.names['blog_loading'], 'reset loading route was added'); + ok(router.router.recognizer.names['blog_error'], 'reset error route was added'); + + ok(!router.router.recognizer.names['news.blog'], 'nested reset route was not created'); + ok(!router.router.recognizer.names['news.blog_loading'], 'nested reset loading route was not added'); + ok(!router.router.recognizer.names['news.blog_error'], 'nested reset error route was not added'); + }); } diff --git a/packages/ember/tests/routing/substates_test.js b/packages/ember/tests/routing/substates_test.js index fb409ea1b27..11b1e7a62e4 100644 --- a/packages/ember/tests/routing/substates_test.js +++ b/packages/ember/tests/routing/substates_test.js @@ -794,7 +794,7 @@ QUnit.test('Default error event moves into nested route, prioritizing more speci templates['grandma'] = 'GRANDMA {{outlet}}'; templates['grandma/error'] = 'ERROR: {{model.msg}}'; - templates['grandma/mom_error'] = 'MOM ERROR: {{model.msg}}'; + templates['mom_error'] = 'MOM ERROR: {{model.msg}}'; Router.map(function() { this.route('grandma', function() { @@ -842,7 +842,7 @@ QUnit.test('Prioritized substate entry works with preserved-namespace nested rou Router.map(function() { this.route('foo', function() { - this.route('foo.bar', { path: '/bar', resetNamespace: true }, function() { + this.route('bar', { path: '/bar' }, function() { }); }); }); @@ -865,6 +865,37 @@ QUnit.test('Prioritized substate entry works with preserved-namespace nested rou equal(jQuery('#app', '#qunit-fixture').text(), 'YAY'); }); +QUnit.test('Prioritized substate entry works with reset-namespace nested routes', function() { + expect(2); + + templates['bar_loading'] = 'BAR LOADING'; + templates['bar/index'] = 'YAY'; + + Router.map(function() { + this.route('foo', function() { + this.route('bar', { path: '/bar', resetNamespace: true }, function() { + }); + }); + }); + + App.ApplicationController = Controller.extend(); + + let deferred = RSVP.defer(); + App.BarRoute = Route.extend({ + model() { + return deferred.promise; + } + }); + + bootApplication('/foo/bar'); + + equal(jQuery('#app', '#qunit-fixture').text(), 'BAR LOADING', 'foo.bar_loading was entered (as opposed to something like foo/foo/bar_loading)'); + + run(deferred, 'resolve'); + + equal(jQuery('#app', '#qunit-fixture').text(), 'YAY'); +}); + QUnit.test('Prioritized loading substate entry works with preserved-namespace nested routes', function() { expect(2); @@ -1090,4 +1121,40 @@ if (isEnabled('ember-application-engines')) { equal(jQuery('#app', '#qunit-fixture').text(), 'BLOG ERROR', 'news/blog_loading was entered'); }); + + QUnit.test('Slow Promise from an Engine application route enters the mounts loading state with resetNamespace', function() { + expect(1); + + templates['blog_loading'] = 'BLOG LOADING'; + + // Register engine + let BlogEngine = Engine.extend(); + registry.register('engine:blog', BlogEngine); + + // Register engine route map + let BlogMap = function() {}; + registry.register('route-map:blog', BlogMap); + + Router.map(function() { + this.route('news', function() { + this.mount('blog', { resetNamespace: true }); + }); + }); + + let deferred = RSVP.defer(); + let BlogRoute = Route.extend({ + model() { + return deferred.promise; + } + }); + + var blog = container.lookup('engine:blog'); + blog.register('route:application', BlogRoute); + + bootApplication('/news/blog'); + + equal(jQuery('#app', '#qunit-fixture').text(), 'BLOG LOADING', 'news/blog_loading was entered'); + + run(deferred, 'resolve'); + }); }