Is it possible to get By object having WebElement - java

I'm using #FindBy annotations to locate WebElements. But I want to use these WebElements in methods as parameters. I had success with this only if WebElement was found on page. But if I want to wait for it I must use By (with same locator) instead.
E.g.
private static final String SEARCHFIELD_LOC = "#search input[placeholder]";
#FindBy(css = SEARCHFIELD_LOC)
public WebElement searchField;
public By searchField_by = new By.ByCssSelector(SEARCHFIELD_LOC);
That way I can use this WebElement as By object in some method, like
public boolean isElementDisplayedWithWait(final By by) { .... }
So for each element I have 4 lines of code. I'm to lazy and searching for a way to simplify that.

I found totally another solution. I will store my locators as strings.
public String searchField = ".slide.slide_search input[placeholder]"
So only 1 line of code per locator. And I hope supporting only CSS locators will be enough. I will NOT use #FindBy for getting WebElements. Instead, I will get WebElements using special method that will wait for element and will log what's going on. I will implement getting WebElement like this:
WebElement we = wait.until(ExpectedConditions.visibilityOfElementLocated(new By.ByCssSelector(locator)));
where wait is
Wait<WebDriver> wait = new FluentWait<WebDriver>( driver )
.withTimeout(timeout, TimeUnit.SECONDS)
.pollingEvery(1, TimeUnit.SECONDS)
.ignoring( StaleElementReferenceException.class ) ;

The alternate of having wait/assertions/verification methods with webelement itself is available with extended webelement in QAF formerly ISFW. For example:
#FindBy(css = SEARCHFIELD_LOC)
public WebElement searchField;
searchField.waitForPresent();
searchField.assertPresent(); //auto wait
searchField.verifyPresent(); //auto wait
Reference:
QAF Selenium testing-frameworks
ISFW FAQ

Related

Unable to find locators(Xpath,Css) ,in the webpage

I am trying to automate application,tried first to find xpath or CSS locators unable to find looks no frame also inside the element.
I am able to handle using JavaScript but unable to enter full text in the search box,it's trimming some text,,please help me.
JavaScript which i tried.
((JavascriptExecutor)driver).executeScript("return document.querySelector('#app').shadowRoot.querySelector('#base > wego-search-form').shadowRoot.querySelector('div > wego-hotel-search-form').shadowRoot.querySelector('#loc').shadowRoot.querySelector('#item0 > div.disable-select.city-country-name').click();");
((JavascriptExecutor)driver).executeScript("return document.querySelector('#app').shadowRoot.querySelector('#base > wego-search-form').shadowRoot.querySelector('div > wego-hotel-search-form').shadowRoot.querySelector('#dates').shadowRoot.querySelector('#depart').shadowRoot.querySelector('#btn').click();");
My scenario i want to click search form and enter some destination details,If possible anyway i can handle this case using locators suggest me
Shadow DOM Elements are used in this website. Shadow DOM provides encapsulation for the JavaScript, CSS, and templating in a Web Component.
Shadow DOM allows hidden DOM trees to be attached to elements in the
regular DOM tree — this shadow DOM tree starts with a shadow root,
underneath which can be attached to any elements you want, in the same
way as the normal DOM.
Refer this To get details about it or for more details Google it.
Now handle Shadow element take reference from this blog. I've tried the below code to enter text as you expected and its working for me.
public static WebDriver driver;
public static void main(String[] args){
System.setProperty("webdriver.chrome.driver", "driver_path");
driver = new ChromeDriver();
driver.get("https://www.wego.com.my/hotels");
driver.manage().timeouts().implicitlyWait(15, TimeUnit.SECONDS);
driver.manage().window().maximize();
WebElement root1 = driver.findElement(By.id("app"));
WebElement shadowRoot1 = expandRootElement(root1);
WebElement root2 = shadowRoot1.findElement(By.tagName("wego-search-form"));
WebElement shadowRoot2 = expandRootElement(root2);
WebElement root3 = shadowRoot2.findElement(By.tagName("wego-hotel-search-form"));
WebElement shadowRoot3 = expandRootElement(root3);
WebElement root4 = shadowRoot3.findElement(By.id("loc"));
WebElement shadowRoot4 = expandRootElement(root4);
shadowRoot4.findElement(By.cssSelector("div.root-container div.container")).click();
WebElement element = shadowRoot4.findElement(By.id("searchKeywordInput"));
Actions action = new Actions(driver);
action.click(element).sendKeys(Keys.chord(Keys.CONTROL, "a"));
element.sendKeys(Keys.DELETE);
element.sendKeys("narendra");
}
public static WebElement expandRootElement(WebElement element) {
WebElement newElement = (WebElement) ((JavascriptExecutor) driver).executeScript("return arguments[0].shadowRoot", element);
return newElement;
}
Updated
As an alternative of sendKeys(), JavascriptExecutor can be used to set the value of text box. Use below code
WebElement element = shadowRoot4.findElement(By.id("searchKeywordInput"));
JavascriptExecutor javascriptExecutor = (JavascriptExecutor) driver;
javascriptExecutor.executeScript("arguments[0].value='Your Text Goes Here';", element);
I've tested this so many time and this is working fine in each case.
Note: using JavascriptExecutor may not trigger the search result auto suggestion.

