function to take screenshots for cucumber-html-reporter generates "function timed out after 5000.." error - protractor

I am using protractor-cucumber-framework and I wanted to generate html report for the tests I wrote. I decided to use cucumber-html-reporter to achieve it. In my hooks.js I wrote a this.After object to take screenshot on test failure:
this.After(function(scenario, callback) {
if (scenario.isFailed()) {
browser.takeScreenshot().then(function(buffer) {
return scenario.attach(new Buffer(buffer, 'base64'), function(err) {
callback(err);
});
});
}
else {
callback();
}
});
Everything works just fine, the report is generated and the screenshots are taken and attached only on test failure. But I also got an error message when After step is proceeded (so when there is some failure):
function timed out after 5000 milliseconds
I would like to get rid of this message as it also appears on my html report. Can anyone provide me solution to do that?

Below code is working for me. I have added this in step definition js file. At the end of scenario in the report, it adds the screenshot.
defineSupportCode(({After}) => {
After(function(scenario) {
if (scenario.isFailed()) {
var attach = this.attach;
return browser.takeScreenshot().then(function(png) {
var decodedImage = new Buffer(png, "base64");
return attach(decodedImage, "image/png");
});
}
});
});

I had similar problem and it failed even after waiting for 60 seconds. The issue was that i didn't had proper callback implemented.
The below code worked for me. ( I am new to JavaScript and so my callback usage might be the right way. Please feel free to educate me if there is better way to do it. :))
After(function(scenario,done)
{
const world = this;
if (scenario.result.status === 'failed') {
browser.takeScreenshot().then(function (stream) {
let decodedImage = new Buffer(stream.replace(/^data:image\/(png|gif|jpeg);base64,/, ''), 'base64');
world.attach(decodedImage, 'image/png');
}).then(function () {
done();
});
}else {
done();
}
});

Your code seems absolutely fine.
Perhaps, it just needs longer timeouts?
You can set a timeout on hooks like this:
this.After({ timeout: 20 * 1000 }, function (scenario, callback) {
if (scenario.isFailed()) {
browser.takeScreenshot().then(function(buffer) {
return scenario.attach(new Buffer(buffer, 'base64'), function(err) {
callback(err);
});
});
}
else {
callback();
}
});

Related

Screenshot is not attached to report but available in 'allure-results' folder or failure Specs screenshot

In Allure Reporters [with Protractor], The screenshot which is being taken is only at the end of the test. Due to which the screenshot which is showing in the Allure Reports are of login page only and not of the application once user logged in.
Here is the code, I am using to generate the Allure Reports.
allureReporterSetup: function() {
const AllureReporter = require('jasmine-allure-reporter');
jasmine.getEnv().addReporter(new AllureReporter({
resultsDir: 'allure-results'
}));
jasmine.getEnv().afterEach(function (done) {
browser.takeScreenshot().then(function (png) {
allure.createAttachment('Screenshot', function () {
return new Buffer(png, 'base64')
}, 'image/png')();
done();
})
});
},
This may help you
onPrepare() {
var originalAddExpectationResult = jasmine.Spec.prototype.addExpectationResult;
jasmine.Spec.prototype.addExpectationResult = function () {
if (!arguments[0]) {
browser.takeScreenshot().then(function (png) {
allure.createAttachment('Screenshot', function () {
return new Buffer(png, 'base64')
}, 'image/png')();
})
}
return originalAddExpectationResult.apply(this, arguments);
};
var AllureReporter = require('jasmine-allure-reporter');
jasmine.getEnv().addReporter(new AllureReporter({
resultsDir: 'allure-results',
}));
}
As I understand Protractor is a tool for testing Angular JS web sites. It's quite like the Selenium so it is not anyhow connected with your issue.
Your issue is connected with a testing framework that you use.
What happens now is your screenshot is probably taken at the end of a test (after scenario/after test) or even after test suite.
What you need is to take the screenshot right after test step or even right after a test failure. I guess after your test failure some teardown actions are performed. The thing is you need to take a screenshot before those actions. This way it will be taken in time.

Changed values are lost after returning from promise

I am doing E2E Test with protractor, using cucumber for test scenarios.
I face a strange problem: I lost the values I set inside of the promise.
What am I doing wrong?
My step-file.js:
var loggedUser = new User("dummyname", "dummyrole");
this.When(/^I click on a user name$/, function(){
userelem.element(by.className('username')).getText().then(function (txt) {
loggedUser.username = txt;
});
});
this.Then(/^The username of the object "loggedUser" is set to a new value$/, function(){
var answer = "dummyname" != loggedUser.username;
assert.isOk(answer, "username is still dummyname!"); //this line fails since usrname is set back to dummyname again!
});
Thank you #yong for your suggestion on my first post (which I have deleted meanwhile). Finally I understood what you meant. Here is the solution:
(Instead of upgrading to cucumber 2,) I "return" the promise from within the "When-Step", so that protractor waits until it is fullfilled before it executes the next step-function:
var loggedUser = new User("dummyname", "dummyrole");
this.When(/^I click on a user name$/, function(){
return userelem.element(by.className('username')).getText().then(function (txt) {
loggedUser.username = txt;
});
});
Now in the next step-functions, the username is the updated username.
The root cause is you need to make each step definition return a promise, I write a simple code for you and it worked well.
Make step definition return a promise is the key point.
For Cucumber 2:
var { defineSupportCode } = require("cucumber");
defineSupportCode(function({ Given, When, Then }) {
let title = 'test';
Given(/^open npmjs.com$/, function() {
browser.get("http://www.npmjs.com");
return browser.getTitle().then(function(title1){
title = title1;
console.log('title: ' + title);
});
});
Then(/^verify page title$/, function() {
return expect(title).to.equal('npm');
});
});
For Cucumber 1:
module.exports = function() {
let title = 'test';
this.Given(/^open npmjs.com$/, function() {
browser.get("http://www.npmjs.com");
return browser.getTitle().then(function(title1){
title = title1;
console.log('title: ' + title);
});
});
this.Then(/^verify page title$/, function() {
return expect(title).to.equal('npm');
});
};

