Skip to content

Commit

Permalink
updating code
Browse files Browse the repository at this point in the history
  • Loading branch information
agracio committed Nov 13, 2024
1 parent 572d9d5 commit 273edff
Show file tree
Hide file tree
Showing 27 changed files with 2,502 additions and 232 deletions.
12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,8 @@ https://github.com/adamgruber/mochawesome
- Converts <skipped> test messages to Mochawesome test context values.
- Converts <failure> and <error> elements to Mochawesome error stack.
- Test suites are displayed in alphabetical order by `file` and `classname` attributes.
- Tests suites without any tests are not displayed.
- Attachments currently not supported.
- Tests suites without any tests are excluded from Mochawesome and JUnit.
- Attachments are not supported.

### JUnit

Expand All @@ -70,10 +70,10 @@ https://github.com/adamgruber/mochawesome

### Visual Studio TRX

- Converts **<Output><ErrorInfo><Message>** to JUnit **<failure>** message.
- Converts **<Output><ErrorInfo><StackTrace>** to JUnit **<failure>** stack trace.
- Converts **<Output><StdErr>** to JUnit **<system-err>**.
- Converts **<Output><StdOut>** to JUnit **<system-out>**.
- Converts `Output/ErrorInfo/Message` to JUnit **<failure>** message.
- Converts `Output/ErrorInfo/StackTrace` to JUnit **<failure>** stack trace.
- Converts `Output/StdErr` to JUnit **<system-err>**.
- Converts `Output/StdOut` to JUnit **<system-out>**.
- Tests are ordered by name in Mochawesome.
- Does not resolve test suite times in JUnit output.

Expand Down
81 changes: 74 additions & 7 deletions src/junit.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,17 @@ const path = require('path');
const parser = require('p3x-xml2json');
const crypto = require("crypto");
const marge = require('mochawesome-report-generator');
const xmlFormat = require('xml-formatter');
const _ = require('lodash');

let skippedTests = 0;
let failedTests = 0;
let suites = [];

function parseTrx(){

}

