Call a function from js file in Robot Framework - javascript

There is a possibility to execute javascript from file.
Execute JavaScript ${CURDIR}/js_to_execute.js
But how can I call a function by name from this file?

There is no way to call a function using Execute Javascript out of the box. But it's possible to pass a function name as an argument.
Execute Javascript ${CURDIR}/js/utils.js ARGUMENTS clickElement ${locator}
The content of utils.js file:
var utils = utils || {};
(function (arguments) {
utils.clickElement = function (locator) {
// implementation of the function
}
// this piece of code does the trick
// get function name from arguments and remove it from the list of arguments
var functionName = [].shift.call(arguments);
// call the function and pass the arguments
utils[functionName].apply(this, arguments);
})(arguments);

This will be little ugly but it works. You need to inject your whole Javascript code using Execute Javascript first and than you will be able to call the function you injected.
You can inject Javascript using below javascript code on google home page,
var s = document.createElement('script');
s.text = "document.getElementById('hplogo').addEventListener('click',function(){alert('Google');});";
document.getElementsByTagName('head')[0].appendChild(s);
In robotframework you can do the same using Execute Javascript
Open Browser https://www.google.com
Wait Until Page Contains Element id=hplogo
Execute Javascript var s = document.createElement('script');s.text = "document.body.addEventListener('click',function(){alert('Google')})";document.getElementsByTagName('head')[0].appendChild(s);
Sleep 2s #wait so that injected javascript gets evaluated
Execute Javascript document.getElementById('hplogo').click();
Sleep 10s #wait and click anywhere on body of the page to see the alert
Your s.text variable will have whole javascript code from js file in one line. Of course if your javascript is long than it will be dirty work but in one of the site I had to do this for pdf export and it is working fine.

Related

Function not available after document.body.appendChild(scriptElement)

Here is the code I am using for dynamically including script tag with src in my HTML page. There is a function in that newly imported javascript file that I want to use:
var scriptFile= document.createElement("script");
scriptFile.src = "something.js";
var something=document.body.appendChild(scriptFile);
something.js contains a function called doSomething(). Now when I call this function immediately after the appendChild above, it say doSomething is not defined. However, when I fire it from the Chrome console, it executes successfully. I am not sure why is this happening.
When you append the script tag with the source, first the source will be parsed and compiled which is an asyn process ( non blocking ).
If you try to invoke the method immediately this would throw an error as the source has not been compiled yet.
Bind a load event which gets triggered when the script is available. This will make sure you are running the contents of the script tag only after it has completely loaded.
var scriptFile= document.createElement("script");
scriptFile.addEventListener('load', function() {
console.log('Script is ready to execute');
// invoke your function here
});
scriptFile.src = "something.js";
var something=document.body.appendChild(scriptFile);
Listen for onload
var scriptFile= document.createElement("script")
scriptFile.src = "something.js"
document.body.appendChild(scriptFile)
scriptFile.onload = () => {
// call something.js functions here
}

Invoking a p:remoteCommand via a JavaScript function passing a message local to that function to another function through the "oncomplete" handler

This question is purely based on this previously asked question (courtesy) but the question is messed up completely with the Java EE 7 WebSockets API attempting to show the actual practical approach/scenario which is now very unlikely to receive any answer based on <p:remoteCommand>.
Given below a snippet of JavaScript (this is only a test scenario).
<script type="text/javascript">
function test() {
var message = "myMessage";
window["myFunction"]();
// This is literally interpreted as a JavaScript function "myFunction()".
// "myFunction()" in turn is associated with a <p:remoteCommand>.
}
$(document).ready(test);
function notifyAll() {
alert("notifyAll() invoked.");
}
</script>
The test() function is invoked as soon as the page is loaded which causes the following <p:remoteCommand> to trigger which in turn invokes another JavaScript function, namely notifyAll(), using an oncomplete handler that simply alerts the said message.
<h:form>
<p:remoteCommand process="#this"
name="myFunction"
actionListener="#{bean.listener}"
oncomplete="notifyAll()"
ignoreAutoUpdate="true"/>
</h:form>
Assume that the local JavaScript variable message inside the test() function is assigned a JSON message which is asynchronously received through a WebSockets channel.
The notifyAll() function in turn has to send a notification message (myMessage local to the test() function - actually a JSON message which is received previously in the test() function) to another WebSockets channel which is completely ignored in this question for brevity.
Is it possible to pass the value of var message = "myMessage" local to the test() function to another function notifyAll() through the oncomplete handler of the given <p:remoteCommand>?
Declaring message as a global JavaScript variable may overwhelm the functionality of WebSockets as the message is received asynchronously i.e. a new message may be received while the processing of <p:remoteCommand> is still going on/awaiting to complete. Thus, declaring message as a global JavaScript variable is not an option.
.
I'm not seeing a better way than passing it as a parameter into the <p:remoteCommand> function and having the oncomplete function extract from it.
function test() {
var message = "myMessage";
myFunction([{name: "message", value: message}]);
}
function notifyAll(data) {
var message = decodeURIComponent(data.match(/&message=([^&]*)/)[1]);
// ...
}
<p:remoteCommand name="myFunction" ... oncomplete="notifyAll(this.data)" />
The data argument is already injected in the JS function scope by oncomplete and it represents the XHR query string. The regex extracts the parameter from it. Note that the regex assumes that the parameter is never in the beginning of the query string, which is true as it always starts with JSF/PF specific parameters, so it can be kept simple (JS regex is tricky with negative lookbehind).

Dynamically load jQuery and use it in another function

