Get last added element of specific class to DOM via xpath - java

My site generates an arbitrary number of popups (please don't judge my monitization strategy). Each popup is a complex div that I eventually want reference to via xpath, but I specifically want the last one that was added to the DOM, since that will be the one that's on top.
So I tried this
//*[#class="popupContent"][last()]
I know I could try this
(//*[#class="popupContent"])[2]
but I have no idea how many of these popups there are.
But in the case of having multiple popups on screen at once, I get a reference to the first one (I'm not sure if this is reliably the case or not). Each of these popups has a popupContainer, but for the sake of this question, it is off limits.
Given that there are multiple elements with this class, how can I get the one that is on top (and thus interactive)?
These things aren't siblings, children, or parents of each other. Also, I have no idea how many exist on the page at any given time. I also do not have any control over the content or structure of the popups beyond that I know the class for one of their internal components (popupContent).
If this is not possible, please explain why.
I'm looking for a solution that will be compatible in a Java Selenium testing environment. No jquery please.

According to this question: Do WebDriver findElements retain the Table rows order on its retrieval findElements guarantees order, so in a Selenium environment, I could get the list from that and retrieve the last item.
List<WebElement> popups = findElements(By.classname("popup"));
WebElement activePopup = popups.get(popups.size - 1);

Xpath (//*[#class="popupContent"])[last()] and code below should give you same result - last element in the DOM:
List<WebElement> popups = driver.findElements(By.className("popup"));
WebElement activePopup = popups.get(popups.size - 1);
If last element not the top one try to get focused element:
WebElement focused = driver.switchTo().activeElement();
if (focused.getAttribute("class").equals("popupContent"))
//my active/interactive popup
You can check if last one is active now:
WebElement lastOne = driver.findElement(By.xpath("(//*[#class="popupContent"])[last()]"));
if (lastOne.equals(driver.switchTo().activeElement()))
//last one is active

Another approach could be to look for the popup with the highest z-index value:
private WebElement getTopPopUp() {
List<WebElement> allPopUps = driver.findElements(By.classname("popup"));
WebElement topPopUp = allPopUs.get(0);
for(WebElement popUp : allPopUs) {
if(Integer.parseInt(popUp.getCssValue("z-index")) > Integer.parseInt(topPopUp.getCssValue("z-index"))) {
topPopUp = popUp;
}
}
return topPopUp;
}

Related

Why do some elements exist but not interactable/displayed?

I'm pretty new to testing, trying to gain a better understanding of what exactly is going on. I'm finding some of our test codes are failing when the css selector element has a waitUntilCanInteract or waitUntilDisplayed attached to it even though when I do a chrome inspect the element is showing up in the browser. Changing them to a waitUntilExists gets them to a passing point so I was wondering what exactly is going on to create this situation?
Precisesly Selenium deals with three unique states of an element.
Presence of element within the html: This state of an element can be detected through the ExpectedCondition presenceOfElementLocated() where the expectation is to check if the element is present in the DOM of a page. This does not necessarily mean that the element is visible.
Exmaple:
WebElement element = new WebDriverWait(driver, 20).until(ExpectedConditions.presenceOfElementLocated(By.cssSelector("css_of_element")));
Visibility of element within the html: This state of an element can be detected through the ExpectedCondition visibilityOfElementLocated() where the expectation is to check if the element is present in the DOM of a page and visible. Visibility means that the element is not only displayed but also has a height and width that is greater than 0.
Exmaple:
WebElement element = new WebDriverWait(driver, 20).until(ExpectedConditions.visibilityOfElementLocated(By.cssSelector("css_of_element")));
Element to be clickable: This state of an element can be detected through the ExpectedCondition elementToBeClickable() where the expectation is to check if the element visible and enabled so that you can click it.
Exmaple:
WebElement element = new WebDriverWait(driver, 20).until(ExpectedConditions.elementToBeClickable(By.cssSelector("css_of_element")));
You can find a detailed discussion in Selenium: Check for the presence of element
Well, the developers decided to make it so.
See, the elements can exist on the DOM but be invisible or un-interactable. But not the other way around.
If your tests are passing when waitingUntilExists and failing otherwise, you probably have to prolong the waiting period if you want to make them pass. But this is just my guesswork without seeing any of your code.
Simple answer is that sometimes when designer work on web pages especially while working on Foundation CSS framework or bootstrap, they intentionally hide the original CSS/HTML tags and elements while placing foundation or bootstrap based design overlays like fancy buttons on the page which causes the original elements to be hidden.
The best approach may be like:
I. You can declare a WebElement while calling an element to precisely targeting it and using moveToElement command instead of simply calling FindElement.By.xxxxx
ex:
//*** Calling a WebElement and using moveToElement command***//
WebElement (anyElementname) = browser.findElement(By.partialLinkText("xxxxxxxxxxx"));
action.moveToElement(anyElementname).perform();
//*** Waiting for 8 seconds***//
Thread.sleep(8000, 80000);
II. You can use 'waits' for providing page load time and interacting between elements
III. Avoid copying the overlayed css elemenet especially xpath, instead, copy the xpath from the original source of the Div/button/li
Let me know if it works for you or not. Cheers!

Can Selenium Find the correct Web Element on a responsive, resized website?

My web application is responsive. I.e. it resizes for different screen sizes. Our developers have coded for this, and as a result, most web elements found by xpath actually have 2. One of them for full screen and one for "mobile" or small screen.
For example, if I use Firepath to search for the Xpath by just link text, it will say "2 elements found" Only one can be seen, then if I resize the browser window, that one disappears and the other one can be seen.
My question is: Can I use one Selenium script for both screen size? It would automatically use whichever element is visible or is clickable? If I script for just one of the two elements, I'll get "Element is not clickable" exception.
Or s the best solution to add all of the mobile elements to the page object separately, and create my methods something like:
if (DesktopWebElement.isVisible)
do the test for desktop
else if (MobileWebElement.isVisible)
do the test for mobile
It seems like quite a bit of extra work to add every mobile element separately and script every method in the page object like this. In the interest of saving time, I was just wondering if there's a way I could use an Xpath that finds both elements, and Selenium will find the first one that is visible.
I have the situations where the columns that get displayed vary by screen size in addition to different buttons.
My page files contain methods like
public int getScreenWidth() {
Dimension aScreen = webDriver.manage().window().getSize();
return aScreen.getWidth();
}
Then my code has things like:
if (screenWidth < 1440 ) {
...
}
Developers worked out with the business which columns dropped and at which widths.
There are screen width cutoff values set by the developers that determine which button gets displayed. So I click on the desktop or mobile button depending on the current screen width. Ask development to document the screen width cutoff values that they coded.
Have you tried ExpectedConditions.visibilityOfElementLocatedBy, as per documents it would give you a visible element attached to the DOM. This would give you ability to just interact with the element that is visible our of the two
WebDriver driver = new FirefoxDriver();
WebDriverWait wait = new WebDriverWait(driver, 30);
WebElement element = wait.until(ExpectedConditions.visibilityOfElementLocatedBy(By.xpath(".//someXpath")));
element.click();
You can also try elementTobeClickable condition if that suits you better. Note that this is NOT going to slow your tests down, as soon as the element is visible/clickable it will be returned.

selenium very similar xpaths?

I have two buttons on a page that have really similar xpaths -
the button im trying to click -
/html/body/div[#id='wrapper']/div[#id='content']/div[#id='contentarea']/div[#id='votecontent']/div[#id='votetext']/div[#id='voteboxes']/div[#id='votenow'][2]/form/input[2]
and the other button im trying to ignore -
/html/body/div[#id='wrapper']/div[#id='content']/div[#id='contentarea']/div[#id='votecontent']/div[#id='votetext']/div[#id='voteboxes']/div[#id='votenow'][1]/form/input[2]
the only difference between the two is the
[#id='votenow'][1]
and
[#id='votenow'][2]
but I can't figure out how to interact with the one that has the votenow[2], whichever way I go about it, it always seems to interact with the first one because that's the first one it finds
this is for java using the firefox driver, any suggestions would be great :)
Just find them both and get the desired one by index:
List<WebElement> buttons = driver.findElements(By.xpath("your xpath"));
WebElement secondButton = buttons.get(1);
First
Please talk to your developers! It is really bad practice to assign the same id to two different elements (in your case buttons) on the same page! It makes life for DEV and QA unnecessarily harder than it need be!
Second
The xpath-expressions you posted already contain the differentiation between these two buttons. So you just need to find the first one and click it.
via xpath:
You can use xpath - should be enough to search for the elements with id="votenow". As said before, you can be pretty precise in this case and already filter for the 2nd button:
WebElement button02 = driver.findElement(By.xpath("//div[#id='votenow'][2]/form/input[2]"));
button02.click();
via id:
As #alecxe already pointed out, you can also first go for a more general search of several elements and then filter the right one out. Personally I would use the id in this case:
List<WebElement> buttonWrappers = driver.findElements(By.id("votenow"));
// you want the button-input-element in the 2nd wrapper element, indexing starts at 0, so do this:
WebElement button02 = buttonWrappers.get(1).findElement(By.xpath("//input[2]"));
// since it seems there are several input elements below the desired div, you can use xpath again

How do I loop through bar graph elements in webdriver?

Selecting relational highchart SVG images with Selenium
I asked the above question recently, whereby I wanted to click on some elements within a highchart in order to complete some functions in my automated tests. I've now got around this but have a further question. In some scenarios I will have 3 bars on the highchart; in others I may have more; or less. The code I have written, it is apparent, is restrictive in that it cannot dynamically handle if there is a change to the number of high charts on a page (I've written the code below):
public static void barChartSelector(InternetExplorerDriver driver)
{
driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
WebElement parent = driver.findElement(By.className("highcharts-series-group"));
List<WebElement> children = parent.findElements(By.tagName("rect"));
children.get(0).click();
children.get(1).click();
children.get(2).click();
children.get(3).click();
}
I know, conceptually, that some kind of looping needs to take place so that if I use the barChartSelector method on a highchart that has 4 elements to click on, the method can loop through and handle this. Similarly, if there is another highchart with only 3 elements on, the code should handle this too. This would just make the code more dynamic and future-proof.
Does anyone have any advice on the best way to implement the above?
Maybe instead of
List<WebElement> children = parent.findElements(By.tagName("rect"));
children.get(0).click();
children.get(1).click();
children.get(2).click();
children.get(3).click();
you could try
List<WebElement> children = parent.findElements(By.tagName("rect"));
for (WebElement cur : children) {
cur.click();
}

Xpath is found even though web object not visible

I am automating a code using selenium 2.0. I select one (or several) user(s) from a list. Then I click on an add button which makes the user name(s) visible on a grid. Each user will have a valid Xpath when visible on the grid. However, even after erasing all user names from the grid which actually disappears if there is no user names displayed, the Xpath still does not return null. I am using Xpath to check if it returns null when the object (user name) is not visible, but it does not work as expected. Is there any other way to solve my problem? I am pretty new with Selenium. I am using selenium 2.0. Bellow is a section of my code. Your help will be very appreciated.
//Check if user is present on the grid
By checkuser = By.xpath( ".//*[#id='sharing_list']/tbody/tr/td[1]/span");
//if the grid is not empty, which means the grid is visible...
if(null!=checkuser) //where the problem is!!
{
//Click the button to erase the names in the grid, then the grid desapears
webDriver.findElement(By.xpath("//*[#id='sharing_list']/tbody/tr/td[4]/span")).click();
Thread.sleep(2000);
//more code
//............
}
I can see two things happening here:
First is that your XPath is generic enough that it is selecting some other element that isn't a user. To see if this is the case, then in Chrome, go to the page and do the necessary actions to get it in the state you want. Next, press Ctrl-Shift-J, click on Console, and type in $x("//*[#id='sharing_list']/tbody/tr/td[4]/span"). Chrome will then show you which element your selector is selecting.
Your task then, is to identify if its selecting some other element, or whether the element is just not visible. It is definitely possible to have an element on a page, but not visible, and WebDriver WILL select invisible elements (unless you are doing By.linkText()). If you want to check to see if an element is visible do a element.isDisplayed().

Categories

Resources