testbeats 2.2.7 → 2.2.9
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/package.json +3 -2
- package/src/beats/beats.attachments.js +1 -1
- package/src/beats/beats.js +2 -2
- package/src/cli.js +18 -0
- package/src/commands/manual-sync.command.js +79 -0
- package/src/helpers/constants.js +1 -0
- package/src/index.d.ts +8 -3
- package/src/manual/parsers/gherkin.js +237 -0
- package/src/manual/sync.helper.js +148 -0
- package/src/platforms/base.platform.js +13 -108
- package/src/platforms/chat.platform.js +4 -1
- package/src/platforms/slack.platform.js +14 -1
- package/src/targets/base.target.js +114 -1
- package/src/targets/chat.js +11 -4
- package/src/targets/custom.target.js +3 -2
- package/src/targets/github-output.target.js +64 -0
- package/src/targets/github.target.js +316 -0
- package/src/targets/index.js +5 -2
- package/src/targets/slack.js +32 -12
- package/src/targets/teams.js +14 -8
- package/src/utils/context.utils.js +5 -0
- package/src/targets/github.js +0 -310
package/src/targets/github.js
DELETED
|
@@ -1,310 +0,0 @@
|
|
|
1
|
-
const request = require('phin-retry');
|
|
2
|
-
const { getPercentage, truncate, getPrettyDuration } = require('../helpers/helper');
|
|
3
|
-
const extension_manager = require('../extensions');
|
|
4
|
-
const { HOOK, STATUS, TARGET } = require('../helpers/constants');
|
|
5
|
-
const logger = require('../utils/logger');
|
|
6
|
-
|
|
7
|
-
const PerformanceTestResult = require('performance-results-parser/src/models/PerformanceTestResult');
|
|
8
|
-
const { getValidMetrics, getMetricValuesText } = require('../helpers/performance');
|
|
9
|
-
const TestResult = require('test-results-parser/src/models/TestResult');
|
|
10
|
-
const { getPlatform } = require('../platforms');
|
|
11
|
-
|
|
12
|
-
const STATUSES = {
|
|
13
|
-
GOOD: '✅',
|
|
14
|
-
WARNING: '⚠️',
|
|
15
|
-
DANGER: '❌'
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
async function run({ result, target }) {
|
|
19
|
-
setTargetInputs(target);
|
|
20
|
-
const payload = getMainPayload();
|
|
21
|
-
if (result instanceof PerformanceTestResult) {
|
|
22
|
-
await setPerformancePayload({ result, target, payload });
|
|
23
|
-
} else {
|
|
24
|
-
await setFunctionalPayload({ result, target, payload });
|
|
25
|
-
}
|
|
26
|
-
const message = getMarkdownMessage({ result, target, payload });
|
|
27
|
-
logger.info(`🔔 Publishing results to GitHub PR...`);
|
|
28
|
-
return await publishToGitHub({ target, message });
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
async function setFunctionalPayload({ result, target, payload }) {
|
|
32
|
-
await extension_manager.run({ result, target, payload, hook: HOOK.START });
|
|
33
|
-
setMainContent({ result, target, payload });
|
|
34
|
-
await extension_manager.run({ result, target, payload, hook: HOOK.AFTER_SUMMARY });
|
|
35
|
-
setSuiteContent({ result, target, payload });
|
|
36
|
-
await extension_manager.run({ result, target, payload, hook: HOOK.END });
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
function setTargetInputs(target) {
|
|
40
|
-
target.inputs = Object.assign({}, default_inputs, target.inputs);
|
|
41
|
-
if (target.inputs.publish === 'test-summary-slim') {
|
|
42
|
-
target.inputs.include_suites = false;
|
|
43
|
-
}
|
|
44
|
-
if (target.inputs.publish === 'failure-details') {
|
|
45
|
-
target.inputs.include_failure_details = true;
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
function getMainPayload() {
|
|
50
|
-
return {
|
|
51
|
-
content: []
|
|
52
|
-
};
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
function setMainContent({ result, target, payload }) {
|
|
56
|
-
const titleText = getTitleText(result, target);
|
|
57
|
-
const resultText = getResultText(result);
|
|
58
|
-
const durationText = getPrettyDuration(result.duration, target.inputs.duration);
|
|
59
|
-
|
|
60
|
-
let content = `## ${titleText}\n\n`;
|
|
61
|
-
content += `**Results**: ${resultText}\n`;
|
|
62
|
-
content += `**Duration**: ${durationText}\n\n`;
|
|
63
|
-
|
|
64
|
-
payload.content.push(content);
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
function getTitleText(result, target) {
|
|
68
|
-
let text = target.inputs.title ? target.inputs.title : result.name;
|
|
69
|
-
if (target.inputs.title_suffix) {
|
|
70
|
-
text = `${text} ${target.inputs.title_suffix}`;
|
|
71
|
-
}
|
|
72
|
-
if (target.inputs.title_link) {
|
|
73
|
-
text = `[${text}](${target.inputs.title_link})`;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
const status = result.status !== 'PASS' ? STATUSES.DANGER : STATUSES.GOOD;
|
|
77
|
-
return `${status} ${text}`;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
function getResultText(result) {
|
|
81
|
-
const percentage = getPercentage(result.passed, result.total);
|
|
82
|
-
return `${result.passed} / ${result.total} Passed (${percentage}%)`;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
function setSuiteContent({ result, target, payload }) {
|
|
86
|
-
let suite_count = 0;
|
|
87
|
-
if (target.inputs.include_suites) {
|
|
88
|
-
for (let i = 0; i < result.suites.length && suite_count < target.inputs.max_suites; i++) {
|
|
89
|
-
const suite = result.suites[i];
|
|
90
|
-
if (target.inputs.only_failures && suite.status !== 'FAIL') {
|
|
91
|
-
continue;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
// if suites length eq to 1 then main content will include suite summary
|
|
95
|
-
if (result.suites.length > 1) {
|
|
96
|
-
payload.content.push(getSuiteSummary({ target, suite }));
|
|
97
|
-
suite_count += 1;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
if (target.inputs.include_failure_details) {
|
|
101
|
-
// Only attach failure details if there were failures
|
|
102
|
-
if (suite.failed > 0) {
|
|
103
|
-
payload.content.push(getFailureDetails(suite));
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
function getSuiteSummary({ target, suite }) {
|
|
111
|
-
const platform = getPlatform(TARGET.GITHUB);
|
|
112
|
-
const text = platform.getSuiteSummaryText(target, suite);
|
|
113
|
-
return `### ${suite.name}\n${text}\n\n`;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
function getFailureDetails(suite) {
|
|
117
|
-
let content = `<details>\n<summary>❌ Failed Tests</summary>\n\n`;
|
|
118
|
-
const cases = suite.cases;
|
|
119
|
-
for (let i = 0; i < cases.length; i++) {
|
|
120
|
-
const test_case = cases[i];
|
|
121
|
-
if (test_case.status === 'FAIL') {
|
|
122
|
-
content += `**Test**: ${test_case.name}\n`;
|
|
123
|
-
content += `**Error**: \n\`\`\`\n${truncate(test_case.failure ?? 'N/A', 500)}\n\`\`\`\n\n`;
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
content += `</details>\n\n`;
|
|
127
|
-
return content;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
function getMarkdownMessage({ result, target, payload }) {
|
|
131
|
-
return payload.content.join('');
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
async function publishToGitHub({ target, message }) {
|
|
135
|
-
const { url, repo, owner, pull_number } = extractGitHubInfo(target);
|
|
136
|
-
const token = target.inputs.token || process.env.GITHUB_TOKEN;
|
|
137
|
-
|
|
138
|
-
if (!token) {
|
|
139
|
-
throw new Error('GitHub token is required. Set GITHUB_TOKEN environment variable or provide token in target inputs.');
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
if (!pull_number) {
|
|
143
|
-
throw new Error('Pull request number not found. This target only works in GitHub Actions triggered by pull requests.');
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
const comment_body = target.inputs.comment_title ?
|
|
147
|
-
`${target.inputs.comment_title}\n\n${message}` :
|
|
148
|
-
message;
|
|
149
|
-
|
|
150
|
-
const headers = {
|
|
151
|
-
'Authorization': `token ${token}`,
|
|
152
|
-
'Accept': 'application/vnd.github.v3+json',
|
|
153
|
-
'User-Agent': 'testbeats'
|
|
154
|
-
};
|
|
155
|
-
|
|
156
|
-
if (target.inputs.update_comment) {
|
|
157
|
-
// Try to find existing comment and update it
|
|
158
|
-
const existingComment = await findExistingComment({ owner, repo, pull_number, token, comment_title: target.inputs.comment_title });
|
|
159
|
-
if (existingComment) {
|
|
160
|
-
return request.patch({
|
|
161
|
-
url: `${url}/repos/${owner}/${repo}/issues/comments/${existingComment.id}`,
|
|
162
|
-
headers,
|
|
163
|
-
body: { body: comment_body }
|
|
164
|
-
});
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
// Create new comment
|
|
169
|
-
return request.post({
|
|
170
|
-
url: `${url}/repos/${owner}/${repo}/issues/${pull_number}/comments`,
|
|
171
|
-
headers,
|
|
172
|
-
body: { body: comment_body }
|
|
173
|
-
});
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
async function findExistingComment({ owner, repo, pull_number, token, comment_title }) {
|
|
177
|
-
if (!comment_title) return null;
|
|
178
|
-
|
|
179
|
-
try {
|
|
180
|
-
const url = `https://api.github.com/repos/${owner}/${repo}/issues/${pull_number}/comments`;
|
|
181
|
-
const headers = {
|
|
182
|
-
'Authorization': `token ${token}`,
|
|
183
|
-
'Accept': 'application/vnd.github.v3+json',
|
|
184
|
-
'User-Agent': 'testbeats'
|
|
185
|
-
};
|
|
186
|
-
|
|
187
|
-
const response = await request.get({ url, headers });
|
|
188
|
-
const comments = JSON.parse(response.body);
|
|
189
|
-
|
|
190
|
-
return comments.find(comment => comment.body.includes(comment_title));
|
|
191
|
-
} catch (error) {
|
|
192
|
-
logger.warn('Failed to find existing comment:', error.message);
|
|
193
|
-
return null;
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
function extractGitHubInfo(target) {
|
|
198
|
-
let url = target.inputs.url;
|
|
199
|
-
let owner = target.inputs.owner;
|
|
200
|
-
let repo = target.inputs.repo;
|
|
201
|
-
let pull_number = target.inputs.pull_number;
|
|
202
|
-
|
|
203
|
-
if (!owner || !repo) {
|
|
204
|
-
const repository = process.env.GITHUB_REPOSITORY;
|
|
205
|
-
const ref = process.env.GITHUB_REF;
|
|
206
|
-
[owner, repo] = repository.split('/');
|
|
207
|
-
if (ref && ref.includes('refs/pull/')) {
|
|
208
|
-
pull_number = ref.replace('refs/pull/', '').replace('/merge', '');
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
if (!url) {
|
|
213
|
-
url = `https://api.github.com`;
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
return {
|
|
217
|
-
url,
|
|
218
|
-
owner,
|
|
219
|
-
repo,
|
|
220
|
-
pull_number
|
|
221
|
-
};
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
async function setPerformancePayload({ result, target, payload }) {
|
|
225
|
-
await extension_manager.run({ result, target, payload, hook: HOOK.START });
|
|
226
|
-
await setPerformanceMainContent({ result, target, payload });
|
|
227
|
-
await extension_manager.run({ result, target, payload, hook: HOOK.AFTER_SUMMARY });
|
|
228
|
-
await setTransactionContent({ result, target, payload });
|
|
229
|
-
await extension_manager.run({ result, target, payload, hook: HOOK.END });
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
async function setPerformanceMainContent({ result, target, payload }) {
|
|
233
|
-
const titleText = getTitleText(result, target);
|
|
234
|
-
let content = `## ${titleText}\n\n`;
|
|
235
|
-
|
|
236
|
-
const metrics = getValidMetrics(result.metrics);
|
|
237
|
-
if (metrics.length > 0) {
|
|
238
|
-
content += `**Performance Metrics**:\n`;
|
|
239
|
-
content += getMetricValuesText(metrics);
|
|
240
|
-
content += '\n\n';
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
content += `**Duration**: ${getPrettyDuration(result.duration, target.inputs.duration)}\n\n`;
|
|
244
|
-
payload.content.push(content);
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
async function setTransactionContent({ result, target, payload }) {
|
|
248
|
-
let transaction_count = 0;
|
|
249
|
-
if (target.inputs.include_suites) {
|
|
250
|
-
for (let i = 0; i < result.transactions.length && transaction_count < target.inputs.max_suites; i++) {
|
|
251
|
-
const transaction = result.transactions[i];
|
|
252
|
-
if (target.inputs.only_failures && transaction.status !== 'FAIL') {
|
|
253
|
-
continue;
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
payload.content.push(getTransactionSummary({ target, transaction }));
|
|
257
|
-
transaction_count += 1;
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
function getTransactionSummary({ target, transaction }) {
|
|
263
|
-
let content = `### ${transaction.name}\n`;
|
|
264
|
-
content += `**Status**: ${transaction.status === 'PASS' ? STATUSES.GOOD : STATUSES.DANGER}\n`;
|
|
265
|
-
content += `**Duration**: ${getPrettyDuration(transaction.duration, target.inputs.duration)}\n`;
|
|
266
|
-
|
|
267
|
-
if (transaction.metrics && transaction.metrics.length > 0) {
|
|
268
|
-
const metrics = getValidMetrics(transaction.metrics);
|
|
269
|
-
if (metrics.length > 0) {
|
|
270
|
-
content += `**Metrics**: ${getMetricValuesText(metrics)}\n`;
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
content += '\n';
|
|
275
|
-
return content;
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
async function handleErrors({ target, errors }) {
|
|
279
|
-
logger.error('GitHub target errors:', errors);
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
const default_inputs = {
|
|
283
|
-
token: undefined,
|
|
284
|
-
comment_title: undefined,
|
|
285
|
-
update_comment: false,
|
|
286
|
-
owner: undefined,
|
|
287
|
-
repo: undefined,
|
|
288
|
-
pull_number: undefined,
|
|
289
|
-
title: undefined,
|
|
290
|
-
title_suffix: undefined,
|
|
291
|
-
title_link: undefined,
|
|
292
|
-
include_suites: true,
|
|
293
|
-
include_failure_details: false,
|
|
294
|
-
only_failures: false,
|
|
295
|
-
max_suites: 10,
|
|
296
|
-
duration: 'long',
|
|
297
|
-
publish: 'test-summary'
|
|
298
|
-
};
|
|
299
|
-
|
|
300
|
-
const default_options = {
|
|
301
|
-
condition: STATUS.PASS_OR_FAIL
|
|
302
|
-
};
|
|
303
|
-
|
|
304
|
-
module.exports = {
|
|
305
|
-
name: 'GitHub',
|
|
306
|
-
run,
|
|
307
|
-
handleErrors,
|
|
308
|
-
default_inputs,
|
|
309
|
-
default_options
|
|
310
|
-
};
|