Is it possible to wait on a ExpectedConditions.visibilityOf without getting a failure if the element has not become visible? I want to handle a situation, where a button might has become visible through an animation and click it away.
browser.wait(conditions.visibilityOf(button), 500).then(function (visible) {
if (visible) {
return button.click().then(function () {/*...*/});
}
});
I found out, that I can handle the rejected promise returned by wait to suppress the timeout error:
browser.wait(conditions.visibilityOf(button), 500).then(function () {
// It is visible
return button.click().then(function () {/*...*/});
}, function() {
// It is not visible
if (shouldExpectVisibility) {
// If I want to fail, I could reject again
return protractor.promise.rejected('No such button');
}
else {
// If I don't want to fail, I do nothing
}
});
Related
I have a SweetAlert2 popup that's verifying images uploaded by users. After the user decides I need to continue/stop the main function.
But it just ignores the .then function appended to Swal.
So if the img have good resolution, it returns true. And else it just returns false. Even if the popup shows. It already ran the rest of the main function code.
Img verify function:
function verifyimage(imgurl) {
return new Promise((resolve) => {
var tmpImg = new Image();
tmpImg.src = imgurl;
$(tmpImg).on("load", function () {
var orgWidth = tmpImg.width; //w
var orgHeight = tmpImg.height; //h
if (orgHeight <= 720 || orgWidth <= 1500) {
Swal.fire({
position: "center",
icon: "error",
title: `title`,
showConfirmButton: true,
showCancelButton: true
}).then((result) => {
if (result.isConfirmed) {
resolve(true); //img ok
} else {
resolve(false); //dont upload
}
});
} else {
resolve(true); //upload, good resolution
}
});
});
}
Main function:
$(document).on("click", "#upload-it", async function() {
var valueimg = geturl();
var imgvefify = await verifyimage(valueimg);
if (!imgvefify) {
console.log("nope, invalid img");
return false;
}
//upload to server etc..
});
You've phrased this question as if SweetAlert2 is not respecting your then, but I think it's actually the case that jQuery is not waiting for or respecting your return false; you're issuing it in an async function and jQuery simply doesn't know how to await Promises in event handlers.
Your function passed to on returns a Promise, because all async functions return a Promise in all cases. It seems like you want the return false to cancel the default behavior of the #upload-it button that presumably submits a form, but JavaScript event handlers don't understand when event handlers return Promises, and jQuery doesn't either. This makes it impossible to use return false to cancel the default behavior in an async function event handler.
Instead, make sure to immediately prevent the default behavior and stop propagation before awaiting anything, which you can do by calling methods on the event object. Having prevented the default behavior, you won't be able to "continue" it once the async function completes, but you can still programmatically submit the form.
$(document).on("click", "#upload-it", async function(event) {
event.preventDefault();
event.stopPropagation();
// validate the form
// upload to server etc..
});
it('AddnewSupplier1',function() {
var i =0;
var isenabled=false;
var count=0;
element(by.css("path[d*='M20.995']")).click();
element(by.cssContainingText('div[class="mat-step-text-label ng-star-inserted"]','Supplier Maintenance')).getText().then(function(text) {
console.log(text);
}).then(function() {
do {
if (i>0) {
console.log("Clicking on NextButton");
element(by.css("button[class='mat-paginator-navigation-next mat-icon-button']")).click();
}
(element.all(by.xpath("//table[#class='mat-table']/tbody/tr/td[1]"))).each(function(webelement) {
webelement.getText().then(function(text) {
if(text=="IACE") {
count++;
console.log("Element is found");
//break;
}
});
});
var nextbutton = element(by.css("button[aria-label='Next page']"));
nextbutton.isEnabled().then(function(isEnabled) {
var isenabled=isEnabled;
console.log(isenabled);
}).then(function() {
i++;
console.log(i);
});
}
while(isenabled);
})
});
I have to check if Supplier ID "IACE" is present in the table.
For that I have written code taking all the values in the first column of the table and check using "each".
If the element is present in the first page the code works. But if it is in second page or third then I have to click on the next button. Before clicking on the next button I need to check if the button is enabled or disabled. If the button is enabled, then I click on the next button and check if the element is present in that page and so on. If the button is disabled, then it means element is not present and I have to fail the testcase.
For this I have written code below. I have used Do ...while because i the first page it has to check without condition (i.e next button is enabled or not).
The issue happening is:
I have stored the isEnabled() value in isenabled variable.I have initialised this variable to false.
But when I run the testcase, though my value is in second page, it is not going to second page. Instead it checks in the first page and stops the test. This is happening because in while(isenabled), isenabled is stored as false. I think before executing the isEnabled() function while(isenabled) is getting executed. Therefor while(isenabled) is getting false value which is initialised value.
I am unable to find where and how to resolve the promise here.
I tried adding async and await ,But when i add these it shows error (red cross mark).Di need to import anything before i add these async and await to my protractor scripts. I have done (SELENIUM_PROMISE_MANAGER: false, ) this in my configuration file.What else i need to do other than this to add async and await.
Still not sure what it is you are trying to accomplish but resolving the promise should work if you change your code like this:
}).then(async function() { //add async
do {
// your code here up until var nextbutton = see next line
var nextbutton = element(by.css("button[aria-label='Next page']"));
isenabled = await nextbutton.isEnabled(); // await isEnabled and store result in isenabled variable
console.log(isenabled);
i++;
console.log(i);
}
while(isenabled);
if you can't use async/await you could also do the following:
.then(function() {
function repeatMe() { // replace do with a function
if (i>0) {
console.log("Clicking on NextButton");
element(by.css("button[class='mat-paginator-navigation-next mat-icon-button']")).click();
}
(element.all(by.xpath("//table[#class='mat-table']/tbody/tr/td[1]"))).each(function(webelement) {
webelement.getText().then(function(text) {
if(text=="IACE") {
count++;
console.log("Element is found");
//break;
}
});
});
var nextbutton = element(by.css("button[aria-label='Next page']"));
nextbutton.isEnabled().then(function(isEnabled) {
console.log(isEnabled); // no need for isenabled variable anymore
i++;
console.log(i);
if (isEnabled) {
repeatMe(); // call repeatMe if isEnabled is true
}
});
}
repeatMe(); // replace while with calling function repeatMe once
})
I am using a JS Promise to asynchronously get the user's location inside getLocation(). And then I'm making an Ajax request to the server inside postLocation().
$('#add_location_btn').on('click', function () {
if ($('#code').val().length === 0) {
window.alert('Enter a valid code!');
} else {
getLocation().then(function (pos) {
$('#addlocation_loader').attr('hidden', false); // Show loading sign
return pos;
}).then(function (pos) {
postLocation(pos);
});
}
$('#addlocation_loader').attr('hidden', true); // Hide loading sign
});
However, eventually changing addlocation_loader 'hidden' attribute to true is not working, meaning that the attribute is properly set to false but never turns true.
Edit
It's worth noting that I want to hide the loading sign after postLocation() is executed.
I have tried setting 'hidden' to true in a third then() statement, but the sign now never shows up. It seems that the show and hide statements are quickly executed after one another, which is confusing (When I comment out the hide statement the sign is normally shown, which means that both execute).
getLocation().then(function (pos) {
$('#addlocation_loader').attr('hidden', false); // Show loading sign
return pos;
}).then(function (pos) {
postLocation(pos);
}).then(function () {
$('#addlocation_loader').attr('hidden', true); // Hide loading sign
});
You are using an asynchronous function to the attribute to false. That means that probably
$('#addlocation_loader').attr('hidden', true);
is executed before
$('#addlocation_loader').attr('hidden', false);
You may have your hide/show loader backwards. Looks Like you set hidden to false when the location is returned and to true when the button is pressed.
Perhaps something like this would work:
$("#add_location_btn").on("click", function() {
const $loader = $("#addlocation_loader");
if ($("#code").val().length === 0) {
window.alert("Enter a valid code!");
} else {
$loader.attr("hidden", false); // Show when requested
getLocation()
.then(function(pos) {
$loader.attr("hidden", true); // Hide when returned
postLocation(pos);
});
}
});
If you want to hide the loader, you should do it within the .then() callback, because that is when the promise has been resolved. So what you want to do is:
Show loader before executing postLocation()
Remember to return the promise from postLocation() (which you didn't do in the code)
Hide the loader when the promise is resolved (after posting the position has succeeded)
Here is your fixed code:
// Perform async operation
getLocation().then(function (pos) {
// Show loader
$('#addlocation_loader').attr('hidden', false);
// Post location. Remember to return the promise to chain it!
return postLocation(pos);
}).then(function() {
// Hide loader when position is successfully posted
$('#addlocation_loader').attr('hidden', true);
});
I have two onclick functions on page:
function fn_name_1() {
//do buch of stuff
}
function fn_name_2() {
//do buch of stuff
}
When user clicks on fn_name_1() it starts executing which is take a while (about 4 seconds to complete).
If user clicks on fn_name_2() while fn_name_1() is in process it messes fn_name_1().
I had tried to setTimeOut for several second for fn_name_2() but it an awful user experience because when they hit the button they expect immediate result on their actions.
Basically I want to:
Scenario 1
User hit fn_name_2() button, its check if fn_name_1() is running and if it is not - proceed with executing fn_name_2() as normal.
Scenario 2
User hit fn_name_2() button, its check if fn_name_1() is running and if it dit, wait for completion and start executing automatically right after fn_name_1() is completed.
How it can be solved?
The simplest way is to have a variable which is accessible to both functions:
let runningTestOne = false;
function testOne(callback) {
console.log('Starting testOne');
runningTestOne = true;
setTimeout(() => {
runningTestOne = false;
console.log('done with testOne');
if (callback) {
callback();
}
}, 5000);
};
function testTwo() {
if (runningTestOne) {
console.log('cannot run');
return;
} else {
console.log('can run test two');
}
};
function runWithCallback() {
testOne(testTwo);
}
<button onclick="testOne()">Test One</button>
<button onclick="testTwo()">Test Two</button>
<button onclick="runWithCallback()">With callback</button>
Make fn_name_1 asynchronous, and let it return a promise, then you can store that promise, and only execute the second task when the promise resolved:
function fn_name_1() {
//do buch of stuff
return new Promise(resolve => {
setTimeout(resolve, 1000);
});
}
let isDone = fn_name_1();
function fn_name_2() {
isDone.then(() => {
//do buch of stuff
});
}
something like this?
var wait=false;
function fn_name_1() {
wait=true;
//do bunch of stuff
wait=false;
}
function fn_name_2() {
while(wait){/*do nothing*/}
//do bunch of stuff
}
I have a function that checks the status of connection to server. I want to run checkOnline every 5 seconds. If the connection is lost, and it keeps failing then the fadeIn for error message keeps running as well (creates a flashing effect). How can I change it so that the fade functions are only run once, when it fails, but still keeps checking the connection.
$(function () {
var url = 'https://examplesite.com/';
function checkOnline() {
$.get(url).done(function () {
window.location = url;
}).fail(function () {
$('.errortext').hide().fadeIn(500);
$('.loadingtext').fadeOut(500);
});
};
window.setInterval(function () { checkOnline(); }, 5000);
setTimeout(function () { checkOnline(); }, 2000);
});
Keep track of whether the fade effects already ran. Then check it before running the fade. Here's an example that uses a variable called fadeFlag:
var fadeFlag = false;
function fadeOnce() {
// Has fade happened already?
if (fadeFlag === false) {
// No. Then do the effects
$('.errortext').hide().fadeIn(500);
$('.loadingtext').fadeOut();
fadeFlag = true;
}
}
function checkOnline() {
$.get(url).done(function () {
window.location = url;
}).fail(function () {
// Try the fade effects
fadeOnce();
});
};
The first time fadeOnce() is called, it runs the effects and then sets fadeFlag to true. The next time, it sees that fadeFlag is true, so it does nothing.