-->

Ember - Return a promise from beforeModel not work

2019-04-10 11:16发布

问题:

In my ApplicationRoute I implement a beforeModel hook that does a server call to see if a valid session exists. If it does, then the app navigates to the 'dashboard' route, otherwise it goes to the 'login' route. I am trying to implement some tests and I can't seem to get it to work with QUnit. I keep getting:

Assertion failed: You have turned on testing mode, which disabled the run-loop's autorun. You will need to wrap any code with asynchronous side-effects in an Ember.run

Here is a plunker http://plnkr.co/edit/e0Q4qz?p=preview I have simulated a server call by creating a doLater method that returns a promise and executes a method in a few milliseconds. The only way I can get it to work is to not return a promise from the beforeModel hook. Am I using App.deferReadiness() and Ember.run() correctly?

App = Ember.Application.create({});

App.Router.map(function() {
  this.resource('dashboard');
  this.resource('login');
});

App.ApplicationRoute = Ember.Route.extend({
  alreadyChecked: false,

  beforeModel: function() {
    var route = this;

    // uncomment this line to stop this hook returning a promise
    // return route.transitionTo(localStorage.user ? 'dashboard' : 'login');

    if (!this.alreadyChecked) {
      return doLater(function() {
        // without the alreadyChecked flag, this function gets called twice
        route.set('alreadyChecked', false);
        route.transitionTo(localStorage.user ? 'dashboard' : 'login');
      });
    }
  }
});

App.LoginRoute = Ember.Route.extend({
  actions: {
    login: function() {
      var route = this;
      doLater(function() {
        localStorage.user = "Bill";
        route.transitionTo('dashboard');
      });
    }
  }
});

App.DashboardRoute = Ember.Route.extend({
  actions: {
    logout: function() {
      var route = this;
      doLater(function() {
        localStorage.user = "";
        route.transitionTo('login');
      });
    }
  }
});

function doLater(fn) {
  return Ember.RSVP.Promise(function(resolve, reject) {
    setTimeout(function() {
      resolve(fn());
    }, 500);
  });
}


// run the tests
if (true) { // toggle this boolean to run the app in testing mode
  App.rootElement = '#ember-testing';
  App.setupForTesting();
  App.injectTestHelpers();

  module('integration tests', {
    setup: function() {
      Ember.run(function() {
        App.reset();
        localStorage.user = "";
        App.deferReadiness();
      });
    }
  });

  test('can navigate to login page', function() {
    expect(1);
    Ember.run(App, 'advanceReadiness');
    visit("/login").then(function() {
      ok(true, "Tests work");
    });
  });
}

回答1:

First off, adding the example is awesome, shows you tried, and makes it so much easier to debug.

In your case there is no need to advance readiness, and the problem is setTimeout lives outside of the run loop, but Ember.run.later doesn't and works the same!

http://plnkr.co/edit/lZKM4JBluQ27rcnfXlWS?p=preview

http://plnkr.co/edit/wFOCGQt3ahiuqi8el0rB?p=preview