Uncaught Error: Assertion Failed: calling set on destroyed object - ember.js

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)
});

Related

Ember 3.0 acceptance redirect test hangs forever

I have a simple acceptance test written in the modern RFC 268 format for Ember 3.0.
The test is for a page where, if the user is unauthenticated, the URL immediately redirects to /login.
...
module('Acceptance | index', function (hooks) {
setupApplicationTest(hooks);
test('visiting / logged out', async function(assert) {
assert.expect(1);
// Test hangs here forever.
await visit('/');
assert.equal(currentURL(), '/login');
});
});
This test worked great using the older format with moduleForAcceptance.
Unfortunately, this test hangs forever in Ember 3.0. Am I missing something here? Is there a better way to test a redirect?
There are no errors in the console, and the addition of some console.log statements show that the await is where the test hangs.
I found the reason why this was failing. I have an Ember mixin that I use to enhance all of my routes. The mixin checks for whether or not a user is authenticated, and redirects to /login as needed.
export default Mixin.create({
session: service(),
beforeModel() {
this._super(...arguments);
return new EmberPromise((resolve) => {
if (authenticated) {
resolve();
return;
}
this.transitionTo('login');
});
}
});
You'll notice that I am not resolving if authenticated is falsey. That worked fine with my app and the test syntax in 2.18.
The docs say the following regarding that hook I am overriding in my mixin.
returns Any | Promise
if the value returned from this hook is a promise, the transition will pause until the transition resolves. Otherwise, non-promise return values are not utilized in any way.
To me, the bit about "non-promise return values" implies that I should be able to do what I'm doing. Especially considering this worked in 2.18, but I wonder if this was one of those "wow, how did that ever work in the first place" scenarios. Clearly this syntax isn't working in 3.0. since the transition pauses forever when testing.
The answer for me was to ensure I always resolve/reject something. In this case, I had to add an explicit reject() so that the promise chain doesn't hang.
export default Mixin.create({
session: service(),
beforeModel() {
this._super(...arguments);
return new EmberPromise((resolve) => {
if (authenticated) {
resolve();
return;
}
this.transitionTo('login');
reject();
});
}
});
My test was fine. It was the mixin that needed updating in order to work properly with Ember 3.0 and the latest testing syntax.
The issue was not what your beforeModel hook resolves if user is authenticated but that your Promise does not resolve at all. You don't have to return a Promise in beforeModel hook but if you return one it will block the transition until the Promise is resolved. Since it's not clear how ember should react if another transition is called while current transition is blocked (not resolved/rejected promise), resolving or rejecting is correct behavior. Please have in mind that in a Promise return does not have any other meaning than ending your execution. It does not resolve or reject your Promise.
Another reason visit() could hang is that it waits for timers like Ember.run.later() to resolve, causing a non-obvious block somewhere in the application.
AlphaGit on github summarized the issue with an example, saying:
Most of the actions that Ember.testing executes (like visit) will
append to a promise that gets executed action after action in the
right order. In order to pass to the next action, Ember.testing makes
sure that there is nothing pending, so that the step can be considered
complete and move forward.
Along with the things that are tested for, pending AJAX requests are
verified, and also scheduled timers. These timers may arise from, you
guessed it, Ember.run.later calls. If for any reason you would have in
your code periodic Ember.run.later methods (so that one is always
waiting to be excuted), it's likely that you'll face this issue.
I've faced it myself in a similar scenario: My server returns a OAuth
access token with 100 hours until expired, so ember-simpleAuth
registers a call close to the expiration time with Ember.run.later to
refresh the token. This will, however, prevent the test from moving
along. My specific situations has been fixed in further versions but
any similar behavior will reproduce the issue (which is likely a
conclusion of the current design of Ember.testing).
Here are a couple other examples of users running into similar issues:
https://stackoverflow.com/a/58526993/3257984
https://stackoverflow.com/a/27887807/3257984

Ember Mirage: route answered only if debugger is ran

