How can I get EvaluateScriptAsync() in CefSharp working? - c#

I have a database of URLs and the screen will load 9 URLs at once like a table and load another 9 URLs after 5 minutes. I have some script to run after each ULR finished loading using the FrameLoadEnd Event Handler. I keep getting this error when the 19th URL is loading.
unable to execute javascript at this time, scripts can only be executed within a v8context. use the iwebbrowser.canexecutejavascriptinmainframe property to guard against this exception
If there is a mistake, this error should pop up when the 1st URL is loaded but it always pops up when the 19th URL is loaded which is the third round the screen display 9 new URLs.
Here is the Code
wb.FrameLoadEnd += OnBrowserFrameLoadEnd;
private async void OnBrowserFrameLoadEnd(object sender, FrameLoadEndEventArgs e)
{
ChromiumWebBrowser wb = (ChromiumWebBrowser)sender;
await wb.EvaluateScriptAsync("document.body.style.overflow='hidden';");
int clientHeight = 0;
int clientWidth = 0;
int innerHeight = 0;
int innerWidth = 0;
string clientRecHeightScript = #"function myFunction() {" +
"var rect = document.body.getBoundingClientRect();" +
"ch = rect.height;" +
"var rect = document.body.getBoundingClientRect();" +
"cw = rect.width;" +
"var ih = window.innerHeight;" +
"var iw = window.innerWidth;" +
"const webLength = [ch, cw, ih, iw];" +
"return webLength;" +
"}" +
"myFunction();";
await wb.EvaluateScriptAsync(clientRecHeightScript).ContinueWith(x => //Error occurs here
{
var response = x.Result;
if (response.Result != null)
{
var clientRec = response.Result;
string[] arr = ((System.Collections.IEnumerable)clientRec).Cast<object>()
.Select(y => y.ToString())
.ToArray();
Int32.TryParse(arr[0], out clientHeight);
Int32.TryParse(arr[1], out clientWidth);
Int32.TryParse(arr[2], out innerHeight);
Int32.TryParse(arr[3], out innerWidth);
}
});
}
Is there any error I might have done to cause this error?

Does the webpage have JavaScript on it? If not then it won't have a V8Context.
It's also possible the V8Context hasn't finished loading for the main frame. FrameLoadEnd is called once per frame. Sub frames may finish loading first.
The website failed to load, you can check the http://cefsharp.github.io/api/91.1.x/html/P_CefSharp_FrameLoadEndEventArgs_HttpStatusCode.htm
A navigation has occurred and the V8Context has been freed . If Load is called or the html document navigates internally you the frame for which FrameLoadEnd was called may no longer have a valid V8Context.
You can evaluate JavaScript directly against the frame to avoid the check. The FrameLoadEndEventArgs.Frame property can be used to access the frame. You can then directly call IFrame.EvaluateScriptAsync

Related

Selenium and Aurelia, how make test wait for the page to load and HTTP calls to complete

