Merge branch 'main' into plugin-options
This commit is contained in:
commit
cf0b2a9ca7
|
|
@ -81,12 +81,18 @@ export async function loadPlugins(pluginNames, options, context, queue) {
|
|||
plugins.push(p);
|
||||
} catch (error) {
|
||||
// try global
|
||||
let { default: plugin } = await importGlobalSilent(name);
|
||||
if (plugin) {
|
||||
let p = new plugin(options, context, queue);
|
||||
plugins.push(p);
|
||||
} else {
|
||||
console.error("Couldn't load plugin %s: %s", name, error_);
|
||||
try {
|
||||
let { default: plugin } = await importGlobalSilent(name);
|
||||
if (plugin) {
|
||||
let p = new plugin(options, context, queue);
|
||||
plugins.push(p);
|
||||
} else {
|
||||
console.error("Couldn't load plugin %s: %s", name, error_);
|
||||
// if it fails here, let it fail hard
|
||||
throw error;
|
||||
}
|
||||
} catch {
|
||||
console.error("Couldn't find/load plugin %s", name);
|
||||
// if it fails here, let it fail hard
|
||||
throw error;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,69 +1,95 @@
|
|||
import path from 'node:path';
|
||||
import { parse } from 'node:url';
|
||||
|
||||
import jrp from 'junit-report-builder';
|
||||
|
||||
import fs from 'node:fs';
|
||||
import merge from 'lodash.merge';
|
||||
import { getLogger } from '@sitespeed.io/log';
|
||||
|
||||
const log = getLogger('sitespeedio.plugin.budget');
|
||||
|
||||
import merge from 'lodash.merge';
|
||||
/**
|
||||
* 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) {
|
||||
// lets have one suite per URL
|
||||
const urls = Object.keys(merge({}, results.failing, results.working));
|
||||
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) {
|
||||
// The URL can be an alias
|
||||
let name = url;
|
||||
if (url.startsWith('http')) {
|
||||
const parsedUrl = parse(url);
|
||||
name = url.startsWith('http') ? url : url;
|
||||
parsedUrl.hostname.replaceAll('.', '_') +
|
||||
'.' +
|
||||
parsedUrl.path.replaceAll('.', '_').replaceAll('/', '_');
|
||||
}
|
||||
const suiteName = `${options.budget.friendlyName || 'sitespeed.io'}.${url}`;
|
||||
let suiteTests = 0;
|
||||
let suiteFailures = 0;
|
||||
let testCasesXml = '';
|
||||
|
||||
const suite = jrp
|
||||
.testSuite()
|
||||
.name(options.budget.friendlyName || 'sitespeed.io' + '.' + name);
|
||||
|
||||
if (results.failing[url]) {
|
||||
for (const result of results.failing[url]) {
|
||||
suite
|
||||
.testCase()
|
||||
.className(name)
|
||||
.name(result.type + '.' + result.metric)
|
||||
.failure(
|
||||
result.metric + ' is ' + result.friendlyValue ||
|
||||
result.value +
|
||||
' and limit ' +
|
||||
result.limitType +
|
||||
' ' +
|
||||
result.friendlyLimit ||
|
||||
result.limit + ' ' + url
|
||||
);
|
||||
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 (results.working[url]) {
|
||||
for (const result of results.working[url]) {
|
||||
suite
|
||||
.testCase()
|
||||
.className(name)
|
||||
.name(result.type + '.' + result.metric)
|
||||
.standardOutput(
|
||||
result.metric + ' is ' + result.friendlyValue ||
|
||||
result.value +
|
||||
' and limit ' +
|
||||
result.limitType +
|
||||
' ' +
|
||||
result.friendlyLimit ||
|
||||
result.limit + ' ' + url
|
||||
);
|
||||
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));
|
||||
jrp.writeTo(file);
|
||||
fs.writeFileSync(file, xml);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,36 +1,44 @@
|
|||
import path from 'node:path';
|
||||
import fs from 'node:fs';
|
||||
import { EOL } from 'node:os';
|
||||
import tap from 'tape';
|
||||
import { getLogger } from '@sitespeed.io/log';
|
||||
|
||||
const log = getLogger('sitespeedio.plugin.budget');
|
||||
|
||||
export function writeTap(results, dir) {
|
||||
const file = path.join(dir, 'budget.tap');
|
||||
log.info('Write budget to %s', path.resolve(file));
|
||||
const tapOutput = fs.createWriteStream(file);
|
||||
tap.createStream().pipe(tapOutput);
|
||||
|
||||
const lines = [];
|
||||
lines.push('TAP version 13');
|
||||
let testCount = 0;
|
||||
|
||||
// Iterate over each result group (e.g. "passing" and "failing")
|
||||
for (const resultType of Object.keys(results)) {
|
||||
const urls = Object.keys(results.failing);
|
||||
|
||||
const group = results[resultType];
|
||||
if (!group) {
|
||||
continue;
|
||||
}
|
||||
const urls = Object.keys(group);
|
||||
for (const url of urls) {
|
||||
for (const result of results.failing[url]) {
|
||||
tap(result.type + '.' + result.metric + ' ' + url, function (t) {
|
||||
let extra = '';
|
||||
if (resultType === 'failing') {
|
||||
extra =
|
||||
' limit ' + result.limitType + ' ' + result.friendlyLimit ||
|
||||
result.limit + EOL;
|
||||
}
|
||||
t.ok(
|
||||
resultType === 'failing' ? false : true,
|
||||
result.type + '.' + result.metric + ' ' + result.friendlyValue ||
|
||||
result.value + ' ' + extra + ' ' + url
|
||||
);
|
||||
t.end();
|
||||
});
|
||||
for (const result of group[url]) {
|
||||
testCount += 1;
|
||||
const testTitle = `${result.type}.${result.metric} ${url}`;
|
||||
let extra = '';
|
||||
if (resultType === 'failing') {
|
||||
extra = ` limit ${result.limitType} ${result.friendlyLimit || result.limit}`;
|
||||
}
|
||||
const valueDisplay = result.friendlyValue || result.value;
|
||||
|
||||
lines.push(`# ${testTitle}`);
|
||||
const status = resultType === 'failing' ? 'not ok' : 'ok';
|
||||
lines.push(
|
||||
`${status} ${testCount} ${testTitle} ${valueDisplay}${extra ? ` ${extra}` : ''}`
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lines.push(`1..${testCount}`);
|
||||
fs.writeFileSync(file, lines.join(EOL) + EOL);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,7 +25,6 @@
|
|||
"fast-stats": "0.0.7",
|
||||
"import-global": "1.1.1",
|
||||
"influx": "5.9.3",
|
||||
"junit-report-builder": "3.2.1",
|
||||
"lodash.get": "4.4.2",
|
||||
"lodash.merge": "4.6.2",
|
||||
"lodash.set": "4.3.2",
|
||||
|
|
@ -34,7 +33,6 @@
|
|||
"ora": "8.0.1",
|
||||
"pug": "3.0.3",
|
||||
"simplecrawler": "1.1.9",
|
||||
"tape": "5.8.1",
|
||||
"yargs": "17.7.2"
|
||||
},
|
||||
"bin": {
|
||||
|
|
|
|||
|
|
@ -96,7 +96,6 @@
|
|||
"fast-stats": "0.0.7",
|
||||
"import-global": "1.1.1",
|
||||
"influx": "5.9.3",
|
||||
"junit-report-builder": "3.2.1",
|
||||
"lodash.get": "4.4.2",
|
||||
"lodash.merge": "4.6.2",
|
||||
"lodash.set": "4.3.2",
|
||||
|
|
@ -105,7 +104,6 @@
|
|||
"ora": "8.0.1",
|
||||
"pug": "3.0.3",
|
||||
"simplecrawler": "1.1.9",
|
||||
"tape": "5.8.1",
|
||||
"yargs": "17.7.2"
|
||||
},
|
||||
"overrides": {
|
||||
|
|
|
|||
Loading…
Reference in New Issue