From 497bead274d921ab2d92e8a4cf5f41935371766f Mon Sep 17 00:00:00 2001 From: Sam Van Campenhout Date: Thu, 18 Mar 2021 23:45:38 +0100 Subject: [PATCH] Add a refresh method to the RouterService This adds a `refresh` method to the RouterService as described in RFC 631 RFC PR: https://github.com/emberjs/rfcs/pull/631 --- .../setup-router-service-refresh-polyfill.js | 45 ++++++++ .../setup-router-service-refresh-polyfill.js | 4 + .../acceptance/router-service-refresh-test.js | 100 ++++++++++++++++++ tests/dummy/app/router.js | 7 +- tests/dummy/app/templates/application.hbs | 5 - ...up-router-service-refresh-polyfill-test.js | 39 +++++++ 6 files changed, 194 insertions(+), 6 deletions(-) create mode 100644 addon/initializers/setup-router-service-refresh-polyfill.js create mode 100644 app/initializers/setup-router-service-refresh-polyfill.js create mode 100644 tests/acceptance/router-service-refresh-test.js delete mode 100644 tests/dummy/app/templates/application.hbs create mode 100644 tests/unit/initializers/setup-router-service-refresh-polyfill-test.js diff --git a/addon/initializers/setup-router-service-refresh-polyfill.js b/addon/initializers/setup-router-service-refresh-polyfill.js new file mode 100644 index 0000000..f65bc75 --- /dev/null +++ b/addon/initializers/setup-router-service-refresh-polyfill.js @@ -0,0 +1,45 @@ +/* eslint-disable ember/no-private-routing-service */ +import { assert } from '@ember/debug'; + +export function initialize(application) { + const RouterService = application.resolveRegistration('service:router'); + + RouterService.reopen({ + /** + * Refreshes all currently active routes, doing a full transition. + * If a routeName is provided and refers to a currently active route, + * it will refresh only that route and its descendents. + * Returns a promise that will be resolved once the refresh is complete. + * All resetController, beforeModel, model, afterModel, redirect, and setupController + * hooks will be called again. You will get new data from the model hook. + * + * @method refresh + * @param {String} [routeName] the route to refresh (along with all child routes) + * @return Transition + * @public + */ + refresh(routeName) { + if (!routeName) { + return this._router._routerMicrolib.refresh(); + } + + assert( + `The route "${routeName}" was not found`, + this._router.hasRoute(routeName) + ); + + assert( + `The route "${routeName}" is currently not active`, + this.isActive(routeName) + ); + + let pivotRoute = this._router._routerMicrolib.getRoute(routeName); + + return this._router._routerMicrolib.refresh(pivotRoute); + }, + }); +} + +export default { + initialize, +}; diff --git a/app/initializers/setup-router-service-refresh-polyfill.js b/app/initializers/setup-router-service-refresh-polyfill.js new file mode 100644 index 0000000..6357e3f --- /dev/null +++ b/app/initializers/setup-router-service-refresh-polyfill.js @@ -0,0 +1,4 @@ +export { + default, + initialize, +} from 'ember-router-service-refresh-polyfill/initializers/setup-router-service-refresh-polyfill'; diff --git a/tests/acceptance/router-service-refresh-test.js b/tests/acceptance/router-service-refresh-test.js new file mode 100644 index 0000000..56731e7 --- /dev/null +++ b/tests/acceptance/router-service-refresh-test.js @@ -0,0 +1,100 @@ +/* eslint-disable ember/no-classic-classes */ + +import Route from '@ember/routing/route'; +import { visit } from '@ember/test-helpers'; +import { setupApplicationTest } from 'ember-qunit'; +import { module, test } from 'qunit'; + +module('Acceptance | RouterService | refresh', function (hooks) { + setupApplicationTest(hooks); + + test('it can be used to re-run the model hooks of active routes', async function (assert) { + const routerService = this.owner.lookup('service:router'); + + let parentCounter = 0; + this.owner.register( + 'route:parent', + Route.extend({ + model() { + ++parentCounter; + }, + }) + ); + + let childCounter = 0; + this.owner.register( + 'route:parent.child', + Route.extend({ + model() { + ++childCounter; + }, + }) + ); + + let sisterCounter = 0; + this.owner.register( + 'route:parent.sister', + Route.extend({ + model() { + ++sisterCounter; + }, + }) + ); + + await visit('/'); + assert.equal(parentCounter, 1); + assert.equal(childCounter, 0); + assert.equal(sisterCounter, 0); + + await routerService.refresh(); + assert.equal(parentCounter, 2); + assert.equal(childCounter, 0); + assert.equal(sisterCounter, 0); + + await routerService.refresh('application'); + assert.equal(parentCounter, 3); + assert.equal(childCounter, 0); + assert.equal(sisterCounter, 0); + + await routerService.transitionTo('parent.child'); + assert.equal(parentCounter, 3); + assert.equal(childCounter, 1); + assert.equal(sisterCounter, 0); + + await routerService.refresh('parent.child'); + assert.equal(parentCounter, 3); + assert.equal(childCounter, 2); + assert.equal(sisterCounter, 0); + + await routerService.refresh('parent'); + assert.equal(parentCounter, 4); + assert.equal(childCounter, 3); + assert.equal(sisterCounter, 0); + + await routerService.transitionTo('parent.sister'); + assert.equal(parentCounter, 4); + assert.equal(childCounter, 3); + assert.equal(sisterCounter, 1); + + await routerService.refresh(); + assert.equal(parentCounter, 5); + assert.equal(childCounter, 3); + assert.equal(sisterCounter, 2); + }); + + test('it verifies that the provided route exists', async function (assert) { + const routerService = this.owner.lookup('service:router'); + + assert.throws(() => { + routerService.refresh('this-route-does-not-exist'); + }); + }); + + test('it verifies that the provided route is active', async function (assert) { + const routerService = this.owner.lookup('service:router'); + + assert.throws(() => { + routerService.refresh('this-route-does-not-exist'); + }); + }); +}); diff --git a/tests/dummy/app/router.js b/tests/dummy/app/router.js index 64e543a..d179f60 100644 --- a/tests/dummy/app/router.js +++ b/tests/dummy/app/router.js @@ -6,4 +6,9 @@ export default class Router extends EmberRouter { rootURL = config.rootURL; } -Router.map(function () {}); +Router.map(function () { + this.route('parent', { path: '/' }, function () { + this.route('child'); + this.route('sister'); + }); +}); diff --git a/tests/dummy/app/templates/application.hbs b/tests/dummy/app/templates/application.hbs deleted file mode 100644 index 1001d14..0000000 --- a/tests/dummy/app/templates/application.hbs +++ /dev/null @@ -1,5 +0,0 @@ -{{page-title "Dummy"}} - -

