@testomatio/reporter 2.0.1-beta-2-ignore-xml → 2.0.1-beta.2
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/adapter/codecept.js +0 -2
- package/lib/adapter/cypress-plugin/index.js +0 -2
- package/lib/adapter/mocha.js +0 -1
- package/lib/adapter/nightwatch.d.ts +4 -0
- package/lib/adapter/nightwatch.js +80 -0
- package/lib/adapter/webdriver.d.ts +1 -1
- package/lib/adapter/webdriver.js +18 -8
- package/lib/bin/cli.js +73 -8
- package/lib/bin/reportXml.js +4 -2
- package/lib/bin/startTest.js +3 -2
- package/lib/bin/uploadArtifacts.js +5 -4
- package/lib/client.js +18 -9
- package/lib/config.js +2 -2
- package/lib/data-storage.d.ts +1 -1
- package/lib/data-storage.js +17 -7
- package/lib/junit-adapter/csharp.d.ts +1 -0
- package/lib/junit-adapter/csharp.js +11 -1
- package/lib/pipe/bitbucket.d.ts +2 -0
- package/lib/pipe/bitbucket.js +38 -26
- package/lib/pipe/debug.js +17 -3
- package/lib/pipe/github.d.ts +2 -2
- package/lib/pipe/github.js +35 -3
- package/lib/pipe/gitlab.d.ts +2 -0
- package/lib/pipe/gitlab.js +27 -9
- package/lib/pipe/html.d.ts +1 -0
- package/lib/pipe/html.js +1 -3
- package/lib/pipe/index.js +17 -7
- package/lib/pipe/testomatio.d.ts +3 -2
- package/lib/pipe/testomatio.js +85 -75
- package/lib/replay.d.ts +31 -0
- package/lib/replay.js +237 -0
- package/lib/reporter.d.ts +12 -12
- package/lib/services/artifacts.d.ts +1 -1
- package/lib/services/key-values.d.ts +1 -1
- package/lib/services/logger.d.ts +1 -1
- package/lib/services/logger.js +1 -2
- package/lib/template/testomatio.hbs +443 -68
- package/lib/uploader.js +10 -6
- package/lib/utils/utils.d.ts +3 -1
- package/lib/utils/utils.js +54 -21
- package/lib/xmlReader.js +54 -19
- package/package.json +8 -9
- package/src/adapter/codecept.js +0 -2
- package/src/adapter/cypress-plugin/index.js +0 -2
- package/src/adapter/mocha.js +0 -1
- package/src/adapter/nightwatch.js +88 -0
- package/src/adapter/webdriver.js +2 -2
- package/src/bin/cli.js +70 -2
- package/src/bin/reportXml.js +4 -1
- package/src/bin/startTest.js +2 -1
- package/src/bin/uploadArtifacts.js +2 -1
- package/src/client.js +1 -2
- package/src/config.js +2 -2
- package/src/junit-adapter/csharp.js +13 -1
- package/src/pipe/bitbucket.js +22 -24
- package/src/pipe/debug.js +18 -3
- package/src/pipe/github.js +1 -2
- package/src/pipe/gitlab.js +27 -9
- package/src/pipe/html.js +3 -4
- package/src/pipe/testomatio.js +106 -105
- package/src/replay.js +245 -0
- package/src/services/logger.js +1 -2
- package/src/template/testomatio.hbs +443 -68
- package/src/uploader.js +11 -6
- package/src/utils/utils.js +31 -12
- package/src/xmlReader.js +69 -17
|
@@ -5,12 +5,13 @@ import pc from 'picocolors';
|
|
|
5
5
|
import createDebugMessages from 'debug';
|
|
6
6
|
import TestomatClient from '../client.js';
|
|
7
7
|
import { APP_PREFIX } from '../constants.js';
|
|
8
|
-
import {
|
|
8
|
+
import { getPackageVersion } from '../utils/utils.js';
|
|
9
9
|
import { config } from '../config.js';
|
|
10
10
|
import { readLatestRunId } from '../utils/utils.js';
|
|
11
11
|
import dotenv from 'dotenv';
|
|
12
12
|
|
|
13
13
|
const debug = createDebugMessages('@testomatio/reporter:upload-cli');
|
|
14
|
+
const version = getPackageVersion();
|
|
14
15
|
console.log(pc.cyan(pc.bold(` 🤩 Testomat.io Reporter v${version}`)));
|
|
15
16
|
const program = new Command();
|
|
16
17
|
|
package/src/client.js
CHANGED
|
@@ -31,7 +31,6 @@ class Client {
|
|
|
31
31
|
* Create a Testomat client instance
|
|
32
32
|
* @returns
|
|
33
33
|
*/
|
|
34
|
-
// eslint-disable-next-line
|
|
35
34
|
constructor(params = {}) {
|
|
36
35
|
this.paramsForPipesFactory = params;
|
|
37
36
|
this.pipeStore = {};
|
|
@@ -79,7 +78,7 @@ class Client {
|
|
|
79
78
|
try {
|
|
80
79
|
const filterPipe = this.pipes.find(p => p.constructor.name.toLowerCase() === `${pipe.toLowerCase()}pipe`);
|
|
81
80
|
|
|
82
|
-
if (!filterPipe
|
|
81
|
+
if (!filterPipe?.isEnabled) {
|
|
83
82
|
// TODO:for the future for the another pipes
|
|
84
83
|
console.warn(
|
|
85
84
|
APP_PREFIX,
|
package/src/config.js
CHANGED
|
@@ -6,10 +6,10 @@ const debug = createDebugMessages('@testomatio/reporter:config');
|
|
|
6
6
|
/* for possibility to use multiple env files (reading different paths)
|
|
7
7
|
const envFileVars = dotenv.config({ path: '.env' }).parsed; */
|
|
8
8
|
|
|
9
|
-
if (process.env.TESTOMATIO_API_KEY) {
|
|
9
|
+
if (process.env.TESTOMATIO_API_KEY && !process.env.TESTOMATIO) {
|
|
10
10
|
process.env.TESTOMATIO = process.env.TESTOMATIO_API_KEY;
|
|
11
11
|
}
|
|
12
|
-
if (process.env.TESTOMATIO_TOKEN) {
|
|
12
|
+
if (process.env.TESTOMATIO_TOKEN && !process.env.TESTOMATIO) {
|
|
13
13
|
process.env.TESTOMATIO = process.env.TESTOMATIO_TOKEN;
|
|
14
14
|
}
|
|
15
15
|
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import path from 'path';
|
|
1
2
|
import Adapter from './adapter.js';
|
|
2
3
|
|
|
3
4
|
class CSharpAdapter extends Adapter {
|
|
@@ -7,10 +8,21 @@ class CSharpAdapter extends Adapter {
|
|
|
7
8
|
if (example) t.example = { ...example[1].split(',') };
|
|
8
9
|
const suite = t.suite_title.split('.');
|
|
9
10
|
t.suite_title = suite.pop();
|
|
10
|
-
t.file =
|
|
11
|
+
t.file = namespaceToFileName(t.file);
|
|
11
12
|
t.title = title.trim();
|
|
12
13
|
return t;
|
|
13
14
|
}
|
|
15
|
+
|
|
16
|
+
getFilePath(t) {
|
|
17
|
+
const fileName = namespaceToFileName(t.file);
|
|
18
|
+
return fileName;
|
|
19
|
+
}
|
|
14
20
|
}
|
|
15
21
|
|
|
16
22
|
export default CSharpAdapter;
|
|
23
|
+
|
|
24
|
+
function namespaceToFileName(fileName) {
|
|
25
|
+
const fileParts = fileName.split('.');
|
|
26
|
+
fileParts[fileParts.length - 1] = fileParts[fileParts.length - 1]?.replace(/\$.*/, '');
|
|
27
|
+
return `${fileParts.join(path.sep)}.cs`;
|
|
28
|
+
}
|
package/src/pipe/bitbucket.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { APP_PREFIX, testomatLogoURL } from '../constants.js';
|
|
2
2
|
import { ansiRegExp, isSameTest } from '../utils/utils.js';
|
|
3
3
|
import { statusEmoji, fullName } from '../utils/pipe_utils.js';
|
|
4
|
-
import
|
|
4
|
+
import { Gaxios } from 'gaxios';
|
|
5
5
|
import pc from 'picocolors';
|
|
6
6
|
import humanizeDuration from 'humanize-duration';
|
|
7
7
|
import merge from 'lodash.merge';
|
|
@@ -40,6 +40,13 @@ export class BitbucketPipe {
|
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
this.isEnabled = true;
|
|
43
|
+
this.client = new Gaxios({
|
|
44
|
+
baseURL: 'https://api.bitbucket.org/2.0',
|
|
45
|
+
headers: {
|
|
46
|
+
'Content-Type': 'application/json',
|
|
47
|
+
'Authorization': `Bearer ${this.token}`
|
|
48
|
+
}
|
|
49
|
+
});
|
|
43
50
|
|
|
44
51
|
debug('Bitbucket Pipe: Enabled');
|
|
45
52
|
}
|
|
@@ -166,26 +173,21 @@ export class BitbucketPipe {
|
|
|
166
173
|
|
|
167
174
|
// Construct Bitbucket API URL for comments
|
|
168
175
|
// eslint-disable-next-line max-len
|
|
169
|
-
const commentsRequestURL =
|
|
176
|
+
const commentsRequestURL = `/repositories/${this.ENV.BITBUCKET_WORKSPACE}/${this.ENV.BITBUCKET_REPO_SLUG}/pullrequests/${this.ENV.BITBUCKET_PR_ID}/comments`;
|
|
170
177
|
|
|
171
178
|
// Delete previous report
|
|
172
|
-
await deletePreviousReport(
|
|
179
|
+
await deletePreviousReport(this.client, commentsRequestURL, this.hiddenCommentData);
|
|
173
180
|
|
|
174
181
|
// Add current report
|
|
175
182
|
debug(`Adding comment via URL: ${commentsRequestURL}`);
|
|
176
183
|
debug(`Final Bitbucket API call body: ${body}`);
|
|
177
184
|
|
|
178
185
|
try {
|
|
179
|
-
const addCommentResponse = await
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
{
|
|
183
|
-
|
|
184
|
-
Authorization: `Bearer ${this.token}`,
|
|
185
|
-
'Content-Type': 'application/json',
|
|
186
|
-
},
|
|
187
|
-
},
|
|
188
|
-
);
|
|
186
|
+
const addCommentResponse = await this.client.request({
|
|
187
|
+
method: 'POST',
|
|
188
|
+
url: commentsRequestURL,
|
|
189
|
+
data: { content: { raw: body } }
|
|
190
|
+
});
|
|
189
191
|
|
|
190
192
|
const commentID = addCommentResponse.data.id;
|
|
191
193
|
// eslint-disable-next-line max-len
|
|
@@ -210,18 +212,16 @@ export class BitbucketPipe {
|
|
|
210
212
|
updateRun() {}
|
|
211
213
|
}
|
|
212
214
|
|
|
213
|
-
async function deletePreviousReport(
|
|
215
|
+
async function deletePreviousReport(client, commentsRequestURL, hiddenCommentData) {
|
|
214
216
|
if (process.env.BITBUCKET_KEEP_OUTDATED_REPORTS) return;
|
|
215
217
|
|
|
216
218
|
// Get comments
|
|
217
219
|
let comments = [];
|
|
218
220
|
|
|
219
221
|
try {
|
|
220
|
-
const response = await
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
'Content-Type': 'application/json',
|
|
224
|
-
},
|
|
222
|
+
const response = await client.request({
|
|
223
|
+
method: 'GET',
|
|
224
|
+
url: commentsRequestURL
|
|
225
225
|
});
|
|
226
226
|
comments = response.data.values;
|
|
227
227
|
} catch (e) {
|
|
@@ -236,11 +236,9 @@ async function deletePreviousReport(axiosInstance, commentsRequestURL, hiddenCom
|
|
|
236
236
|
try {
|
|
237
237
|
// Delete previous comment
|
|
238
238
|
const deleteCommentURL = `${commentsRequestURL}/${comment.id}`;
|
|
239
|
-
await
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
'Content-Type': 'application/json',
|
|
243
|
-
},
|
|
239
|
+
await client.request({
|
|
240
|
+
method: 'DELETE',
|
|
241
|
+
url: deleteCommentURL
|
|
244
242
|
});
|
|
245
243
|
} catch (e) {
|
|
246
244
|
console.warn(`Can't delete previously added comment with testomat.io report. Ignored.`);
|
package/src/pipe/debug.js
CHANGED
|
@@ -25,7 +25,22 @@ export class DebugPipe {
|
|
|
25
25
|
|
|
26
26
|
debug('Creating debug file:', this.logFilePath);
|
|
27
27
|
fs.writeFileSync(this.logFilePath, '');
|
|
28
|
-
|
|
28
|
+
|
|
29
|
+
// Create symlink to ensure consistent path to latest debug file
|
|
30
|
+
const symlinkPath = path.join(os.tmpdir(), 'testomatio.debug.latest.json');
|
|
31
|
+
try {
|
|
32
|
+
// Remove existing symlink if it exists
|
|
33
|
+
if (fs.existsSync(symlinkPath)) {
|
|
34
|
+
fs.unlinkSync(symlinkPath);
|
|
35
|
+
}
|
|
36
|
+
// Create new symlink pointing to the timestamped debug file
|
|
37
|
+
fs.symlinkSync(this.logFilePath, symlinkPath);
|
|
38
|
+
debug('Created symlink:', symlinkPath, '->', this.logFilePath);
|
|
39
|
+
} catch (err) {
|
|
40
|
+
debug('Failed to create symlink:', err.message);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
console.log(APP_PREFIX, '🪲 Debug file created');
|
|
29
44
|
this.testomatioEnvVars = Object.keys(process.env)
|
|
30
45
|
.filter(key => key.startsWith('TESTOMATIO_'))
|
|
31
46
|
.reduce((acc, key) => {
|
|
@@ -92,10 +107,10 @@ export class DebugPipe {
|
|
|
92
107
|
|
|
93
108
|
async finishRun(params) {
|
|
94
109
|
if (!this.isEnabled) return;
|
|
95
|
-
this.logToFile({ actions: 'finishRun', params });
|
|
96
110
|
await this.batchUpload();
|
|
97
111
|
if (this.batch.intervalFunction) clearInterval(this.batch.intervalFunction);
|
|
98
|
-
|
|
112
|
+
this.logToFile({ action: 'finishRun', params });
|
|
113
|
+
console.log(APP_PREFIX, '🪲 Debug Saved to', this.logFilePath);
|
|
99
114
|
}
|
|
100
115
|
|
|
101
116
|
toString() {
|
package/src/pipe/github.js
CHANGED
|
@@ -3,7 +3,6 @@ import path from 'path';
|
|
|
3
3
|
import pc from 'picocolors';
|
|
4
4
|
import humanizeDuration from 'humanize-duration';
|
|
5
5
|
import merge from 'lodash.merge';
|
|
6
|
-
import { Octokit } from '@octokit/rest';
|
|
7
6
|
import { APP_PREFIX, testomatLogoURL } from '../constants.js';
|
|
8
7
|
import { ansiRegExp, isSameTest } from '../utils/utils.js';
|
|
9
8
|
import { statusEmoji, fullName } from '../utils/pipe_utils.js';
|
|
@@ -64,6 +63,7 @@ class GitHubPipe {
|
|
|
64
63
|
if (!this.issue) return;
|
|
65
64
|
|
|
66
65
|
if (runParams.tests) runParams.tests.forEach(t => this.addTest(t));
|
|
66
|
+
const { Octokit } = await import('@octokit/rest');
|
|
67
67
|
|
|
68
68
|
this.octokit = new Octokit({
|
|
69
69
|
auth: this.token,
|
|
@@ -154,7 +154,6 @@ class GitHubPipe {
|
|
|
154
154
|
if (this.tests.length > 0) {
|
|
155
155
|
body += '\n<details>\n<summary><h3>🐢 Slowest Tests</h3></summary>\n\n';
|
|
156
156
|
body += this.tests
|
|
157
|
-
// eslint-disable-next-line no-unsafe-optional-chaining
|
|
158
157
|
.sort((a, b) => b?.run_time - a?.run_time)
|
|
159
158
|
.slice(0, 5)
|
|
160
159
|
.map(t => `* ${fullName(t)} (${humanizeDuration(parseFloat(t.run_time))})`)
|
package/src/pipe/gitlab.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import createDebugMessages from 'debug';
|
|
2
|
-
import
|
|
2
|
+
import { Gaxios } from 'gaxios';
|
|
3
3
|
import pc from 'picocolors';
|
|
4
4
|
import humanizeDuration from 'humanize-duration';
|
|
5
5
|
import merge from 'lodash.merge';
|
|
@@ -45,6 +45,12 @@ class GitLabPipe {
|
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
this.isEnabled = true;
|
|
48
|
+
this.client = new Gaxios({
|
|
49
|
+
baseURL: 'https://gitlab.com/api/v4',
|
|
50
|
+
headers: {
|
|
51
|
+
'Content-Type': 'application/json',
|
|
52
|
+
}
|
|
53
|
+
});
|
|
48
54
|
|
|
49
55
|
debug('GitLab Pipe: Enabled');
|
|
50
56
|
}
|
|
@@ -149,7 +155,6 @@ class GitLabPipe {
|
|
|
149
155
|
if (this.tests.length > 0) {
|
|
150
156
|
body += '\n<details>\n<summary><h3>🐢 Slowest Tests</h3></summary>\n\n';
|
|
151
157
|
body += this.tests
|
|
152
|
-
// eslint-disable-next-line no-unsafe-optional-chaining
|
|
153
158
|
.sort((a, b) => b?.run_time - a?.run_time)
|
|
154
159
|
.slice(0, 5)
|
|
155
160
|
.map(t => `* ${fullName(t)} (${humanizeDuration(parseFloat(t.run_time))})`)
|
|
@@ -158,16 +163,21 @@ class GitLabPipe {
|
|
|
158
163
|
}
|
|
159
164
|
|
|
160
165
|
// eslint-disable-next-line max-len
|
|
161
|
-
const commentsRequestURL =
|
|
166
|
+
const commentsRequestURL = `/projects/${this.ENV.CI_PROJECT_ID}/merge_requests/${this.ENV.CI_MERGE_REQUEST_IID}/notes`;
|
|
162
167
|
|
|
163
168
|
// delete previous report
|
|
164
|
-
await deletePreviousReport(
|
|
169
|
+
await deletePreviousReport(this.client, commentsRequestURL, this.hiddenCommentData, this.token);
|
|
165
170
|
|
|
166
171
|
// add current report
|
|
167
172
|
debug(`Adding comment via url: ${commentsRequestURL}`);
|
|
168
173
|
|
|
169
174
|
try {
|
|
170
|
-
const addCommentResponse = await
|
|
175
|
+
const addCommentResponse = await this.client.request({
|
|
176
|
+
method: 'POST',
|
|
177
|
+
url: commentsRequestURL,
|
|
178
|
+
params: { access_token: this.token },
|
|
179
|
+
data: { body }
|
|
180
|
+
});
|
|
171
181
|
|
|
172
182
|
const commentID = addCommentResponse.data.id;
|
|
173
183
|
// eslint-disable-next-line max-len
|
|
@@ -192,14 +202,18 @@ class GitLabPipe {
|
|
|
192
202
|
updateRun() {}
|
|
193
203
|
}
|
|
194
204
|
|
|
195
|
-
async function deletePreviousReport(
|
|
205
|
+
async function deletePreviousReport(client, commentsRequestURL, hiddenCommentData, token) {
|
|
196
206
|
if (process.env.GITLAB_KEEP_OUTDATED_REPORTS) return;
|
|
197
207
|
|
|
198
208
|
// get comments
|
|
199
209
|
let comments = [];
|
|
200
210
|
|
|
201
211
|
try {
|
|
202
|
-
const response = await
|
|
212
|
+
const response = await client.request({
|
|
213
|
+
method: 'GET',
|
|
214
|
+
url: commentsRequestURL,
|
|
215
|
+
params: { access_token: token }
|
|
216
|
+
});
|
|
203
217
|
comments = response.data;
|
|
204
218
|
} catch (e) {
|
|
205
219
|
console.error('Error while attempt to retrieve comments on GitLab Merge Request:\n', e);
|
|
@@ -212,8 +226,12 @@ async function deletePreviousReport(axiosInstance, commentsRequestURL, hiddenCom
|
|
|
212
226
|
if (comment.body.includes(hiddenCommentData)) {
|
|
213
227
|
try {
|
|
214
228
|
// delete previous comment
|
|
215
|
-
const deleteCommentURL = `${commentsRequestURL}/${comment.id}
|
|
216
|
-
await
|
|
229
|
+
const deleteCommentURL = `${commentsRequestURL}/${comment.id}`;
|
|
230
|
+
await client.request({
|
|
231
|
+
method: 'DELETE',
|
|
232
|
+
url: deleteCommentURL,
|
|
233
|
+
params: { access_token: token }
|
|
234
|
+
});
|
|
217
235
|
} catch (e) {
|
|
218
236
|
console.warn(`Can't delete previously added comment with testomat.io report. Ignore.`);
|
|
219
237
|
}
|
package/src/pipe/html.js
CHANGED
|
@@ -66,6 +66,8 @@ class HtmlPipe {
|
|
|
66
66
|
// empty
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
+
async prepareRun() {}
|
|
70
|
+
|
|
69
71
|
updateRun() {
|
|
70
72
|
// empty
|
|
71
73
|
}
|
|
@@ -235,7 +237,6 @@ class HtmlPipe {
|
|
|
235
237
|
</select>`,
|
|
236
238
|
),
|
|
237
239
|
);
|
|
238
|
-
/* eslint-disable */
|
|
239
240
|
handlebars.registerHelper('emptyDataComponent', () => {
|
|
240
241
|
const svgFilePath = path.join(__dirname, '..', 'template', 'emptyData.svg');
|
|
241
242
|
const svgContent = fs.readFileSync(svgFilePath, 'utf8');
|
|
@@ -252,13 +253,12 @@ class HtmlPipe {
|
|
|
252
253
|
<div>`,
|
|
253
254
|
);
|
|
254
255
|
});
|
|
255
|
-
/* eslint-enable */
|
|
256
256
|
handlebars.registerHelper('pageDispleyElements', tests => {
|
|
257
257
|
// We wrapp the lines to the HTML format we need
|
|
258
258
|
const totalTests = JSON.parse(
|
|
259
259
|
JSON.stringify(tests)
|
|
260
260
|
.replace(/<script>/g, '<script>')
|
|
261
|
-
.replace(/<\/script>/g, '</script>'),
|
|
261
|
+
.replace(/<\/script>/g, '</script>'),
|
|
262
262
|
);
|
|
263
263
|
|
|
264
264
|
const paginationOptions = {
|
|
@@ -285,7 +285,6 @@ class HtmlPipe {
|
|
|
285
285
|
|
|
286
286
|
statuses.forEach(status => {
|
|
287
287
|
for (const option in paginationOptions) {
|
|
288
|
-
// eslint-disable-next-line no-prototype-builtins
|
|
289
288
|
if (paginationOptions.hasOwnProperty(option)) {
|
|
290
289
|
const pageSize = paginationOptions[option];
|
|
291
290
|
let filteredItems = totalTests;
|