selenium get li element based on index position and click the checkbox - java

I have this HTML now using Selenium I ant to toggle the li element with given index position say 1, where it indicates I want to click the toggle checkbox for spring.
<ul id="todo-list" data-woven="abc">
<li class="active" data-index="0">
<div class="view">
<input class="toggle" type="checkbox">
<label>Java</label>
<button class="destroy"></button>
</div>
<input class="edit">
</li>
<li class="active" data-index="1">
<div class="view">
<input class="toggle" type="checkbox">
<label>Spring</label>
<button class="destroy"></button>
</div>
<input class="edit">
</li></ul>
I am completely new to selenium so not able to understand how can we achieve this.
I know to get the UL elements using the code:
driver.findElement(By.id("todo-list"));
Now how can get the li element based on its index and click the corresponding checkbox.

To click on the checkbox element with respect to the ancestor <li> nodes index attribute you can use either of the following Locator Strategies:
cssSelector:
driver.findElement(By.cssSelector("ul#todo-list li.active[data-index='1'] input")).click();
xpath:
driver.findElement(By.xpath("//ul[#id='todo-list']//li[#class='active' and #data-index='1']//input")).click();

You can use xpath to locate an element with data-index=1
driver.findElement(By.xpath("//li[#data-index='1']//input[#class='toggle']"));
Or with cssSelector
driver.findElement(By.cssSelector("[data-index='1'] .toggle"));

You could find the element you are looking for directly with the answer #Guy gave you and that would be the right way if you knew exactly what the data-index attribute would be set to, but you could also find a collection of the li elements and then proceed to do what you need within each like this:
var container = driver.findElement(By.id("todo-list"));
var elements = container.findElements(By.tagName("li"));
with elements you can loop through each or go directly to the one you want.

Related

Java and Selenium: Problems accessing checkbox elements in a modal

Page with a pop-up page/modal window, showing a list of (among others) checkboxes.
I'm having problems with Selenium not finding these elements.
Markup:
<td class="journalTabell-arkivstatus">
<div class="hb-grid hb-grid--gapSM">
<div data-e2e-selector="saksjournal-checkbox-style" style="visibility: visible;">
<input type="checkbox" data-e2e-selector="saksjournal-checkbox" id="erArkivert-0">
<div class="hb-label">
<label data-e2e-selector="saksjournal-checkbox-lbl" for="erArkivert-0"</label>
</div>
</div>
<div id="journalPostArkiverStatus-0">
<div class="haster ng-star-inserted">
<hb-ikon class="haster-ikon hb-ikon id="hb-alert-circle-98ec342a-966f-4f5f-9d84-24a2ac4c271e" aria-hidden="true">
<svg focusable="false">
<use xlink:href="assets/sprite.symbol.svg#ikon-hb-alert-circle"></use>
</svg>
</hb-ikon>
<div class="ng-star-inserted">Ikke arkivert</div>
<div class="ng-star-inserted">Last ned journalpostog kryss av når du har arkivert manuelt
</div>
</div>
</div>
</div>
</td>
There are several of these checkboxes, with id's erArkivert-0, -1, -2 and so on.
See the attached screenshot.
The Java/Selenium code looking for the elements:
public List<WebElement> getArkiveringsKnapper() {
Wait().until(ExpectedConditions.visibilityOfElementLocated(By.cssSelector("[data-e2e-selector='saksjournal-poster']")));
return driver.findElements(By.cssSelector("[data-e2e-selector='saksjournal-checkbox']"));
}
When using the above method (cssSelector for the checkbox element) to find the checkboxes, all three are found. However, Selenium throws the following error
org.openqa.selenium.ElementClickInterceptedException: element click intercepted
Earlier, I've solved this by clicking the label for the checbox instead of the checkbox itself.
But if I use this to find the checkboxes
return driver.findElements(By.cssSelector("[data-e2e-selector='saksjournal-checkbox-lbl']"));
Selenium throws this error:
org.openqa.selenium.ElementNotInteractableException: element not interactable
And if I try using xPath with the first part of the selector (since there are several of them, and they end with -0, -1, -2 etc.):
return driver.findElements(By.xpath("//*[#id=\"^erArkivert-\"]"));
Selenium doesn't find the elements at all.
Any ideas? I'm seeing more and more of these problems in test suites where the code has been working before, but after some change, Angular upgrade or other markup change, it stops working.
I have to answer my own question here, as I was able to find the cause with the help of someone much better with frontend than me.
The problem wasn't with the modal at all, which makes sense since it was able to find the element but just couldn't interact with it.
Also, it's worth remembering that - at least in my humble experience - it's allmost never possible for Selenium (or Cypress, for that matter) to directly click a checkbox. I allmost allways have to click the label element, and then verify the click on the checkbox element.
However, in this case, the label element had a size of zero. So it couldn't be clicked (hence, the "element not interactable" error).
So I solved it by click the element in which the element is placed, by going first to the outside of that, and then clicking the only inside that element.
List<WebElement> elements = driver.findElements(By.cssSelector("[data-e2e-selector=\"saksjournal-checkbox-style\"]"));
for (WebElement element : elements) {
Wait(4).until(ExpectedConditions.visibilityOf(element.findElement(By.tagName("div")))).click();
assertThat(element.findElement(By.cssSelector("[data-e2e-selector='saksjournal-checkbox']")).isSelected()).isTrue();
}

