Converting xpath from String to WebElement - java

Right now I use the below method in my BasePage and I wish to call this method to my other pages.
So in the below method, the parameter is (String xpathExpression), How do I change this to WebElement and use other element locators which will be defined in other pages.
protected boolean CheckSorting(String xpathExpression) {
List<WebElement> issueTypeDropdown = new LinkedList<>(driver.findElements(By.xpath(xpathExpression)));
LinkedList<String> issueTypes = new LinkedList<String>();
for (int i = 0; i < issueTypeDropdown.size(); i++) {
//System.out.println(issueTypeDropdown.get(i).getText());
issueTypes.add(issueTypeDropdown.get(i).getText());
}
return Compare(issueTypes);
}

You can't get the locator from WebElement. If you want the locator strategy to be dynamic you can send By to the method
protected boolean CheckSorting(By by) {
List<WebElement> issueTypeDropdown = new LinkedList<>(driver.findElements(by));
//...
}
Uses:
CheckSorting(By.xpath(xpathExpression));

Related

Select a textbox at the location where i have a match for the header data and data in first column

This is how my page looks like on which I need to work.
The DOM looks like this
Scenario :- I need to traverse the table and where the header data(BH001 etc) and first column data(ABC etc) matches the data input by the user, I need to click on the textbox corresponding to it.
I have written the below specified code but its not working :-
public static void getMarksBox(WebDriver driver, String user, String taskCode) {
UserData userNm = TestData.findUserById(user);
String userName = userNm.getName();
WebElement table = WaitUtils.waitForElement(driver, By.cssSelector("table.eds-o-table.cvr-c-table--marksbook"));
List<WebElement> tableCols = table.findElements(By.cssSelector("td.eds-o-table__cell"));
int columnIndex = -1;
for(int i=1; i<tableCols.size();i++)
{
if(userName.equals(tableCols.get(i).findElement(By.cssSelector(".v-label-cvr-c-data-nav-link")).getText()))
{
columnIndex = i;
break;
}
}
List<WebElement> tableRows = table.findElements(By.cssSelector("tr.eds-o-table__row"));
List<WebElement> tableHeaders = tableRows.get(1).findElements(By.cssSelector(".v-label-cvr-u-margin-right--sm"));
WebElement textBox = table.findElement(By.cssSelector(".v-textfield"));
for(WebElement header :tableHeaders)
{
if(taskCode.equals(header.getText()))
{
textBox = tableRows.get(columnIndex);
textBox.click();
WaitUtils.sleepInSeconds(5);
break;
}
}
}
As #supputuri suggested, you can find the matched row or cell directly via XPath/Css selector to avoid complex loop to reduce execution time.
public static void getMarksBox(WebDriver driver, String user, String taskCode) {
UserData userNm = TestData.findUserById(user);
String userName = userNm.getName();
WebElement table = WaitUtils.waitForElement(driver,
By.cssSelector("table.eds-o-table.cvr-c-table--marksbook"));
WebElement matchedRow = table.findElement(By.xpath(
String.format("./tobdy/tr[td[1][normalize-space(.)='%s']]", userName)))
WebElement matchedTextBox = matchedRow.findElement(
By.cssSelector("./td:nth-child(2) input.v-textfield-eds-c-input"))
matchedTextBox.click()
// or you can directly find the matchedTextBox in one findElement
String xpath = String.format(
"./tobdy/tr[td[1][normalize-space(.)='%s']]" +
"/td[2]//input[contains(#class,'v-textfield-eds-c-input')]", userName)
WebElement matchedTextBox = table.findElement(By.xpath(xpath))
matchedTextBox.click()
}
Use this below xpath to access the inputbox directly, rather doing the loops that you have written in the above method.
//td[position()=count(//th[contains(.,'First Name')]/preceding-sibling::th)+1 and normalize-space(.)='ABC']/ancestor::tr//td[position()=count(//th[contains(.,'BH001')]/preceding-sibling::th)+1]//input[contains(#class,'v-textfield-eds-c-input')]
Here is the general notation:
//td[position()=count(//th[contains(.,'your reference column name')]/preceding-sibling::th)+1 and normalize-space(.)='reference value']/ancestor::tr//td[position()=count(//th[contains(.,'target column name')]/preceding-sibling::th)+1]//input[contains(#class,'v-textfield-eds-c-input')]

Select items from 3 dropdowns with dependency of first one