function getArray() {
var j = document.createElement('script');
j.src = "http://code.jquery.com/jquery-2.1.4.min.js";
var head = document.getElementsByTagName('head')[0];
head.appendChild(j);
var my_array = [];
j.addEventListener('load',function(){
// Some jQuery codes to fill my_array
});
return my_array;
}
I use above code to dynamically load jQuery in console, and then use jQuery to get some data from the DOM and store them in the array. However, it returns an empty array. I think it is because loading jQuery takes some time and the function gets returned before the jQuery is loaded and the jQuery codes are executed.
So before getArray() returns, I must make sure the jQuery codes have been executed. I've tried to put return my_array inside the addEventListener, of course it won't work because that way it will return the anonymous function. I can think of some ways to deal with this issue, like making the my_array a global so I don't have to return the function, or putting the jQuery loading codes to another loadjQuery function and call it before I execute the jQuery codes, but is there a better way to do it?
The problem is due to asynchronous call of loading jquery script.
The best way to do it will be, write a function to load a script and pass the callback function, then on successful load of script call your callback function, eg:
function loadScript(callback) {
var j = document.createElement('script');
j.src = "http://code.jquery.com/jquery-2.1.4.min.js";
var head = document.getElementsByTagName('head')[0];
head.appendChild(j);
j.addEventListener('load',function(){
if(typeof(callback) == "function")
});
}
function getArray(){
var my_array = [];
// Some jQuery codes to fill my_array
return my_array;
}
loadScript(getArray)
Unfortunately, that can't be done. JavaScript is an event based single-thread asynchronous language. What you're trying to do can't work in that type of environment.
However, it's likely you simply need to load jQuery before processing this function (even using a simple <script> tag) to solve your issue. Otherwise, you're likely to encounter a very noticeable delay when calling the function due to the downloading & evaluating of the jQuery library. Another issue would be that if you call the function more then 1 time, you'll load jQuery again, and that might create a big big mess.
Alternatively, if you "insist" on using jQuery & have it only loaded once your function is called, you could return a Promise that will resolve to your array like this (This will require a supporting browser or some polyfills / promise library):
function getArray() {
var j = document.createElement('script');
j.src = "http://code.jquery.com/jquery-2.1.4.min.js";
var head = document.getElementsByTagName('head')[0];
head.appendChild(j);
return new Promise(function(resolve, reject) {
j.addEventListener('load',function(){
var my_array = [];
// Some jQuery codes to fill my_array
resolve(my_array);
});
});
}

Call javascript functions of preloaded library from application

I have a WebView where a some page was already loaded webview.loadUrl(URL);
Also there is an amount of javascript functions like a:
function myFunction1() {// some code}
function myFunction2() {// some code}
function myFunction3() {// some code}
I need to preload all functions onto WebView (when page is already loaded) and then execute them one by one from application as webview.loadUrl("javascript: myFunction1()"); or webview.loadUrl("javascript: myFunction3()");
Is this possible to preload library instead of attaching js in the html code <script src="js/myLibrary.js"></script>?
It's not difficult. Look, at first you should use console.log. It might help you to understand what happening during the script execution:
console.log('This message should appear as a debug message in Logcat.');
put this into javascript and you will get this message in the LogCat:
11-15 12:31:10.652: I/Web Console(16214): This message should appear as a debug message in Logcat.:1
Then, your question: you must load your javascript functions in the row:
wv.loadUrl("javascript: var globalVar; function init() {globalVar=1;}; function global() {init(); var gl = '2'; console.log(globalVar);}");
, here we have init() function that initializes globalVar and global, that init all vars and print result in logcat!
After preloading you could execute any functions:
wv.loadUrl("javascript: global();");
and you will see in logcat "1"! And that all!

How can I block the execution of a script until another one is loaded?

Suppose I load these scripts from my host webpage :
<script src="http://www.mywebsite.com/widget/widget.js?type=normal" type="text/javascript"></script>
<script src="http://www.mywebsite.com/widget/widget.js?type=rotation" type="text/javascript"></script>
and I'd like to execute the second one only when the first one have finished (totally; it can contain asynch functions).
How can I do it?
You're already doing it right : the scripts are executed in the order of integration in the page, not loading.
EDIT : as MaxArt pointed, this may not be what you're looking for. His answer is a good one.
But generally, you'll want to use javascript usual patterns and event based logic to avoid this kind of problems :
have most of your files define only classes and functions (some of them taking callbacks as parameters)
have a main file launching this and calling the sequence of actions, the asynchronicity being handled via callbacks.
My main code usually looks (a little) like this :
$(window).ready(function() {
var myBigEngine = new BigEngine();
myBigEngine.init(function(){
// do other things, no need to add other layers as user events and ajax message callback will do the rest
});
});
Wrap the whole script in a function, like this:
(function(id) {
var id = setInterval(function() {
if (!window.doneLoading) return;
clearInterval(id);
// The whole script file goes here
...
}, 50);
})();
The setInterval polls the (global, sorry) variable doneLoading. In the first script, you have to set doneLoading to true or any other non-false value when your async function is completely loaded, like at the end of an AJAX request maybe?
Edit: since I'm suggesting to add a global variable to the script, it may as well add a global function. So instead of setting up a setInterval call, wrap the second script inside a function... but like this:
function doneLoading() {
// The whole script file goes here
...
}
In the first script file, at the end of your callback function, just call doneLoading().
Try applying defer="defer" attribute to second <script> declaration like
<script src="http://www.mywebsite.com/widget/widget.js?type=rotation" type="text/javascript" defer="defer" ></script>
you can put second script init function in window.onload event
window.onload = function(){
// call function from second script
}
or with jQuery
$(function(){
// call function from second script
});
you can event add second script with this function
function loadScript(src){
var f=document.createElement('script');
if(f){
f.setAttribute("type","text/javascript");
f.setAttribute("src",src);
document.getElementsByTagName("head")[0].appendChild(f);
}
}

Categories