How to wait until selenium completes its navigation? - c#

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

Related

Do/While infinitely loops issue c#

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)

C# WebBrowser stuck on navigating when used in for loop

I have a for loop that changes the URL
for (int i = 1; i < max; i += 50)
{
completed = false;
string currkey = country;
crawler.Navigate(new Uri("http://www.example.net/func.php?dom=" + currkey + "&key=&start=" + i));
Console.WriteLine("Navigating to " + "http://www.example.net/func.php?dom=" + currkey + "&key=&start=" + i);
while (!completed)
{
Application.DoEvents();
Thread.Sleep(500);
}
}
This is my documentcompleted handler
crawler.Refresh();
Console.WriteLine("Getting universities");
getUniversities();
Console.WriteLine("Finished getting universities");
completed = true;
When i get rid of the for loop and use a single link, it seems to navigate to the website correctly, but when i use for loop to load websites in order, it seems that the web browser gets stuck in the second iteration.
Example:
currkey = United States
In the first iteration, the website link will be http://www.example.net/func.php?dom="United States"&key=&start=1, and on the next one it will be http://www.example.net/func.php?dom="United States"&key=&start=51. The navigation gets stuck when trying to load the second link.
I have used the boolean completed to note that the current iteration is finished, but it is still stuck.
Any kind of help is appreciated
Your Thread.Sleep call is blocking the WebBrowser from continuing to load. What you should be doing is attaching to the DocumentCompleted event, and then loading the next page. Please don't use this while/sleep combination in WinForms - you should use the events that the controls expose.
Attach the event:
crawler.DownloadCompleted += CrawlerDocumentCompleted;
Event handler:
private void CrawlerDocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
//The document has loaded - now do something
}
A final thought
As it looks like you are implementing a crawler, why are you using the WebBrowser control in WinForms to navigate. Surely all you are interested in is the html that the server serves up? Or is the page using JavaScript to load additional elements into the DOM, requiring you to use the WebBrowser?
You could use the WebClient class and the DownloadString or DownloadStringAsync methods. See https://msdn.microsoft.com/en-us/library/fhd1f0sw(v=vs.110).aspx

C# webBrowser.Document.GetElementByTagName index

the C# webBrowser form object allows me to access links within a page by their html Tag name.
By storing all of the links of a page in a collection
HtmlElementCollection links = webBrowser1.Document.GetElementsByTagName("a");
I can then cycle through them by their index and have the browser "click" on the link
links[i].InvokeMember("Click");
So, this index works with int, until this strange underhanded error pops, saying the value must be between 0 and -1 (-__-)
private void executeExtraction()
{
HtmlElementCollection links = webBrowser1.Document.GetElementsByTagName("a");
try
{
isDone = false;
Task find = Task.Factory.StartNew(() => links[i].InvokeMember("Click"));
}
catch (Exception e)
{
Debug.Print(e.ToString());
}
i++;
toolStripTextBox1.Text = i.ToString();
}
the "int i" is publicly declared outside this method
EDIT - Okay I found my problem...
it's a timer that is calling executeExtraction, so every tick, the HtmlElementCollection "links" gets created.
then a task takes links[i].InvokeMember("Click") on a new thread.
So, it had to do with the threading. the "links" list gets broken when it tries to access while an other thread is busy with it.
How do I solve this ?
thanks

Selenium WaitDriver doesn't wait for element to be clickable

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#

While loop program flow

I'm having trouble with my program flow in a while loop I created.
while (reader.Read())
{
// Store scenario information
int Id = (int)reader["ScenarioID"];
string Data = reader["ScenarioData"].ToString();
string Url = "http://google.com";
// Initialize result information
int HasSucceeded = 0;
var screenshot = new Byte[] { };
// Navigate to webBrowser
webBrowser2.Navigate(Url);
webBrowser2.DocumentCompleted += WebBrowserDocumentCompleted;
// Do test
TestScenarios(Url, HasSucceeded);
// Take screenshot
TakeScreenshot(screenshot);
// Insert results
InsertResults(Id, HasSucceeded, screenshot);
// Mark scenario for deletion
MarkScenario(Id);
}
private void WebBrowserDocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs Url)
{
MessageBox.Show("Operation has completed!");
}
The expected flow of the program should be
Read an item in the table
Initialize some variables/store some values
Navigate the webBrowser control toe the URL
When the webBrowser control is finished, do a test
Take a screenshot
Insert results into new table
Mark the item in the original table for deletion
Loop back to #1 until all items have been covered.
However, what is happening is everything in the while loop is running properly in order except for the webBrowser2.Navigate line, which does not show the Url until the while loop has exited. Immediately after the Url shows, 5 sequential messages "Operation has completed" (for the 5 items in the table) appear. How can I fix my flow?
Try this solution. Wrap your loop in another thread than UI thread. then make use of AutoResetEvent
new Thread(() =>
{
AutoResetEvent signal = new AutoResetEvent(false);
while (reader.Read())
{
// Store scenario information
int Id = (int)reader["ScenarioID"];
string Data = reader["ScenarioData"].ToString();
string Url = "http://google.com";
// Initialize result information
int HasSucceeded = 0;
var screenshot = new Byte[] { };
Action action = () =>
{
webBrowser2.Tag = signal;
// Navigate to webBrowser
webBrowser2.Navigate(Url);
webBrowser2.DocumentCompleted -= WebBrowserDocumentCompleted;
webBrowser2.DocumentCompleted += WebBrowserDocumentCompleted;
};
webBrowser2.Invoke(action);
signal.WaitOne();//Wait till it finishes
// Do test
TestScenarios(Url, HasSucceeded);
// Take screenshot
TakeScreenshot(screenshot);
// Insert results
InsertResults(Id, HasSucceeded, screenshot);
// Mark scenario for deletion
MarkScenario(Id);
}
}).Start();
private void WebBrowserDocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs Url)
{
MessageBox.Show("Operation has completed!");
((AutoResetEvent)((WebBrowser)sender).Tag).Set();
}
I asked worker thread to wait till the document loads then continue execution. simple.
Hope this helps
The Navigate method is probably queuing an event which will be later handled on the same thread your code is running in (the UI thread). You may have to put your code into a separate background worker thread to allow the UI events to be processed before your loop is finished.
I recomend you to ckeck the async and await operation if you are devleloping in .NET 4.5 Frammework. This propably will solve your problem.
Async and Await in MSDN

Categories