@testomatio/reporter 0.8.0-beta.9 → 0.8.2-beta.1
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 +251 -0
- package/README.md +77 -7
- package/lib/ArtifactStorage.js +131 -0
- package/lib/adapter/codecept.js +25 -24
- package/lib/adapter/cucumber/current.js +16 -12
- package/lib/adapter/cucumber/legacy.js +14 -10
- package/lib/adapter/cypress-plugin/index.js +7 -7
- package/lib/adapter/jasmine.js +5 -4
- package/lib/adapter/jest.js +7 -11
- package/lib/adapter/mocha.js +69 -18
- package/lib/adapter/playwright.js +28 -31
- package/lib/adapter/webdriver.js +2 -1
- package/lib/bin/reportXml.js +5 -2
- package/lib/bin/startTest.js +2 -2
- package/lib/client.js +73 -49
- package/lib/constants.js +17 -2
- package/lib/fileUploader.js +103 -64
- package/lib/junit-adapter/csharp.js +0 -1
- package/lib/junit-adapter/index.js +3 -2
- package/lib/pipe/csv.js +133 -0
- package/lib/pipe/github.js +137 -73
- package/lib/pipe/gitlab.js +229 -0
- package/lib/pipe/index.js +27 -12
- package/lib/pipe/testomatio.js +71 -44
- package/lib/reporter.js +2 -0
- package/lib/util.js +46 -13
- package/lib/xmlReader.js +47 -31
- package/package.json +12 -3
- package/testcafe/index.js +0 -61
- package/testcafe/package.json +0 -14
package/lib/pipe/testomatio.js
CHANGED
|
@@ -1,100 +1,114 @@
|
|
|
1
|
+
const debug = require('debug')('@testomatio/reporter:pipe:testomatio');
|
|
1
2
|
const chalk = require('chalk');
|
|
2
3
|
const axios = require('axios');
|
|
3
4
|
const JsonCycle = require('json-cycle');
|
|
4
|
-
const {
|
|
5
|
-
const { APP_PREFIX } = require('../constants');
|
|
5
|
+
const { APP_PREFIX, STATUS } = require('../constants');
|
|
6
6
|
const { isValidUrl } = require('../util');
|
|
7
7
|
|
|
8
|
+
const { TESTOMATIO_RUN } = process.env;
|
|
8
9
|
if (TESTOMATIO_RUN) {
|
|
9
10
|
process.env.runId = TESTOMATIO_RUN;
|
|
10
11
|
}
|
|
11
12
|
|
|
13
|
+
/**
|
|
14
|
+
* @typedef {import('../../types').Pipe} Pipe
|
|
15
|
+
* @typedef {import('../../types').TestData} TestData
|
|
16
|
+
* @class TestomatioPipe
|
|
17
|
+
* @implements {Pipe}
|
|
18
|
+
*/
|
|
12
19
|
class TestomatioPipe {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
constructor(params, store = {}) {
|
|
20
|
+
constructor(params, store) {
|
|
21
|
+
this.isEnabled = false;
|
|
16
22
|
this.url = params.testomatioUrl || process.env.TESTOMATIO_URL || 'https://app.testomat.io';
|
|
17
23
|
this.apiKey = params.apiKey || process.env.TESTOMATIO;
|
|
18
|
-
|
|
19
|
-
console.log(APP_PREFIX, 'Testomatio Pipe: ', this.apiKey ? 'API KEY' : '*no api key*');
|
|
20
|
-
}
|
|
24
|
+
debug('Testomatio Pipe: ', this.apiKey ? 'API KEY' : '*no api key*');
|
|
21
25
|
if (!this.apiKey) {
|
|
22
26
|
return;
|
|
23
27
|
}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
}
|
|
27
|
-
this.store = store;
|
|
28
|
+
debug('Testomatio Pipe: Enabled');
|
|
29
|
+
this.store = store || {};
|
|
28
30
|
this.title = params.title || process.env.TESTOMATIO_TITLE;
|
|
31
|
+
this.groupTitle = params.groupTitle || process.env.TESTOMATIO_RUNGROUP_TITLE;
|
|
32
|
+
this.env = process.env.TESTOMATIO_ENV;
|
|
29
33
|
this.axios = axios.create();
|
|
30
34
|
this.isEnabled = true;
|
|
35
|
+
// do not finish this run (for parallel testing)
|
|
31
36
|
this.proceed = process.env.TESTOMATIO_PROCEED;
|
|
32
37
|
this.runId = params.runId || process.env.runId;
|
|
33
38
|
this.createNewTests = !!process.env.TESTOMATIO_CREATE;
|
|
34
39
|
|
|
35
40
|
if (!isValidUrl(this.url.trim())) {
|
|
36
41
|
this.isEnabled = false;
|
|
37
|
-
console.
|
|
38
|
-
APP_PREFIX,
|
|
39
|
-
chalk.red(`Error creating report on Testomat.io, report url '${this.url}' is invalid`),
|
|
40
|
-
);
|
|
41
|
-
return;
|
|
42
|
+
console.error(APP_PREFIX, chalk.red(`Error creating report on Testomat.io, report url '${this.url}' is invalid`));
|
|
42
43
|
}
|
|
43
44
|
}
|
|
44
45
|
|
|
45
|
-
|
|
46
|
+
/**
|
|
47
|
+
* @returns Promise<void>
|
|
48
|
+
*/
|
|
49
|
+
async createRun() {
|
|
46
50
|
if (!this.isEnabled) return;
|
|
47
51
|
|
|
48
|
-
runParams
|
|
49
|
-
|
|
50
|
-
|
|
52
|
+
const runParams = Object.fromEntries(
|
|
53
|
+
Object.entries({
|
|
54
|
+
api_key: this.apiKey.trim(),
|
|
55
|
+
group_title: this.groupTitle,
|
|
56
|
+
env: this.env,
|
|
57
|
+
title: this.title,
|
|
58
|
+
}).filter(([, value]) => !!value)
|
|
59
|
+
);
|
|
60
|
+
|
|
51
61
|
if (this.runId) {
|
|
52
|
-
return this.axios.put(`${this.url.trim()}/api/reporter/${this.runId}`, runParams)
|
|
62
|
+
return this.axios.put(`${this.url.trim()}/api/reporter/${this.runId}`, runParams);
|
|
53
63
|
}
|
|
54
|
-
|
|
64
|
+
|
|
55
65
|
try {
|
|
56
66
|
const resp = await this.axios.post(`${this.url.trim()}/api/reporter`, runParams, {
|
|
57
67
|
maxContentLength: Infinity,
|
|
58
68
|
maxBodyLength: Infinity,
|
|
59
|
-
})
|
|
69
|
+
});
|
|
60
70
|
this.runId = resp.data.uid;
|
|
61
71
|
this.runUrl = `${this.url}/${resp.data.url.split('/').splice(3).join('/')}`;
|
|
62
72
|
this.store.runUrl = this.runUrl;
|
|
63
|
-
this.store.runId = this.runId;
|
|
73
|
+
this.store.runId = this.runId;
|
|
64
74
|
console.log(APP_PREFIX, '📊 Report created. Report ID:', this.runId);
|
|
65
75
|
process.env.runId = this.runId;
|
|
66
76
|
} catch (err) {
|
|
67
|
-
console.
|
|
77
|
+
console.error(
|
|
68
78
|
APP_PREFIX,
|
|
69
79
|
'Error creating Testomat.io report, please check if your API key is valid. Skipping report',
|
|
70
80
|
);
|
|
71
81
|
}
|
|
72
82
|
}
|
|
73
83
|
|
|
74
|
-
|
|
84
|
+
/**
|
|
85
|
+
*
|
|
86
|
+
* @param testData data
|
|
87
|
+
* @returns
|
|
88
|
+
*/
|
|
89
|
+
addTest(data) {
|
|
75
90
|
if (!this.isEnabled) return;
|
|
76
91
|
if (!this.runId) return;
|
|
77
92
|
data.api_key = this.apiKey;
|
|
78
93
|
data.create = this.createNewTests;
|
|
79
94
|
const json = JsonCycle.stringify(data);
|
|
80
95
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
} catch (err) {
|
|
96
|
+
return this.axios.post(`${this.url}/api/reporter/${this.runId}/testrun`, json, {
|
|
97
|
+
maxContentLength: Infinity,
|
|
98
|
+
maxBodyLength: Infinity,
|
|
99
|
+
headers: {
|
|
100
|
+
// Overwrite Axios's automatically set Content-Type
|
|
101
|
+
'Content-Type': 'application/json',
|
|
102
|
+
},
|
|
103
|
+
})
|
|
104
|
+
.catch((err) => {
|
|
91
105
|
if (err.response) {
|
|
92
106
|
if (err.response.status >= 400) {
|
|
93
|
-
const
|
|
107
|
+
const responseData = err.response.data || { message: '' };
|
|
94
108
|
console.log(
|
|
95
109
|
APP_PREFIX,
|
|
96
110
|
chalk.blue(this.title),
|
|
97
|
-
`Report couldn't be processed: (${err.response.status}) ${
|
|
111
|
+
`Report couldn't be processed: (${err.response.status}) ${responseData.message}`,
|
|
98
112
|
);
|
|
99
113
|
return;
|
|
100
114
|
}
|
|
@@ -102,18 +116,31 @@ class TestomatioPipe {
|
|
|
102
116
|
} else {
|
|
103
117
|
console.log(APP_PREFIX, chalk.blue(this.title), "Report couldn't be processed", err);
|
|
104
118
|
}
|
|
105
|
-
}
|
|
106
|
-
|
|
119
|
+
});
|
|
107
120
|
}
|
|
108
121
|
|
|
122
|
+
/**
|
|
123
|
+
* @param {import('../../types').RunData} params
|
|
124
|
+
* @returns
|
|
125
|
+
*/
|
|
109
126
|
async finishRun(params) {
|
|
110
127
|
if (!this.isEnabled) return;
|
|
128
|
+
|
|
129
|
+
const { status, parallel } = params;
|
|
130
|
+
|
|
131
|
+
let status_event;
|
|
132
|
+
|
|
133
|
+
if (status === STATUS.FINISHED) status_event = 'finish';
|
|
134
|
+
if (status === STATUS.PASSED) status_event = 'pass';
|
|
135
|
+
if (status === STATUS.FAILED) status_event = 'fail';
|
|
136
|
+
if (parallel) status_event += '_parallel';
|
|
137
|
+
|
|
111
138
|
try {
|
|
112
139
|
if (this.runId && !this.proceed) {
|
|
113
140
|
await this.axios.put(`${this.url}/api/reporter/${this.runId}`, {
|
|
114
141
|
api_key: this.apiKey,
|
|
115
|
-
status_event
|
|
116
|
-
|
|
142
|
+
status_event,
|
|
143
|
+
tests: params.tests,
|
|
117
144
|
});
|
|
118
145
|
if (this.runUrl) {
|
|
119
146
|
console.log(APP_PREFIX, '📊 Report Saved. Report URL:', chalk.magenta(this.runUrl));
|
|
@@ -134,4 +161,4 @@ class TestomatioPipe {
|
|
|
134
161
|
}
|
|
135
162
|
}
|
|
136
163
|
|
|
137
|
-
module.exports = TestomatioPipe;
|
|
164
|
+
module.exports = TestomatioPipe;
|
package/lib/reporter.js
CHANGED
package/lib/util.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const { URL } = require('url');
|
|
2
|
-
const { sep } = require('path');
|
|
2
|
+
const { sep, basename } = require('path');
|
|
3
3
|
const chalk = require('chalk');
|
|
4
4
|
const fs = require('fs');
|
|
5
5
|
const isValid = require('is-valid-path');
|
|
@@ -7,7 +7,7 @@ const isValid = require('is-valid-path');
|
|
|
7
7
|
/**
|
|
8
8
|
* @param {String} testTitle - Test title
|
|
9
9
|
*
|
|
10
|
-
* @returns {String} testId
|
|
10
|
+
* @returns {String|null} testId
|
|
11
11
|
*/
|
|
12
12
|
const parseTest = testTitle => {
|
|
13
13
|
const captures = testTitle.match(/@T([\w\d]+)/);
|
|
@@ -21,7 +21,7 @@ const parseTest = testTitle => {
|
|
|
21
21
|
/**
|
|
22
22
|
* @param {String} suiteTitle - suite title
|
|
23
23
|
*
|
|
24
|
-
* @returns {String} suiteId
|
|
24
|
+
* @returns {String|null} suiteId
|
|
25
25
|
*/
|
|
26
26
|
const parseSuite = suiteTitle => {
|
|
27
27
|
const captures = suiteTitle.match(/@S([\w\d]+)/);
|
|
@@ -63,12 +63,12 @@ const fetchSourceCodeFromStackTrace = (stack = '') => {
|
|
|
63
63
|
// .map(l => l.match(/\[(.*?)\]/)?.[1] || l) // minitest format
|
|
64
64
|
// .map(l => l.split(':')[0])
|
|
65
65
|
.map(l => l.trim())
|
|
66
|
-
.map(l => l.split(' ').find(p => p.includes(':')))
|
|
67
|
-
.filter(l => isValid(l
|
|
66
|
+
.map(l => l.split(' ').find(p => p.includes(':')) || '')
|
|
67
|
+
.filter(l => isValid(l?.split(':')[0]))
|
|
68
68
|
|
|
69
69
|
// // filter out 3rd party libs
|
|
70
|
-
.filter(l => !l
|
|
71
|
-
.filter(l => !l
|
|
70
|
+
.filter(l => !l?.includes(`vendor${ sep}`))
|
|
71
|
+
.filter(l => !l?.includes(`node_modules${ sep}`))
|
|
72
72
|
.filter(l => fs.existsSync(l.split(':')[0]))
|
|
73
73
|
.filter(l => fs.lstatSync(l.split(':')[0]).isFile())
|
|
74
74
|
|
|
@@ -79,6 +79,8 @@ const fetchSourceCodeFromStackTrace = (stack = '') => {
|
|
|
79
79
|
const prepend = 3;
|
|
80
80
|
const source = fetchSourceCode(fs.readFileSync(file).toString(), { line, prepend, limit: 7 })
|
|
81
81
|
|
|
82
|
+
if (!source) return '';
|
|
83
|
+
|
|
82
84
|
return source.split('\n')
|
|
83
85
|
.map((l, i) => {
|
|
84
86
|
if (i === prepend) return `${line} > ${chalk.bold(l)}`;
|
|
@@ -137,15 +139,46 @@ const fetchSourceCode = (contents, opts = {}) => {
|
|
|
137
139
|
}
|
|
138
140
|
}
|
|
139
141
|
|
|
140
|
-
const isSameTest = (test, t) =>
|
|
142
|
+
const isSameTest = (test, t) => (typeof t === 'object')
|
|
143
|
+
&& (typeof test === 'object')
|
|
144
|
+
&& t.title === test.title
|
|
145
|
+
&& t.suite_title === test.suite_title
|
|
146
|
+
&& Object.values(t.example || {}) === Object.values(test.example || {})
|
|
147
|
+
&& t.test_id === test.test_id;
|
|
148
|
+
|
|
149
|
+
const getCurrentDateTime = () => {
|
|
150
|
+
const today = new Date();
|
|
151
|
+
|
|
152
|
+
return `${today.getFullYear() }_${ today.getMonth() + 1 }_${ today.getDate() }_${
|
|
153
|
+
today.getHours() }_${ today.getMinutes() }_${ today.getSeconds()}`;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* @param {Object} test - Test adapter object
|
|
158
|
+
*
|
|
159
|
+
* @returns {String|null} testInfo as one string
|
|
160
|
+
*/
|
|
161
|
+
const specificTestInfo = test => {
|
|
162
|
+
// TODO: afterEach has another context.... need to add specific handler, maybe...
|
|
163
|
+
if (test?.title && test?.file) {
|
|
164
|
+
|
|
165
|
+
return `${basename(test.file).split(".").join("#")
|
|
166
|
+
}#${
|
|
167
|
+
test.title.split(" ").join("#")}`;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
return null;
|
|
171
|
+
};
|
|
141
172
|
|
|
142
173
|
module.exports = {
|
|
143
|
-
parseTest,
|
|
144
|
-
parseSuite,
|
|
145
|
-
ansiRegExp,
|
|
146
174
|
isSameTest,
|
|
147
|
-
isValidUrl,
|
|
148
175
|
fetchSourceCode,
|
|
149
176
|
fetchSourceCodeFromStackTrace,
|
|
150
177
|
fetchFilesFromStackTrace,
|
|
151
|
-
|
|
178
|
+
getCurrentDateTime,
|
|
179
|
+
specificTestInfo,
|
|
180
|
+
isValidUrl,
|
|
181
|
+
ansiRegExp,
|
|
182
|
+
parseTest,
|
|
183
|
+
parseSuite
|
|
184
|
+
}
|
package/lib/xmlReader.js
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
|
+
const debug = require('debug')('@testomatio/reporter:xml');
|
|
1
2
|
const path = require("path");
|
|
2
3
|
const chalk = require('chalk');
|
|
3
4
|
const fs = require("fs");
|
|
4
|
-
|
|
5
|
-
// const util = require("util"); // you can see a result
|
|
6
5
|
const { XMLParser } = require("fast-xml-parser");
|
|
7
|
-
const { APP_PREFIX,
|
|
6
|
+
const { APP_PREFIX, STATUS } = require('./constants');
|
|
8
7
|
const { fetchFilesFromStackTrace, fetchSourceCode, fetchSourceCodeFromStackTrace } = require('./util');
|
|
9
8
|
const upload = require('./fileUploader');
|
|
10
9
|
const pipesFactory = require('./pipe');
|
|
11
10
|
const adapterFactory = require('./junit-adapter');
|
|
12
11
|
|
|
12
|
+
|
|
13
13
|
const TESTOMATIO_URL = process.env.TESTOMATIO_URL || "https://app.testomat.io";
|
|
14
14
|
const TESTOMATIO = process.env.TESTOMATIO; // key?
|
|
15
15
|
const { TESTOMATIO_RUNGROUP_TITLE, TESTOMATIO_TITLE, TESTOMATIO_ENV, TESTOMATIO_RUN } = process.env;
|
|
@@ -36,7 +36,9 @@ class XmlReader {
|
|
|
36
36
|
};
|
|
37
37
|
this.runId = opts.runId || TESTOMATIO_RUN;
|
|
38
38
|
this.adapter = adapterFactory(null, opts)
|
|
39
|
-
this.
|
|
39
|
+
if (!this.adapter) throw new Error('XML adapter for this format not found');
|
|
40
|
+
|
|
41
|
+
this.opts = opts || {};
|
|
40
42
|
const store = {}
|
|
41
43
|
this.pipes = pipesFactory(opts, store);
|
|
42
44
|
|
|
@@ -92,7 +94,7 @@ class XmlReader {
|
|
|
92
94
|
create_tests: true,
|
|
93
95
|
name,
|
|
94
96
|
tests_count: parseInt(tests, 10),
|
|
95
|
-
passed_count: parseInt(tests - failures, 10),
|
|
97
|
+
passed_count: parseInt(tests, 10) - parseInt(failures, 10),
|
|
96
98
|
failed_count: parseInt(failures, 10),
|
|
97
99
|
skipped_count: 0,
|
|
98
100
|
tests: resultTests,
|
|
@@ -119,49 +121,62 @@ class XmlReader {
|
|
|
119
121
|
}
|
|
120
122
|
|
|
121
123
|
processTRX(jsonSuite) {
|
|
122
|
-
|
|
124
|
+
let defs = jsonSuite?.TestRun?.TestDefinitions?.UnitTest;
|
|
125
|
+
if (!Array.isArray(defs)) defs = [defs].filter(d => !!d);
|
|
126
|
+
|
|
127
|
+
const tests = defs.map(td => {
|
|
123
128
|
const title = td.name.replace(/\(.*?\)/, '').trim();
|
|
124
129
|
let example = td.name.match(/\((.*?)\)/);
|
|
125
130
|
if (example) example = { ...example[1].split(',')};
|
|
126
|
-
const suite = td.TestMethod.className.split('.');
|
|
131
|
+
const suite = td.TestMethod.className.split(', ')[0].split('.');
|
|
127
132
|
const suite_title = suite.pop();
|
|
128
133
|
return {
|
|
129
134
|
title,
|
|
130
135
|
example,
|
|
131
136
|
file: suite.join('/'),
|
|
137
|
+
description: td.Description,
|
|
132
138
|
suite_title,
|
|
133
139
|
id: td.Execution.id,
|
|
134
140
|
}
|
|
135
141
|
}) || [];
|
|
136
142
|
|
|
137
|
-
|
|
143
|
+
let result = jsonSuite?.TestRun?.Results?.UnitTestResult;
|
|
144
|
+
if (!Array.isArray(result)) result = [result].filter(d => !!d);
|
|
145
|
+
|
|
146
|
+
const results = result.map(td => ({
|
|
138
147
|
id: td.executionId,
|
|
139
148
|
run_time: parseFloat(td.duration),
|
|
140
149
|
status: td.outcome,
|
|
141
|
-
stack: td.Output.StdOut
|
|
150
|
+
stack: td.Output.StdOut,
|
|
151
|
+
files: td?.ResultFiles?.ResultFile?.map(rf => rf.path)
|
|
142
152
|
}));
|
|
143
153
|
|
|
154
|
+
|
|
144
155
|
results.forEach(r => {
|
|
145
|
-
const test = tests.find(t => t.id === r.id) || {};
|
|
156
|
+
const test = tests.find(t => t.id === r.id) || { };
|
|
146
157
|
r.suite_title = test.suite_title;
|
|
147
158
|
r.title = test.title?.trim();
|
|
159
|
+
if (test.code) r.code = test.code;
|
|
160
|
+
if (test.description) r.description = test.description;
|
|
148
161
|
if (test.example) r.example = test.example;
|
|
149
162
|
if (test.file) r.file = test.file;
|
|
150
163
|
r.create = true;
|
|
151
|
-
if (r.status === 'Passed') r.status = PASSED;
|
|
152
|
-
if (r.status === 'Failed') r.status = FAILED;
|
|
153
|
-
if (r.status === 'Skipped') r.status = SKIPPED;
|
|
164
|
+
if (r.status === 'Passed') r.status = STATUS.PASSED;
|
|
165
|
+
if (r.status === 'Failed') r.status = STATUS.FAILED;
|
|
166
|
+
if (r.status === 'Skipped') r.status = STATUS.SKIPPED;
|
|
154
167
|
delete r.id;
|
|
155
168
|
});
|
|
169
|
+
|
|
170
|
+
debug(results);
|
|
156
171
|
|
|
157
172
|
const counters = jsonSuite?.TestRun?.ResultSummary?.Counters || {};
|
|
158
173
|
|
|
159
174
|
const failed_count = parseInt(counters.failed, 10) + parseInt(counters.error, 10);
|
|
160
175
|
|
|
161
|
-
let status = PASSED;
|
|
162
|
-
if (failed_count > 0) status = FAILED;
|
|
176
|
+
let status = STATUS.PASSED.toString();
|
|
177
|
+
if (failed_count > 0) status = STATUS.FAILED;
|
|
163
178
|
|
|
164
|
-
this.tests = results;
|
|
179
|
+
this.tests = results.filter(t => !!t.title);
|
|
165
180
|
|
|
166
181
|
return {
|
|
167
182
|
status,
|
|
@@ -212,9 +227,7 @@ class XmlReader {
|
|
|
212
227
|
const contents = fs.readFileSync(file).toString();
|
|
213
228
|
t.code = fetchSourceCode(contents, { ...t, lang: this.stats.language })
|
|
214
229
|
} catch (err) {
|
|
215
|
-
|
|
216
|
-
console.log(err)
|
|
217
|
-
}
|
|
230
|
+
debug(err)
|
|
218
231
|
}
|
|
219
232
|
});
|
|
220
233
|
}
|
|
@@ -264,7 +277,10 @@ class XmlReader {
|
|
|
264
277
|
|
|
265
278
|
async uploadArtifacts() {
|
|
266
279
|
for (const test of this.tests.filter(t => !!t.stack)) {
|
|
267
|
-
|
|
280
|
+
let files = [];
|
|
281
|
+
if (test.files.length) files = test.files.map(f => path.join(process.cwd(), f))
|
|
282
|
+
files = [...files, ...fetchFilesFromStackTrace(test.stack)];
|
|
283
|
+
debug('Uploading files', files)
|
|
268
284
|
test.artifacts = await Promise.all(files.map(f => upload.uploadFileByPath(f, this.runId)));
|
|
269
285
|
}
|
|
270
286
|
}
|
|
@@ -277,7 +293,7 @@ class XmlReader {
|
|
|
277
293
|
group_title: this.requestParams.group_title,
|
|
278
294
|
};
|
|
279
295
|
|
|
280
|
-
|
|
296
|
+
debug("Run", runParams);
|
|
281
297
|
|
|
282
298
|
return Promise.all(this.pipes.map(p => p.createRun(runParams)));
|
|
283
299
|
}
|
|
@@ -290,17 +306,17 @@ class XmlReader {
|
|
|
290
306
|
this.formatErrors();
|
|
291
307
|
this.formatTests();
|
|
292
308
|
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
}
|
|
309
|
+
debug(
|
|
310
|
+
'Uploading data',
|
|
311
|
+
{
|
|
312
|
+
...this.stats,
|
|
313
|
+
tests: this.tests,
|
|
314
|
+
})
|
|
299
315
|
|
|
300
316
|
const dataString = {
|
|
301
317
|
...this.stats,
|
|
302
318
|
api_key: this.requestParams.apiKey,
|
|
303
|
-
|
|
319
|
+
status: 'finished',
|
|
304
320
|
tests: this.tests,
|
|
305
321
|
};
|
|
306
322
|
|
|
@@ -335,9 +351,9 @@ function reduceTestCases(prev, item) {
|
|
|
335
351
|
// prepend system output
|
|
336
352
|
stack = `${testCaseItem['system-out'] || testCaseItem.output || testCaseItem.log || ''}\n\n${stack}`.trim()
|
|
337
353
|
|
|
338
|
-
let status = PASSED;
|
|
339
|
-
if ('failure' in testCaseItem || 'error' in testCaseItem) status = FAILED;
|
|
340
|
-
if ('skipped' in testCaseItem) status = SKIPPED;
|
|
354
|
+
let status = STATUS.PASSED.toString();
|
|
355
|
+
if ('failure' in testCaseItem || 'error' in testCaseItem) status = STATUS.FAILED;
|
|
356
|
+
if ('skipped' in testCaseItem) status = STATUS.SKIPPED;
|
|
341
357
|
|
|
342
358
|
prev.push({
|
|
343
359
|
create: true,
|
package/package.json
CHANGED
|
@@ -1,26 +1,33 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@testomatio/reporter",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.2-beta.1",
|
|
4
4
|
"description": "Testomatio Reporter Client",
|
|
5
5
|
"main": "./lib/reporter.js",
|
|
6
|
+
"typings": "typings/index.d.ts",
|
|
6
7
|
"repository": "git@github.com:testomatio/reporter.git",
|
|
7
8
|
"author": "Michael Bodnarchuk <davert@testomat.io>,Koushik Mohan <koushikmohan1996@gmail.com>",
|
|
8
9
|
"license": "MIT",
|
|
9
10
|
"dependencies": {
|
|
11
|
+
"@aws-sdk/client-s3": "^3.279.0",
|
|
12
|
+
"@aws-sdk/lib-storage": "^3.279.0",
|
|
10
13
|
"@octokit/rest": "^19.0.5",
|
|
11
14
|
"aws-sdk": "^2.1072.0",
|
|
12
15
|
"axios": "^0.25.0",
|
|
13
16
|
"callsite-record": "^4.1.4",
|
|
14
17
|
"chalk": "^4.1.0",
|
|
15
18
|
"commander": "^4.1.1",
|
|
19
|
+
"csv-writer": "^1.6.0",
|
|
20
|
+
"debug": "^4.3.4",
|
|
16
21
|
"dotenv": "^16.0.1",
|
|
17
22
|
"fast-xml-parser": "^4.0.8",
|
|
18
23
|
"glob": "^8.0.3",
|
|
19
24
|
"has-flag": "^5.0.1",
|
|
25
|
+
"humanize-duration": "^3.27.3",
|
|
20
26
|
"is-valid-path": "^0.1.1",
|
|
21
27
|
"json-cycle": "^1.3.0",
|
|
22
28
|
"lodash.memoize": "^4.1.2",
|
|
23
|
-
"lodash.merge": "^4.6.2"
|
|
29
|
+
"lodash.merge": "^4.6.2",
|
|
30
|
+
"uuid": "^9.0.0"
|
|
24
31
|
},
|
|
25
32
|
"files": [
|
|
26
33
|
"bin",
|
|
@@ -28,6 +35,7 @@
|
|
|
28
35
|
"testcafe"
|
|
29
36
|
],
|
|
30
37
|
"scripts": {
|
|
38
|
+
"clear-exportdir": "rm -rf export/",
|
|
31
39
|
"pretty": "prettier --write .",
|
|
32
40
|
"lint": "eslint lib",
|
|
33
41
|
"lint:fix": "eslint lib --fix",
|
|
@@ -35,15 +43,16 @@
|
|
|
35
43
|
"init": "cd ./tests/adapter/examples/cucumber && npm i",
|
|
36
44
|
"test:unit": "mocha tests",
|
|
37
45
|
"test:adapter": "mocha './tests/adapter/index.test.js'",
|
|
46
|
+
"test:pipes": "mocha './tests/pipes/*_test.js'",
|
|
38
47
|
"test:adapter:jest:example": "jest './tests/adapter/examples/jest/index.test.js' --config='./tests/adapter/examples/jest/jest.config.js'",
|
|
39
48
|
"test:adapter:mocha:example": "mocha './tests/adapter/examples/mocha/index.test.js' --config='./tests/adapter/examples/mocha/mocha.config.js'",
|
|
40
49
|
"test:adapter:jasmine:example": "./tests/adapter/examples/jasmine/passReporterOpts.sh && jasmine './tests/adapter/examples/jasmine/index.test.js' --reporter=./../../../lib/adapter/jasmine.js",
|
|
41
50
|
"test:adapter:codecept:example": "codeceptjs run --config='./tests/adapter/examples/codecept/codecept.conf.js'",
|
|
42
51
|
"test:adapter:cucumber:example": "cd ./tests/adapter/examples/cucumber && npx cucumber-js"
|
|
43
52
|
},
|
|
44
|
-
"peerDependencies": {},
|
|
45
53
|
"devDependencies": {
|
|
46
54
|
"@cucumber/cucumber": "^8.6.0",
|
|
55
|
+
"@redocly/cli": "^1.0.0-beta.125",
|
|
47
56
|
"@wdio/reporter": "^7.16.13",
|
|
48
57
|
"chai": "^4.3.6",
|
|
49
58
|
"codeceptjs": "^3.2.3",
|
package/testcafe/index.js
DELETED
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
const TestomatClient = require('@testomatio/reporter/lib/client');
|
|
2
|
-
const TRConstants = require('@testomatio/reporter/lib/constants');
|
|
3
|
-
const util = require('@testomatio/reporter/lib/util');
|
|
4
|
-
|
|
5
|
-
module.exports = () => {
|
|
6
|
-
const apiKey = process.env.TESTOMATIO;
|
|
7
|
-
let failed = false;
|
|
8
|
-
|
|
9
|
-
if (apiKey === '' || apiKey === undefined) {
|
|
10
|
-
throw new Error('Testomat.io API key cannot be empty');
|
|
11
|
-
}
|
|
12
|
-
const client = new TestomatClient({ apiKey });
|
|
13
|
-
|
|
14
|
-
return {
|
|
15
|
-
reportTaskStart(startTime, userAgents) {
|
|
16
|
-
console.log('TestCafe started with: ', userAgents);
|
|
17
|
-
client.createRun();
|
|
18
|
-
},
|
|
19
|
-
|
|
20
|
-
reportFixtureStart(name) {
|
|
21
|
-
console.log(`Suite : ${name}`);
|
|
22
|
-
},
|
|
23
|
-
|
|
24
|
-
reportTestDone(name, testRunInfo) {
|
|
25
|
-
let status = TRConstants.PASSED;
|
|
26
|
-
let message = '';
|
|
27
|
-
|
|
28
|
-
if (testRunInfo.skipped) {
|
|
29
|
-
status = TRConstants.SKIPPED;
|
|
30
|
-
}
|
|
31
|
-
if (testRunInfo.errs.length) {
|
|
32
|
-
status = TRConstants.FAILED;
|
|
33
|
-
message = this.renderErrors(testRunInfo.errs);
|
|
34
|
-
failed = true;
|
|
35
|
-
}
|
|
36
|
-
console.log(` - ${name} : ${status}`);
|
|
37
|
-
client.addTestRun(util.parseTest(name), status, {
|
|
38
|
-
error: testRunInfo.errs.length ? testRunInfo.errs[0] : null,
|
|
39
|
-
message,
|
|
40
|
-
title: name,
|
|
41
|
-
time: testRunInfo.durationMs,
|
|
42
|
-
});
|
|
43
|
-
},
|
|
44
|
-
|
|
45
|
-
renderErrors(errors) {
|
|
46
|
-
let errorMessage = '';
|
|
47
|
-
errors.forEach((error, id) => {
|
|
48
|
-
errorMessage = `${errorMessage}${this.formatError(error, `${id + 1} `)}\n`;
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
console.log(errorMessage);
|
|
52
|
-
return errorMessage.replace(util.ansiRegExp(), '');
|
|
53
|
-
},
|
|
54
|
-
|
|
55
|
-
reportTaskDone() {
|
|
56
|
-
const status = failed ? TRConstants.FAILED : TRConstants.PASSED;
|
|
57
|
-
console.log(`Status : ${status}`);
|
|
58
|
-
client.updateRunStatus(status);
|
|
59
|
-
},
|
|
60
|
-
};
|
|
61
|
-
};
|
package/testcafe/package.json
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "testcafe-reporter-testomatio",
|
|
3
|
-
"version": "0.1.1",
|
|
4
|
-
"description": "",
|
|
5
|
-
"main": "index.js",
|
|
6
|
-
"scripts": {
|
|
7
|
-
"test": "echo \"Error: no test specified\" && exit 1"
|
|
8
|
-
},
|
|
9
|
-
"author": "koushikmohan1996@gmail.com",
|
|
10
|
-
"license": "ISC",
|
|
11
|
-
"dependencies": {
|
|
12
|
-
"@testomatio/reporter": "^0.6.10"
|
|
13
|
-
}
|
|
14
|
-
}
|