Skip to content

Instantly share code, notes, and snippets.

@pdehaan
Created January 10, 2024 08:10
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save pdehaan/6997d1244205d799909c26a4793e3701 to your computer and use it in GitHub Desktop.
Save pdehaan/6997d1244205d799909c26a4793e3701 to your computer and use it in GitHub Desktop.
Convert a glob of JUnit XML reports into a JSON object... for reasons.
import fs from "node:fs";
import fg from "fast-glob";
import { XMLParser } from "fast-xml-parser";
import { z } from "zod";
const testsuiteSchema = z.object({
testsuite: z.array(
z.object({
testcase: z.array(
z.object({
properties: z
.object({
property: z.array(
z.any()
// z.object({
// name: z.string(),
// value: z.string(),
// })
),
})
.optional(),
name: z.string(),
classname: z.string(),
time: z.coerce.number(),
skipped: z.any().optional(),
// If something was logged to STDOUT.
"system-out": z.string().trim().optional(),
}).strict()
),
name: z.string(),
timestamp: z.coerce.date(),
hostname: z.string(),
tests: z.coerce.number(),
failures: z.coerce.number(),
skipped: z.coerce.number(),
time: z.coerce.number(),
errors: z.coerce.number(),
}).strict(),
),
id: z.string(),
name: z.string(),
tests: z.coerce.number(),
failures: z.coerce.number(),
skipped: z.coerce.number(),
errors: z.coerce.number(),
time: z.coerce.number(),
}).strict();
const $testsuites = junitToJSON("./artifacts/*.xml", testsuiteSchema);
fs.writeFileSync("test.json", JSON.stringify($testsuites, null, 2));
function junitToJSON(xmlGlob, schema) {
const testsuites = fg.globSync(xmlGlob).reduce((acc = [], file = "") => {
const data = readXMLSync(file);
data.testsuites.testsuite = data.testsuites.testsuite.map((suite) => {
// Make sure `testcase[]` is always an array.
if (!Array.isArray(suite.testcase)) {
suite.testcase = [suite.testcase];
}
// Make sure `properties.property[]` is always an array.
suite.testcase = suite.testcase.map((testcase) => {
if (
testcase.properties?.property &&
!Array.isArray(testcase.properties.property)
) {
testcase.properties.property = [testcase.properties.property];
}
return testcase;
});
return suite;
});
return acc.concat(data.testsuites);
}, []);
if (schema) {
return z.array(schema).parse(testsuites);
}
return testsuites;
}
function readXMLSync(file) {
const parser = new XMLParser({
ignoreAttributes: false,
ignoreDeclaration: true,
attributeNamePrefix: "",
});
const xmlStr = fs.readFileSync(file, "utf-8");
return parser.parse(xmlStr);
}
{
"name": "fxa-functional-tests-artifacts-xml",
"version": "1.0.0",
"description": "",
"main": "index.js",
"type": "module",
"scripts": {
"start": "node index"
},
"keywords": [],
"author": "Peter deHaan <peter@deseloper.com>",
"license": "MPL-2.0",
"dependencies": {
"fast-glob": "^3.3.2",
"fast-xml-parser": "^4.3.2",
"zod": "^3.22.4"
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment