96 lines
3.0 KiB
JavaScript
96 lines
3.0 KiB
JavaScript
import path from 'node:path';
|
|
import fs from 'node:fs';
|
|
import merge from 'lodash.merge';
|
|
import { getLogger } from '@sitespeed.io/log';
|
|
|
|
const log = getLogger('sitespeedio.plugin.budget');
|
|
|
|
/**
|
|
* Escapes XML special characters.
|
|
*
|
|
* @param {string} str - The text to escape.
|
|
* @returns {string} The escaped text.
|
|
*/
|
|
function xmlEscape(str) {
|
|
return String(str)
|
|
.replaceAll('&', '&')
|
|
.replaceAll('<', '<')
|
|
.replaceAll('>', '>')
|
|
.replaceAll('"', '"')
|
|
.replaceAll("'", ''');
|
|
}
|
|
|
|
/**
|
|
* Wraps a string in a CDATA block.
|
|
*
|
|
* @param {string} str - The string to wrap.
|
|
* @returns {string} The CDATA-wrapped string.
|
|
*/
|
|
function cdata(str) {
|
|
return `<![CDATA[${str}]]>`;
|
|
}
|
|
|
|
/**
|
|
* Writes a JUnit XML report mimicking the original output.
|
|
*
|
|
* @param {object} results - Object containing `failing` and `working` results.
|
|
* @param {string} dir - Directory where `junit.xml` will be written.
|
|
* @param {object} options - Options (expects `options.budget.friendlyName`).
|
|
*/
|
|
export function writeJunit(results, dir, options) {
|
|
const failing = results.failing || {};
|
|
const working = results.working || {};
|
|
const urls = Object.keys(merge({}, failing, working));
|
|
|
|
let totalTests = 0;
|
|
let totalFailures = 0;
|
|
let suitesXml = '';
|
|
|
|
for (const url of urls) {
|
|
const suiteName = `${options.budget.friendlyName || 'sitespeed.io'}.${url}`;
|
|
let suiteTests = 0;
|
|
let suiteFailures = 0;
|
|
let testCasesXml = '';
|
|
|
|
if (failing[url]) {
|
|
for (const result of failing[url]) {
|
|
suiteTests++;
|
|
totalTests++;
|
|
suiteFailures++;
|
|
totalFailures++;
|
|
const testCaseName = `${result.type}.${result.metric}`;
|
|
const failureMessage = `${result.metric} is ${result.friendlyValue || result.value}`;
|
|
testCasesXml += ` <testcase classname="${xmlEscape(url)}" name="${xmlEscape(testCaseName)}">\n`;
|
|
testCasesXml += ` <failure message="${xmlEscape(failureMessage)}"/>\n`;
|
|
testCasesXml += ` </testcase>\n`;
|
|
}
|
|
}
|
|
|
|
if (working[url]) {
|
|
for (const result of working[url]) {
|
|
suiteTests++;
|
|
totalTests++;
|
|
const testCaseName = `${result.type}.${result.metric}`;
|
|
const systemOutMessage = `${result.metric} is ${result.friendlyValue || result.value}`;
|
|
testCasesXml += ` <testcase classname="${xmlEscape(url)}" name="${xmlEscape(testCaseName)}">\n`;
|
|
testCasesXml += ` <system-out>${cdata(systemOutMessage)}</system-out>\n`;
|
|
testCasesXml += ` </testcase>\n`;
|
|
}
|
|
}
|
|
|
|
suitesXml += ` <testsuite name="${xmlEscape(suiteName)}" tests="${suiteTests}" failures="${suiteFailures}" errors="0" skipped="0">\n`;
|
|
suitesXml += testCasesXml;
|
|
suitesXml += ` </testsuite>\n`;
|
|
}
|
|
|
|
const xml =
|
|
`<?xml version="1.0" encoding="UTF-8"?>\n` +
|
|
`<testsuites tests="${totalTests}" failures="${totalFailures}" errors="0" skipped="0">\n` +
|
|
suitesXml +
|
|
`</testsuites>\n`;
|
|
|
|
const file = path.join(dir, 'junit.xml');
|
|
log.info('Write junit budget to %s', path.resolve(file));
|
|
fs.writeFileSync(file, xml);
|
|
}
|