-
Notifications
You must be signed in to change notification settings - Fork 0
/
junit.ts
109 lines (101 loc) · 3.37 KB
/
junit.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
/* eslint-disable @typescript-eslint/no-explicit-any */
import fs from 'fs';
import { Junit, JunitFailureInfo } from '../types';
import parseString from 'xml2js';
import * as core from '@actions/core';
const unpackage = (testsuites: any): Junit => {
const main = testsuites['$'] || testsuites.testsuite[0]['$'];
const testsuite: any[] = Array.isArray(testsuites.testsuite)
? testsuites.testsuite
: [testsuites];
const errors =
testsuite
?.map((test: any) => +test['$'].errors)
.reduce((acc: number, curr: number) => acc + curr, 0) || 0;
const skipped =
testsuite
?.map((test: any) => +test['$'].skipped)
.reduce((acc: number, curr: number) => acc + curr, 0) || 0;
const testSuiteFailures = testsuite?.filter((test: any) => +test['$'].failures > 0);
const failureCase: JunitFailureInfo[] | undefined = testSuiteFailures
.map((testSuiteFailure: any) => {
const testCaseFailures: any[] = testSuiteFailure.testcase.filter(
(testCase: any) => testCase.failure,
);
return testCaseFailures.map((testCaseFailure: any) => ({
classname: testCaseFailure['$'].classname.trim(),
name: testCaseFailure['$'].name.trim(),
time: `${parseFloat(testCaseFailure['$'].time).toFixed(2)}s`,
error: getTestFailureMessage(testCaseFailure),
}));
})
.flat();
return {
tests: +main.tests,
failures: {
count: +main.failures,
info: failureCase,
},
errors: +main.errors || errors,
skipped,
time: `${parseFloat(main.time).toFixed(2)}s`,
};
};
const getTestFailureMessage = (testCaseFailure: any): string => {
const failure = testCaseFailure?.failure?.[0];
if (failure) {
if (typeof failure === 'string') {
return failure.split('\n')?.[0]?.trim() || 'unhandled string error';
} else if (typeof failure === 'object') {
return (
failure['$']?.message?.substring(0, 128) ||
failure.message?.substring(0, 128) ||
'unhandled object error'
);
}
}
return 'unknown failure';
};
const parseContent = (xml: string): Promise<Junit> => {
return new Promise((resolve, reject) => {
parseString.parseString(xml, (err, parseResult) => {
if (err) {
return reject(err);
}
if (!parseResult?.testsuites && !parseResult?.testsuite) {
return reject(new Error('invalid or missing xml content'));
}
const result = unpackage(parseResult.testsuites || parseResult.testsuite);
resolve(result);
});
});
};
export const parseFile = async (file: string): Promise<Junit | undefined> => {
return new Promise((resolve, reject) => {
if (!file || file === '') {
core.info('no file specified');
resolve(undefined);
} else {
fs.readFile(
file,
'utf8',
async (err: NodeJS.ErrnoException | null, data: string) => {
if (err) {
core.error(`failed to read file: ${file}. error: ${err.message}`);
reject(err);
} else {
try {
const info = await parseContent(data);
// console.log('====== junit ======');
// console.log(JSON.stringify(info, null, 2));
resolve(info);
} catch (error) {
core.error(`failed to parseContent. err: ${error.message}`);
reject(error);
}
}
},
);
}
});
};