TestMace
0.0.1-beta.24
Search
K

Script

This is a node that runs JavaScript scripts. It is going to help you solve the following tasks:
  • create complex tests to check the results of one or several nodes;
  • generate test data;
  • change other nodes' variables;
  • perform operations to bring the tested system to a specific state (set_up, tear_down);
  • debug and access all nodes' states.

Editing a Script

The script node editing window is divided into two parts: the code editor and the console output window. To close the console window click the
button.
You can use the toolbar above the output window to manage the console behavior:
/
- choose what to do with the console output.
- clear the window every time you run a script,
- display all previous outputs.
  • - auto scroll to the last line of the output
  • - clear the console window

Running a Script

Click the RUN button to execute the script. The execution continues to the last line of the code and finishes when all asynchronous tasks are done (e.g.setTimeout). The execution is successful if:
  • there are no syntax errors in the code;
  • the exceptions raised had been handled;
  • the execution took no more than 30 seconds (otherwise the execution will be terminated).
Since the call to the script is wrapped in a function, you need to use the return;command to avoid errors while terminating the script.
To get an error after script termination raise an exception withthrow new Error('Something went wrong');

Libraries

The script is executed in the Node.js virtual environment. Some Node.js modules and all standard JavaScript features supported by V8 are available.
The ECMAScript 6 standard is supported as well.

Available Node.js modules

  • fs - a module for interacting with the file system

Available third party libraries

  • lodash - a library that provides lots of utility functions
  • moment.js - a library for managing dates
  • CryptoJS - a collection of cryptographic algorithms
  • random-js - a mathematically correct random number generator library
  • faker.js - a library that lets you generate random data for different entities properties
  • chai.js - a library that provides a convenient API for making assertions
  • request - a library that provides a powerful HTTP client
  • axios - a library for sending HTTP requests

Execution Context

Objects and functions of the script's global scope are listed below.

Accessing third party modules

Every module stated above is automatically added to the execution context and is available in the global scope.

lodash

_.sum([1, 2, 3, 4]) // 10

moment.js

const now = moment(new Date()).format();

CryptoJS

const hash = crypto.MD5('Message');

random-js

const randomEngine = new random.Random();
const shuffledArray = randomEngine.shuffle([1,2,3,4,5]);

faker.js

const person = {
'name': faker.name.findName(),
'email': faker.internet.email()
};

chai.js

const foo = 'bar';
// success
assert.equal(foo, 'bar');
expect(foo).to.equal('bar');
// failure
assert.equal(1, 0);

request

Therequest library doesn't provide an interface for working with async/await. One possible solution is wrapping request call in Promiseobject
await new Promise((resolve, reject) => {
request('https://docs-ru.testmace.com', (error, response, body) => {
try {
assert.equal(error, null);
assert.equal(response.statusCode, 206);
assert.notEqual(body, null);
resolve();
} catch(e) {
reject(e);
}
});
})

axios

await axios.get('https://api.ipify.org?format=json')

console.*

There are different console output methods, such as log, info, warn, error, debug, exception.
Their signatures are equal to the signatures of their standard versions. In the console every method type is highlighted in different color. Each colored line contains the particular row and column where the output method is called. Events of the exception type are displayed along with the stack trace.

Asynchronous Code

In previous versions, TestMace tried to automatically determine if the script had been finished. In new versions, this feature and finish() function were removed.
A script can contain asynchronous calls (e.g. Promise, setTimeout, addEventListener, built-in modules callbacks, etc.). JavaScript provides a convenient mechanism for working with asynchronous operations - async/await. TestMace has full support of async/await and use this mechanism while performing asynchronous operations to determine if the script had been finished. Let's consider the following example:
console.log('1');
setTimeout(() => console.log('2'), 100);
console.log('3');
Console output:
1
3
To fix this you should explicitly mark the operation as an asynchronous usind await keyword. For convenience, we've added delay function that will allow you to pause the script. This function is compatible with async/await:
console.log('1');
await delay(100);
console.log('2');
console.log('3');
New console output:
1
2
3
For further information about async/await we recommend to read a documentation. Following modules support async/await:
  • fs.promises
  • axios