#FindBy Selenium how to find ::before

::before
I have following structure on site and ::before is checkbox where I'm supposed to click, found some topics, but there was no #FindBy annotation. Is it possible to find this ::before in code?
<span class="jJ">
<label class="kJ" data-test-id="checkbox_bonus_card">
<input class="mJ" type="checkbox" name="hasBonusCard" value="">
<span data-test-id="checkbox_bonus_card_text" class="lJ">
::before
</span>
</label>
</span>
I tried #FindBy(xpath = "//label[#data-test-id='checkbox_bonus_card'//span['::before']") but it didn't work.
If I use something like this:
#FindBy(xpath = "//input[#name='hasBonusCard']")
WebElement checkboxBonusCard;
Error appears:
Caused by: ElementClickInterceptedException: element click intercepted: Element <input class="mJ" type="checkbox" name="hasBonusCard" value=""> is not clickable at point (568, 78). Other element would receive the click: <span class="jJ">...</span>
I think it's in input field that you wanna interact with :
#FindBy(name = "hasBonusCard")
WebElement bonusCard;
:: basically means this :
In CSS, ::before creates a pseudo-element that is the first child of
the selected element. It is often used to add cosmetic content to an
element with the content property.
More :
Since ::after & ::before are a pseudo element which allows you to
insert content onto a page from CSS (without it needing to be in the
HTML). While the end result is not actually in the DOM, it appears on
the page as if it is - you see it but can't really locate it with
xpath for example
Read more here

How fetch first element from json list in selenium java?

I have following list of ids in json format. I want to access first id in selenium using java. I tried using
String item = driver.findElement(By.xpath("//ul//li[1]")).getText();
but didn't help.
<body>
<div id="json">
<span class="collapser"></span>
{
<ul class="obj collapsible">
<li>
<span class="prop" title="<root>.hdps">
<span class="q">"</span>
hdps
<span class="q">"</span>
</span>
:
<span class="collapser"></span>
[
<ul class="array collapsible">
<li>
<span class="num">65085</span>
,
</li>
<li>
<span class="num">65089</span>
,
</li>
<li>
<span class="num">65711</span>
,
</li>
</ul>
]
</li>
</ul>
}
</div>
What i understand is you are trying to read the value of ID attribute of an element. I am really not sure the intent of your question . But this how you can get to the value of ID.
You will need to get reference to the element using one of the various element locators. In this case, you have leveraged By.xpath(). You can validate the correctness of the XPATH used by Firefox Xpath checker. Once you use correct XPATH , you will get reference of webElement.
WebElement wElement = driver.findElement(By.xpath("//ul//li[1]"));
// validate the correct XPATH using available tools - ex : firefox xpath checker etc.
You will need to get the value of id attribute of the element.
String requiredID = wElement.getAttribute("id");
Let me know if this works.
As pendem answered that, you can get first ID as you want by using xpath. But here I found that the xpath which you have used find 2 elements as there are two ul elements having li. If you use xpath having specific ul with class attribute as - .//ul[# class="array collapsible"]//li[1] will work.
If you have provided all the relevant HTML, it should be as simple as the below.
String id = driver.findElement(By.cssSelector("span.num")).getText();
This just returns the first instance of the IDs.
If that doesn't work, you'll have to do some more digging, e.g. is this in an IFRAME or is it a timing issue or ?

Issue selecting an xpath when it is unknown if it will be on the page

