Selenium (Java) wait until JavaScript / Vue / Angular loaded - javascript

I am writing an automation tool for a website. Therefore I am using Selenium in Java. For the real automation purpose I mainly use JavaScript through JavascriptExecutor. Most of the time everything works fine, but sometimes it crashes (e.g. 1 out of 10 times). I have the impression that then the code was just too fast. I am using implicit wait and explicit wait from the WebdriverWait class. I think this waits just wait for the dom or the elements within, but they are not waiting until all scripts are done. Therefore I need a function or snippet. As mentioned the website is using vue and angular.
Thanks in advance!

In your case, there is possibility that your script get failed while navigating to other html page by clicking link or button. If your application fails/crashes in these scenarios, include page load time on implicit wait as well.

You can add an explicit wait that will wait for angular to finish processing any pending requests:
public static ExpectedCondition angularHasFinishedProcessing() {
return new ExpectedCondition() {
#Override
public Boolean apply(WebDriver driver) {
JavascriptExecutor jsexec = ((JavascriptExecutor) driver)
String result = jsexec.executeScript("return (window.angular != null) && (angular.element(document).injector() != null) && (angular.element(document).injector().get('$http').pendingRequests.length === 0)")
return Boolean.valueOf(result);
}
};
}
To use it you would then do:
WebDriverWait wait = new WebDriverWait(driver, 15, 100);
wait.until(angularHasFinishedProcessing());
This used to remove of lot of flakiness in Angular automation for me.
I would suggest always using explicit waits and never using implicit waits, implicit waits will make negative checks take forever. Also don't ever mix implicit and explicit waits, it has the potential to cause all sorts of strange undefined behaviour.

Related

WebBrowser.Navigate with JavaScript

I am using the WebBrowser Component in System.Windows.Forms. The code loads content from a website and returns it properly. There is a JavaScript which is executed and loading some of the DOMs after the page has loaded completely.
The JavaScript is not finished loading by the time the .Navigate method finished execution. If I set a Breakpoint on the .Navigate in Debug mode, It will, clearly because .Navigate is asynchronous, run through the process of loading the page including the scripts.
void LoadPageWithScripts() {
Browser.Navigate("mypagewithscriptsurl");
// whatever comes next prevents the DOM generated by the script from beeing loaded
// ... e.g.:
Console.WriteLine("whatever");
// use Browser.Document later
}
I know, this question is similar to the one provided here: JavaScript only works...
Unfortunately, I have no Influence on the page which is loaded, so the approaches I have seen there, are not suitable for my needs.
I have tried to simply work with Thread.Sleep, as suggested by many forums. But even this won't work. As soon as the code continues to run past the .Navigate method, the JavaScript is lost. Only setting a break point on it will work currently.
Browser.Navigate("pageUrl");
Browser.Navigate("pageurl");
// Very bad solution
Thread.Sleep(2000);
while (true)
{
if (Browser.ReadyState == WebBrowserReadyState.Complete)
{
// do something
break;
}
else
{
Application.DoEvents();
}
}
Using the DocumentCompleted Event will not work, since the Script is not loaded before the document is in completed state.
Browser.Navigate("pageUrl");
Browser.DocumentCompleted += (o, e) =>
{
var text = Browser.DocumentText;
Console.WriteLine(text);
};
Hope to find some help.

How can I detect when an AngularJS app is finished loading a page using JavaScript?

