In some use cases, it is useful to be able to run a piece of
js code in the browser from within a test. For instance, accessing
window object to perform actions like
window.focus(), retrieving viewport dimensions using
window.innerWidth, running a plugin in form of
js snippet at the beginning of a test to see how the site function with that plugin installed.
In some lesser used cases, you may want to run custom
js code that take arguments from the test, do some process using the arguments and return some data to the test for validation. For example, setting a timeout to hide an element from page (whose reference was supplied from test) and return some data back to the test for validation. Doing these js tricks may not be required if your tests only purely validate actions that an end user can perform but still there may be some use cases depending on what you're trying to accomplish.
Following functions can be used for running
js from a test:
executeScript(stringScript, ...args) executeAsyncScript(stringScript, ...args)
jsscript is injected into the page and executed as the body of an anonymous function. It's execution context will be the current browsing context.
The arguments passed to a function can be accessed in the script using arguments object. See below:
button = findElement('Submit', by.text)executeScript('arguments.style.display = "none"', button)
executeScriptruns the script synchronously on the page which means it will block page execution until completed. The script can have a
returnstatement to return a value. Test resumes only once the script has completed execution.
executeAsyncScriptrun the script asynchronously on the page and doesn't block page execution. If you're accessing network or need to run script with a delay, use this function. Note that even though it runs the script asynchronously on page, the test will block until the execution has finished.
Being asynchronous, the test can't know about the completion on it's own. It's your responsibility to invoke a provided callback to inform the completion. The callback is injected as the last argument to
executeAsyncScriptand is used to return a value as well. See below:
noteId = 150data = executeAsyncScript(` const callback = arguments[arguments.length - 1]; fetch('https://some-app.com/notes/' + arguments) .then(data => callback(data)) .catch(error => callback(error));`, noteId)noteDescription = data.desc
We now know how to use the functions, access arguments and use callback. Let's understand what can be passed as arguments, how values returned from the script are resolved and few other things like adjusting timeout value.
You can supply any supported type in ZWL as arguments such as
mapare internally converted to be accessed as
To pass an
element, just use the
findElementfunctions. ZWL internally converts
elementIdto real element before passing to script. You may then access the
elementin script as if it was retrieved using one of the element retrieval method such as
document.getElementById(ID). If some of your
elementIds are kept in a
map, those are also converted recursively into a real elements before passing to script. You can assume that ZWL's elementIds are the real elements.
Whatever values you returned from script are converted back into ZWL supported types. For instance, if you return a js
object it will be converted to a
array it will be converted to a
list, if it's an element it will be converted into an
Anything kept in js
object will be recursively converted into ZWL types too so that you can use the returned values as you'd use any other value in a ZWL test.
A script is allowed to run for a limited time before a timeout error occurs to prevent forever running scripts. If you think your scripts may take more time than the default timeout, you can check options to customize it.
# Get window's viewport widthviewportHeight = executeScript('window.innerWidth')