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().
Related
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.
I am working on a development script using Selenium and Appium and I'm running into the issue of the wrong element being picked up by the Selenium Locator.
Essentially, I want to click a button that has no ID assigned to it; so the only thing I have left to identify it by is its text.
public Element button1(){
By locator = By.xpath("//android.widget.TextView[#text='button1']");
return new xElement(driver.findElement(locator), locator);
}
This is my my locator method to get the button1 object. By the way, no other button on the screen has text anywhere close to button1's text. The method click called on the button has the format:
public void clickBtn1(){
button1().click();
}
The button being clicked essentially has the text "wheelbarrow". This is just to clarify that the button being pressed has text no where close to button1's value.
I have used UI automator multiple times to confirm button1's actual text value. The weird thing is the script works occasionally, so I'm not sure what the issue is.
I have also tried a "wait for enabled" method to account for race conditions.
Try using the Appium inspector to search for your button. You can type in the xpath and search for the element to see what it finds. The other nice thing about the inspector is you can see how the native control attributes map to the Appium attributes. 'text' may not be the attribute you actually want. Also, have you tried searching on properties on the Button itself (instead of TextView)?
If it's working occasional, First try to use different element other than xpath. Second, try to give some sleep command before you perform that action like
WebDriverWait wait = new WebDriverWait(driver, 10);
wait.until(ExpectedConditions.visibilityOfElementLocated(ByLocator(locator)));
I'm trying to click on an element on a page; the element is clearly visible on screen. There is a toaster that might pop up, so I'm trying to write a defense: if the toaster is on screen, close the toaster first, then continue clicking through to the next page. I am using PageFactory, so I have an element to contain the toaster and one for the close button for the toaster. My "deal with toaster" method is as follows:
if (driver.findElements(By.cssSelector("#toaster")).size() > 0
&& toaster.isDisplayed()) {
toasterClose.click();
}
When I do this in chrome, however, I'm getting org.openqa.selenium.WebDriverException: unknown error: Element is not clickable at point (994, 758)
Pausing the test execution, I cannot see the toaster on the screen. I figure the devs must be hiding it by making it render in a far away, unscrollable location. So as a stopgap measure, I added a condition that if the x coordinate was greater than 800, don't click. With that in place, I get:
org.openqa.selenium.WebDriverException: unknown error: Element is not clickable at point (547, 725). Other element would receive the click: <div id="toaster">...</div>
What's going on? How can the toaster not be clickable but would somehow receive the click anyway? Firefox can handle the test just fine, with or without the 800 pixel workaround; it's only Chrome having this issue.
For Clarification: The goal of the test is NOT to click the toaster. The goal is to click another element on the page. The test reported that a toaster was in the way, so I attempted to write a step to close the toaster if it is displayed. I have not seen this toaster, so I'm not exactly sure what it is, but chrome keeps reporting that it's in the way. All our toasters site-wide use a basic template that includes a close button so the user can close the toaster, which is what I'm trying to click. Firefox never has this issue and does not report the existence of any toasters.
I'm calling it a toaster because that's what our site calls it, because that's what it's called in whatever framework we got it from (jQuery UI? Backbone?). If I pause execution, I cannot see any toasters at this point in the test, but jQuery tells me it exists and is visible. However, the element found with jQuery has just the default pieces of our toaster setup: a div, an empty div where the message should be, and the close button. Clearly it's not meant to be rendered at this time, but Chrome thinks it's in the way.
I'm assuming by "toaster" you mean some sort of javascript modal popup with a close button.
Identifying the correct problem
You're testing the existence and visibility of the #toaster element, but not the toasterClose element that you're clicking. There's no guarantee that just because one element exists and is displayed, another is as well. From the error, it appears that the #toaster element overlaps the toasterClose element, making it unclickable.
Troubleshooting clickability
Once you've properly selected toasterClose, manually use devtools and inspect to see why it's unclickable. Is it visible and unobstructed? Is the toasterClose element something of zero height/width? Is there dynamic JavaScript modifying the page post-load? Is it actually positioned in view of the page? (I've had elements render visibly at the edge of the window only to be obstructed by the browser's scroll bars.1)
Alternative
You should also see if you really need to use this toasterClose element. How does would a human close this popup? Would they press Escape? Would they click outside the popup window, on the overlay element? Do they do something else that triggers some sort of closeModal() javascript function? You can also do any of these things using Selenium.
Last Resort
One thing you can always do to remove such a popup is to run your own javascript to modify the DOM and remove the offending element(s) altogether:
driver.execute_script(<<-javascript)
var toaster = document.getElementById("toaster");
toaster.parentNode.removeChild(toaster);
var overlay = document.getElementById("modal_overlay");
overlay.parentNode.removeChild(overlay);
javascript
Future/Additional
If this is a regular issue for you, I would suggest wrapping this code in try/catches and a retry mechanism to make it resilient to javascript dynamically loaded elements.
1 Update
Just to elaborate on the scrollbar issue I had, because it turned out that it was a very similar problem to yours.
Here, the "I'm Feeling Lucky" button is out of view. If Selenium tries to click on it, first it will attempt to scroll it into view.
Here's an example of what Selenium would attempt to do. Notice how the button is now "in view".
However, Chrome on OSX is styled in such a way that the scrollbars are normally hidden. The moment that Selenium issues the scroll command, the scrollbars appear and the following click command fails to reach the button.
The solution was to use javascript to scroll the window manually:
page.execute_script(<<-javascript)
document.getElementById("gbqfsb").scrollIntoView(true);
// or if that doesn't work:
window.scrollTo(0, document.getElementById("gbqfsb").getBoundingClientRect().top);
javascript
Try the following code. Should work:
if (driver.findElements(By.cssSelector("#toaster")).size() > 0
&& toaster.isDisplayed()) {
Actions builder = new Actions(driver);
builder.moveToElement(toasterClose).moveByOffset(2,2).click().build().perform();
}
Can you close toaster using escape key from keyboard manually.
If you can than use following:
Actions action = new Actions(driver);
action.sendKeys(Keys.ESCAPE).build().perform();
It seems the toaster was partially rendered and fixed to the DOM just below the bottom edge of the screen, using position:fixed to stop it from showing up until it's ready to be populated with data and animated onto the screen. When chrome tried to click on links that were below the bottom edge of the screen, it predicted that it'd hit the toaster and didn't actually bother scrolling.
After some googling, I added the following utility function:
public static void ScrollElementIntoView(WebDriver driver, WebElement element) {
((JavascriptExecutor) driver).executeScript("arguments[0].scrollIntoView(true);", element);
}
Then I call this method before clicking any link on that page, and voila, no more toaster problems!
I am trying to select an option Pune(PNQ) from "Leaving from" list box of this page
http://book.spicejet.com/
driver.get("http://book.spicejet.com");
Thread.sleep(50000);
Select S = new Select(driver.findElement(By.id("ControlGroupSearchView_AvailabilitySearchInputSearchVieworiginStation1")));
S.selectByValue("PNQ");
But I am receiving this error:
org.openqa.selenium.ElementNotVisibleException
I am new to Selenium. Please help.
Straight from the Selenium source -
/**
* Thrown to indicate that although an element is present on the DOM, it is not visible, and so is
* not able to be interacted with.
*/
public class ElementNotVisibleException...
As it says, the element is there on the DOM, but not visible to operate with. If there a preemptive action you have to take before that element exists, then do that.
An example would be Google image searches. When you click on an image, a black box appears with the picture. That element is always there, but you have to click on an image to make it appear.
Sounds like the same thing is happening with your select box.
Edit
I took the liberty of further looking at your particular issue.. it looks like that site hides that <select> tag because it's filled in by some jQuery stuff.
Instead of using the select tag and selecting by value, do,
driver.findElement(By.id("ControlGroupSearchView_AvailabilitySearchInputSearchVieworiginStation1_CTXT").sendKeys("PNQ");
driver.findElement(By.cssSelector("a[value='PNQ']").click();
Hope this helps.
Is there a trick to selecting page elements one after the other using java webdriver? I am trying to set values in a form which is inside an iframe.
I first switch the iframe and can access and change the values in the first dropdown. I can create a WebElement from the other pages but I cannot interact with them at all.
The code below allows me to change the value of the first drop down.
driver.switchTo().frame(0)
WebElement fromList = driver.findElement(By.id("foo"));
r.selectItemByText(fromList, "var");
I cannot access any of the elements after that. I can find them all with no error but I cant do anything with them as it says they have no data.
WebElement fromList = driver.findElement(By.id("bar"));
r.selectItemByText(fromList, "foo");**
The second line returns this error:
NoSuchElementException: Cannot locate element with value 'foo'
If I remove it I get no error so the code is seeing the element just not the values of it.
If the elements are on a different frame, you must switch back to the default frame, then back down again.
Something like:
driver.switchTo().defaultContent();
Reason is because once you switch to a frame, if you switch to another frame, the search will be only of the child frames to that original frame. So you must switch back to the top frame, and back down again.