Explicit wait for two elements using OR operator in Selenium

I am trying to find either one of the 2 elements in selenium (java). If anyone is found then that should be clicked. following is not working;                                                            
WebDriverWait wait5 = new WebDriverWait(driver, 5);
wait5.until(ExpectedConditions.elementToBeClickable(By.xpath("//a[#data-period='R6M'] || //span[#title='FYTD']"))).click();
The xpath is invalid, or is a single |
wait5.until(ExpectedConditions.elementToBeClickable(By.xpath("//a[#data-period='R6M'] | //span[#title='FYTD']"))).click();
You can also use ExpectedConditions.or fo this
wait5.until(ExpectedConditions.or(
ExpectedConditions.elementToBeClickable(By.xpath("//a[#data-period='R6M']")),
ExpectedConditions.elementToBeClickable(By.xpath("//span[#title='FYTD']"))));
To get WebElement from one of two conditions you can build your own implementation
public ExpectedCondition<WebElement> customCondition(By... locators) {
#Override
public WebElement apply(WebDriver d) {
for (By locator in locators) {
return ExpectedConditions.elementToBeClickable(locator).apply(d);
}
}
}
WebElement element = wait4.until(customCondition(By.xpath("//a[#data-period='R6M']"), By.xpath("//span[#title='FYTD']")));
To induce WebDriverWait for either one of the 2 elements using Selenium java client you can use the following Locator Strategy:
WebDriverWait wait = new WebDriverWait(driver, 10);
wait.until(ExpectedConditions.or(
ExpectedConditions.elementToBeClickable(By.xpath("//a[#data-period='R6M']")),
ExpectedConditions.elementToBeClickable(By.xpath("//span[#title='FYTD']"))
));
Reference
You can find a relevant discussion in:
How to wait for either of the two elements in the page using selenium xpath

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);

Using #FindBy to find WebElementS

We have a list of elements specified by "//input[contains(#name,'smcNetwork')]".
I am used to finding a webelement like
#FindBy(xpath = "//*[id='myId']") private WebElement myelement;
But, as per subject the xpath I mentioned returns an array (or list I guess) where I would do
List<WebElement> networks = driver.findElementsBy( xpath = "//input[contains(#name,'smcNetwork')]")
I would like to do this in a FindBy but it did not seem to work:
#FindBy( xpath = "//input[contains(#name,'smcNetwork')]") private List<WebElement> networks;
but that was returning null. Can you do this somehow?
You can do something like this.
#FindAll({#FindBy(xpath = "yourpath")})
public List<WebElement> networks;
To locate web element using #FindBy, you can use below snippet
#FindBy(xpath = "xpathValue")
private static WebElement xpathName;
here, you can change access level by making it as public or private/protected.
If you are making it as private then use this locator within that class and define a common method as public.

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