Running spec after promise has been resolved - jasmine

I came across an issue with running a spec that should be executed after a promise has been resolved. See the commented simplified example below.
I tried using IIFE or calling done() function in the spec but none of these seemed to work.
// getIds() is a simple promise which returns an array of ids
getIds().then(function (ids) {
console.log('IDS: ' + ids); // all good so far
// This test is never run
it('dummy test', function () {
console.log('TEST HAS BEEN RUN');
});
});

You can use browser.wait() to wait until your promise is complete. Or you can put your test inside the then block:
it('should test', function() {
getIds().then(function (ids) {
// some action.
expect()...
});
});
Also, you can put the promise in a beforeEach or a beforeAll (jasmine 2). Assign the ids to a variable declared inside a describe. The value should be available for your test to use.

Related

Mocha timing out when test in Promise callback fails

If I have the following module:
module.exports = kontinue => {
Promise.resolve({error:null})
.then(o => {
console.log('promise resolved');
// say something goes wrong here
if(true)
kontinue({error:'promise resolved but something else went wrong'});
else kontinue(o);
})
.catch(error => {
console.log('caught error');
kontinue({error:'promise rejected, or resolved but then continuation threw exception'})
});
};
And the following test:
const assert = require('assert').strict;
const target = require('./the_above_code.js');
it('should not timeout', (done) => {
target((sut) => {
console.log('continuation called');
assert.ok(false); // the test for sut.error === what I expected was failing
done();
});
});
It outputs:
promise resolved
continuation called
caught error
...
Error: Timeout of 2000ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves.
I realise this is because the .catch() is returning a new promise which is not resolving, but that's not what I really want during testing.
How do I test the object a promise resolves to, fail the test if necessary, have Mocha report that failure?
Perhaps there is somewhere else other than in the continuation (which never returns in the code that uses this module) that I can put the tests?
I'm sure monads can reduce the amount of boilerplate code here, but using them surely would violate Kernighan's maxim.

RSVP Promises: Why does parent promise wait on non-chained inner promise?

We're using an older version of RSVP.js in several apps we built.
I expected this promise to resolve immediately while outputting "1", "2", and after 5 seconds "3". I thought the inner promise would be treated like a value.
new RSVP.Promise(function (resolve) {
console.log('1');
var p = new RSVP.Promise(function (innerResolve) {
setTimeout(function () {
console.log('3');
innerResolve();
}, 5000);
});
resolve(p);
}).then(function () {
console.log('2');
});
Instead, the inner promise appears to be treated as a chained promise.
So the actual output above is: "1", 5 second delay, "3", "2".
It was always my understanding from the RSVP documentation (https://github.com/tildeio/rsvp.js/#chaining) that chained promises have to follow a certain format. For example, if I actually wanted the latter behavior (1, 5sec delay, 3, 2), I would have written the code this way:
new RSVP.Promise(function (resolve) {
console.log('1. Simply resolve a value which happens to be a promise');
resolve();
}).then(function () {
var p = new RSVP.Promise(function (innerResolve) {
setTimeout(function () {
console.log('3. This took a long time!');
innerResolve();
}, 5000);
});
// Return a promise in a then() to chain it!
return p;
}).then(function () {
console.log('2. You should see this immediately!');
});
The reason why I'm asking is because the parent promise is outside of my control (it's part of some internal framework plumbing code). My function just gets passed the parent promise's resolve and reject functions.
function myFunction(parentResolve, parentReject) {
// my code here..
parentResolve(new RSVP.Promise(...)); // want to resolve promise as value
}
My workaround is to wrap the promise in an object, but it's not ideal.
new RSVP.Promise(function (parentResolve) { // no control over this
console.log('1');
// my code here..
var p = new RSVP.Promise(function (innerResolve) {
setTimeout(function () {
console.log('3');
innerResolve();
}, 5000);
});
// Wrap it in an object
parentResolve({result: p});
}).then(function () {
console.log('2');
});
Is RSVP's chaining behavior correct? Is there a better solution than wrapping my promise in an object? I may try upgrading to the latest version of RSVP to see if it's simply due to my outdated version.
I thought the inner promise would be treated like a value.
No, unfortunately resolve never does that. It always resolves thenables, there is no way to fulfill with a promise.
Is RSVP's chaining behaviour correct?
Yes.
Is there a better solution than wrapping my promise in an object?
Not really. But I wouldn't consider that a workaround. Either you want to transport some data that should be available immediately, before the inner promise resolves - then that data should go on the wrapper object. Or you don't need anything before the inner promise resolves and you want to await it anyway - then the default behaviour of resolving a promise with a promise is just the right thing for you. See also this answer.

