Reputation: 2030
Often, we'll need to schedule an update after rendering completes in reaction to a property change. Here is an example:
page_changed: function() {
Ember.run.scheduleOnce('afterRender', this, this.scroll);
}.observes('current_page')
This doesn't work for testing since there is no runloop at the time scheduleOnce
is called. We can simply wrap scheduleOnce
in an Ember.run
page_changed: function() {
Ember.run(function() {
Ember.run.scheduleOnce('afterRender', that, that.scroll);
});
).observes('current_page')
..but I'm being told that's not the right way to go about it. I thought I'd reach out and get some other ideas.
For reference here is the issue that I opened up in ember.js#10536
Upvotes: 1
Views: 2841
Reputation: 1087
the entry point into your application in the test needs the Ember.run
. For example
test('foo', function() {
user.set('foo', 1); // may have side-affects
});
test('foo', function() {
Ember.run(function() {
user.set('foo', 1);
});
});
Why is this needed? Ember.run
wraps the root of all call-stacks that are triggered by click/user-actions/ajax etc. Why? The run-loop is what allows ember to batch
When writing unit-tests, we are "faking" the user or network actions, it isn't obvious to ember what groups of changes you want to "Batch" together.
We can think of Ember.run
as a way to create a batch or transactions of changes. Ultimately we use this, to batch DOM reads/writes to interact with the DOM in an ideal way.
Although maybe frustrating until you get the hang of it, it is a great way to create concise and deterministic tests. For example:
test('a series of grouped things', function() {
Ember.run(function() {
component.set('firstName', 'Stef');
component.set('lastName', 'Penner');
// DOM isn't updated yet
});
// DOM is updated;
// assert the DOM is updated
Ember.run(function() {
component.set('firstName', 'Kris');
component.set('lastName', 'Selden');
// DOM isn't updated yet, (its still Stef Penner)
});
// DOM is once again updated, its now Kris Selden.
});
Why is this cool? Ember gives us fine-grained control, which lets us not only very easily test aspects of our app in isolation, but also test sequences of what may be user or ajax pushed based actions, without needing to incorporate those aspects.
As time goes on, we hope to improve the test helpers and clarity around this.
Upvotes: 1
Reputation: 2030
Looks like this is the way to do it according to @StefanPenner's comment. Instead of modifying the app code itself just wrap the render call with an Ember.run
test('it renders', function() {
expect(2);
var component = this.subject();
var that = this;
equal(component._state, 'preRender');
// Wrapping render in Ember.run
Ember.run(function() {
that.render();
});
equal(component._state, 'inDOM');
});
Upvotes: 1
Reputation: 37389
..but I'm being told that's not the right way to go about it.
The reason is that you need the Ember.run
call when the callback is executed, not when it's scheduled. Like this:
Ember.run.scheduleOnce('afterRender', function() {
Ember.run(function() {
that.scroll();
});
});
In your code, Ember.run
is being called when the callback is scheduled. This is likely unnecessary as you're probably already in the run loop. My version ensures that when the callback is called, even if the run loop finished long ago, that another run loop is started and that.scroll()
is called within the run loop. Does that makes sense?
Upvotes: 0