/**
* @param {ConverterOptions} options
* @param {string|Buffer} xml
Expand All @@ -20,6 +25,7 @@ function parseXml(options, xml){
object: true,
arrayNotation: true,
sanitize: false,
reversible: true,
}

let json;
Expand All @@ -31,7 +37,6 @@ function parseXml(options, xml){
throw `\nCould not read JSON from converted input ${options.testFile}.\n ${e.message}`;
}


if(!json || !json.testsuites || !json.testsuites.length){
if(json && json.testsuite){
json.testsuites = [{testsuite: json.testsuite}];
Expand All @@ -44,9 +49,18 @@ function parseXml(options, xml){

if(options.saveIntermediateFiles){
let fileName = `${path.parse(options.testFile).name}-converted.json`;
fs.writeFileSync(path.join(options.reportDir, fileName), JSON.stringify(json, null, 2), 'utf8')
fs.writeFileSync(path.join(options.reportDir, fileName), JSON.stringify(json, null, 2), 'utf8');
}

// if(options.saveIntermediateFiles){
// let jsonString = JSON.stringify(json, null, 2).replaceAll('
', '').replaceAll('
', '');
//
// json = JSON.parse(jsonString);
// let fileName = `${path.parse(options.testFile).name}-converted.xml`;
// fs.writeFileSync(path.join(options.reportDir, fileName), xmlFormat(parser.toXml(json), {forceSelfClosingEmptyTag: true}), 'utf8');
//
// }

if(!json.testsuites[0].testsuite){
throw `\nNo <testsuite> elements in <testsuites> element in converted ${options.testFile}`;
}
Expand All @@ -59,12 +73,58 @@ function parseXml(options, xml){
json.testsuites[0].testsuite = _.sortBy(json.testsuites[0].testsuite, ['classname'])
}
else{
json.testsuites[0].testsuite.sort((a,b) => a.name - b.name);
//json.testsuites[0].testsuite.sort((a,b) => a.name - b.name);
//json.testsuites[0].testsuite = _.sortBy(json.testsuites[0].testsuite, ['name'])
}

if(options.testType === 'trx' && json.testsuites[0].testsuite[0].testcase.length !== 0){

json.testsuites[0].testsuite[0].testcase = _.sortBy(json.testsuites[0].testsuite[0].testcase, ['name']);

let classnames = _.map(json.testsuites[0].testsuite[0].testcase, 'classname')
.filter((value, index, array) => array.indexOf(value) === index);

classnames = _.sortBy(classnames, [function(o) { return o; }]);

let time = _.sumBy(json.testsuites[0].testsuite, suite => _.sumBy(suite.testcase, function(testCase) { return Number(testCase.time); }));

json.testsuites[0].time = time;
json.testsuites[0].testsuite[0].time = time;

if(classnames.length > 1){

let testSuites = [];
classnames.forEach((classname) => {

let testcases = _.filter(json.testsuites[0].testsuite[0].testcase, { 'classname': classname});
let time = _.sumBy(testcases, function(testCase) { return Number(testCase.time); });
const failures = testcases.filter((testCase) => testCase.status === 'Failed').length;
const skipped = testcases.filter((testCase) => testCase.status === 'Skipped').length;

testSuites.push(
{
name: classname,
tests: `${testcases.length}`,
failures: `${failures}`,
skipped: `${skipped}`,
time: `${time}`,
testcase: testcases,
}
);
});

json.testsuites[0].testsuite = testSuites;
}

else{
json.testsuites[0].testsuite[0].time = time;
json.testsuites[0].testsuite[0].name = json.testsuites[0].testsuite[0].testcase[0].classname;

}

if(options.junit){
fs.writeFileSync(path.join(options.reportDir, options.junitReportFilename), xmlFormat(parser.toXml(json), {forceSelfClosingEmptyTag: true}), 'utf8');
}
}

return json.testsuites[0];
Expand All @@ -84,9 +144,12 @@ function getError(testcase){
let failure = testcase.failure ? testcase.failure : testcase.error
let fail = failure[0];
let prefix = fail.type ? `${fail.type}: ` : ''
let diff = !fail.type || fail.type === 'Error' ? null : `${fail.message}`;
if(fail.message || fail.$t){
let diff = null;
// diff = !fail.type || fail.type === 'Error' ? null : `${fail.message}`;
if(fail.message){
message = `${prefix}${fail.message.replaceAll('&#xD;', '').replaceAll('&#xA;', '')}`;
}
if(fail.$t){
estack = fail.$t.replaceAll('&#xD;', '\n');
}
else if(typeof fail === 'string'){
Expand Down Expand Up @@ -134,11 +197,15 @@ function getContext(testcase){
}

if(testcase["system-out"] && testcase["system-out"].length !== 0){
if(testcase["system-out"][0] !== skipped){
let systemout = testcase["system-out"][0];
if(systemout.$t){
systemout = systemout.$t;
}
if(systemout !== skipped){
context.push(
{
title: 'system-out',
value: testcase["system-out"][0]
value: systemout
}
);
}
Expand Down
8 changes: 6 additions & 2 deletions src/nunit-junit.xslt
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,14 @@
<xsl:if test="test-case">
<testsuite name="{@name}" classname="{@fullname}" tests="{@testcasecount}" time="{@duration}" failures="{@failed}" skipped="{@skipped}" timestamp="{@start-time}">
<xsl:apply-templates select="test-case"/>
<xsl:apply-templates select="properties"/>
<xsl:apply-templates select="output"/>
</testsuite>
<xsl:apply-templates select="test-suite"/>
</xsl:if>

<xsl:if test="not(test-case)">
<xsl:apply-templates/>
<xsl:apply-templates select="test-suite"/>
</xsl:if>
</xsl:template>

Expand All @@ -37,7 +40,8 @@
<xsl:value-of select="./failure/stack-trace"/>
</failure>
</xsl:if>
<xsl:apply-templates/>
<xsl:apply-templates select="properties"/>
<xsl:apply-templates select="output"/>
</testcase>
</xsl:template>

Expand Down
10 changes: 9 additions & 1 deletion src/trx-junit.xslt
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,15 @@
</error>
</xsl:if>
<xsl:if test="contains($outcome, 'NotExecuted')">
<skipped message="{$message}"/>
<xsl:if test="$message != ''">
<skipped message="{$message}"/>
</xsl:if>
<xsl:if test="$stdout != ''">
<skipped message="{$stdout}"/>
</xsl:if>
<xsl:if test="$stdout = '' and $message = ''">
<skipped/>
</xsl:if>
</xsl:if>
<xsl:if test="$stderr">
<system-err>
Expand Down
2 changes: 1 addition & 1 deletion src/xslt.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ async function processXml(options, xmlString){
throw `\nXML parsed from ${options.testFile} is invalid \n${e.message}`;
}

if(options.junit){
if(options.junit && options.testType !== 'trx'){
fs.writeFileSync(path.join(options.reportDir, options.junitReportFilename), parsedXml, 'utf8');
}

Expand Down
4 changes: 3 additions & 1 deletion src/xunit-junit.xslt
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,13 @@
<xsl:if test="test">
<testsuite name="{replace(replace(@name, 'Test collection for ', ''),'TestSuite.','')}" tests="{@total}" time="{@time}" passed="{@passed}" failed="{@failed}" skipped="{@skipped}">
<xsl:apply-templates select="test"/>
<xsl:apply-templates select="traits"/>
<xsl:apply-templates select="output"/>
</testsuite>
<xsl:apply-templates select="collection"/>
</xsl:if>
<xsl:if test="not(test)">
<xsl:apply-templates/>
<xsl:apply-templates select="collection"/>
</xsl:if>
</xsl:template>

Expand Down
54 changes: 11 additions & 43 deletions tests/converter.junit.test.js
Original file line number Diff line number Diff line change
@@ -1,84 +1,52 @@
const path = require('path');
const fs = require("fs");

const expect = require('@jest/globals').expect;
const test = require('@jest/globals').test;
const beforeAll = require('@jest/globals').beforeAll;
const afterAll = require('@jest/globals').afterAll;
const describe = require('@jest/globals').describe;

const converter = require('../src/converter');
const setup = require("./setup");

describe("JUnit converter tests", () => {

const outDir= './tests/data/tmp';
const reportDir= './tests/data/result';

beforeAll(() => {
if(fs.existsSync(outDir)){
fs.rmSync(outDir, { recursive: true, force: true });
}
setup.removeTempDir();
});

// afterAll(() => {
// if(fs.existsSync(outDir)){
// fs.rmSync(outDir, { recursive: true, force: true });
// }
// setup.removeTempDir();
// });

function getFilename(file){
return `${path.parse(file).name}-mochawesome.json`
}

/**
* @returns {TestReportConverterOptions}
*/
function createOptions(file, type){
return {
testFile: path.join(__dirname, `data/source/${file}`),
testType: type,
reportDir: outDir,
reportFilename: getFilename(file),
}
}

