So basically I have some helpers method to help me debug my protractor test cases, one of my main ones is tho wait for an element to be clickable, I'm creating this loop to give the necessary time for protractor to find and make sure the element is enabled, but when an element is not found, either by the element not being found or a typo on my scrip, I would like the test run to STOP and mark it as a FAILURE..
async WaitToBeClickable(element){
try{
for(var i = 0; i <= 3000; i++){
var wait = await browser.wait(this.EC.elementToBeClickable(element), i);
if(wait == true){
break;
}else{
//this is where I want to fail
}
}
}catch(err){
//this is where I want to fail
await console.log(`WAIT TO BE CLICKABLE FAILED:\n${element.parentElementArrayFinder.locator_.value}\n\nError:\n${err}\n`);
}
};
this would help me a LOT debugging my script since I'm working on VSC, but I can not seem to find a way yet to make the test FAIL and thereby to CLOSE the browser at the first fail, I've seen protractor-fail-fast and protractor-bail-fast but it seems to be for the jasmine test cases not for function, I WOULD REALLY APPRECIATE any help please, protractor its driving me a bit nuts lol...
//method
const checkData = () = {
return new Promise((resolve)=>{
if(true){
// Success case
return resolve(true)
}
else{
// Fail case
return reject(false)
}
})
}
//Specfile
it('sample test',async ()=>{
Let data = await checkData();
expect(data).toEqual(true);
})
Based on resolved value test will pass or fail
If you function throws error you can just use the done function that jasmine provides
Example:
it('should do something', async done => {
try {
await weDoSomething();
} catch (e) {
done.fail(e); // mark test as failed
}
done(); // mark test as done
}, 1 * 60 * 1000);
async function weDoSomething() {
throw Error('failed function');
}
Did you try simply re-throwing the error in the catch? I believe that should cause the test to fail.
Comments:
You do not need to await a console.log as it is an synchronous operation.
broser.waits throw exceptions when the element is not found within the timeout period, it appears your for loop is not required at all.
This function will wait 3000ms for the element to be clickable and if that does not happen it will throw an exception which will be caught in the catch. It will log the message and then rethrow the error which will fail your test (assuming the error is not being caught and handled further up)
async WaitToBeClickable(element){
try {
await browser.wait(this.EC.elementToBeClickable(element), 3000);
} catch (err) {
console.log(`WAIT TO BE CLICKABLE FAILED:\n${element.parentElementArrayFinder.locator_.value}\n\nError:\n${err}\n`);
throw new Error(err);
}
};
Related
I'm trying to exit the function if an error occurs inside the try block, and still run cleanup code in finally.
I'm doing it by using shouldContinue that is set to true initially, and set it to false inside the catch block if the execution shouldn't continue.
async uploadToServer() {
let response;
let shouldContinue = true;
try {
response = await this.uploderService.uploadFileToServer();
} catch (error) {
this.displayUploadError(error);
shouldContinue = false;
} finally {
// run cleanup code anyway
this.resetSession();
}
if (!shouldContinue) {
return;
}
this.saveResponse(response);
// continue execution here
// ...
}
Is there a better way to exit the function after an error occurs inside the try block and still run code in finally?
One way (probably my personal preferred way) is to simply put everything inside the try (if possible - e.g. if nothing else inside the try could throw an unrelated error):
async uploadToServer() {
try {
const response = await this.uploderService.uploadFileToServer();
this.saveResponse(response);
// continue execution here
// ...
} catch (error) {
this.displayUploadError(error);
} finally {
// run cleanup code anyway
this.resetSession();
}
}
Another way is to return in the catch (finally still runs):
async uploadToServer() {
let response;
try {
response = await this.uploderService.uploadFileToServer();
} catch (error) {
this.displayUploadError(error);
return;
} finally {
// run cleanup code anyway
this.resetSession();
}
this.saveResponse(response);
// continue execution here
// ...
}
Finally will be executed anyway, either the try executed or catch.
Check the description in the link below for more information.
Mozilla developer reference
I want to be able to catch any and all failures within my specs and log it into my Test Rail integration. I have this pattern currently where I wrap all the tests in a try/catch and on the CATCH, I mark the test as FAILURE and add it to the results array. Inside the afterAll it will then send all these results to Test Rail's API.
This all works mostly correct. However, occasionally one of my tests will fail but will NOT trigger the catch part of the code. My spec file will proceed all the way to the done() and finish the full test and THEN will show my failure once the code gets to the AfterAll section.
I assume this is because the expect(lookForSomeObject) is still processing/looking in the background and it doesn't finally finish doing its thing until the spec file gets to the AfterAll section.
How can I update this pattern to be better.
afterAll(async () => {
var r = await tr.addResultsForCases(testCases);
});
it('Verifies Search', async (done) => {
var testCaseInfo: AddResultForCase = {
case_id: 75
}
try {
const locatorSearchBarInput = await loc.locatorSearchBarInput();
await waitToBeDisplayed(locatorSearchBarInput);
await locatorSearchBarInput.sendKeys('35011');
const locatorSearchButton = await loc.locatorSearchButton();
await waitToBeDisplayed(locatorSearchButton);
await click(locatorSearchButton);
await expect(await loc.locatorSearchBarText()).toBeDisplayed();
testCaseInfo.status_id = TestRailStatus.Passed;
testCases.results.push(testCaseInfo);
done();
} catch (e) {
testCaseInfo.status_id = TestRailStatus.Failed;
testCases.results.push(testCaseInfo);
done.fail(e);
}
});
I would get rid of try/catch
Add case_id into your it description at the env like so Verifies Search #75
Create your own custom jasmine reporter as described here https://jasmine.github.io/tutorials/custom_reporter
register it within your onPrepare()
specDone function will take spec parameter (or result, whatever you call it). Then add this snippet
specDone: function(spec) {
if (spec.status === 'passed') {
// do anything on success
} else if (spec.status === 'failed') {
let case_id = spec.description.match(/#([^#]*$)/)[1];
// report your case_id as failed
} else {
// block for skipped test cases (marked by `xit` or `xdescribe`)
}
}
That's it. No need to add your handling to each test case anymore.
My application has a Terms and Conditions modal that only appears under certain circumstances. I would like the Protractor test to click Agree if the modal appears, and do nothing if it doesn't appear.
Here is my method, I try to wait for the element to appear, and catch the error if it doesn't appear. If the button appears, it is clicked and the test passes. However, if the button does not appear, the test fails with the "Element is taking too long..." message.
waitForAgree() {
var until = protractor.ExpectedConditions;
try {
browser.wait(until.visibilityOf(this.agreeTerms), 5000, 'Element taking too long to appear in the DOM');
this.agreeTerms.click();
this.continue.click();
} catch (e) {
console.log("not found");
}
}
I have also tried the following. This gives me the message Failed: Cannot read property 'agreeTerms' of undefined
clickAgree() {
//browser.ignoreSynchronization = true;
this.agreeTerms.isPresent().then(function (isPresent) {
if (isPresent) {
this.agreeTerms.click();
this.continue.click();
}
else {
console.log("not found");
}
})
}
Any help on either of these errors is appreciated.
In order to elaborate why your first method didn't work, we should note that using Promise and not be calling .catch is usually a bad idea because most of the time we will never know what was wrong and try/catch won't help at all. And this is also the reason that your alternate method succeded because you are calling .catch on the Promise.
If you are interested in making your first approach work, then you should be using async/await, something like this:
async waitForAgree() {
var until = protractor.ExpectedConditions;
try {
await browser.wait(until.visibilityOf(this.agreeTerms), 5000, '...too long...');
this.agreeTerms.click();
this.continue.click();
} catch (e) {
console.log("not found");
}
}
Note the keyword async in front of the function name and await in front of the call to browser.wait. Note this is an ES6 feature.
Hope it helps
I would store that web element in a list and if the size of list is equal to 1 (in this case I know button is present, so click it). If that list returns zero than print the fail message.
I will use findElements instead of findElement.
This is how you can defined the list :
var elems = driver.findElements(By.xpath("your locator"));
For calculating the size, you can use this :
var keys = Object.keys(elems);
var len = keys.length
and after that :
if (len == 1){
this.agreeTerms.click();
this.continue.click();
}
else{
Do something here, button did not appear.
}
I tried an alternate method to my first one, and it works. I'm not entirely sure why the first one in my question fails, and this succeeds. If anyone would like to elaborate, thank you ahead of time. If you just need a working method, here:
waitForAgree() {
var until = protractor.ExpectedConditions;
browser.wait(until.visibilityOf(this.agreeTerms), 5000, 'Element taking too long to appear in the DOM')
.then(() => {
this.agreeTerms.click();
this.continue.click();
})
.catch((error) =>{
console.log("not found");
});
}
I have the following test:
it.only('validation should fail', function(done) {
var body = {
title: "dffdasfsdfsdafddfsadsa",
description: "Postman Description",
beginDate: now.add(3, 'd').format(),
endDate: now.add(4, 'd').format()
}
var rules = eventsValidation.eventCreationRules();
var valMessages = eventsValidation.eventCreationMessages();
indicative
.validateAll(rules, body, valMessages)
.then(function(data) {
console.log("SHOULD NOT GET HERE");
should.fail("should not get here");
done();
})
.catch(function(error) {
console.log("SHOULD GET HERE");
console.log(error);
});
done();
});
The test execution path is correct. When I have validating data, it goes to "SHOULD NOT GET HERE". The test is really to make sure it doesn't. And when I put in non validating data the code does go to "SHOULD GET HERE". So the validation rules work.
What I'm trying to do is make sure the test fails when when I have bad validation data and it validates. However when I run it as it is with good data it validates, runs the fail, but mocha still marks it as the passing. I want it to fail if the execution gets to "SHOULD NOT GET HERE".
I've tried throw new Error("fail"); as well with no luck. In both cases it actually seems to run the code in the .catch block as well.
Any suggestions?
I found solutions for this in a similar question. This question is written because those solutions don't seem to be working for me.
You can call assert.fail:
it("should return empty set of tags", function()
{
assert.fail("actual", "expected", "Error message");
});
Also, Mocha considers the test has failed if you call the done() function with a parameter.
For example:
it("should return empty set of tags", function(done)
{
done(new Error("Some error message here"));
});
Though the first one looks clearer to me.
In the ES2017 async/await world chai-as-promised is not needed as much. Although simple rejections are one place chai-as-promised remains a little neater to use.
A catch is required if you want to test the error in more detail.
it.only('validation should fail', async function(){
let body = { ... }
let rules = eventsValidation.eventCreationRules()
let valMessages = eventsValidation.eventCreationMessages()
try {
await indicative.validateAll(rules, body, valMessages)
} catch (error) {
expect(error).to.be.instanceOf(Error)
expect(error.message).to.match(/Oh no!/)
return
}
expect.fail(null, null, 'validateAll did not reject with an error')
// or throw new Error('validateAll did not reject with an error')
})
async/await requires Node.js 7.6+ or a compiler like Babel
Use chai-as-promised, with native Mocha promise handlers.
var chai = require('chai').use(require('chai-as-promised'));
var should = chai.should(); // This will enable .should for promise assertions
You no longer need done, simply return the promise.
// Remove `done` from the line below
it.only('validation should fail', function(/* done */) {
var body = {
title: "dffdasfsdfsdafddfsadsa",
description: "Postman Description",
beginDate: now.add(3, 'd').format(),
endDate: now.add(4, 'd').format()
}
var rules = eventsValidation.eventCreationRules();
var valMessages = eventsValidation.eventCreationMessages();
// Return the promise
return indicative
.validateAll(rules, body, valMessages)
.should.be.rejected; // The test will pass only if the promise is rejected
// Remove done, we no longer need it
// done();
});
This simple throw is working for me
describe('Lead', () => {
it('should create a new lead', async () => {
throw 'not implemented'
})
})
I'm trying to check if an element exists before I can execute this line:
driver.findElement(webdriver.By.id('test'));
This throws an error "no such element" if the id test doesn't exist in the document, even in a try-block.
I've found answers for Java, where you can check if the size is 0, but in Node.js this throws an error before I can check the size:
throw error; ^ NoSuchElementError: no such element
You can leverage the optional error handler argument of then().
driver.findElement(webdriver.By.id('test')).then(function(webElement) {
console.log('Element exists');
}, function(err) {
if (err.state && err.state === 'no such element') {
console.log('Element not found');
} else {
webdriver.promise.rejected(err);
}
});
I couldn't find it explicitly stated in the documentation, but determined this from the function definition in webdriver/promise.js in the selenium-webdriver module source:
/**
* Registers a callback on this Deferred.
* #param {Function=} opt_callback The callback.
* #param {Function=} opt_errback The errback.
* #return {!webdriver.promise.Promise} A new promise representing the result
* of the callback.
* #see webdriver.promise.Promise#then
*/
function then(opt_callback, opt_errback) {
Just use the isElementPresent(locatorOrElement) method. Here's a link to the code:
http://selenium.googlecode.com/git/docs/api/javascript/source/lib/webdriver/webdriver.js.src.html#l777
Aaron Silverman's answer did not work as expected (err.state was undefined and a NoSuchElementError was always thrown)—though the concept of using the optional callbacks still works.
Since I was getting the same error as the OP is referencing, I believe NoSuchElementError should be referenced when determining if the targeted element exists or not. As its name implies, it is the error that is thrown when an element does not exist. So the condition in the errorCallback should be:
err instanceof webdriver.error.NoSuchElementError
So the complete code block would be as follows (I also am using async/await for those taking advantage of that syntax):
var existed = await driver.findElement(webdriver.By.id('test')).then(function() {
return true; // It existed
}, function(err) {
if (err instanceof webdriver.error.NoSuchElementError) {
return false; // It was not found
} else {
webdriver.promise.rejected(err);
}
});
// Handle value of existed appropriately here
Aaron Silverman's answer did not work for me fully, though some parts of it were helpful. Arthur Weborg's answer worked for me, though some parts caused issues.
My typical use case for "checking if element exists" is when I want to click the element, only if it exists, since if it doesn't exist, it crashes the program due to the error. All I want is for the application to note that it doesn't exist, and move on. So what I do is:
await driver.findElement(By.id("test")).then(found => {
driver.findElement(By.id("test")).click()
}, error => {
if (error instanceof webdriver.error.NoSuchElementError) {
console.log('Element not found.');
}
});
Just a simple console log, and it moves on with the rest of the program like I wanted.
Here the summary for newbies like me ;-)
As described here:
For consistency with the other Selenium language bindings, WebDriver#isElementPresent() and WebElement#isElementPresent() have been deprecated. These methods will be removed in v3.0. Use the findElements command to test for the presence of an element:
driver.findElements(By.css('.foo')).then(found => !!found.length);
There are 2 possible cases for this answer:
Case 1: Using promises
Case 2: Using async / await
Case 1 using promises, then() and the error callback is already well explained in the accepted answer.
In Case 2, if you're in an async function (and therefore likely to be using await statements), then you have the pleasure of avoiding all the nasty chaining of then()'s and callbacks. 🎉
In this scenario, you can simply check if an element exists with code like this:
const check = async (webdriver) => {
let elementExists;
// Solution HERE:
try {
await webdriver.findElement(webdriver.By.id('test'));
elementExists = true;
} catch (error) { // error thrown is: "NoSuchElementError: no such element"
elementExists = false;
}
if (elementExists) {
// Element exists behaviour...
} else {
// Element doesn't exist behaviour...
}
};
You want to check whether an element exists before you want to find an element in the UI. You can wait until the element gets located in the UI, and then you can perform the find element operation.
Example: Below code waits until the element located, and then it will perform the find element.
driver.wait(webdriver.until.elementLocated(webdriver.By.id(LocatorValue)), 20000)
.then(() => {
return driver.findElement(webdriver.By.id('test'));
}).then((element) => {
// Perform any operation what you to do.
return element.click();
}).catch(() => {
console.log('Element not found');
})
This is the code that worked for me.
var assert = require('assert');
var expect = require('chai').expect;
var should = require('chai').should();
var chai = require('chai');
var chaiAsPromised = require('chai-as-promised');
chai.use(chaiAsPromised);
chai.should();
var webdriver = require('selenium-webdriver');
By = webdriver.By;
until = webdriver.until;
describe('checking if an element id exists', function() {
it.only('element id exists', function () {
var driver = new webdriver.Builder().forBrowser('chrome').build();
driver.get('http://localhost:3000');
this.timeout(6000);
return driver.wait(until.elementLocated(By.id('idWeWantToTest')), 5 * 1000).then((e) => {
}, function(err) {
throw {
msg: "element not found"
}
}).then(() => {
driver.quit();
});
});
})