I am having a really hard time with the following issue. I am trying to navigate through some web pages that have various inputs (text boxes/dropdowns/buttons) followed by a continue button at the bottom to move onto the next screen. My tests frequently fall over because they can't always locate the first element in order to interact with it.
As far as I'm aware the page doesn't do any fancy AJAX post loading or anything, so once the page has loaded then Webdriver should be able to locate every element.
My code:
FRAMEWORK
public static void WaitOnPageForXPathClickable(string selector)
{
new WebDriverWait(Driver.Instance, TimeSpan.FromSeconds(20)).Until(ExpectedConditions.ElementToBeClickable((By.XPath(selector))));
}
TEST CASE
Utilities.WaitOnPageForXPathClickable("the xpath of the continue button and checks that it is clickable");
Driver.Instance.FindElement(By.XPath("the xpath of the first button that I want to click")).Click();
Do I need to try and include a function to ensure that the page is fully loaded before test execution? I am confused about this because I have read that Selenium already waits for the page to load by default. I would have thought that waiting for one element (the continue button) to be clickable should mean that all other elements are ready by then too? I really want to avoid having to wait for every single element before clicking it.
Can you please share html, have you tried with Visible instead of Clickable
public static void WaitOnPageForXPathClickable(string selector)
{
new WebDriverWait(Driver.Instance, TimeSpan.FromSeconds(20)).Until(ExpectedConditions.ElementIsVisible((By.XPath(selector))));
}
You can wait for the page to be fully loaded with something like:
public void WaitForPageToBeFullyLoaded(int timeSpan)
{
new WebDriverWait(Driver, TimeSpan.FromSeconds(timeSpan)).Until(
d => ((IJavaScriptExecutor) d).ExecuteScript("return document.readyState").Equals("complete"));
}
You can check for displayed or Enabled element too.
You have made things much more complicated by waiting for the Continue Button Utilities.WaitOnPageForXPathClickable("the xpath of the continue button and checks that it is clickable"); where as in the next step trying to click() on the First Button Driver.Instance.FindElement(By.XPath("the xpath of the first button that I want to click")).Click();
Test Scenario:
Ideally your Test Scenario should have been :
TEST CASE:
Utilities.WaitOnPageForXPathClickable("the xpath of the first button that I want to click");
Driver.Instance.FindElement(By.XPath("the xpath of the first button that I want to click")).Click();
FRAMEWORK:
public static void WaitOnPageForXPathClickable(string selector)
{
IWebElement element = new WebDriverWait(Driver.Instance, TimeSpan.FromSeconds(20)).Until(ExpectedConditions.ElementToBeClickable((By.XPath(//label[#class='replaced-input-label replaced-input-label--radio']))));
elementClick();
}
Related
I've got a webpage with a button which Selenium keeps insisting is not visible, although it most definitely is.
The page source:
<button id="product-catalog-page_order-selected-button" class="btn btn-grey
mq-apla-button opens-overlay" data-bind="click: runOrderWizard, enable:
hasSelection"><span localize-me="">Order Selected</span></button>
<span localize-me="">Order Selected</span>
Below this, there is a dynamic table with a list of entries. The entries have a checkbox in the first cell. Before checking this box, the above button is disabled. However, immediately after clicking it the button is enabled.
I've tried accessing (clicking) this button with both the ID and the XPath. I've tried the XPath for both the <button and the <span elements. Every time I try, I get this error:
Result StackTrace:
at OpenQA.Selenium.Remote.RemoteWebDriver.UnpackAndThrowOnError(Response
errorResponse)
at OpenQA.Selenium.Remote.RemoteWebDriver.Execute(String
driverCommandToExecute, Dictionary`2 parameters)
at OpenQA.Selenium.Remote.RemoteWebElement.Click()
at Common.Navigation.Elements.Button.ClickById(String id) in d:\Source
\Workspaces\QA\NewMySite\Common\Elements\PageElements.cs:line 51
at MyAutomation.Pages.NewOrderPage.OrderSelected() in d:\Source
\Workspaces\QA\NewMySite\MyAutomation\Pages\NewOrderPage.cs:line 36
at Tests.RegressionTests.Ordering.Ordering.User_Order_New_Hardware() in
d:\Source\Workspaces\QA\NewMySite\Tests\RegressionTests\Ordering
\Ordering.cs:line 29
Result Message:
Test method
Tests.RegressionTests.Ordering.Ordering.User_Order_New_Hardware threw
exception:
OpenQA.Selenium.ElementNotVisibleException: **element not visible**
(Session info: chrome=62.0.3202.94)
(Driver info: chromedriver=2.33.506120
(e3e53437346286c0bc2d2dc9aa4915ba81d9023f),platform=Windows NT 10.0.15063
x86_64)
I've also stopped the Selenium test immediately after the checkbox is clicked and before the button is clicked (there's a 3 sec. hard wait). Then I've manually tried to click the button, and it works fine.
There are no duplicate IDs, either; Only one element has this ID on the page.
Any ideas what I'm doing wrong here, or how to get around this if it's Selenium being dumb?
Here's the relevant program/test code:
Button.ClickById("product-catalog-page_order-selected-button"));
public class Button {
public static void ClickById (string id) {
FindById(id).Click();
}
private static IWebElement FindById (string id) {
return GCDriver.Instance.FindElement(By.Id(id));
}
}
I've also tried waiting for the element to be visible, using this code. No luck. The wait just times out.
Wait.WaitForVisibleId("product-catalog-page_order-selected-button");
public class Wait {
public static void WaitForVisibleId (string id) {
GCDriver.WaitForVisibleId(id);
}
}
public class GCDriver {
public static void WaitForVisibleId (string id) {
var wait = new WebDriverWait(GCDriver.Instance,
TimeSpan.FromSeconds(30));
wait.Until(driver => driver.FindElement(By.Id(id)).Displayed);
}
}
A while back, I had another button on this very site that I couldn't access with Selenium. That was a file upload button. I don't remember the Exception, but I solved that one with this code:
public static void ActionsUploadButtonId (string id, string filepath) {
Actions actions = new Actions(GCDriver.Instance);
actions.MoveToElement(GCDriver.Instance.FindElement(By.Id(id)));
actions.Click();
actions.Build().Perform();
}
This doesn't work here, though. It seems to do something, though: When I feed the ID of the problematic button ("product-catalog-page_order-selected-button") to that method, it selects the VERY FIRST clickable element on the page, which is the Front Page link, with its own unique ID, located several hundred lines of HTML code above the element with the actual ID.
Update
To troubleshoot, I followed a tip I found in another thread on Stack Overflow: To use Submit() instead of Clic(). However, that resulted in another (probably logical) exception:
OpenQA.Selenium.NoSuchElementException: no such element: Element was not in a form, so could not submit.
Update 2
I also tried getting the button by the class. There are two button with the exact same classes, but maybe it then would get the first one? Anyway, that results in the exact same ElementNotVisible exception.
Update 3
Both waiting for visible and waiting for clickable times out.
wait.Until(driver => driver.FindElement(By.Id(id)).Displayed);
wait.Until(ExpectedConditions.ElementToBeClickable(By.Id(id)));
Update 4
The html code for the checkbox (which enables the button when selected):
<td class="table-column-checkbox">
<input type="checkbox" data-bind="checked: $parent.selected, checkedValue: href" value="/Api/CatalogProducts/ProductOfferings/PO-6E32-CE4C-C169">
</td>
Update 5
The button is invisible to Selenium whether or not it's enabled or not:
Checkbox not selected
Button disabled
Result: OpenQA.Selenium.ElementNotVisibleException: element not visible
Checkbox selected
Button enabled
Result: OpenQA.Selenium.ElementNotVisibleException: element not visible
Update 6
Looking at the source code, there are no elements on the page set to "invisible", so that should eliminate any invisible parent element messing up.
The only thing of not I can find is that there are two blocks of HTML code with a button with the same ID that are commented out on the page. These appear before the button in question, but I wouldn't expect Selenium to bother about commented-out HTML code?
I've never found Selenium to be wrong about things like this. I would always assume that Selenium is correct and your locator is off or something is going on in the page that you aren't aware of.
The first way I find locators is using the dev console in Chrome. I personally like the Chrome dev tools the best but it's a personal preference (mostly). Use $$(locator) to test CSS selectors and $x(locator) to test XPath. Those will return a collection of elements found by those locators. You can instantly tell if you are finding more than one and if the one you want is not the first one, etc. I generally do this just by browsing to the page but if you are still having issues, put a breakpoint and stop on the offending line and then do this in the browser.
If that still doesn't work, I would add some debug commands to investigate the page before the offending line, e.g.
Console.WriteLine(Driver.FindElements(locator).Count);
and make sure the locator is finding the right elements, the expected number of elements are found, etc.
You can also put a breakpoint on the line that is causing the problem, run the script in debug mode, and then use the Immediate window to test code.
Other comments
I mentioned in a comment that you should pass By classes around instead of strings and you asked for an example.
public class Button
{
public static void Click(By locator)
{
Find(locator).Click();
}
private static IWebElement Find(By locator)
{
return GCDriver.Instance.FindElement(locator);
}
}
and you would call it like
Button.Click(By.Id("id"));
Your WaitForVisible() method is more complicated than it needs to be. You should read up on ExpectedConditions. There are a lot of already available things to wait for that will take care of 90+% of the waits you will need. I would also pass in a TimeSpan so that you can have control over the wait time instead of always having to wait a hardcoded 30s.
public class GCDriver
{
public static void WaitForVisible(By locator, TimeSpan timeout)
{
new WebDriverWait(GCDriver.Instance, timeout).Until(ExpectedConditions.ElementIsVisible(locator));
}
}
You could even go one step further and pass in a Func that will allow you to wait for anything, not just visible.
public class GCDriver
{
public static void Wait(Func<IWebDriver, IWebElement> func, TimeSpan timeout)
{
new WebDriverWait(GCDriver.Instance, timeout).Until(func);
}
}
and call it like
GCDriver.Wait(ExpectedConditions.ElementIsVisible(locator), TimeSpan.FromSeconds(10));
After hours of troubleshooting I was able to found the cause. Or, at least, part of it.
When I used FindElements instead of FindElement, it counted TWO elements with the same ID. These were not visible when I took a snapshot of the page source at the time of the button click.
However, the reason for this is that the page containing the button has 3 tabs. The button should be the same for all tabs, since it's an "Order Selected" button and the tabs contain lists of things to order. But since unique IDs have only recently been implemented, this page wasn't created with that in mind.
So, the DOM contains at least 2 identical IDs which are not visible in the page source. The strange thing though, is that FindElements should have returned 3 elements and not 2. Or possibly the DOM is even more messy.
Anyway: When I told Selenium to click the SECOND element, like this, it
GCDriver.Instance.FindElements(By.Id(id))[1].Click();
it worked.
Based on most of the answers i have seen, the solution here seems to be the way to go Wait for page load in Selenium . This makes sense, but it isn't working for me. I have a website written in Angular JS and i am trying to navigate through the a paginated list of results. Clicking the link to the next page just reloads a div and doesn't actually trigger a page load. The content on page 1 is different to page 2, but in terms of HTML, there isn't an element i can check for to know if the next page is up or not.
As a temporary fix, i have put a 3 second thread sleep into the navigation so that by the time the thread has woken up, phantomJS has rendered the new content. This is of course a bad fix since it usually takes less than a second, but cold potentially take a few seconds longer some day.
I was wondering if angular communicates with the dom in a way that allows phantomJS to read it and figure out the status?
Usually when tags get refreshed they became stale. You can wait for the <div> tag to become stale using ExpectedConditions
IWebElement div = driver.FindElement(div locator); // locate the refreshing <div>
WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(10));
wait.Until(ExpectedConditions.StalenessOf(div));
And now you need to check that there are items after the previous list was deleted
wait.Until(d =>
{
ReadOnlyCollection<IWebElement> items = d.FindElements(items locator); // locate the items list
return items.Count > 0;
});
I was wondering if it is possible to check with C# and selenium web driver if a certain webpage is opened in the default browser?
My idea is to link certain ticketing system's time tracker with toggl.
For instance - on click of the "Time Track" button in the ticketing system, the program to click the toggl start button programmatically, at the same time.
Yes, it's possible.
You can devise a solution that checks the default window's URL or title.
if (driver.Url == 'http://some_url') { /* you are there */ }
or
if (driver.Title == 'Some Title') { /* the window is open and currently there */ }
Now, if you are running a browser manually using your own browser, then expect Selenium to detect that, then i'm sorry, but that is not possible.
In addition to #sircapsalot answer:
This won't be enough since the goal is to
on click of the "Time Track" button in the ticketing system
First you should be sure that the page has been loaded and the IWebElement is clickable. Without going in some advanced usage (like JS validation of the page state), this should do the work just fine:
var wait = new WebDriverWait(_driver, TimeSpan.FromSeconds(15));
var myElement = wait.Until(x => x.FindElement(By.Id("timeTrackBtnId")));
if(myElement.Displayed)
myElement.Click();
Then go for the
program to click the toggl start button programmatically, at the same time
I'm not sure how you'll sync and how page's JS events will handle this simultaneous actions, but you can try with System.Threading. If the page is created by you maybe this second part (click button, click togl) is better to be handled in the JS code.
I'm automating tests for our webapp in Selenium WebDriver for C#. One of our test scenarios identified an issue with clicking the save button multiple times resulting in multiple identical records.
The standard IWebElement.Click() causes Selenium to block until the page is fully loaded. That means by the time our second click comes around to executing, the postback has been performed and we're not on the form page anymore.
Does anyone know a means of 'manually' clicking an element that won't cause Selenium to block?
You could either wait for a predetermined amount of time for the page to load:
driver.Manage().Timeouts().ImplicitlyWait(TimeSpan.FromSeconds(10));
...or to be more dynamic and wait for your button to appear:
var driver = new WebDriver();
var wait = new WebDriverWait(driver, TimeSpan(0, 1, 0));
wait.Until(d => d.FindElement(By.Id("button"));
Source: https://stackoverflow.com/a/7811812/2006048
Selenium also has source code that is similar to the second method: http://selenium.googlecode.com/svn/trunk/dotnet/src/WebDriver.Support/UI/ExpectedConditions.cs
Let me know if it works out for you. I personally use these options with WatiN:
browser.WaitForComplete();
...or:
browser.WaitUntilContainsText("Text");
It's a shame Selenium does not have the first one.
If we use JavaScript to send our click events, Selenium will not be blocked and we can click multiple times. However, because our click triggers a page load, we cannot reference the element directly. Instead, we need to specify the location to click and then fire our click events.
Because our WebApp uses JQuery, I was able to use the code specified here:
How to simulate a click by using x,y coordinates in JavaScript?
So in the end our C# logic looks something like this:
IWebElement element = driver.findElement(By.id("foobar"));
Point point = element.Location;
IJavascriptExecutor jscript = (IJavascriptExecutor)driver;
jscript.executeScript("$(document.elementFromPoint(arguments[0], arguments[0])).click();", point.X, point.Y);
Although this sends the click event I'm not 100% sure that the element receives it; I'll run some experiments and see.
What you need to do is click via javascript. In java this is done like this:
IJavascriptExecutor executor = (JavascriptExecutor)driver;
executor.executeScript("arguments[0].click();", driver.findElement(By.id("gbqfd")));
executor.executeScript("arguments[0].click();", driver.findElement(By.id("gbqfd")));
I actually imagine it is very similar, this will not block selenium and you should be able to chain a few on before the page comes back.
if this newer approach is too slow you might be quicker doing it all in js, e.g.
IJavascriptExecutor executor = (JavascriptExecutor)driver;
executor.executeScript("document.getElementById(id).click();");
executor.executeScript("document.getElementById(id).click();");
I am not able to Click on SubMenu item using selenium webdriver using c#.
I am using IE9 and FireFox 13.
I have tried Action Builder but it does not work.
It gives an error saying element cannot be clicked.
WebDriverWait Wait = new WebDriverWait(webDriver, TimeSpan.FromSeconds(5));
IWebElement menu = Wait.Until((d) => webDriver.FindElement(By.Id("id1")));
IWebElement menuOption = Wait.Until((d)=>webDriver.FindElement(By.Id("ID2")));
Actions builder = new Actions(webDriver);
builder.MoveToElement(menu).Build().Perform();
Thread.Sleep(5);
//then click when menu option is visible
menuOption.Click();
I have used even javascript :
js.ExecuteScript("return $(\"a:contains('ID1')\").mouseover();"); // Mouse hove to main menu
webDriver.FindElement(By.Id("ID2")).Click();
Please give some solution to click on hidden elements
You could use Expected Conditions to wait for element being clickable after hovering above it (Thread.sleep() is almost always the bad choice. And 5 ms won't be enough.).
The docs for this class (ExpectedConditions in OpenQA.Selenium.Support.UI namespace) are broken as I can see them now, but if you can follow the Java code in the link above, here are the Expected conditions for Java - it's really almost the same in C#, too.
Instead of using the statement Thread.sleep(). You can try to click on the element after making sure that it is displayed.
After you get the WebElement you want to click on , check if it is displayed by using the isDisplayed() method within the ExpectedContition statement about which #Slanec is talking about in the above post.
By this you can make sure you will click on the element only after Wait.Until() returns true. i.e the menuOption is displayed.
I'm writing the code in java as I do not know C#. But I guess you can figure out what I'm trying to say -
new WebDriverWait(driver, 60).until(new ExpectedCondition<Boolean>() {
public Boolean apply(WebDriver driver ) {
return driver.findElement(By.Id("ID2")).isDisplayed();
}
});
I hope that this helps you.