how to detect changes to an element - java

im using selenium to automate a test.
so i have an element that is changed by javascript. how can i detect a change to the text of that item? for example, here is the html of the item before and after it's change:
before:
<span id="errorMsg"></span>
after:
<span id="errorMsg">All fields not filled out</span>
this is a current helper function i have that i call after clicking the submit button. it works very well when loading new pages but it doesn't work in this scenario.
public void waitforText(String search) {
WebDriverWait myWait = new WebDriverWait(Grid.driver(), Grid.getNewTimeOut() / 1000L);
ExpectedCondition conditionToCheck = new ExpectedCondition() {
public Boolean apply(WebDriver input) {
WebElement bodyTag = Grid.driver().findElement(By.tagName("body"));
return Boolean.valueOf(bodyTag.getText().contains(search));
}
};
myWait.until(conditionToCheck);
}
and how im calling the function:
waitforText("All fields not filled out");
i can see the message changed in the page but selenium does not detect this. i know i can use a direct sleep or wait instead but i really do not want to go this route if it can be avoided

Use textToBePresentInElement expected condition:
WebDriverWait wait = new WebDriverWait(driver, 30);
wait.until(ExpectedConditions.textToBePresentInElement(By.id("errorMsg"), "All fields not filled out"));

Related

How to click on a button until an element appears on a page in selenium

I have a page with a dynamic table that periodically updates with new data. What I am trying to do is to click on a button and reload the page let's say every 3 seconds until the element from that table appears.
I know the xpath of the element I want to appear, but I just can't make it work using the FluentWait. I tried configuring as seen in the code below, but when I'm calling the method it keeps clicking on the button extremly fast disregarding the pollingEvery configuration and without giving the page enough time to fully reload itself. Code can be checked out below.
What I am not sure about is the return statement. I don't fully grasp what should it be if I only need to click on a button until that element appears.
What am I missing?
public void clickButtonUntilElementIsDisplayed(WebElement elementToBeClicked, String xPathOfElementToBeDisplayed){
FluentWait<WebDriver> wait = new FluentWait<>(getDriver());
wait.pollingEvery(Duration.ofSeconds(5));
wait.withTimeout(Duration.ofSeconds(200));
wait.ignoring(NoSuchElementException.class);
Function<WebDriver,WebElement> clickUntilAppearance = webDriver -> {
while(webDriver.findElements(By.xpath(xPathOfElementToBeDisplayed)).isEmpty())
click(elementToBeClicked);
return webDriver.findElement(By.xpath(xPathOfElementToBeDisplayed));
};
wait.until(clickUntilAppearance);
}
The class in which this method can be found extends Page
The return type of the Functional Interface need to be changed. There are 2 cases in which the functional interface will keep looping.
Value returned is null.
It threw an Exception mentioned as part of ignoring
Don't use while. Use if condition. For every pollingEvery this Functional Interface would get executed.
Function<WebDriver,WebElement> clickUntilAppearance = webDriver -> {
List<WebElement> tableElements = webDriver.findElements(By.xpath(xPathOfElementToBeDisplayed));
if(tableElements.isEmpty()) {
click(elementToBeClicked);
return null;
} else {
return tableElements.get(0);
}
};
WebDriverWait w = new WebDriverWait(driver, 5);// 5seconds
w.until(ExpectedConditions.visibilityOfElementLocated(By.cssSelector("element_location")));

Selenium in Java - can't go to the next page [duplicate]