I'm trying to write Robot Framework tests to cover certain use cases for a third party AngularJS app.
I have the requirement that I need to use Python 3.5+ and SeleniumLibrary (rather than the old Selenium2Library).
I've attempted to adapt the wait_until_angular_ready keyword from https://github.com/rickypc/robotframework-extendedselenium2library to work outside of the context of this library, updating it to work with Python 3.5+ and SeleniumLibrary.
The keyword executes the following JavaScript to check when Angular is Ready, but it seems to always return true immediately
var cb = arguments[arguments.length-1];
if(window.angular){
var $inj;
try {
$inj = angular.element(document.querySelector('[data-ng-app],[ng-app],.ng-scope')
||document).injector()
||angular.injector(['ng'])
} catch(ex) {
$inj = angular.injector(['ng'])
};
$inj.get = $inj.get||$inj;
$inj.get('$browser').notifyWhenNoOutstandingRequests(function() {
cb(true) // it's always returning here
})
} else {
cb(true)
}
This is called from within my version of the wait_until_angular_ready keyword which is located in a class which subclasses the SeleniumLibrary WaitingKeywords class.
The code which executes the Js looks like
WebDriverWait(self.driver, 100, 0.1). \
until(lambda driver: driver.execute_async_script(script), error)
Have I made a mistake here or is this not the correct way to check when angular has finished rendering the page? I will admit that I am not very familiar with AngularJS
Probably you resolved this issue, but if anyone in the future have the same inconvenience can use this library for Angular in Robot framework: https://github.com/Selenium2Library/robotframework-angularjs
you can use document.readyState , it will return "complete" as a response if the page is rendered completly.

Qt function runJavaScript() does not execute JavaScript code

I am trying to implement the displaying of a web page in Qt. I chose to use the Qt WebEngine to achieve my task. Here's what I did :
Wrote a sample web page consisting of a empty form.
Wrote a JS file with just an API to create a radio button inside the form.
In my code, it looks like this :
View = new QWebEngineView(this);
// read the js file using qfile
file.open("path to jsFile");
myJsApi = file.Readall();
View->page()->runjavascript (myjsapi);
View->page()->runjavascript ("createRadioButton(\"button1\");");
I find that the runJavaScript() function has no effect on the web page. I can see the web page in the output window, but the radio button I expected is not present. What am I doing wrong?
I think you will have to connect the signal loadFinished(bool) of your page() to a slot, then execute runJavaScript() in this slot.
void yourClass::mainFunction()
{
View = new QWebEngineView(this);
connect( View->page(), SIGNAL(loadFinished(bool)), this, SLOT(slotForRunJS(bool)));
}
void yourClass::slotForRunJS(bool ok)
{
// read the js file using qfile
file.open("path to jsFile");
myJsApi = file.Readall();
View->page()->runJavaScript(myjsapi);
View->page()->runJavaScript("createRadioButton(\"button1\");");
}
I had this problem, runJavascript didn't have any effect. I had to put some html content into the view (with page().setHtml("") before running it.
Check the application output, it might contain JavaScript errors. Even if your JS code is valid, you might encounter the situation where the script is run before DOMContentLoaded event, that is document.readyState == 'loading'. Therefore, the DOM might not be available yet, as well as variables or functions provided by other scripts. If you depend on them for your code to run, when you detect this readyState, either wait for the event or try calling the function later, after a timeout. The second approach with timeout might be needed if you need to get the result of the code execution, as this can be done only synchronously.

Delay while loading locally stored file on webView

