@testomatio/reporter 2.3.7 β 2.3.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -1
- package/lib/bin/cli.js +14 -4
- package/lib/bin/reportXml.js +5 -2
- package/lib/client.d.ts +1 -1
- package/lib/client.js +2 -2
- package/lib/junit-adapter/csharp.d.ts +0 -1
- package/lib/junit-adapter/csharp.js +40 -7
- package/lib/junit-adapter/nunit-parser.d.ts +82 -0
- package/lib/junit-adapter/nunit-parser.js +431 -0
- package/lib/pipe/testomatio.d.ts +2 -1
- package/lib/pipe/testomatio.js +2 -1
- package/lib/uploader.js +4 -0
- package/lib/utils/utils.js +182 -21
- package/lib/xmlReader.d.ts +32 -26
- package/lib/xmlReader.js +111 -52
- package/package.json +8 -4
- package/src/bin/cli.js +16 -4
- package/src/bin/reportXml.js +5 -2
- package/src/client.js +2 -2
- package/src/junit-adapter/csharp.js +45 -6
- package/src/junit-adapter/nunit-parser.js +472 -0
- package/src/pipe/testomatio.js +2 -1
- package/src/uploader.js +5 -0
- package/src/utils/utils.js +195 -19
- package/src/xmlReader.js +134 -46
- package/types/types.d.ts +364 -0
- package/types/vitest.types.d.ts +93 -0
package/README.md
CHANGED
|
@@ -13,7 +13,7 @@ Testomat.io Reporter (this npm package) supports:
|
|
|
13
13
|
- π [Stack traces](./docs/stacktrace.md) and error messages
|
|
14
14
|
- π [GitHub](./docs/pipes/github.md), [GitLab](./docs/pipes/gitlab.md) & [Bitbucket](./docs/pipes/bitbucket.md) integration
|
|
15
15
|
- π
Realtime reports
|
|
16
|
-
- ποΈ Other test frameworks supported via [
|
|
16
|
+
- ποΈ Other test frameworks supported via [JUnit XML](./docs/junit.md) with [XML import configuration](./docs/xml-imports.md)
|
|
17
17
|
- πΆββοΈ Steps _(work in progress)_
|
|
18
18
|
- π [Logger](./docs/logger.md) _(work in progress, supports Jest for now)_
|
|
19
19
|
- βοΈ Custom properties and metadata _(work in progress)_
|
|
@@ -129,6 +129,7 @@ Bring this reporter on CI and never lose test results again!
|
|
|
129
129
|
- [CSV](./docs/pipes/csv.md)
|
|
130
130
|
- [HTML report](./docs/pipes/html.md)
|
|
131
131
|
- [Bitbucket](./docs/pipes/bitbucket.md)
|
|
132
|
+
- π [Linking Tests](./docs/linking-tests.md)
|
|
132
133
|
- π [JUnit](./docs/junit.md)
|
|
133
134
|
- ποΈ [Artifacts](./docs/artifacts.md)
|
|
134
135
|
- π [Workflows](./docs/workflows.md)
|
package/lib/bin/cli.js
CHANGED
|
@@ -37,12 +37,17 @@ program
|
|
|
37
37
|
program
|
|
38
38
|
.command('start')
|
|
39
39
|
.description('Start a new run and return its ID')
|
|
40
|
-
.
|
|
40
|
+
.option('--kind <type>', 'Specify run type: automated, manual, or mixed')
|
|
41
|
+
.action(async (opts) => {
|
|
41
42
|
(0, utils_js_1.cleanLatestRunId)();
|
|
42
43
|
console.log('Starting a new Run on Testomat.io...');
|
|
43
44
|
const apiKey = process.env['INPUT_TESTOMATIO-KEY'] || config_js_1.config.TESTOMATIO;
|
|
44
45
|
const client = new client_js_1.default({ apiKey });
|
|
45
|
-
|
|
46
|
+
const createRunParams = {};
|
|
47
|
+
if (opts.kind) {
|
|
48
|
+
createRunParams.kind = opts.kind;
|
|
49
|
+
}
|
|
50
|
+
client.createRun(createRunParams).then(() => {
|
|
46
51
|
console.log(process.env.runId);
|
|
47
52
|
process.exit(0);
|
|
48
53
|
});
|
|
@@ -70,6 +75,7 @@ program
|
|
|
70
75
|
.description('Run tests with the specified command')
|
|
71
76
|
.argument('<command>', 'Test runner command')
|
|
72
77
|
.option('--filter <filter>', 'Additional execution filter')
|
|
78
|
+
.option('--kind <type>', 'Specify run type: automated, manual, or mixed')
|
|
73
79
|
.action(async (command, opts) => {
|
|
74
80
|
const apiKey = process.env['INPUT_TESTOMATIO-KEY'] || config_js_1.config.TESTOMATIO;
|
|
75
81
|
const title = process.env.TESTOMATIO_TITLE;
|
|
@@ -108,8 +114,12 @@ program
|
|
|
108
114
|
process.exit(code);
|
|
109
115
|
});
|
|
110
116
|
};
|
|
117
|
+
const createRunParams = {};
|
|
118
|
+
if (opts.kind) {
|
|
119
|
+
createRunParams.kind = opts.kind;
|
|
120
|
+
}
|
|
111
121
|
if (apiKey) {
|
|
112
|
-
await client.createRun().then(runTests);
|
|
122
|
+
await client.createRun(createRunParams).then(runTests);
|
|
113
123
|
}
|
|
114
124
|
else {
|
|
115
125
|
await runTests();
|
|
@@ -145,7 +155,7 @@ program
|
|
|
145
155
|
.option('--lang <lang>', 'Language used (python, ruby, java)')
|
|
146
156
|
.option('--timelimit <time>', 'default time limit in seconds to kill a stuck process')
|
|
147
157
|
.action(async (pattern, opts) => {
|
|
148
|
-
if (!pattern.endsWith('.xml')) {
|
|
158
|
+
if (!pattern.endsWith('.xml') && !pattern.includes('*')) {
|
|
149
159
|
pattern += '.xml';
|
|
150
160
|
}
|
|
151
161
|
let { javaTests, lang } = opts;
|
package/lib/bin/reportXml.js
CHANGED
|
@@ -25,7 +25,7 @@ program
|
|
|
25
25
|
.option('--timelimit <time>', 'default time limit in seconds to kill a stuck process')
|
|
26
26
|
.option('--env-file <envfile>', 'Load environment variables from env file')
|
|
27
27
|
.action(async (pattern, opts) => {
|
|
28
|
-
if (!pattern.endsWith('.xml')) {
|
|
28
|
+
if (!pattern.endsWith('.xml') && !pattern.includes('*')) {
|
|
29
29
|
pattern += '.xml';
|
|
30
30
|
}
|
|
31
31
|
let { javaTests, lang } = opts;
|
|
@@ -37,7 +37,10 @@ program
|
|
|
37
37
|
lang = lang?.toLowerCase();
|
|
38
38
|
if (javaTests === true || (lang === 'java' && !javaTests))
|
|
39
39
|
javaTests = 'src/test/java';
|
|
40
|
-
const runReader = new xmlReader_js_1.default({
|
|
40
|
+
const runReader = new xmlReader_js_1.default({
|
|
41
|
+
javaTests,
|
|
42
|
+
lang,
|
|
43
|
+
});
|
|
41
44
|
const files = glob_1.glob.sync(pattern, { cwd: opts.dir || process.cwd() });
|
|
42
45
|
if (!files.length) {
|
|
43
46
|
console.log(constants_js_1.APP_PREFIX, `Report can't be created. No XML files found π₯`);
|
package/lib/client.d.ts
CHANGED
package/lib/client.js
CHANGED
|
@@ -131,7 +131,7 @@ class Client {
|
|
|
131
131
|
*
|
|
132
132
|
* @returns {Promise<any>} - resolves to Run id which should be used to update / add test
|
|
133
133
|
*/
|
|
134
|
-
async createRun(params) {
|
|
134
|
+
async createRun(params = {}) {
|
|
135
135
|
if (!this.pipes || !this.pipes.length)
|
|
136
136
|
this.pipes = await (0, index_js_1.pipesFactory)(params || this.paramsForPipesFactory || {}, this.pipeStore);
|
|
137
137
|
debug('Creating run...');
|
|
@@ -139,7 +139,7 @@ class Client {
|
|
|
139
139
|
if (!this.pipes?.filter(p => p.isEnabled).length)
|
|
140
140
|
return Promise.resolve();
|
|
141
141
|
this.queue = this.queue
|
|
142
|
-
.then(() => Promise.all(this.pipes.map(p => p.createRun())))
|
|
142
|
+
.then(() => Promise.all(this.pipes.map(p => p.createRun(params))))
|
|
143
143
|
.catch(err => console.log(constants_js_1.APP_PREFIX, err))
|
|
144
144
|
.then(() => {
|
|
145
145
|
const runId = this.pipeStore?.runId;
|
|
@@ -7,24 +7,57 @@ const path_1 = __importDefault(require("path"));
|
|
|
7
7
|
const adapter_js_1 = __importDefault(require("./adapter.js"));
|
|
8
8
|
class CSharpAdapter extends adapter_js_1.default {
|
|
9
9
|
formatTest(t) {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
10
|
+
// Extract example from title if not already present
|
|
11
|
+
if (!t.example) {
|
|
12
|
+
const exampleMatch = t.title.match(/\((.*?)\)/);
|
|
13
|
+
if (exampleMatch) {
|
|
14
|
+
// Extract parameters as object with numeric keys for API
|
|
15
|
+
const params = exampleMatch[1].split(',').map(param => param.trim()).filter(param => param !== '');
|
|
16
|
+
t.example = {};
|
|
17
|
+
params.forEach((param, index) => {
|
|
18
|
+
t.example[index] = param;
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
// Remove parameters from title to avoid duplicates in Test Suite
|
|
23
|
+
// The example field will be used for grouping on import
|
|
24
|
+
t.title = t.title.replace(/\(.*?\)/, '').trim();
|
|
14
25
|
const suite = t.suite_title.split('.');
|
|
15
26
|
t.suite_title = suite.pop();
|
|
16
27
|
t.file = namespaceToFileName(t.file);
|
|
17
|
-
t.title = title.trim();
|
|
18
28
|
return t;
|
|
19
29
|
}
|
|
20
30
|
getFilePath(t) {
|
|
21
|
-
|
|
31
|
+
if (!t.file)
|
|
32
|
+
return null;
|
|
33
|
+
// Normalize path separators for cross-platform compatibility
|
|
34
|
+
let filePath = t.file.replace(/\\/g, '/');
|
|
35
|
+
// If file already has .cs extension, use it directly
|
|
36
|
+
if (filePath.endsWith('.cs')) {
|
|
37
|
+
// Make relative path if it's absolute
|
|
38
|
+
if (path_1.default.isAbsolute(filePath)) {
|
|
39
|
+
// Try to find project-relative path
|
|
40
|
+
const cwd = process.cwd().replace(/\\/g, '/');
|
|
41
|
+
if (filePath.startsWith(cwd)) {
|
|
42
|
+
filePath = path_1.default.relative(cwd, filePath).replace(/\\/g, '/');
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return filePath;
|
|
46
|
+
}
|
|
47
|
+
// Convert namespace path to file path
|
|
48
|
+
const fileName = namespaceToFileName(filePath);
|
|
22
49
|
return fileName;
|
|
23
50
|
}
|
|
24
51
|
}
|
|
25
52
|
module.exports = CSharpAdapter;
|
|
26
53
|
function namespaceToFileName(fileName) {
|
|
54
|
+
if (!fileName)
|
|
55
|
+
return '';
|
|
56
|
+
// If already a .cs file path, clean it up
|
|
57
|
+
if (fileName.endsWith('.cs')) {
|
|
58
|
+
return fileName.replace(/\\/g, '/');
|
|
59
|
+
}
|
|
27
60
|
const fileParts = fileName.split('.');
|
|
28
61
|
fileParts[fileParts.length - 1] = fileParts[fileParts.length - 1]?.replace(/\$.*/, '');
|
|
29
|
-
return `${fileParts.join(
|
|
62
|
+
return `${fileParts.join('/')}.cs`;
|
|
30
63
|
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Enhanced NUnit XML Parser that properly handles test-suite hierarchy
|
|
3
|
+
* and parameterized tests
|
|
4
|
+
*/
|
|
5
|
+
export class NUnitXmlParser {
|
|
6
|
+
constructor(options?: {});
|
|
7
|
+
options: {};
|
|
8
|
+
tests: any[];
|
|
9
|
+
stats: {
|
|
10
|
+
total: number;
|
|
11
|
+
passed: number;
|
|
12
|
+
failed: number;
|
|
13
|
+
skipped: number;
|
|
14
|
+
inconclusive: number;
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Parse NUnit XML test-run structure
|
|
18
|
+
* @param {Object} testRun - Parsed XML test-run object
|
|
19
|
+
* @returns {Object} - Parsed test results
|
|
20
|
+
*/
|
|
21
|
+
parseTestRun(testRun: any): any;
|
|
22
|
+
/**
|
|
23
|
+
* Recursively parse test-suite elements based on their type
|
|
24
|
+
* @param {Object|Array} testSuite - Test suite object or array
|
|
25
|
+
* @param {Array} parentPath - Current path in the hierarchy
|
|
26
|
+
*/
|
|
27
|
+
parseTestSuite(testSuite: any | any[], parentPath?: any[]): void;
|
|
28
|
+
/**
|
|
29
|
+
* Process child elements of a test suite
|
|
30
|
+
* @param {Object} testSuite - Test suite object
|
|
31
|
+
* @param {Array} currentPath - Current path in hierarchy
|
|
32
|
+
*/
|
|
33
|
+
processChildren(testSuite: any, currentPath: any[]): void;
|
|
34
|
+
/**
|
|
35
|
+
* Parse test-case elements (actual tests)
|
|
36
|
+
* @param {Object|Array} testCases - Test case object or array
|
|
37
|
+
* @param {Array} suitePath - Path to the test suite
|
|
38
|
+
* @param {Object} parentSuite - Parent test suite for context
|
|
39
|
+
*/
|
|
40
|
+
parseTestCases(testCases: any | any[], suitePath: any[], parentSuite: any): void;
|
|
41
|
+
/**
|
|
42
|
+
* Parse individual test case
|
|
43
|
+
* @param {Object} testCase - Test case object
|
|
44
|
+
* @param {Array} suitePath - Path to the test suite
|
|
45
|
+
* @param {Object} parentSuite - Parent test suite for context
|
|
46
|
+
* @returns {Object|null} - Parsed test object
|
|
47
|
+
*/
|
|
48
|
+
parseTestCase(testCase: any, suitePath: any[], parentSuite: any): any | null;
|
|
49
|
+
/**
|
|
50
|
+
* Extract method name and parameters from test name
|
|
51
|
+
* @param {string} testName - Full test name
|
|
52
|
+
* @returns {Object} - Extracted information
|
|
53
|
+
*/
|
|
54
|
+
extractParameters(testName: string): any;
|
|
55
|
+
/**
|
|
56
|
+
* Parse parameter string into array of parameters
|
|
57
|
+
* @param {string} paramString - Parameter string
|
|
58
|
+
* @returns {Array} - Array of parameters
|
|
59
|
+
*/
|
|
60
|
+
parseParameterString(paramString: string): any[];
|
|
61
|
+
/**
|
|
62
|
+
* Extract method name from test name (fallback)
|
|
63
|
+
* @param {string} testName - Test name
|
|
64
|
+
* @returns {string} - Method name
|
|
65
|
+
*/
|
|
66
|
+
extractMethodName(testName: string): string;
|
|
67
|
+
/**
|
|
68
|
+
* Build file path from suite path and class name
|
|
69
|
+
* @param {Array} suitePath - Suite path array
|
|
70
|
+
* @param {string} className - Class name
|
|
71
|
+
* @param {Object} parentSuite - Parent suite for context
|
|
72
|
+
* @returns {string} - File path
|
|
73
|
+
*/
|
|
74
|
+
buildFilePath(suitePath: any[], className: string, parentSuite: any): string;
|
|
75
|
+
/**
|
|
76
|
+
* Group parameterized tests by base method name
|
|
77
|
+
* @param {Array} tests - Array of parsed tests
|
|
78
|
+
* @returns {Object} - Grouped tests
|
|
79
|
+
*/
|
|
80
|
+
groupParameterizedTests(tests: any[]): any;
|
|
81
|
+
}
|
|
82
|
+
export default NUnitXmlParser;
|