I am trying to make some tests using selenium based Katalon Studio. In one of my tests I have to write inside a textarea. The problem is that I get the following error:
...Element MyElement is not clickable at point (x, y)... Other element would receive the click...
In fact my element is place inside some other diva that might hide it but how can I make the click event hit my textarea?
Element ... is not clickable at point (x, y). Other element would receive the click" can be caused for different factors. You can address them by either of the following procedures:
Element not getting clicked due to JavaScript or AJAX calls present
Try to use Actions Class:
WebElement element = driver.findElement(By.id("id1"));
Actions actions = new Actions(driver);
actions.moveToElement(element).click().build().perform();
Element not getting clicked as it is not within Viewport
Try to use JavascriptExecutor to bring the element within Viewport:
JavascriptExecutor jse1 = (JavascriptExecutor)driver;
jse1.executeScript("scroll(250, 0)"); // if the element is on top.
jse1.executeScript("scroll(0, 250)"); // if the element is at bottom.
Or
WebElement myelement = driver.findElement(By.id("id1"));
JavascriptExecutor jse2 = (JavascriptExecutor)driver;
jse2.executeScript("arguments[0].scrollIntoView()", myelement);
The page is getting refreshed before the element gets clickable.
In this case induce some wait.
Element is present in the DOM but not clickable.
In this case add some ExplicitWait for the element to be clickable.
WebDriverWait wait2 = new WebDriverWait(driver, 10);
wait2.until(ExpectedConditions.elementToBeClickable(By.id("id1")));
Element is present but having temporary Overlay.
In this case induce ExplicitWait with ExpectedConditions set to invisibilityOfElementLocated for the Overlay to be invisible.
WebDriverWait wait3 = new WebDriverWait(driver, 10);
wait3.until(ExpectedConditions.invisibilityOfElementLocated(By.xpath("ele_to_inv")));
Element is present but having permanent Overlay.
Use JavascriptExecutor to send the click directly on the element.
WebElement ele = driver.findElement(By.xpath("element_xpath"));
JavascriptExecutor executor = (JavascriptExecutor)driver;
executor.executeScript("arguments[0].click();", ele);
I assume, you've checked already that there is no any other component overlapping here (transparent advertisement-iframes or some other component of the DOM => seen quite often such things in input/textfield elements) and, when manually (slowly) stepping your code, it's working smoothly, then ajax calls might cause this behaviour.
To avoid thread.sleep, try sticking with EventFiringWebDriver and register a handle to it.
(Depending on your application's techstack you may work it for Angular, JQuery or wicket in the handler, thus requiring different implementations)
(Btw: This approach also got me rid of "StaleElementException" stuff lots of times)
see:
org.openqa.selenium.support.events.EventFiringWebDriver
org.openqa.selenium.support.events.WebDriverEventListener
driveme = new ChromeDriver();
driver = new EventFiringWebDriver(driveme);
ActivityCapture handle=new ActivityCapture();
driver.register(handle);
=> ActivityCapture implements WebDriverEventListener
e.g. javascriptExecutor to deal with Ajax calls in a wicket/dojo techstack
#Override
public void beforeClickOn(WebElement arg0, WebDriver event1) {
try {
System.out.println("After click "+arg0.toString());
//System.out.println("Start afterClickOn - timestamp: System.currentTimeMillis(): " + System.currentTimeMillis());
JavascriptExecutor executor = (JavascriptExecutor) event1;
StringBuffer javaScript = new StringBuffer();
javaScript.append("for (var c in Wicket.channelManager.channels) {");
javaScript.append(" if (Wicket.channelManager.channels[c].busy) {");
javaScript.append(" return true;");
javaScript.append(" }");
;
;
;
javaScript.append("}");
javaScript.append("return false;");
//Boolean result = (Boolean) executor.executeScript(javaScript.toString());
WebDriverWait wait = new WebDriverWait(event1, 20);
wait.until(new ExpectedCondition<Boolean>() {
public Boolean apply(WebDriver driver) {
return !(Boolean) executor.executeScript(javaScript.toString());
}
});
//System.out.println("End afterClickOn - timestamp: System.currentTimeMillis(): " + System.currentTimeMillis());
} catch (Exception ex) {
//ex.printStackTrace();
}
}
As #DebanjanB said, your button (or another element) could be temporarily covered by another element, but you can wait and click it even if you don't know which element is covering the button.
To do this, you can define your own ExpectedCondition with the click action:
public class SuccessfulClick implements ExpectedCondition<Boolean> {
private WebElement element;
public SuccessfulClick(WebElement element) { //WebElement element
this.element = element;
}
#Override
public Boolean apply(WebDriver driver) {
try {
element.click();
return true;
} catch (ElementClickInterceptedException | StaleElementReferenceException | NoSuchElementException e) {
return false;
}
}
}
and then use this:
WebDriverWait wait10 = new WebDriverWait(driver, 10);
wait10.until(elementToBeClickable(btn));
wait10.until(new SuccessfulClick(btn));
Try Thread.Sleep()
Implicit - Thread.Sleep()
So this isn’t actually a feature of Selenium WebDriver, it’s a common feature in most programming languages though.
But none of that matter.
Thread.Sleep() does exactly what you think it does, it’s sleeps the thread. So when your program runs, in the majority of your cases that program will be some automated checks, they are running on a thread.
So when we call Thread.Sleep we are instructing our program to do absolutely nothing for a period of time, just sleep.
It doesn’t matter what our application under test is up to, we don’t care, our checks are having a nap time!
Depressingly though, it’s fairly common to see a few instances of Thread.Sleep() in Selenium WebDriver GUI check frameworks.
What tends to happen is a script will be failing or failing sporadically, and someone runs it locally and realises there is a race, that sometimes WedDriver is losing. It could be that an application sometimes takes longer to load, perhaps when it has more data, so to fix it they tell WebDriver to take a nap, to ensure that the application is loaded before the check continues.
Thread.sleep(5000);
The value provided is in milliseconds, so this code would sleep the check for 5 seconds.
I was having this problem, because I had clicked into a menu option that expanded, changing the size of the scrollable area, and the position of the other items. So I just had my program click back on the next level up of the menu, then forward again, to the level of the menu I was trying to access. It put the menu back to the original positioning so this "click intercepted" error would no longer happen.
The error didn't happen every time I clicked an expandable menu, only when the expandable menu option was already all the way at the bottom of its scrollable area.

Refreshing page until element appears - JAVA - Selenium

I'm new to coding and I have the following issue while automating with Selenium using Java:
I'm testing a SaaS solution and I need to refresh my page until a certain item appears in my inbox. I cannot simply use the findelement statement since the item only appears after some time and onlywhen the page has been refreshed. Furthermore all inbox items have a unique sequential number in the title. This unique number I have saved as a string variable and I want to use this string variable to see if the inbox item has appeared after refreshing the page some times.
Code for finding unique string:
//Get expense report number
String filename = strng;
String ExpenseReportNumber = StringUtils.substringBefore(filename, " submitted"); // returns Expense Report #XXXX
Now I need to create a loop in which the site keeps refreshing until it gets a hit on the string variable. When it finds the string variable I then can select the top element on the inbox and continue my test.
Can someone assist me with this issue or give me advice on how to create the same outcome but then with a better approach? Much appreciated!
Wait<WebDriver> wait12 = new FluentWait<WebDriver>(driver)
.withTimeout(600, TimeUnit.SECONDS)
.pollingEvery(15, TimeUnit.SECONDS)
.ignoring(NoSuchElementException.class);
WebElement ToDoExpense = wait12.until(new Function<WebDriver, WebElement>() {
public WebElement apply(WebDriver driver) {
driver.navigate().refresh();
return driver.findElement(By.partialLinkText(ExpenseReportNumber));
}
});
I have found the solution myself
public WebDriverWait wait = new WebDriverWait(driver, 10); //throws exception if element is not found within 10 seconds
driver.navigate().refresh();
wait.until(ExpectedConditions.presenceOfElementLocated(By.partialLinkText(ExpenseReportNumber)));
//continue your test
I worked it out like this:
do
{
driver.refresh();
driver.waitForPageToLoad();
driver.wait(1000);
} while (driver.findElements(By.locator(element)).size() < 1);

Selenium Webdriver to wait until the asyn postback update panel request is completed

I have a table with clickable column header. On first click, the column should get sorted in ascending order and on second click column should get sorted in descending order.
The sorting has been implemented using async postback update-panel (I am not sure how it is done, it is an aspx page).
I would like to automate the sorting functionality using Selenium Webdriver. How can I implement the WAIT condition for the page where page doesn't get reloaded but only the table contents are reloaded.
waitForElementPresent wouldn't work, as no new element is displayed or hid on clicking the header.
PS: Java implentation required.
I have added a sample program that is related to a jquery table. Below is the flow of execution of the code:
First, it will navigate to the site.
Since I am taking the second column "Position" into consideration, it will retrieve first text under the column.
Then, click on the column header "Position" for sorting in ascending
Wait, 10 seconds(max), till the first text changes.
Print the result accordingly.
Again, click on the column header "Position" for sorting in descending
Wait, 10 seconds(max), till the first text changes.
Print the result accordingly.
public class TestSortTable{
static WebDriver driver;
public static void main(String[] args){
driver = new FirefoxDriver();
driver.manage().window().maximize();
driver.manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS);
driver.get("http://www.datatables.net/examples/basic_init/table_sorting.html");
//For Ascending in column "Position"
String result = clickAndWaitForChangeText(By.xpath("//table[#id='example']//th[2]"), By.xpath("//table[#id='example']//tr[1]/td[2]"), "Ascending");
if(result.contains("Fail"))
System.err.println(result);
else
System.out.println(result);
//For Descending in column "Position"
result = clickAndWaitForChangeText(By.xpath("//table[#id='example']//th[2]"), By.xpath("//table[#id='example']//tr[1]/td[2]"),"Descending");
if(result.contains("Fail"))
System.err.println(result);
else
System.out.println(result);
driver.close();//closing browser instance
}
//For clicking on header and waiting till the first text in the column changes
public static String clickAndWaitForChangeText(By Header_locator, By first_text_locator, String sortorder){
try{
String FirstText = driver.findElement(first_text_locator).getText();
System.out.println("Clicking on the header for sorting in: "+sortorder); //sortorder -> String representing sort order Ascending/Descending
driver.findElement(Header_locator).click();//Click for ascending/Descending
//Below code will wait till the First Text changes for ascending/descending
boolean b = new WebDriverWait(driver,10).until(ExpectedConditions.invisibilityOfElementWithText(first_text_locator, FirstText));
if(b==true){
return "Pass: Waiting Ends. Text has changed from '"+FirstText+"' to '"+driver.findElement(first_text_locator).getText()+"'";
}
else{
return "Fail: Waiting Ends. Text hasn't changed from '"+FirstText+"'.";
}
}catch(Throwable e){
return "Fail: Error while clicking and waiting for the text to change: "+e.getMessage();
}
}
}
NOTE:- You can use the method clickAndWaitForChangeText accordingly in your code for the relevant result(s).
You should wait until the JQuery.active retuns 0. Mine is written in c#. Addition to this, we can also wait for a specific element that you know will satisfy your wait criteria. You can use fluentWait or write your custom wait to wait until the element exists.
public void WaitForAjax()
{
var wait = new WebDriverWait(driver, TimeSpan.FromSeconds(15));
wait.Until(d => (bool)(d as IJavaScriptExecutor).ExecuteScript("return jQuery.active == 0"));
}
EDIT: Java version
public void waitForAjaxLoad(WebDriver driver) throws InterruptedException{
JavascriptExecutor executor = (JavascriptExecutor)driver;
if((Boolean) executor.executeScript("return window.jQuery != undefined")){
while(!(Boolean) executor.executeScript("return jQuery.active == 0")){
Thread.sleep(1000);
}
}
return;
}
Directly taken from here