Welcome to Ember

- -{{outlet}} \ No newline at end of file diff --git a/tests/unit/initializers/setup-router-service-refresh-polyfill-test.js b/tests/unit/initializers/setup-router-service-refresh-polyfill-test.js new file mode 100644 index 0000000..e1808b2 --- /dev/null +++ b/tests/unit/initializers/setup-router-service-refresh-polyfill-test.js @@ -0,0 +1,39 @@ +import Application from '@ember/application'; + +import config from 'dummy/config/environment'; +import { initialize } from 'dummy/initializers/setup-router-service-refresh-polyfill'; +import { module, test } from 'qunit'; +import Resolver from 'ember-resolver'; +import { run } from '@ember/runloop'; + +module( + 'Unit | Initializer | setup-router-service-refresh-polyfill', + function (hooks) { + hooks.beforeEach(function () { + this.TestApplication = class TestApplication extends Application { + modulePrefix = config.modulePrefix; + }; + this.TestApplication.initializer({ + name: 'initializer under test', + initialize, + }); + + this.application = this.TestApplication.create({ + autoboot: false, + Resolver, + }); + }); + + hooks.afterEach(function () { + run(this.application, 'destroy'); + }); + + test('it adds the polyfilled refresh method to the RouterService', async function (assert) { + await this.application.boot(); + const applicationInstance = this.application.buildInstance(); + const routerService = applicationInstance.lookup('service:router'); + + assert.equal(typeof routerService.refresh, 'function'); + }); + } +);