I'm facing a weird issue with Ember mirage. I try to use it inside an integration test.
The code looks like this:
moduleForComponent('editors/steps/call-handler', 'Integration | Component | editors/steps/call handler', {
integration: true,
beforeEach: function () {
startApp();
this.server = startMirage();
...
},
afterEach: function () {
this.server.shutdown();
}
});
test('...', function (assert) {
...
this.server.post('/projects/12/actionwords/1/switch/', () => {
assert.ok(true, "switch route is called");
return {
... some JSON data
}
});
this.render(hbs`{{editors/steps/call-handler
...
}}`);
... and then some manipulation of the component
});
Now the test fails when I run it this way. The weird part is, if I place a debugger just before rendering the component, the test will pass.
I guess there are some timeout issues sooner or later that the debugger hides. Usually, I would only render the component in a callback to ensure the route has been correctly setup but this.server.post returns nothing not has any callbacks (except if I missed something in the doc).
I also tried setting the timing option (just in case) but it doesn't change anything (as expected).
I also tried to add an homemade sleep function to wait one second without the debugger and then the tests work fine. Of course if I can have a clean way to get them working, I'd prefer ;)
Did anyone face the same kind of issues and found a way to solve it ?
Best regards,
Vincent

How to test Ember error substate, with Ember CLI test runner?

I've set up an error substate route/controller/template according to http://guides.emberjs.com/v2.2.0/routing/loading-and-error-substates/. Manually browsing my app, I can trigger error conditions and get directed to the substate. Confirmed with Ember Inspector.
I'd like to automatically test the substate. However, Ember CLI's test runner fails any test when a route's model hook rejects. In other words, the test fails before I can navigate to the error substate.
How can I automatically test my error substate?
Ember: 2.2.0
Ember CLI: 1.13.13
Unfortunately it doesn't seem to be easy to do this in a clean manner.
In its internal tests, Ember uses bootApplication to the route which errors (see github) and is able to directly catch the error. Unfortunately if you try and do any form of try/catch or then/catch around a call to visit in your tests you will find it fails.
When you visit a link which results in an error substate from your acceptance test then Ember's defaultActionHandlers.error gets fired. By design it is not meant to be overridable. It calls logError which calls Ember.default.Logger.error.
So to test this substate we need to temporarily overwrite that method. We can also peek inside the ember container to access the currentRouteName like so (using sinon for the spying):
test('when there is an API error an error message is shown', function(assert) {
const emberLoggerError = Ember.Logger.error;
Ember.Logger.error = sinon.spy();
visit('/users/');
andThen(() => {
// This could be nicer and less private with `getOwner`
let { currentRouteName } = this.application.__container__.lookup('router:main');
assert.equal(currentRouteName, 'users.index_error', 'The current route name is correct');
assert.equal(Ember.Logger.error.callCount, 1, 'The error logger was called');
// Restore the Ember.Logger
Ember.Logger.error = emberLoggerError;
});
});
Things can get even more complicated though. If your visit happens inside a Promise (it did in our case because we were using ember-page-object for our tests) then you have more to deal with...
In a separate loop onerrorDefault of RSVP is triggered which calls Test.adapter.exception AND Ember.default.Logger.error (again!) - passing the stack. So in this case you need to stub and spy on Test.adapter.exception and expect Ember.default.Logger.error to have been called twice!

Can't figure out how to wrap function in Ember run loop

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

Errors from debounced function calls in testing ember application

I'm testing an relatively large Ember application (http://wheelmap.org/map) with QUnit and having problems with debounced calls e.g. changing the url to have a permalink of a map view inside the app or doing a manual AJAX request while testing.
I followed the documentation at http://emberjs.com/guides/testing/integration/
Now when I reset the application state by calling App.reset() in the module setup it resets all bindings, etc. to variables and dependant controllers.
module('Map', {
setup: function() {
App.reset();
}
});
This seems to be good to have a clean working environment, but leads to errors where variables are accessiable by Ember.set and Ember.get e.g. this.get('controllers.toolbar'):
Cannot call method 'set' of null
So the first test allways runs great, but further tests break because of debounced function calls from the first test. So what I think I have to do is stop this debounced calls somehow.
Other options would be checking if all needed variables are set in this function calls. But this seems to be cumbersome when adding conditions only for testing.
What do you think?
Thank you in advance!
I found the answer by searching through the RunLoop source files:
Ember.run.cancelTimers()
It's not part of the documentation. Maybe a problem of poor documentation or not beeing part of the public API.
Now I just call it in the module test teardown function:
module('Map', {
setup: function() {
// ...
},
teardown: function() {
Ember.run.cancelTimers()
}
});
We ran into a similar problem and decided to disable debounce during testing.
You can check if in testing mode using if(Ember.testing){...}.