What I want to do is:
1- Going to this site: http://www.emlakyonetim.com.tr/tr-TR/sitelerimiz ,
2- Click on first dropdown - click a city, click second dropdown - click a county, click third dropdown.
At short, second dropdown items are dependant on first one and third dropdown items are dependant on second one.
Code (not complete):
#Test
public void SiteCek() throws InterruptedException
{
driver.get("http://www.emlakyonetim.com.tr/tr-TR/sitelerimiz");
Thread.sleep(2000);
driver.findElement(By.id("select2-city-list-container")).click();
List<WebElement> elm = driver.findElements(By.xpath("//*[#class='select2-results__option']"));
for(int i = 1; i < elm.size(); i++)
{
By ID = By.id(driver.findElements(By.xpath("//*[#class='select2-results__option']")).get(i).getText());
System.out.println(ID);
driver.findElements(By.xpath("//*[#class='select2-results__option']")).get(i).click();
Thread.sleep(500);
}
}
I get java.lang.IndexOutOfBoundsException: Index: 2, Size: 0 error after city "ADANA".
If I manage to handle first error, I will write second and third for loops, so code is this for now.
When the issue is solved I want to get all cities first. Then countys of each city. Then sites of each county. This must be done dynamically as the size of city list, county list and site list. To do that I need three nested for loops. After all of that, each value must be written in excel.
here is the code that worked for me
public void testMethod() {
driver.manage().window().maximize();
WebElement firstDropDown = driver.findElement(By.id("select2-city-list-container"));
firstDropDown.click();
sleep();
List<WebElement> citiesEls = getCitiesEls();
Map<String, Map<String, List<String>>> cityData = new HashMap<>();
for (int i = 0; i < citiesEls.size(); i++) {
//we need to take this element every iteration, because it gets reloaded every time we open the dropdown
WebElement cityEl = driver.findElement(By.id("select2-city-list-results")).findElements(By.xpath("//*[contains(#id,'select2-city-list-result')]")).get(i);
String cityText = cityEl.getText();
cityEl.click();
sleep();
cityData.put(cityText, getRegions());
firstDropDown.click();
sleep();
}
System.out.println(cityData);
}
private Map<String, List<String>> getRegions() {
WebElement secondDropDown = driver.findElement(By.id("select2-region-list-container"));
secondDropDown.click();
sleep();
List<WebElement> regionsEls = getRegionEls();
Map<String, List<String>> regionData = new HashMap<>();
for (int i = 0; i < regionsEls.size(); i++) {
WebElement regionEl = driver.findElement(By.id("select2-region-list-results")).findElements(By.xpath("//*[contains(#id,'select2-region-list-result')]")).get(i);
String regionText = regionEl.getText();
regionEl.click();
WebElement thirdDropDown = driver.findElement(By.id("select2-site-list-container"));
thirdDropDown.click();
List<WebElement> sitesEl = getSiteEls();
List<String> sitesTexts = getSites(sitesEl);
//populate region data
regionData.put(regionText, sitesTexts);
secondDropDown.click();
sleep();
}
return regionData;
}
private List<String> getSites(List<WebElement> sitesEl) {
List<String> sitesTexts = new ArrayList<>();
for (WebElement siteEl : sitesEl) {
sitesTexts.add(siteEl.getText());
}
return sitesTexts;
}
private List<WebElement> getSiteEls() {
WebElement ulSites = driver.findElement(By.id("select2-site-list-results"));
return ulSites.findElements(By.xpath("//*[contains(#id,'select2-site-list-result')]"));
}
private List<WebElement> getRegionEls() {
return driver.findElement(By.id("select2-region-list-results")).findElements(By.xpath("//*[contains(#id,'select2-region-list-result')]"));
}
private List<WebElement> getCitiesEls() {
return driver.findElement(By.id("select2-city-list-results")).findElements(By.xpath("//*[contains(#id,'select2-city-list-result')]"));
}
As data is dynamically changed after each click, you might need some delays after each click. The code below worked well on Mac+Chrome. Nevertheless, if it fails from you end do add a sleep method call after each click.
private void sleep() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new IllegalStateException(e);
}
}
Important note: Sleeps are not recommended to use, and should be used only as the quick temporary workaround. More robust solution is to use smart waits

Shadow Root - click in a href under several shadow roots

