Use the following Node.js and Bash script examples with the mabl Reporting API batch results endpoint to programmatically retrieve a set of test run results and transform them into a CSV format.
Other CSV export options
If you're looking for a different way to export mabl test results as a CSV, you can try one of the following:
- From the UI - download a CSV of recent results from the Results > By test page or from the plan details page for a specific plan.
- Google Sheets - set up a custom integration to append mabl test results to a Google Sheet after every run.
Setup
Before implementing the script, make sure you have the correct setup:
To execute the example script you’ll need Node.js installed, which is available for all major platforms here.
With Node.js installed, you also need to install the axios library to communicate with APIs like the mabl Reporting API. From the command line, in a directory where you want to create this script, you can install the axios library using npm, which comes with Node.js:
npm install axios@1.3.3 --save
To execute the example scripts, you need a command line capable of executing bash scripts with a couple common utilities installed:
- curl
- jq: available for a variety of platforms here
Retrieving results
Save the following script as index.js in the same directory where you installed axios. You can edit the TEST_RUN_FIELDS list in the script if you do not want all fields in the CSV output or want to change their order. If you add a field whose value is a list, such as test_labels or plan_labels, join it to a string first (for example, value.join('|')), since a raw array isn't valid CSV.
const axios = require('axios');
const process = require('process');
const fs = require('fs');
// Check correct number of arguments and provide usage help
if (process.argv.length < 5 || process.argv.length > 6) {
console.error('Usage: node index.js WORKSPACE_ID MABL_API_KEY OUTPUT_FILE [API_QUERY_PARAMS]');
process.exit(1);
}
const WORKSPACE_ID = process.argv[2];
const MABL_API_KEY = process.argv[3];
const OUTPUT_FILE = process.argv[4];
const QUERY_PARAMS = process.argv[5];
let MABL_ENDPOINT_URL = `https://api.mabl.com/results/workspace/${WORKSPACE_ID}/testRuns?${QUERY_PARAMS || ''}`;
// Ordered fields to extract from each test run
const TEST_RUN_FIELDS = [
'application_id',
'application_name',
'environment_id',
'environment_name',
'initial_url',
'scenario_name',
'browser',
'browser_version',
'execution_runner_type',
'plan_id',
'plan_name',
'plan_run_id',
'test_id',
'test_version',
'test_name',
'test_type',
'branch',
'test_run_id',
'test_run_app_url',
'is_ad_hoc_run',
'failure_category',
'start_time',
'end_time',
'run_time',
'status',
'success',
'trigger_type',
'triggering_deployment_event_id',
'emulation_mode',
'metrics.cumulative_speed_index',
'metrics.cumulative_api_response_time',
'metrics.accessibility_rule_violations.critical',
'metrics.accessibility_rule_violations.serious',
'metrics.accessibility_rule_violations.moderate',
'metrics.accessibility_rule_violations.minor',
'outcome',
'failure_summary.step_number',
'failure_summary.flow_name',
'failure_summary.error',
'failure_summary.image_artifact_url',
'failure_analysis.headline',
];
function getField(object, field) {
return field
.split('.')
.reduce((currentObject, currentField) => currentObject === undefined || currentObject === null ? undefined : currentObject[currentField], object);
}
async function getRunResults(cursor) {
let url = MABL_ENDPOINT_URL;
if (cursor !== undefined) {
url += `&cursor=${cursor}`;
}
console.log(cursor === undefined ? 'Fetching first page of results...' : `Fetching results with cursor=${cursor}`);
const response = await axios.get(url, {
auth: {
username: 'key',
password: MABL_API_KEY,
}
});
if (!response.data) {
console.error(`No data returned from mabl API; full response: ${JSON.stringify(response)}`);
}
return response.data;
}
function getResultFields(results) {
return results.map(result => {
return TEST_RUN_FIELDS
.map(field => escapeValue(getField(result, field)));
});
}
function escapeValue(value) {
if (value === undefined || value === null) {
return '';
}
// RFC 4180: quote any field that contains a comma, double-quote, or newline,
// and escape embedded double-quotes by doubling them.
const stringValue = String(value);
if (/[",\r\n]/.test(stringValue)) {
return '"' + stringValue.replace(/"/g, '""') + '"';
}
return stringValue;
}
function resultRowsToCsv(results) {
return results
.map(row => row.join(','))
.join('\n') + '\n';
}
async function getAllRunResults() {
let cursor;
const outStream = fs.createWriteStream(OUTPUT_FILE);
outStream.write(resultRowsToCsv([TEST_RUN_FIELDS]));
do {
const data = await getRunResults(cursor);
if (data) {
cursor = data.cursor;
const results = getResultFields(data.test_results || []);
outStream.write(resultRowsToCsv(results));
} else {
break;
}
} while (cursor !== undefined);
outStream.end();
}
getAllRunResults().catch(console.error);
This script automatically paginates through every page of results. For large result sets, expect it to make several requests. It will print a log line for each page.
Running the script
The index.js script can be called from the command line to retrieve results from the mabl Reporting API and save them to a CSV file:
node index.js WORKSPACE_ID API_KEY OUT_FILE QUERY_PARAMETER_STRING
The index.js script takes the following arguments:
-
WORKSPACE_ID: replaceWORKSPACE_IDbelow with your workspace ID -
API_KEY: have a workspace owner create or use an existing mabl Viewer API key and replaceAPI_KEYwith the key secret. -
OUT_FILE: replaceOUT_FILEwith the output file name, such as results.csv -
QUERY_PARAMETER_STRING: use this optional argument to filter your results.
See the API documentation for the batch run results endpoint to find available query parameters and create an appropriate query parameter string to replace QUERY_PARAMETER_STRING. For example, to specify all runs within a time range, use https://www.epochconverter.com/ to get the desired start and end timestamp in milliseconds and use them with in a query parameter string as follows: earliest_run_start_time=1677506400000&latest_run_start_time=1677592800000
Save the following bash script as mabl_results_to_csv.sh. This script transforms the JSON results of the mabl batch results endpoint into a CSV format. Some notes to keep in mind:
- If you do not want all fields or want to change their order, edit the list of fields at the top of the
jqfilter. Each entry is the path to a field. - For a nested value, list each key in order. For example,
["metrics","cumulative_speed_index"]. - Array-valued fields, such as
test_labelsorplan_labels, aren't supported by this list as written, because jq's@csvrejects arrays; to include one, join it to a string in a customjqexpression first.
#!/bin/bash
# Transform mabl batch results JSON (provided on STDIN) into CSV.
# Pass -p to include the header row.
# Uses jq's built-in @csv, which is RFC 4180 compliant: it quotes any field
# containing a comma, double-quote, or newline and escapes embedded quotes.
if [[ "$1" == "-h" || "$1" == "-help" || "$1" == "--help" || "$1" == "help" ]]; then
echo "Transform mabl batch results JSON (provided on STDIN) into a CSV format with an optional header row (included if -p is provided as an argument)"
echo "Example usage: cat results.json | $0 -p > results.csv"
exit 0
fi
print_header=false
[ "$1" == "-p" ] && print_header=true
jq -r --argjson print_header "$print_header" '
# Each entry is the path (as an array) to a field in a test result.
[
["application_id"],
["application_name"],
["environment_id"],
["environment_name"],
["initial_url"],
["scenario_name"],
["browser"],
["browser_version"],
["execution_runner_type"],
["plan_id"],
["plan_name"],
["plan_run_id"],
["test_id"],
["test_version"],
["test_name"],
["test_type"],
["branch"],
["test_run_id"],
["test_run_app_url"],
["is_ad_hoc_run"],
["failure_category"],
["start_time"],
["end_time"],
["run_time"],
["status"],
["success"],
["trigger_type"],
["triggering_deployment_event_id"],
["emulation_mode"],
["metrics","cumulative_speed_index"],
["metrics","cumulative_api_response_time"],
["metrics","accessibility_rule_violations","critical"],
["metrics","accessibility_rule_violations","serious"],
["metrics","accessibility_rule_violations","moderate"],
["metrics","accessibility_rule_violations","minor"],
["outcome"],
["failure_summary","step_number"],
["failure_summary","flow_name"],
["failure_summary","error"],
["failure_summary","image_artifact_url"],
["failure_analysis","headline"]
] as $fields
| (if $print_header then [$fields[] | join(".")] | @csv else empty end),
(.test_results[] | [ $fields[] as $p | getpath($p) ] | @csv)
'
After saving the script, you can make it executable from the bash command prompt with: chmod +x mabl_results_to_csv.sh
Call the script from a bash command line to process results from the mabl Reporting API and save them to a CSV. Replace WORKSPACE_ID with your workspace ID and replace API_KEY with a mabl Viewer API key.
curl "https://api.mabl.com/results/workspace/WORKSPACE_ID/testRuns?advanced_metrics=true" \
-u "key:API_KEY" \
| ./mabl_results_to_csv.sh -p \
> results.csv
Retrieving additional results
mabl test results are paginated. To retrieve more than the most recent page of runs, you need to call the endpoint repeatedly, advancing through each page of results. You can create a second bash script as mabl_get_batch_results.sh that calls the first and handles this paging for you:
#!/bin/bash
# Check number of arguments and provide help with usage example if incorrect
if [[ $# -lt 3 || $# -gt 4 ]]; then
echo "Usage: $0 WORKSPACE_ID API_KEY OUTPUT_FILENAME [query parameter string]"
echo "Example: $0 'MY-WORKSPACE-ID-w' 'API-KEY' 'results.csv' 'test_id=MY-TEST-ID-j&earliest_run_start_time=1677587852400'"
exit 1
fi
workspace_id=$1
api_key=$2
output_file=$3
endpoint="https://api.mabl.com/results/workspace/$workspace_id/testRuns?"
# Add given query parameters to API endpoint URL
if [ "$4" ]; then
endpoint+="$4"
fi
# Set initial cursor value to null
cursor="null"
# Add header row to output file
echo '{"test_results":[]}' | ./mabl_results_to_csv.sh -p >> $output_file
# Loop until there is no more data to retrieve
while : ; do
echo "Getting batch of results with cursor = $cursor"
# Make API call with current cursor value
if [[ $cursor != "null" ]]; then
response=$(curl -s "$endpoint&cursor=$cursor" -u "key:$api_key")
else
response=$(curl -s "$endpoint" -u "key:$api_key")
fi
# Extract cursor value from API response
cursor=$(echo $response | jq -r '.cursor')
# Process data from API response
echo "Processing batch of results with cursor = $cursor"
echo "$response" | ./mabl_results_to_csv.sh >> $output_file
echo "Finished processing batch of results with cursor = $cursor"
[[ $cursor != "null" ]] || break
done
After saving the script, make it executable from a bash command prompt with:
chmod +x mabl_get_batch_results.sh
Call the script from the command line to retrieve multiple pages of results from the mabl Reporting API and save them to a CSV file:
./mabl_get_batch_results.sh WORKSPACE_ID API_KEY OUT_FILE 'QUERY_PARAMETER_STRING'
The script takes the following arguments:
-
WORKSPACE_ID: replaceWORKSPACE_IDbelow with your workspace ID -
API_KEY: have a workspace owner create or use an existing mabl Viewer API key and replaceAPI_KEYwith the key secret. -
OUT_FILE: replaceOUT_FILEwith the name of the output file, such as results.csv -
QUERY_PARAMETER_STRING: use this optional argument to filter your results.
See the API documentation for the API batch run results endpoint to find available query parameters and create an appropriate query parameter string to replace QUERY_PARAMETER_STRING. For example, to specify all runs within a time range, use https://www.epochconverter.com/ to get the desired start and end timestamp in milliseconds and use them with in a query parameter string as follows: earliest_run_start_time=1677506400000&latest_run_start_time=1677592800000