testbeats 2.1.4 → 2.1.5

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "testbeats",
3
- "version": "2.1.4",
3
+ "version": "2.1.5",
4
4
  "description": "Publish test results to Microsoft Teams, Google Chat, Slack and InfluxDB",
5
5
  "main": "src/index.js",
6
6
  "types": "./src/index.d.ts",
@@ -61,6 +61,20 @@ class BeatsApi {
61
61
  }
62
62
  });
63
63
  }
64
+
65
+ /**
66
+ *
67
+ * @param {string} run_id
68
+ * @returns {import('./beats.types').IFailureAnalysisMetric[]}
69
+ */
70
+ getFailureAnalysis(run_id) {
71
+ return request.get({
72
+ url: `${this.getBaseUrl()}/api/core/v1/test-runs/${run_id}/failure-analysis`,
73
+ headers: {
74
+ 'x-api-key': this.config.api_key
75
+ }
76
+ });
77
+ }
64
78
  }
65
79
 
66
80
  module.exports = { BeatsApi }
@@ -147,11 +147,12 @@ class Beats {
147
147
  try {
148
148
  logger.info('🪄 Fetching Failure Analysis...');
149
149
  await this.#setTestRun('Failure Analysis Status', 'failure_analysis_status');
150
+ const metrics = await this.api.getFailureAnalysis(this.test_run_id);
150
151
  this.config.extensions.push({
151
152
  name: 'failure-analysis',
152
153
  hook: HOOK.AFTER_SUMMARY,
153
154
  inputs: {
154
- data: this.test_run
155
+ data: metrics
155
156
  }
156
157
  });
157
158
  } catch (error) {
@@ -9,12 +9,6 @@ export type IBeatExecutionMetric = {
9
9
  added: number
10
10
  removed: number
11
11
  flaky: number
12
- product_bugs: number
13
- environment_issues: number
14
- automation_bugs: number
15
- not_a_defects: number
16
- to_investigate: number
17
- auto_analysed: number
18
12
  failure_summary: any
19
13
  failure_summary_provider: any
20
14
  failure_summary_model: any
@@ -38,3 +32,9 @@ export type IErrorCluster = {
38
32
  failure: string
39
33
  count: number
40
34
  }
35
+
36
+ export type IFailureAnalysisMetric = {
37
+ id: string
38
+ name: string
39
+ count: number
40
+ }
@@ -14,7 +14,7 @@ const { MIN_NODE_VERSION } = require('../helpers/constants');
14
14
  class PublishCommand {
15
15
 
16
16
  /**
17
- * @param {import('../index').PublishOptions} opts
17
+ * @param {import('../index').CommandLineOptions} opts
18
18
  */
19
19
  constructor(opts) {
20
20
  this.opts = opts;
@@ -28,6 +28,7 @@ class PublishCommand {
28
28
  this.#buildConfig();
29
29
  this.#validateOptions();
30
30
  this.#setConfigFromFile();
31
+ this.#mergeConfigOptions();
31
32
  this.#processConfig();
32
33
  this.#validateConfig();
33
34
  this.#processResults();
@@ -76,6 +77,14 @@ class PublishCommand {
76
77
  }
77
78
  }
78
79
 
80
+ #mergeConfigOptions() {
81
+ if (this.opts.config && typeof this.opts.config === 'object') {
82
+ this.opts.config.project = this.opts.project || this.opts.config.project;
83
+ this.opts.config.run = this.opts.run || this.opts.config.run;
84
+ this.opts.config.api_key = this.opts['api-key'] || this.opts.config.api_key;
85
+ }
86
+ }
87
+
79
88
  #processConfig() {
80
89
  const processed_config = processData(this.opts.config);
81
90
  /**@type {import('../index').PublishConfig[]} */
@@ -149,6 +158,9 @@ class PublishCommand {
149
158
  throw new Error('targets must be an array');
150
159
  }
151
160
  for (const target of config.targets) {
161
+ if (target.enable === false || target.enable === 'false') {
162
+ continue;
163
+ }
152
164
  if (!target.name) {
153
165
  throw new Error(`'config.targets[*].name' is missing`);
154
166
  }
@@ -210,6 +222,9 @@ class PublishCommand {
210
222
  await beats.run(config, result);
211
223
  if (config.targets) {
212
224
  for (const target of config.targets) {
225
+ if (target.enable === false || target.enable === 'false') {
226
+ continue;
227
+ }
213
228
  target.extensions = target.extensions || [];
214
229
  target.extensions = config.extensions.concat(target.extensions);
215
230
  await target_manager.run(target, result);
@@ -5,8 +5,8 @@ class BaseExtension {
5
5
 
6
6
  /**
7
7
  *
8
- * @param {import('..').Target} target
9
- * @param {import('..').Extension} extension
8
+ * @param {import('..').ITarget} target
9
+ * @param {import('..').IExtension} extension
10
10
  * @param {import('..').TestResult} result
11
11
  * @param {any} payload
12
12
  * @param {any} root_payload
@@ -26,31 +26,32 @@ class FailureAnalysisExtension extends BaseExtension {
26
26
  }
27
27
 
28
28
  #setText() {
29
- const data = this.extension.inputs.data;
30
- if (!data) {
31
- return;
32
- }
33
-
34
29
  /**
35
- * @type {import('../beats/beats.types').IBeatExecutionMetric}
30
+ * @type {import('../beats/beats.types').IFailureAnalysisMetric[]}
36
31
  */
37
- const execution_metrics = data.execution_metrics[0];
38
-
39
- if (!execution_metrics) {
40
- logger.warn('⚠️ No execution metrics found. Skipping.');
32
+ const metrics = this.extension.inputs.data;
33
+ if (!metrics || metrics.length === 0) {
34
+ logger.warn('⚠️ No failure analysis metrics found. Skipping.');
41
35
  return;
42
36
  }
43
37
 
38
+ const to_investigate = metrics.find(metric => metric.name === 'To Investigate');
39
+ const auto_analysed = metrics.find(metric => metric.name === 'Auto Analysed');
40
+
44
41
  const failure_analysis = [];
45
42
 
46
- if (execution_metrics.to_investigate) {
47
- failure_analysis.push(`🔎 To Investigate: ${execution_metrics.to_investigate}`);
43
+ if (to_investigate && to_investigate.count > 0) {
44
+ failure_analysis.push(`🔎 To Investigate: ${to_investigate.count}`);
45
+ }
46
+ if (auto_analysed && auto_analysed.count > 0) {
47
+ failure_analysis.push(`🪄 Auto Analysed: ${auto_analysed.count}`);
48
48
  }
49
- if (execution_metrics.auto_analysed) {
50
- failure_analysis.push(`🪄 Auto Analysed: ${execution_metrics.auto_analysed}`);
49
+
50
+ if (failure_analysis.length === 0) {
51
+ return;
51
52
  }
52
53
 
53
- this.text = failure_analysis.join('  •  ');
54
+ this.text = failure_analysis.join('    ');
54
55
  }
55
56
 
56
57
  }
@@ -17,9 +17,15 @@ const { FailureAnalysisExtension } = require('./failure-analysis.extension');
17
17
 
18
18
  async function run(options) {
19
19
  const { target, result, hook } = options;
20
+ /**
21
+ * @type {import("..").IExtension[]}
22
+ */
20
23
  const extensions = target.extensions || [];
21
24
  for (let i = 0; i < extensions.length; i++) {
22
25
  const extension = extensions[i];
26
+ if (extension.enable === false || extension.enable === 'false') {
27
+ continue;
28
+ }
23
29
  const extension_runner = getExtensionRunner(extension, options);
24
30
  const extension_options = Object.assign({}, extension_runner.default_options, extension);
25
31
  if (extension_options.hook === hook) {
@@ -4,7 +4,7 @@ const { getTeamsMetaDataText, getSlackMetaDataText, getChatMetaDataText } = requ
4
4
 
5
5
  /**
6
6
  * @param {object} param0
7
- * @param {import('..').Target} param0.target
7
+ * @param {import('..').ITarget} param0.target
8
8
  * @param {import('..').MetadataExtension} param0.extension
9
9
  */
10
10
  async function run({ target, extension, result, payload, root_payload }) {
@@ -65,13 +65,13 @@ class SmartAnalysisExtension extends BaseExtension {
65
65
  for (const item of smart_analysis) {
66
66
  rows.push(item);
67
67
  if (rows.length === 3) {
68
- texts.push(rows.join('  •  '));
68
+ texts.push(rows.join('    '));
69
69
  rows.length = 0;
70
70
  }
71
71
  }
72
72
 
73
73
  if (rows.length > 0) {
74
- texts.push(rows.join('  •  '));
74
+ texts.push(rows.join('    '));
75
75
  }
76
76
 
77
77
  this.text = this.mergeTexts(texts);
@@ -3,9 +3,9 @@
3
3
  *
4
4
  * @param {object} param0 - the payload object
5
5
  * @param {object} param0.payload - the payload object
6
- * @param {import("..").Extension} param0.extension - the extension to add
6
+ * @param {import("..").IExtension} param0.extension - the extension to add
7
7
  * @param {string} param0.text - the text to include
8
- * @return {void}
8
+ * @return {void}
9
9
  */
10
10
  function addSlackExtension({ payload, extension, text }) {
11
11
  if (extension.inputs.separator) {
@@ -44,9 +44,9 @@ function addSlackExtension({ payload, extension, text }) {
44
44
  *
45
45
  * @param {object} param0 - the payload object
46
46
  * @param {object} param0.payload - the payload object
47
- * @param {import("..").Extension} param0.extension - the extension to add
47
+ * @param {import("..").IExtension} param0.extension - the extension to add
48
48
  * @param {string} param0.text - the text to include
49
- * @return {void}
49
+ * @return {void}
50
50
  */
51
51
  function addTeamsExtension({ payload, extension, text }) {
52
52
  if (extension.inputs.title) {
@@ -79,9 +79,9 @@ function addTeamsExtension({ payload, extension, text }) {
79
79
  *
80
80
  * @param {object} param0 - the payload object
81
81
  * @param {object} param0.payload - the payload object
82
- * @param {import("..").Extension} param0.extension - the extension to add
82
+ * @param {import("..").IExtension} param0.extension - the extension to add
83
83
  * @param {string} param0.text - the text to include
84
- * @return {void}
84
+ * @return {void}
85
85
  */
86
86
  function addChatExtension({ payload, extension, text }) {
87
87
  let updated_text = text;
@@ -18,8 +18,8 @@ function getMetaDataText(params) {
18
18
  *
19
19
  * @param {object} param0 - the payload object
20
20
  * @param {Object} param0.elements - The elements to generate metadata text from
21
- * @param {import('..').Target} param0.target - The result object
22
- * @param {import('..').Extension} param0.extension - The result object
21
+ * @param {import('..').ITarget} param0.target - The result object
22
+ * @param {import('..').IExtension} param0.extension - The result object
23
23
  * @param {Object} param0.result - The result object
24
24
  * @param {string} param0.default_condition - The default condition object
25
25
  * @return {string} The generated metadata text
@@ -50,8 +50,8 @@ async function getSlackMetaDataText({ elements, target, extension, result, defau
50
50
  *
51
51
  * @param {object} param0 - the payload object
52
52
  * @param {Object} param0.elements - The elements to generate metadata text from
53
- * @param {import('..').Target} param0.target - The result object
54
- * @param {import('..').Extension} param0.extension - The result object
53
+ * @param {import('..').ITarget} param0.target - The result object
54
+ * @param {import('..').IExtension} param0.extension - The result object
55
55
  * @param {Object} param0.result - The result object
56
56
  * @param {string} param0.default_condition - The default condition object
57
57
  * @return {string} The generated metadata text
@@ -82,8 +82,8 @@ async function getTeamsMetaDataText({ elements, target, extension, result, defau
82
82
  *
83
83
  * @param {object} param0 - the payload object
84
84
  * @param {Object} param0.elements - The elements to generate metadata text from
85
- * @param {import('..').Target} param0.target - The result object
86
- * @param {import('..').Extension} param0.extension - The result object
85
+ * @param {import('..').ITarget} param0.target - The result object
86
+ * @param {import('..').IExtension} param0.extension - The result object
87
87
  * @param {Object} param0.result - The result object
88
88
  * @param {string} param0.default_condition - The default condition object
89
89
  * @return {string} The generated metadata text
package/src/index.d.ts CHANGED
@@ -4,14 +4,30 @@ import { Schedule, User } from 'rosters';
4
4
  import { ParseOptions } from 'test-results-parser';
5
5
  import TestResult from 'test-results-parser/src/models/TestResult';
6
6
 
7
+ export interface ITarget {
8
+ name: TargetName;
9
+ enable?: string | boolean;
10
+ condition?: Condition;
11
+ inputs?: SlackInputs | TeamsInputs | ChatInputs | CustomTargetInputs | InfluxDBTargetInputs;
12
+ extensions?: IExtension[];
13
+ }
14
+
15
+ export interface IExtension {
16
+ name: ExtensionName;
17
+ enable?: string | boolean;
18
+ condition?: Condition;
19
+ hook?: Hook;
20
+ inputs?: ReportPortalAnalysisInputs | ReportPortalHistoryInputs | HyperlinkInputs | MentionInputs | QuickChartTestSummaryInputs | PercyAnalysisInputs | CustomExtensionInputs | MetadataInputs | CIInfoInputs | AIFailureSummaryInputs;
21
+ }
22
+
7
23
  export type ExtensionName = 'report-portal-analysis' | 'hyperlinks' | 'mentions' | 'report-portal-history' | 'quick-chart-test-summary' | 'metadata' | 'ci-info' | 'custom' | 'ai-failure-summary';
8
24
  export type Hook = 'start' | 'end' | 'after-summary';
9
25
  export type TargetName = 'slack' | 'teams' | 'chat' | 'custom' | 'delay';
10
26
  export type PublishReportType = 'test-summary' | 'test-summary-slim' | 'failure-details';
11
27
 
12
28
  export interface ConditionFunctionContext {
13
- target: Target;
14
- extension?: Extension,
29
+ target: ITarget;
30
+ extension?: IExtension,
15
31
  result: TestResult;
16
32
  }
17
33
  export type ConditionFunction = (ctx: ConditionFunctionContext) => boolean | Promise<boolean>;
@@ -67,12 +83,7 @@ export interface AIFailureSummaryInputs extends ExtensionInputs {
67
83
  failure_summary: string;
68
84
  }
69
85
 
70
- export interface Extension {
71
- name: ExtensionName;
72
- condition?: Condition;
73
- hook?: Hook;
74
- inputs?: ReportPortalAnalysisInputs | ReportPortalHistoryInputs | HyperlinkInputs | MentionInputs | QuickChartTestSummaryInputs | PercyAnalysisInputs | CustomExtensionInputs | MetadataInputs | CIInfoInputs | AIFailureSummaryInputs;
75
- }
86
+
76
87
 
77
88
  export interface PercyAnalysisInputs extends ExtensionInputs {
78
89
  url?: string;
@@ -90,14 +101,14 @@ export interface PercyAnalysisOutputs {
90
101
  project?: object;
91
102
  }
92
103
 
93
- export interface PercyAnalysisExtension extends Extension {
104
+ export interface PercyAnalysisExtension extends IExtension {
94
105
  inputs?: PercyAnalysisInputs;
95
106
  outputs?: PercyAnalysisOutputs;
96
107
  }
97
108
 
98
109
  export interface CustomExtensionFunctionContext {
99
- target: Target;
100
- extension: Extension,
110
+ target: ITarget;
111
+ extension: IExtension,
101
112
  result: TestResult;
102
113
  payload: any;
103
114
  root_payload: any;
@@ -109,13 +120,13 @@ export interface CustomExtensionInputs extends ExtensionInputs {
109
120
  load: string | CustomExtensionFunction;
110
121
  }
111
122
 
112
- export interface CustomExtension extends Extension {
123
+ export interface CustomExtension extends IExtension {
113
124
  inputs?: CustomExtensionInputs;
114
125
  outputs?: any;
115
126
  }
116
127
 
117
128
  export interface LinkUrlFunctionContext {
118
- target: Target;
129
+ target: ITarget;
119
130
  extension: HyperlinksExtension,
120
131
  result: TestResult;
121
132
  }
@@ -132,7 +143,7 @@ export interface HyperlinkInputs extends ExtensionInputs {
132
143
  links: Link[];
133
144
  }
134
145
 
135
- export interface HyperlinksExtension extends Extension {
146
+ export interface HyperlinksExtension extends IExtension {
136
147
  inputs?: HyperlinkInputs;
137
148
  }
138
149
 
@@ -148,7 +159,7 @@ export interface MetadataInputs extends ExtensionInputs {
148
159
  data?: Metadata[];
149
160
  }
150
161
 
151
- export interface MetadataExtension extends Extension {
162
+ export interface MetadataExtension extends IExtension {
152
163
  inputs?: MetadataInputs;
153
164
  }
154
165
 
@@ -202,7 +213,7 @@ export interface InfluxDBTargetInputs {
202
213
  }
203
214
 
204
215
  export interface CustomTargetFunctionContext {
205
- target: Target;
216
+ target: ITarget;
206
217
  result: TestResult;
207
218
  }
208
219
 
@@ -212,12 +223,7 @@ export interface CustomTargetInputs {
212
223
  load: string | CustomTargetFunction;
213
224
  }
214
225
 
215
- export interface Target {
216
- name: TargetName;
217
- condition?: Condition;
218
- inputs?: SlackInputs | TeamsInputs | ChatInputs | CustomTargetInputs | InfluxDBTargetInputs;
219
- extensions?: Extension[];
220
- }
226
+
221
227
 
222
228
  export interface CustomResultOptions {
223
229
  type: string;
@@ -232,8 +238,8 @@ export interface PublishReport {
232
238
  show_failure_analysis?: boolean;
233
239
  show_smart_analysis?: boolean;
234
240
  show_error_clusters?: boolean;
235
- targets?: Target[];
236
- extensions?: Extension[];
241
+ targets?: ITarget[];
242
+ extensions?: IExtension[];
237
243
  results?: ParseOptions[] | PerformanceParseOptions[] | CustomResultOptions[];
238
244
  }
239
245
 
@@ -241,8 +247,8 @@ export interface PublishConfig {
241
247
  api_key?: string;
242
248
  project?: string;
243
249
  run?: string;
244
- targets?: Target[];
245
- extensions?: Extension[];
250
+ targets?: ITarget[];
251
+ extensions?: IExtension[];
246
252
  results?: ParseOptions[] | PerformanceParseOptions[] | CustomResultOptions[];
247
253
  }
248
254
 
@@ -251,7 +257,7 @@ export interface PublishOptions {
251
257
  }
252
258
 
253
259
  export interface CommandLineOptions {
254
- config?: string;
260
+ config?: string | PublishConfig;
255
261
  project?: string;
256
262
  run?: string;
257
263
  'api-key'?: string;
@@ -15,7 +15,7 @@ class BasePlatform {
15
15
 
16
16
  /**
17
17
  *
18
- * @param {import('..').Target} target
18
+ * @param {import('..').ITarget} target
19
19
  * @param {import('test-results-parser').ITestSuite} suite
20
20
  */
21
21
  getSuiteSummaryText(target, suite) {
@@ -74,7 +74,7 @@ class BasePlatform {
74
74
 
75
75
  /**
76
76
  *
77
- * @param {import('..').Target} target
77
+ * @param {import('..').ITarget} target
78
78
  * @param {import('test-results-parser').ITestSuite} suite
79
79
  */
80
80
  #getSuiteDurationText(target, suite) {
@@ -84,7 +84,7 @@ class BasePlatform {
84
84
 
85
85
  /**
86
86
  *
87
- * @param {import('..').Target} target
87
+ * @param {import('..').ITarget} target
88
88
  * @param {import('test-results-parser').ITestSuite} suite
89
89
  */
90
90
  getSuiteDuration(target, suite) {
@@ -2,9 +2,9 @@ const path = require('path');
2
2
  const { STATUS } = require('../helpers/constants');
3
3
 
4
4
  /**
5
- *
6
- * @param {object} param0
7
- * @param {import('../index').Target} param0.target
5
+ *
6
+ * @param {object} param0
7
+ * @param {import('../index').ITarget} param0.target
8
8
  */
9
9
  async function run({result, target}) {
10
10
  if (typeof target.inputs.load === 'string') {
@@ -10,10 +10,10 @@ const TestSuite = require('test-results-parser/src/models/TestSuite');
10
10
  const { STATUS } = require('../helpers/constants');
11
11
 
12
12
  /**
13
- *
14
- * @param {object} param0
13
+ *
14
+ * @param {object} param0
15
15
  * @param {PerformanceTestResult | TestResult} param0.result
16
- * @param {import('..').Target} param0.target
16
+ * @param {import('..').ITarget} param0.target
17
17
  */
18
18
  async function run({ result, target }) {
19
19
  target.inputs = Object.assign({}, default_inputs, target.inputs);
@@ -34,10 +34,10 @@ async function run({ result, target }) {
34
34
  }
35
35
 
36
36
  /**
37
- *
38
- * @param {object} param0
37
+ *
38
+ * @param {object} param0
39
39
  * @param {PerformanceTestResult | TestResult} param0.result
40
- * @param {import('..').Target} param0.target
40
+ * @param {import('..').ITarget} param0.target
41
41
  */
42
42
  function getMetrics({ result, target }) {
43
43
  const influx_metrics = [];
@@ -60,10 +60,10 @@ function getMetrics({ result, target }) {
60
60
  }
61
61
 
62
62
  /**
63
- * @param {object} param0
63
+ * @param {object} param0
64
64
  * @param {PerformanceTestResult} param0.result
65
- * @param {import('..').Target} param0.target
66
- * @returns
65
+ * @param {import('..').ITarget} param0.target
66
+ * @returns
67
67
  */
68
68
  function getPerfRunInfluxMetric({ result, target }) {
69
69
  const tags = Object.assign({}, target.inputs.tags);
@@ -88,8 +88,8 @@ function getPerfRunInfluxMetric({ result, target }) {
88
88
  }
89
89
 
90
90
  /**
91
- *
92
- * @param {Metric} metric
91
+ *
92
+ * @param {Metric} metric
93
93
  */
94
94
  function setPerfInfluxMetricFields(metric, fields) {
95
95
  let name = metric.name;
@@ -110,8 +110,8 @@ function setPerfInfluxMetricFields(metric, fields) {
110
110
  }
111
111
 
112
112
  /**
113
- *
114
- * @param {Transaction} transaction
113
+ *
114
+ * @param {Transaction} transaction
115
115
  */
116
116
  function getTransactionInfluxMetric(transaction, target) {
117
117
  const tags = Object.assign({}, target.inputs.tags);
@@ -133,10 +133,10 @@ function getTransactionInfluxMetric(transaction, target) {
133
133
  }
134
134
 
135
135
  /**
136
- * @param {object} param0
136
+ * @param {object} param0
137
137
  * @param {TestResult | TestSuite} param0.result
138
- * @param {import('..').Target} param0.target
139
- * @returns
138
+ * @param {import('..').ITarget} param0.target
139
+ * @returns
140
140
  */
141
141
  function getTestInfluxMetric({ result, target }, measurement) {
142
142
  const tags = Object.assign({}, target.inputs.tags);
@@ -158,10 +158,10 @@ function getTestInfluxMetric({ result, target }, measurement) {
158
158
  }
159
159
 
160
160
  /**
161
- * @param {object} param0
161
+ * @param {object} param0
162
162
  * @param {TestCase} param0.result
163
- * @param {import('..').Target} param0.target
164
- * @returns
163
+ * @param {import('..').ITarget} param0.target
164
+ * @returns
165
165
  */
166
166
  function getTestCaseInfluxMetric({ result, target }) {
167
167
  const tags = Object.assign({}, target.inputs.tags);