@testomatio/reporter 1.4.11-beta-bitbucket-pipe β 1.4.11-beta.1-json
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 +2 -1
- package/lib/adapter/playwright.js +3 -0
- package/lib/pipe/bitbucket.js +43 -36
- package/lib/pipe/testomatio.js +2 -0
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -11,7 +11,7 @@ Testomat.io Reporter (this npm package) supports:
|
|
|
11
11
|
- π Integarion with all popular [JavaScript/TypeScript frameworks](./docs/frameworks.md)
|
|
12
12
|
- ποΈ Screenshots, videos, traces [uploaded into S3 bucket](./docs/artifacts.md)
|
|
13
13
|
- π [Stack traces](./docs/stacktrace.md) and error messages
|
|
14
|
-
- π [GitHub](./docs/pipes/github.md)
|
|
14
|
+
- π [GitHub](./docs/pipes/github.md), [GitLab](./docs/pipes/gitlab.md) & [Bitbucket](./docs/pipes/bitbucket.md) integration
|
|
15
15
|
- π
Realtime reports
|
|
16
16
|
- ποΈ Other test frameworks supported via [JUNit XML](./docs/junit.md)
|
|
17
17
|
- πΆββοΈ Steps _(work in progress)_
|
|
@@ -128,6 +128,7 @@ Bring this reporter on CI and never lose test results again!
|
|
|
128
128
|
- [Gitlab](./docs/pipes/gitlab.md)
|
|
129
129
|
- [CSV](./docs/pipes/csv.md)
|
|
130
130
|
- [HTML report](./docs/pipes/html.md)
|
|
131
|
+
- [Bitbucket](./docs/pipes/bitbucket.md)
|
|
131
132
|
- π [JUnit](./docs/junit.md)
|
|
132
133
|
- ποΈ [Artifacts](./docs/artifacts.md)
|
|
133
134
|
- π [Workflows](./docs/workflows.md)
|
package/lib/pipe/bitbucket.js
CHANGED
|
@@ -8,7 +8,7 @@ const { APP_PREFIX, testomatLogoURL } = require('../constants');
|
|
|
8
8
|
const { ansiRegExp, isSameTest } = require('../utils/utils');
|
|
9
9
|
const { statusEmoji, fullName } = require('../utils/pipe_utils');
|
|
10
10
|
|
|
11
|
-
//!
|
|
11
|
+
//! BITBUCKET_ACCESS_TOKEN environment variable is required for this functionality to work
|
|
12
12
|
//! and your pipeline trigger should be a pull request
|
|
13
13
|
|
|
14
14
|
/**
|
|
@@ -23,8 +23,8 @@ class BitbucketPipe {
|
|
|
23
23
|
this.store = store;
|
|
24
24
|
this.tests = [];
|
|
25
25
|
// Bitbucket PAT looks like bbpat-*****
|
|
26
|
-
this.token = params.
|
|
27
|
-
this.hiddenCommentData =
|
|
26
|
+
this.token = params.BITBUCKET_ACCESS_TOKEN || process.env.BITBUCKET_ACCESS_TOKEN || this.ENV.BITBUCKET_ACCESS_TOKEN;
|
|
27
|
+
this.hiddenCommentData = `Testomat.io report: ${process.env.BITBUCKET_BRANCH || ''}`;
|
|
28
28
|
|
|
29
29
|
debug(
|
|
30
30
|
chalk.yellow('Bitbucket Pipe:'),
|
|
@@ -32,11 +32,6 @@ class BitbucketPipe {
|
|
|
32
32
|
`Project key: ${this.ENV.BITBUCKET_PROJECT_KEY}, Pull request ID: ${this.ENV.BITBUCKET_PR_ID}`,
|
|
33
33
|
);
|
|
34
34
|
|
|
35
|
-
if (!this.ENV.BITBUCKET_PROJECT_KEY || !this.ENV.BITBUCKET_PR_ID) {
|
|
36
|
-
debug(`CI pipeline should be run in a Pull Request to have the ability to add the report comment.`);
|
|
37
|
-
return;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
35
|
if (!this.token) {
|
|
41
36
|
debug(`Hint: Bitbucket CI variables are unavailable for unprotected branches by default.`);
|
|
42
37
|
return;
|
|
@@ -47,6 +42,11 @@ class BitbucketPipe {
|
|
|
47
42
|
debug('Bitbucket Pipe: Enabled');
|
|
48
43
|
}
|
|
49
44
|
|
|
45
|
+
async cleanLog(log) {
|
|
46
|
+
const stripAnsi = (await import('strip-ansi')).default;
|
|
47
|
+
return stripAnsi(log);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
50
|
// Prepare the run (if needed)
|
|
51
51
|
async prepareRun() {}
|
|
52
52
|
|
|
@@ -71,6 +71,12 @@ class BitbucketPipe {
|
|
|
71
71
|
|
|
72
72
|
if (runParams.tests) runParams.tests.forEach(t => this.addTest(t));
|
|
73
73
|
|
|
74
|
+
// Clean up the logs from ANSI codes
|
|
75
|
+
for (let i = 0; i < this.tests.length; i++) {
|
|
76
|
+
this.tests[i].message = await this.cleanLog(this.tests[i].message || '');
|
|
77
|
+
this.tests[i].stack = await this.cleanLog(this.tests[i].stack || '');
|
|
78
|
+
}
|
|
79
|
+
|
|
74
80
|
// Create a comment on Bitbucket
|
|
75
81
|
const passedCount = this.tests.filter(t => t.status === 'passed').length;
|
|
76
82
|
const failedCount = this.tests.filter(t => t.status === 'failed').length;
|
|
@@ -79,15 +85,15 @@ class BitbucketPipe {
|
|
|
79
85
|
// Constructing the table
|
|
80
86
|
let summary = `${this.hiddenCommentData}
|
|
81
87
|
|
|
82
|
-
|
|
|
88
|
+
|  | ${statusEmoji(
|
|
83
89
|
runParams.status,
|
|
84
90
|
)} ${runParams.status.toUpperCase()} ${statusEmoji(runParams.status)} |
|
|
85
91
|
| --- | --- |
|
|
86
|
-
| Tests | βοΈ
|
|
87
|
-
| Summary | ${statusEmoji('failed')} **${failedCount}** failed; ${statusEmoji(
|
|
92
|
+
| **Tests** | βοΈ **${this.tests.length}** tests run |
|
|
93
|
+
| **Summary** | ${statusEmoji('failed')} **${failedCount}** failed; ${statusEmoji(
|
|
88
94
|
'passed',
|
|
89
95
|
)} **${passedCount}** passed; **${statusEmoji('skipped')}** ${skippedCount} skipped |
|
|
90
|
-
| Duration | π
|
|
96
|
+
| **Duration** | π **${humanizeDuration(
|
|
91
97
|
parseInt(
|
|
92
98
|
this.tests.reduce((a, t) => a + (t.run_time || 0), 0),
|
|
93
99
|
10,
|
|
@@ -98,61 +104,62 @@ class BitbucketPipe {
|
|
|
98
104
|
)}** |
|
|
99
105
|
`;
|
|
100
106
|
|
|
101
|
-
if (this.ENV.
|
|
107
|
+
if (this.ENV.BITBUCKET_BRANCH && this.ENV.BITBUCKET_COMMIT) {
|
|
102
108
|
// eslint-disable-next-line max-len
|
|
103
|
-
summary += `| Job | π· [
|
|
109
|
+
summary += `| **Job** | π· [#${this.ENV.BITBUCKET_BUILD_NUMBER}](https://bitbucket.org/${this.ENV.BITBUCKET_REPO_FULL_NAME}/pipelines/results/${this.ENV.BITBUCKET_BUILD_NUMBER}") by commit: **${this.ENV.BITBUCKET_COMMIT}** |`;
|
|
104
110
|
}
|
|
105
111
|
|
|
106
112
|
const failures = this.tests
|
|
107
113
|
.filter(t => t.status === 'failed')
|
|
108
114
|
.slice(0, 20)
|
|
109
115
|
.map(t => {
|
|
110
|
-
let text =
|
|
111
|
-
|
|
112
|
-
if (t.message)
|
|
116
|
+
let text = `${statusEmoji('failed')} ${fullName(t)}\n`;
|
|
117
|
+
if (t.message) {
|
|
113
118
|
text += `> ${t.message
|
|
114
119
|
.replace(/[^\x20-\x7E]/g, '')
|
|
115
120
|
.replace(ansiRegExp(), '')
|
|
116
121
|
.trim()}\n`;
|
|
117
|
-
|
|
118
|
-
|
|
122
|
+
}
|
|
123
|
+
if (t.stack) {
|
|
124
|
+
text += `\n\`\`\`diff\n${t.stack
|
|
125
|
+
.replace(ansiRegExp(), '')
|
|
126
|
+
.replace(
|
|
127
|
+
/^[\s\S]*################\[ Failure \]################/g,
|
|
128
|
+
'################[ Failure ]################',
|
|
129
|
+
)
|
|
130
|
+
.trim()}\n\`\`\`\n`;
|
|
131
|
+
}
|
|
119
132
|
if (t.artifacts && t.artifacts.length && !this.ENV.TESTOMATIO_PRIVATE_ARTIFACTS) {
|
|
120
133
|
t.artifacts
|
|
121
134
|
.filter(f => !!f)
|
|
122
|
-
.filter(f => f.endsWith('.png'))
|
|
123
135
|
.forEach(f => {
|
|
124
136
|
if (f.endsWith('.png')) {
|
|
125
|
-
text += `\n`;
|
|
126
|
-
|
|
137
|
+
text += `\n`;
|
|
138
|
+
} else {
|
|
139
|
+
text += `[π ${path.basename(f)}](${f})\n`;
|
|
127
140
|
}
|
|
128
|
-
text += `[π ${path.basename(f)}](${f})\n`;
|
|
129
|
-
return text;
|
|
130
141
|
});
|
|
131
142
|
}
|
|
132
|
-
|
|
133
|
-
text += '\n---\n';
|
|
134
|
-
|
|
143
|
+
text += `\n---\n`;
|
|
135
144
|
return text;
|
|
136
145
|
});
|
|
137
146
|
|
|
138
147
|
let body = summary;
|
|
139
148
|
|
|
140
149
|
if (failures.length) {
|
|
141
|
-
body += `\n
|
|
142
|
-
if (failures.length >
|
|
143
|
-
body +=
|
|
150
|
+
body += `\nπ₯ **Failures (${failures.length})**\n\n* ${failures.join('\n* ')}\n`;
|
|
151
|
+
if (failures.length > 10) {
|
|
152
|
+
body += `\n> Notice: Only the first 10 failures are shown.`;
|
|
144
153
|
}
|
|
145
|
-
body += '\n\n</details>';
|
|
146
154
|
}
|
|
147
155
|
|
|
148
156
|
if (this.tests.length > 0) {
|
|
149
|
-
body +=
|
|
157
|
+
body += `\n\n**π’ Slowest Tests**\n\n`;
|
|
150
158
|
body += this.tests
|
|
151
159
|
.sort((a, b) => b.run_time - a.run_time)
|
|
152
160
|
.slice(0, 5)
|
|
153
|
-
.map(t => `*
|
|
161
|
+
.map(t => `* **${fullName(t)}** (${humanizeDuration(parseFloat(t.run_time))})`)
|
|
154
162
|
.join('\n');
|
|
155
|
-
body += '\n</details>';
|
|
156
163
|
}
|
|
157
164
|
|
|
158
165
|
// Construct Bitbucket API URL for comments
|
|
@@ -164,6 +171,7 @@ class BitbucketPipe {
|
|
|
164
171
|
|
|
165
172
|
// Add current report
|
|
166
173
|
debug(`Adding comment via URL: ${commentsRequestURL}`);
|
|
174
|
+
debug(`Final Bitbucket API call body: ${body}`);
|
|
167
175
|
|
|
168
176
|
try {
|
|
169
177
|
const addCommentResponse = await axios.post(
|
|
@@ -179,7 +187,7 @@ class BitbucketPipe {
|
|
|
179
187
|
|
|
180
188
|
const commentID = addCommentResponse.data.id;
|
|
181
189
|
// eslint-disable-next-line max-len
|
|
182
|
-
const commentURL =
|
|
190
|
+
const commentURL = `https://bitbucket.org/${this.ENV.BITBUCKET_WORKSPACE}/${this.ENV.BITBUCKET_REPO_SLUG}/pull-requests/${this.ENV.BITBUCKET_PR_ID}#comment-${commentID}`;
|
|
183
191
|
|
|
184
192
|
console.log(APP_PREFIX, chalk.yellow('Bitbucket'), `Report created: ${chalk.magenta(commentURL)}`);
|
|
185
193
|
} catch (err) {
|
|
@@ -235,7 +243,6 @@ async function deletePreviousReport(axiosInstance, commentsRequestURL, hiddenCom
|
|
|
235
243
|
} catch (e) {
|
|
236
244
|
console.warn(`Can't delete previously added comment with testomat.io report. Ignored.`);
|
|
237
245
|
}
|
|
238
|
-
|
|
239
246
|
// Pass next env var if need to clear all previous reports;
|
|
240
247
|
// only the last one is removed by default
|
|
241
248
|
if (!process.env.BITBUCKET_REMOVE_ALL_OUTDATED_REPORTS) break;
|
package/lib/pipe/testomatio.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@testomatio/reporter",
|
|
3
|
-
"version": "1.4.11-beta-
|
|
3
|
+
"version": "1.4.11-beta.1-json",
|
|
4
4
|
"description": "Testomatio Reporter Client",
|
|
5
5
|
"main": "./lib/reporter.js",
|
|
6
6
|
"typings": "typings/index.d.ts",
|
|
@@ -33,6 +33,7 @@
|
|
|
33
33
|
"lodash.merge": "^4.6.2",
|
|
34
34
|
"minimatch": "^9.0.3",
|
|
35
35
|
"promise-retry": "^2.0.1",
|
|
36
|
+
"strip-ansi": "^7.1.0",
|
|
36
37
|
"uuid": "^9.0.0"
|
|
37
38
|
},
|
|
38
39
|
"files": [
|