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);
Related
I am trying to automate the scheduling of pinning items to Pinterest using Tailwindapp.com. I am using a console app in .NET (C#) with Selenium Chromedriver. I start up the browser and enable the tailwind extension and login to tailwind. Then I go to the site I am trying to pin images from, get to the product page, search for the button and attempt to click it. That's where it falls apart. The 'Schedule' button in Tailwind appears over all images on the page as you hover. When I do an XPath search, it only returns a single button for the whole page (the console line below shows 1).
public static void ClickScheduleButton(IWebDriver driver) {
// get all the buttons and then use the first one
IList<IWebElement> buttons = driver.FindElements(By.XPath("//*[#id='tw_schedule_btn']"));
Console.WriteLine("Number of items found: " + buttons.Count());
IWebElement scheduleButton = buttons.ElementAt(0);
Actions actions = new Actions(driver);
actions.MoveToElement(scheduleButton).Click().Perform();
}
On the perform method, I get the following error: OpenQA.Selenium.WebDriverException: 'javascript error: Failed to execute 'elementsFromPoint' on 'Document': The provided double value is non-finite.
From what I've read, this seems to mean that there are more than 1 element but I seem to have ruled this out with the number of buttons found. I have attempted to do a wait to make sure it's available but I do not believe that is the issue.
I have tried to find an example just trying to do this with a Pinterest button because in theory it would be the same logic but I cannot find anything for that either.
My assumption is that it's a problem just getting the button to appear on the correct image? But that's just a guess.
If I'm understanding this correctly you want to click the button who appears in when you hoover the mouse in the image.
If you can hoover the mouse and wait until the expected xpath appears should solve it.
protected virtual void HooverMouse(By element)
{
Actions action = new Actions(driver);
action.MoveToElement(driver.FindElement(element)).Build().Perform();
}
The element will be the first image you have, so you must change your driver.findelements("the button") to the "images" and loop arround this.
In pseudo code will be:
Hoover(image) -> WaitButtonAppears -> ClickButton
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.
<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.
I'm running a Test Project in visual studios that is running test steps for a web page.
The web page itself is coded poorly. it has something like this:
<a id ="GenericButton" </a>
<div id="UniqueID" class="ThisDiv">
<a id ="GenericButton" </a>
</div>
The issue that I'm having is that I need to click the link inside the div "UniqueID". I don't want to click the first web button.
Using the code:
HtmlHyperlink myLink = new HtmlHyperlink(this.IE);
myLink.SearchProperties[HtmlHyperlink.PropertyNames.Id] = "GenericButton";
Mouse.Click(myLink)
Will result in clicking the first button instead of the button inside of the div "UniqueID"
I can get the div using:
HtmlDiv myDiv = new HtmlDiv(this.IE);
myDiv.SearchProperties[HtmlCheckBox.PropertyNames.Class] = "ThisDiv";
myDiv.SearchProperties[HtmlCheckBox.PropertyNames.Id] = "UniqueID";
//The below line is later referenced
int myDivTag = myDiv.TagInstance;
But how can I then capture the a frame inside of this div?
Furthermore, when debugging, the object myDiv doesn't seem to properly set itself in Visual Studios until running the line "int myDivTag = myDiv.TagInstance;". Why doesn't the debugger know which object I am referring to until after that happens?
I am using Visual Studio libraries for these operations instead of something like WatiN.
If the web page is poorly code, then the tests for the web page should fail for the bits that have been implemented poorly - simple.
In HTML, two elements should not have the same ID. So write your tests expecting only the single element. Since the input markup to your test will provide two elements with the same ID, the test should fail and remain failed until the problem is actually fixed on the other end.
There's no point hacking your tests to work with bad implements as it defeats the point of testing.
Update: I have found this answer which may give a solution to finding the nested a element.
Adrian gave the helpful link to the fix: How to handle testing an HTML control that lacks sufficiently unique attributes?
HtmlDiv myDiv = new HtmlDiv(this.IE);
myDiv.SearchProperties[HtmlDiv.PropertyNames.Class] = "ThisDiv";
myDiv.SearchProperties[HtmlDiv.PropertyNames.Id] = "UniqueID";
HtmlHyperlink myLink = new HtmlHyperlink(myDiv);
myLink.SearchProperties[HtmlHyperlink.PropertyNames.id] = "GenericButton";
Mouse.Click(myLink);
I have a C# Windows Forms Application with a webBrowser within it. It visits a Wikia chatroom, where it then locates the element which contains the chat output. This element looks like:
<div style="" id="Chat_XXXXX" class="Chat">
<ul>
(chat text)
</ul>
</div>
The purpose of the program is to retrieve the chat text every once in a while for logging purposes. This is easily done by parsing the "InnerHtml" of the "Chat_XXXXX" element. However, I also need to clear the text from the window when I do this (for various reasons, I cannot leave the text in the window). I figured I would just erase the chat text portion of the element, as this is how it is done with a handy javascript file called "chat hacks" for Wiki chat (here). Or at least, I think that's how it does it. If you look at the function "clearWindow" in that file, you can see what it does:
NodeChatController.prototype.clearWindow = function() {
this.viewDiscussion.chatUL.html('');
this.inlineAlert(i18n['cleared']);
}
I have tried setting the InnerHtml of "Chat_XXXXX" using the following three strings (not all at the same time, of course):
HtmlDocument document = webBrowser1.Document;
document.GetElementById("Chat_XXXXX").InnerHtml = ""
document.GetElementById("Chat_XXXXX").InnerHtml = "<ul></ul>"
document.GetElementById("Chat_XXXXX").InnerHtml = "<ul><li class=\"inline-alert\"> Window cleared. </li></ul>"
However, although these clear the window (and in the case of the last one prints a message), the chat no longer updates as new messages show up. The only fix is to reload the page, which isn't an option, because reloading the page brings in a whole load of chat history (which I'm trying to avoid). I've also tried importing that javascript mentioned above into the page using:
HtmlElement head = webBrowser1.Document.GetElementsByTagName("head")[0];
HtmlElement scriptEl = webBrowser1.Document.CreateElement("script");
scriptEl.SetAttribute("type", "text/javascript");
scriptEl.SetAttribute("src", "https://db.tt/66q8UQbY");
head.AppendChild(scriptEl);
This javascript creates a button which clears the chat window. The button clears the window just fine, but again, the chat no longer updates. I know this button works correctly without stopping further incoming chat in "regular" browsers (Chrome, Firefox, Opera, etc). I've used it many times, and the script itself is quite popular in the Wikia community. I've already followed the steps here: "WPF WebBrowser Control - position:fixed Element jumps while scrolling (Windows 8)" to get the browser to act as close as it can (?) to Internet Explorer. I've already checked the Body field of the document after altering the InnerHtml to make sure that my replacements didn't alter anything important. Just for clarity, here's an example of what is contained in the (chat text) portion of my original example:
<li class="you" data-user="UserNameHere" id="entry-c812">
...avatars and junk...
<span class="message">hello</span>
</li>
I honestly have no idea what could be causing the chat element to stop updating after it has been edited (especially since it works outside of this program), so I don't know what information to include. Whatever you need, I'll provide it. Here's the javascript from Wikia which generates the chat output window: chat_js2. Look for "Chat_" to find the part which originally generates the window. I don't know where the output is updated in that file though.