Handling Lazy loading with webdriver in C# - c#

I am trying test a single page web application with webdriver in page object model using C#. The website javascript intensive and does Lazy loading
i have tried using Explicit wait and used the following code to check if the javascript is active
return jQuery.active == 0
I have tried using a combination of both to know if the page has loaded
WebDriverWait _wait = new WebDriverWait(Driver, TimeSpan.FromSeconds(25));
bool WhenToExit = true;
while (WhenToExit) // Handle timeout somewhere
{
var ajaxIsComplete = (bool)(Browser.Driver as IJavaScriptExecutor).ExecuteScript("return jQuery.active == 0");
if (ajaxIsComplete)
{
bool isDisplayed = _wait.Until(d => d.FindElement(By.ClassName("example"))).Displayed;
if(isDisplayed)
{
break;
}
}
Thread.Sleep(100);
int Timer = 0;
Timer++;
if (Timer == 100)
{
WhenToExit = false;
}
}
.But still the webdriver does not wait for the page to load , it just keeps executing the steps ,So i have been forced to use Thread.Sleep(), which i dont want use and its not good practices.
Can some tell me how to Get around this issue,Thank u in advance

Related

What is the best way to check a url is valid every second (or less), with Task await or ContinueWith in a C# Window Forms Application (.NET)