Finally, it can be mentioned that script execution can take up to 30 seconds. If it takes more then the error will be thrown.
There is an object used to access the project and the current Script node. It is called tm and it is available in the global scope.

tm

  • currentNode: nodeAPI - the current Script node API
  • project: nodeAPI - the project node API
  • env: envAPI - the API for accessing the environment variables
  • cookies: cookie[] - a list of cookies used in the project
  • system: object - an object, which contains the system's environment variables

nodeAPI

  • parent: nodeAPI - returns a parent node API and null for the project node
  • name: string - the given node name
  • type: string - the given node type
  • path: string - the path to the given node starting with the project root
  • children: nodeAPI[] - a list of child nodes APIs
  • findChild(name: string): nodeAPI - searches for the child node using its name and returns null if the node doesn't exist
  • next: nodeAPI - the API of the next node in the group. If the given node is the last one, null is returned
  • prev: nodeAPI - the API of the previous node in the group. If the given node is the first one, null is returned
  • nextNodes: nodeAPI[] - a list of all next nodes in the group. If the given node is the last one, an empty list is returned
  • prevNodes: nodeAPI[] - a list of all previous nodes in the group. If the given node is the first one, an empty list is returned
  • vars: object - the object that stores all static variables of the given node
  • dynamicVars: object - the object that stores all dynamic variables of the given node
  • setDynamicVar(name: string, value: any): void - sets the name dynamic variable with a certain value for the given node

requestNodeAPI

The interface of a RequestStep node is more advanced.
  • request: object - the object that stores the node's request configuration
  • response: object - the object that stores the results of the last request

envAPI

  • active: string - the active environment title
  • vars: object - the object that stores the current environment variables

Code examples

Recursive traversal of the node's children

The code below shows how to pass data to all children of the current node.
const current = tm.currentNode;
const parent = current.parent;
if (!parent) {
console.warn(`Parent of ${current.path} not found`);
return;
}
const value = parent.vars['ID'];
if (!value) {
console.warn(`Node ${parent.path} hasn't have value for ID`);
return;
}
console.log(`Parent ID = ${value}`);
const setIDToNode = (node) => {
node.setDynamicVar('ID', value);
};
const traverseDescendants = (node, func, depth) => {
node.children.forEach((child) => {
func(node);
indent = '\t'.repeat(depth);
console.debug(
`${indent}${child.path}`,
`${indent}Value: ${child.dynamicVars['ID']}`
);
traverseDescendants(child, func, depth+1);
});
};
traverseDescendants(parent, setIDToNode, 0);

Searching a node by its name

You can find a child node by its name using the findChild method:
const current = tm.currentNode;
const scriptNode = current.parent.findChild(current.name);
assert.equal(current, scriptNode);

Data generation and storing it to the variable

Here is how you can generate a random string identifier using the faker library and setting as the next node's dynamic variable UUI. Once you've run the script, you can reference this variable in an URL, request body, etc.
const current = tm.currentNode;
const uuid = faker.random.uuid();
const anotherNode = current.next;
anotherNode.setDynamicVar('UUID', uuid);

File Representation

{
"type": "object",
"properties": {
"type": {
"description": "Type of Script node",
"const": "Script",
"type": "string"
},
"script": {
"description": "Javascript code",
"type": "string"
},
"children": {
"description": "List of children names",
"type": "array",
"items": {
"type": "string"
},
"default": []
},
"variables": {
"$ref": "#/definitions/NodeVariables",
"description": "Node variables dictionary"
},
"name": {
"description": "Node name",
"type": "string"
}
},
"required": [
"children",
"name",
"script",
"type",
"variables"
],
"definitions": {
"NodeVariables": {
"type": "object",
"additionalProperties": {
"type": "string"
}
}
},
"$schema": "http://json-schema.org/draft-07/schema#"
}