I have this piece of selenium code I am trying to write, where it will select a page number indicator at the bottom of the page and count the elements on it. However, this number indictor does not appear if there is only one page, and it will be updated in the future and eventually contain more than one page.
How can I make a piece of selenium code will select the last page if it finds that the page number indicator is there? I have it search the webpage for a piece of text that only appears if the indictor appears, but I am stuck on what to do after that to make it select the last page in the page number indicator.
<div class="view view-blog view-id-blog view-display-id-page_1 view-dom-id-3e6e5f93bb52f8bc5f35e55c6da86526">
<div class="view-filters">
<form id="views-exposed-form-blog-page-1" class="views-exposed-form-blog-page-1" accept-charset="UTF-8" method="get" action="/blogs">
</div>
<div class="view-content">
<div class="item-list">
</div>
<h2 class="element-invisible">Pages</h2>
<ul class="pager">
<li class="pager__item pager__item--current">1</li>
<li class="pager__item">
<li class="pager__item">
<li class="pager__item">
<li class="pager__item">
<li class="pager__item">
<li class="pager__item pager__item--next">
<li class="pager__item pager__item--last">
<li class="pager__item pager__item--last"> is the text that coressponds to the button I want to select.
In this there is 5 pages, so it would go to page 5, but I am trying to write it so that it just goes to the last page in the current list, so that I dont have to hardcode a number to jump to.
My current code is just
if (driver.getPageSource().contains("pager")) {
WebElement k = driver.getPageSource().findElement("pager__item pager__item--last");
The pager__item pager__item--last is the string inside of the code that corresponds to the button I want to select <li class="pager__item pager__item--last">
What would be a good way to do this search for this string and then click on the button it corresponds to if you are unsure it even exists inside of your code?
Thanks
Taking Google search result as an example
Write a conditional statement for Page Number indicator
When the condition is satisfied, using FindElements method get all the <a> tags representing Page Numbers
Get the Last element from the list which will be the last page
Do a Click of element
The Best way is using try catch..
try{
driver.findElementByXpath("Your xpath of last page button").click();//you can use any other locator
}catch(NoSuchElementException e){
//if you get exception then it means that element is not present(it means there is only one page)
//if you have multiple page then that button will be present and it'll not through any exception and it will trigger click event in that element and you'll get navigated to last page
}

differentiate two html elements with same class

I have this html code below and I want to differentiate between these two PagePostsSectionPagelet as I only want to find web elements from the first PagePostsSectionPagelet. Is there any way I can do it without using <div id="PagePostsSectionPagelet-183102686112-0" as the value will not always be the same?
<div id="PagePostsSectionPagelet-183102686112-0" data-referrer="PagePostsSectionPagelet-183102686112-0">
<div class="_1k4h _5ay5">
<div class="_5sem">
</div>
</div>
<div id="PagePostsSectionPagelet-183102686112-1" class="" data-referrer="PagePostsSectionPagelet-183102686112-1" style="">
<div class="_1k4h _5ay5">
<div class="_5dro _5drq">
<div class="clearfix">
<span class="_5em9 lfloat _ohe _50f4 _50f7">Earlier in 2015</span>
<div id="u_jsonp_3_4e" class="_6a uiPopover rfloat _ohf">
</div>
</div>
<div id="u_jsonp_3_4j" class="_5sem">
<div id="u_jsonp_3_4g" class="_5t6j">
<div class="_1k4h _5ay5">
<div class="_5sem">
</div>
</div>
Tried using //div[#class='_1k4h _5ay5']//div[#class ='_5sem'] but it will return both.
Using //div[#class='_5dro _5drq']//span[contains(#class,'_5em9 lfloat _ohe _50f4 _50f7') and contains(text(), '')] will help me find the second PagePostsSectionPagelet instead.
you need to use the following xpath:
//div[contains(#class,'_1k4h') and contains(#class,'_5ay5')]
as selenium doesn't work properly with search of several classes in one attribute.
I mean By.Class("_1k4h _5ay5") will found nothing in any case and By.Xpath("//div[#class='_1k4h _5ay5']") can also found nothing in case of class will be "_5ay5 _1k4h" or " _5ay5 _1k4h".(as they possibly generated automatically, its may be have different position on page reload)
But for the best result by performance and by correctness I think will be the following xpath:
".//div[contains(#id, 'PagePostsSectionPagelet')][1]" -- for first div
".//div[contains(#id, 'PagePostsSectionPagelet')][2]" -- for second div
I see that dynamic in the div id is only the number so you can use something like:
WebElement element = driver.FindElements(By.XPath("//div[contains(.,'PagePostsSectionPagelet')])")[1];
This will take only the first web element.
Try using a css selector as below and refine further if required.
The code below returns a List of matching WebElements and then you grab the first one in the List.
List<WebElement> listOfElements = driver.findElements(By.cssSelector("div[data-referrer]"));
WebElement myElement = listOfElements.get(0);
Hint: use the Chrome console to test your css and xpath selectors directly. e.g. use
$$("div[data-referrer]") in the console to reveal what will get selected.

Categories