I'm new to C# .Net and Visual Studio 2022 - What I'm trying to achieve is to have a timer running every second to check that a website url is valid/is up. If the url IS reachable and the current WebView2 is not showing that website, then it should navigate to it. If it's already showing that website, it should do nothing else. If it was showing that website, but now it's no longer valid, the WebView should navigate to my custom error page. If whilst on the custom error page the website becomes available again, it should (re)load the website.
In my particular scenario I'm making a webView load localhost (127.0.0.1) for now. I want to continuously check the website is ip, and if it goes down, show custom error, if it comes back, show the website.
Not sure I'm explaining that very well. From the research I have done, I believe I need Task and also await using async method.
Here's my current timer and checkurl code as well as navigtionstarted and navigationcompeted:
private void webView_NavigationStarting(object sender, CoreWebView2NavigationStartingEventArgs e)
{
timerCheckRSLCDURL.Enabled = false;
}
private void webView_NavigationCompleted(object sender, Microsoft.Web.WebView2.Core.CoreWebView2NavigationCompletedEventArgs e)
{
if (e.IsSuccess)
{
Debug.WriteLine("JT:IsSuccess");
((Microsoft.Web.WebView2.WinForms.WebView2) sender).ExecuteScriptAsync("document.querySelector('body').style.overflow='hidden'");
}
else if (!e.IsSuccess)
{
Debug.WriteLine("JT:IsNOTSuccess");
webView.DefaultBackgroundColor = Color.Blue;
//webView.CoreWebView2.NavigateToString(Program.htmlString);
}
timerCheckRSLCDURL.Enabled = true;
}
private void timerCheckRSLCDURL_Tick(object sender, EventArgs e)
{
Debug.WriteLine("Timer Fired! Timer.Enabled = " + timerCheckRSLCDURL.Enabled);
CheckURL(Properties.Settings.Default.URL, Properties.Settings.Default.Port);
}
private async void CheckURL(string url, decimal port)
{
timerCheckRSLCDURL = false;
Program.isWebSiteUp = false;
string webViewURL = BuildURL();
Debug.WriteLine("Checking URL: " + webViewURL);
try
{
var request = WebRequest.Create(webViewURL);
request.Method = "HEAD";
var response = (HttpWebResponse) await Task.Factory.FromAsync < WebResponse > (request.BeginGetResponse, request.EndGetResponse, null);
if (response.StatusCode == HttpStatusCode.OK)
{
Program.isWebSiteUp = true;
}
}
catch (System.Net.WebException exception)
{
Debug.WriteLine("WebException: " + exception.Message);
if (exception.Message.Contains("(401) Unauthorized"))
{
Program.isWebSiteUp = false;
}
else
{
Program.isWebSiteUp = false;
} // This little block is unfinished atm as it doesn't really affect me right now
}
catch (Exception exception)
{
Debug.WriteLine("Exception: " + exception.Message);
Program.isWebSiteUp = false;
}
if (Program.isWebSiteUp == true && webView.Source.ToString().Equals("about:blank"))
{
Debug.WriteLine("JT:1");
Debug.WriteLine("isWebSiteUp = true, webView.Source = about:blank");
webView.CoreWebView2.Navigate(webViewURL);
}
else if (Program.isWebSiteUp == true && !webView.Source.ToString().Equals(webViewURL))
{
Debug.WriteLine("JT:2");
Debug.WriteLine("isWebSiteUp = true\nwebView.Source = " + webView.Source.ToString() + "\nwebViewURL = " + webViewURL + "\nWebView Source == webViewURL: " + webView.Source.ToString().Equals(webViewURL) + "\n");
webView.CoreWebView2.Navigate(webViewURL);
}
else if (Program.isWebSiteUp == false && !webView.Source.ToString().Equals("about:blank"))
{
Debug.WriteLine("JT:3");
Debug.WriteLine("This SHOULD be reloading the BSOD page!");
webView.CoreWebView2.NavigateToString(Program.htmlString);
}
}
private string BuildURL()
{
string webViewURL;
string stringURL = Properties.Settings.Default.URL;
string stringPort = Properties.Settings.Default.Port.ToString();
string stringURLPORT = $ "{stringURL}:{stringPort}";
if (stringPort.Equals("80"))
{
webViewURL = stringURL;
}
else
{
webViewURL = stringURLPORT;
}
if (!webViewURL.EndsWith("/"))
{
webViewURL += "/";
}
//For now, the URL will always be at root, so don't need to worry about accidentally
//making an invalid url like http://example.com/subfolder/:port
//although potentially will need to address this at a later stage
Debug.WriteLine("BuildURL returns: " + webViewURL);
return webViewURL;
}
So the timer is fired every 1000ms (1 second) because I need to actively check the URL is still alive. I think the way I'm controlling the timer is wrong - and I imagine there's a better way of doing it, but what I want to do is this...
Check website URL every 1 second
To avoid repeating the same async task, I'm trying to disable the timer so it does not fire a second time whilst the async checkurl is running
Once the async/await task of checking the url has finished, the timer should be re-enabled to continue monitoring is the website url is still up
If the website is down, it should show my custom error page (referred to as BSOD) which is some super basic html loaded from resources and 'stored' in Program.htmlString
if the the website is down, and the webview is already showing the BSOD, the webview should do nothing. The timer should continue to monitor the URL.
if the website is up and the webview is showing the BSOD, then it should navigate to the checked url that is up. If the website is up, and the webview is already showing the website, then the webview should do nothing. The timer should continue to monitor the URL.
From other research, I'm aware I shouldn't be using private async void - eg shouldn't be using it as a void. But I've not yet figured out / understood the correct way to do this
In the Immediate Window, it appears that webView_NavigationCompleted is being fired twice (or sometimes even a few times) instantly as the immediate window output will show JT:IsSuccess or JT:IsNOTSuccess a few times repeated in quick succession. Is that normal? I'm assuming something isn't correct there.
The main problem appears to be due to the timer being only 1 second. If I change the timer to fire every 30 seconds for example, it seems to work ok, but when it's every second (I may even need it less than that at some point) it's not really working as expected. Sometimes the BSOD doesn't load at all for example, as well as the webView_NavigationCompleted being fire multiple times in quick succession etc.
Could someone pretty please help me make this code better and correct.
I've searched countless websites etc and whilst there is some good info, some of it seems overwhelming / too technical so to speak. I had to lookup what "antecedent" meant earlier as it's a completely new word to me! :facepalm:
Many thanks inadvance
This answer will focus on the Task timer loop to answer the specific part of your question "check a url is valid every second". There are lots of answers about how to perform the actual Ping (like How do you check if a website is online in C#) and here's the Microsoft documentation for Ping if you choose to go that route.
Since it's not uncommon to set a timeout value of 120 seconds for a ping request, it calls into question whether it would have any value to do this on a steady tick of one second. My suggestion is that it would make more sense to:
Make a background thread
Perform a synchronous ping (wait for the result) on the background thread.
Marshal the ping result onto the UI thread to perform the other tasks you have laid out.
Synchronously wait a Task.Delay on the background thread before performing the next ping.
Here is how I personally go about doing that in my own production code:
void execPing()
{
Task.Run(() =>
{
while (!DisposePing.IsCancellationRequested)
{
var pingSender = new Ping();
var pingOptions = new PingOptions
{
DontFragment = true,
};
// https://learn.microsoft.com/en-us/dotnet/api/system.net.networkinformation.ping?view=net-6.0#examples
// Create a buffer of 32 bytes of data to be transmitted.
string data = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
byte[] buffer = Encoding.ASCII.GetBytes(data);
int timeout = 120;
try
{
// https://stackoverflow.com/a/25654227/5438626
if (Uri.TryCreate(textBoxUri.Text, UriKind.Absolute, out Uri? uri)
&& (uri.Scheme == Uri.UriSchemeHttp ||
uri.Scheme == Uri.UriSchemeHttps))
{
PingReply reply = pingSender.Send(
uri.Host,
timeout, buffer,
pingOptions);
switch (reply.Status)
{
case IPStatus.Success:
Invoke(() => onPingSuccess());
break;
default:
Invoke(() => onPingFailed(reply.Status));
break;
}
}
else
{
Invoke(() => labelStatus.Text =
$"{DateTime.Now}: Invalid URI: try 'http://");
}
}
catch (Exception ex)
{
// https://stackoverflow.com/a/60827505/5438626
if (ex.InnerException == null)
{
Invoke(() => labelStatus.Text = ex.Message);
}
else
{
Invoke(() => labelStatus.Text = ex.InnerException.Message);
}
}
Task.Delay(1000).Wait();
}
});
}
What works for me is initializing it when the main window handle is created:
protected override void OnHandleCreated(EventArgs e)
{
base.OnHandleCreated(e);
if (!(DesignMode || _isHandleInitialized))
{
_isHandleInitialized = true;
execPing();
}
}
bool _isHandleInitialized = false;
Where:
private void onPingSuccess()
{
labelStatus.Text = $"{DateTime.Now}: {IPStatus.Success}";
// Up to you what you do here
}
private void onPingFailed(IPStatus status)
{
labelStatus.Text = $"{DateTime.Now}: {status}";
// Up to you what you do here
}
public CancellationTokenSource DisposePing { get; } = new CancellationTokenSource();
Example 404:

How to wait for one of multiple elements to be displayed first using Selenium?

I'm very new to Selenium so I'm looking for some insight on how to achieve this.
Essentially, I want my driver to wait for one of multiple elements to be visible. This is because the website can produce mixed results when using automation, so I want to treat this like it's "error handling" (if that makes sense).
Basically, if element a is visible first, do this. However, if element b is visible first, do that.
How would I be able to achieve this using the Selenium webdriver for C#? I read that combining the elements' XPath value by using "|" worked, however when I tried it with my application, I had no luck.
Any answers are greatly appreciated! Thank you in advance!
To me this is kind of tough because your elements could be displayed within a split second of each other. Wonder if you could put this in a case and break when you get one?
So what I was thinking was seeing if the element was present first.
public static bool IsElementPresent_byXpath(string xpath)
{
bool result;
try { result = Driver.FindElement(By.XPath(xpath)).Displayed; }
catch (NoSuchElementException) { return false; }
catch (StaleElementReferenceException) { return false; }
return result;
}
So if you do it this way, then you can write something like the below but my concern is that if the page is not changing, ele1, ele2 and ele3 could be invoked. So this code would need to be changed.
bool ele1 = Browser.IsElementPresent_byXpath("//[#id='1']");
bool ele2 = Browser.IsElementPresent_byXpath("//[#id='2']");
bool ele3 = Browser.IsElementPresent_byXpath("//[#id='3']");
if (ele1)
{
//do this
}
else if (ele2 || ele1 == false)
{
//do this
}
else if (ele3 || ele1 == false || ele2 == false)
{
//do this
}
I would go with Wait.Until, so that I can combine the benefits of waiting with set timeout for an event, and returning me matching locator, so I can base my logic on the result.
This is java code, but C# has equivalents.
Assuming I am waiting for either firstBy, secondBy or thirdBy:
By locatorOfElementThatIsPresent = new WebDriverWait(webDriver, timeoutInSeconds).<By>until(driver -> {
// findElements does not throw when element not found, so I skip try-catch
if (driver.findElements(firstBy).size() > 0) return firstBy;
if (driver.findElements(secondBy).size() > 0) return secondBy;
if (driver.findElemetns(thirdBy).size() > 0) return thirdBy;
return null; // neither found, so the method will be retried until timeout
});

How do I pass the C # Selenium Error Page?

I'm writing a bot with c # selenium. (The working logic of the bot is simply that there are 20 companies on each page, they go back to the detail page and get the data back. They go through all the companies in order. After getting the data of the last company, they continue to the next page.) After visiting 200-250 companies, the page in the picture opens. Bot's stopping progress. If I press the F5 menu manually, the bot continues to progress, but it doesn't work when we try with the code.
How do I resolve this error?
Error Page
I noticed it was on the way back from the detail page of this page. To go back;
driver.navigate().Back();
driver.navigate().GoToUrl("");
//I tried to go back with the codes but the solution was not.
I get this Error because the error page does not pass.
Bot needs to visit all companies without encountering an error page.
A correct approach for this is to wait for some amount of time for some element you expect on the page using WebDriverWait.
In this example, I wait for 10 seconds and look for element id 'some-id'.
You can change the criteria by replacing By.Id("some-id") with some other condition.
More about By class.
var wait = new WebDriverWait(driver, TimeSpan.FromSeconds(10));
driver.Navigate().GoToUrl("https://www.somedomain.com");
var validPage = false;
try
{
validPage = wait.Until(c =>
{
try
{
return driver.FindElement(By.Id("some-id")) != null;
}
catch
{
return false;
}
});
}
catch
{
// not exist
}
if (validPage == true)
{
// ok.
}
else
{
}

Check if element is clickable in Selenium ChromeDriver

I am trying to find an element by XPath, and if it is not found then execute the function again. It seems to be getting stuck on the finding element function, and I'm not sure why. I have this working on another site with the same method. Here are my attempts:
Attempt 1:
while (url == "https://drygoods.phish.com/dept/posters-prints-and-paper-goods")
try
{
driver[task].FindElement(By.XPath($"//img[contains(#alt, '{Keyword}')]")).Click();
}
catch
{
Thread.Sleep(1000);
DryGoodsFindProductKeyword(Keyword, task);
}
Attempt 2:
if (driver[task].Url != "https://drygoods.phish.com/dept/posters-prints-and-paper-goods")
{
driver[task].FindElement(By.XPath("//div[2]/div[2]/div/button")).Click();
driver[task].Url = "https://drygoods.phish.com/cart/";
//SolveCaptcha(task);
driver[task].FindElement(By.Id("GoToCheckout")).Click();
MessageBox.Show("Click Checkout");
Thread.Sleep(5000);
}
else
{
driver[task].FindElement(By.XPath($"//img[contains(#alt, '{Keyword}')]")).Click();
WebDriverWait wait = new WebDriverWait(driver[task], TimeSpan.FromSeconds(5));
wait.Until(ExpectedConditions.UrlContains("product"));
Thread.Sleep(1000);
DryGoodsFindProductKeyword(Keyword, task);
Here is my full method as well:
public void DryGoodsFindProductKeyword(string Keyword, int task)
{
String url = driver[task].Url;
driver[task].Url = "https://drygoods.phish.com/dept/posters-prints-and-paper-goods";
if (driver[task].Url != "https://drygoods.phish.com/dept/posters-prints-and-paper-goods")
{
driver[task].FindElement(By.XPath("//div[2]/div[2]/div/button")).Click();
driver[task].Url = "https://drygoods.phish.com/cart/";
//SolveCaptcha(task);
driver[task].FindElement(By.Id("GoToCheckout")).Click();
MessageBox.Show("Click Checkout");
Thread.Sleep(5000);
}
else
{
driver[task].FindElement(By.XPath($"//img[contains(#alt, '{Keyword}')]")).Click();
WebDriverWait wait = new WebDriverWait(driver[task], TimeSpan.FromSeconds(5));
wait.Until(ExpectedConditions.UrlContains("product"));
Thread.Sleep(1000);
DryGoodsFindProductKeyword(Keyword, task);
}
}
Thank you in advance for the help! Please let me know if I can add anymore information.
To make sure the page is loaded instead of just your URL changing you can use
wait.Until(d => ((IJavaScriptExecutor)d).ExecuteScript("return document.readyState").Equals("complete"));
Maybe your element isn't displayed yet or it isn't enabled. They're both properties on your IWebElement. You could use this to see if it's clickable:
wait.Until(ExpectedConditions.ElementToBeClickable(byLocatorGoesHere)).Click();
If it's slightly out of view you may need to use Javascript to click it (or just scroll it into view).

Selenium RC Reminiscing the "End of the World"

To explain the title.. Selenium RC keeps insisting that
A system shutdown has already been scheduled
and refusing to conduct automated tests because of this.. I can understand the logic here, I mean you're not going to do your homework if you thought the world would end with a nuclear holocaust...
However.. this is not the Cold War and when I inspect several basic things (such as using shutdown \a, completing a full restart) I find that this is actually not the case!
How can I convince selenium that the world is not going to end and that it should probably do the work I'm telling it to?
N.B. Here, selenium has "refused" to initialise any instance of IE, and will continue to hang until it times out, regardless of clicking Yes or No. I'm using NUnit, c#/.net4.0 to control the tests.
To fix this I replaced the default "runSeleniumTest" function with the below patched version as a user extension:
function runSeleniumTest() {
runOptions = new RemoteRunnerOptions();
var testAppWindow;
if (runOptions.isMultiWindowMode()) {
try{
testAppWindow = openSeparateApplicationWindow('Blank.html', true);
}
catch (e) {
window.onunload = function () { };
window.location.reload();
return;
}
} else if (sel$('selenium_myiframe') != null) {
var myiframe = sel$('selenium_myiframe');
if (myiframe) {
testAppWindow = myiframe.contentWindow;
}
}
else {
proxyInjectionMode = true;
testAppWindow = window;
}
selenium = Selenium.createForWindow(testAppWindow, proxyInjectionMode);
if (runOptions.getBaseUrl()) {
selenium.browserbot.baseUrl = runOptions.getBaseUrl();
}
if (!debugMode) {
debugMode = runOptions.isDebugMode();
}
if (proxyInjectionMode) {
LOG.logHook = logToRc;
selenium.browserbot._modifyWindow(testAppWindow);
}
else if (debugMode) {
LOG.logHook = logToRc;
}
window.selenium = selenium;
commandFactory = new CommandHandlerFactory();
commandFactory.registerAll(selenium);
currentTest = new RemoteRunner(commandFactory);
var doContinue = runOptions.getContinue();
if (doContinue != null) postResult = "OK";
currentTest.start();
}
I found that the "a system shutdown has already been scheduled" error occurred inside of "openSeparateApplicationWindow". I also found that refreshing the selenium test runner window after the error occurred would "restart" the test without the error. Therefore, I patched the "runSeleniumTest" with the following try catch statement so the test runner window reloads if there's an error in "openSeparateApplicationWindow":
try{
testAppWindow = openSeparateApplicationWindow('Blank.html', true);
}
catch (e) {
window.onunload = function () { };
window.location.reload();
return;
}
I also used my blog post for a more specific example of selenium user extensions
Selenium isn't doing anything in this case. That's the IE HTA agent (a built-in Windows process) that's preventing you from doing anything. Perhaps rebooting the machine will do the trick? It looks like you may just have a pending Windows update that's scheduled a future reboot.

Categories

Resources