App
The App
variable contains functions relating to the app itself.
Location
Returns the current location (if applicable for the given application type – non-webapps do not support this).
Example
var loc = App.location();
DANGER
Only web
Navigate
Navigates to the given url. If the url is relative (e.g. somefolder/somefile.html
) it will get appended to the current url.
Parameters
url
a string representing the destination for the navigation act
Example
// Absolute url
App.navigate("http://dr.dk");
// Relative url
App.navigate("news");
DANGER
Only web
Session write
Store a value in the current session storage. This will be available across flows and for all applications.
Parameters
key
a string denoting the key to store the value undervalue
an object to storeoptions
an optional options object. Supported options are;expires
a timeout in minutes - after this interval has passed the value will be deleted. Default is 1440 min (= 1 day).
Example
// Storing a simple value - a string
App.session().write("mykey", "myvalue");
// Storing an object - expires in 1 hour
App.session().write("myotherkey", { greeting: "hello" }, { expires: 60 });
Session read
Read a value stored in the current session.
Parameters
key
a string denoting the key to retrieve the value for
Example
var v = App.session().read("mykey"); // e.g. will return 'myvalue'
Session delete
Delete a value.
Parameters
key
a string denoting the key to delete
Returns
The value deleted.
Example
var v = App.session().delete("mykey"); // e.g. will return 'myvalue'
Quit
Quits the application - be aware that this is a hard shutdown and the user will not be prompted to save any information before the application exits.
Example
App.quit();
Focused field
Returns a special field, which targets the currently focused UI element of the application. This can help in tricky cases where a UI element must be reached by means of tabbing. Using App.focusedField, even such a UI element can support actions like inspect, read and input. App.focusedField can also be used to verify that the focus is where it’s meant to be.
Example
var inspect = App.focusedField.inspect();
Getting access to an embedded browser instance
Note: The following only applies to native applications with embedded Internet Explorers and java applications with the teamdev JxBrowser commercial browser component.
Use the App.browserAt(path, options)
method to get a DOM
instance. The path
argument is the path to the UI element containing the embedded browser - this normally has the url of the page displayed as its name and you can thus use the url as (part of) the path.
Alternatively you can use an existing field;
var f = new Field("**/Browser");
var b = App.browserAt(f);
// or simply
b = f.asBrowser();
DOM methods
Once you have DOM
object you can invoke methods on it and read selected properties.
Title
var b = App.browserAt("**/Browser");
// Access the title
var title = b.title;
Location
var b = App.browserAt("**/Browser");
// Access the location
var url = b.location;
Eval(js)
Eval some JavaScript in the embedded instance.
WARNING
Note that apps running in Chrome and Edge are getting new restrictions rolled out for browser plugins at some point in 2024, which severely limit what can be done with eval in those browsers. Hosted browsers and java are not affected by this.
var b = App.browserAt("**/Browser");
var result = b.eval("(function() { return 'Hello, world!'; })();");
getElementById(id)
Returns a DOMElement
(see below) given one with the id
exists.
var b = App.browserAt("**/Browser");
var elm = b.getElementById("foo");
getElementsByTagName(tag)
Returns an array of DOMElement
s with the given tag.
var b = App.browserAt("**/Browser");
var allInputElements = b.getElementsByTagName("input");
querySelector(query)
Returns the first DOMElement
matching the given query.
var b = App.browserAt("**/Browser");
var foo = b.querySelector(".foo");
querySelectorAll(query)
Returns an array of DOMElement
s matching the given query.
var b = App.browserAt("**/Browser");
var allFooClassedElements = b.querySelectorAll(".foo");
DOMElement methods
A DOMElement represents a single element in the DOM. It has the following properties and methods.
TagName
// we assume we have gotten the `elm` from a `DOM` method invocation e.g. via `getElementById`
var tag = elm.tagName;
InnerText, innerHTML and outerHtml
var t = elm.innerText;
var hi = elm.innerHTML;
var ho = elm.outerHTML;
Checked
Applies only to checkbox
es and radio
buttons.
var isChecked = elm.checked;
// we can also check the input using this property
if (!isChecked) {
elm.checked = true;
}
GetAttribute(attr)
Get an attribute of the DOMElement
.
var id = elm.getAttribute("id");
Click()
Click the DOMElement
- only makes sense for elements that are clickable.
elm.click();
Input
Get/set the value of an input- or textarea element.
var content = elm.input;
// update it
elm.input = "foo";
Select()
Selects an element.
elm.select();
Eval()
Evaluate javascript with the dom element available in the variable element
. Note: this is currently only supported for the teamdev JxBrowser embedded in a java app.
// trigger event on an input
elm.eval('element.dispatchEvent(new Event("input", { bubbles: true }));');
QuerySelectorAll()
Query for child elements under the given element. Note: this is currently only supported for the teamdev JxBrowser embedded in a java app.
var allInputs = elm.querySelectorAll("form input");
Set browser popup behavior
For the embedded chrome browser, you can specify what should happen when the automated page wants to open a popup window. The available options are as follows:
default
the popup is loaded in a new desktop window. Note that no automation is available in such a popup window. As the name suggests, this is the default behavior.prevent
no popup window is shown. The user does not see any indication that a popup window was requested by the sitenavigate
the main window navigates to the url that was to be shown in a popup. This allows automation of the popup content but no further automation of the page that triggerede creation of the popup.
App.setPopupBehavior("prevent");
Evaluate javascript inside the application
For browser apps (chrome/firefox/edge/IE) and some java apps (with a javascript interpreter available) it is possible to send bits of javascript to the app in order to do things that are not directly possible with the combined Sirenia flow api.
WARNING
This is an advanced topic which often requires experimentation to gain the required knowledge of the app internals. This feature is also experimental in the sense that it can potentially affect the stability of the host application. Make sure to test thoroughly for regressions in the host app if you must go this route.
An example of using this api:
// Extract information by calling a static method in a java app
var patientId = App.eval(
"Class.get('com.somecompany.MainController').static.getCurrentPatientId()"
);
// Trigger event in web app
App.eval(
"document.querySelector('#userName').dispatchEvent(new InputEvent('input'))"
);
Java eval engines
In java apps there is sometimes a choice of javascript engines to choose between. The following example shows how to obtain a json summary of available engines:
var availableEngines = App.eval("", { engine: "list-engines" });
The returned result shows the name of the engine and a list of aliases by which it can be selected.
Example output:
{
"Oracle Nashorn": ["nashorn", "Nashorn"],
"Graal.js": [
"Graal.js",
"graal.js",
"Graal-js",
"graal-js",
"Graal.JS",
"Graal-JS",
"GraalJS",
"GraalJSPolyglot",
"js",
"JS",
"JavaScript",
"javascript",
"ECMAScript",
"ecmascript"
]
}
If no engine is specified in the engine
option, the engine that responds to the js
alias will be selected.
The following eval example targets the nashorn
engine (which is available in java versions 8 through 14):
App.eval("Class.get('com.somecompany.SomeClass').static.aStaticMethod()", {
engine: "nashorn",
});
Java eval api
For java apps, multiple javascript statements can be passed to eval. The value of the last statement is returned.
A small javascript api is made available to make it easier to interact with the host application.
Class.get(String name)
resolves the given fully qualified class name and returns the class. The above examples demonstrate how to access static members on the return value.Class.info(String name|Object o|Class c)
returns a json summary of a class either identified by its full name or by an instance of it or by a reference to the class. Themethods
array includes all public methods from the class itself and all of its super classes. TheownMethods
array includes all public and private methods on the class itself.
Example output:
{
"name": "eu.sirenia.dugong.TestClass",
"methods": [
"public boolean eu.sirenia.dugong.TestClass.testMethod(int,java.lang.String)",
"public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException",
"public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException",
"public final void java.lang.Object.wait() throws java.lang.InterruptedException",
"public boolean java.lang.Object.equals(java.lang.Object)",
"public java.lang.String java.lang.Object.toString()",
"public native int java.lang.Object.hashCode()",
"public final native java.lang.Class java.lang.Object.getClass()",
"public final native void java.lang.Object.notify()",
"public final native void java.lang.Object.notifyAll()"
],
"ownMethods": [
"public boolean eu.sirenia.dugong.TestClass.testMethod(int,java.lang.String)"
],
"fields": [
"public java.lang.String eu.sirenia.dugong.TestClass.someField",
"public final java.lang.String eu.sirenia.dugong.TestClass.argField"
],
"constructors": ["public eu.sirenia.dugong.TestClass(java.lang.String)"]
}
Class.inspect(Object o)
returns a json representation of the given object complete with all public and private fields of primitive type. To follow references see the next overload.Class.inspect(Object o, int depth)
returns a nested json representation where the number of public/private field references followed is given in the second argument.
Tip: Start with low values of depth
as the data returned may grow exponentially as depth is increased.
Class.inspect(Object o, String[] refs)
returns a json representation of the object obtained by following field names given in the string array starting at the given object. Only primitive valued fields are included. To follow references from the target object see the next overload.Class.inspect(Object o, String[] refs, int depth)
returns a nested json representation of the object obtained by following the references given in the string array starting at the given object. The depth of the nesting is determined by the third argument.Class.deref(Object o, String[] refs)
returns the java Object (not a json representation) obtained by following the given references from the given object. This is a convenience allowing safe traversal of an object graph without having to handle individual null checks and other edge cases.
Examples:
// Examine members of host application class
var json = App.eval('Class.info("com.somecompany.MainController")');
// Examine field values from host application object
var script =
'var o = Class.get("com.somecompany.MainController").static.getInstance();' +
"Class.inspect(o, 2);";
var json = App.eval(script);
// Extract system properties from java vm
var script =
'var props = Class.get("java.lang.System").static.getProperties();' +
"Class.inspect(props, 1);";
var systemProperties = App.eval(script);
// Follow a list of references
var script =
'var c = Class.get("com.somecompany.MainController");' +
'Class.deref(c, ["selectedPatient", "basicInfo", "name"]);';
var patientName = App.eval(script);
Getting hold of object instances
The observant reader may at this point wonder how to get hold of object instances in practice, when all we have access to are static fields and methods.
Static fields and methods will sometimes give access to object instances, but there is a more direct approach. See Field.eval
Eval in web apps
For web app types there is currently an extra limitation imposed on the script you pass to App.eval
. It has to consist of a single javascript expression.
This needn’t limit the creativity you can employ when automating your app. The following example demonstrates a way to execute sequential statements despite this limitation.
var script =
"var userNameInput = document.querySelector('#form input.user-name');" +
"userNameInput.value = userNameInput.value.reverse();" +
"userNameInput.dispatchEvent(new InputEvent('input'));" +
"return userNameInput.value;";
var wrappedScript = "(function() {" + script + " })();";
var reversedName = App.eval(wrappedScript);
This construct is known as an Immediately Invoked Anonymous Function. It allows us to wrap sequential statements into a single expression thus circumventing the single-expression limitation.
Return values
App.eval
always returns a string. To make the format of the string predictable, it is a good idea to make sure to format the result of your javascript statements as a string you can parse in your flow. If you return something other than a string from your eval javascript, a default serialization will attempt to serialize the result for you. For web apps, this consists of normal json serialization, which should be fine in most cases. For java apps, however, default serialization will not be quite so universally useful.
One thing to note is that strings returned from eval in a java app are currently subjected to some transformations before arriving back in your flow. You can undo those transformations by running the text through a function such as the following:
function restoreJson(j) {
return j
.replace(/<quote>/g, '"')
.replace(/<colon>/g, ":")
.replace(/<semicolon>/g, ";");
}
With such a function, json text returned from App.eval
can be parsed in your flow with ease as in the following example:
var serializedResult = App.eval("Class.info('java.util.ArrayList')");
var result = JSON.parse(restoreJson(serializedResult));
Script errors in eval
Any exceptions raised while evaluating your eval script will be raised as exceptions in your flow. This allows them to be handled in the normal way with a try/catch construct.