I am trying to get some tests to pass for an ember addon. It was working fine until yesterday I added some code that runs later in the run loops using Em.run.next.
Here is what Im doing in my test.
visit('/').then(function() {
find('bm-select').click();
andThen(function() {
equal(1,1, 'yay');
});
});
The problem is when click is triggered, the later function is executed after andThen. By that time all my tests are done and it throws error. I am under the impression andThen should wait for all async stuff to finish.
This is what what my code looks like when click is triggered(focusOut event is triggered on click)
lostFocus: function() {
if(this.get('isOpen')) {
Em.run.later(this, function() {
var focussedElement = document.activeElement;
var isFocussedOut =
this.$().has(focussedElement).length === 0 && !this.$().is(focussedElement);
if(isFocussedOut) {
this.closeOptions({focus:false});
}
}, 0);
}
}.on('focusOut'),
You can see that it gives an error Uncaught TypeError: Cannot read property 'has' of undefined. This is from the focusOut method. By the time the function executes the components _state is 'destroying' and this.$() returns undefined.
I tried the wait helper and still I am not able to get the tests to work. How is this normally done.
I have extracted the tests to run in a bin. Here is the link to it.
After further debugging, the problem is one of tags 'bm-select' has it focusOut event triggered in the teardown method of testing. So by the time the run loop code executes the component is not inDOM.
I just added a hidden input field in the test app. Once I've run all the tests, I set focus to the hidden input field and use the wait test helper. Now all the run loop code is done by the time the tear down method is executed.
Related
I'm working with chart.js in ember and am encountering a bug where the graph breaks when I update the graph data without rerendering the component.
I am dynamically rendering this component with the {{component totalsChartComponent}} helper so if I could just set totalsChartComponent to undefined and let it cycle through the run loop before I update the components data it should clear out the component and allow it to render properly
I was trying to do this like this:
Ember.run.next(this, function () {
this.set('totalsChartComponent', undefined);
})
Ember.run.next(this, function () {
this.set('totalsChartComponent', "ember-chart");
})
but this does not appear to be running the second function a full run loop cycle after the first. Does this look like it should be working or am I doing something wrong here?
changing my code to
Ember.run.schedule("actions", this, function () {
this.set('totalsChartComponent', undefined);
});
Ember.run.next(this, function () {
this.set('totalsChartComponent', "ember-chart");
});
Here is my understanding of why this works:
Ember.run.next schedules an event in the run loop cycle after the current one. I believe that my two Ember.run.next calls were getting evaluated during the same run loop, waiting for that loop to finish and then both throwing their action into the following loop. Basically the multiple calls to next were not creating multiple run loops.
Ember.run.schedule schedules something in the current loop or creates one. By doing this and then calling Ember.run.next I achieve the desired effect of creating one run loop for the first function and then creating another run loop for the second one.
I have a component that integrates 2 third-party libraries, imagesLoaded and Isotope.
I get conflicting test failures when running tests in the browser and cli mode. The error is:
Error: 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 a run
or
TypeError: 'undefined' is not an object (evaluating 'self.$().isotope')
When I try to wrap callbacks in an ember run loop, they pass in cli-mode, but then fail in browser mode. I can't seem to find the right combination. The issue seems to happen in the callback of imagesLoaded, as if I remove that plugin, it seems to pass fine.
I've tried multiple combinations, but here is my latest code. If anyone has insight on how to properly use the run loop in this component, that would be helpful.
handleLoad: function() {
let self = this;
Ember.run.scheduleOnce('afterRender', this, function(){
self.$().imagesLoaded( function() {
Ember.run(function() {
self.$().isotope({itemSelector: ".card-container"});
self.$().isotope('shuffle');
});
}); // imagesLoaded
}); // Ember.run
}.on('didInsertElement')
You'll have to wrap your existing code in a
Ember.run(function () {
// Ember.run.scheduleOnce('afterRender....
});
as this tells Ember where the run loop starts and ends - in production/development this is done for you by Ember itself, but in testing you have to wrap it.
As an alternative you can start & end a run loop manually by calling it explicitly (see the API Docs):
Ember.run.begin() // <-- tell Ember that your starting a run loop
//Ember.run.scheduleOnce('afterRender', ...
//more ember run loops here
Ember.run.end(); // <-- tell Ember that the run loop ends here
working in ember-cli testing. After all tests passed it returns extra two test with errors.
Uncaught Error: Assertion Failed: calling set on destroyed object
Source : '../dist/assets/vendor.js:13269'
this is one unit test configuration
import Ember from "ember";
import { test,moduleFor } from 'ember-qunit';
import startApp from '../helpers/start-app';
var App;
module('An Integration test',{
setup:function(){
App=startApp();
},
teardown: function() {
Ember.run(App, 'destroy');
}
});
This is either because in the result of a promise or any other deferred code you do not check the destroy status of an object, or because you didn't teardown something that has been setup and interact with DOM events or anything external to the core of Ember.
I used to have this especially on some jQuery plugins which I mapped to Ember, and during the tests the plugins were destroying too slowly and I was then either not using a run loop, or not checking the destroyed status of the Ember object I was manipulating.
You can do so with:
if ( !(obj.get('isDestroyed') || obj.get('isDestroying')) ) {
// do your destroying code setting stuff
}
Also think about destroying any jQuery plugins that might have been initialised in the code of your views (anything setup in didInsertElement should be teardown in willDestroyElement for example).
Ok i struggled with similar thing. So basically when you have "this.set()" inside a promise, it might happen that the promise takes too long to resolve, and the user already clicked away from that page, in this case you are trying to set something, that is already destroyed. I found the simplest solution to be just a simple check in the beginning of the promise.
if (this.isDestroyed) {
return;
}
this.set('...');
...
Edit: alternatively you can use Ember.trySet.
The issue is related to a promise not completely resolving and another test getting run immediately after.
You should give Ember Concurrency a try.
import { task, timeout } from 'ember-concurrency';
myFunction: task(function * () {
// do somethinng
yield timeout(1000); // wait for x milliseconds
// do something else
}).drop(),
I had a similar issue in an integration test. To resolve, in the integration test, I waited before performing the next action.
import wait from 'ember-test-helpers/wait';
wait().then(() => {
// perform action (which previously used to cause an exception)
});
Using ember.js v 1.5.1.
I use karma and qunit to test my ember application. In several of my tests I have situations where 1.a user clicks->2.an async call is made to our server->and then 3. a transition via "this.transitionToRoute('someroute')" in the controller is called. When it hits the transitionToRoute method while testing, karma hangs. Tried wrapping it with an ember.run call but didn't seem to help.
When I comment out the transition call it runs, and fails accordingly.
Example Test Code where it hangs and doesn't reach equal calls
test('successful registration request', function() {
setupMockRegistrationRequests();
visit("/register")
.fillIn('#email', 'test2')
.fillIn('#password','password')
.click('#submit')
.andThen(function() {
equal(find(".register-page .form-alert").length, 0, "Should be no error");
equal(find(".login-page").length, 1, "Should be on login screen");
});
});
Controller Code
Test case runs
//this.transitionToRoute('login');
Test case hangs
this.transitionToRoute('login');
Any body know why it is hanging?/What I can do to allow it continue?
The problem was that it was transitioning, but there were more async requests being made by the next route that were not being handled by my mockjax requests. This caused the testing environment to hang without any errors being thrown.
Update: here's a fiddle of my problem. The tests pass once, and fail the next time:
http://jsfiddle.net/samselikoff/hhk6u/4/
The problem is departments has events.on("userSet:company"), so both variables respond to the event.
This is a general question about unit testing. In my app, a certain event is fired, and several other pieces of my app listen for this event. I'd like to unit test each piece separately, since they are performing different functions; but to do this, I have to fire off the event in each test.
This causes problems, since the first test must fire off the event, triggering the listeners in the other tests. How can I keep my tests atomic while still testing multiple event listeners?
(I am using QUnit, but I think this is a more general unit-testing question).
Answer:
Jeferson is correct. One easy way to solve this, is to use events.once instead of events.on. This way you clean up your events from each test.
All your calls to async methods should be tested using "asyncTest" methods and making sure you wrap your calls in other functions that calls QUnit.start() when the assertions data are ready to be collected and analyzed.
I updated your JSFiddle with working code: http://jsfiddle.net/hhk6u/8/
The new code is:
QUnit.config.autostart = false;
QUnit.config.testTimeOut = 1000;
asyncTest('Some test that needs companies.', function() {
function getCompanies() {
var companies = new Companies();
ok(1);
start();
}
setTimeout(getCompanies, 500);
});
asyncTest('Some other async test that triggers a listener in companies.', function() {
var companies = new Companies();
events.trigger("userSet:company", { name: "Acme", id: 1 });
stop();
events.on('fetched:departments', function(response) {
console.log(response);
deepEqual(response, [1, 2, 3]);
start();
});
});
See my answer in this other question for more details:
Test fails then succeeds
Hope this helps you!