@testomatio/reporter 0.8.0-beta.30 → 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/README.md +16 -9
- package/lib/ArtifactStorage.js +131 -0
- 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 +53 -18
- 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 +61 -42
- package/lib/constants.js +9 -3
- package/lib/fileUploader.js +8 -4
- package/lib/junit-adapter/csharp.js +0 -1
- package/lib/junit-adapter/index.js +3 -2
- package/lib/pipe/csv.js +20 -6
- package/lib/pipe/github.js +19 -12
- package/lib/pipe/gitlab.js +24 -24
- package/lib/pipe/index.js +5 -3
- package/lib/pipe/testomatio.js +67 -42
- package/lib/reporter.js +2 -0
- package/lib/util.js +31 -13
- package/lib/xmlReader.js +19 -15
- package/package.json +8 -4
package/lib/adapter/mocha.js
CHANGED
|
@@ -1,63 +1,98 @@
|
|
|
1
1
|
// eslint-disable-next-line global-require, import/no-extraneous-dependencies
|
|
2
2
|
const Mocha = require('mocha');
|
|
3
|
+
const debug = require('debug')('@testomatio/reporter:adapter:mocha');
|
|
3
4
|
const chalk = require('chalk');
|
|
4
5
|
const TestomatClient = require('../client');
|
|
5
|
-
const
|
|
6
|
-
const { parseTest } = require('../util');
|
|
6
|
+
const { STATUS } = require('../constants');
|
|
7
|
+
const { parseTest, specificTestInfo } = require('../util');
|
|
8
|
+
const ArtifactStorage = require('../ArtifactStorage');
|
|
7
9
|
|
|
8
10
|
const { EVENT_RUN_BEGIN, EVENT_RUN_END, EVENT_TEST_FAIL, EVENT_TEST_PASS, EVENT_TEST_PENDING } = Mocha.Runner.constants;
|
|
9
11
|
|
|
10
12
|
function MochaReporter(runner, opts) {
|
|
11
13
|
Mocha.reporters.Base.call(this, runner);
|
|
12
|
-
let passes = 0; let failures = 0; let skipped = 0;
|
|
14
|
+
let passes = 0; let failures = 0; let skipped = 0;
|
|
15
|
+
let artifactStore;
|
|
13
16
|
|
|
14
17
|
const apiKey = opts?.reporterOptions?.apiKey || process.env.TESTOMATIO;
|
|
15
18
|
|
|
16
19
|
if (!apiKey) {
|
|
17
|
-
|
|
20
|
+
debug('TESTOMATIO key is empty, ignoring reports');
|
|
18
21
|
return;
|
|
19
22
|
}
|
|
20
23
|
const client = new TestomatClient({ apiKey });
|
|
21
24
|
|
|
22
25
|
runner.on(EVENT_RUN_BEGIN, () => {
|
|
23
26
|
client.createRun();
|
|
27
|
+
|
|
28
|
+
const params = {
|
|
29
|
+
toFile: true
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
artifactStore = runner._workerReporter !== undefined
|
|
33
|
+
? new ArtifactStorage(params)
|
|
34
|
+
: new ArtifactStorage();
|
|
24
35
|
});
|
|
25
36
|
|
|
26
|
-
runner.on(EVENT_TEST_PASS, test => {
|
|
37
|
+
runner.on(EVENT_TEST_PASS, async(test) => {
|
|
27
38
|
passes += 1;
|
|
28
39
|
console.log(chalk.bold.green('✔'), test.fullTitle());
|
|
29
40
|
const testId = parseTest(test.title);
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
41
|
+
|
|
42
|
+
const specificTest = specificTestInfo(test);
|
|
43
|
+
const content = await artifactStore.artifactByTestName(specificTest);
|
|
44
|
+
|
|
45
|
+
debug(`test=${specificTest} content = `, content);
|
|
46
|
+
|
|
47
|
+
client.addTestRun(
|
|
48
|
+
STATUS.PASSED,
|
|
49
|
+
{
|
|
50
|
+
test_id: testId,
|
|
51
|
+
title: test.title,
|
|
52
|
+
time: test.duration,
|
|
53
|
+
},
|
|
54
|
+
content
|
|
55
|
+
);
|
|
34
56
|
});
|
|
35
57
|
|
|
36
58
|
runner.on(EVENT_TEST_PENDING, test => {
|
|
37
59
|
skipped += 1;
|
|
38
60
|
console.log('skip: %s', test.fullTitle());
|
|
39
61
|
const testId = parseTest(test.title);
|
|
40
|
-
client.addTestRun(
|
|
62
|
+
client.addTestRun(STATUS.SKIPPED, {
|
|
41
63
|
title: test.title,
|
|
64
|
+
test_id: testId,
|
|
42
65
|
time: test.duration,
|
|
43
66
|
});
|
|
44
67
|
});
|
|
45
68
|
|
|
46
|
-
runner.on(EVENT_TEST_FAIL, (test, err) => {
|
|
69
|
+
runner.on(EVENT_TEST_FAIL, async(test, err) => {
|
|
47
70
|
failures += 1;
|
|
48
71
|
console.log(chalk.bold.red('✖'), test.fullTitle(), chalk.gray(err.message));
|
|
49
72
|
const testId = parseTest(test.title);
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
});
|
|
73
|
+
|
|
74
|
+
const specificTest = specificTestInfo(test);
|
|
75
|
+
const content = await artifactStore.artifactByTestName(specificTest);
|
|
76
|
+
|
|
77
|
+
debug(`fail test=${specificTest} content = `, content);
|
|
78
|
+
|
|
79
|
+
client.addTestRun(
|
|
80
|
+
STATUS.FAILED, {
|
|
81
|
+
error: err,
|
|
82
|
+
test_id: testId,
|
|
83
|
+
title: test.title,
|
|
84
|
+
time: test.duration,
|
|
85
|
+
},
|
|
86
|
+
content
|
|
87
|
+
);
|
|
55
88
|
});
|
|
56
89
|
|
|
57
90
|
runner.on(EVENT_RUN_END, () => {
|
|
58
|
-
const status = failures === 0 ?
|
|
59
|
-
console.log(chalk.bold(status), `${passes} passed, ${failures} failed`);
|
|
91
|
+
const status = failures === 0 ? STATUS.PASSED : STATUS.FAILED;
|
|
92
|
+
console.log(chalk.bold(status), `${passes} passed, ${failures} failed, ${skipped} skipped`);
|
|
60
93
|
client.updateRunStatus(status);
|
|
94
|
+
|
|
95
|
+
artifactStore.cleanup();
|
|
61
96
|
});
|
|
62
97
|
}
|
|
63
98
|
|
|
@@ -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
|
@@ -1,26 +1,28 @@
|
|
|
1
|
+
const debug = require('debug')('@testomatio/reporter:client');
|
|
1
2
|
const createCallsiteRecord = require('callsite-record');
|
|
2
3
|
const { sep, join } = require('path');
|
|
3
4
|
const fs = require('fs');
|
|
4
5
|
const chalk = require('chalk');
|
|
6
|
+
const { randomUUID } = require('crypto');
|
|
5
7
|
const upload = require('./fileUploader');
|
|
6
|
-
const {
|
|
8
|
+
const { APP_PREFIX } = require('./constants');
|
|
7
9
|
const pipesFactory = require('./pipe');
|
|
8
10
|
|
|
9
|
-
|
|
11
|
+
/**
|
|
12
|
+
* @typedef {import('../types').TestData} TestData
|
|
13
|
+
* @typedef {import('../types').RunStatus} RunStatus
|
|
14
|
+
*/
|
|
10
15
|
|
|
11
|
-
class
|
|
16
|
+
class Client {
|
|
12
17
|
/**
|
|
13
18
|
* Create a Testomat client instance
|
|
14
19
|
*
|
|
15
20
|
* @param {*} params
|
|
16
21
|
*/
|
|
17
22
|
constructor(params = {}) {
|
|
18
|
-
this.title = params.title || process.env.TESTOMATIO_TITLE;
|
|
19
|
-
this.createNewTests = params.createNewTests ?? !!process.env.TESTOMATIO_CREATE;
|
|
20
|
-
this.proceed = process.env.TESTOMATIO_PROCEED;
|
|
21
|
-
this.env = TESTOMATIO_ENV;
|
|
22
23
|
this.parallel = params.parallel;
|
|
23
24
|
const store = {};
|
|
25
|
+
this.uuid = randomUUID();
|
|
24
26
|
this.pipes = pipesFactory(params, store);
|
|
25
27
|
this.queue = Promise.resolve();
|
|
26
28
|
this.totalUploaded = 0;
|
|
@@ -31,11 +33,11 @@ class TestomatClient {
|
|
|
31
33
|
/**
|
|
32
34
|
* Used to create a new Test run
|
|
33
35
|
*
|
|
34
|
-
* @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
|
|
35
37
|
*/
|
|
36
38
|
createRun() {
|
|
37
39
|
// all pipes disabled, skipping
|
|
38
|
-
if (!this.pipes.filter(p => p.isEnabled).length) return;
|
|
40
|
+
if (!this.pipes.filter(p => p.isEnabled).length) return Promise.resolve();
|
|
39
41
|
|
|
40
42
|
const runParams = {
|
|
41
43
|
title: this.title,
|
|
@@ -47,27 +49,37 @@ class TestomatClient {
|
|
|
47
49
|
|
|
48
50
|
this.queue = this.queue
|
|
49
51
|
.then(() => Promise.all(this.pipes.map(p => p.createRun(runParams))))
|
|
50
|
-
.catch(err => console.log(APP_PREFIX, err))
|
|
51
|
-
|
|
52
|
+
.catch(err => console.log(APP_PREFIX, err))
|
|
53
|
+
.then(() => undefined); // fixes return type
|
|
54
|
+
debug('Run', this.queue);
|
|
52
55
|
return this.queue;
|
|
53
56
|
}
|
|
54
57
|
|
|
55
58
|
/**
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
* @
|
|
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>}
|
|
59
65
|
*/
|
|
60
|
-
async addTestRun(
|
|
66
|
+
async addTestRun(status, testData, storeArtifacts = []) {
|
|
61
67
|
// all pipes disabled, skipping
|
|
62
68
|
if (!this.pipes.filter(p => p.isEnabled).length) return;
|
|
63
69
|
|
|
70
|
+
if (!testData) testData = {
|
|
71
|
+
title: 'Unknown test',
|
|
72
|
+
suite_title: 'Unknown suite',
|
|
73
|
+
}
|
|
74
|
+
|
|
64
75
|
const {
|
|
65
|
-
error =
|
|
76
|
+
error = null,
|
|
66
77
|
time = '',
|
|
67
78
|
example = null,
|
|
68
79
|
files = [],
|
|
69
80
|
filesBuffers = [],
|
|
70
81
|
steps,
|
|
82
|
+
code = null,
|
|
71
83
|
title,
|
|
72
84
|
suite_title,
|
|
73
85
|
suite_id,
|
|
@@ -77,30 +89,34 @@ class TestomatClient {
|
|
|
77
89
|
|
|
78
90
|
const uploadedFiles = [];
|
|
79
91
|
|
|
80
|
-
if (testId) testData.test_id = testId;
|
|
81
|
-
|
|
82
92
|
let stack = '';
|
|
83
93
|
|
|
84
94
|
if (error) {
|
|
85
|
-
stack = this.formatError(error);
|
|
86
|
-
message = error
|
|
95
|
+
stack = this.formatError(error) || '';
|
|
96
|
+
message = error?.message;
|
|
87
97
|
}
|
|
88
98
|
if (steps) {
|
|
89
99
|
stack = this.formatSteps(stack, steps);
|
|
90
100
|
}
|
|
91
101
|
|
|
102
|
+
if (Array.isArray(storeArtifacts) && storeArtifacts.length) {
|
|
103
|
+
debug("CLIENT storeArtifact", storeArtifacts);
|
|
104
|
+
files.push(...storeArtifacts);
|
|
105
|
+
}
|
|
106
|
+
|
|
92
107
|
if (Array.isArray(global.testomatioArtifacts)) {
|
|
108
|
+
debug("CLIENT global[testomatioArtifacts]", global.testomatioArtifacts);
|
|
93
109
|
files.push(...global.testomatioArtifacts);
|
|
94
110
|
global.testomatioArtifacts = [];
|
|
95
111
|
}
|
|
96
112
|
|
|
97
113
|
for (const file of files) {
|
|
98
|
-
uploadedFiles.push(upload.uploadFileByPath(file, this.
|
|
114
|
+
uploadedFiles.push(upload.uploadFileByPath(file, this.uuid));
|
|
99
115
|
}
|
|
100
116
|
|
|
101
117
|
for (const [idx, buffer] of filesBuffers.entries()) {
|
|
102
118
|
const fileName = `${idx + 1}-${title.replace(/\s+/g, '-')}`;
|
|
103
|
-
uploadedFiles.push(upload.uploadFileAsBuffer(buffer, fileName, this.
|
|
119
|
+
uploadedFiles.push(upload.uploadFileAsBuffer(buffer, fileName, this.uuid));
|
|
104
120
|
}
|
|
105
121
|
|
|
106
122
|
const artifacts = await Promise.all(uploadedFiles);
|
|
@@ -115,6 +131,7 @@ class TestomatClient {
|
|
|
115
131
|
status,
|
|
116
132
|
stack,
|
|
117
133
|
example,
|
|
134
|
+
code,
|
|
118
135
|
title,
|
|
119
136
|
suite_title,
|
|
120
137
|
suite_id,
|
|
@@ -126,38 +143,40 @@ class TestomatClient {
|
|
|
126
143
|
|
|
127
144
|
this.queue = this.queue
|
|
128
145
|
.then(() => Promise.all(this.pipes.map(p => p.addTest(data))))
|
|
129
|
-
.catch(err => console.log(APP_PREFIX, err))
|
|
146
|
+
.catch(err => console.log(APP_PREFIX, err))
|
|
147
|
+
.then(() => undefined);
|
|
130
148
|
return this.queue;
|
|
131
149
|
}
|
|
132
150
|
|
|
133
151
|
/**
|
|
134
|
-
* Update run status
|
|
135
152
|
*
|
|
136
|
-
*
|
|
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.
|
|
137
157
|
*/
|
|
138
|
-
updateRunStatus(status, isParallel) {
|
|
158
|
+
updateRunStatus(status, isParallel = false) {
|
|
139
159
|
// all pipes disabled, skipping
|
|
140
|
-
if (!this.pipes.filter(p => p.isEnabled).length) return;
|
|
160
|
+
if (!this.pipes.filter(p => p.isEnabled).length) return Promise.resolve();
|
|
141
161
|
|
|
142
|
-
|
|
143
|
-
if (status === FINISHED) statusEvent = 'finish';
|
|
144
|
-
if (status === PASSED) statusEvent = 'pass';
|
|
145
|
-
if (status === FAILED) statusEvent = 'fail';
|
|
146
|
-
if (isParallel) statusEvent += '_parallel';
|
|
147
|
-
const runParams = { status, statusEvent };
|
|
162
|
+
const runParams = { status, parallel: isParallel };
|
|
148
163
|
|
|
149
164
|
this.queue = this.queue
|
|
150
165
|
.then(() => Promise.all(this.pipes.map(p => p.finishRun(runParams))))
|
|
166
|
+
.then(() => {
|
|
167
|
+
debug("TOTAL uploaded files", this.totalUploaded);
|
|
168
|
+
|
|
169
|
+
if (upload.isArtifactsEnabled() && this.totalUploaded > 0) {
|
|
170
|
+
console.log(
|
|
171
|
+
APP_PREFIX,
|
|
172
|
+
`🗄️ Total ${this.totalUploaded} artifacts ${
|
|
173
|
+
process.env.TESTOMATIO_PRIVATE_ARTIFACTS ? 'privately' : chalk.bold('publicly')
|
|
174
|
+
} uploaded to S3 bucket `,
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
})
|
|
151
178
|
.catch(err => console.log(APP_PREFIX, err));
|
|
152
179
|
|
|
153
|
-
if (upload.isArtifactsEnabled() && this.totalUploaded > 0) {
|
|
154
|
-
console.log(
|
|
155
|
-
APP_PREFIX,
|
|
156
|
-
`🗄️ Total ${this.totalUploaded} artifacts ${
|
|
157
|
-
process.env.TESTOMATIO_PRIVATE_ARTIFACTS ? 'privately' : chalk.bold('publicly')
|
|
158
|
-
} uploaded to S3 bucket `,
|
|
159
|
-
);
|
|
160
|
-
}
|
|
161
180
|
return this.queue;
|
|
162
181
|
}
|
|
163
182
|
|
|
@@ -198,4 +217,4 @@ class TestomatClient {
|
|
|
198
217
|
}
|
|
199
218
|
}
|
|
200
219
|
|
|
201
|
-
module.exports =
|
|
220
|
+
module.exports = Client;
|
package/lib/constants.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const chalk = require('chalk');
|
|
2
2
|
|
|
3
3
|
const APP_PREFIX = chalk.gray('[TESTOMATIO]');
|
|
4
|
+
const TESTOMAT_ARTIFACT_SUFFIX = "testomatio_artifact_";
|
|
4
5
|
|
|
5
6
|
const CSV_HEADERS = [
|
|
6
7
|
{ id: 'suite_title', title: 'Suite_title' },
|
|
@@ -10,11 +11,16 @@ const CSV_HEADERS = [
|
|
|
10
11
|
{ id: 'stack', title: 'Stack' },
|
|
11
12
|
];
|
|
12
13
|
|
|
13
|
-
|
|
14
|
+
const STATUS = {
|
|
14
15
|
PASSED: 'passed',
|
|
15
16
|
FAILED: 'failed',
|
|
16
17
|
SKIPPED: 'skipped',
|
|
17
18
|
FINISHED: 'finished',
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
module.exports = {
|
|
18
22
|
APP_PREFIX,
|
|
19
|
-
|
|
20
|
-
|
|
23
|
+
TESTOMAT_ARTIFACT_SUFFIX,
|
|
24
|
+
CSV_HEADERS,
|
|
25
|
+
STATUS,
|
|
26
|
+
}
|
package/lib/fileUploader.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
const debug = require('debug')('@testomatio/reporter:
|
|
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');
|
|
@@ -88,7 +88,9 @@ const uploadUsingS3 = async (filePath, runId) => {
|
|
|
88
88
|
return;
|
|
89
89
|
}
|
|
90
90
|
|
|
91
|
-
const {
|
|
91
|
+
const {
|
|
92
|
+
S3_REGION, S3_ACCESS_KEY_ID, S3_SECRET_ACCESS_KEY, S3_ENDPOINT, TESTOMATIO_PRIVATE_ARTIFACTS, S3_BUCKET
|
|
93
|
+
} = getConfig();
|
|
92
94
|
|
|
93
95
|
const file = fs.readFileSync(filePath);
|
|
94
96
|
|
|
@@ -139,7 +141,9 @@ const uploadUsingS3 = async (filePath, runId) => {
|
|
|
139
141
|
|
|
140
142
|
const uploadUsingS3AsBuffer = async (buffer, fileName, runId) => {
|
|
141
143
|
|
|
142
|
-
const {
|
|
144
|
+
const {
|
|
145
|
+
S3_REGION, S3_ACCESS_KEY_ID, S3_SECRET_ACCESS_KEY, S3_ENDPOINT, TESTOMATIO_PRIVATE_ARTIFACTS, S3_BUCKET
|
|
146
|
+
} = getConfig();
|
|
143
147
|
|
|
144
148
|
const ACL = TESTOMATIO_PRIVATE_ARTIFACTS ? 'private' : 'public-read';
|
|
145
149
|
|
|
@@ -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
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
const debug = require('debug')('@testomatio/reporter:pipe:csv');
|
|
1
2
|
const path = require('path');
|
|
2
3
|
const fs = require('fs');
|
|
3
4
|
const csvWriter = require('csv-writer');
|
|
@@ -6,9 +7,16 @@ const merge = require('lodash.merge');
|
|
|
6
7
|
const { isSameTest, getCurrentDateTime } = require('../util');
|
|
7
8
|
const { CSV_HEADERS } = require('../constants');
|
|
8
9
|
|
|
10
|
+
/**
|
|
11
|
+
* @typedef {import('../../types').Pipe} Pipe
|
|
12
|
+
* @typedef {import('../../types').TestData} TestData
|
|
13
|
+
* @class CsvPipe
|
|
14
|
+
* @implements {Pipe}
|
|
15
|
+
*/
|
|
9
16
|
class CsvPipe {
|
|
10
|
-
|
|
11
|
-
|
|
17
|
+
|
|
18
|
+
constructor(params, store) {
|
|
19
|
+
this.store = store || {};
|
|
12
20
|
this.title = params.title || process.env.TESTOMATIO_TITLE;
|
|
13
21
|
this.isEnabled = true;
|
|
14
22
|
this.results = [];
|
|
@@ -54,10 +62,16 @@ class CsvPipe {
|
|
|
54
62
|
* @param {Object} headers - csv file headers. Example: [{ id: 'suite_title', title: 'Suite_title' }]
|
|
55
63
|
*/
|
|
56
64
|
async saveToCsv(data, headers) {
|
|
65
|
+
debug('Data', data);
|
|
57
66
|
// First, we check whether the export directory exists: if yes - OK, no - create it.
|
|
58
67
|
this.checkExportDir();
|
|
59
68
|
|
|
60
|
-
|
|
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...`));
|
|
61
75
|
// Create csv writer object
|
|
62
76
|
const writer = csvWriter.createObjectCsvWriter({
|
|
63
77
|
path: this.outputFile,
|
|
@@ -66,7 +80,7 @@ class CsvPipe {
|
|
|
66
80
|
// Save csv file based on the current data
|
|
67
81
|
try {
|
|
68
82
|
await writer.writeRecords(data);
|
|
69
|
-
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}`));
|
|
70
84
|
} catch (e) {
|
|
71
85
|
console.log('Unknown error: ', e);
|
|
72
86
|
}
|
|
@@ -98,8 +112,8 @@ class CsvPipe {
|
|
|
98
112
|
}
|
|
99
113
|
|
|
100
114
|
/**
|
|
101
|
-
*
|
|
102
|
-
* @
|
|
115
|
+
* @param {{ tests?: TestData[] }} runParams
|
|
116
|
+
* @returns {Promise<void>}
|
|
103
117
|
*/
|
|
104
118
|
async finishRun(runParams) {
|
|
105
119
|
if (!this.isEnabled) return;
|
package/lib/pipe/github.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
const debug = require('debug')('@testomatio/reporter:pipe:github');
|
|
1
2
|
const path = require('path');
|
|
2
3
|
const chalk = require('chalk');
|
|
3
4
|
const humanizeDuration = require('humanize-duration');
|
|
@@ -6,6 +7,12 @@ const { Octokit } = require('@octokit/rest');
|
|
|
6
7
|
const { APP_PREFIX } = require('../constants');
|
|
7
8
|
const { ansiRegExp, isSameTest } = require('../util');
|
|
8
9
|
|
|
10
|
+
/**
|
|
11
|
+
* @typedef {import('../../types').Pipe} Pipe
|
|
12
|
+
* @typedef {import('../../types').TestData} TestData
|
|
13
|
+
* @class GitHubPipe
|
|
14
|
+
* @implements {Pipe}
|
|
15
|
+
*/
|
|
9
16
|
class GitHubPipe {
|
|
10
17
|
constructor(params, store = {}) {
|
|
11
18
|
this.isEnabled = false;
|
|
@@ -16,27 +23,23 @@ class GitHubPipe {
|
|
|
16
23
|
this.repo = process.env.GITHUB_REPOSITORY;
|
|
17
24
|
this.hiddenCommentData = `<!--- testomat.io report ${process.env.GITHUB_WORKFLOW || ''} -->`;
|
|
18
25
|
|
|
19
|
-
|
|
20
|
-
console.log(APP_PREFIX, 'GitHub Pipe: ', this.token ? 'TOKEN' : '*no token*', this.ref, this.repo);
|
|
21
|
-
}
|
|
26
|
+
debug('GitHub Pipe: ', this.token ? 'TOKEN' : '*no token*', 'Ref:', this.ref, 'Repo:', this.repo);
|
|
22
27
|
|
|
23
28
|
if (!this.token || !this.ref || !this.repo) return;
|
|
24
29
|
this.isEnabled = true;
|
|
25
30
|
const matchedIssue = this.ref.match(/refs\/pull\/(\d+)\/merge/);
|
|
26
31
|
if (!matchedIssue) return;
|
|
27
|
-
this.issue = matchedIssue[1];
|
|
32
|
+
this.issue = parseInt(matchedIssue[1], 10);
|
|
33
|
+
|
|
28
34
|
this.start = new Date();
|
|
29
35
|
|
|
30
|
-
|
|
31
|
-
console.log(APP_PREFIX, 'GitHub Pipe: Enabled');
|
|
32
|
-
}
|
|
36
|
+
debug('GitHub Pipe: Enabled');
|
|
33
37
|
}
|
|
34
38
|
|
|
35
39
|
async createRun() {}
|
|
36
40
|
|
|
37
|
-
updateRun() {}
|
|
38
|
-
|
|
39
41
|
addTest(test) {
|
|
42
|
+
debug('Adding test:', test);
|
|
40
43
|
if (!this.isEnabled) return;
|
|
41
44
|
|
|
42
45
|
const index = this.tests.findIndex(t => isSameTest(t, test));
|
|
@@ -51,6 +54,7 @@ class GitHubPipe {
|
|
|
51
54
|
|
|
52
55
|
async finishRun(runParams) {
|
|
53
56
|
if (!this.isEnabled) return;
|
|
57
|
+
if (!this.issue) return;
|
|
54
58
|
|
|
55
59
|
if (runParams.tests) runParams.tests.forEach(t => this.addTest(t));
|
|
56
60
|
|
|
@@ -58,7 +62,7 @@ class GitHubPipe {
|
|
|
58
62
|
auth: this.token,
|
|
59
63
|
});
|
|
60
64
|
|
|
61
|
-
const [owner, repo] = this.repo.split('/');
|
|
65
|
+
const [owner, repo] = (this.repo || '').split('/');
|
|
62
66
|
if (!(owner || repo)) return;
|
|
63
67
|
|
|
64
68
|
// ... create a comment on GitHub
|
|
@@ -69,7 +73,7 @@ class GitHubPipe {
|
|
|
69
73
|
let summary = `${this.hiddenCommentData}
|
|
70
74
|
|
|
71
75
|
| [](https://testomat.io) | ${
|
|
72
|
-
statusEmoji(runParams.status)} ${runParams.status.toUpperCase()} ${statusEmoji(runParams.status)} |
|
|
76
|
+
statusEmoji(runParams.status,)} ${runParams.status.toUpperCase()} ${statusEmoji(runParams.status)} |
|
|
73
77
|
| --- | --- |
|
|
74
78
|
| Tests | ✔️ **${this.tests.length}** tests run |
|
|
75
79
|
| Summary | ${statusEmoji('failed')} **${failedCount}** failed; ${statusEmoji(
|
|
@@ -150,11 +154,13 @@ class GitHubPipe {
|
|
|
150
154
|
.join('\n');
|
|
151
155
|
body += '\n</details>';
|
|
152
156
|
}
|
|
157
|
+
|
|
153
158
|
|
|
154
159
|
await deletePreviousReport(this.octokit, owner, repo, this.issue, this.hiddenCommentData);
|
|
155
160
|
|
|
156
161
|
// add report as comment
|
|
157
162
|
try {
|
|
163
|
+
debug('Adding comment\n', body);
|
|
158
164
|
const resp = await this.octokit.rest.issues.createComment({
|
|
159
165
|
owner,
|
|
160
166
|
repo,
|
|
@@ -163,6 +169,7 @@ class GitHubPipe {
|
|
|
163
169
|
});
|
|
164
170
|
|
|
165
171
|
const url = resp.data?.html_url;
|
|
172
|
+
debug('Comment URL:', url);
|
|
166
173
|
this.store.githubUrl = url;
|
|
167
174
|
|
|
168
175
|
console.log(APP_PREFIX, chalk.yellow('GitHub'), `Report created: ${chalk.magenta(url)}`);
|
|
@@ -221,7 +228,7 @@ async function deletePreviousReport(octokit, owner, repo, issue, hiddenCommentDa
|
|
|
221
228
|
comment_id: comment.id,
|
|
222
229
|
});
|
|
223
230
|
} catch (e) {
|
|
224
|
-
console.
|
|
231
|
+
console.warn(`Can't delete previously added comment with testomat.io report. Ignore.`);
|
|
225
232
|
}
|
|
226
233
|
|
|
227
234
|
// pass next env var if need to clear all previous reports;
|