StaleElementException when Clicking on a TableRow in an Angular WebPage - c#

<div id="crm" class="row gridrow clickable ng-scope" ng-repeat="customer in customerList" ng-click="gotoRecord(customer.id)">
<i class="col m1 s1 tiny fa fa-male"></i>
<div class="col m3 s11 ng-binding"> Allard</div>
<div class="col m2 s12 ng-binding"></div>
</div>
I have this snippet of HTML, it's one row displaying as a result of a search action for a Customer with nameCustomer 'Allard'. I want to click on this customer to continue to the next page but most of the time this results in a StaleElementException.
I tried it two different ways, using Protractor and without Protractor.
First way:
IWebElement elem = driver.FindElement(By.XPath("//*[contains(text(),'" + nameCustomer + "')]//parent::div[contains(#id,'crm')]"));
ExplicitWait.WaitAndClick(driver, elem);
Second way:
var customers = driver.FindElements(NgBy.Repeater("customer in customerList"));
foreach (var customer in customers)
{
if (elem.Text.Equals(nameCustomer))
{
elem.Click();
}
}

The problem (I think)
With StaleReferenceExceptions, an IWebElement that was previously created is no longer attached to the DOM (you probably already know this). What's most likely happening is this:
1: You click search.
2: Selenium executes driver.FindElement(...) and finds a matching element.
3: Then the search function finishes and the DOM updates. The old IWebElement found previously is gone.
4: Then Selenium tries to click on the element (which no longer exists, causing the StaleElementException. There is an element that matches the one that was there before, but it's not the same element in Selenium's eyes.)
Your statement that this happens "most of the time" makes me suspect this is the case even more, because the exception would depend on the order of events, which would vary depending on the relative speeds of Selenium vs. the web-page.
How to resolve (if this is your problem)
You need to find something on the page that will indicate to Selenium that the search action is done. This is where the creativity of writing GUI automation scripts comes in. If there is something on the page that you know will change as a result of the load, craft an explicit wait to ensure that is complete. Maybe there is a loading bar that shows up, or a message that appears when the search is done. You could grab an element that doesn't match your search before clicking search, then do an explicit wait to make sure it disappears before going on to look for the result you do expect to be there.
It would look something like this below.
# Search action performed before this.
WebDriverWait wait= new WebDriverWait(driver, TimeSpan.FromSeconds(secondsToWait));
wait.Until(ExpectedConditions.InvisibilityOfElementLocated(By.XPath( expressionForElementThatWillDissapear )));
IWebElement elem = driver.FindElement(By.XPath("//*[contains(text(),'" + nameCustomer + "')]//parent::div[contains(#id,'crm')]"));
ExplicitWait.WaitAndClick(driver, elem);
I'd recommend making the method to implement the explicit wait above. These situations will come up often.

Related

*Problems finding specific element on page

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.

Can get an element inside an iFrame but .Click() does nothing

I'm using Selenium from C# to try and periodically do a search on a website and pull in updated data. Unfortunately the website needs a login I can't make public so I can't actually give any kind of reproducible test case.
Using the Selenium IDE I've made a script that successfully logs in and pulls the data, so the task is possible. However when exporting that script to C#, there's a failure when trying to use an iframe that appears during the process.
By reading around I've discovered I need to add code to explicitly find and switch to the iframe, which I've done;
driver.SwitchTo().Frame(driver.FindElement(By.Id("popup_iframe")));
Inside that frame is an area to the left with items to click. Each item when clicked changes the visible controls to the right. When I break at this point and click manually, everything works as expected. If I call the .Click on the IWebElement representing the object, nothing happens so the next step in the program fails because it gets another object, tries to click it and an exception's thrown because the object isn't visible.
I'm leaving 5s delays between operations in case delays are needed.
The code on the website in this area is;
<div id="ctl00_popup_workarea_left_container" class="popup-workarea-left-container" style="overflow: auto; height: 337px; display: block;">
<a id="LBTN1" class="navbutton navbutton_Disabled_Selected">Basic Search</a>
<a id="LBTN2" class="navbutton">Advanced Search</a>
<a id="LBTN3" class="navbutton">Permit Holder Search</a>
<a id="LBTN4" class="navbutton">Assessor Search</a>
</div>
The code I'm using to get and click the IWebElement is
driver.FindElement(By.Id("LBTN3")).Click();
I've also tried
Actions act = new Actions(driver);
act.MoveToElement(driver.FindElement(By.Id("LBTN3"))).Click().Perform();
Aside from the fact these don't throw exceptions, I've confirmed I'm getting an IWebElement interactively;
driver.FindElement(By.Id("LBTN3"))
{Element (id = f42435b5-139c-4334-be12-4a1f9f48221f)}
Coordinates: {OpenQA.Selenium.Remote.RemoteCoordinates}
Displayed: true
Enabled: true
Id: "f42435b5-139c-4334-be12-4a1f9f48221f"
Location: {X = 0 Y = 112}
LocationOnScreenOnceScrolledIntoView: {X = 0 Y = 112}
Selected: false
Size: {Width = 200 Height = 29}
TagName: "a"
Text: "Permit Holder Search"
WrappedDriver: {OpenQA.Selenium.Firefox.FirefoxDriver}
So I'm getting the element. The HTML appears to show that it really is the element- I'm not accidentally using the ID of something containing it. The original script created in the Selenium IDE works fine- I just can't automate it so it kicks off unattended in the early hours. I know this is vague because I can't give people access to play to reproduce it, but does anyone have an idea of what could be going wrong and how to fix?
For completeness I thought I'd better put an answer here.
I never identified why none of the attempts I made worked, but I discovered that injecting Javascript into the page to click the element I wanted to click did work. So assuming .FindElement() is able to get the element to click, the following snippet worked for me in a number of places where IWebElement.Click() proved uncooperative.
IWebElement ibtn3 = driver.FindElement(By.Id("LBTN3"));
IJavaScriptExecutor executor = (IJavaScriptExecutor)driver;
executor.ExecuteScript("arguments[0].click();", ibtn3);

