ATF Custom Step Configuration for Calling Server Scripts with Inputs and Output
Hi,
As part of my ATF test development, I ran into cases where I wanted to call a server-side script and pass in inputs and get outputs. Essentially, I wanted the functionality of the the OOTB Run Server Side Script, but I wanted to have inputs and additional outputs.
I created a step configuration that includes input variables and output variables of different types, such as Document ID, Table, JSON, String. It also includes an input script like the OOTB Run Server Side Script. I've found having this step configuration available has made my testing easier. I use this step configuration, to pass parameters and other step outputs to server side scripts.
Here is the information on creating this step configuration:
Step Environment: Server Independent
Category: Server
Description Script:
function generateDescription() {
// the global variable 'step' represents the current glide record
try {
// the global variable 'step' represents the current glide record
var tokens = [];
var description = "";
// your code here
description = "Run the script using the following parameters.";
if (!gs.nil(step.inputs.u_table.getDisplayValue())) {
description += "\n-- u_table: " + step.inputs.u_table.getDisplayValue() + "";
}
if (!gs.nil(step.inputs.u_record.getDisplayValue())) {
description += "\n-- u_record: " + step.inputs.u_record.getDisplayValue() + "";
}
if (!gs.nil(step.inputs.u_input_string)) {
description += "\n-- u_input_string: " + step.inputs.u_input_string + "";
}
if (!gs.nil(step.inputs.u_json_string)) {
description += "\n-- u_json_string: " + step.inputs.u_json_string;
}
var outparams = "";
var scriptString = step.inputs.u_script + "";
if(scriptString.search(/^[\s]*outputs\.u_json_string/gm) != -1){
outparams += "-- u_json_string\n";
}
if(scriptString.search(/^[\s]*outputs\.u_out_string/gm) != -1){
outparams += "-- u_out_string\n";
}
if(scriptString.search(/^[\s]*outputs\.u_table/gm) != -1){
outparams += "-- u_table\n";
}
if(scriptString.search(/^[\s]*outputs\.u_record/gm) != -1){
outparams += "-- u_record\n";
}
if(!gs.nil(outparams)){
description += "\n\n The script outputs values in following output parameters\n";
description += outparams;
}
description = gs.getMessage(description, tokens);
return description;
} catch (e) {
return description + "\n" + e;
}
}
generateDescription();
Execution Script:
(function executeStep(inputs, outputs, stepResult, timeout) {
var outputMessage = "";
var errorText = "";
if (gs.nil(inputs.u_script)) {
// desired outcome did not occur within the timeout
errorText = "Missing the script parameter.";
outputMessage += "Failure! {0}";
stepResult.setOutputMessage(get.getMessage(outputMessage, errorText));
stepResult.setFailed();
}
try {
var script = inputs.u_script;
eval(script); // Here is where the input script gets executed.
} catch (e) {
outputMessage += "\n\nFailure! {0}";
stepResult.setOutputMessage(gs.getMessage(outputMessage, e));
stepResult.setFailed();
}
}(inputs, outputs, stepResult, timeout));
Input Variables:
- Table: Column Name = u_table, Type = Table Name
- Record: Column Name = u_record, Type = Document ID
- Input String: Column Name = u_input_string, Type = String, Max = 5000
- JSON String: Column Name = u_json_string, Type = String, Max = 5000
- Script: Column Name = u_script, Type = Script
Output Variables:
- Table: Column Name = u_table, Type = Table Name
- Record: Column Name = u_record, Type = Document ID
- Out String: Column Name = u_out_string, Type = String, Max = 5000
- JSON String: Column Name = u_json_string, Type = String, Max = 5000
Default Value for the u_script Input Variable:
Because this is executing inside the function in the Execute script, it has access to the variables set in that function: inputs, outputs, stepResult, timeout.
try {
// Get the input parameters
var record = (!gs.nil(inputs.u_record)) ? inputs.u_record + "" : "";
var table = (!gs.nil(inputs.u_table )) ? inputs.u_table + "" : "";
var jsonString = (!gs.nil(inputs.u_json_string )) ? inputs.u_json_string + "" : "";
var inputString = (!gs.nil(inputs.u_input_string )) ? inputs.u_input_string + "" : "";
if(!gs.nil(inputs.u_json_string)){
var obj = JSON.parse(jsonString);
}
// add content to the message;
var message = "";
// set the "success" variable to false if there is an expected result in the script
var success = true;
// Set output values
// outputs.u_record = "";
// outputs.u_table= "";
// outputs.u_json_string = "";
// outputs.u_out_string = "";
if (success) {
stepResult.setOutputMessage('Success!' + message);
stepResult.setSuccess();
} else {
stepResult.setOutputMessage('Failure!\n' + message);
stepResult.setFailed();
}
} catch (e) {
stepResult.setOutputMessage('Failure!\n' + e + message);
stepResult.setFailed();
}
// The inputs are a map of the variables defined in the inputs related list below.
// Inputs are consumed in the step configuration. Input
// values may be hardcoded or mapped from the outputs of a previous step.
// If a test author using your step uses mapping to pass in an output from a previous
// test step then when referencing the input variable the mapping will be resolved
// automatically
// Example:
// var myRecords = new GlideRecord(inputs.table);
//
// The outputs are a map of the variables defined in the outputs related list.
// Outputs should be set (assigned) in order to pass data out of a test step that
// can be consumed my mapping as an input to subsequent steps.
// Example:
// outputs.table = gr.getRecordClassName()
//
//
// Note that inputs and outputs are strongly typed as defined in their variable definition.
// Their behavior is the same as a dictionary defined field of the same type in a table.
//
// The stepResult is a simple API for controlling the step pass/fail and logging with three
// methods:
// stepResult.setFailed: Causes step to fail
//
// stepResult.setSuccess: Causes step to succeed
//
// stepResult.setOutputMessage: Log a message to step results after step executes.
// Can only be called once or will overwrite previous
// message
//
// If neither setFailed or setSuccess is called the default is to succeed.
//
// See 'Record Query' for an example of a scripted step config
// or see test 'Check change approvals get generated'
//
// Example usage of step timeout in script
// var counter = 0;
// // 'timeout' is a field on the step form
// while (counter <= timeout) {
// if (desiredOutcome) {
// stepResult.setOutputMessage('Success!');
// stepResult.setSuccess();
// return;
// }
// counter++;
// gs.sleep(1000);
// }
//
// // desired outcome did not occur within the timeout
// stepResult.setOutputMessage('Failure!');
// stepResult.setFailed();
//
Note: This custom step configuration cannot be used to run Jasmine tests. It's not a problem for me because I don't know how to run Jasmine tests.
One thing, I've been thinking about doing is adding a few more output variables, so that I can get more than one record ID from a script.
I hope that this helps someone else as much as it has helped me.
Thanks,
Cody
https://www.servicenow.com/community/developer-articles/atf-custom-step-configuration-for-calling-server-scripts-with/ta-p/2324226