testbeats 2.5.0 → 2.7.0
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 +1 -1
- package/src/beats/beats.api.js +14 -0
- package/src/beats/beats.js +33 -4
- package/src/beats/beats.types.d.ts +8 -0
- package/src/commands/generate-config.command.js +2 -1
- package/src/extensions/failure-signatures.extension.js +43 -0
- package/src/extensions/index.js +3 -0
- package/src/helpers/constants.js +1 -0
- package/src/index.d.ts +23 -2
package/package.json
CHANGED
package/src/beats/beats.api.js
CHANGED
|
@@ -71,6 +71,20 @@ class BeatsApi {
|
|
|
71
71
|
});
|
|
72
72
|
}
|
|
73
73
|
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* @param {string} run_id
|
|
77
|
+
* @returns {import('./beats.types').IFailureSignature[]}
|
|
78
|
+
*/
|
|
79
|
+
getFailureSignatures(run_id) {
|
|
80
|
+
return request.get({
|
|
81
|
+
url: `${this.getBaseUrl()}/api/core/v1/automation/test-run-executions/${run_id}/failure-signatures`,
|
|
82
|
+
headers: {
|
|
83
|
+
'x-api-key': this.config.api_key
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
|
|
74
88
|
/**
|
|
75
89
|
*
|
|
76
90
|
* @param {string} run_id
|
package/src/beats/beats.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
const { getCIInformation } = require('../helpers/ci');
|
|
2
2
|
const logger = require('../utils/logger');
|
|
3
3
|
const { BeatsApi } = require('./beats.api');
|
|
4
|
-
const { HOOK, PROCESS_STATUS } = require('../helpers/constants');
|
|
4
|
+
const { HOOK, PROCESS_STATUS, EXTENSION } = require('../helpers/constants');
|
|
5
5
|
const TestResult = require('test-results-parser/src/models/TestResult');
|
|
6
6
|
const { BeatsAttachments } = require('./beats.attachments');
|
|
7
7
|
|
|
@@ -55,6 +55,9 @@ class Beats {
|
|
|
55
55
|
if (this.ci) {
|
|
56
56
|
payload.ci_details = [this.ci];
|
|
57
57
|
}
|
|
58
|
+
if (this.config.metadata) {
|
|
59
|
+
payload.metadata = Object.assign(this.result.metadata || {}, this.config.metadata);
|
|
60
|
+
}
|
|
58
61
|
return payload;
|
|
59
62
|
}
|
|
60
63
|
|
|
@@ -96,6 +99,7 @@ class Beats {
|
|
|
96
99
|
await this.#attachFailureSummary();
|
|
97
100
|
await this.#attachFailureAnalysis();
|
|
98
101
|
await this.#attachSmartAnalysis();
|
|
102
|
+
await this.#attachFailureSignatures();
|
|
99
103
|
await this.#attachErrorClusters();
|
|
100
104
|
}
|
|
101
105
|
|
|
@@ -166,6 +170,30 @@ class Beats {
|
|
|
166
170
|
}
|
|
167
171
|
}
|
|
168
172
|
|
|
173
|
+
async #attachFailureSignatures() {
|
|
174
|
+
if (this.result.status !== 'FAIL') {
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
if (this.config.show_failure_signatures === false) {
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
try {
|
|
181
|
+
logger.info('🔍 Fetching Failure Signatures...');
|
|
182
|
+
const signatures = await this.api.getFailureSignatures(this.test_run_id);
|
|
183
|
+
this.config.extensions.push({
|
|
184
|
+
name: EXTENSION.FAILURE_SIGNATURES,
|
|
185
|
+
hook: HOOK.AFTER_SUMMARY,
|
|
186
|
+
order: 400,
|
|
187
|
+
inputs: {
|
|
188
|
+
data: signatures
|
|
189
|
+
}
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
catch (error) {
|
|
193
|
+
logger.error(`❌ Unable to attach failure signatures: ${error.message}`, error);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
169
197
|
#getDelay() {
|
|
170
198
|
if (process.env.TEST_BEATS_DELAY) {
|
|
171
199
|
return parseInt(process.env.TEST_BEATS_DELAY);
|
|
@@ -200,10 +228,11 @@ class Beats {
|
|
|
200
228
|
}
|
|
201
229
|
|
|
202
230
|
async #attachErrorClusters() {
|
|
203
|
-
|
|
231
|
+
// Legacy feature: only attach if explicitly enabled
|
|
232
|
+
if (this.config.show_error_clusters !== true) {
|
|
204
233
|
return;
|
|
205
234
|
}
|
|
206
|
-
if (this.
|
|
235
|
+
if (this.result.status !== 'FAIL') {
|
|
207
236
|
return;
|
|
208
237
|
}
|
|
209
238
|
try {
|
|
@@ -212,7 +241,7 @@ class Beats {
|
|
|
212
241
|
this.config.extensions.push({
|
|
213
242
|
name: 'error-clusters',
|
|
214
243
|
hook: HOOK.AFTER_SUMMARY,
|
|
215
|
-
order:
|
|
244
|
+
order: 401,
|
|
216
245
|
inputs: {
|
|
217
246
|
data: res.values
|
|
218
247
|
}
|
|
@@ -432,7 +432,8 @@ class GenerateConfigCommand {
|
|
|
432
432
|
{ title: 'Metadata', value: 'metadata' },
|
|
433
433
|
{ title: 'AI Failure Summary', value: 'ai-failure-summary' },
|
|
434
434
|
{ title: 'Smart Analysis', value: 'smart-analysis' },
|
|
435
|
-
{ title: '
|
|
435
|
+
{ title: 'Failure Signatures', value: 'failure-signatures' },
|
|
436
|
+
{ title: 'Error Clusters (Legacy)', value: 'error-clusters' }
|
|
436
437
|
];
|
|
437
438
|
}
|
|
438
439
|
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
const { BaseExtension } = require('./base.extension');
|
|
2
|
+
const { STATUS, HOOK } = require("../helpers/constants");
|
|
3
|
+
const { truncate } = require('../helpers/helper');
|
|
4
|
+
|
|
5
|
+
class FailureSignaturesExtension extends BaseExtension {
|
|
6
|
+
|
|
7
|
+
constructor(target, extension, result, payload, root_payload) {
|
|
8
|
+
super(target, extension, result, payload, root_payload);
|
|
9
|
+
this.#setDefaultOptions();
|
|
10
|
+
this.#setDefaultInputs();
|
|
11
|
+
this.updateExtensionInputs();
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
run() {
|
|
15
|
+
this.#setText();
|
|
16
|
+
this.attach();
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
#setDefaultOptions() {
|
|
20
|
+
this.default_options.hook = HOOK.AFTER_SUMMARY,
|
|
21
|
+
this.default_options.condition = STATUS.PASS_OR_FAIL;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
#setDefaultInputs() {
|
|
25
|
+
this.default_inputs.title = 'Top Failures';
|
|
26
|
+
this.default_inputs.title_link = '';
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
#setText() {
|
|
30
|
+
const signatures = this.extension.inputs.data;
|
|
31
|
+
if (!signatures || !signatures.length) {
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const texts = [];
|
|
36
|
+
for (const signature of signatures) {
|
|
37
|
+
texts.push(`${truncate(signature.signature, 150)} - ${this.bold(`(x${signature.count})`)}`);
|
|
38
|
+
}
|
|
39
|
+
this.text = this.platform.bullets(texts);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
module.exports = { FailureSignaturesExtension }
|
package/src/extensions/index.js
CHANGED
|
@@ -13,6 +13,7 @@ const { EXTENSION } = require('../helpers/constants');
|
|
|
13
13
|
const { checkCondition } = require('../helpers/helper');
|
|
14
14
|
const logger = require('../utils/logger');
|
|
15
15
|
const { ErrorClustersExtension } = require('./error-clusters.extension');
|
|
16
|
+
const { FailureSignaturesExtension } = require('./failure-signatures.extension');
|
|
16
17
|
const { FailureAnalysisExtension } = require('./failure-analysis.extension');
|
|
17
18
|
const { BrowserstackExtension } = require('./browserstack.extension');
|
|
18
19
|
|
|
@@ -75,6 +76,8 @@ function getExtensionRunner(extension, options) {
|
|
|
75
76
|
return new ErrorClustersExtension(options.target, extension, options.result, options.payload, options.root_payload);
|
|
76
77
|
case EXTENSION.BROWSERSTACK:
|
|
77
78
|
return new BrowserstackExtension(options.target, extension, options.result, options.payload, options.root_payload);
|
|
79
|
+
case EXTENSION.FAILURE_SIGNATURES:
|
|
80
|
+
return new FailureSignaturesExtension(options.target, extension, options.result, options.payload, options.root_payload);
|
|
78
81
|
default:
|
|
79
82
|
return require(extension.name);
|
|
80
83
|
}
|
package/src/helpers/constants.js
CHANGED
|
@@ -27,6 +27,7 @@ const EXTENSION = Object.freeze({
|
|
|
27
27
|
FAILURE_ANALYSIS: 'failure-analysis',
|
|
28
28
|
SMART_ANALYSIS: 'smart-analysis',
|
|
29
29
|
ERROR_CLUSTERS: 'error-clusters',
|
|
30
|
+
FAILURE_SIGNATURES: 'failure-signatures',
|
|
30
31
|
BROWSERSTACK: 'browserstack',
|
|
31
32
|
HYPERLINKS: 'hyperlinks',
|
|
32
33
|
MENTIONS: 'mentions',
|
package/src/index.d.ts
CHANGED
|
@@ -20,10 +20,10 @@ export interface IExtension {
|
|
|
20
20
|
condition?: Condition;
|
|
21
21
|
hook?: Hook;
|
|
22
22
|
order?: number;
|
|
23
|
-
inputs?: ReportPortalAnalysisInputs | ReportPortalHistoryInputs | HyperlinkInputs | MentionInputs | QuickChartTestSummaryInputs | PercyAnalysisInputs | CustomExtensionInputs | MetadataInputs | CIInfoInputs | AIFailureSummaryInputs | BrowserstackInputs;
|
|
23
|
+
inputs?: ReportPortalAnalysisInputs | ReportPortalHistoryInputs | HyperlinkInputs | MentionInputs | QuickChartTestSummaryInputs | PercyAnalysisInputs | CustomExtensionInputs | MetadataInputs | CIInfoInputs | AIFailureSummaryInputs | BrowserstackInputs | FailureSignaturesInputs | ErrorClustersInputs;
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
export type ExtensionName = 'report-portal-analysis' | 'hyperlinks' | 'mentions' | 'report-portal-history' | 'quick-chart-test-summary' | 'metadata' | 'ci-info' | 'custom' | 'ai-failure-summary';
|
|
26
|
+
export type ExtensionName = 'report-portal-analysis' | 'hyperlinks' | 'mentions' | 'report-portal-history' | 'quick-chart-test-summary' | 'metadata' | 'ci-info' | 'custom' | 'ai-failure-summary' | 'failure-signatures' | 'error-clusters';
|
|
27
27
|
export type Hook = 'start' | 'end' | 'after-summary';
|
|
28
28
|
export type TargetName = 'slack' | 'teams' | 'chat' | 'github' | 'github-output' | 'custom' | 'delay';
|
|
29
29
|
export type PublishReportType = 'test-summary' | 'test-summary-slim' | 'failure-details';
|
|
@@ -86,6 +86,23 @@ export interface AIFailureSummaryInputs extends ExtensionInputs {
|
|
|
86
86
|
failure_summary: string;
|
|
87
87
|
}
|
|
88
88
|
|
|
89
|
+
export interface FailureSignaturesInputs extends ExtensionInputs {
|
|
90
|
+
data?: Array<{
|
|
91
|
+
id: string;
|
|
92
|
+
signature: string;
|
|
93
|
+
failure_type: string;
|
|
94
|
+
count: number;
|
|
95
|
+
}>;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export interface ErrorClustersInputs extends ExtensionInputs {
|
|
99
|
+
data?: Array<{
|
|
100
|
+
test_failure_id: string;
|
|
101
|
+
failure: string;
|
|
102
|
+
count: number;
|
|
103
|
+
}>;
|
|
104
|
+
}
|
|
105
|
+
|
|
89
106
|
export interface BrowserStackAutomationBuild {
|
|
90
107
|
name: string;
|
|
91
108
|
hashed_id: string;
|
|
@@ -305,10 +322,13 @@ export interface PublishReport {
|
|
|
305
322
|
api_key?: string;
|
|
306
323
|
project?: string;
|
|
307
324
|
run?: string;
|
|
325
|
+
metadata?: Record<string, string>;
|
|
308
326
|
show_failure_summary?: boolean;
|
|
309
327
|
show_failure_analysis?: boolean;
|
|
310
328
|
show_smart_analysis?: boolean;
|
|
329
|
+
/** @deprecated Use show_failure_signatures instead. Legacy feature - only enabled when explicitly set to true. */
|
|
311
330
|
show_error_clusters?: boolean;
|
|
331
|
+
show_failure_signatures?: boolean;
|
|
312
332
|
targets?: ITarget[];
|
|
313
333
|
extensions?: IExtension[];
|
|
314
334
|
results?: ParseOptions[] | PerformanceParseOptions[] | CustomResultOptions[];
|
|
@@ -318,6 +338,7 @@ export interface PublishConfig {
|
|
|
318
338
|
api_key?: string;
|
|
319
339
|
project?: string;
|
|
320
340
|
run?: string;
|
|
341
|
+
metadata?: Record<string, string>;
|
|
321
342
|
targets?: ITarget[];
|
|
322
343
|
extensions?: IExtension[];
|
|
323
344
|
results?: ParseOptions[] | PerformanceParseOptions[] | CustomResultOptions[];
|