How to apply wait time when Your page javascript reloads the element - javascript

Hi i am using selenium webdriver to automate my script and i have used wait.until condition in my script to click on the delivery bttn in below html page. The problem is selenium is finding my element but since the java script reloads the certain element. And the delivery bttn only becomes clickable after reloading. And my selenium script throws "stale element reference: element is not attached to the page document". What should i do to overcome this error.
WebElement delibttn=wait.until(ExpectedConditions.elementToBeClickable(By.xpath("(//button[#class='btn-to-cart nopricetohide btn btn-primary your-catalog-deliver btn-block btn-unpadded tocart-rounded'])[1]")));
delibttn.click();
WebElement contshopping=wait.until(ExpectedConditions.elementToBeClickable(By.xpath("//a[#class='btn btn-link full-width mb-10']")));
Screenshot:

there are two ways to solve your problem.
1) Run the code with Async, that way you can 'await' a line of code, for example..
function async(test1){
await driver.findElement(By.id("");
driver.click();
});
or you can also do the following
2)
function (test1) {
let element = driver.findElement(By.id(elementId));
driver.wait(until.elementIsVisible(element), 10000).then(async () =>{
element.click();
});
This wait in number 2, is the one that i use in my code and it always works.
A very barbaric way of doing it would be to add a ridiculous wait time to check that it isn't something else showing an error similar to a wait problem
driver.sleep(10000);
or
thread.sleep(10000);
(the measurement is in milliseconds unless defined otherwise)
Please let me know if these solutions do not solve the problem.

as Jack suggested you could use async, but I always used an infinte while loop
Code i have given below is in python, but you can use the logic in java too
def wait_for_element():
val = True
while val:
web_elem = driver.find_element_by_id('id')
try:
web_elem.is_displayed()
except Exception as ex:
val = True
else:
val = False
i know infinite loop is not a better way than async, but if there are cases where you can't use async you can use this. Also keep in mind to put timeout for loop, otherwise you would looping infinitely when the page was unresponsive or has not loaded.

the reason it is still throwing this issue is because you are not handling your exceptions properly, this is a response to it still throwing stale element errors.
Add something like this to your project, if you look at the bottom of my code you will see that i have added exceptions to catch errors so it does not affect the code the way it is doing.
driver.findElement(By.id(buttonID)).then(pageElement => {
driver.wait(until.elementIsVisible(pageElement), 10000).then( () => {
pageElement.click();
next();
})
.catch(ex => {
console.log(ex.message, ex.stack)
});
}).catch(ex => {console.log(ex.message, ex.stack)});
This is the example of how i am using catches, however many promises you have in your function the more catches you will need, if you hover over an element in Visual Code / Studio you will be able to see if it throws a promise or not.

Related

Clicking on an element, if present, in webdriverio

I'm using the latest Appium and webdriverion versions to test a native app, and what I need to do in my script is Click on a Button if it exists, but continue if it doesn't.
So, if the Accept and Close button is present it will be clicked, and then the subsequent Allow button will be clicked.
But, if the Accept and Close button isn't present, then it will go straight to the next command and click the Allow button straight away.
My code currently looks like this;
if (await expect(await closeCMP).isExisting()) {
await closeCMP.click()
} else {
var allow = await $('~Allow');
await allow.click();
}
});
});
However, if the Accept and Close button isn't present then the test fails (with a timeout error, as the Accept and Close element cannot be found) rather than perform the next command (which is to click on the Allow button).
Is there an obvious reason for this?
Also, do I need the else part of the loop, or will just the if part of the loop suffice?
I appreciate that some may perceive this as bad practice, but I'm not able to alter anything on the DOM, or do anything with Cookies so I'm not sure what else I can do to get this to work.
Any help would be greatly appreciated.
There's more than one way to do this, but I generally use try/catch blocks to prevent tests from failing when evaluating conditionals.
Here's an example of how it could look using this approach.
try {
await expect(await closeCMP).isExisting();
await closeCMP.click();
} catch {
console.warn('one of the previous lines threw an error');
}
const allow = await $('~Allow');
await allow.click();

How to introduce delay between tests in protractor