C# Selenium - Click/Clear/SendKeys not functioning properly, but works in Debug mode

So I'm having an issue where my commands to change date fields only work half the time and I'm not sure what is causing this. Yet, when I run them in Debug mode and go step-by-step thru the method, it does what it's suppose to do. Below is the code that from my method:
PermitExpirationRangeStart = DateTime.Today.AddDays(121).ToShortDateString();
PermitExpirationRangeEnd = DateTime.Today.AddDays(150).ToShortDateString();
//Wait until we have the facility name.
//WaitFor(() => !string.IsNullOrEmpty(FindElement(By.Id("ctl32_ctl04_ctl05_txtValue")).GetAttribute("name")));
Sleep(5000);
FindElement(By.Id("ctl32_ctl04_ctl03_txtValue")).Clear();
FindElement(By.Id("ctl32_ctl04_ctl03_txtValue")).SendKeys(PermitExpirationRangeStart);
FindElement(By.Id("ctl32_ctl04_ctl05_txtValue")).Clear();
FindElement(By.Id("ctl32_ctl04_ctl05_txtValue")).SendKeys(PermitExpirationRangeEnd);
The page in question that I'm trying to test is the code below (this is just one of the date fields):
<td class="ParamEntryCell" style="padding-right:0px;">
<div id="ctl32_ctl04_ctl05">
<div style="white-space:nowrap;" onactivate="event.cancelBubble=true;">
<input id="ctl32_ctl04_ctl05_txtValue" class="null" type="text" size="28" value="12/22/2016" name="ctl32$ctl04$ctl05$txtValue"/>
<input id="ctl32_ctl04_ctl05_ddDropDownButton" type="image" style="cursor:pointer;" title="Select a value" alt="Select a value" name="ctl32$ctl04$ctl05$ddDropDownButton" src="/Reports/Reserved.ReportViewerWebControl.axd?OpType=Resource&Version=12.0.4422.0&Name=Microsoft.Reporting.WebForms.calendar.gif"/>
</div>
</div>
</td>
I've tried rearranging the lines of code in my test, I've even tried setting the elements to IWebElements before I do a Clear or SendKeys method on it. Google search yields no results that helps figure this problem out.
The answer is most likely to be: you need to introduce wait in your code.
In debug mode, you executed your code "slowly" one step at a time, the browser instance has more than enough time to respond.
Please try introducing "Wait" mechanism.
WebDriverWait wait = new WebDriverWait(driver, 30);
wait.until(ExpectedConditions.visibilityOfElementLocated(By.id("locator")));
for both of your ctl32_ctl04_ctl03_txtValue and ctl32_ctl04_ctl05_txtValue elements
May be you are facing problem due to execution speed, Here due to fast execution sometime selenium doesn't focus to the input before setting value. You should try using .Click() to provide focus to the element before setting the value as below :-
IWebElement el = driver.FindElement(By.Id("ctl32_ctl04_ctl03_txtValue"));
el.Click();
el.Clear();
el.SendKeys(PermitExpirationRangeStart);
IWebElement el1 = FindElement(By.Id("ctl32_ctl04_ctl05_txtValue"));
el1.Click();
el1.Clear();
el1.SendKeys(PermitExpirationRangeEnd);
Note: No need to find element again and again in the same page. You need to find just one time and stored into a variable for further action.
Hope it helps..:)
Element.SendKeys("");
Element.SendKeys(number);

