26. Appendix 8: Working with JavaScript require statements

26.1. Introduction

Vitaq AI provides the ability to develop test action scripts in JavaScript. In order to use JavaScript code in Test Action scripts a client needs to be started, this can either be the vitaq_client, which offers the choice of using the Selenium Webdriver or Webdriver IO or the scriptworks_vitaq_client which provides the interface to the Scriptworks visual JS scripting development environment. These clients provides an environment in which the JavaScript is executed.

This document will discuss ways to work with JavaScript effectively in Vitaq AI.

26.2. setUp/tearDown

Within Vitaq AI, the user can define a Test Action script in JavaScript for each of the test actions. In addition to this there is the option to create setUp and tearDown scripts that are executed on each seed, before and after any of the action scripts, respectively.

26.3. jsFunctions

There is also a jsFunctions script, which allows the user to define functions and variables that may be used in multiple of the test action scripts. The jsFunctions script is prepended to each of the test actions scripts and will be executed as a part of each test actions script. This means that the jsFunctions script cannot define variables that will get modified by the test action script, since the next Test Action would redefine the value. In order to save values, the data should be stored in Vitaq AI using the “sendDataToVitaq” and “readDataFromVitaq” commands, which can handle objects and arrays as well as single values.

26.4. More complexity

As the complexity of the project grows, it may be desirable to split certain classes and functions out into separate files and use the standard nodejs mechanisms for exporting them and requiring them into the functions that have already been described above. The files will not be visible in the Vitaq UI, but can be edited using your favourite IDE.

Let’s imagine I have a file called myFunctions.js that has the following content:

function helloWorld() {

console.log("**********************************************");

console.log("Hello World");

console.log("**********************************************");

}

exports.helloWorld = helloWorld;

In the first instance it makes sense to keep this file in the same location as the other files used by Vitaq. As complexity grows the location of scripts may also need to be re-ordered to specific needs.

This script exports the helloWorld function. In order to use it in a Test Action script we will need to “require” it. To make the function available to all test actions we should do this in the jsFunctions script.

26.5. NODE_PATH

Recall that the JavaScript is being executed in the context of the client. This means that if we use relative paths in the require statements we have to specify paths which are relative to the location of the client and not the other scripts. This might not be possible or easy.

Alternatively, we can specify absolute paths, in which we give the full filesystem path to the file we are requiring. This works well on Linux-like filesystems, but does not work on Windows where we need to specify a drive-letter. This approach also has the problem that the paths end up being hard coded into the script, so become less transportable.

The best solution is to use the NODE_PATH environment variable. This allows us to specify a list of paths that node will search along to find the module specified, in the same way as any standard search path mechanism. This approach maintains transportability, other users/projects simply need to define a correct NODE_PATH environment variable. See this link for more details: https://nodejs.org/api/modules.html#modules_loading_from_the_global_folders

We can then add a standard require statement into the jsFunctions file e.g.:

const myFunctions = require("myFunctions");

and we can see the path that it was loaded from using:

console.log("Path to the module: ", require.resolve("myFunctions"));

and now we can use the helloWorld function in a test action script using:

myFunctions.helloWorld()

26.6. Modifications

Now that we can load myFunctions, we may want to make modifications to it. However, it will quickly be noticed that modifications have no effect until the client is stopped and restarted. This is because require maintains a cache of the files that have been loaded and consequently does not re-load the file after a change. It quickly becomes annoying to have to stop and re-start the client after every edit. Fortunately, we can remove the file from the cache so that it is reloaded. We can do this with the following lines of code inserted into the top of the setup file:

const path = require.resolve("myFunctions");

delete require.cache[path]

Now every time the test is run the myFunctions file will be removed from the cache and the file reloaded. An entry such as the above will need to be made for each file that is being edited, if there are a lot of files the file names could be put in an array and a loop used to remove them from the cache as shown below:

1
2
3
4
5
6
7
async function setUp() {
    deleteFunctions = ["myFunctions"];
    for(let i = 0; i < deleteFunctions.length; i += 1) {
        const path = require.resolve(deleteFunctions[i])
        delete require.cache[path]
    }
}