I would like make the Test to wait until the page has loaded properly before proceeding with the next step. I used wait until but,
var wait = new WebDriverWait(driver, TimeSpan.FromSeconds(time));
var elements = wait.Until(x => x.FindElements(by));
I am facing the problem of the element being present when the data has not been fully loaded yet.This is making the test to fail. I don't want to use the Thread.Sleep(time); since it's not a very good approach to this problem.
Is there any option like the one provided by jQuery like "jQuery.active"
I am using C# with webdriver 3 to write that test. I would like to make the test to wait for the page to complete loading and all the HTTP calls to done before proceeding with the next step is there any way to do this ?. Thank you in advance
Find an element in the dynamic portion of the page and wait for it. I do this all the time and it works just fine.
Tracking of the HTTP calls can be done using the following code
var javaScriptExecutor = Browser.Driver as IJavaScriptExecutor;
Browser.Driver.Manage().Timeouts().AsynchronousJavaScript = TimeSpan.FromSeconds(5);
return javaScriptExecutor?.ExecuteAsyncScript("var cb = arguments[arguments.length - 1];" +
"if (window.webpackJsonp && document.querySelector('[aurelia-app]')) { cb('Aurelia composed') }" +
"document.addEventListener('aurelia-http-client-requests-drained', function (e) {" +
" cb('aurelia-http-client-requests-drained')" +
"}, false);");
above code uses the event aurelia-http-client-requests-drained which is triggered when all the HTTP request has been drained
For knowing if the page is composed, following code can be used
var javaScriptExecutor = Browser.Driver as IJavaScriptExecutor;
Browser.Driver.Manage().Timeouts().AsynchronousJavaScript = TimeSpan.FromSeconds(5);
return javaScriptExecutor?.ExecuteAsyncScript("var cb = arguments[arguments.length - 1];" +
"if (window.webpackJsonp && document.querySelector('[aurelia-app]')) { cb('Aurelia composed') }" +
"document.addEventListener('aurelia-composed', function (e) {" +
" cb('Aurelia App composed')" +
"}, false);");
This take advantage of the aurelia-composed

Page.Client Script process after the button click event

