I am using cucumber-js to run tests with selenium-webdriver.
I want to add a screenshot capture of the browser when any step times out.
I am using global timeout for all the steps as:
this.setDefaultTimeout(3 * 60 * 1000);
in my hooks file.
How do I register to the global timeout event (if such even exists)?
Selenium Webdriver js do provide function to get screenshot, you just need to use it in After, which is similar to #AfterClass tag in TestNG
The After scenario will execute after every scenario in Feature and check the result of the Scenario, it it is FAILED it will take the screenshot.
The reason for failure can be anything, like a bug, or DEFAULT_TIMEOUT
You need to add this in your world.js
this.After(function (scenario) {
if (scenario.isFailed()) {
// take a screenshot
// driver.takeScreenshot() is defined in webDriver.js
return driver.takeScreenshot()
.then(function (screenShot) {
scenario.attach(new Buffer(screenShot, 'base64'), 'image/png');
return driver.close()
.then(function () {
return driver.quit();
});
});
}
else {
return driver.close()
.then(function () {
return driver.quit();
});
}
});
Related
I use selenium in javascript along with jest to do some end-to-end testing of an application. Eventually those tests runs in headless mode on Azure Pipelines
To help debugging, I would like to have a video record in case a test fails. I could not find any package which would work with jest and selenium.
I already managed to upload a screenshot in case of failure so I have takeScreenshot working.
For the video, I wanted to try to take a picture every second before "compiling" it with ffpmeg but I have a memory issue.
FATAL ERROR: Reached heap limit Allocation failed - JavaScript heap out of memory
Am I on the right track?
public async startRecording() {
this.recording = true;
let count = 1;
let interval;
while (this.recording) {
interval = setInterval(async () => {
await this.takeScreenshot(`test_${count}`);
count += 1;
}, 1000);
}
clearInterval(interval);
}
public stopRecording() {
this.recording = false;
}
I'm trying to use Page objects with multiple browser instances in a single test script.. i got a solution but i'm looking for some better solution.
My PageObject file
var facebook=function(browserInstance){
browserInit=browserInstance;
element=browserInit.element;
this.email=function(){
return element(By.id("email"));
}
this.password=function(){
return element(By.id("pass"));
}
this.submit=function(){
return element(By.xpath("//input[#aria-label='Log In']"));
}
}
module.exports=facebook;
usage of Page objects with multiple browser
this.When(/^User entered the text in the email field$/,async function(){
// for using in browser2 i need to initialize it
page2=new facebook(browser2);
console.log(await page2.title()+" browser2");
await page2.search().sendKeys("testing");
browser2.sleep(3000);
//for using in first browser i need to initialize it
page1=new facebook(firstBrowser);
console.log(await page1.title()+" browser1")
await page1.email().sendKeys(testData.Login.CM[0].Username);
screenshots.takeScreenshot("newScreenshot");
firstBrowser.sleep(5000);
//now if i want again in browser2.. i need to initialize the page2 with browser instance again like below
page2=new facebook(browser2);
console.log(await page2.title()+" browser2");
});
how to overcome this initialization every time
I think you can do something like this in your conf.js file
baseUrl: 'http://localhost:8081',
onPrepare: function() {
global.browsers = {
a: browser,
b: browser.forkNewDriverInstance()
};
}
And inside the spec file or the step definition file, you can have
this.When(/^User entered the text in the email field$/,async function(){
let browser1 = browsers.a;
let browser2 = browsers.b
//do your stuff
.
.
.
.
.
});
Let me explain the situation, I'm currently testing an AngularJS website with Protractor, I'm not developing it at all.
The problem is that, absolutely randomly the website is sometime not rendered. It's just blank with a different title :
If title is "VideoGame" -> then website is rendered
If title is "VideoGame-env-DEV" -> Then the website isn't rendered
No idea why and it's not my concern.
However, when testing with Protractor, it's obviously crashing.
So I wanted to bypass the problem with a temporary solution :
function specialRefreshFunction() {
cpt=0;
browser.get('https://blablabla');
browser.waitForAngular();
var title = browser.getTitle();
while (title != "VideoGame") {
cpt=cpt+1;
//sleep(1000);
//browser.sleep(1000);
browser.get('https://blablabla');
browser.waitForAngular();
//browser.navigate().refresh();
console.log("fail "+cpt);
}
console.log("Correct title " + title);}
My solution isn't working. I'm a total newbie with NodeJS & Protractor, so the code might be a nonsense.
W10 64bits
Latest Chromedriver
Protractor 5.4.2
NodeJS 10.15.0
Angular 6.1.10
Thank you !
You can go with browser.wait
let EC = protractor.ExpectedConditions;
browser.wait(
EC.presenceOf($('#some-element')),
10000,
'Element did not appear after route change'
).then(function() {}, function() {
browser.refresh();
});
In this particular example protractor will wait 10 sec for element #some-element to appear on the page. browser.wait returns a promise, so you can handle both cases - when element appear or element did not appear. Just put some specific selector instead of $('#some-element') that present on your page, by which you can consider page as loaded
Though, if you rely only on title value, you can use titleContains
var EC = protractor.ExpectedConditions;
// Waits for the title to contain 'foo'.
browser.wait(EC.titleContains('foo'), 5000);
Try to create a custom promise like that:
public vierifyPageTitle() {
return new Promise((resolve, reject) => {
const refreshUserData = setInterval(() => {
return browser.getTitle().then((title) => {
if (title === 'VideoGame') {
clearInterval(refreshUserData);
resolve(title);
}
}, (err) => {
browser.refresh();
reject(err);
});
}, 1000);
});
}
It's refreshing every 1000 ms if title is not equal to expected.
Got the following code:
On AngularJS client:
var getDocs = function() {
return $http.get('/api/docs/list')
.then(function(result) {
return result.data; // bookmark1 line
});
}
On Node.Js BE:
function getDocs() {
return Doc.find({}).exec()
.then(function success(docs) {
return res.send(docs);
});
}
Works perfectly.
When I change the first line in getDocs() to
return Doc.find({}).select('-field1 -field2').exec()
I see the docs after the query execution without the fields (as expected) but for some reason The http is pending and chrome debugger is not stopping on the success callback of the client (bookmark1 line).
Edit:
Changed to
return Cv.find({}).select('-field1 -field2').exec(function success(err, cvs) {
return res.send(cvs);
});
Got the following error in client:
Solution:
Well It turn out I had another field which depends on field1
docSchema.virtual('field3').get(function () {
return this.field1.toString("base64");
});
It fails and for some reason it didnt appear on my IDE.
I Added a null check and that solve the problem.
Not sure if I should remove this question or maybe it has value for other people. Feel free to say what you think.
I have a test that clicks a button and redirects to a user dashboard. When this happens Webdriver returns:
javascript error: document unloaded while waiting for result.
To fix this I insert browser.sleep(2000) at the point where redirection occurs and assuming my CPU usage is low, this solves the issue. However, 2000 ms is arbitrary and slow. Is there something like browser.waitForAngular() that will wait for the angular to load on the redirected page before the expect(..)?
it('should create a new user', () => {
$signUp.click();
$email.sendKeys((new Date().getTime()) + '#.com');
$password.sendKeys('12345');
$submit.click();
browser.sleep(2000); // Need alternative to sleep...
// This doesn't do it...
// browser.sleep(1);
// browser.waitForAngular();
$body.evaluate('user')
.then((user) => {
expect(user).toBe(true);
});
});
do you think something like this could work for you? This will wait up to 10 seconds for the url to include the text 'pageTwo', or whatever you put in.
var nextPageButton = $('#nextPage');
nextPageButton.click().then(function(){
return browser.driver.wait(function() {
return browser.driver.getCurrentUrl().then(function(url) {
return /pageTwo/.test(url);
});
}, 10000);
};
Just stick in the regex of the url you are expecting.
Alternatively, you could wait for an element from the next page to appear as well:
var nextPageButton = $('#nextPage');
nextPageButton.click();
var elementFromSecondPage = $('#coolElement');
browser.wait(protractor.until.elementIsVisible(elementFromSecondPage), 5000, 'Error: Element did not display within 5 seconds');
When using .click, protractor will naturally wait for angular to finish the action attached to the click, such as changing the page. But, while the page change, you may still be needing something specific to be loaded, so the test fails before that part is available. Using this, it should wait for the click part to finish, then wait for the element to appear.
To expand on user2020347's answer:
Thanks that solved my issue. I wonder why this isn't a built in function. I'll be using this in many places to wait for browser navigation.
To make it more concise, I made a little helper:
Object.assign(global, {
waitUntilURLContains: string => {
let fn = () => {
return browser.driver.wait(() => {
return browser.driver.getCurrentUrl().then((url) => {
return url.includes(string);
});
}, waitDelay);
}
return fn.bind(null, string);
}
});
In my test:
$button.click().then(waitUntilURLContains('dashboard'));
keeping it very simple. I was also running into the same problem but was able to solve it using the following code :
page.setUsername(objectrepository.userdetails.useremail);
page.setPassword(objectrepository.userdetails.userpassword);
page.login().click();
**browser.wait(EC.visibilityOf(page.greetingMessageElement()), 5000);**
page.greetingMessageElement().getText()
.then(function (value){
expect(browser.getCurrentUrl()).toContain("#/mytickets");
});