implements a reporter flag w/ junit reporter type

This commit is contained in:
Andrew 2023-12-15 10:09:20 -05:00
parent f0e22cb5df
commit 2103ab20bf

View File

@ -1,7 +1,9 @@
const fs = require('fs'); const fs = require('fs');
const chalk = require('chalk'); const chalk = require('chalk');
const os = require('os');
const path = require('path'); const path = require('path');
const { forOwn } = require('lodash'); const xmlbuilder = require('xmlbuilder');
const { forOwn, result } = require('lodash');
const { exists, isFile, isDirectory } = require('../utils/filesystem'); const { exists, isFile, isDirectory } = require('../utils/filesystem');
const { runSingleRequest } = require('../runner/run-single-request'); const { runSingleRequest } = require('../runner/run-single-request');
const { bruToEnvJson, getEnvVars } = require('../utils/bru'); const { bruToEnvJson, getEnvVars } = require('../utils/bru');
@ -165,6 +167,73 @@ const getCollectionRoot = (dir) => {
return collectionBruToJson(content); return collectionBruToJson(content);
}; };
const makeJunitOutput = async (results, outputPath) => {
const output = {
testsuites: {
testsuite: []
}
};
results.forEach((result, idx) => {
const assertionTestCount = result.assertionResults ? result.assertionResults.length : 0;
const testCount = result.testResults ? result.testResults.length : 0;
const totalTests = assertionTestCount + testCount;
const suite = {
'@name': result.suitename,
'@errors': 0,
'@failures': 0,
'@skipped': 0,
'@tests': totalTests,
'@timestamp': new Date().toISOString().split('Z')[0],
'@hostname': os.hostname(),
'@time': result.runtime.toFixed(3),
testcase: []
};
result.assertionResults &&
result.assertionResults.forEach((assertion) => {
const testcase = {
'@name': `${assertion.lhsExpr} ${assertion.rhsExpr}`,
'@status': assertion.status,
'@classname': result.request.url,
'@time': (result.runtime / totalTests).toFixed(3)
};
if (assertion.status === 'fail') {
suite['@failures']++;
testcase.failure = [{ '@type': 'failure', '@message': assertion.error }];
}
suite.testcase.push(testcase);
});
result.testResults &&
result.testResults.forEach((test) => {
const testcase = {
'@type': 'testcase',
'@name': test.description,
'@status': test.status,
'@classname': result.request.url,
'@time': (result.runtime / totalTests).toFixed(3)
};
if (test.status === 'fail') {
suite['@failures']++;
testcase.failure = [{ '@type': 'failure', '@message': test.error }];
}
suite.testcase.push(testcase);
});
output.testsuites.testsuite.push(suite);
});
fs.writeFileSync(outputPath, xmlbuilder.create(output).end({ pretty: true }));
};
const builder = async (yargs) => { const builder = async (yargs) => {
yargs yargs
.option('r', { .option('r', {
@ -186,7 +255,13 @@ const builder = async (yargs) => {
}) })
.option('output', { .option('output', {
alias: 'o', alias: 'o',
describe: 'Path to write JSON results to', describe: 'Path to write file results to',
type: 'string'
})
.option('format', {
alias: 'f',
describe: 'Format for the file results',
default: 'json',
type: 'string' type: 'string'
}) })
.option('insecure', { .option('insecure', {
@ -204,12 +279,16 @@ const builder = async (yargs) => {
.example( .example(
'$0 run request.bru --output results.json', '$0 run request.bru --output results.json',
'Run a request and write the results to results.json in the current directory' 'Run a request and write the results to results.json in the current directory'
)
.example(
'$0 run request.bru --output results.xml --format junit',
'Run a request and write the results to results.xml in junit format in the current directory'
); );
}; };
const handler = async function (argv) { const handler = async function (argv) {
try { try {
let { filename, cacert, env, envVar, insecure, r: recursive, output: outputPath } = argv; let { filename, cacert, env, envVar, insecure, r: recursive, output: outputPath, format } = argv;
const collectionPath = process.cwd(); const collectionPath = process.cwd();
// todo // todo
@ -297,6 +376,11 @@ const handler = async function (argv) {
} }
} }
if (['json', 'junit'].indexOf(format) === -1) {
console.error(chalk.red(`Format must be one of "json" or "junit"`));
return;
}
// load .env file at root of collection if it exists // load .env file at root of collection if it exists
const dotEnvPath = path.join(collectionPath, '.env'); const dotEnvPath = path.join(collectionPath, '.env');
const dotEnvExists = await exists(dotEnvPath); const dotEnvExists = await exists(dotEnvPath);
@ -360,6 +444,8 @@ const handler = async function (argv) {
while (currentRequestIndex < bruJsons.length) { while (currentRequestIndex < bruJsons.length) {
const iter = bruJsons[currentRequestIndex]; const iter = bruJsons[currentRequestIndex];
const { bruFilepath, bruJson } = iter; const { bruFilepath, bruJson } = iter;
const start = process.hrtime();
const result = await runSingleRequest( const result = await runSingleRequest(
bruFilepath, bruFilepath,
bruJson, bruJson,
@ -371,7 +457,11 @@ const handler = async function (argv) {
collectionRoot collectionRoot
); );
results.push(result); results.push({
...result,
runtime: process.hrtime(start)[0] + process.hrtime(start)[1] / 1e9,
suitename: bruFilepath.replace('.bru', '')
});
// determine next request // determine next request
const nextRequestName = result?.nextRequestName; const nextRequestName = result?.nextRequestName;
@ -413,7 +503,12 @@ const handler = async function (argv) {
results results
}; };
if (format === 'json') {
fs.writeFileSync(outputPath, JSON.stringify(outputJson, null, 2)); fs.writeFileSync(outputPath, JSON.stringify(outputJson, null, 2));
} else if (format === 'junit') {
makeJunitOutput(results, outputPath);
}
console.log(chalk.dim(chalk.grey(`Wrote results to ${outputPath}`))); console.log(chalk.dim(chalk.grey(`Wrote results to ${outputPath}`)));
} }
@ -432,5 +527,6 @@ module.exports = {
desc, desc,
builder, builder,
handler, handler,
printRunSummary printRunSummary,
makeJunitOutput
}; };