How to access a Button with Selenium, when the button seems to be generated from JavaScript

I try to run some tests on a webpage. I'm a beginner and until now everything went pretty smooth.
I'm changing some values in a webform and now want to press the "Save & Exit" button.
But when I look at the available source with the webdriver (driver.getPageSource();) , I don't see a button, only the below JavaScript. I shortened the script to one button - the one button I would like to click.
function getToolbarCfg() {
return [{ btnId: 2, icon:'images/obj16/tsave_.gif', text:'Save & Exit', qtip:'Save Record and Exit', handler:function() { cwc.getCenterWindow().tpzExecute('2',null,'detail'); } }];
Any help is appreciated.
You can have the WebDriver wait explicitly for an element to appear.
public static IWebElement WaitForElementToAppear(IWebDriver driver, int waitTime, By waitingElement)
{
IWebElement wait = new WebDriverWait(driver, TimeSpan.FromSeconds(waitTime)).Until(ExpectedConditions.ElementExists(waitingElement));
return wait;
}
Read up on Selenium's WebDriver Wait Documentation for a good explanation of explicit and implicit waits.
EDIT:
You could alternatively do this:
public static IWebElement WaitForElementToAppear(IWebDriver driver, int waitTime, By waitingElement)
{
WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(waitTime));
var element = wait.Until(d =>
{
var elem = driver.FindElement(waitingElement);
if (elem.Enabled)
return elem;
else return null;
});
return element;
}
This will ping the element every .5 seconds until whatever waitTime is defined or to be OR until the element is present.
Found the solution:
The button is executing a JavaScript when clicked. One can directly call that JavaScript function with the Selenium WebDriver.
((JavascriptExecutor) driver).executeScript("cwc.getCenterWindow().tpzExecute('3',null,'detail');");
Hope this answer helps people having the same troubles.

Categories