Prevent protractor from exiting before jasmine reporter completes - jasmine

I've created a jasmine reporter that performs an slow async task, but protractor is exiting before the reporter's work is completed. How to make protractor wait for the slow reporter to complete?
In my reporter, the slow task is simulated by the setTimeout :
// SlowReporter.js
SlowReporter.prototype.reportRunnerResults = function() {
var p = q.defer();
setTimeout(function() {
console.log("Slow reporting op finished");
p.resolve();
}, 10000);
return p.promise;
};
But, when I wire it in,
// protractor.conf.js
onPrepare: function() {
jasmine.getEnv().addReporter(new SlowReporter());
}
..protractor exits before the slow task completes. Protractor doesn't seem to respect the promise returned by the reporter.
Please note I'm not asking how to make protractor wait on an async test, rather how to make protractor wait on a jasmine reporter after the tests complete.

FFR solved by defining a .completed() method in SlowReporter which returns a promise that is resolved when the reporter completes; then have protractor.spec.js return this promise in the onCleanUp(). Only works with protractor >= 1.1.0 .
Open to better answers/approaches.

Related

How to listen global events with Cypress?

We have an application that polls the server periodically until a task is completed. We fire a global event so that Cypress can catch and find out if the task is finished but we had trouble using document.addEventListener on Cypress. Here's what we're doing:
document.addEventListener('queryEnd', () => {
cy.get('.chart').should('be.visible')
cy.get('.table').should('be.visible')
})
However; when we use it in a spec, it doesn't work expected and we're not able to catch it. Also, Cypress doesn't wait for the test and runs afterEach without waiting for the callback to run.
The reason why your code isn't working like you expect is because in Cypress, your tests run in a separate frame than your application under test (AUT). The event you're waiting for will never fire inside of Cypress's document.
To get the document of the AUT, use cy.document() like this:
cy.document()
.then($document => {
// now $document is a reference to the AUT Document
$document.addEventListener(...)
})
To make Cypress wait for your event before continuing, you can just wrap it in a Cypress.Promise. The Cypress docs have an example about waiting for a Promise to complete. For your queryEnd event, it would look something like this:
cy.document() // get a handle for the document
.then($document => {
return new Cypress.Promise(resolve => { // Cypress will wait for this Promise to resolve
const onQueryEnd = () => {
$document.removeEventListener('queryEnd', onQueryEnd) // cleanup
resolve() // resolve and allow Cypress to continue
}
$document.addEventListener('queryEnd', onQueryEnd)
})
})
.then(() => {
cy.get('.chart').should('be.visible')
cy.get('.table').should('be.visible')
})

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"

mocha returns a promise, false positive passes, and throws exception indicating error

I'm writing unit tests using Mocha and shouldjs, and bluebird.
According to the documentation (http://shouldjs.github.io/#assertion-finally) I should be able to return a Promise, and get it tested.
It is being run, but not tested. An assertion is thrown, but the test seemingly passes
Here is my code. It's pretty well straight out of the shouldjs docs:
'use strict';
require('should');
var Promise = require('bluebird');
describe('demo should error', function () {
it('I should fail - but Im not', function () {
var prm = new Promise(function(resolve, reject) { resolve(10); });
return prm.should.be.finally.equal(9);
});
});
When I run this in mocha, I get the following:
>>> mocha tests/demo.js
(node) child_process: options.customFds option is deprecated. Use options.stdio instead.
․Unhandled rejection AssertionError: expected 10 to be 9
at Assertion.fail (/Users/andrew/projects/DELETE_ME/2016-02-07/node_modules/should/lib/assertion.js:91:17)
at Assertion.Object.defineProperty.value (/Users/andrew/projects/DELETE_ME/2016-02-07/node_modules/should/lib/assertion.js:163:19)
...
1 passing (14ms)
So an exception is thrown, but the test seemingly passes.
I also get a false positive when I use native Promise, not bluebird, but the stack trace isn't shown.
Any help gratefully received...
I was using an outdated Mocha...
npm i -g mocha
Did the trick

protractor fdescribe and fit does not work with jasmine-reporters

I have this in my conf.js
onPrepare: function() {
/**if comment out then spec runs, if uncomment then spec doesn't run
var jasmineReporters = require('jasmine-reporters');
jasmine.getEnv().addReporter(new jasmineReporters.JUnitXmlReporter({
consolidateAll: true,
filePrefix: 'portal_tests_xmloutput',
savePath: './test_results_report'
})); **/
},
};
in my test_spec.js I have
fdescribe('my tests', function() {
it('test1', function(){
});
fit('test2', function(){
});
it('test3', function(){
});
}
When I run my tests it will output
[launcher] Running 1 instances of WebDriver
Started
No specs found
Finished in 0.002 seconds
if I comment out the jasmine reporters declaration in my conf.js and run again it will run the correct test and output
[launcher] Running 1 instances of WebDriver
Started
test2
.
1 spec, 0 failures
Finished in 5.675 seconds
Why is jasmine-reporters interfering with focused spec testing? I would like to have jasmine-reporters enable while running with fdescribe and fit. I'm using
protractor: 1.8.0
jasmine-reporters: 2.0.4
The problem is solved in jasmine-reporters#2.0.5.
Apparently when using fit, jasmine does not call the suiteStarted or suiteDone callbacks. jasmine-reporters relies upon these methods being called, and was blowing up as a result. As of 2.0.5, it now tries to detect if these methods have been called and adapt.
Did you provide the framework='jasmine2' setting in your config file? https://github.com/angular/protractor/blob/master/docs/referenceConf.js#L253

Changing message for timeout using Jasmine done

For now I have the following code...
it("takes a long time", function(done) {});
Problem is when it times out I get the following message...
Error: Timeout - Async callback was not invoked within timeout
specified by jasmine.DEFAULT_TIMEOUT_INTERVAL.
Can I change this message to something more specific?
That is an error regarding your test, not an error of the code you're testing. If your test is properly written, you shouldn't encounter that error. The test itself is timing out. If you're writing a test to see how/whether some code times out, you would write the test to handle that, rather than the test just throwing an error because you didn't call done() in time.
// this function times out if passed "true" as second argument
function testThis(done, shouldTimeout) {
setTimeout(function() {
shouldTimeout || done();
}, 500);
}
it("timed out", function(done) {
var timedOut = false;
setTimeout(function() {
testThis(function() {
// this shouldn't be called because we're telling `testThis` to timeout
timedOut = true;
}. true);
});
setTimeout(function() {
expect(timedOut).toBeFalsy();
done();
}, 505);
});
If you needed to wait a long time to see if something times out, you can change how long Jasmine will wait before a test throws a timeout error by changing jasmine.DEFAULT_TIMEOUT_INTERVAL = 10000;
Jasmine does have a mock for setInterval and setTimeout that let you run that code synchronously, and saves all that wasted time waiting.

Resources