I have a list of links inside of several shadowRoots. Already solved this problem.
public WebElement expandRootElement(WebElement element) {
WebElement ele = (WebElement) ((JavascriptExecutor) driver).executeScript("return arguments[0].shadowRoot",element);
return ele;
}
WebElement root5_adminPanel = shadowRoot4_MduiContainerChild2.findElement(By.cssSelector("#layout > border-layout > ng-view > admin-panel"));
WebElement shadowRoot5_AdminPanel= expandRootElement(root5_adminPanel);
WebElement root6_breadCrumb = shadowRoot5_AdminPanel.findElement(By.cssSelector("#layout > border-layout > breadcrumb"));
WebElement shadowRoot6_breadCrumb = expandRootElement(root6_breadCrumb);
WebElement root6_domainPanel = shadowRoot5_AdminPanel.findElement(By.cssSelector("#layout > border-layout > ng-view > gdsr-domain-panel"));
WebElement shadowRoot6_domainPanel = expandRootElement(root6_domainPanel);
WebElement root7_selectDomain = shadowRoot6_domainPanel.findElement(By.cssSelector("#domainContainer > domain-panel-item.ng-binding.last"));
WebElement shadowRoot7_selectDomain = expandRootElement(root7_selectDomain);
When I reach this shadowRoot7, I have a list of items with the same name, which I already created a List to fix it.
List<WebElement> rows_table = shadowRoot6_domainPanel.findElements(By.cssSelector("#domainContainer > domain-panel-item:nth-child(n)"));
(They are around 45 items)
This will select all of them, in this case all the domain-panel-item rows.
My problem is that each domain-panel-item still contain another shadowRoot (the same path for all of them) an i would like to select a random item, not the first or last one, for example, the item number 43.
enter image description here
My solution was this one but it doesn't work because it doesnt access to the link that i want:
public void clickSelectedDomain(String domain) {
List<WebElement> rows_table = shadowRoot6_domainPanel.findElements(By.cssSelector("#domainContainer > gdsr-domain-panel-item:nth-child(n)"));
int rows_count = rows_table.size();
for (int row=0; row<rows_count; row++) {
if(rows_table.get(row).getAttribute("href").contains(domain)) {
rows_table.get(row).click();
}
}
}
Some have an idea how to fix this?
You solved the problem by calling recursively executeScript() in order to get the imbricated Shadow DOMs but actually you could have just called executeScript() once, and inside got the Shadow DOMs successively.
driver.executeScript( function ()
{
var root1 = document.querySelector( 'selector string 1' ).shadowRoot
var root2 = root1.querySelector( 'selector string 2' ).shadowRoot
var root3 = root2.querySelector( 'selector string 3' ).shadowRoot
...
return foundElement
}
Anyways, in the for() {} loop, you should extract the ultimate Shadow DOM one last time, and then select the <a> element to check its content.

Trying to search and return the webelement

I am trying to search the webelement and trying to return back like in below code. Will it work. I have multiple times after returning it traversing back to first element always. Please help me in this.
public obj1 obj2(String Name)
{
WebElement ayName = isApplicationList(Name);
if (ayName.getText().equals(Name))
{
WebElement edit = driver.findElement(By.name("action_edit_application"));
click(edit);
obj1 obj1 = obj3.method(browser, obj1.class);
return obj1.edit(Name);
}
return null;
}
//method2
public WebElement obj2(String displayName)
{
WebElement element1 = null;
WebElement Element = deriver.findElement(By.name("abcjl"));
List<WebElement> ElementList = Element.findElements(By.tagName("your tagName"));
for (WebElement sinElement : ElementList)
{
WebElement ayName = ApplicationElement.findElement(By.name("my_name"));
if (erDisplayName.getText().equals(displayName))
{
element1 = erDisplayName;
return element1;
}
}
return element;
}
why don't you use XPath or CSS to find the element you are interested an click on it rather than iterate all the child elements.
This will find the element with name my_name and has visible text as displayName and whose parent element has name abcjl
//*[#name='abcjl']//tagName[#name='my_name' and text()='displayName']

Why is this WebElement not returning the full xpath?

I have several WebElements such that executing the following
List<WebElement> customers = driver.findElements(By.xpath("//div[#id='Customers']/table/tbody/tr"));
System.out.println(customers.size());
would print 5.
So then why does the following code
List<WebElement> customers = driver.findElements(By.xpath("//div[#id='Customers']/table/tbody/tr"));
for (WebElement customer : customers) {
if (customer.getText().equals("SQA")) {
WebElement test = customer;
System.out.println(test);
break;
}
}
print xpath: //div[#id='Customers']/table/tbody/tr and fail to actually include the specific index of the path? The above xpath is absolutely useless; I'm expecting the location of where SQA was found.
xpath: //div[#id='Customers']/table/tbody/tr[4]
I think it just prints the locator used to find the element. If you want the index, just change your code to
List<WebElement> customers = driver.findElements(By.xpath("//div[#id='Customers']/table/tbody/tr"));
for (int i = 0; i < customers.size(); i++)
{
if (customers.get(i).getText().equals("SQA"))
{
System.out.println(i);
break;
}
}

Categories

Resources