Unable to click on EditText field which is not in focus - java

I have a situation like I am automating a native app using Appium(1.3.7) and Selenium webdriver (2.44) in that I want to enter the Date of birth field, when I am clicking on Date of birth field, it will pop up a window to enter the date of birth details, that window is not in focus and appium is failing to enter the details on the window as it is not able to recognize here I am not able to attach the screenshot.
Please help me how to get in to focus and enter the Date of birth details..
Using Google I tried a lot of methods like:
Method : 1
List<WebElement> Listed= dr.findElementsByClassName("android.widget.EditText");
for(int i=0; i <=Listed.size()-1;i++){
System.out.println("EditText Number="+i);
Listed.get(i).sendKeys("10");
Method : 2
WebElement Touch1= dr.findElement(By.xpath("//*[#class='android.widget.EditText']"));
//
TouchAction action = new TouchAction(dr);
action.press(168,440);
action.waitAction(300);
action.perform();
Method : 3
dr.tap(168,440,405,591);
Method : 4
WebElement DOBsample = dr.findElementByXPath("//*[text()[contains(.,'20')]]");
DOBsample.click();
Method : 5
dr.sendKeyEvent(66);

For pickers, you must use swiping over them. I´m using Scala, but logic is obvious:
def setYear(year: Int): Unit = {
require(year >= 1915 || year <= 2015)
val picker = drvr.findElementById("cz.app.yearPicker")
while (picker.getText != year.toString) {
if (picker.getText.toInt > year) {
swipePickerDown(picker)
} else { swipePickerUp(picker) }
}
}
def swipePickerUp(picker: AndroidElement): Unit = {
picker.swipe(SwipeElementDirection.UP, 400)
}
def swipePickerDown(picker: AndroidElement): Unit = {
picker.swipe(SwipeElementDirection.DOWN, 400)
}

Related

Closing (dismissing) Chrome print preview with Java + Selenium

My Selenium test looks something like this: customer selects a financial product, fills some necessary data and is presented with terms / agreement document in print preview (as required by local law). After printing / closing the print preview dialog customer enters more data and proceeds further, selects some options and finally gets another print preview of the contract. After that he confirms contract and process is done. I run my tests against Chrome version 75.
So far I've tried two things:
1. Switching to the print preview using Selenium, navigating to the "Cancel" button trough DOM and clicking it. But because the dialog uses shadow DOM it's very ugly, hard to maintain and frequently breaks after Chrome updates.
2. Tried using Robot class from awt, it works well when running locally but fails when running on Selenium grid because Chrome window is not focused and does not receive keyboard events.
Current state of the method handling the closure of print dialog:
public void closePrintPreview() {
WebDriverWait wait = new WebDriverWait(driver, 5);
wait.until(driver -> driver.getWindowHandles().size() > 1);
driver.switchTo().window(driver.getWindowHandles().toArray()[1].toString());
wait.until(d -> {
if (d.getWindowHandles().size() > 1) {
d.switchTo().window(driver.getWindowHandles().toArray()[1].toString());
try {
Robot robot = new Robot();
robot.keyPress(KeyEvent.VK_ESCAPE);
robot.keyRelease(KeyEvent.VK_ESCAPE);
} catch (AWTException e) {
throw new RuntimeException(e);
}
return false;
}
return true;
});
driver.switchTo().window(driver.getWindowHandles().toArray()[0].toString());
}
So my question would be if there is a simpler way to get the "Cancel" button in print print preview or maybe some way to force the Chrome window to be focused so it can receive key events from Robot?
Here is the solution in python.
You can update the same to work in java.
Python:
def cancelPrintPreview():
# get the current time and add 180 seconds to wait for the print preview cancel button
endTime = time.time() + 180
# switch to print preview window
driver.switch_to.window(driver.window_handles[-1])
while True:
try:
# get the cancel button
cancelButton = driver.execute_script(
"return document.querySelector('print-preview-app').shadowRoot.querySelector('#sidebar').shadowRoot.querySelector('print-preview-header#header').shadowRoot.querySelector('paper-button.cancel-button')")
if cancelButton:
# click on cancel
cancelButton.click()
# switch back to main window
driver.switch_to.window(driver.window_handles[0])
return True
except:
pass
time.sleep(1)
if time.time() > endTime:
driver.switch_to.window(driver.window_handles[0])
break
Java:
public void closePrintPreview() {
String jsCancel = "return document.querySelector('print-preview-app')" +
".shadowRoot.querySelector('#sidebar')" +
".shadowRoot.querySelector('print-preview-header#header')" +
".shadowRoot.querySelector('paper-button.cancel-button')";
WebDriverWait wait = new WebDriverWait(driver, 5);
JavascriptExecutor jse = (JavascriptExecutor) driver;
WebElement cancelButton;
wait.until(driver -> driver.getWindowHandles().size() > 1);
driver.switchTo().window(driver.getWindowHandles().toArray(new String[0])[1]);
while (driver.getWindowHandles().size() > 1) {
driver.switchTo().window(driver.getWindowHandles().toArray(new String[0])[1]);
cancelButton = (WebElement) jse.executeScript(jsCancel);
cancelButton.click();
}
driver.switchTo().window(driver.getWindowHandles().toArray(new String[0])[0]);
}
You can check my answer here for more information on working with shadow-root elements.
Here is the Java implementation based on answer by #supputuri:
public void closePrintPreview() {
String jsCancel = "return document.querySelector('print-preview-app')" +
".shadowRoot.querySelector('#sidebar')" +
".shadowRoot.querySelector('print-preview-header#header')" +
".shadowRoot.querySelector('paper-button.cancel-button')";
WebDriverWait wait = new WebDriverWait(driver, 5);
JavascriptExecutor jse = (JavascriptExecutor) driver;
WebElement cancelButton;
wait.until(driver -> driver.getWindowHandles().size() > 1);
driver.switchTo().window(driver.getWindowHandles().toArray(new String[0])[1]);
while (driver.getWindowHandles().size() > 1) {
driver.switchTo().window(driver.getWindowHandles().toArray(new String[0])[1]);
cancelButton = (WebElement) jse.executeScript(jsCancel);
cancelButton.click();
}
driver.switchTo().window(driver.getWindowHandles().toArray(new String[0])[0]);
}

React.js How to define a custom id, rather than showing react-select-1--value in html

How do I change the React.js application to stop randomly allocating inputIds, so that Selenium will work consistently?
I'm working with Selenium and a React.js application. The application is constantly under development. I have a Selenium method working to randomly select the react dropdowns using a single, reusable method, but the ids of the react dropdowns keep changing for some reason, perhaps each time the application is built, so this creates rework for the Selenium testing.
Selenium Method: (in JAVA)
Other than those react-select inputIds changing, this method works to randomly select options in the react dropdowns, but it needs to be cleaned up. It will select an option whether or not there is already an option selected by navigating away, then back to the dropdown.
public String RandomSelect(WebDriver mydriver, String myid)
{
try{
Actions actions = new Actions(mydriver);
actions.pause(300);
WebElement dropdown = mydriver.findElement(By.id(myid));
String scrollElementIntoMiddle = "var viewPortHeight = Math.max(document.documentElement.clientHeight, window.innerHeight || 0);" +"var elementTop = arguments[0].getBoundingClientRect().top;"+"window.scrollBy(0, elementTop-(viewPortHeight/2));";
((JavascriptExecutor) mydriver).executeScript(scrollElementIntoMiddle, dropdown);
//((JavascriptExecutor) mydriver).executeScript(
// "arguments[0].scrollIntoView();", dropdown);
actions.moveToElement(dropdown).click().build().perform();
actions.pause(1000);
actions.sendKeys(Keys.DELETE).build().perform();
actions.pause(1000);
actions.sendKeys(Keys.TAB).build().perform();
actions.pause(1000);
actions.moveToElement(dropdown).click().build().perform();
actions.pause(1000);
// actions.pause(3000);
//actions.sendKeys(Keys.DELETE);
WebDriverWait wait = new WebDriverWait(mydriver, 10);
wait.until(ExpectedConditions.elementToBeClickable(By.className("Select-option")));
List<WebElement> options = mydriver.findElements(By.className("Select-option"));
List<String> stroptions = new ArrayList<>();
System.out.println(options.size());
for (WebElement option: options) {
stroptions.add(option.getText());
System.out.println(option.getText());
}
Random rand = new Random();
int randomNum = rand.nextInt((options.size()));
String randomoption = stroptions.get(randomNum).toString();
actions.sendKeys(randomoption+Keys.RETURN).click().build().perform();
System.out.println("Random Option Is: "+ randomoption);
// mydriver.findElement(By.className("main-container"));
options.clear();
return randomoption;
}
catch (Exception ex)
{
System.out.println("React Select Error: " + ex.toString());
return null;
}
}
Using the Selenium Method:
Doing something like this 100's of times is easier than typing all the Selenium methods 100's of times.
WebDriver driver;
driver = new EdgeDriver();
ReactDropdown mydropdown = new ReactDropdown();
mydropdown.RandomSelect(driver, "react-select-1--value");
How can I remove the dynamically assigned "react-select-1--value" and define the id as something more intuitive like "mydropdown--value", so that each time the application builds the id is maintained?
This is an example of the rendered html:
React.js html output
<div class="prop-row">
<div class="dropdown field mydropdown ">
<div class="field-label">
<label for="mydropdown">mydropdownlabel</label>
</div>
<div class="Select mydropdown undefined is-searchable Select--single">
<div class="Select-control">
<span class="Select-multi-value-wrapper" id="react-select-1--value">
<div class="Select-placeholder">Select...</div>
Getting rid of that ugly:
id="react-select-1--value"
and changing it to
id="mydropdown--value"
so that it is the same, detailed, and predictable every time would be ideal for testing. There's more than 15 dropdowns on one page and without an intuitive id, I need to either change my Selenium method, or the developers need to add better ids to the application. We want our Selenium tests to run in a pipeline using TestNG, and that will never work until this is resolved. Going with changing the react inputId's seems more Configuration Management(CM) friendly anyway to me. All those inputs should be managed through CM.
I just started with react, and it's not the most intuitive to me, yet...
You can use xpath as -
//span[#class='Select-multi-value-wrapper' and #id[starts-with(#id,'react-select')
I found a solution on my own, inputId is the key to make a unique id and to remove that react-select ugly. Here is an example....
import React from 'react';
import Select from 'react-select';
const options = [
{ value: 'chocolate', label: 'Chocolate' },
{ value: 'strawberry', label: 'Strawberry' },
{ value: 'vanilla', label: 'Vanilla' }
];
export class Counter extends React.Component {
state = {
selectedOption: null,
}
handleChange = (selectedOption) => {
this.setState({ selectedOption });
console.log(`Option selected:`, selectedOption);
}
render() {
const { selectedOption } = this.state;
return (
<Select
value={selectedOption}
inputId="mydropdown"
onChange={this.handleChange}
options={options}
/>
);
}
}
After an inputId is statically defined, my Selenium method seems to work better.

StaleElementReference Exception with Webdriver using firefox v57

Using the following code I keep getting a StaleElementReference Exception when running my Webdriver scripts on firefox v57. I have tried all sorts of things but other than a thread.sleep or catching the exception and retrying I cannot get it to work.
public List<String> readCoIData (String column) throws StaleElementReferenceException
{
int colNumber 0;
WebTable searchResuIts = getTable();
FluentWait<WebDriver> wait = new FluentWait<WebDriver>(driver)
wait.pollingEvery(250, TimeLinit.MILLISECONDS);
wait.with Timeout (30, TimeLlnit.SECONDS);
wait.ignoring (NoSuchElementException.class) ;
CustomLogger.addInfo(Logger, "Ensure that that the first column row contains text: " column ) ;
int colcount = searchResults.getColumnCount();
CustomLogger. addlnfoLog4jOnly (logger , "colcount= " colcount )
for (int col =1; col <= colcount; col++)
{
CustomLogger.addInfoLogJOnly(Logger, "Get the cell data for col. Use a string as this does not go stale unlike a reference.'
String locator = String.format("pf_table_t2 > tbody:nthchild(1) > tr:nth-child(1) > td:nth-child(%d)", col);
wait.until(ExpectedConditions.presenceOfElementLocated(By. cssSelector (locator))) ;
if (driver.FindElement(By.cssSelector(locator)).getText().contains (column))
{
colNumber = Col;
}
}
CustomLogger.addInfoLogJOnly(Logger, "Assert that there is not 0 columns");
assertThat (column + " not found' , colNumber !=0, is (true));
List<String> colvalues = new ArrayList<String>();
CustomLogger.addInfoLogJOnly(Logger, "Get the object .pf_paging a");
List<WebElement) paging = driver.findElements(By.cssSelector(.pf_paging a));
if (paging.size() !=0)
{
CustomLogger.addlnfoLogJOnly ( logger , "If more than one result page, wait..." );
wait.until(ExpectedConditions.visibilityOf((WebElement driver.findElement(By.cssSelector(".pf_paging_next_active"))));
CustomLogger.addInfoLogJOnly(Logger, "Span with '>>' is found so loop through all pages and get the data");
while (driver.findElements(By.cssSelector(".pf_paging_next_active")). size() == 1)
{
for (int i=2; i<=searchResults.getRowCount(); i++)
{
String locator = String.format("pf_table_t2 > tbody:nthchild(1) > tr:nth-child(%d) > td:nth-child(%d)", I, colNumber); col);
String cell Text = driver.findElement(By.cssSelector(locator)).getText();
colvalues.add(cellText);
}
CustomLogger.addInfoLogJOnLy(Logger, "Click the Next Page Button ' to move onto next page and wait for the page to be visible.'
driver.findElements(By.cssSelector(.pf_paging_next_active a").click();
CustomLogger.addInfoLogJ0nly(Logger, "Get & Wait for table after clicking next");
// Get the element table again as we have clicked 'filter' and the DOM would have changed.
searchResults.getTable();}
public WebTable getTable()
{
CustomLogger.addInfoLog450nLy(Iogger, "Wait and get the element table again as we have previously clicked 'filter' and the DOM would have changed");
new WebDriverWait(driver, 30).until((ExpectedConditions<Boolean> driver -> ((JavascriptExecutor) driver).executeScript("return document.readyState").equals("complete"));
FluentWait(WebDriver» wait new
wait.pollingEvery(250, TimeLinit.MILLISECONDSS);
wait.with Timeout(30, TimeLlnit.SECONDS);
wait.ignoring (NoSuchEIementException.class) ;
CustomLogger.addInfo(Logger, "Wait until the results table is present..
wait.until(ExpectedConditions.presenceOfElementLocated(By.cssSelector("pf_table_t2"))));
CustomLogger.addInfo(Logger, "Get the results table");
SearchResuItsTable = driver.FindElement(By.cssSelector("pf_table_t2"));
WebTabIe searchResuIts = new WebTable(SearchResultsTabIe);
return searchResuIts;
}
Essentially the page has some filter criteria and a results table with results in blocks of 10 rows, to get each new set you have to click >. i.e. the page changes.
I get StaleElement exception in two places, the first happens getting the text at the locator (then setting colNumber = Col). The 2nd happens adding celltext to the colValues array.
In both places I have only just got the element again. Any help would be appreciated. Thanks in advance.
Note I don't get this with the Chrome browser.
Just to give you a brief (if you are not aware already)
StaleElementReferenceException happens because location of the webelement on the webpage (more preciously on the DOM) has changed due some ajax or similar reason. Coincidentally this happens exactly between you capturing the webelement and performing the action on the webelement.
For Example:
//Getting the webelement
WebElement element = driver.findElement(By.id("id_value"));
//Location of the webelement changes between these two steps.
//Click on the webelement
element.click();
One of the ways to handle the StaleElementReferenceException is to catch the exception at the place where it happens and handle it by re-initializing the webelement.
For example:
try {
WebElement element = driver.findElement(By.id("id_value"));
element.click();
} catch (StaleElementReferenceException e) {
Thread.sleep(3000);
WebElement element = driver.findElement(By.id("id_value"));
element.click();
}
There are also others ways to handle the StaleElementReferenceException. You can refer this link.
Better and cleaner way to handle StaleElementReferenceException is to make method for action that you are trying to perform.
For Example:
If you want click on a webelement.
public static void click(By by) throws Exception {
try {
driver.findElement(by).click();
} catch(StaleElementReferenceException staleElement) {
Thread.sleep(3000);
driver.findElement(by).click();
} catch (Exception e) {
e.printStacktrace();
}
}
Similarly you can create other methods.

Select options from Autopopulate text boxes using Selenium webdriver

Driver.findElement(By.xpath("//*[#id='client']")).sendKeys("Ho");
Driver.manage().timeouts().implicitlyWait(1,TimeUnit.MINUTES);
WebElement dropdown = (new WebDriverWait(Driver, 10)).until(ExpectedConditions.presenceOfElementLocated(By.xpath("//*[#id='client']")));
Driver.findElement(By.xpath("//*[#id='collapseClientInfo']/div/form/div[3]/div[2]/ul/li[1]/a")).sendKeys(Keys.ENTER);
Could you please help me to select auto populate value from drop down list:
We've Client textbox which is an auto-populate box.
When I enter "ho" text in the client field, it shows me the drop down which has values related to my entered text i.e. ho, then I have to select those values which are available under list.
In above code I've tried with Press Enter but unable to select the value.
Could you please check the above code and help me out for the same?
You should try as below :-
WebDriverWait wait = new WebDriverWait(Driver, 60);
//wait until loader invisible
wait.until(ExpectedConditions.invisibilityOfElementLocated(By.id("loaderDiv")));
//this sleep is required because after invisibility of loader focus goes to first input which is Requisition Number
//If you are filling form from first input no need to for this sleep
//if you want to input directly to client field need to sleep to avoid focus first
Thread.sleep(3000);
//Now find the client input and set value
WebElement client = wait.until(ExpectedConditions.visibilityOfElementLocated(By.id("client")));
client.sendKeys("Ho");
//Now find all the showing option
List<WebElement> dropdownOptions = wait.until(ExpectedConditions.presenceOfAllElementsLocatedBy(By.cssSelector("ul.dropdown-menu a")));
//Now select the first option
dropdownOptions.get(0).click();
Below approach might be helpful:
// Enter text in auto complete text box
driver.findElement(By.xpath("//*[#id='client']")).sendKeys("Ho");
// Wait for options to display
Thread.sleep(5000);
// Option to select
String optionToSelect = "Honda";
Boolean isOptionSelected = Boolean.FALSE;
// Get the options displayed
List<WebElement> options = driver.findElements(By
.cssSelector("ul.dropdown-menu a"));
// Select option
for (WebElement webElement : options) {
if (webElement.getText().equalsIgnoreCase(optionToSelect)) {
webElement.click();
isOptionSelected = Boolean.TRUE;
}
}
if (isOptionSelected) {
// Option is selected
} else {
// Expected option is not displayed. Fail the script
}
Try this:
Select drpCountry = new Select(driver.findElement(By.id("searchOptions")));
drpCountry.selectByVisibleText("By author");

How to deal with datepicker in Appium Android

My android application is using datepicker but i am not able to select date through datepicker. I used following code in application for datepicker but it does not work.
List<WebElement> pick = driver.findElements(By.className("android.widget.EditText"));
pick.get(0).sendKeys("21");
pick.get(1).sendKeys("Mar");
pick.get(2).sendKeys("1989");
Swipe method will help you to scroll calendar dates , Make sure that you have added Java-client JARs to your project then only swipe method will support.
Example :
First click on your calendar icon and then use following code :
Thread.sleep(5000);
for(int y=0;y<3;y++)
{
driver.swipe(350,511,350,577,0);
}
Swipe Syntax :
driver.swipe(startx, starty, endx, endy, duration);
Note : Above in code I have used sample co-ordinates so you change it according to your need. You can get exact co-ordinates from bound values of that date picker.
I have used loop in above code as I want to swipe 3 times , so it is something like if current date is 1st may then it will swipe till 4th may.
you can modify loop as per your need.
I have used Xpath to perform Datepicker action & it is working properly.
driver.findElement(By.xpath("//android.widget.NumberPicker[#index='0']")).sendKeys("Jan");
driver.findElement(By.xpath("//android.widget.NumberPicker[#index='1']")).sendKeys("24");
driver.findElement(By.xpath("//android.widget.NumberPicker[#index='2']")).sendKeys("1987");
For all the user who are still finding the way to select date can use the below code. I am using this code and working perfectly for me. It will work for calendar attached.
do {
WebElement source = driver.findElement(By.xpath("//android.view.View[#instance='0']"));
WebElement destination = driver.findElement(By.xpath("//android.view.View[#instance='22']"));
TouchAction action = new TouchAction((PerformsTouchActions)driver);
System.out.println("Dragging item");
action.longPress(source).moveTo(destination).release().perform();
boolean bul = driver.findElementsByXPath("//android.view.View[#content-desc='24 January 2018']").isEmpty();
} while(bul!=false);
driver.findElementByAccessibilityId("24 January 2018").click();
NOTE: I used drag and drop touch action to scroll and this will scroll uptill given date is not found. I just selected same years previous month date. You can use same touch action to select desired year.
I wanted to do the same thing, but for a "calendar" mode DatePicker instead of the "spinner" mode. This is my solution, which has been working fine for me.
from datetime import datetime
datePickerYearTextViewXpath = "//android.widget.TextView[#resource-id='android:id/date_picker_header_year']"
# initialize your appium driver here
driver = getAppiumDriver()
# define some screen dimensions
screenSize = driver.get_window_size()
halfScreenWidth = screenSize['width'] // 2
halfScreenHeight = screenSize['height'] // 2
def getDatePickerCurrentDate(driver):
yearTextView = driver.find_element_by_xpath(datePickerYearTextViewXpath)
yearString = yearTextView.text
dateTextView = driver.find_element_by_xpath("//android.widget.TextView[#resource-id='android:id/date_picker_header_date']")
dateString = dateTextView.text
fullDateString = '{}, {}'.format(dateString, yearString)
currentDate = datetime.strptime(fullDateString, '%a, %b %d, %Y').date()
return currentDate
def setDatePickerDate(driver, targetDate):
# driver is an appium driver
# targetDate must be a datetime.date, not a datetime
currentDate = getDatePickerCurrentDate(driver)
if targetDate.year != currentDate.year:
yearTextView = driver.find_element_by_xpath(datePickerYearTextViewXpath)
yearTextView.click()
# you may need to adjust the following numbers
# depending on your screen size
swipeAmountPerYear = 49
yearsPerScreen = 8
swipeDuration = 400
yearOffset = targetDate.year - currentDate.year
# if target year is older, swipe up (negative)
swipeDirection = -1 if yearOffset < 0 else 1
swipeVector = yearsPerScreen * swipeAmountPerYear * swipeDirection
while True:
elements = driver.find_elements_by_xpath("//android.widget.TextView[#resource-id='android:id/text1']".format(targetDate.year))
found = False
for element in elements:
if element.text == str(targetDate.year):
element.click()
found = True
break
if found:
break
else:
driver.swipe(halfScreenWidth, halfScreenHeight, halfScreenWidth, halfScreenHeight - swipeVector, swipeDuration)
currentDate = getDatePickerCurrentDate(driver)
if targetDate.month != currentDate.month:
monthOffset = targetDate.month - currentDate.month
prevOrNext = 'prev' if monthOffset < 0 else 'next'
prevOrNextButtonXpath = "//android.widget.ImageButton[#resource-id='android:id/{}']".format(prevOrNext)
for i in range(abs(monthOffset)):
driver.find_element_by_xpath(prevOrNextButtonXpath).click()
targetDateContentDescription = targetDate.strftime('%d %B %Y')
driver.find_element_by_xpath("//android.view.View[#resource-id='android:id/month_view']/android.view.View[#content-desc='{}']".format(targetDateContentDescription)).click()
currentDate = getDatePickerCurrentDate(driver)
if currentDate != targetDate:
raise ValueError('Unable to set date picker({}) to target date({})!'.format(currentDate, targetDate))
Check if this helps
driver.FindElement(By.Id("com.eos.eos_du_su:id/ed_manufdate")).Click();
((AndroidElement)(driver.FindElement(By.XPath("//android.widget.NumberPicker[#index='0']//android.widget.Button[#index=0]")))).Tap(1, 2);
((AndroidElement)(driver.FindElement(By.XPath("//android.widget.NumberPicker[#index='1']//android.widget.Button[#index=0]")))).Tap(1, 2);
((AndroidElement)(driver.FindElement(By.XPath("//android.widget.NumberPicker[#index='2']//android.widget.Button[#index=0]")))).Tap(1, 2);
driver.FindElement(By.Id("android:id/button1")).Click();

Categories