When I click on a button it displays me a form with another buttons and I want to click on one of them. Here is a video with this (really short one), please watch http://screencast.com/t/zyliSemW1s1
So I click on button "Buy Tickets" simply like that:
button.Click();
And then I wait for the next button to be clickable.
I use the next code:
WebDriverWait wait = new WebDriverWait(Driver, TimeSpan.FromSeconds(15));
IWebElement element = wait.Until(ExpectedConditions.ElementToBeClickable(myButton));
After that I click on button that I waited for:
element.Click();
And I get error: Element is not clickable at that point.
As I know, the method ExpectedConditions.ElementToBeClickable() waits for 2 conditions: element is visible and element is enabled.
When I use Thread.Sleep(3000) before clicking on the second button the code works and button is clickable.
I saw similar issue and the solution was to wait for handlers of this button:Selenium Wait doesn't wait for Element to be Clickable
But what to do if I don't know what handles it? I think it handles by jQuery and I use the next code to wait till it stops executing:
var ajaxIsComplete = (bool)
((IJavaScriptExecutor)Driver).ExecuteScript("return jQuery.active == 0");
If it returns "false" I wait and check again.
But it still doesn't work.
So for now my flow goes like that:
I click on button "Buy Tickets"
I wait till jQuery stops executing
I wait till element is clickable using ExpectedConditions.ElementToBeClickable() method
I click on the element and it returns me an error that it is not clickable.
Please guys tell me what is wrong in my flow and how to manage it correct.
Update:
I'm adding HTML code of buttons:
I click to this one:
<button class="btn btn-warning play-now" name="button" type="submit">Buy Tickets</button>
And wait for this one:
<img alt="Credit Card" class="merchant" src="https://numgames-production.s3.amazonaws.com/uploads/merchant/image/21/CC_Offline.png?AWSAccessKeyId=AKIAJ2Q64HPERGHAJJUA&Expires=1470984765&Signature=Qj%2BFSQ3ElctkY6KTMfzp%2FedPjPo%3D">
Denis,
As mentioned in comments to OP, here are a few little extension methods that may help your quest:
public static void WaitForAjax(this IWebDriver driver, int timeoutSecs = 10, bool throwException = false)
{
for (var i = 0; i < (timeoutSecs*10); i++)
{
var javaScriptExecutor = driver as IJavaScriptExecutor;
var ajaxIsComplete = javaScriptExecutor != null && (bool)javaScriptExecutor.ExecuteScript("return jQuery.active == 0");
if (ajaxIsComplete) return;
Thread.Sleep(100);
}
if (throwException)
{
throw new Exception("WebDriver timed out waiting for AJAX call to complete");
}
}
public static bool ElementExists(this IWebDriver driver, By condition)
{
return ElementExists(driver, condition, new TimeSpan());
}
public static bool ElementExists(this IWebDriver driver, By condition, TimeSpan timeSpan)
{
bool isElementPresent = false;
if (timeSpan == default(TimeSpan))
{
timeSpan = TimeSpan.FromMilliseconds(15000);
}
var driverWait = new WebDriverWait(driver, (TimeSpan)timeSpan);
driverWait.IgnoreExceptionTypes(typeof(WebDriverTimeoutException));
isElementPresent = driverWait.Until(x => x.FindElements(condition).Any());
return isElementPresent;
}
public static IWebElement FindElementAfterWait(this IWebDriver driver, By condition, int fromSeconds = 90)
{
bool isElementPresent = false;
IWebElement singleElement = null;
var driverWait = new WebDriverWait(driver, TimeSpan.FromSeconds(fromSeconds));
driverWait.IgnoreExceptionTypes(typeof(WebDriverTimeoutException));
try
{
isElementPresent = driverWait.Until(ExpectedConditions.ElementExists(condition)) != null;
if (isElementPresent)
{
singleElement = driver.FindElement(condition);
}
}
catch
{
// log any errors
}
return singleElement;
}
usages:
bool elementExists = _driver.ElementExists(By.Id("submitButton"));
var submitButton = _driver.FindElementAfterWait(By.Id("submitButton"));
submitButton.Click();
_driver.WaitForAjax();
// then do other code stuff...
Hopefully, a combo of these may get you out of the fix.
This is a typical problem of working with any asynchronous (AJAX) page.
You don't need to use any "magic" methods like sleeps, jquery.active and expected conditions.
Web pages are usually built that way that user would see when operation ends - like some message appears, or some button become enabled. I believe in your case something like this also happens after you click "Buy Tickets" - you need to notice this and wait for this in your code.
That wait needs to be performed using Explicit wait for your specific case. This is the only reliable way to manage asynchronous pages (including elements not yet clickable).
You can see more detailed overview in my blog post - Approaches to handling AJAX in Web Automation (Selenium)
Instead of using ElementToBeClickable, try using presenceOfElementLocated. I think your expected element is not present on DOM, so try using presenceOfElementLocated first. Once it is present on DOM, then use ElementToBeClickable.
// public void test()
{
IWebDriver driver = new ChromeDriver();
ClickSaveButton(driver,"MyButton",10); //Wait for 10 seconds
}
//Customized wait block
public void ClickSaveButton(IWebDriver driver,String ElementID = "" int TimeOut)
{
Console.Error.WriteLine("Waiting....");
try
{
driver.FindElement(By.Id(ElementID)).Click();
}
catch (Exception exception)
{
Thread.Sleep(1000);
if (TimeOut > 0) ClickSaveButton(driver, TimeOut - 1);
}
}
I was facing this problem and checking if the element is clickable, visible, located (or their combinations) etc was not enough. Selenium was still not waiting and trying to click on the element.
The only solution I found for my case was a bad practice, but functional as a workaround. I try to get the element inside a Loop with Try/Catch as found here in Falgun Cont response:
StackExchange - How to wait for element to be clickable in WebDriver with C#
Related
I am automating interaction with a website where the user will have to refresh the pages n times manually (sometimes 3 or 5 or even longer) so that the buttons appear on the web page. To overcome this issue, I created a do / while loop that should refresh the page until the button is visible so it can be clicked. The problem is it goes out of sync and infinitely loops. I tried the script below, but it still doesn't stop refreshing. Any idea how to make it stop refreshing as soon as the element is visible? by default, the element will not be visible, so the user will have to refresh the page first. The refresh works, but it is very quick, and it doesn't give enough time to check the state of visibility of the button, and maybe that's why it goes into an infinite loop
int retries = 0;
bool isElementVisible = false;
do {
await Page.ReloadAsync(new PageReloadOptions() { Timeout = 5000 });
isElementVisible = await Page.IsVisibleAsync("input[name='elementname']");
retries ++;
while (!isElementVisible)
The problem with your code is that IsVisibleAsync will resolve to false immediately.
You could wait for visible with some timeout using WaitForSelectorAsync. For instance, 5 seconds:
int retries = 0;
bool isElementVisible = false;
do {
await Page.ReloadAsync(new PageReloadOptions() { Timeout = 5000 });
try {
// The default State is Visible
await Page.WaitForSelectorAsync("input[name='elementname']", new(){ Timeout = 5000});
isElementVisible = true;
} catch(Exception ex) {
retries ++;
}
} while (!isElementVisible)
Ok here my code and but it immediately executes
private static ChromeDriver mainDriver;
mainDriver.Navigate().GoToUrl(srFetchUrl);
string srPageSource = mainDriver.PageSource;
I have to get the source code after the page is actually navigated to new page and page is loaded
You can try this method, this will wait until page loads completely and you can add your expected time to page load.
public void E_WaitForPageLoad() throws Exception
{
JavascriptExecutor js = (JavascriptExecutor)driver;
//This loop will rotate for 100 times to check If page Is ready after every 1 second.
//You can replace your if you wants to Increase or decrease wait time.
int waittime;
waittime = 60;
for (int i=0; i<waittime; i++)
{
try
{
Thread.sleep(1000);
}catch (InterruptedException e) {}
//To check page ready state.
if (js.executeScript("return document.readyState").toString().equals("complete"))
{
//System.out.println("Wait for Page Load : "+js.executeScript("return document.readyState").toString());
break;
}
}
System.out.println("\nWeb-Page Loaded.");
}
Thank You,
Ed D, India.
Specify , implicit or explicit wait till the element in the page is loaded.
refer this link for C# wait syntax
I have 5 pages (A, B, C, D, E). A, B, C and D each contain a button with Id 'Next' which will load the next page. Now, the problem is that sometimes (not always), the 'Next' button is not clicked on each page.
My code is supposed to do the following: we launch the browser and navigate to page A by URL. Then, we perform some logic on the page and then we click the 'Next' button. We arrive at page B and perform some logic on the page and click the 'Next' button. We arrive at page C, and immediately click the 'Next' button without doing anything else. On page D, we perform some logic and click the 'Next' button. (and so on...)
Now, the problem is that on page C, the 'Next' button is not always clicked, but it does not throw an error for FindElement. So it tries to perform the logic on page D and the web driver crashes because it's still on page C. How can I fix this problem? Notice that I do use a dynamic webdriverWait for the element to be present on the page, but this makes so difference because the same the locator it always the same (ID = 'Next'). Also, notice I don't return my PageObjects - I'm not sure whether this is absolutely required.
Any thoughts?
Here is my code:
public class Page
{
public Page()
{
PageFactory.InitElements(SeleniumTests.driver, this);
}
}
public static class SeleniumTests
{
public static IWebDriver driver { get; set; }
}
class Page_1 : Page
{
[FindsBy(How = How.Id, Using = "Next")]
public void Continue()
{
btnNext.SafeClick();
}
}
class Page_2 : Page
{
[FindsBy(How = How.Id, Using = "Next")]
public void Continue()
{
btnNext.SafeClick();
}
}
class Page_3 : Page
{
[FindsBy(How = How.Id, Using = "Next")]
public void Continue()
{
btnNext.SafeClick();
}
}
class Page_4 : Page
{
[FindsBy(How = How.Id, Using = "Next")]
public void Continue()
{
btnNext.SafeClick();
}
}
class Page_5 : Page
{
[FindsBy(How = How.Id, Using = "Next")]
public void Continue()
{
btnNext.SafeClick();
}
}
public static class SeleniumExtensionMethod
{
public static WebDriverWait wait = new WebDriverWait(SeleniumTests.driver, TimeSpan.FromSeconds(15));
public static void SafeClick(this IWebElement webElement, Double seconds = 15)
{
try
{
wait.Until(ExpectedConditions.ElementToBeClickable(webElement)).Click();
}
catch (System.Reflection.TargetInvocationException ex)
{
Console.WriteLine(ex.InnerException);
}
}
}
And finally:
public partial class Form1 : Form
{
Page_1 pageA = new Page_1();
pageA.PerformSomeLogic();
pageA.Continue();
Page_2 pageB = new Page_2();
pageB.PerformSomeLogic();
pageB.Continue();
Page_3 pageC = new Page_3();
// Don't do anything here, just continue.
pageC.Continue();
Page_4 pageD = new Page_4();
pageD.PerformSomeLogic();// -----> here is crashes, as the previous line 'pageC.Continue()' was not really executed, it seems as though the button was clicked 2 times on page B
pageD.Continue() ;
Page_5 pageE = new Page_5();
pageD.PerformSomeLogic();
pageE.Continue();
}
Edit: what I want, ideally is to do some kind of dynamic wait which actually would work in this case. I can also use Thread.Sleep(); and this solve my problem but it's a code smell and I want to avoid it.
You have 2 options:
make sure the page is loaded and you are on that page
You can add a wait.Until() for a unique element of the page.
get the selector for the button based on the parent page
Since you have Continue() in each page object you could use a css selector to identify the button based on the parent page or based on a unique section from the page that does not exists in the other ones, else what is the point on having the same method with the same selector in every page. You can find this easily by using FireBug. Simply navigate to the page, right click the element, click Inspect Element with FireBug, then right click the element and click 'Copy CSS Path'.
For example:
Lets say you have a div with id='pageC'.
You could use a css selector like #pageC #Next
Your code is overly complex for the task you are attempting to complete.
First get your IWebDriver:
IWebDriver driver = new FirefoxDriver();
You should be using an implicit wait in this scenario, lets start with our timeout set to 15 seconds. This will ensure that driver.FindElements() calls will search for 15 seconds before timing out. If the element is found before the 15 seconds is up, it will stop the wait at that moment.
driver.Manage().Timeouts().ImplicitlyWait(TimeSpan.FromSeconds(15));
Since your next button has the same id on each of the pages, we can use By.Id("Next") to find the next button on each page.
driver.FindElement(By.Id("Next"));
Putting it all together:
IWebDriver driver = new FirefoxDriver();
driver.Manage().Timeouts().ImplicitlyWait(TimeSpan.FromSeconds(15));
for(int page = 1; page < 5; page++) //Iterate through all pages, click next when applicable
{
driver.FindElement(By.Id("Next"));
}
or
IWebDriver driver = new FirefoxDriver();
driver.Manage().Timeouts().ImplicitlyWait(TimeSpan.FromSeconds(15));
if(driver.FindElement(By.Id("Next")).Enabled) //Ensure button is clickable before proceeding
{
driver.FindElement(By.Id("Next"));
}
My guess is that your script is going fast enough that it is clicking the Next button a second time on Page 2 before Page 3 gets a chance to load. One way around this is to wait for the Next button to be stale. The basic logic would be
1. Click the Next button
2. Wait for the Next button to be stale
The code might look something like
public void Continue()
{
btnNext.SafeClick();
btnNext.WaitForStale();
}
where .WaitForStale() is just
wait.until(ExpectedConditions.stalenessOf(btnNext));
Also, unless you've really simplified your Page Objects, there doesn't seem to be a reason to have Page_1 through Page_5. If they all have the same functionality, they should be in a single page and you would just reinstantiate the page each time you move to the next page, e.g.
public partial class Form1 : Form
{
Page_1 pageA = new Page_1();
pageA.PerformSomeLogic();
pageA.Continue();
pageB = new Page_1(); <-- this can be Page_1() or better yet, renamed to ContentPage or something that better represents the type of page rather than the page number.
pageB.PerformSomeLogic();
pageB.Continue();
I'm recording Coded UI Tests with VS 2012, which shall test the functions of a web application.
After I loaded the web page, I click on a button to start p.e. a job application.
After the next page has loaded on the same site, my problem begins.
The entry controls are at the end of the web site.
To take a look and input data into the entry controls, I must scroll down.
The recording produced the following method in the UIMap.
Designer.cs:
public void Scrollen()
{
#region Variable Declarations
Playback.PlaybackSettings.WaitForReadyLevel = WaitForReadyLevel.AllThreads;
this.UIGoogleMozillaFirefoxWindow.UIItemPropertyPage.UIBewerbungDemoFirmaDocument.WaitForControlExist();
this.UIGoogleMozillaFirefoxWindow.UIItemPropertyPage.UIBewerbungDemoFirmaDocument.WaitForControlReady();
Playback.PlaybackSettings.WaitForReadyLevel = WaitForReadyLevel.UIThreadOnly;
WinControl uIBewerbungDemoFirmaDocument = this.UIGoogleMozillaFirefoxWindow.UIItemPropertyPage.UIBewerbungDemoFirmaDocument;
#endregion
// Click "Job application" document
Point pt = new Point(1390, 553);
int count = 0;
while (!uIBewerbungDemoFirmaDocument.TryGetClickablePoint(out pt) && count < 20)
{
count++;
System.Threading.Thread.Sleep(10);
if (count == 20)
Console.WriteLine("ClickablePoint not found");
}
Mouse.Click(uIBewerbungDemoFirmaDocument, new Point(1390, 553));
Mouse.MoveScrollWheel(10);
}
As You can see, I tried WaitForControlExist, WaitForControlReady, TryGetClickablePoint and the method MoveScrollWheel.
But neither Mouse.Click nor Mouse.MoveScrollWheel are working.
And in the next method, where I click into the first of the entry fields, I get a message at execution time, that the click event produces an error, because the control is hidden (because it's down below on the website, out of visible range).
After several tests this is making me crazy.
Any idea what has gone wrong and how can I scroll down the web site, so my entry controls are in visible range?
You can try Control.EnsureClickable(). Or you can use below mentioned function to scroll the page until the control is not clickable.
public static void ScrollAndClick(HtmlControl Control)
{
bool isClickable = false;
if (Control.TryFind())
{
while (!isClickable)
{
try
{
Control.EnsureClickable();
Mouse.Click(Control);
isClickable = true;
}
catch (FailedToPerformActionOnHiddenControlException)
{
Mouse.MoveScrollWheel(-1);
throw;
}
}
}
else
{
throw new AssertInconclusiveException("Control Not Found");
}
}
You can also add condition related to timeout to make sure it don't go to infinite loop.
Let me know if you are having issue with this at your end.
So I'm working with selenium firefox webdrivers in c# winform and I have this code below to get the handle of the popup that shows when you click on the "webtraffic_popup_start_button" and it should get the handle of the popup but the popup handle is same as current one.
string current = driver.CurrentWindowHandle;
driver.FindElement(By.XPath("//*[#id='webtraffic_popup_start_button']")).Click();
Thread.Sleep(Sleep_Seconds);
popup = driver.CurrentWindowHandle;
Thread.Sleep(3000);
driver.SwitchTo().Window(current);
Thread.Sleep(1000);
Any help with this would be much appreciated thank you
This is what pop up looks like.
WebDriver does absolutely no tracking whatsoever to detect which window is actually in the foreground in the OS, and does no automatic switching when new browser windows are opened. That means the proper way to get the handle of a newly-opened popup window is a multi-step process. To do so, you would:
Save the currently-focused window handle into a variable so that you
can switch back to it later.
Get the list of currently opened window handles.
Perform the action that would cause the new window to appear.
Wait for the number of window handles to increase by 1.
Get the new list of window handles.
Find the new handle in the list of handles.
Switch to that new window.
In code using the .NET language bindings, that would look something like this:
string currentHandle = driver.CurrentWindowHandle;
ReadOnlyCollection<string> originalHandles = driver.WindowHandles;
// Cause the popup to appear
driver.FindElement(By.XPath("//*[#id='webtraffic_popup_start_button']")).Click();
// WebDriverWait.Until<T> waits until the delegate returns
// a non-null value for object types. We can leverage this
// behavior to return the popup window handle.
WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(5));
string popupWindowHandle = wait.Until<string>((d) =>
{
string foundHandle = null;
// Subtract out the list of known handles. In the case of a single
// popup, the newHandles list will only have one value.
List<string> newHandles = driver.WindowHandles.Except(originalHandles).ToList();
if (newHandles.Count > 0)
{
foundHandle = newHandles[0];
}
return foundHandle;
});
driver.SwitchTo().Window(popupWindowHandle);
// Do whatever you need to on the popup browser, then...
driver.Close();
driver.SwitchTo().Window(currentHandle);
Alternatively, if you're using the .NET bindings, there's a PopupWindowFinder class in the WebDriver.Support assembly that is specifically designed to do these operations for you. Using that class is much simpler.
// Get the current window handle so you can switch back later.
string currentHandle = driver.CurrentWindowHandle;
// Find the element that triggers the popup when clicked on.
IWebElement element = driver.FindElement(By.XPath("//*[#id='webtraffic_popup_start_button']"));
// The Click method of the PopupWindowFinder class will click
// the desired element, wait for the popup to appear, and return
// the window handle to the popped-up browser window. Note that
// you still need to switch to the window to manipulate the page
// displayed by the popup window.
PopupWindowFinder finder = new PopupWindowFinder(driver);
string popupWindowHandle = finder.Click(element);
driver.SwitchTo().Window(popupWindowHandle);
// Do whatever you need to on the popup browser, then...
driver.Close();
// Switch back to parent window
driver.SwitchTo().Window(currentHandle);
If the lastly opened window is your target then simply do the following after the click
driver.SwitchTo().Window(driver.WindowHandles.ToList().Last());
EDIT
//You may need to go back to parent window to perform additional actions;
// to the new window
driver.SwitchTo().Window(driver.WindowHandles.ToList().Last());
// to the new window
driver.SwitchTo().Window(driver.WindowHandles.ToList().First());
//or
driver.SwitchTo().DefaultContent();
I've got some code you might like. The quickest solution is to use Popup Finder, but I've made my own method as well. I would never rely on the order the Window Handles are in to select the appropriate window. Popup Window Finder:
PopupWindowFinder finder = new PopupWindowFinder(driver);
driver.SwitchTo().Window(newWin);
My Custom method. Basically you pass it the element you want to click, your webdriver, and optionally the time to wait before searching after you click the element.
It takes all of your current handles and makes a list. It uses that list to eliminate the previously existing windows from accidentally getting switched to. Then it clicks the element that launches the new window. There should always be some sort of a delay after the click, as nothing happens instantly. And then it makes a new list and compares that against the old one until it finds a new window or the loop expires. If it fails to find a new window it returns null, so if you have an iffy webelement that doesn't always work, you can do a null check to see if the switch worked.
public static string ClickAndSwitchWindow(IWebElement elementToBeClicked,
IWebDriver driver, int timer = 2000)
{
System.Collections.Generic.List<string> previousHandles = new
System.Collections.Generic.List<string>();
System.Collections.Generic.List<string> currentHandles = new
System.Collections.Generic.List<string>();
previousHandles.AddRange(driver.WindowHandles);
elementToBeClicked.Click();
Thread.Sleep(timer);
for (int i = 0; i < 20; i++)
{
currentHandles.Clear();
currentHandles.AddRange(driver.WindowHandles);
foreach (string s in previousHandles)
{
currentHandles.RemoveAll(p => p == s);
}
if (currentHandles.Count == 1)
{
driver.SwitchTo().Window(currentHandles[0]);
Thread.Sleep(100);
return currentHandles[0];
}
else
{
Thread.Sleep(500);
}
}
return null;
}