Why does my XPath get stuck on first result when I add /text()?

Here's a simulation of the HTML I am trying to use my XPath on:
<div class="stream-links">
<div>
value I need
</div>
<div>
value I need
</div>
<div>
value I need
</div>
</div>
Now, when I use the XPath pattern //div[#class='stream-links']/div/a in my browser it selects the <a ...> node. Everytime I press enter it selects the next one, but when I use the pattern //div[#class='stream-links']/div/a/text() it gets stuck on the text of the first <a ...> node so when I press enter it does not move to the next. (Using Firebug plugin on FireFox btw to inspect element)
I'm coding a program in C# and the amount of divs under the parent div is a variable so I can't use //div[#class='stream-links']/div[number here]/a/text() because I need to get all of them.
My code for using the Xpath is HtmlNodeCollection NODECOL1 = MEDOC.DocumentNode.SelectNodes("//div[#class='stream-links']/div/a[1]");
So my questions are:
1) Is there a particular reason Firebug doesn't jump to the next <a...> or is it a 'bug' on the plugin's side?
2) Will my code work nevertheless or do I need to approach it in another way?
There're a few things not right with the rest of my code so I can't see if that part of my code actually works or not, wouldn't ask question 2 if I could test it myself right now.
For your HTML, this XPath selects three a elements:
//div[#class='stream-links']/div/a
This XPath selects three text nodes:
//div[#class='stream-links']/div/a/text()
This XPath selects one a element:
//div[#class='stream-links']/div/a[1]
My code for using the Xpath is HtmlNodeCollection NODECOL1 =
MEDOC.DocumentNode.SelectNodes("//div[#class='stream-links']/div/a[1]");
1) Is there a particular reason Firebug doesn't jump to the next
or is it a 'bug' on the plugins side?
//div[#class='stream-links']/div/a[1] only selects one a element.
2) Will my code work nevertheless or do I need to approach it in
another way?
There's a few things not right with the rest of my code so I can't see
if that part of my code actually works or not, wouldn't ask question 2
if I could test it myself right now.
That's not a reasonable question to ask given what you've shown us. Perhaps knowing what the above XPaths return will help you answer it for yourself.

Unable to find ::before css selector element in selenium webdriver

I want to test for checkbox checked or unchecked condition.
This is the html code for checkbox checked
<div classs="input-control checkbox">
<span class="check">
::before
</span>
</div>
::before is css selector.
when i hoverover to checkbox, it shows webelement as span.check::before
but
driver.FindElement(By.CssSelector("span.check::before"));
throws element not found exception.
Any help will be highly appreciated.
In my case, I have removed the pseudo-element ::before from the CSS selector as seen below and it works
Instead of:
Actions action = new Actions(driver);
action.moveToElement(driver.findElement(By.cssSelector("span.check::before"))).build().perform();
I gave:
Actions action = new Actions(driver);
action.moveToElement(driver.findElement(By.cssSelector("span.check"))).build().perform();
Using pseudo-elements in the selector is actually not supposed to work as mentioned here and here.
I'm not sure of your exact html as the comment mentions there is no ::after in your example html. Based on your description this seems similar to the below, but not sure if that is what your exact situation is.
I have utilized the mouse move and then wait for the element that displays the modified css/class. The mouse would move and hover to the first element and then wait for the second to appear on the screen. This works similarly for click and then wait for element to appear.
Hover code example - needs to match your code language and structure...
[Actions Instance goes here].MoveToElement([IWebElementGoesHere]).Perform();
you can also just do
[Actions Instance].Click([IWebElementGoesHere]).Perform();
Reference for this library: https://code.google.com/p/selenium/wiki/AdvancedUserInteractions
I have faced similar issue with pseudo css selectors (::before alike), I have overcome the issue using "Actions" class of selenium java.
Actions action = new Actions(driver);
action.moveToElement(driver.findElement(By.cssSelector("button[id$='save-button']"))).build().perform();
Hope it helps.
Thanks,
Sampath

Categories

Resources