Protractor: browser.wait function, isElementPresent times out

Here is my code. For some reason it cannot detect the element present, and just times out. Site is in angular. I have tried isPresent, as well as ExpectedConditions and it times out nonetheless. For some reason, it just cannot detect the element no matter how I try to locate it. I have tried multiple elements as well. I'm open to any ideas.
browser.wait(function()
{
return browser.isElementPresent(by.xpath('//[#id="ngdialog1"]/div[2]/div/div')).then(function(present)
{
console.log('\n' + 'looking for element')
if(present)
{
console.log('\n' + 'recognized dialog');
var jccSelect = element(by.xpath('//*[#id="ghId_GameSelectBottomRow"]/div[1]'));
jccSelect.click();
return true;
}
})}, 50000);
});
You have kept return statement in if(present){return true;}, if present value is false then control will not be return, that's why your getting timed out issue.
I have rearranged the code as below:
EC = protractor.ExpectedConditions;
targetElement=element(by.xpath('//[#id="ngdialog1"]/div[2]/div/div'));
browser.wait(function(){
return EC.visibilityOf(targetElement).call().then(function(present){
console.log('\n' + 'looking for element')
if(present)
{
//do what would you like to do
return true;
}
else{
//do what would you like to do
return false;
}
});
}, 50000);

Promise and Callback

The below lines of code has been written by one of my colleague. This code is working for me, but I am actually looking for an explanation for each line. specially It is confusing with callback. It will be great if anyone can comment each line.
Thanks
function startApp(done) {
console.log("Inside startApp");
browser.get(baseUrl).then(
function (success) {
console.log("LOG MESSAGE - URL launched successfully");
browser.waitForAngular();
done();
},
function (failure) {
console.log("LOG MESSAGE - Fail to launch URL");
done(failure);
}
);
}

How to I find an element with protractor?

It seems if I use element(by.css('#id')); protractor sometimes finds it, sometimes doesn't. Depends if the DOM is settled down or not. I've gone and some this:
getElementsByCss: function (cssVal) {
// return element.all((by.css(cssVal)));
return self.wait(function () {
return element.all((by.css(cssVal))).then(function (els) {
return els;
}, function () {
return false; // Don't fail, we want to retry
});
}, GET_ELEMENT_TIMEOUT, 'Elements not found by css: ' + cssVal);
},
which plays a game of retrying to get the element for some timeout period. We use 5 seconds. This seems to work, most of the time. When running locally we have no issues.
When we run on the cloud (sauce labs) they have exceedingly slow VMs. We still get random issues. Unfortunately these games have to be played everywhere, for example:
getText: function (el) {
return self.wait(function () {
return el.getText();
}, 1000, 'Could not get element text.');
},
or
expectBulletin: function () {
var el, text;
return self.wait(function () {
// Find the bulleting element, make sure it's visible and grab its text.
// If any of those three fail, try all of them again.
return self.getElementByCss('.bulletin')
.then(function (elm) {
el = elm;
return self.isElementVisible(el);
}, function () {
return false;
})
.then(function () {
return self.getText(el).then(function (text) {
return text;
}, function () {
return false;
});
}, function () {
return false;
});
}, 10000, 'Count not get bulletin text.')
.then(function (result) {
text = result;
return self.executeScript('arguments[0].click();', el.getWebElement());
})
.then(function () {
return self.isElementNotVisible(el);
})
.then(function () {
return text;
});
},
all the self.waits are just a browser.driver.wait wrapper...
wait: function (fn, timeout, msg) {
return browser.driver.wait(fn, timeout, msg);
},
This feels like a big pack of bandaids and its not working all the time. expectBulletin works 99% of the time locally, but when run remotely on the slow VMs, it works about 50% of the time. Sometimes the text comes back blank, or issues about clicking an invisible element or just not finding the bulletin element.
Is there any better way to do this?
it seems if I use element(by.css('#id')); protractor sometimes finds
it, sometimes doesn't.
You know that it returns a promise right? If you want to get the absolute value of an element, you have to handle its promise first.
Protractor waits for the page to be ready before doing any assertions. If your page is "ready" but some data is missing, there is something wrong with your angular application.