/**
* @param {TestReportConverterOptions} options
* @param {string?} reportFilename
*/
function compare(options, reportFilename){
let createdReport = fs.readFileSync(path.join(outDir, options.reportFilename), 'utf8');
let report = fs.readFileSync(path.join(reportDir, reportFilename ?? options.reportFilename), 'utf8');

expect(createdReport.replaceAll(/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/g,'')).toBe(report.replaceAll(/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/g,''));
}

test('convert junit-jenkins.xml', async() => {
let options = createOptions('junit-jenkins.xml', 'junit');
let options = setup.createOptions('junit-jenkins.xml', 'junit');

await converter(options);
compare(options);
setup.compare(options);
});

test('convert junit-notestsuites.xml', async() => {
let options = createOptions('junit-notestsuites.xml', 'junit');
let options = setup.createOptions('junit-notestsuites.xml', 'junit');

await converter(options);
compare(options, 'junit-jenkins-mochawesome.json');
setup.compare(options, 'junit-jenkins-mochawesome.json');
});

test('convert junit-testsuites-noattributes.xml', async() => {
let options = createOptions('junit-testsuites-noattributes.xml', 'junit');
let options = setup.createOptions('junit-testsuites-noattributes.xml', 'junit');

await converter(options);
compare(options, 'junit-jenkins-mochawesome.json');
setup.compare(options, 'junit-jenkins-mochawesome.json');
});

test('convert junit-mocha-xunit.xml', async() => {
let options = createOptions('junit-mocha-xunit.xml', 'junit')
let options = setup.createOptions('junit-mocha-xunit.xml', 'junit')

await converter(options);
compare(options);
setup.compare(options);
});

});
Loading

0 comments on commit 273edff

Please sign in to comment.