I am loading locally stored html(files with javascript) file on my webView. These files are loaded in my ViewController.swift. However, there is a delay of ~2-3 seconds when the app is launched for the first time. I have searched over the web regarding an approach to fix this issue, with the approaches I observed(First loading of the webView delays the loading of resources), I have decided to use NSNotificationCenter in my ViewController and call them from 'AppDelegate' so that the webView is loaded first time.
Upon implementation of these changes in my code, there is delay in the LaunchScreen of the application, which is as expected as I am loading a file here, however, the loading time of ViewController is still the same.
I am calling the Notifications
AppDelegate.swift
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// Override point for customization after application launch.
NSNotificationCenter.defaultCenter().postNotificationName("loadDefaultWebViewID",object:nil)
return true
}
ViewController.swift
override func viewDidLoad() {
super.viewDidLoad()
NSNotificationCenter.defaultCenter().addObserver(self, selector: "loadDefaultWebView:", name: "loadDefaultWebViewID", object: nil)
if let htmlURL = NSBundle.mainBundle().URLForResource("index", withExtension: "html") {
let requestObject = NSURLRequest(URL: htmlURL)
webViewHTML5Player.loadRequest(requestObject)
}
I wish to reduce the delay that on the ViewController.If this is not the correct way, Is there any other better optimized approached to achieve this task ?
Edit 1
I have tried using WKWebView(with preferences.javaScriptEnabled = true) and UIWebView for this purpose, loading time is still the same.
This is a common problem occuring in UIWebView. My point is to make the web view initially hidden (showing some sort of "Loading..." indicator instead), then make it un-hidden in the webViewDidFinishLoad: method. and also you can refer this link UIWebView lower loading time with a more efficient way and Preloading webView doesn't work - trying to reduce loading time

What are the inner workings of the Selenium waitFor mechanism?

I am trying to customize the behavior of Selenium's click command, (via user-extentions.js), by intercepting calls to doClick(locator). Basically I need to delay click actions whenever our application's "busy indicator" is being displayed.
(Now the standard answer for this kind of thing is to insert a waitFor into the script for those situations. Indeed, we currently have zillions of them throughout our scripts. I'm trying to eliminate those.)
Detecting the page element is the trivial part. The tricky part is getting the script to actually wait. My promising looking, but failed attempt looks like this:
var nativeClick = Selenium.prototype.doClick;
Selenium.prototype.doClick = function(locator) {
this.doWaitForCondition("!selenium.browserbot.findElementOrNull('busy-indicator')", 5000);
return nativeClick.call(this, locator);
}
The doWaitForCondition gets called before every click, but it does not wait when the condition evaluates to false. nativeClick always gets called immediately, and so no delay is introduced. I suspect that the doWaitForCondition function doesn't actually do any waiting per se, but rather establishes the conditions for it within the command execution loop. And in this case the click command is already in play, and I'm trying to run a command within a command.
Can somebody shed some light on how Selenium command execution and waitFor works, or offer suggestions on how this might be done?
I have finally solved this. And with an approach that is much better than trying to intercept click processing in its various forms. My refined goal is: to delay execution of script command completion when our application is "busy".
How Selenium command processing works:
Upon completion, each selenium command returns an ActionResult object, (see ActionHandler.prototype.execute). The terminationCondition attribute on this object is a function that determines when it is okay for selenium to proceed to the next command, (TestLoop.prototype.continueTestWhenConditionIsTrue). Basically, selenium repeatedly executes the condition function until it yields true. The result object it quite trivial:
function ActionResult(terminationCondition) {
this.terminationCondition = terminationCondition;
}
Customizing it:
I want to delay execution any time myAppIsBusy() returns true. Of course all of the standard delays need to remain in place as well, like waiting for page loads, and explicit waitFor conditions as scripted. The solution is to redefine the selenium result object in my user-extensions.js, as follows:
function ActionResult(terminationCondition) {
this.terminationCondition = function() {
// a null terminationCondition means okay to continue
return (!terminationCondition || terminationCondition()) && !myAppIsBusy();
}
}
The great thing is that this is at a low enough level that it works for the IDE, as well as for RC.
Note that this does not affect Accessor or Assert command types, which return different result objects. But that should be fine, because those commands don't effect the state of the application.
Well, a look at the java drivers com.thoughtworks.selenium.Wait class reveals this:
public void wait(String message, long timeoutInMilliseconds, long intervalInMilliseconds) {
long start = System.currentTimeMillis();
long end = start + timeoutInMilliseconds;
while (System.currentTimeMillis() < end) {
if (until()) return;
try {
Thread.sleep(intervalInMilliseconds);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
throw new WaitTimedOutException(message);
}
I am not to deep into selenium but I excpect that every waitXXX Method points to this.
So, Selenium is working with Thread.sleep(). While this might not look like an ideal solution it shows at least that you cant make it worse by using Thread.sleep() on your own if neccessary. ;-)

Categories

Resources