A temporary CSS element is obscuring the xpath element I want to click on.
Using Selenium in Visual Studio I am writing a testcase where an element is to be clicked. But when I run this testcase I get an error message telling me:
OpenQA.Selenium.ElementClickInterceptedException Element <a href="#/app/customer/handling/devices"> is not clickable at point (105,221) because another element <div class="showbox layout-align-center-center layout-row ng-star-inserted"> obscures it
The obscuring element seems to be a CSS element. But when I manually go to this page, inspect the code and search for this css element
<div class="showbox layout-align-center-center layout-row ng-star-inserted"> I get no results.
I am therefore assuming that this CSS element is temporary and can only be identified for a couple of seconds while the page is loadning. This assumption is confirmed by the fact that if I put a static wait method Task.Delay(4000).Wait();in this testcase I am able to complete this testcase.
But I'm not interested in a solution where I use a static wait method in my code. I want to be able to wait for the temporary css to disappear so I can click on my desired xpath element.
This is my code:
var wait = new WebDriverWait(driver, TimeSpan.FromSeconds(30));
wait.Until(ExpectedConditions.ElementIsVisible(By.CssSelector("div class='showbox layout-align-center-center layout-row ng-star-inserted'")));
//Waiting for temporary CSS element to be visible
wait.Until(ExpectedConditions.InvisibilityOfElementLocated(By.CssSelector("div class='showbox layout-align-center-center layout-row ng-star-inserted'")));
//Waiting for temporary CSS element to disappear
wait.Until(ExpectedConditions.ElementToBeClickable(By.XPath("//div/div[2]/nav[2]/div/ul/li[1]/a"))).Click();
//Click on "Handle Tools" link
As you can read by the comments in my code I do three things here:
1. I wait for the temporary CSS to be loaded
2. I wait for it to disappear
3. I click on the xpath element.
When I do this I get an error telling me the CSS selector I'm trying to find is invalid.
If I skip the first step and just wait for the CSS to disappear/be "invisible" it will check this too fast and look before the CSS element have started loading.
So either I'm not writing the By.CssSelector format correctly. Or the CSS element is called something else than Visual Studio is calling it in the error message I quoted above.
So please tell me if I'm using an incorrect way to identify this CSS element here:
wait.Until(ExpectedConditions.InvisibilityOfElementLocated(By.CssSelector("div class='showbox layout-align-center-center layout-row ng-star-inserted'")));
OR
Is there another way to dynamically wait out temporary CSS elements?
OR
Can I somehow confirm that the name of obscuring CSS element is actually called "div class='showbox layout-align-center-center layout-row ng-star-inserted'"?
So I am not 100% sure what you mean by a CSS element. But I am thinking it is just a misnomer you may have picked up. But CSS is cascading style sheets. It is markup that defines the appearance of a Document Object Model (DOM) element.
XPath is an approach to defining the location of an element for a driver to find it and interact with it. Css selectors are an alternative approach to finding elements in the xml or html of a DOM. So there is no such thing as a "Css element" or an "XPath element". They are just "elements".
As for fixing the issue you are seeing, you could try this:
//This is called an extension method. It adds methods to an existing class. This should go in a static class that is accessible to your driver.
private static void WaitUntil(this IWebDriver driver, Func<bool> Condition, float timeout)
{
float timer = timeout;
while (!Condition.Invoke() && timer > 0f)
{
System.Threading.Thread.Sleep(500);
timer -= 0.5f;
}
System.Threading.Thread.Sleep(500);
}
driver.WaitUntil(() => driver.FindElementsBy(By.CssSelector(".showbox.layout-align-center-center.layout-row.ng-star-inserted").length == 0);
Also of note, your CSS Selectors in your description are completely invalid. I suggest reading up on them in a tutorial like this one.
The problem in this case was that my CSS selector was indeed invalid. Removing the "div class=" in the beginning and adding a "." solved the problem.
Before:
"div class='showbox layout-align-center-center layout-row ng-star-inserted'"
After:
".showbox.layout-align-center-center.layout-row.ng-star-inserted"
Thanks to Asyranok for providing me with the correct format for my CSS selector.
I did not need the rest of your code in this case, instead I just copied the correct CSS format from your code into my solution and it worked. :)
I usually only use xpath when trying to identify an element so I'm not that used to CSS and what format to use.
Also thanks to JeffC for confirming my suspicion that I need to wait for the obscuring element to first load and then wait again for it to disappear.
Related
I just started with implementing some UItests for a new WebApplication and noticed that sometimes tests are failing when I wait for an Element to Exist while the same test succeeds when waiting for the Element to be Visible.
So my question is, is there a fixed order in which a webelement becomes "existing", "clickable", "visible","displayed" etc. or does this fully depends on how the developer has implemented the webpage or maybe the JS Framework which is used implementing the application?
It all depends on how the page was written.
Web element does not evolve from one status to another. It may change, but as a result of some dynamic content, actions performed on the page etc.
What those statuses tell us:
Existing - is when the element is in the page DOM. It does not necessarily has to be visible or interactable. It is the basic status, as element cannot have other statuses, without existing. Using the driver.FindElement(By.Xpath(".//*")) will find all elements on the page.
Displayed - when element exists it may be visible. Use IWebElement.Displayed (C# syntax) to check if the element is displayed.
Clickable - sometimes when you try to click the element, the exception may be thrown, saying that something else would get the click. To deal with those issues, see this answer.
How do you check if the element exists? If you are just searching for element, it will throw exception, when element is not found. I suggest using WebDriverWait, to look for element, for certain time period, to deal with loading:
Wait = new WebDriverWait(driver, new TimeSpan(0, 0, timeoutSecond));
Wait.Until(d => d.FindElement(by));
Precisely, Selenium can deal with 3 distinct states of a WebElement with in the HTML DOM:
Element is Exists i.e. Present.
Element is Visible.
Element is Interactive / Clickable.
Honestly, you don't have to keep track of the order in which a webelement becomes existing, clickable, visible, displayed etc.
If your usecase is to validate the existence of any element you need to induce WebDriverWait setting the ExpectedConditions as ElementExists() which is the expectation for checking that an element is present on the DOM of a page. This does not necessarily mean that the element is visible. So the effective line of code will be:
IWebElement element = new WebDriverWait(driver, TimeSpan.FromSeconds(30)).Until(ExpectedConditions.ElementExists(By.CssSelector("element_cssSelector")));
If your usecase is to extract any attribute of any element you need to induce WebDriverWait setting the ExpectedConditions as ElementIsVisible(locator) which is an expectation for checking that an element is present on 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. So in your usecase effectively the line of code will be:
IWebElement element = new WebDriverWait(driver, TimeSpan.FromSeconds(30)).Until(ExpectedConditions.ElementIsVisible(By.ClassName("element_classname")));
If your usecase is to invoke click() on any element you need to induce WebDriverWait setting the ExpectedConditions as ElementToBeClickable() which is an expectation for for checking an element is visible and enabled such that you can click it. So in your usecase effectively the line of code will be:
new WebDriverWait(driver, TimeSpan.FromSeconds(20)).Until(ExpectedConditions.ElementToBeClickable(By.XPath("element_xpath"))).Click();
The below code is not working and it always throws No such element exception at line 2.
wait.IgnoreExceptionTypes(typeof(NoSuchElementException));
wait.Until(ExpectedConditions.ElementToBeClickable(By.XPath(element)));
There could be 2 issues here:
You are trying to find the element before its visible for that you can wait for the element by doing
wait.Until(ExpectedConditions.ElementExists(By.XPath(element)));
where element is the XPath of the element you are trying to find.
You are not finding the element using the correct XPath. If you are using an absolute XPath, avoid doing because while absolute XPath can find the element faster, if the DOM structure changes your path may no longer work.
It is also possible that you are not running your browser in fullscreen, at least this was a valid issue I was facing when my current projects' GUI got changed over. Adding driver.Manage().Window.Maximize(); to my ClassInitialize fixed the issue in a whim.
Another option is that maybe your element is either embedded into an iframe or is overlapped by one.
As mentioned in this answer https://stackoverflow.com/a/44724688/6045154 , I have solved a similar issue with:
IWebElement button = driver.FindElement(By.ClassName("transfer__button"));
IJavaScriptExecutor executor = (IJavaScriptExecutor)driver;
executor.ExecuteScript("arguments[0].click();", button);
Of course this needs to be edited to find your element by the right selector.
I know there are existing questions on this topic, but none of them seems to help me with this:
I've got a lightbox with several elements.
I can find and access all of these elements, except ONE, using the XPath.
These are the items:
Text header: No problem
Text: No problem
Input field: No problem
Text: No problem
Text: No problem
Button (upload file): THIS IS SEEMINGLY IMPOSSIBLE FOR Selenium TO FIND
Button (cancel): No problem
Button (send): No problem
The XPaths for all the elements:
.//*[#id='overlays']/overlay--master/div/div/overlay-lightbox/div/div[1] /content-placeholder/h1
.//*[#id='overlays']/overlay--master/div/div/overlay-lightbox/div/div[3]/content-placeholder/ul[1]/li[1]/label/span
.//*[#id='overlays']/overlay--master/div/div/overlay-lightbox/div/div[3]/content-placeholder/ul[1]/li[1]/div/div/input
.//*[#id='overlays']/overlay--master/div/div/overlay-lightbox/div/div[3]/content-placeholder/label
.//*[#id='overlays']/overlay--master/div/div/overlay-lightbox/div/div[3]/content-placeholder/span
.//*[#id='overlays']/overlay--master/div/div/overlay-lightbox/div/div[3]/content-placeholder/a/input
.//*[#id='overlays']/overlay--master/div/div/overlay-lightbox/div/div[5]/content-placeholder/button[1]
.//*[#id='overlays']/overlay--master/div/div/overlay-lightbox/div/div[5]/content-placeholder/button[2]
The problematic element is this:
.//*[#id='overlays']/overlay--master/div/div/overlay-lightbox/div/div[3]/content-placeholder/a/input
As far as I can see, there's no reason why it should be different from the other elements (textfield, button, text)?
I'm accessing all these elements with an implicit wait, to check that they've all loaded before continuing.
GCDriver.WaitForVisible("//*[#id='overlays']/overlay--master/div/div/overlay-lightbox/div/div[3]/content-placeholder/a/input");
From the GCDriver (Selenium Driver) class:
public static void WaitForVisible (string xpath) {
var wait = new WebDriverWait(GCDriver.Instance,
TimeSpan.FromSeconds(10));
wait.Until(driver =>
driver.FindElement(By.XPath(xpath)).Displayed);
}
Now, as mentioned, this works for all the other elements, as well as accessing them directly. For this, the wait times out with WebDriverTimeoutEsception:
Result Message:
Test method Tests.Regression_tests.VerifyOverlays.Verify_Update_Ticket_OverlayContent threw exception:
OpenQA.Selenium.WebDriverTimeoutException: Timed out after 10 seconds
Also, of course, trying to ACCESS the button with .Click() also fails:
GCDriver.Instance.FindElement(By.XPath(".//*[#id='overlays']/overlay--master/div/div/overlay-lightbox/div/div[3]/content-placeholder/a/input")).Click();
Result Message:
Test method Tests.Regression_tests.VerifyOverlays.Verify_Update_Ticket_OverlayContent threw exception:
System.InvalidOperationException: unknown error: Element is not clickable at point (-208, 307)
Here's the html code for the element:
<a class="btn btn-grey file-input-container btn-small" data-bind="enable: !uploading() "
style="margin-top: 10px; padding: 7px 12px; "data-tooltipped=""
aria-describedby="tippy-tooltip-32"
data-original-title="Add Attachment">
<i class="fa fa-cloud-upload"/>
<span class="mq-for-small-hide">
<span localize-me="">Add Attachment</span>
</span>
<input data-bind="upload: addAttachments,
enable: !uploading()" type="file"/>
</a>
I've tried some other ways of getting the element, but since this is quite (imo) "messy" html, with no unique ID's or good class names, I've been unable to figure out how.
And it REALLY bugs me that I cannot find it by the XPath. There are 8 elements on the page, all visible and accessible, but this ONE element is impossible to find with Selenium.
The element is there; I can manually click the button on the page while Selenium runs it.
UPDATE:
I also tried using .Enabled instead of .Displayed. Same result.
UPDATE 2:
There are two answers below, and I have to select one as the "winner".
Shubham Jain gives an answer that, while not the exact thing I was trying to to, is a very good work-around. By using JavaScriptExecutor to try clicking the button, it also checks if the button is visible. However, the answer given doesn't do what it tries to do; Clicking doesn't work quite that way. See Solution below to see the correct/working code to click a button using JavaScriptExectutor.
smit9234's answer is exactly what I'm trying to do, although clicking doesn't work that way. To click the button, JS is necessary in this case. However, the question was how to check .Displayed, and that works with the modified XPath he gave me from the code excerpt.
Solution
The XPath of the element (button) is, according to FirePath:
.//*[#id='overlays']/overlay--master/div/div/overlay-lightbox/div/div[3]/content-placeholder/a/input
This, however, doesn't work. Selenium simply cannot find it, even though it's clearly there.
THIS XPath, however, does work:
.//*[#id='overlays']/overlay--master/div/div/overlay-lightbox/div/div[3]/content-placeholder/a/span/span
However, it works with reagards to the .Displayed check. It does NOT work with Click(). To be able to click the button, I began with Shubham Jain's code example and created this method in the Driver class, to be able to use JavaScript (with Selenium's JavaScriptExecutor) to click the button:
using OpenQA.Selenium.Interactions;
public static void JSClick (string xpath) {
IWebElement icon = Instance.FindElement(By.XPath(xpath));
Actions ob = new Actions(Instance);
ob.Click(icon);
IAction action = ob.Build();
action.Perform();
}
Looking at the html snippet you posted, it seems like this is a file attachment function. Based on the html structure of the snippet, try using the following xpath:
.//*[#id='overlays']/overlay--master/div/div/overlay-lightbox/div/div[3]/content-placeholder/a/span/span
You should then be able to use the click(); method to click the "Add Attachments"
I assume that clicking on the input doesn't do anything, however you should be able to use the sendKeys(); method for sending the "file path" to the input element.
Use below XPath :-
//input[#type='file' and contains(#data-bind,'upload: addAttachments')]
You can use javascriptexecutorof selenium to click on button. It operated directly on JS of page.
In java :-
WebElement element = driver.findElement(By.id("gbqfd"));
JavascriptExecutor executor = (JavascriptExecutor)driver;
executor.executeScript("arguments[0].click();", element);
I don't know more about c# but I believe it something like
IWebElement clicks = driver.FindElement(By.Id("gbqfq"));
IJavaScriptExecutor js = (IJavaScriptExecutor)driver;
js.ExecuteScript("arguments[0].click();", clicks);
Change the locator in above elements as per your convenience.
Below you will find more details of javascriptexecutor
https://seleniumhq.github.io/selenium/docs/api/java/org/openqa/selenium/JavascriptExecutor.html
Hope it will help you :)
May be input element is not visible on the page. you may not use displayed function for that element and try with enabled as given below.
public static void WaitForEnabled (string xpath) {
var wait = new WebDriverWait(GCDriver.Instance,
TimeSpan.FromSeconds(10));
wait.Until(driver =>
driver.FindElement(By.XPath(xpath)).Enabled);
}
if the above is not working, you try to click on anchor tag instead of input.
It seems like it's not clickable. It looks like there's some javascript on the page with a function called "uploading()".
since you're button has this on it
enable: !uploading()
just a test to verify if this is actually the cause, put a breakpoint before your click. on the browser dev tools stick a breakpoint in the uploading() function on the javascript file and see what it's returning.
If this is the case you'll have to use the javascript executor to bypass this.
Element im trying to locate:
WebDriverWait wait = new WebDriverWait(browser, TimeSpan.FromSeconds(60));
wait.Until(ExpectedConditions.ElementExists(By.XPath("//use[#xlink:href='#core_mail']")));
File.WriteAllText("html.txt", browser.PageSource);
that just times out.. and the page loads way before 60 seconds.
svg has a default namespace. You have to account for it:
//svg:use[#xlink:href='#core_mail']
Or ignore it with local-name():
//*[local-name() = 'use' and #xlink:href='#core_mail']
Though, to be fair, you don't have to dive into the markup that deeply, your "email" button is much higher in the tree - see the very first parent element partially visible on the screenshot - that's your desired element you probably want to locate instead.
I am new to Selenium and looking forward to learn more, I am using Selenium WebDriver with C#.
This is how I initiate a WebElement:
CarouselSliderNextButton = DriverInitializer.driver
.FindElement(By.XPath("//a[#class='buttons next']"));
But if the element doesn't exist for some reason; hidden for instance, then it doesn't work. I know that I can check if the element exists before I can initiate, but would love to hear from experts if I am doing this the right way.
I am not claiming to be an expert, but this is what I think:
You do not "initiate" a web element
You find a web element
First of all, you need to find the element you want to locate from html file. You may use Google or Firefox in developer mode to find it. I recommend you to install "Firebug" for Firefox, it is very useful.
There are multiple reasons why an element you can "see" from HTML file but you can not locate:
1: this element is on an iframe, this case requires you to locate this iframe first then locate the element
2: this element is not visible yet, for example, a drop down arrow button will only appear if you hover your mouse over a certain area first.
But you are heading to the right direction.
If you're trying to intialise the WebElement, I use:
WebElement element = driver.findElement(By.tagName("div"));
as most DOMs have div tags.
Then after trying to find an element that exists (and isn't a div tag), check:
if (element.getTagName().equals("div")){
System.out.println("Element not found...");
}
var instantEstimateDiv: WebElement? = null => Kotlin
WebElement? instantEstimateDiv= null; => JAVA