Good Day Every one here is my code
if (type.Contains("Loan Date"))
{
prenda.LoanMonth = year.ToString() + "/" + month.ToString();
string a = servs.CheckLM(prenda);
if (Convert.ToInt32 (a) != 0)
{
Page.ClientScript.RegisterStartupScript(Page.GetType(), "key", "myFunction();", true);
//create a popup if yes process if no exit
//if yes
prenda.LoanMonth = year.ToString() + "/" + month.ToString();
servs.DeletePrendaLM(prenda);
foreach (DataRow row1 in table.Rows)
{
prenda.Bcode = row1["Bcode"].ToString();
prenda.Jprincipal = Convert.ToDecimal(row1["JPrincipal"].ToString());
prenda.Aprincipal = Convert.ToDecimal(row1["APrincipal"].ToString());
prenda.Cprincipal = Convert.ToDecimal(row1["CPrincipal"].ToString());
prenda.LoanMonth = year.ToString() + "/" + month.ToString();
user.UserID = Session["UserID"].ToString();
servs.UploadPrendaLM(prenda, user);
}
}
the porblem is that the Page.ClientScript.RegisterStartupScript(Page.GetType(), "key", "myFunction();", true); doesnt execute as soon as the process pass over it, it executes myFunction() after finishing the button click proceses, i cant create a if clause below it because it finishes first all of the process before i can use the myFunction() i need that process first before continuing the proceses below //
so can you help me find a work around?
Few things that might help
The script block added by the RegisterStartupScript method executes when the page finishes loading but before the page's OnLoad event is raised. >> Where are you adding script in the page (Which event?) try it in OnInit or init complete events.
You can check whether script added or not through IsStartupScriptRegistered method (Page.ClientScript namespace)

Confused on some webBrowser functions

public void PortalLogin()
{
string portalUrl = "URL";
string portalEmail = "email";
string portalPassword = "password";
// Run when page finishes navigating
webBrowser2.DocumentCompleted += (s, e) =>
{
HtmlElement head = webBrowser2.Document.GetElementsByTagName("head")[0];
HtmlElement testScript = webBrowser2.Document.CreateElement("script");
IHTMLScriptElement element = (IHTMLScriptElement)testScript.DomElement;
element.text = "function PortalLogin() { document.getElementById('username').value = '" + portalEmail + "'; document.getElementById('password').value = '" + portalPassword + "'; document.getElementById('credentials').submit(); }";
head.AppendChild(testScript);
webBrowser2.Document.InvokeScript("PortalLogin");
};
// Navigate to the portal
webBrowser2.Navigate(portalUrl);
while (this.webBrowser2.ReadyState != WebBrowserReadyState.Complete)
{
Application.DoEvents();
Thread.Sleep(100);
}
}
I have the code segment above that is supposed to Navigate to a specific URL and then upon Navigate's completion, execute invoke a script to login to the webpage there. Now, because the entire PortalLogin() function is inside of a while loop, I have to include the:
while (this.webBrowser2.ReadyState != WebBrowserReadyState.Complete)
{
Application.DoEvents();
Thread.Sleep(100);
}
segment so that the while loop doesn't just interrupt the Navigate function a ton. However, the code isn't quite working, the flow seems to be off when I step through it with breakpoints. I think part of the problem is I don't quite understand how the while loop testing ReadState != Complete works. Could someone enlighten me?
Better use WebClient with cookies or HttpWebRequest and HttpWebResponse.

jQuery post request is not sent until first post request is completed

I have a function which have a long execution time.
public void updateCampaign()
{
context.Session[processId] = "0|Fetching Lead360 Campaign";
Lead360 objLead360 = new Lead360();
string campaignXML = objLead360.getCampaigns();
string todayDate = DateTime.Now.ToString("dd-MMMM-yyyy");
context.Session[processId] = "1|Creating File for Lead360 Campaign on " + todayDate;
string fileName = HttpContext.Current.Server.MapPath("campaigns") + todayDate + ".xml";
objLead360.createFile(fileName, campaignXML);
context.Session[processId] = "2|Reading The latest Lead360 Campaign";
string file = File.ReadAllText(fileName);
context.Session[processId] = "3|Updating Lead360 Campaign";
string updateStatus = objLead360.updateCampaign(fileName);
string[] statusArr = updateStatus.Split('|');
context.Session[processId] = "99|" + statusArr[0] + " New Inserted , " + statusArr[1] + " Updated , With " + statusArr[2] + " Error , ";
}
So to track the Progress of the function I wrote a another function
public void getProgress()
{
if (context.Session[processId] == null)
{
string json = "{\"error\":true}";
Response.Write(json);
Response.End();
}else{
string[] status = context.Session[processId].ToString().Split('|');
if (status[0] == "99") context.Session.Remove(processId);
string json = "{\"error\":false,\"statuscode\":" + status[0] + ",\"statusmsz\":\"" + status[1] + "\" }";
Response.Write(json);
Response.End();
}
}
To call this by jQuery post request is used
reqUrl = "AjaxPages/lead360Campaign.aspx?processid=" + progressID + "&action=updatecampaign";
$.post(reqUrl);
setTimeout(getProgress, 500);
get getProgress is :
function getProgress() {
reqUrl = "AjaxPages/lead360Campaign.aspx?processid=" + progressID + "&action=getProgress";
$.post(reqUrl, function (response) {
var progress = jQuery.parseJSON(response);
console.log(progress)
if (progress.error) {
$("#fetchedCampaign .waitingMsz").html("Some error occured. Please try again later.");
$("#fetchedCampaign .waitingMsz").css({ "background": "url(common/images/ajax_error.jpg) no-repeat center 6px" });
return;
}
if (progress.statuscode == 99) {
$("#fetchedCampaign .waitingMsz").html("Update Status :"+ progress.statusmsz );
$("#fetchedCampaign .waitingMsz").css({ "background": "url(common/images/ajax_loded.jpg) no-repeat center 6px" });
return;
}
$("#fetchedCampaign .waitingMsz").html("Please Wait... " + progress.statusmsz);
setTimeout(getProgress, 500);
});
}
But the problem is that I can't see the intermediate message. Only the last message is been displayed after a long lime of ajax loading message
Also on the browser console I just see that after a long time first requested is completed and after that the second request is completed. but there should be for getProgress ?
I have checked jquery.doc and it says that $post is an asynchronous request.
Can anyone please explain what is wrong with the code or logic?
You are in a situation discussed here:
ASP.net session request queuing
While a request for a given user's session is processed, other requests for the same session are waiting. You need to run your long function in a background thread and let the request that initiates it finish. However, the background thread will not have access to session, and you will need a different mechanism to communicate its progress.
From the information you've provided, I would suspect that it's not your javascript code that's being synchronous, but rather the server-side code. You can test this by using Firebug or Chrome's dev tools to look at the start and end times of the two AJAX requests. If I'm right, you'll see that the second request begins after half a second, but doesn't complete until after the first one.
If that's the case, possible causes are:
Running in a dev environment in Visual Studio, especially in debug mode, seems to reduce the amount of asynchronicity. The dev environment seems to like to process one request at a time.
See Igor's answer about session request queueing.
You may have code that explicitly locks resources and causes the second request to block until the long-running request is done.
One other possible culprit is the fact that most browsers only allow a limited number of concurrent requests to a particular domain. If you have a few requests pending at any given moment, the browser may just be queuing up the remaining requests until they return.

Using webdriver PageFactory to pick certain page

I have a web project where clicking a button navigates to another page. The new page can be 1 of three possible pages depending on data in the server. (The url may be the same for 2 of those pages)
I have three classes representing expected elements on each page using the PageObject model.
What is the best way to actually find what page actually got loaded? Is there an OR type of wait that I can wait on three unique elements and get the one that actually got loaded?
Yes, it is possible to check the presence of unique element (which identifies the page) and then return respective page in the framework.
However, a test should know the page it is expecting next and should assume that the correct page has loaded and perform further actions/assertions. You can even put an assertion here to verify correct page has loaded. If a different page has loaded, then the test eventually fails as assertions would fail.
This way test becomes more readable and describes flow of application.
Also, setting up test data upfront for the tests, is always advisable. This way you would know what data is available on server and test would know which page would render.
I had a similar issue where I needed to detect if a login was for a new user (the login page then goes to a terms & conditions page rather than direct to the home page).
Initially I just waited and then tested the second page but this was just a pain so I came up with this.
To Test the result with this:
var whichScreen = waitForEitherElementText(By.CssSelector(HeaderCssUsing), "HOME SCREEN", "home", terms.getHeaderLocator(), terms.headerText, "terms", driver, MAX_STALE_RETRIES);
if(whichScreen.Item1 && whichScreen.Item2 == "terms")
{
terms.aggreeToTerms();
}
The method that this calls is :
protected Tuple<bool, string> waitForEitherElementText(By locator1, string expectedText1, string return1Ident,
By locator2, string expectedText2, string return2Ident, IWebDriver driver, int retries)
{
var retryCount = 0;
string returnText = "";
WebDriverWait explicitWait = new WebDriverWait(driver, TimeSpan.FromSeconds(globalWaitTime));
driver.Manage().Timeouts().ImplicitlyWait(TimeSpan.FromSeconds(0.5));
while (retryCount < retries)
{
try
{
explicitWait.Until<bool>((d) =>
{
try
{
if (Equals(d.FindElement(locator1).Text, expectedText1)) { returnText = return1Ident; };
}
catch (NoSuchElementException)
{
if (Equals(d.FindElement(locator2).Text, expectedText2)) { returnText = return2Ident; };
}
return (returnText != "");
});
return Tuple.Create(true, returnText);
}
catch (StaleElementReferenceException e)
{
Console.Out.WriteLine(DateTime.UtcNow.ToLocalTime().ToString() +
":>>> -" + locator1.ToString() + " OR " + locator2.ToString() + "- <<< - " +
this.GetType().FullName + "." + System.Reflection.MethodBase.GetCurrentMethod().Name +
" : " + e.Message);
retryCount++;
}
}
return Tuple.Create(false,"");
}
The explicit wait until uses a boolean so will loop around for the full wait time (I have a very slow Test server so I set this to 60 seconds). the implicit wait is set to half a second so the element tests will attempt every half a second and loop around until either true is returned or it fails.
I use a Tuple so that I can detect which screen I am on, and in this case agree to the terms & conditions which then sets me back on my normal page path

Categories

Resources