I am exploring protractor tool with cucumber and test is executing super fast.. in order to know if really elements are getting clicked or not, I am using sleep() method but failing. I am also using another method wait() with expected conditions which is also failing.. In fact, I understood click() method on the link element itself is failing.. That is, unable to click on element which I desired, however when I print on console element is printing all its attributes and methods.
please find the code snippet as below;
When(/^I click on "(.*?)" link$/, (callback) => {
console.log("Clicking... ");
browser.wait(EC.visibilityOf(login.confirmInstructions), 5*1000, "Waiting for Confirmation link...");
var confirmLink = login.confirmInstructions;
var isClickable = EC.elementToBeClickable(confirmLink);
browser.wait(isClickable, 10*1000, "Element clickable");
confirmLink.click();
browser.sleep(10*10000);
login.confirmInstructions.click();
//browser.wait(validateText(element(by.binding('myvar'))), 5000, "");
//browser.wait(EC.presenceOf(confirmation.confirmScreen), 60*1000);
console.log("waited");
return callback;
});
What I am missing here.?
I'm not sure I understand the question but if you just want to see if your "confirmInstructions" is clicked, you should use a debugger and set a breakpoint before the method
I understood, the best way to wait for web elements in protractor is to use, wait() rather than sleep. However, I was looking either of the way (by using wait / sleep) to slow down the test execution, as it is useful while implementing test scenarios in order to recognize web elements. Finally, following method for now I am using it.. but still if there is a better way to handle please put your comments..
const sleep = (milliseconds) => {
return new Promise(resolve => setTimeout(resolve, milliseconds));
}
and calling sleep from my function as: await sleep(2000);
For now, I am able to move forward with writing tests.. I am also sure there is a better way in protractor API, yet to find it's implementation.

Protractor on AngularJS - 'script timeout: result was not received in 11 seconds' after login page change

