@testomatio/reporter 1.1.0-beta.codeceptjs-before-suite-logs → 1.1.0-beta.fix-junit-reporter
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/Changelog.md +8 -0
- package/lib/adapter/codecept.js +3 -31
- package/lib/adapter/cucumber/current.js +2 -1
- package/lib/adapter/cucumber/legacy.js +2 -1
- package/lib/adapter/cypress-plugin/index.js +46 -26
- package/lib/adapter/mocha.js +2 -1
- package/lib/bin/startTest.js +2 -1
- package/lib/config.js +5 -0
- package/lib/pipe/testomatio.js +4 -1
- package/lib/utils/utils.js +6 -1
- package/lib/xmlReader.js +106 -104
- package/package.json +1 -1
package/Changelog.md
CHANGED
package/lib/adapter/codecept.js
CHANGED
|
@@ -78,35 +78,8 @@ function CodeceptReporter(config) {
|
|
|
78
78
|
global.testomatioDataStore.steps = [];
|
|
79
79
|
});
|
|
80
80
|
|
|
81
|
-
let hookSteps = [];
|
|
82
|
-
let suiteHookRunning = false;
|
|
83
|
-
|
|
84
|
-
event.dispatcher.on(event.suite.before, (suite) => {
|
|
85
|
-
suiteHookRunning = true;
|
|
86
|
-
hookSteps = [];
|
|
87
|
-
global.testomatioDataStore.steps = [];
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
event.dispatcher.on(event.test.before, () => {
|
|
91
|
-
suiteHookRunning = false;
|
|
92
|
-
global.testomatioDataStore.steps = []
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
event.dispatcher.on(event.hook.started, (suite) => {
|
|
96
|
-
// global.testomatioDataStore.steps = [];
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
event.dispatcher.on(event.hook.passed, (suite) => {
|
|
100
|
-
if (suiteHookRunning) hookSteps.push(...global.testomatioDataStore.steps);
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
event.dispatcher.on(event.hook.failed, (suite) => {
|
|
104
|
-
if (suiteHookRunning) hookSteps.push(...global.testomatioDataStore.steps);
|
|
105
|
-
});
|
|
106
|
-
|
|
107
81
|
event.dispatcher.on(event.test.started, test => {
|
|
108
82
|
testTimeMap[test.id] = Date.now();
|
|
109
|
-
|
|
110
83
|
if (global.testomatioDataStore) global.testomatioDataStore.currentlyRunningTestId = getIdFromTestTitle(test.title);
|
|
111
84
|
});
|
|
112
85
|
|
|
@@ -116,7 +89,7 @@ function CodeceptReporter(config) {
|
|
|
116
89
|
await Promise.all(reportTestPromises);
|
|
117
90
|
|
|
118
91
|
if (upload.isArtifactsEnabled()) {
|
|
119
|
-
uploadAttachments(client, videos, '🎞️
|
|
92
|
+
uploadAttachments(client, videos, '🎞️ Uploading', 'video');
|
|
120
93
|
uploadAttachments(client, traces, '📁 Uploading', 'trace');
|
|
121
94
|
}
|
|
122
95
|
|
|
@@ -136,7 +109,7 @@ function CodeceptReporter(config) {
|
|
|
136
109
|
suite_title: test.parent && test.parent.title,
|
|
137
110
|
message: testObj.message,
|
|
138
111
|
time: getDuration(test),
|
|
139
|
-
steps:
|
|
112
|
+
steps: global.testomatioDataStore.steps.join('\n') || null,
|
|
140
113
|
test_id: testId,
|
|
141
114
|
});
|
|
142
115
|
// output.stop();
|
|
@@ -160,7 +133,6 @@ function CodeceptReporter(config) {
|
|
|
160
133
|
...stripExampleFromTitle(title),
|
|
161
134
|
suite_title: suite.title,
|
|
162
135
|
test_id: testId,
|
|
163
|
-
steps: hookSteps.join('\n') || null,
|
|
164
136
|
error,
|
|
165
137
|
time: 0,
|
|
166
138
|
});
|
|
@@ -189,7 +161,7 @@ function CodeceptReporter(config) {
|
|
|
189
161
|
message: testObj.message,
|
|
190
162
|
time: getDuration(test),
|
|
191
163
|
files,
|
|
192
|
-
steps:
|
|
164
|
+
steps: global.testomatioDataStore?.steps?.join('\n') || null,
|
|
193
165
|
})
|
|
194
166
|
.then(pipes => {
|
|
195
167
|
testId = pipes.filter(p => p.pipe.includes('Testomatio'))[0]?.result?.data?.test_id;
|
|
@@ -6,6 +6,7 @@ const { STATUS, TESTOMAT_TMP_STORAGE_DIR } = require('../../constants');
|
|
|
6
6
|
const TestomatClient = require('../../client');
|
|
7
7
|
const logger = require('../../storages/logger');
|
|
8
8
|
const { parseTest, fileSystem } = require('../../utils/utils');
|
|
9
|
+
const { TESTOMATIO } = require('../../config');
|
|
9
10
|
|
|
10
11
|
const { GherkinDocumentParser, PickleParser } = formatterHelpers;
|
|
11
12
|
const { getGherkinScenarioLocationMap, getGherkinStepMap } = GherkinDocumentParser;
|
|
@@ -29,7 +30,7 @@ class CucumberReporter extends Formatter {
|
|
|
29
30
|
this.failures = [];
|
|
30
31
|
this.cases = [];
|
|
31
32
|
|
|
32
|
-
this.client = new TestomatClient({ apiKey: options.apiKey ||
|
|
33
|
+
this.client = new TestomatClient({ apiKey: options.apiKey || TESTOMATIO });
|
|
33
34
|
this.status = STATUS.PASSED;
|
|
34
35
|
|
|
35
36
|
global.testomatioRunningEnvironment = 'cucumber:current';
|
|
@@ -4,6 +4,7 @@ const chalk = require('chalk');
|
|
|
4
4
|
const { parseTest, fileSystem } = require('../../utils/utils');
|
|
5
5
|
const { STATUS, TESTOMAT_TMP_STORAGE_DIR } = require('../../constants');
|
|
6
6
|
const TestomatClient = require('../../client');
|
|
7
|
+
const { TESTOMATIO } = require('../../config');
|
|
7
8
|
|
|
8
9
|
const createTestomatFormatter = apiKey => {
|
|
9
10
|
if (!apiKey || apiKey === '') {
|
|
@@ -151,4 +152,4 @@ const createTestomatFormatter = apiKey => {
|
|
|
151
152
|
};
|
|
152
153
|
};
|
|
153
154
|
|
|
154
|
-
module.exports = createTestomatFormatter(
|
|
155
|
+
module.exports = createTestomatFormatter(TESTOMATIO);
|
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
const { STATUS } = require('../../constants');
|
|
2
2
|
const { parseTest, parseSuite } = require('../../utils/utils');
|
|
3
3
|
const TestomatClient = require('../../client');
|
|
4
|
+
const { TESTOMATIO } = require('../../config');
|
|
4
5
|
|
|
5
6
|
const testomatioReporter = on => {
|
|
6
|
-
if (!
|
|
7
|
+
if (!TESTOMATIO) {
|
|
7
8
|
console.log('TESTOMATIO key is empty, ignoring reports');
|
|
8
|
-
return
|
|
9
|
+
return;
|
|
9
10
|
}
|
|
10
|
-
const client = new TestomatClient({ apiKey:
|
|
11
|
+
const client = new TestomatClient({ apiKey: TESTOMATIO });
|
|
11
12
|
|
|
12
|
-
on('before:run', async
|
|
13
|
+
on('before:run', async run => {
|
|
13
14
|
// TODO: looks like client.env does not exist
|
|
14
15
|
if (!client.env) {
|
|
15
|
-
client.env = `${run.browser.displayName},${run.system.osName}
|
|
16
|
+
client.env = `${run.browser.displayName},${run.system.osName}`;
|
|
16
17
|
}
|
|
17
18
|
await client.createRun();
|
|
18
19
|
});
|
|
@@ -26,8 +27,9 @@ const testomatioReporter = on => {
|
|
|
26
27
|
const lastAttemptIndex = test.attempts.length - 1;
|
|
27
28
|
const latestAttempt = test.attempts[lastAttemptIndex];
|
|
28
29
|
|
|
29
|
-
|
|
30
|
-
const
|
|
30
|
+
// latestAttempt.duration && latestAttempt.error were available in adapters version up to 13 JFYI
|
|
31
|
+
const time = latestAttempt.duration || latestAttempt.wallClockDuration || test.duration;
|
|
32
|
+
let error = latestAttempt.error;
|
|
31
33
|
|
|
32
34
|
let title = test.title.pop();
|
|
33
35
|
const examples = title.match(/\(example (#\d+)\)/);
|
|
@@ -40,23 +42,28 @@ const testomatioReporter = on => {
|
|
|
40
42
|
const testId = parseTest(title);
|
|
41
43
|
const suiteId = parseSuite(suiteTitle);
|
|
42
44
|
|
|
43
|
-
if (error) {
|
|
44
|
-
error
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
}
|
|
45
|
+
if (!error && test.displayError) {
|
|
46
|
+
error = { message: test.displayError };
|
|
47
|
+
error.inspect = function () {
|
|
48
|
+
// eslint-disable-line func-names
|
|
49
|
+
return this.message;
|
|
50
|
+
};
|
|
50
51
|
}
|
|
51
52
|
|
|
53
|
+
const formattedError = error
|
|
54
|
+
? {
|
|
55
|
+
message: error.message,
|
|
56
|
+
inspect:
|
|
57
|
+
error.inspect ||
|
|
58
|
+
function () {
|
|
59
|
+
return this.message;
|
|
60
|
+
},
|
|
61
|
+
}
|
|
62
|
+
: '';
|
|
63
|
+
|
|
52
64
|
const screenshots = Array.isArray(results.screenshots)
|
|
53
65
|
? results.screenshots
|
|
54
|
-
.filter(
|
|
55
|
-
screenshot =>
|
|
56
|
-
screenshot?.path &&
|
|
57
|
-
screenshot?.path.includes(title) &&
|
|
58
|
-
screenshot?.takenAt
|
|
59
|
-
)
|
|
66
|
+
.filter(screenshot => screenshot?.path && screenshot?.path.includes(title) && screenshot?.takenAt)
|
|
60
67
|
.map(screenshot => screenshot.path)
|
|
61
68
|
: [];
|
|
62
69
|
|
|
@@ -64,17 +71,30 @@ const testomatioReporter = on => {
|
|
|
64
71
|
|
|
65
72
|
let state;
|
|
66
73
|
switch (test.state) {
|
|
67
|
-
case 'passed':
|
|
68
|
-
|
|
74
|
+
case 'passed':
|
|
75
|
+
state = STATUS.PASSED;
|
|
76
|
+
break;
|
|
77
|
+
case 'failed':
|
|
78
|
+
state = STATUS.FAILED;
|
|
79
|
+
break;
|
|
69
80
|
case 'skipped':
|
|
70
|
-
case 'pending':
|
|
81
|
+
case 'pending':
|
|
71
82
|
default:
|
|
72
83
|
state = STATUS.SKIPPED;
|
|
73
84
|
}
|
|
74
85
|
|
|
75
|
-
addSpecTestsPromises.push(
|
|
76
|
-
|
|
77
|
-
|
|
86
|
+
addSpecTestsPromises.push(
|
|
87
|
+
client.addTestRun(state, {
|
|
88
|
+
title,
|
|
89
|
+
time,
|
|
90
|
+
example,
|
|
91
|
+
error: formattedError,
|
|
92
|
+
files,
|
|
93
|
+
suite_title: suiteTitle,
|
|
94
|
+
test_id: testId,
|
|
95
|
+
suite_id: suiteId,
|
|
96
|
+
}),
|
|
97
|
+
);
|
|
78
98
|
}
|
|
79
99
|
|
|
80
100
|
await Promise.all(addSpecTestsPromises);
|
package/lib/adapter/mocha.js
CHANGED
|
@@ -4,6 +4,7 @@ const chalk = require('chalk');
|
|
|
4
4
|
const TestomatClient = require('../client');
|
|
5
5
|
const { STATUS, TESTOMAT_TMP_STORAGE_DIR } = require('../constants');
|
|
6
6
|
const { parseTest, fileSystem } = require('../utils/utils');
|
|
7
|
+
const { TESTOMATIO } = require('../config');
|
|
7
8
|
|
|
8
9
|
const { EVENT_RUN_BEGIN, EVENT_RUN_END, EVENT_TEST_FAIL, EVENT_TEST_PASS, EVENT_TEST_PENDING } =
|
|
9
10
|
Mocha.Runner.constants;
|
|
@@ -15,7 +16,7 @@ function MochaReporter(runner, opts) {
|
|
|
15
16
|
let skipped = 0;
|
|
16
17
|
// let artifactStore;
|
|
17
18
|
|
|
18
|
-
const apiKey = opts?.reporterOptions?.apiKey ||
|
|
19
|
+
const apiKey = opts?.reporterOptions?.apiKey || TESTOMATIO;
|
|
19
20
|
|
|
20
21
|
const client = new TestomatClient({ apiKey });
|
|
21
22
|
|
package/lib/bin/startTest.js
CHANGED
|
@@ -5,6 +5,7 @@ const chalk = require('chalk');
|
|
|
5
5
|
const TestomatClient = require('../client');
|
|
6
6
|
const { APP_PREFIX, STATUS } = require('../constants');
|
|
7
7
|
const { version } = require('../../package.json');
|
|
8
|
+
const { TESTOMATIO } = require('../config');
|
|
8
9
|
|
|
9
10
|
console.log(chalk.cyan.bold(` 🤩 Testomat.io Reporter v${version}`));
|
|
10
11
|
|
|
@@ -20,7 +21,7 @@ program
|
|
|
20
21
|
|
|
21
22
|
if (opts.envFile) require('dotenv').config(opts.envFile); // eslint-disable-line
|
|
22
23
|
|
|
23
|
-
const apiKey = process.env['INPUT_TESTOMATIO-KEY'] ||
|
|
24
|
+
const apiKey = process.env['INPUT_TESTOMATIO-KEY'] || TESTOMATIO;
|
|
24
25
|
const title = process.env.TESTOMATIO_TITLE;
|
|
25
26
|
|
|
26
27
|
if (launch) {
|
package/lib/config.js
CHANGED
|
@@ -8,6 +8,10 @@ const debug = require('debug')('@testomatio/reporter:config');
|
|
|
8
8
|
const dotenv = require('dotenv');
|
|
9
9
|
const envFileVars = dotenv.config({ path: '.env' }).parsed; */
|
|
10
10
|
|
|
11
|
+
const TESTOMATIO = process.env.TESTOMATIO || process.env.TESTOMATIO_API_KEY || process.env.TESTOMATIO_TOKEN || '';
|
|
12
|
+
if (TESTOMATIO === 'undefined') console.error('TESTOMATIO is "undefined". Something went wrong. Contact dev team.');
|
|
13
|
+
process.env.TESTOMATIO = TESTOMATIO;
|
|
14
|
+
|
|
11
15
|
// select only TESTOMATIO related variables (only to print them in debug)
|
|
12
16
|
const testomatioEnvVars =
|
|
13
17
|
Object.keys(process.env)
|
|
@@ -22,3 +26,4 @@ debug('TESTOMATIO variables:', testomatioEnvVars);
|
|
|
22
26
|
const config = process.env;
|
|
23
27
|
|
|
24
28
|
module.exports = config;
|
|
29
|
+
module.exports.TESTOMATIO = TESTOMATIO;
|
package/lib/pipe/testomatio.js
CHANGED
|
@@ -5,6 +5,7 @@ const JsonCycle = require('json-cycle');
|
|
|
5
5
|
const { APP_PREFIX, STATUS } = require('../constants');
|
|
6
6
|
const { isValidUrl, foundedTestLog } = require('../utils/utils');
|
|
7
7
|
const { parseFilterParams, generateFilterRequestParams, setS3Credentials, } = require('../utils/pipe_utils');
|
|
8
|
+
const { TESTOMATIO } = require('../config');
|
|
8
9
|
|
|
9
10
|
if (process.env.TESTOMATIO_RUN) {
|
|
10
11
|
process.env.runId = process.env.TESTOMATIO_RUN;
|
|
@@ -20,7 +21,7 @@ class TestomatioPipe {
|
|
|
20
21
|
constructor(params, store) {
|
|
21
22
|
this.isEnabled = false;
|
|
22
23
|
this.url = params.testomatioUrl || process.env.TESTOMATIO_URL || 'https://app.testomat.io';
|
|
23
|
-
this.apiKey = params.apiKey ||
|
|
24
|
+
this.apiKey = params.apiKey || TESTOMATIO;
|
|
24
25
|
debug('Testomatio Pipe: ', this.apiKey ? 'API KEY' : '*no api key*');
|
|
25
26
|
if (!this.apiKey) {
|
|
26
27
|
return;
|
|
@@ -32,6 +33,7 @@ class TestomatioPipe {
|
|
|
32
33
|
this.sharedRun = !!process.env.TESTOMATIO_SHARED_RUN;
|
|
33
34
|
this.groupTitle = params.groupTitle || process.env.TESTOMATIO_RUNGROUP_TITLE;
|
|
34
35
|
this.env = process.env.TESTOMATIO_ENV;
|
|
36
|
+
this.label = process.env.TESTOMATIO_LABEL;
|
|
35
37
|
|
|
36
38
|
this.axios = axios.create({
|
|
37
39
|
baseURL: `${this.url.trim()}`,
|
|
@@ -130,6 +132,7 @@ class TestomatioPipe {
|
|
|
130
132
|
jira_id: this.jiraId,
|
|
131
133
|
env: this.env,
|
|
132
134
|
title: this.title,
|
|
135
|
+
label: this.label,
|
|
133
136
|
shared_run: this.sharedRun,
|
|
134
137
|
}).filter(([, value]) => !!value),
|
|
135
138
|
);
|
package/lib/utils/utils.js
CHANGED
|
@@ -141,7 +141,12 @@ const fetchSourceCode = (contents, opts = {}) => {
|
|
|
141
141
|
// remove special chars from title
|
|
142
142
|
if (!lineIndex && opts.title) {
|
|
143
143
|
const title = opts.title.replace(/[([@].*/g, '');
|
|
144
|
-
|
|
144
|
+
|
|
145
|
+
if (opts.lang === 'java') {
|
|
146
|
+
lineIndex = lines.findIndex(l => l.includes(`test${title}`));
|
|
147
|
+
} else {
|
|
148
|
+
lineIndex = lines.findIndex(l => l.includes(title));
|
|
149
|
+
}
|
|
145
150
|
}
|
|
146
151
|
|
|
147
152
|
if (opts.prepend) {
|
package/lib/xmlReader.js
CHANGED
|
@@ -1,35 +1,36 @@
|
|
|
1
1
|
const debug = require('debug')('@testomatio/reporter:xml');
|
|
2
|
-
const path = require(
|
|
2
|
+
const path = require('path');
|
|
3
3
|
const chalk = require('chalk');
|
|
4
|
-
const fs = require(
|
|
5
|
-
const { XMLParser } = require(
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
const { XMLParser } = require('fast-xml-parser');
|
|
6
6
|
const { APP_PREFIX, STATUS } = require('./constants');
|
|
7
|
-
const {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
7
|
+
const {
|
|
8
|
+
fetchFilesFromStackTrace,
|
|
9
|
+
fetchIdFromOutput,
|
|
10
|
+
fetchSourceCode,
|
|
11
|
+
fetchSourceCodeFromStackTrace,
|
|
12
|
+
fetchIdFromCode,
|
|
13
|
+
humanize,
|
|
14
|
+
} = require('./utils/utils');
|
|
14
15
|
const upload = require('./fileUploader');
|
|
15
16
|
const pipesFactory = require('./pipe');
|
|
16
17
|
const adapterFactory = require('./junit-adapter');
|
|
18
|
+
const { TESTOMATIO } = require('./config');
|
|
17
19
|
|
|
18
|
-
const TESTOMATIO_URL = process.env.TESTOMATIO_URL ||
|
|
19
|
-
const { TESTOMATIO_RUNGROUP_TITLE, TESTOMATIO_TITLE, TESTOMATIO_ENV, TESTOMATIO_RUN
|
|
20
|
+
const TESTOMATIO_URL = process.env.TESTOMATIO_URL || 'https://app.testomat.io';
|
|
21
|
+
const { TESTOMATIO_RUNGROUP_TITLE, TESTOMATIO_TITLE, TESTOMATIO_ENV, TESTOMATIO_RUN } = process.env;
|
|
20
22
|
|
|
21
23
|
const options = {
|
|
22
24
|
ignoreDeclaration: true,
|
|
23
25
|
ignoreAttributes: false,
|
|
24
26
|
alwaysCreateTextNode: false,
|
|
25
|
-
attributeNamePrefix:
|
|
27
|
+
attributeNamePrefix: '',
|
|
26
28
|
parseTagValue: true,
|
|
27
29
|
};
|
|
28
30
|
|
|
29
31
|
const reduceOptions = {};
|
|
30
32
|
|
|
31
33
|
class XmlReader {
|
|
32
|
-
|
|
33
34
|
constructor(opts = {}) {
|
|
34
35
|
this.requestParams = {
|
|
35
36
|
apiKey: opts.apiKey || TESTOMATIO,
|
|
@@ -39,18 +40,18 @@ class XmlReader {
|
|
|
39
40
|
group_title: TESTOMATIO_RUNGROUP_TITLE,
|
|
40
41
|
};
|
|
41
42
|
this.runId = opts.runId || TESTOMATIO_RUN;
|
|
42
|
-
this.adapter = adapterFactory(opts.lang?.toLowerCase(), opts)
|
|
43
|
+
this.adapter = adapterFactory(opts.lang?.toLowerCase(), opts);
|
|
43
44
|
if (!this.adapter) throw new Error('XML adapter for this format not found');
|
|
44
45
|
|
|
45
46
|
this.opts = opts || {};
|
|
46
|
-
this.store = {}
|
|
47
|
+
this.store = {};
|
|
47
48
|
this.pipes = pipesFactory(opts, this.store);
|
|
48
49
|
|
|
49
50
|
this.parser = new XMLParser(options);
|
|
50
|
-
this.tests = []
|
|
51
|
-
this.stats = {}
|
|
51
|
+
this.tests = [];
|
|
52
|
+
this.stats = {};
|
|
52
53
|
this.stats.language = opts.lang?.toLowerCase();
|
|
53
|
-
this.filesToUpload = {}
|
|
54
|
+
this.filesToUpload = {};
|
|
54
55
|
|
|
55
56
|
this.version = JSON.parse(fs.readFileSync(path.join(__dirname, '..', 'package.json')).toString()).version;
|
|
56
57
|
console.log(APP_PREFIX, `Testomatio Reporter v${this.version}`);
|
|
@@ -81,8 +82,8 @@ class XmlReader {
|
|
|
81
82
|
} else if (jsonResult.assemblies) {
|
|
82
83
|
return this.processXUnit(jsonResult.assemblies);
|
|
83
84
|
} else {
|
|
84
|
-
console.log(jsonResult)
|
|
85
|
-
throw new Error("Format can't be parsed")
|
|
85
|
+
console.log(jsonResult);
|
|
86
|
+
throw new Error("Format can't be parsed");
|
|
86
87
|
}
|
|
87
88
|
|
|
88
89
|
return this.processJUnit(jsonSuite);
|
|
@@ -95,7 +96,7 @@ class XmlReader {
|
|
|
95
96
|
const resultTests = processTestSuite(testsuite);
|
|
96
97
|
|
|
97
98
|
const hasFailures = resultTests.filter(t => t.status === 'failed').length > 0;
|
|
98
|
-
const status =
|
|
99
|
+
const status = failures > 0 || errors > 0 || hasFailures ? 'failed' : 'passed';
|
|
99
100
|
|
|
100
101
|
this.tests = this.tests.concat(resultTests);
|
|
101
102
|
|
|
@@ -134,21 +135,22 @@ class XmlReader {
|
|
|
134
135
|
let defs = jsonSuite?.TestRun?.TestDefinitions?.UnitTest;
|
|
135
136
|
if (!Array.isArray(defs)) defs = [defs].filter(d => !!d);
|
|
136
137
|
|
|
137
|
-
const tests =
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
138
|
+
const tests =
|
|
139
|
+
defs.map(td => {
|
|
140
|
+
const title = td.name.replace(/\(.*?\)/, '').trim();
|
|
141
|
+
let example = td.name.match(/\((.*?)\)/);
|
|
142
|
+
if (example) example = { ...example[1].split(',') };
|
|
143
|
+
const suite = td.TestMethod.className.split(', ')[0].split('.');
|
|
144
|
+
const suite_title = suite.pop();
|
|
145
|
+
return {
|
|
146
|
+
title,
|
|
147
|
+
example,
|
|
148
|
+
file: suite.join('/'),
|
|
149
|
+
description: td.Description,
|
|
150
|
+
suite_title,
|
|
151
|
+
id: td.Execution.id,
|
|
152
|
+
};
|
|
153
|
+
}) || [];
|
|
152
154
|
|
|
153
155
|
let result = jsonSuite?.TestRun?.Results?.UnitTestResult;
|
|
154
156
|
if (!Array.isArray(result)) result = [result].filter(d => !!d);
|
|
@@ -159,10 +161,9 @@ class XmlReader {
|
|
|
159
161
|
run_time: parseFloat(td.duration) * 1000,
|
|
160
162
|
status: td.outcome,
|
|
161
163
|
stack: td.Output.StdOut,
|
|
162
|
-
files: td?.ResultFiles?.ResultFile?.map(rf => rf.path)
|
|
164
|
+
files: td?.ResultFiles?.ResultFile?.map(rf => rf.path),
|
|
163
165
|
}));
|
|
164
166
|
|
|
165
|
-
|
|
166
167
|
results.forEach(r => {
|
|
167
168
|
const test = tests.find(t => t.id === r.id) || {};
|
|
168
169
|
r.suite_title = test.suite_title;
|
|
@@ -222,7 +223,7 @@ class XmlReader {
|
|
|
222
223
|
|
|
223
224
|
if (testCase.failure) {
|
|
224
225
|
message = testCase.failure.message;
|
|
225
|
-
stack = testCase.failure['stack-trace']
|
|
226
|
+
stack = testCase.failure['stack-trace'];
|
|
226
227
|
}
|
|
227
228
|
if (testCase.reason) {
|
|
228
229
|
message = testCase.reason.message;
|
|
@@ -249,7 +250,6 @@ class XmlReader {
|
|
|
249
250
|
suite_title,
|
|
250
251
|
run_time,
|
|
251
252
|
});
|
|
252
|
-
|
|
253
253
|
});
|
|
254
254
|
});
|
|
255
255
|
});
|
|
@@ -282,12 +282,12 @@ class XmlReader {
|
|
|
282
282
|
passed_count: 0,
|
|
283
283
|
failed_count: 0,
|
|
284
284
|
skipped_count: 0,
|
|
285
|
-
}
|
|
285
|
+
};
|
|
286
286
|
this.tests.forEach(t => {
|
|
287
287
|
this.stats.tests_count++;
|
|
288
288
|
if (t.status === 'passed') this.stats.passed_count++;
|
|
289
289
|
if (t.status === 'failed') this.stats.failed_count++;
|
|
290
|
-
})
|
|
290
|
+
});
|
|
291
291
|
if (this.stats.failed_count) this.stats.status = 'failed';
|
|
292
292
|
|
|
293
293
|
return this.stats;
|
|
@@ -296,7 +296,7 @@ class XmlReader {
|
|
|
296
296
|
fetchSourceCode() {
|
|
297
297
|
this.tests.forEach(t => {
|
|
298
298
|
try {
|
|
299
|
-
const file = this.adapter.getFilePath(t)
|
|
299
|
+
const file = this.adapter.getFilePath(t);
|
|
300
300
|
if (!file) return;
|
|
301
301
|
|
|
302
302
|
if (!this.stats.language) {
|
|
@@ -309,16 +309,16 @@ class XmlReader {
|
|
|
309
309
|
}
|
|
310
310
|
|
|
311
311
|
if (!fs.existsSync(file)) {
|
|
312
|
-
debug('Failed to open file with the source code', file)
|
|
312
|
+
debug('Failed to open file with the source code', file);
|
|
313
313
|
return;
|
|
314
314
|
}
|
|
315
315
|
const contents = fs.readFileSync(file).toString();
|
|
316
|
-
t.code = fetchSourceCode(contents, { ...t, lang: this.stats.language })
|
|
316
|
+
t.code = fetchSourceCode(contents, { ...t, lang: this.stats.language });
|
|
317
317
|
if (t.code) debug('Fetched code for test %s', t.title);
|
|
318
|
-
t.test_id = fetchIdFromCode(t.code, { lang: this.stats.language })
|
|
318
|
+
t.test_id = fetchIdFromCode(t.code, { lang: this.stats.language });
|
|
319
319
|
if (t.test_id) debug('Fetched test id %s for test %s', t.test_id, t.title);
|
|
320
320
|
} catch (err) {
|
|
321
|
-
debug(err)
|
|
321
|
+
debug(err);
|
|
322
322
|
}
|
|
323
323
|
});
|
|
324
324
|
}
|
|
@@ -326,21 +326,22 @@ class XmlReader {
|
|
|
326
326
|
formatTests() {
|
|
327
327
|
this.tests.forEach(t => {
|
|
328
328
|
if (t.file) {
|
|
329
|
-
t.file = t.file.replace(process.cwd() + path.sep, '')
|
|
329
|
+
t.file = t.file.replace(process.cwd() + path.sep, '');
|
|
330
330
|
}
|
|
331
331
|
|
|
332
|
-
this.adapter.formatTest(t)
|
|
332
|
+
this.adapter.formatTest(t);
|
|
333
333
|
|
|
334
334
|
t.title = humanize(t.title);
|
|
335
335
|
});
|
|
336
336
|
}
|
|
337
337
|
|
|
338
338
|
formatErrors() {
|
|
339
|
-
this.tests
|
|
340
|
-
t
|
|
341
|
-
t
|
|
342
|
-
|
|
343
|
-
|
|
339
|
+
this.tests
|
|
340
|
+
.filter(t => !!t.stack)
|
|
341
|
+
.forEach(t => {
|
|
342
|
+
t.stack = this.formatStack(t);
|
|
343
|
+
t.message = this.adapter.formatMessage(t);
|
|
344
|
+
});
|
|
344
345
|
}
|
|
345
346
|
|
|
346
347
|
formatStack(t) {
|
|
@@ -358,7 +359,7 @@ class XmlReader {
|
|
|
358
359
|
async uploadArtifacts() {
|
|
359
360
|
for (const test of this.tests.filter(t => !!t.stack)) {
|
|
360
361
|
let files = [];
|
|
361
|
-
if (test.files?.length) files = test.files.map(f => path.join(process.cwd(), f))
|
|
362
|
+
if (test.files?.length) files = test.files.map(f => path.join(process.cwd(), f));
|
|
362
363
|
files = [...files, ...fetchFilesFromStackTrace(test.stack)];
|
|
363
364
|
|
|
364
365
|
if (!files.length) continue;
|
|
@@ -377,7 +378,7 @@ class XmlReader {
|
|
|
377
378
|
group_title: this.requestParams.group_title,
|
|
378
379
|
};
|
|
379
380
|
|
|
380
|
-
debug(
|
|
381
|
+
debug('Run', runParams);
|
|
381
382
|
|
|
382
383
|
return Promise.all(this.pipes.map(p => p.createRun(runParams)));
|
|
383
384
|
}
|
|
@@ -390,12 +391,10 @@ class XmlReader {
|
|
|
390
391
|
this.formatErrors();
|
|
391
392
|
this.formatTests();
|
|
392
393
|
|
|
393
|
-
debug(
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
tests: this.tests,
|
|
398
|
-
})
|
|
394
|
+
debug('Uploading data', {
|
|
395
|
+
...this.stats,
|
|
396
|
+
tests: this.tests,
|
|
397
|
+
});
|
|
399
398
|
|
|
400
399
|
const dataString = {
|
|
401
400
|
...this.stats,
|
|
@@ -410,59 +409,62 @@ class XmlReader {
|
|
|
410
409
|
|
|
411
410
|
module.exports = XmlReader;
|
|
412
411
|
|
|
413
|
-
|
|
414
412
|
function reduceTestCases(prev, item) {
|
|
415
413
|
let testCases = item.testcase;
|
|
416
414
|
if (!testCases) testCases = item['test-case'];
|
|
417
415
|
if (!Array.isArray(testCases)) {
|
|
418
|
-
testCases = [testCases]
|
|
416
|
+
testCases = [testCases];
|
|
419
417
|
}
|
|
420
418
|
const suiteOutput = item['system-out'] || item.output || item.log || '';
|
|
421
419
|
const suiteErr = item['system-err'] || item.output || item.log || '';
|
|
422
|
-
testCases
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
420
|
+
testCases
|
|
421
|
+
.filter(t => !!t)
|
|
422
|
+
.forEach(testCaseItem => {
|
|
423
|
+
const file = testCaseItem.file || item.filepath || '';
|
|
424
|
+
|
|
425
|
+
let stack = '';
|
|
426
|
+
let message = '';
|
|
427
|
+
if (testCaseItem.error) stack = testCaseItem.error;
|
|
428
|
+
if (testCaseItem.failure) stack = testCaseItem.failure;
|
|
429
|
+
if (testCaseItem?.failure?.['stack-trace']) stack = testCaseItem.failure['stack-trace'];
|
|
430
|
+
if (testCaseItem?.failure?.message) message = testCaseItem.failure.message;
|
|
431
|
+
if (testCaseItem?.error?.message) message = testCaseItem.error.message;
|
|
432
|
+
|
|
433
|
+
if (testCaseItem.failure && testCaseItem.failure['#text']) stack = testCaseItem.failure['#text'];
|
|
434
|
+
if (testCaseItem.error && testCaseItem.error['#text']) stack = testCaseItem.error['#text'];
|
|
435
|
+
if (!message) message = stack.trim().split('\n')[0];
|
|
436
|
+
|
|
437
|
+
// eslint-disable-next-line
|
|
438
|
+
stack = `${
|
|
439
|
+
testCaseItem['system-out'] || testCaseItem.output || testCaseItem.log || ''
|
|
440
|
+
}\n\n${stack}\n\n${suiteOutput}\n\n${suiteErr}`.trim();
|
|
441
|
+
const testId = fetchIdFromOutput(stack);
|
|
442
|
+
|
|
443
|
+
let status = STATUS.PASSED.toString();
|
|
444
|
+
if ('failure' in testCaseItem || 'error' in testCaseItem) status = STATUS.FAILED;
|
|
445
|
+
if ('skipped' in testCaseItem) status = STATUS.SKIPPED;
|
|
446
|
+
|
|
447
|
+
prev.push({
|
|
448
|
+
create: true,
|
|
449
|
+
file,
|
|
450
|
+
stack,
|
|
451
|
+
test_id: testId,
|
|
452
|
+
message,
|
|
453
|
+
line: testCaseItem.lineno,
|
|
454
|
+
// seconds are used in junit reports, but ms are used by testomatio
|
|
455
|
+
run_time: parseFloat(testCaseItem.time || testCaseItem.duration) * 1000,
|
|
456
|
+
status,
|
|
457
|
+
title: testCaseItem.name,
|
|
458
|
+
suite_title: reduceOptions.preferClassname ? testCaseItem.classname : item.name || testCaseItem.classname,
|
|
459
|
+
});
|
|
460
|
+
});
|
|
459
461
|
return prev;
|
|
460
462
|
}
|
|
461
463
|
|
|
462
464
|
function processTestSuite(testsuite) {
|
|
463
465
|
if (!testsuite) return [];
|
|
464
|
-
if (testsuite.testsuite) return processTestSuite(testsuite.testsuite)
|
|
465
|
-
if (testsuite['test-suite']) return processTestSuite(testsuite['test-suite'])
|
|
466
|
+
if (testsuite.testsuite) return processTestSuite(testsuite.testsuite);
|
|
467
|
+
if (testsuite['test-suite']) return processTestSuite(testsuite['test-suite']);
|
|
466
468
|
|
|
467
469
|
let suites = testsuite;
|
|
468
470
|
if (!Array.isArray(testsuite)) {
|
package/package.json
CHANGED