@testomatio/reporter 0.8.0-beta.31 → 0.8.0-beta.32
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/lib/ArtifactStorage.js +87 -88
- package/lib/adapter/codecept.js +24 -18
- 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 +14 -13
- package/lib/adapter/playwright.js +7 -4
- package/lib/adapter/webdriver.js +2 -1
- package/lib/bin/reportXml.js +0 -1
- package/lib/bin/startTest.js +2 -2
- package/lib/client.js +40 -34
- package/lib/constants.js +7 -3
- package/lib/fileUploader.js +1 -1
- package/lib/junit-adapter/csharp.js +0 -1
- package/lib/junit-adapter/index.js +3 -2
- package/lib/pipe/csv.js +18 -6
- package/lib/pipe/github.js +11 -4
- package/lib/pipe/gitlab.js +8 -4
- package/lib/pipe/index.js +5 -3
- package/lib/pipe/testomatio.js +45 -11
- package/lib/util.js +18 -18
- package/lib/xmlReader.js +15 -14
- package/package.json +5 -2
package/lib/adapter/mocha.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
// eslint-disable-next-line global-require, import/no-extraneous-dependencies
|
|
2
|
-
const debug = require('debug')('@testomatio/reporter:adapter:mocha');
|
|
3
2
|
const Mocha = require('mocha');
|
|
3
|
+
const debug = require('debug')('@testomatio/reporter:adapter:mocha');
|
|
4
4
|
const chalk = require('chalk');
|
|
5
5
|
const TestomatClient = require('../client');
|
|
6
|
-
const
|
|
6
|
+
const { STATUS } = require('../constants');
|
|
7
7
|
const { parseTest, specificTestInfo } = require('../util');
|
|
8
8
|
const ArtifactStorage = require('../ArtifactStorage');
|
|
9
9
|
|
|
@@ -26,12 +26,12 @@ function MochaReporter(runner, opts) {
|
|
|
26
26
|
client.createRun();
|
|
27
27
|
|
|
28
28
|
const params = {
|
|
29
|
-
|
|
29
|
+
toFile: true
|
|
30
30
|
};
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
?
|
|
34
|
-
:
|
|
31
|
+
|
|
32
|
+
artifactStore = runner._workerReporter !== undefined
|
|
33
|
+
? new ArtifactStorage(params)
|
|
34
|
+
: new ArtifactStorage();
|
|
35
35
|
});
|
|
36
36
|
|
|
37
37
|
runner.on(EVENT_TEST_PASS, async(test) => {
|
|
@@ -45,9 +45,9 @@ function MochaReporter(runner, opts) {
|
|
|
45
45
|
debug(`test=${specificTest} content = `, content);
|
|
46
46
|
|
|
47
47
|
client.addTestRun(
|
|
48
|
-
|
|
49
|
-
TRConstants.PASSED,
|
|
48
|
+
STATUS.PASSED,
|
|
50
49
|
{
|
|
50
|
+
test_id: testId,
|
|
51
51
|
title: test.title,
|
|
52
52
|
time: test.duration,
|
|
53
53
|
},
|
|
@@ -59,8 +59,9 @@ function MochaReporter(runner, opts) {
|
|
|
59
59
|
skipped += 1;
|
|
60
60
|
console.log('skip: %s', test.fullTitle());
|
|
61
61
|
const testId = parseTest(test.title);
|
|
62
|
-
client.addTestRun(
|
|
62
|
+
client.addTestRun(STATUS.SKIPPED, {
|
|
63
63
|
title: test.title,
|
|
64
|
+
test_id: testId,
|
|
64
65
|
time: test.duration,
|
|
65
66
|
});
|
|
66
67
|
});
|
|
@@ -76,9 +77,9 @@ function MochaReporter(runner, opts) {
|
|
|
76
77
|
debug(`fail test=${specificTest} content = `, content);
|
|
77
78
|
|
|
78
79
|
client.addTestRun(
|
|
79
|
-
|
|
80
|
-
TRConstants.FAILED, {
|
|
80
|
+
STATUS.FAILED, {
|
|
81
81
|
error: err,
|
|
82
|
+
test_id: testId,
|
|
82
83
|
title: test.title,
|
|
83
84
|
time: test.duration,
|
|
84
85
|
},
|
|
@@ -87,7 +88,7 @@ function MochaReporter(runner, opts) {
|
|
|
87
88
|
});
|
|
88
89
|
|
|
89
90
|
runner.on(EVENT_RUN_END, () => {
|
|
90
|
-
const status = failures === 0 ?
|
|
91
|
+
const status = failures === 0 ? STATUS.PASSED : STATUS.FAILED;
|
|
91
92
|
console.log(chalk.bold(status), `${passes} passed, ${failures} failed, ${skipped} skipped`);
|
|
92
93
|
client.updateRunStatus(status);
|
|
93
94
|
|
|
@@ -3,11 +3,12 @@ const crypto = require('crypto');
|
|
|
3
3
|
const os = require('os');
|
|
4
4
|
const path = require('path');
|
|
5
5
|
const fs = require('fs');
|
|
6
|
-
const Status = require('../constants');
|
|
6
|
+
const { APP_PREFIX, STATUS: Status } = require('../constants');
|
|
7
7
|
const TestomatioClient = require('../client');
|
|
8
8
|
const upload = require('../fileUploader');
|
|
9
9
|
const { parseTest } = require('../util');
|
|
10
10
|
|
|
11
|
+
|
|
11
12
|
class TestomatioReporter {
|
|
12
13
|
constructor(config = {}) {
|
|
13
14
|
this.client = new TestomatioClient({ apiKey: config?.apiKey });
|
|
@@ -56,8 +57,9 @@ class TestomatioReporter {
|
|
|
56
57
|
files.push({ path: fileName, type: attachment.contentType });
|
|
57
58
|
}
|
|
58
59
|
|
|
59
|
-
this.client.addTestRun(
|
|
60
|
+
this.client.addTestRun(checkStatus(result.status), {
|
|
60
61
|
error,
|
|
62
|
+
test_id: testId,
|
|
61
63
|
suite_title,
|
|
62
64
|
title,
|
|
63
65
|
files,
|
|
@@ -70,14 +72,15 @@ class TestomatioReporter {
|
|
|
70
72
|
if (!this.client) return;
|
|
71
73
|
|
|
72
74
|
if (this.videos.length && upload.isArtifactsEnabled()) {
|
|
73
|
-
console.log(
|
|
75
|
+
console.log(APP_PREFIX, `🎞️ Uploading ${this.videos.length} videos...`);
|
|
74
76
|
|
|
75
77
|
const promises = [];
|
|
76
78
|
for (const video of this.videos) {
|
|
77
79
|
const { testId, title, attachment, suite_title } = video;
|
|
78
80
|
const file = { path: attachment.path, title, type: attachment.contentType };
|
|
79
81
|
promises.push(
|
|
80
|
-
this.client.addTestRun(
|
|
82
|
+
this.client.addTestRun(undefined, {
|
|
83
|
+
test_id: testId,
|
|
81
84
|
title,
|
|
82
85
|
suite_title,
|
|
83
86
|
files: [file],
|
package/lib/adapter/webdriver.js
CHANGED
|
@@ -43,9 +43,10 @@ class WebdriverReporter extends WDIOReporter {
|
|
|
43
43
|
.filter(el => el.endpoint === screenshotEndpoint && el.result && el.result.value)
|
|
44
44
|
.map(el => Buffer.from(el.result.value, 'base64'));
|
|
45
45
|
|
|
46
|
-
await this.client.addTestRun(
|
|
46
|
+
await this.client.addTestRun(state, {
|
|
47
47
|
error,
|
|
48
48
|
title,
|
|
49
|
+
test_id: testId,
|
|
49
50
|
time: duration,
|
|
50
51
|
filesBuffers: screenshotsBuffers,
|
|
51
52
|
});
|
package/lib/bin/reportXml.js
CHANGED
package/lib/bin/startTest.js
CHANGED
|
@@ -3,7 +3,7 @@ const { spawn } = require('child_process');
|
|
|
3
3
|
const program = require('commander');
|
|
4
4
|
const chalk = require('chalk');
|
|
5
5
|
const TestomatClient = require('../client');
|
|
6
|
-
const { APP_PREFIX,
|
|
6
|
+
const { APP_PREFIX, STATUS } = require('../constants');
|
|
7
7
|
const { version } = require('../../package.json');
|
|
8
8
|
|
|
9
9
|
console.log(chalk.cyan.bold(` 🤩 Testomat.io Reporter v${version}`));
|
|
@@ -42,7 +42,7 @@ program
|
|
|
42
42
|
|
|
43
43
|
const client = new TestomatClient({ apiKey });
|
|
44
44
|
|
|
45
|
-
client.updateRunStatus(FINISHED, true).then(() => {
|
|
45
|
+
client.updateRunStatus(STATUS.FINISHED, true).then(() => {
|
|
46
46
|
console.log(chalk.yellow(`Run ${process.env.TESTOMATIO_RUN} was finished`));
|
|
47
47
|
process.exit(0);
|
|
48
48
|
});
|
package/lib/client.js
CHANGED
|
@@ -3,25 +3,26 @@ const createCallsiteRecord = require('callsite-record');
|
|
|
3
3
|
const { sep, join } = require('path');
|
|
4
4
|
const fs = require('fs');
|
|
5
5
|
const chalk = require('chalk');
|
|
6
|
+
const { randomUUID } = require('crypto');
|
|
6
7
|
const upload = require('./fileUploader');
|
|
7
|
-
const {
|
|
8
|
+
const { APP_PREFIX } = require('./constants');
|
|
8
9
|
const pipesFactory = require('./pipe');
|
|
9
10
|
|
|
10
|
-
|
|
11
|
+
/**
|
|
12
|
+
* @typedef {import('../types').TestData} TestData
|
|
13
|
+
* @typedef {import('../types').RunStatus} RunStatus
|
|
14
|
+
*/
|
|
11
15
|
|
|
12
|
-
class
|
|
16
|
+
class Client {
|
|
13
17
|
/**
|
|
14
18
|
* Create a Testomat client instance
|
|
15
19
|
*
|
|
16
20
|
* @param {*} params
|
|
17
21
|
*/
|
|
18
22
|
constructor(params = {}) {
|
|
19
|
-
this.title = params.title || process.env.TESTOMATIO_TITLE;
|
|
20
|
-
this.createNewTests = params.createNewTests ?? !!process.env.TESTOMATIO_CREATE;
|
|
21
|
-
this.proceed = process.env.TESTOMATIO_PROCEED;
|
|
22
|
-
this.env = TESTOMATIO_ENV;
|
|
23
23
|
this.parallel = params.parallel;
|
|
24
24
|
const store = {};
|
|
25
|
+
this.uuid = randomUUID();
|
|
25
26
|
this.pipes = pipesFactory(params, store);
|
|
26
27
|
this.queue = Promise.resolve();
|
|
27
28
|
this.totalUploaded = 0;
|
|
@@ -32,11 +33,11 @@ class TestomatClient {
|
|
|
32
33
|
/**
|
|
33
34
|
* Used to create a new Test run
|
|
34
35
|
*
|
|
35
|
-
* @returns {Promise} - resolves to Run id which should be used to update / add test
|
|
36
|
+
* @returns {Promise<void>} - resolves to Run id which should be used to update / add test
|
|
36
37
|
*/
|
|
37
38
|
createRun() {
|
|
38
39
|
// all pipes disabled, skipping
|
|
39
|
-
if (!this.pipes.filter(p => p.isEnabled).length) return;
|
|
40
|
+
if (!this.pipes.filter(p => p.isEnabled).length) return Promise.resolve();
|
|
40
41
|
|
|
41
42
|
const runParams = {
|
|
42
43
|
title: this.title,
|
|
@@ -48,22 +49,31 @@ class TestomatClient {
|
|
|
48
49
|
|
|
49
50
|
this.queue = this.queue
|
|
50
51
|
.then(() => Promise.all(this.pipes.map(p => p.createRun(runParams))))
|
|
51
|
-
.catch(err => console.log(APP_PREFIX, err))
|
|
52
|
+
.catch(err => console.log(APP_PREFIX, err))
|
|
53
|
+
.then(() => undefined); // fixes return type
|
|
52
54
|
debug('Run', this.queue);
|
|
53
55
|
return this.queue;
|
|
54
56
|
}
|
|
55
57
|
|
|
56
58
|
/**
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
* @
|
|
59
|
+
* Updates test status and its data
|
|
60
|
+
*
|
|
61
|
+
* @param {string|undefined} status
|
|
62
|
+
* @param {TestData} [testData]
|
|
63
|
+
* @param {string[]} [storeArtifacts]
|
|
64
|
+
* @returns {Promise<void>}
|
|
60
65
|
*/
|
|
61
|
-
async addTestRun(
|
|
66
|
+
async addTestRun(status, testData, storeArtifacts = []) {
|
|
62
67
|
// all pipes disabled, skipping
|
|
63
68
|
if (!this.pipes.filter(p => p.isEnabled).length) return;
|
|
64
69
|
|
|
70
|
+
if (!testData) testData = {
|
|
71
|
+
title: 'Unknown test',
|
|
72
|
+
suite_title: 'Unknown suite',
|
|
73
|
+
}
|
|
74
|
+
|
|
65
75
|
const {
|
|
66
|
-
error =
|
|
76
|
+
error = null,
|
|
67
77
|
time = '',
|
|
68
78
|
example = null,
|
|
69
79
|
files = [],
|
|
@@ -79,20 +89,18 @@ class TestomatClient {
|
|
|
79
89
|
|
|
80
90
|
const uploadedFiles = [];
|
|
81
91
|
|
|
82
|
-
if (testId) testData.test_id = testId;
|
|
83
|
-
|
|
84
92
|
let stack = '';
|
|
85
93
|
|
|
86
94
|
if (error) {
|
|
87
|
-
stack = this.formatError(error);
|
|
88
|
-
message = error
|
|
95
|
+
stack = this.formatError(error) || '';
|
|
96
|
+
message = error?.message;
|
|
89
97
|
}
|
|
90
98
|
if (steps) {
|
|
91
99
|
stack = this.formatSteps(stack, steps);
|
|
92
100
|
}
|
|
93
101
|
|
|
94
102
|
if (Array.isArray(storeArtifacts) && storeArtifacts.length) {
|
|
95
|
-
debug("CLIENT
|
|
103
|
+
debug("CLIENT storeArtifact", storeArtifacts);
|
|
96
104
|
files.push(...storeArtifacts);
|
|
97
105
|
}
|
|
98
106
|
|
|
@@ -103,12 +111,12 @@ class TestomatClient {
|
|
|
103
111
|
}
|
|
104
112
|
|
|
105
113
|
for (const file of files) {
|
|
106
|
-
uploadedFiles.push(upload.uploadFileByPath(file, this.
|
|
114
|
+
uploadedFiles.push(upload.uploadFileByPath(file, this.uuid));
|
|
107
115
|
}
|
|
108
116
|
|
|
109
117
|
for (const [idx, buffer] of filesBuffers.entries()) {
|
|
110
118
|
const fileName = `${idx + 1}-${title.replace(/\s+/g, '-')}`;
|
|
111
|
-
uploadedFiles.push(upload.uploadFileAsBuffer(buffer, fileName, this.
|
|
119
|
+
uploadedFiles.push(upload.uploadFileAsBuffer(buffer, fileName, this.uuid));
|
|
112
120
|
}
|
|
113
121
|
|
|
114
122
|
const artifacts = await Promise.all(uploadedFiles);
|
|
@@ -135,25 +143,23 @@ class TestomatClient {
|
|
|
135
143
|
|
|
136
144
|
this.queue = this.queue
|
|
137
145
|
.then(() => Promise.all(this.pipes.map(p => p.addTest(data))))
|
|
138
|
-
.catch(err => console.log(APP_PREFIX, err))
|
|
146
|
+
.catch(err => console.log(APP_PREFIX, err))
|
|
147
|
+
.then(() => undefined);
|
|
139
148
|
return this.queue;
|
|
140
149
|
}
|
|
141
150
|
|
|
142
151
|
/**
|
|
143
|
-
* Update run status
|
|
144
152
|
*
|
|
145
|
-
*
|
|
153
|
+
* Updates the status of the current test run and finishes the run.
|
|
154
|
+
* @param {RunStatus} status - The status of the current test run. Must be one of "passed", "failed", or "finished"
|
|
155
|
+
* @param {boolean} [isParallel] - Whether the current test run was executed in parallel with other tests.
|
|
156
|
+
* @returns {Promise<void>} - A Promise that resolves when finishes the run.
|
|
146
157
|
*/
|
|
147
|
-
updateRunStatus(status, isParallel) {
|
|
158
|
+
updateRunStatus(status, isParallel = false) {
|
|
148
159
|
// all pipes disabled, skipping
|
|
149
|
-
if (!this.pipes.filter(p => p.isEnabled).length) return;
|
|
160
|
+
if (!this.pipes.filter(p => p.isEnabled).length) return Promise.resolve();
|
|
150
161
|
|
|
151
|
-
|
|
152
|
-
if (status === FINISHED) statusEvent = 'finish';
|
|
153
|
-
if (status === PASSED) statusEvent = 'pass';
|
|
154
|
-
if (status === FAILED) statusEvent = 'fail';
|
|
155
|
-
if (isParallel) statusEvent += '_parallel';
|
|
156
|
-
const runParams = { status, statusEvent };
|
|
162
|
+
const runParams = { status, parallel: isParallel };
|
|
157
163
|
|
|
158
164
|
this.queue = this.queue
|
|
159
165
|
.then(() => Promise.all(this.pipes.map(p => p.finishRun(runParams))))
|
|
@@ -211,4 +217,4 @@ class TestomatClient {
|
|
|
211
217
|
}
|
|
212
218
|
}
|
|
213
219
|
|
|
214
|
-
module.exports =
|
|
220
|
+
module.exports = Client;
|
package/lib/constants.js
CHANGED
|
@@ -11,12 +11,16 @@ const CSV_HEADERS = [
|
|
|
11
11
|
{ id: 'stack', title: 'Stack' },
|
|
12
12
|
];
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
const STATUS = {
|
|
15
15
|
PASSED: 'passed',
|
|
16
16
|
FAILED: 'failed',
|
|
17
17
|
SKIPPED: 'skipped',
|
|
18
18
|
FINISHED: 'finished',
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
module.exports = {
|
|
19
22
|
APP_PREFIX,
|
|
23
|
+
TESTOMAT_ARTIFACT_SUFFIX,
|
|
20
24
|
CSV_HEADERS,
|
|
21
|
-
|
|
22
|
-
}
|
|
25
|
+
STATUS,
|
|
26
|
+
}
|
package/lib/fileUploader.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
const debug = require('debug')('@testomatio/reporter:file-uploader');
|
|
2
2
|
const { S3 } = require('@aws-sdk/client-s3');
|
|
3
|
-
const { Upload } = require(
|
|
3
|
+
const { Upload } = require('@aws-sdk/lib-storage');
|
|
4
4
|
const fs = require('fs');
|
|
5
5
|
const path = require('path');
|
|
6
6
|
const chalk = require('chalk');
|
|
@@ -5,8 +5,7 @@ const PythonAdapter = require('./python');
|
|
|
5
5
|
const RubyAdapter = require('./ruby');
|
|
6
6
|
const CSharpAdapter = require('./csharp');
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
module.exports = function (lang, opts) {
|
|
8
|
+
function AdapterFactory(lang, opts) {
|
|
10
9
|
if (!lang) return new Adapter(opts);
|
|
11
10
|
|
|
12
11
|
if (lang === 'java') {
|
|
@@ -25,3 +24,5 @@ module.exports = function (lang, opts) {
|
|
|
25
24
|
return new CSharpAdapter(opts);
|
|
26
25
|
}
|
|
27
26
|
}
|
|
27
|
+
|
|
28
|
+
module.exports = AdapterFactory;
|
package/lib/pipe/csv.js
CHANGED
|
@@ -7,9 +7,16 @@ const merge = require('lodash.merge');
|
|
|
7
7
|
const { isSameTest, getCurrentDateTime } = require('../util');
|
|
8
8
|
const { CSV_HEADERS } = require('../constants');
|
|
9
9
|
|
|
10
|
+
/**
|
|
11
|
+
* @typedef {import('../../types').Pipe} Pipe
|
|
12
|
+
* @typedef {import('../../types').TestData} TestData
|
|
13
|
+
* @class CsvPipe
|
|
14
|
+
* @implements {Pipe}
|
|
15
|
+
*/
|
|
10
16
|
class CsvPipe {
|
|
11
|
-
|
|
12
|
-
|
|
17
|
+
|
|
18
|
+
constructor(params, store) {
|
|
19
|
+
this.store = store || {};
|
|
13
20
|
this.title = params.title || process.env.TESTOMATIO_TITLE;
|
|
14
21
|
this.isEnabled = true;
|
|
15
22
|
this.results = [];
|
|
@@ -59,7 +66,12 @@ class CsvPipe {
|
|
|
59
66
|
// First, we check whether the export directory exists: if yes - OK, no - create it.
|
|
60
67
|
this.checkExportDir();
|
|
61
68
|
|
|
62
|
-
|
|
69
|
+
if (!this.outputFile) {
|
|
70
|
+
console.log(this, chalk.yellow(`CSV file is not set, ignoring`));
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
console.log(this, chalk.yellow(`The test results will be added to the csv. It will take some time...`));
|
|
63
75
|
// Create csv writer object
|
|
64
76
|
const writer = csvWriter.createObjectCsvWriter({
|
|
65
77
|
path: this.outputFile,
|
|
@@ -68,7 +80,7 @@ class CsvPipe {
|
|
|
68
80
|
// Save csv file based on the current data
|
|
69
81
|
try {
|
|
70
82
|
await writer.writeRecords(data);
|
|
71
|
-
console.log(chalk.green(`Recording completed! You can check the result in file = ${this.outputFile}`));
|
|
83
|
+
console.log(this, chalk.green(`Recording completed! You can check the result in file = ${this.outputFile}`));
|
|
72
84
|
} catch (e) {
|
|
73
85
|
console.log('Unknown error: ', e);
|
|
74
86
|
}
|
|
@@ -100,8 +112,8 @@ class CsvPipe {
|
|
|
100
112
|
}
|
|
101
113
|
|
|
102
114
|
/**
|
|
103
|
-
*
|
|
104
|
-
* @
|
|
115
|
+
* @param {{ tests?: TestData[] }} runParams
|
|
116
|
+
* @returns {Promise<void>}
|
|
105
117
|
*/
|
|
106
118
|
async finishRun(runParams) {
|
|
107
119
|
if (!this.isEnabled) return;
|
package/lib/pipe/github.js
CHANGED
|
@@ -7,6 +7,12 @@ const { Octokit } = require('@octokit/rest');
|
|
|
7
7
|
const { APP_PREFIX } = require('../constants');
|
|
8
8
|
const { ansiRegExp, isSameTest } = require('../util');
|
|
9
9
|
|
|
10
|
+
/**
|
|
11
|
+
* @typedef {import('../../types').Pipe} Pipe
|
|
12
|
+
* @typedef {import('../../types').TestData} TestData
|
|
13
|
+
* @class GitHubPipe
|
|
14
|
+
* @implements {Pipe}
|
|
15
|
+
*/
|
|
10
16
|
class GitHubPipe {
|
|
11
17
|
constructor(params, store = {}) {
|
|
12
18
|
this.isEnabled = false;
|
|
@@ -23,7 +29,8 @@ class GitHubPipe {
|
|
|
23
29
|
this.isEnabled = true;
|
|
24
30
|
const matchedIssue = this.ref.match(/refs\/pull\/(\d+)\/merge/);
|
|
25
31
|
if (!matchedIssue) return;
|
|
26
|
-
this.issue = matchedIssue[1];
|
|
32
|
+
this.issue = parseInt(matchedIssue[1], 10);
|
|
33
|
+
|
|
27
34
|
this.start = new Date();
|
|
28
35
|
|
|
29
36
|
debug('GitHub Pipe: Enabled');
|
|
@@ -31,8 +38,6 @@ class GitHubPipe {
|
|
|
31
38
|
|
|
32
39
|
async createRun() {}
|
|
33
40
|
|
|
34
|
-
updateRun() {}
|
|
35
|
-
|
|
36
41
|
addTest(test) {
|
|
37
42
|
debug('Adding test:', test);
|
|
38
43
|
if (!this.isEnabled) return;
|
|
@@ -49,6 +54,7 @@ class GitHubPipe {
|
|
|
49
54
|
|
|
50
55
|
async finishRun(runParams) {
|
|
51
56
|
if (!this.isEnabled) return;
|
|
57
|
+
if (!this.issue) return;
|
|
52
58
|
|
|
53
59
|
if (runParams.tests) runParams.tests.forEach(t => this.addTest(t));
|
|
54
60
|
|
|
@@ -56,7 +62,7 @@ class GitHubPipe {
|
|
|
56
62
|
auth: this.token,
|
|
57
63
|
});
|
|
58
64
|
|
|
59
|
-
const [owner, repo] = this.repo.split('/');
|
|
65
|
+
const [owner, repo] = (this.repo || '').split('/');
|
|
60
66
|
if (!(owner || repo)) return;
|
|
61
67
|
|
|
62
68
|
// ... create a comment on GitHub
|
|
@@ -148,6 +154,7 @@ class GitHubPipe {
|
|
|
148
154
|
.join('\n');
|
|
149
155
|
body += '\n</details>';
|
|
150
156
|
}
|
|
157
|
+
|
|
151
158
|
|
|
152
159
|
await deletePreviousReport(this.octokit, owner, repo, this.issue, this.hiddenCommentData);
|
|
153
160
|
|
package/lib/pipe/gitlab.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const debug = require('debug')('@testomatio/reporter:pipe:gitlab');
|
|
2
|
-
const axios = require('axios');
|
|
2
|
+
const { default: axios } = require('axios');
|
|
3
3
|
const chalk = require('chalk');
|
|
4
4
|
const humanizeDuration = require('humanize-duration');
|
|
5
5
|
const merge = require('lodash.merge');
|
|
@@ -10,6 +10,11 @@ const { ansiRegExp, isSameTest } = require('../util');
|
|
|
10
10
|
//! GITLAB_PAT environment variable is required for this functionality to work
|
|
11
11
|
//! and your pipeline trigger should be merge_request
|
|
12
12
|
|
|
13
|
+
/**
|
|
14
|
+
* @class GitLabPipe
|
|
15
|
+
* @typedef {import('../../types').Pipe} Pipe
|
|
16
|
+
* @typedef {import('../../types').TestData} TestData
|
|
17
|
+
*/
|
|
13
18
|
class GitLabPipe {
|
|
14
19
|
constructor(params, store = {}) {
|
|
15
20
|
this.isEnabled = false;
|
|
@@ -19,7 +24,6 @@ class GitLabPipe {
|
|
|
19
24
|
// GitLab PAT looks like glpat-nKGdja3jsG4850sGksh7
|
|
20
25
|
this.token = params.GITLAB_PAT || this.ENV.GITLAB_PAT;
|
|
21
26
|
this.hiddenCommentData = `<!--- testomat.io report ${process.env.CI_JOB_NAME || ''} -->`;
|
|
22
|
-
this.axios = axios.create();
|
|
23
27
|
|
|
24
28
|
debug(
|
|
25
29
|
chalk.yellow('GitLab Pipe:'),
|
|
@@ -142,13 +146,13 @@ class GitLabPipe {
|
|
|
142
146
|
const commentsRequestURL = `https://gitlab.com/api/v4/projects/${this.ENV.CI_PROJECT_ID}/merge_requests/${this.ENV.CI_MERGE_REQUEST_IID}/notes`;
|
|
143
147
|
|
|
144
148
|
// delete previous report
|
|
145
|
-
await deletePreviousReport(
|
|
149
|
+
await deletePreviousReport(axios, commentsRequestURL, this.hiddenCommentData, this.token);
|
|
146
150
|
|
|
147
151
|
// add current report
|
|
148
152
|
debug(`Adding comment via url: ${commentsRequestURL}`);
|
|
149
153
|
|
|
150
154
|
try {
|
|
151
|
-
const addCommentResponse = await
|
|
155
|
+
const addCommentResponse = await axios.post(`${commentsRequestURL}?access_token=${this.token}`, { body });
|
|
152
156
|
|
|
153
157
|
const commentID = addCommentResponse.data.id;
|
|
154
158
|
// eslint-disable-next-line max-len
|
package/lib/pipe/index.js
CHANGED
|
@@ -7,7 +7,7 @@ const GitHubPipe = require('./github');
|
|
|
7
7
|
const GitLabPipe = require('./gitlab');
|
|
8
8
|
const CsvPipe = require('./csv');
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
function PipeFactory(params, opts) {
|
|
11
11
|
const extraPipes = [];
|
|
12
12
|
|
|
13
13
|
// Add extra pipes into package.json file:
|
|
@@ -17,7 +17,7 @@ module.exports = function (params, opts) {
|
|
|
17
17
|
|
|
18
18
|
const packageJsonFile = path.join(process.cwd(), 'package.json');
|
|
19
19
|
if (fs.existsSync(packageJsonFile)) {
|
|
20
|
-
const packageJson = fs.readFileSync(packageJsonFile);
|
|
20
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonFile).toString());
|
|
21
21
|
const pipeDefs = packageJson?.testomatio?.pipes || [];
|
|
22
22
|
|
|
23
23
|
for (const pipeDef of pipeDefs) {
|
|
@@ -58,4 +58,6 @@ module.exports = function (params, opts) {
|
|
|
58
58
|
);
|
|
59
59
|
|
|
60
60
|
return pipes;
|
|
61
|
-
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
module.exports = PipeFactory;
|
package/lib/pipe/testomatio.js
CHANGED
|
@@ -2,18 +2,22 @@ const debug = require('debug')('@testomatio/reporter:pipe:testomatio');
|
|
|
2
2
|
const chalk = require('chalk');
|
|
3
3
|
const axios = require('axios');
|
|
4
4
|
const JsonCycle = require('json-cycle');
|
|
5
|
-
|
|
6
|
-
const { TESTOMATIO_RUNGROUP_TITLE, TESTOMATIO_RUN } = process.env;
|
|
7
|
-
const { APP_PREFIX } = require('../constants');
|
|
5
|
+
const { APP_PREFIX, STATUS } = require('../constants');
|
|
8
6
|
const { isValidUrl } = require('../util');
|
|
9
7
|
|
|
10
|
-
|
|
8
|
+
const { TESTOMATIO_RUN } = process.env;
|
|
11
9
|
if (TESTOMATIO_RUN) {
|
|
12
10
|
process.env.runId = TESTOMATIO_RUN;
|
|
13
11
|
}
|
|
14
12
|
|
|
13
|
+
/**
|
|
14
|
+
* @typedef {import('../../types').Pipe} Pipe
|
|
15
|
+
* @typedef {import('../../types').TestData} TestData
|
|
16
|
+
* @class TestomatioPipe
|
|
17
|
+
* @implements {Pipe}
|
|
18
|
+
*/
|
|
15
19
|
class TestomatioPipe {
|
|
16
|
-
constructor(params, store
|
|
20
|
+
constructor(params, store) {
|
|
17
21
|
this.isEnabled = false;
|
|
18
22
|
this.url = params.testomatioUrl || process.env.TESTOMATIO_URL || 'https://app.testomat.io';
|
|
19
23
|
this.apiKey = params.apiKey || process.env.TESTOMATIO;
|
|
@@ -22,10 +26,13 @@ class TestomatioPipe {
|
|
|
22
26
|
return;
|
|
23
27
|
}
|
|
24
28
|
debug('Testomatio Pipe: Enabled');
|
|
25
|
-
this.store = store;
|
|
29
|
+
this.store = store || {};
|
|
26
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;
|
|
27
33
|
this.axios = axios.create();
|
|
28
34
|
this.isEnabled = true;
|
|
35
|
+
// do not finish this run (for parallel testing)
|
|
29
36
|
this.proceed = process.env.TESTOMATIO_PROCEED;
|
|
30
37
|
this.runId = params.runId || process.env.runId;
|
|
31
38
|
this.createNewTests = !!process.env.TESTOMATIO_CREATE;
|
|
@@ -36,11 +43,20 @@ class TestomatioPipe {
|
|
|
36
43
|
}
|
|
37
44
|
}
|
|
38
45
|
|
|
39
|
-
|
|
46
|
+
/**
|
|
47
|
+
* @returns Promise<void>
|
|
48
|
+
*/
|
|
49
|
+
async createRun() {
|
|
40
50
|
if (!this.isEnabled) return;
|
|
41
51
|
|
|
42
|
-
runParams
|
|
43
|
-
|
|
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
|
+
);
|
|
44
60
|
|
|
45
61
|
if (this.runId) {
|
|
46
62
|
return this.axios.put(`${this.url.trim()}/api/reporter/${this.runId}`, runParams);
|
|
@@ -65,6 +81,11 @@ class TestomatioPipe {
|
|
|
65
81
|
}
|
|
66
82
|
}
|
|
67
83
|
|
|
84
|
+
/**
|
|
85
|
+
*
|
|
86
|
+
* @param testData data
|
|
87
|
+
* @returns
|
|
88
|
+
*/
|
|
68
89
|
addTest(data) {
|
|
69
90
|
if (!this.isEnabled) return;
|
|
70
91
|
if (!this.runId) return;
|
|
@@ -98,14 +119,27 @@ class TestomatioPipe {
|
|
|
98
119
|
});
|
|
99
120
|
}
|
|
100
121
|
|
|
122
|
+
/**
|
|
123
|
+
* @param {import('../../types').RunData} params
|
|
124
|
+
* @returns
|
|
125
|
+
*/
|
|
101
126
|
async finishRun(params) {
|
|
102
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
|
+
|
|
103
138
|
try {
|
|
104
139
|
if (this.runId && !this.proceed) {
|
|
105
140
|
await this.axios.put(`${this.url}/api/reporter/${this.runId}`, {
|
|
106
141
|
api_key: this.apiKey,
|
|
107
|
-
status_event
|
|
108
|
-
status: params.status,
|
|
142
|
+
status_event,
|
|
109
143
|
tests: params.tests,
|
|
110
144
|
});
|
|
111
145
|
if (this.runUrl) {
|