Protractor hangs completely when trying to get any element property after logging in (idk if it's related to logging in or related just to switching pages).
it("Should get location of main container", async function() {
await LoginPage.validLogin();
// Works and logs in the dashboard
await browser.sleep(3000);
// Get the main container by class name
const container = await element(by.css(".main-container"));
// Logs properly the element functions (as expected)
console.log(container);
console.log(await container.getLocation()); // Hangs here
});
In this case, I'm trying to get the location of the main container element on the page. The first console.log fires and shows properly, while the second hangs completely, so I get the script timeout. Increasing the timeout time doesn't help at all...
I found online that misusing $timeout in AngularJS instead of using $interval may lead to this strange behaviour, but I really can't skim through the entire (very big!) project's codebase to change everything hoping that it just works, not to talk about the external libraries using $timeout.
I have SELENIUM_PROMISE_MANAGER = false; in my Protractor config so I disabled the built-in Control Flow in order to manually manage the promises using async/await, but even if I use the built-in Control Flow without using async/await I get the very same behaviour and error.
I'm using Jasmine as testing framework.
Maybe I'm missing something? Any help would be much appreciated, thanks!
This is caused by the fact that angular is not stable. Have a look at the link below. I found my answer there. When the page you are trying to test is open go to the browser dev tools and type in the console getAllAngularTestabilities(). There are a few properties here that indicate whether angular is ready to be tested. hasPendingMicrotasts needs to be false. hasPendingMacroTasks needs to be false. isStable needs to be true. I put a screenshot below. In my screenshot hasPendingMacrotasks is true and it must be false. So the page I looked at was not ready to be tested.
Failed: script timeout: result was not received in 11 seconds From: Task: Protractor.waitForAngular() - Locator: By(css selector, #my-btn)
Try something like this:
it("Should get location of main container", async function() {
await LoginPage.validLogin();
const container = await element(by.css(".main-container"));
await browser.wait(protractor.ExpectedConditions.presenceOf(container), 5000, 'Element taking too long to appear in the DOM');
await console.log(await container.getLocation());
});
I don't think that getLocation() exists in the Javascript bindings for selenium. I couldn't find it in the source code anyway. So that promise will never return which is why it hangs. But I the you can achieve basically the same thing with getRect():
it("Should get location of main container", async function() {
await LoginPage.validLogin();
const container = await element(by.css(".main-container"));
await browser.wait(protractor.ExpectedConditions.presenceOf(container), 5000, 'Element taking too long to appear in the DOM');
await console.log(await container.getRect());
});

Why does timer for waits start before the steps are actually executed in a protractor test? Eventloop wrangling

tl;dr: When I run my test case, steps executed seem to work, but the test bails out early on a failure to find an element that hasn't even loaded yet. It seems like the waits I have around locating certain elements are loaded/launched as soon as the test is launched, not when the lines should actually be executed in the test case. I think this is happening because the page is barely (correctly) loaded before the "search" for the element to verify the page has loaded bails out. How do I wrangle the event loop?
This is probably a promise question, which is fine, but I don't understand what's going on. How do I implement my below code to work as expected? I'm working on creating automated E2E test cases using Jasmine2 and Protractor 5.3.0 in an Angular2 web app.
describe('hardware sets', () => {
it('TC3780:My_Test', async function() {
const testLogger = new CustomLogger('TC3780');
const PROJECT_ID = '65';
// Test Setup
browser.waitForAngularEnabled(false); // due to nature of angular project, the app never leaves zones, leaving a macrotask constantly running, thus protractor's niceness with angular is not working on our web app
// Navigate via URL to planviewer page for PROJECT_ID
await planListingPage.navigateTo(PROJECT_ID); // go to listing page for particular project
await planListingPage.clickIntoFirstRowPlans(); // go to first plan on listing page
await planViewerPage.clickOnSetItem('100'); // click on item id 100 in the plan
});
});
planViewerPage.po.ts function:
clickOnSetItem(id: string) {
element(by.id(id)).click();
browser.wait(until.visibilityOf(element(by.css('app-side-bar .card .info-content'))), 30000); // verify element I want to verify is present and visible
return expect(element(by.css('app-side-bar .card .info-content')).getText).toEqual(id); //Verify values match, This line specifically is failing.
}
This is the test case so far. I need more verification, but it is mostly done. I switched to using async function and awaits instead of the typical (done) and '.then(()=>{' statement chaining because I prefer not having to do a bunch of nesting to get things to execute in the right order. I come from a java background, so this insanity of having to force things to run in the order you write them is a bit much for me sometimes. I've been pointed to information like Mozilla's on event loop, but this line just confuses me more:
whenever a function runs, it cannot be pre-empted and will run entirely before any other code
runs (and can modify data the function manipulates).
Thus, why does it seem like test case is pre-evaluated and the timer's set off before any of the pages have been clicked on/loaded? I've implemented the solution here: tell Protractor to wait for the page before executing expect pretty much verbatim and it still doesn't wait.
Bonus question: Is there a way to output the event-loop's expected event execution and timestamps? Maybe then I could understand what it's doing.
The behavior
The code in your function is running asynchronously
clickOnSetItem(id: string) {
element(by.id(id)).click().then(function(){
return browser.wait(until.visibilityOf(element(by.css('app-side-bar .card .info-content'))), 30000);
}).then(function(){
expect(element(by.css('app-side-bar .card .info-content')).getText).toEqual(id);
}).catch(function(err){
console.log('Error: ' + err);
})
}

How can I tell when changes to jquery html() have finished?

I'm using jQuery to change the HTML of a tag, and the new HTML can be a very long string.
$("#divToChange").html(newHTML);
I then want to select elements created in the new HTML, but if I put the code immediately following the above line it seems to create a race condition with a long string where the changes that html() is making may not necessarily be finished rendering. In that case, trying to select the new elements won't always work.
What I want to know is, is there an event fired or some other way of being notified when changes to html() have finished rendering ? I came across the jQuery watch plugin, which works alright as workaround but it's not ideal. Is there a better way ?
As a commenter already mentioned, JavaScript is single threaded, so you can't get race conditions.
What may trip you up however, is the fact that the UI will not update itself based on JavaScript, until a thread is finished. This means that the entire method must finish, including all code after you call html(...), before the browser will render the content.
If your code after calling html(...) relies on the layout of the page being recalculated before continuing, you can do something like this:
$("#divToChange").html(newHTML);
setTimeout(function() {
// Insert code to be executed AFTER
// the page renders the markup
// added using html(...) here
}, 1);
Using setTimeout(...) with a time of 1 in JavaScript defers execution until after the current JavaScript code in the calling function finishes and the browser has updated the UI. This may solve your problem, though it is difficult to tell unless you can provide a reproducible example of the error you're getting.
use .ready jQuery function
$("#divToChange").html(newHTML).ready(function () {
// run when page is rendered
});
It's 7 years latter and I just ran into a scenario exactly like the one #mikel described, where I couldn't avoid a "timer based solution". So, I'm just sharing the solution I developed, in case anyone out there is still having issues with this.
I hate having setTimeouts and setIntervals in my code. So, I created a small plugin that you can put where you think it's best. I used setInterval, but you can change it to setTimeout or another solution you have in mind. The idea is simply to create a promise and keep checking for the element. We resolve the promise once it is ready.
// jquery.ensure.js
$.ensure = function (selector) {
var promise = $.Deferred();
var interval = setInterval(function () {
if ($(selector)[0]) {
clearInterval(interval);
promise.resolve();
}
}, 1);
return promise;
};
// my-app.js
function runWhenMyElementExists () {
// run the code that depends on #my-element
}
$.ensure('#my-element')
.then(runWhenMyElementExists);

Categories