node-reporter-sonarqube
Version:
Node test reporter for SonarQube
63 lines • 2.17 kB
JavaScript
import { getFilename, tag } from './utils.mjs';
function handleEvent(event, testName, tests) {
// eslint-disable-next-line default-case
switch (event.type) {
case 'test:start': {
const { file, name, nesting } = event.data;
while (testName.length > nesting) {
testName.pop();
}
testName.push(name);
const fname = getFilename(file);
tests[fname] ??= [];
break;
}
case 'test:pass':
case 'test:fail':
if (event.data.details.type !== 'suite') {
const file = getFilename(event.data.file);
const duration = event.data.details.duration_ms;
let status;
if (event.type === 'test:fail') {
status = 'fail';
}
else if (event.data.todo || event.data.skip) {
status = 'skip';
}
else {
status = 'pass';
}
tests[file].push({ name: testName.join(' » '), duration, status });
}
break;
}
}
export default async function* sonarQubeReporter(source) {
const testName = [];
const tests = {};
yield '<?xml version="1.0" encoding="UTF-8"?>\n';
yield `${tag('testExecutions', { version: '1' }, false)}\n`;
for await (const event of source) {
handleEvent(event, testName, tests);
}
for (const [file, testCases] of Object.entries(tests)) {
yield `\t${tag('file', { path: file }, false)}\n`;
for (const { name, duration, status } of testCases) {
let inner;
if (status === 'fail') {
inner = tag('failure');
}
else if (status === 'skip') {
inner = tag('skipped');
}
const attrs = {
name,
duration: duration.toFixed(),
};
yield `\t\t${tag('testCase', attrs, !inner, inner)}\n`;
}
yield '\t</file>\n';
}
yield '</testExecutions>\n';
}
//# sourceMappingURL=index.mjs.map