We have had a similar issue where our in house framework is being used to test multiple sites, some of these are using JQuery and some are using AngularJS (and 1 even has a mixture!). Our framework is written in C# so it was important that any JScript being executed was done in minimal chunks (for debugging purposes). It actually took a lot of the above answers and mashed them together (so credit where credit is due @npjohns). Below is an explanation of what we did:
The following returns a true / false if the HTML DOM has loaded:
public bool DomHasLoaded(IJavaScriptExecutor jsExecutor, int timeout = 5)
{
var hasThePageLoaded = jsExecutor.ExecuteScript("return document.readyState");
while (hasThePageLoaded == null || ((string)hasThePageLoaded != "complete" && timeout > 0))
{
Thread.Sleep(100);
timeout--;
hasThePageLoaded = jsExecutor.ExecuteScript("return document.readyState");
if (timeout != 0) continue;
Console.WriteLine("The page has not loaded successfully in the time provided.");
return false;
}
return true;
}
Then we check whether JQuery is being used:
public bool IsJqueryBeingUsed(IJavaScriptExecutor jsExecutor)
{
var isTheSiteUsingJQuery = jsExecutor.ExecuteScript("return window.jQuery != undefined");
return (bool)isTheSiteUsingJQuery;
}
If JQuery is being used we then check that it's loaded:
public bool JqueryHasLoaded(IJavaScriptExecutor jsExecutor, int timeout = 5)
{
var hasTheJQueryLoaded = jsExecutor.ExecuteScript("jQuery.active === 0");
while (hasTheJQueryLoaded == null || (!(bool) hasTheJQueryLoaded && timeout > 0))
{
Thread.Sleep(100);
timeout--;
hasTheJQueryLoaded = jsExecutor.ExecuteScript("jQuery.active === 0");
if (timeout != 0) continue;
Console.WriteLine(
"JQuery is being used by the site but has failed to successfully load.");
return false;
}
return (bool) hasTheJQueryLoaded;
}
We then do the same for AngularJS:
public bool AngularIsBeingUsed(IJavaScriptExecutor jsExecutor)
{
string UsingAngular = @"if (window.angular){
return true;
}";
var isTheSiteUsingAngular = jsExecutor.ExecuteScript(UsingAngular);
return (bool) isTheSiteUsingAngular;
}
If it is being used then we check that it has loaded:
public bool AngularHasLoaded(IJavaScriptExecutor jsExecutor, int timeout = 5)
{
string HasAngularLoaded =
@"return (window.angular !== undefined) && (angular.element(document.body).injector() !== undefined) && (angular.element(document.body).injector().get('$http').pendingRequests.length === 0)";
var hasTheAngularLoaded = jsExecutor.ExecuteScript(HasAngularLoaded);
while (hasTheAngularLoaded == null || (!(bool)hasTheAngularLoaded && timeout > 0))
{
Thread.Sleep(100);
timeout--;
hasTheAngularLoaded = jsExecutor.ExecuteScript(HasAngularLoaded);
if (timeout != 0) continue;
Console.WriteLine(
"Angular is being used by the site but has failed to successfully load.");
return false;
}
return (bool)hasTheAngularLoaded;
}
After we check that the DOM has successfully loaded, you can then use these bool values to do custom waits:
var jquery = !IsJqueryBeingUsed(javascript) || wait.Until(x => JQueryHasLoaded(javascript));
var angular = !AngularIsBeingUsed(javascript) || wait.Until(x => AngularHasLoaded(javascript));