what done() is for and how to use it ( protractor, jasmine)

it('should for something', function check(done) {
browser.sleep(2000);
$('.csTx').isPresent().then(function(result) {
if(result) {
done();
} else {
xPage.clickBack();
check(done);
}
})
}, 30000);
Can someone explain how done() works and what this is for. I googled it but cannot find any information that would be easy enough for me to understand. I am automating with protractor and jasmine. please consider the above code.
You need to use done if your test creates a parallel TaskQueue in your test's Control Flow (read more about promises and control flow).
For example:
describe('Control Flow', function() {
function logFromPromise(text) {
var deferred = protractor.promise.defer();
deferred.then(function() {
console.log(text);
});
deferred.fulfill();
return deferred;
}
it('multiple control flows', function() {
setTimeout(function() {
logFromPromise('1');
});
logFromPromise('0');
});
}
Calling setTime creates a parallel Task Queue in the control:
ControlFlow
| TaskQueue
| | Task<Run fit("multiple control flows") in control flow>
| | | TaskQueue
| | | | Task <logFromPromise('0');>
| TaskQueue
| | Task <setTimeout>
Protractor thinks the test is "done" after 0 is printed. In this example, 1 will probably be printed after the test is completed. To make protractor wait for Task <setTimeout>, you need to call the done function:
it('multiple control flows', function(done) {
setTimeout(function() {
logFromPromise('1').then(function() {
done();
});
});
logFromPromise('0');
});
If you can, let protractor handle when the test is "done". Having parallel TaskQueues can lead to unexpected race conditions in your test.
Here is a sample describe that you can run and see what happens. I have to mention that I don't use Protractor so there might exist some additional considerations to be made concerning its specific capabilities.
describe('Done functionality', function(){
var echoInOneSecond = function(value){
console.log('creating promise for ', value);
return new Promise(function(resolve, reject){
console.log('resolving with ', value);
resolve(value);
});
};
it('#1 this will untruly PASS', function(){
var p = echoInOneSecond('value #1');
p.then(function(value){
console.log('#1 expecting...and value is ', value);
expect(value).toBe('value #1');
});
});
it('#2 this will NOT FAIL', function(){
var p = echoInOneSecond('value #2');
p.then(function(value){
console.log('#2 expecting... and value is ', value);
expect(value).not.toBe('value #2');
});
});
it('3 = will truly FAIl', function(done){
var p = echoInOneSecond('value #3');
p.then(function(value){
console.log('#3 expecting... and value is ', value);
expect(value).not.toBe('value #3');
done();
});
});
it('4 = this will truly PASS', function(done){
var p = echoInOneSecond('value #4');
p.then(function(value){
console.log('#4 expecting... and value is ', value);
expect(value).toBe('value #4');
done();
});
});
});
when running the test you will note the sequence: first promises #1, #2, #3 will be created and resolved one by one. Please note that expectation for #1 and #2 will not be run yet because promises are resolved asynchronously.
Then, since #3 test uses done, after #3 promise is created, functions for thens of all previous promises are evaluated: you will see '#1 expecting...' and '#2 expecting...', but jasmine won't care about that because tests #1 and #2 are already finished and everything concerning them done. Only after those #3 expectation is made and it will truly fail because jasmine does take care of everything that happens before done() is made.
And then you can watch #4 test normal flow -- creating promise, resolving, expectation, everything considered by jasmine so expectation will truly pass.
I haven't used Protractor. For Jasmine, my understanding is that done makes Jasmine wait but not in the traditional sense of timeout. It is not like a timer which is always run. I think done acts as a checkpoint in Jasmine. When Jasmine sees that a spec uses done, it knows that it cannot proceed to the next step (say run next spec or mark this spec as finished i.e. declare verdict of the current spec) unless the code leg containing done has been run.
For example, jasmine passes this spec even though it should fail as it doesn't wait for setTimeout to be called.
fit('lets check done',()=>{
let i=0;
setTimeout(function(){
console.log("in timeout");
expect(i).toBeTruthy();//the spec should fail as i is 0 but Jasmine passes it!
},1000);
//jasmine reaches this point and see there is no expectation so it passes the spec. It doesn't wait for the async setTimeout code to run
});
But if my intention is that Jasmine waits for the the async code in setTimeout, then I use done in the async code
fit('lets check done',(done)=>{
let i=0;
setTimeout(function(){
console.log("in timeout");
expect(i).toBeTruthy();//with done, the spec now correctly fails with reason Expected 0 to be truthy.
done();//this should make jasmine wait for this code leg to be called before declaring the verdict of this spec
},1000);
});
Note that done should be called where I want to check the assertions.
fit('lets check done',(done)=>{
let i=0;
setTimeout(function(){
console.log("in timeout");
expect(i).toBeTruthy();//done not used at the right place, so spec will incorrectly ypass again!.
//done should have been called here as I am asserting in this code leg.
},1000);
done();//using done here is not right as this code leg will be hit inn normal execution of it.
});
In summary, think of done as telling Jasmine - "I am done now" or "I'll be done when this code hits"

Nightwatch, afterEach, browser.pause is not a function

I would like to make pause each test.
I've create this function:
afterEach:function(browser){
browser.pause(2000);
},
But when I run tests, I will get error:
TypeError: browser.pause is not a function
Why ? In tests browser.pause is function.
The answer, provided by beatfactor, on the linked GitHub issue, is
When using afterEach you need to use the done callback argument always if you want to use the browser object. That is for backwards compatibility. So you need to do either:
afterEach(browser, done) {
// ...
done();
}
I've written on GitHub as issue and I've got a solution: https://github.com/nightwatchjs/nightwatch/issues/921
RESOLVE:
use
afterEach(done) {
// ...
done();
}
instead of
afterEach(browser, done) {
// ...
done();
}
When using afterEach, if you want access to the browser object then you need to add the done callback argument after browser to your function.
So your function should look like this:
afterEach(browser, done) {
// ...
done();
}
Note that you have to call done() to signify the completion of the test.
If you only have one argument in afterEach then it's the done argument.
afterEach(done) {
// ...
done();
}
In other words, if you write
afterEach(browser) {
// ...
}
browser is actually the done callback. You've named it browser but that's not what it is.
My dear friend you have to do "something" and then pause!
(i.e. assert the url and then pause!)
Does it works?
afterEach:function(browser){
browser
.assert.urlEquals('http://www.google.com')
.pause(2000)
},

Using Qunit module setUp tearDown to test ajax

I have been finding it quite difficult to get up and running with Qunit for testing jQuery ajax.
In particular I am stumped at trying to use Qunit's module construct with a setUp and tearDown method to reduce repeated code. The following works:
test("ajax request is 200 OK", function () {
var xhr = sinon.useFakeXMLHttpRequest();
var requests = sinon.requests = [];
xhr.onCreate = function (request) {
requests.push(request);
};
var callback_success = sinon.spy();
$.ajax('/course/data', {
success: callback_success,
});
equal(sinon.requests.length, 1);
equal(sinon.requests[0].url, "/course/data");
requests[0].respond(200, { "Content-Type": "application/json" }, '[]');
ok(callback_success.called);
});
I have a JSFiddle which shows a failing test (number 11). (The earlier tests I wrote as I was trying to get my head around everything).
Specifically my question is: why does the test report failure with 'requests is undefined' when I have declared var requests; at global scope on line 115?
Explanation gratefully received! (Edit: For some reason the JSFiddle linked shows problems with sinon.js, not evident when I run the JSFiddle from my fiddle account??)
Found solution to problem with requests being undefined in my JSFiddle. My test module's setup function was never being called. For some reason I was using camelCase to define setup and teardown:
setUp: function () { .....},
tearDown: function() {....}
When of course is should be just plain
setup: function () {...},
teardown: function () {...}
So, basically a case-sensitivity error on my part, compounded with the fact that I'm finding ajax, qunit and sinon quite mind-bending.

Resources