testbeats 2.0.4 → 2.0.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/README.md CHANGED
@@ -13,6 +13,8 @@
13
13
  ![Downloads](https://img.shields.io/npm/dt/test-results-reporter?logo=npm&label=downloads-old)
14
14
  ![Downloads](https://img.shields.io/npm/dt/testbeats?logo=npm)
15
15
  ![Size](https://img.shields.io/bundlephobia/minzip/testbeats)
16
+ ![NodeJs](https://img.shields.io/badge/NodeJS-%3E%3D14.0.0-brightgreen?logo=node.js)
17
+
16
18
 
17
19
  [![Stars](https://img.shields.io/github/stars/test-results-reporter/testbeats?style=social)](https://github.com/test-results-reporter/testbeats/stargazers)
18
20
  ![Downloads](https://img.shields.io/github/downloads/test-results-reporter/testbeats/total?logo=github)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "testbeats",
3
- "version": "2.0.4",
3
+ "version": "2.0.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",
@@ -64,5 +64,9 @@
64
64
  "mocha-multi-reporters": "^1.5.1",
65
65
  "pactum": "^3.2.3",
66
66
  "pkg": "^5.8.0"
67
- }
67
+ },
68
+ "engines": {
69
+ "node": ">=14.0.0"
70
+ },
71
+ "engineStrict": true
68
72
  }
@@ -16,6 +16,7 @@ class Beats {
16
16
  this.result = result;
17
17
  this.api = new BeatsApi(config);
18
18
  this.test_run_id = '';
19
+ this.test_run = null;
19
20
  }
20
21
 
21
22
  async publish() {
@@ -27,6 +28,7 @@ class Beats {
27
28
  await this.#uploadAttachments();
28
29
  this.#updateTitleLink();
29
30
  await this.#attachFailureSummary();
31
+ await this.#attachSmartAnalysis();
30
32
  }
31
33
 
32
34
  #setCIInfo() {
@@ -47,7 +49,7 @@ class Beats {
47
49
 
48
50
  async #publishTestResults() {
49
51
  if (!this.config.api_key) {
50
- logger.warn('😿 No API key provided, skipping publishing results to TestBeats Portal');
52
+ logger.warn('😿 No API key provided, skipping publishing results to TestBeats Portal...');
51
53
  return;
52
54
  }
53
55
  logger.info("🚀 Publishing results to TestBeats Portal...");
@@ -125,55 +127,77 @@ class Beats {
125
127
  if (this.config.show_failure_summary === false) {
126
128
  return;
127
129
  }
128
- const text = await this.#getFailureSummary();
129
- if (!text) {
130
+ try {
131
+ logger.info('✨ Fetching AI Failure Summary...');
132
+ await this.#setTestRun(' AI Failure Summary', 'failure_summary_status');
133
+ this.config.extensions.push({
134
+ name: 'ai-failure-summary',
135
+ hook: HOOK.AFTER_SUMMARY,
136
+ inputs: {
137
+ data: this.test_run
138
+ }
139
+ });
140
+ } catch (error) {
141
+ logger.error(`❌ Unable to attach failure summary: ${error.message}`, error);
142
+ }
143
+ }
144
+
145
+ async #attachSmartAnalysis() {
146
+ if (!this.test_run_id) {
130
147
  return;
131
148
  }
132
- const extension = this.#getAIFailureSummaryExtension(text);
133
- for (const target of this.config.targets) {
134
- target.extensions = target.extensions || [];
135
- target.extensions.push(extension);
149
+ if (!this.config.targets) {
150
+ return;
151
+ }
152
+ if (this.config.show_smart_analysis === false) {
153
+ return;
154
+ }
155
+ try {
156
+ logger.info('🤓 Fetching Smart Analysis...');
157
+ await this.#setTestRun('Smart Analysis', 'smart_analysis_status');
158
+ this.config.extensions.push({
159
+ name: 'smart-analysis',
160
+ hook: HOOK.AFTER_SUMMARY,
161
+ inputs: {
162
+ data: this.test_run
163
+ }
164
+ });
165
+ } catch (error) {
166
+ logger.error(`❌ Unable to attach smart analysis: ${error.message}`, error);
167
+ }
168
+ }
169
+
170
+ #getDelay() {
171
+ if (process.env.TEST_BEATS_DELAY) {
172
+ return parseInt(process.env.TEST_BEATS_DELAY);
136
173
  }
174
+ return 3000;
137
175
  }
138
176
 
139
- async #getFailureSummary() {
140
- logger.info('✨ Fetching AI Failure Summary...');
177
+ async #setTestRun(text, wait_for = 'smart_analysis_status') {
178
+ if (this.test_run && this.test_run[wait_for] === 'COMPLETED') {
179
+ return;
180
+ }
141
181
  let retry = 3;
142
182
  while (retry >= 0) {
143
183
  retry = retry - 1;
144
184
  await new Promise(resolve => setTimeout(resolve, this.#getDelay()));
145
- const test_run = await this.api.getTestRun(this.test_run_id);
146
- const status = test_run && test_run.failure_summary_status;
185
+ this.test_run = await this.api.getTestRun(this.test_run_id);
186
+ const status = this.test_run && this.test_run[wait_for];
147
187
  switch (status) {
148
188
  case 'COMPLETED':
149
- return test_run.execution_metrics[0].failure_summary;
189
+ logger.debug(`☑️ ${text} generated successfully`);
190
+ return;
150
191
  case 'FAILED':
151
- logger.error(`❌ Failed to generate AI Failure Summary`);
192
+ logger.error(`❌ Failed to generate ${text}`);
152
193
  return;
153
194
  case 'SKIPPED':
154
- logger.warn(`❗ Skipped generating AI Failure Summary`);
195
+ logger.warn(`❗ Skipped generating ${text}`);
155
196
  return;
156
197
  }
157
- logger.info(`🔄 AI Failure Summary not generated, retrying...`);
158
- }
159
- logger.warn(`🙈 AI Failure Summary not generated in given time`);
160
- }
161
-
162
- #getDelay() {
163
- if (process.env.TEST_BEATS_DELAY) {
164
- return parseInt(process.env.TEST_BEATS_DELAY);
198
+ logger.info(`🔄 ${text} not generated, retrying...`);
165
199
  }
166
- return 3000;
167
- }
168
-
169
- #getAIFailureSummaryExtension(text) {
170
- return {
171
- name: 'ai-failure-summary',
172
- hook: HOOK.AFTER_SUMMARY,
173
- inputs: {
174
- failure_summary: text
175
- }
176
- };
200
+ logger.warn(`🙈 ${text} not generated in given time`);
177
201
  }
178
202
 
179
203
  }
@@ -0,0 +1,18 @@
1
+ export type IBeatExecutionMetric = {
2
+ id: string
3
+ created_at: string
4
+ updated_at: string
5
+ newly_failed: number
6
+ always_failing: number
7
+ recovered: number
8
+ added: number
9
+ removed: number
10
+ flaky: number
11
+ failure_summary: any
12
+ failure_summary_provider: any
13
+ failure_summary_model: any
14
+ status: string
15
+ status_message: any
16
+ test_run_id: string
17
+ org_id: string
18
+ }
package/src/cli.js CHANGED
@@ -11,6 +11,9 @@ prog
11
11
  .version('2.0.4')
12
12
  .option('-c, --config', 'path to config file')
13
13
  .option('-l, --logLevel', 'Log Level', "INFO")
14
+ .option('--api-key', 'api key')
15
+ .option('--project', 'project name')
16
+ .option('--run', 'run name')
14
17
  .option('--slack', 'slack webhook url')
15
18
  .option('--teams', 'teams webhook url')
16
19
  .option('--chat', 'chat webhook url')
@@ -1,6 +1,7 @@
1
1
  const path = require('path');
2
2
  const trp = require('test-results-parser');
3
3
  const prp = require('performance-results-parser');
4
+ const os = require('os');
4
5
 
5
6
  const beats = require('../beats');
6
7
  const { ConfigBuilder } = require('../utils/config.builder');
@@ -8,6 +9,7 @@ const target_manager = require('../targets');
8
9
  const logger = require('../utils/logger');
9
10
  const { processData } = require('../helpers/helper');
10
11
  const pkg = require('../../package.json');
12
+ const { MIN_NODE_VERSION } = require('../helpers/constants');
11
13
 
12
14
  class PublishCommand {
13
15
 
@@ -20,6 +22,8 @@ class PublishCommand {
20
22
 
21
23
  async publish() {
22
24
  logger.info(`🥁 TestBeats v${pkg.version}`);
25
+
26
+ this.#validateEnvDetails();
23
27
  this.#buildConfig();
24
28
  this.#validateOptions();
25
29
  this.#setConfigFromFile();
@@ -30,6 +34,20 @@ class PublishCommand {
30
34
  logger.info('✅ Results published successfully!');
31
35
  }
32
36
 
37
+ #validateEnvDetails() {
38
+ try {
39
+ const current_major_version = parseInt(process.version.split('.')[0].replace('v', ''));
40
+ if (current_major_version >= MIN_NODE_VERSION) {
41
+ logger.info(`💻 NodeJS: ${process.version}, OS: ${os.platform()}, Version: ${os.release()}, Arch: ${os.machine()}`);
42
+ return;
43
+ }
44
+ } catch (error) {
45
+ logger.warn(`⚠️ Unable to verify NodeJS version: ${error.message}`);
46
+ return;
47
+ }
48
+ throw new Error(`❌ Supported NodeJS version is >= v${MIN_NODE_VERSION}. Current version is ${process.version}`)
49
+ }
50
+
33
51
  #buildConfig() {
34
52
  const config_builder = new ConfigBuilder(this.opts);
35
53
  config_builder.build();
@@ -52,8 +70,7 @@ class PublishCommand {
52
70
  const config_json = require(path.join(cwd, this.opts.config));
53
71
  this.opts.config = config_json;
54
72
  } catch (error) {
55
- logger.error({ error }, `Failed to read config file: '${file_path}' with error: '${error.message}'`);
56
- throw new Error(`Config file not found: ${file_path}`);
73
+ throw new Error(`Failed to read config file: '${file_path}' with error: '${error.message}'`);
57
74
  }
58
75
  }
59
76
  }
@@ -63,7 +80,7 @@ class PublishCommand {
63
80
  /**@type {import('../index').PublishConfig[]} */
64
81
  this.configs = [];
65
82
  if (processed_config.reports) {
66
- for (const report of config.reports) {
83
+ for (const report of processed_config.reports) {
67
84
  this.configs.push(report);
68
85
  }
69
86
  } else {
@@ -72,7 +89,7 @@ class PublishCommand {
72
89
  }
73
90
 
74
91
  #validateConfig() {
75
- logger.info("🛠️ Validating configuration...")
92
+ logger.info("🚓 Validating configuration...")
76
93
  for (const config of this.configs) {
77
94
  this.#validateResults(config);
78
95
  this.#validateTargets(config);
@@ -177,12 +194,12 @@ class PublishCommand {
177
194
  for (const config of this.configs) {
178
195
  for (let i = 0; i < this.results.length; i++) {
179
196
  const result = this.results[i];
180
- const global_extensions = config.extensions || [];
197
+ config.extensions = config.extensions || [];
181
198
  await beats.run(config, result);
182
199
  if (config.targets) {
183
200
  for (const target of config.targets) {
184
201
  target.extensions = target.extensions || [];
185
- target.extensions = global_extensions.concat(target.extensions);
202
+ target.extensions = config.extensions.concat(target.extensions);
186
203
  await target_manager.run(target, result);
187
204
  }
188
205
  } else {
@@ -194,4 +211,4 @@ class PublishCommand {
194
211
 
195
212
  }
196
213
 
197
- module.exports = { PublishCommand }
214
+ module.exports = { PublishCommand }
@@ -0,0 +1,43 @@
1
+ const { BaseExtension } = require('./base.extension');
2
+ const { STATUS, HOOK } = require("../helpers/constants");
3
+
4
+
5
+ class AIFailureSummaryExtension 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 = 'AI Failure Summary ✨';
26
+ this.default_inputs.title_link = '';
27
+ }
28
+
29
+ #setText() {
30
+ const data = this.extension.inputs.data;
31
+ if (!data) {
32
+ return;
33
+ }
34
+
35
+ /**
36
+ * @type {import('../beats/beats.types').IBeatExecutionMetric}
37
+ */
38
+ const execution_metrics = data.execution_metrics[0];
39
+ this.text = execution_metrics.failure_summary;
40
+ }
41
+ }
42
+
43
+ module.exports = { AIFailureSummaryExtension }
@@ -0,0 +1,74 @@
1
+ const logger = require('../utils/logger');
2
+ const { addChatExtension, addSlackExtension, addTeamsExtension } = require('../helpers/extension.helper');
3
+
4
+ class BaseExtension {
5
+
6
+ /**
7
+ *
8
+ * @param {import('..').Target} target
9
+ * @param {import('..').Extension} extension
10
+ * @param {import('..').TestResult} result
11
+ * @param {any} payload
12
+ * @param {any} root_payload
13
+ */
14
+ constructor(target, extension, result, payload, root_payload) {
15
+ this.target = target;
16
+ this.extension = extension;
17
+ this.result = result;
18
+ this.payload = payload;
19
+ this.root_payload = root_payload;
20
+
21
+ this.text = '';
22
+
23
+ /**
24
+ * @type {import('..').ExtensionInputs}
25
+ */
26
+ this.default_inputs = {};
27
+
28
+ /**
29
+ * @type {import('..').IExtensionDefaultOptions}
30
+ */
31
+ this.default_options = {};
32
+ }
33
+
34
+ updateExtensionInputs() {
35
+ this.extension.inputs = Object.assign({}, this.default_inputs, this.extension.inputs);
36
+ switch (this.target.name) {
37
+ case 'teams':
38
+ this.extension.inputs = Object.assign({}, { separator: true }, this.extension.inputs);
39
+ break;
40
+ case 'slack':
41
+ this.extension.inputs = Object.assign({}, { separator: false }, this.extension.inputs);
42
+ break;
43
+ case 'chat':
44
+ this.extension.inputs = Object.assign({}, { separator: true }, this.extension.inputs);
45
+ break;
46
+ default:
47
+ break;
48
+ }
49
+ }
50
+
51
+ attach() {
52
+ if (!this.text) {
53
+ logger.warn(`⚠️ Extension '${this.extension.name}' has no text. Skipping.`);
54
+ return;
55
+ }
56
+
57
+ switch (this.target.name) {
58
+ case 'teams':
59
+ addTeamsExtension({ payload: this.payload, extension: this.extension, text: this.text });
60
+ break;
61
+ case 'slack':
62
+ addSlackExtension({ payload: this.payload, extension: this.extension, text: this.text });
63
+ break;
64
+ case 'chat':
65
+ addChatExtension({ payload: this.payload, extension: this.extension, text: this.text });
66
+ break;
67
+ default:
68
+ break;
69
+ }
70
+ }
71
+
72
+ }
73
+
74
+ module.exports = { BaseExtension }
@@ -7,7 +7,8 @@ const percy_analysis = require('./percy-analysis');
7
7
  const custom = require('./custom');
8
8
  const metadata = require('./metadata');
9
9
  const ci_info = require('./ci-info');
10
- const ai_failure_summary = require('./ai-failure-summary');
10
+ const { AIFailureSummaryExtension } = require('./ai-failure-summary.extension');
11
+ const { SmartAnalysisExtension } = require('./smart-analysis.extension');
11
12
  const { EXTENSION } = require('../helpers/constants');
12
13
  const { checkCondition } = require('../helpers/helper');
13
14
  const logger = require('../utils/logger');
@@ -17,7 +18,7 @@ async function run(options) {
17
18
  const extensions = target.extensions || [];
18
19
  for (let i = 0; i < extensions.length; i++) {
19
20
  const extension = extensions[i];
20
- const extension_runner = getExtensionRunner(extension);
21
+ const extension_runner = getExtensionRunner(extension, options);
21
22
  const extension_options = Object.assign({}, extension_runner.default_options, extension);
22
23
  if (extension_options.hook === hook) {
23
24
  if (await checkCondition({ condition: extension_options.condition, result, target, extension })) {
@@ -35,7 +36,7 @@ async function run(options) {
35
36
  }
36
37
  }
37
38
 
38
- function getExtensionRunner(extension) {
39
+ function getExtensionRunner(extension, options) {
39
40
  switch (extension.name) {
40
41
  case EXTENSION.HYPERLINKS:
41
42
  return hyperlinks;
@@ -56,7 +57,9 @@ function getExtensionRunner(extension) {
56
57
  case EXTENSION.CI_INFO:
57
58
  return ci_info;
58
59
  case EXTENSION.AI_FAILURE_SUMMARY:
59
- return ai_failure_summary;
60
+ return new AIFailureSummaryExtension(options.target, extension, options.result, options.payload, options.root_payload);
61
+ case EXTENSION.SMART_ANALYSIS:
62
+ return new SmartAnalysisExtension(options.target, extension, options.result, options.payload, options.root_payload);
60
63
  default:
61
64
  return require(extension.name);
62
65
  }
@@ -87,7 +87,7 @@ function setPayloadWithMSTeamsEntities(payload) {
87
87
  }
88
88
 
89
89
  const default_options = {
90
- hook: HOOK.END,
90
+ hook: HOOK.AFTER_SUMMARY,
91
91
  condition: STATUS.FAIL
92
92
  }
93
93
 
@@ -0,0 +1,59 @@
1
+ const { BaseExtension } = require('./base.extension');
2
+ const { STATUS, HOOK } = require("../helpers/constants");
3
+
4
+ class SmartAnalysisExtension extends BaseExtension {
5
+
6
+ constructor(target, extension, result, payload, root_payload) {
7
+ super(target, extension, result, payload, root_payload);
8
+ this.#setDefaultOptions();
9
+ this.#setDefaultInputs();
10
+ this.updateExtensionInputs();
11
+ }
12
+
13
+ run() {
14
+ this.#setText();
15
+ this.attach();
16
+ }
17
+
18
+ #setDefaultOptions() {
19
+ this.default_options.hook = HOOK.AFTER_SUMMARY,
20
+ this.default_options.condition = STATUS.PASS_OR_FAIL;
21
+ }
22
+
23
+ #setDefaultInputs() {
24
+ this.default_inputs.title = 'Smart Analysis';
25
+ this.default_inputs.title_link = '';
26
+ }
27
+
28
+ #setText() {
29
+ const data = this.extension.inputs.data;
30
+
31
+ if (!data) {
32
+ return;
33
+ }
34
+
35
+ /**
36
+ * @type {import('../beats/beats.types').IBeatExecutionMetric}
37
+ */
38
+ const execution_metrics = data.execution_metrics[0];
39
+
40
+ const smart_analysis = [];
41
+ if (execution_metrics.always_failing) {
42
+ smart_analysis.push(`🚫 AF: ${execution_metrics.always_failing}`);
43
+ }
44
+ if (execution_metrics.newly_failed) {
45
+ smart_analysis.push(`⭕ NF: ${execution_metrics.newly_failed}`);
46
+ }
47
+ if (execution_metrics.flaky) {
48
+ smart_analysis.push(`❄️ FL: ${execution_metrics.flaky}`);
49
+ }
50
+ if (execution_metrics.recovered) {
51
+ smart_analysis.push(`🟢 RC: ${execution_metrics.recovered}`);
52
+ }
53
+
54
+ this.text = smart_analysis.join(' | ');
55
+ }
56
+
57
+ }
58
+
59
+ module.exports = { SmartAnalysisExtension };
@@ -21,6 +21,7 @@ const TARGET = Object.freeze({
21
21
 
22
22
  const EXTENSION = Object.freeze({
23
23
  AI_FAILURE_SUMMARY: 'ai-failure-summary',
24
+ SMART_ANALYSIS: 'smart-analysis',
24
25
  HYPERLINKS: 'hyperlinks',
25
26
  MENTIONS: 'mentions',
26
27
  REPORT_PORTAL_ANALYSIS: 'report-portal-analysis',
@@ -37,10 +38,13 @@ const URLS = Object.freeze({
37
38
  QUICK_CHART: 'https://quickchart.io'
38
39
  });
39
40
 
41
+ const MIN_NODE_VERSION = 14;
42
+
40
43
  module.exports = Object.freeze({
41
44
  STATUS,
42
45
  HOOK,
43
46
  TARGET,
44
47
  EXTENSION,
45
- URLS
48
+ URLS,
49
+ MIN_NODE_VERSION
46
50
  });
@@ -31,7 +31,7 @@ function processText(raw) {
31
31
  return raw;
32
32
  }
33
33
 
34
- /**
34
+ /**
35
35
  * @returns {import('../index').PublishConfig }
36
36
  */
37
37
  function processData(data) {
@@ -72,9 +72,9 @@ function getResultText({ result }) {
72
72
  }
73
73
 
74
74
  /**
75
- *
75
+ *
76
76
  * @param {object} param0
77
- * @param {string | Function} param0.condition
77
+ * @param {string | Function} param0.condition
78
78
  */
79
79
  async function checkCondition({ condition, result, target, extension }) {
80
80
  if (typeof condition === 'function') {
package/src/index.d.ts CHANGED
@@ -25,6 +25,7 @@ export interface ExtensionInputs {
25
25
  title?: string;
26
26
  title_link?: string;
27
27
  separator?: boolean;
28
+ data?: any;
28
29
  }
29
30
 
30
31
  export interface ReportPortalAnalysisInputs extends ExtensionInputs {
@@ -227,6 +228,7 @@ export interface PublishReport {
227
228
  project?: string;
228
229
  run?: string;
229
230
  show_failure_summary?: boolean;
231
+ show_smart_analysis?: boolean;
230
232
  targets?: Target[];
231
233
  extensions?: Extension[];
232
234
  results?: ParseOptions[] | PerformanceParseOptions[] | CustomResultOptions[];
@@ -265,5 +267,10 @@ export interface CommandLineOptions {
265
267
  mstest?: string;
266
268
  }
267
269
 
270
+ export type IExtensionDefaultOptions = {
271
+ hook: Hook
272
+ condition: Condition
273
+ }
274
+
268
275
  export function publish(options: PublishOptions): Promise<any>
269
276
  export function defineConfig(config: PublishConfig): PublishConfig
@@ -1,72 +0,0 @@
1
- const { STATUS, HOOK } = require("../helpers/constants");
2
- const { addChatExtension, addSlackExtension, addTeamsExtension } = require('../helpers/extension.helper');
3
-
4
- /**
5
- * @param {object} param0
6
- * @param {import('..').Target} param0.target
7
- * @param {import('..').MetadataExtension} param0.extension
8
- */
9
- async function run({ target, extension, result, payload, root_payload }) {
10
- extension.inputs = Object.assign({}, default_inputs, extension.inputs);
11
- if (target.name === 'teams') {
12
- extension.inputs = Object.assign({}, default_inputs_teams, extension.inputs);
13
- await attachForTeams({ target, extension, payload, result });
14
- } else if (target.name === 'slack') {
15
- extension.inputs = Object.assign({}, default_inputs_slack, extension.inputs);
16
- await attachForSlack({ target, extension, payload, result });
17
- } else if (target.name === 'chat') {
18
- extension.inputs = Object.assign({}, default_inputs_chat, extension.inputs);
19
- await attachForChat({ target, extension, payload, result });
20
- }
21
- }
22
-
23
- /**
24
- * @param {object} param0
25
- * @param {import('..').MetadataExtension} param0.extension
26
- */
27
- async function attachForTeams({ target, extension, payload, result }) {
28
- const text = extension.inputs.failure_summary
29
- if (text) {
30
- addTeamsExtension({ payload, extension, text });
31
- }
32
- }
33
-
34
- async function attachForSlack({ target, extension, payload, result }) {
35
- const text = extension.inputs.failure_summary
36
- if (text) {
37
- addSlackExtension({ payload, extension, text });
38
- }
39
- }
40
-
41
- async function attachForChat({ target, extension, payload, result }) {
42
- const text = extension.inputs.failure_summary
43
- if (text) {
44
- addChatExtension({ payload, extension, text });
45
- }
46
- }
47
-
48
- const default_options = {
49
- hook: HOOK.AFTER_SUMMARY,
50
- condition: STATUS.FAIL,
51
- }
52
-
53
- const default_inputs = {
54
- title: 'AI Failure Summary ✨'
55
- }
56
-
57
- const default_inputs_teams = {
58
- separator: true
59
- }
60
-
61
- const default_inputs_slack = {
62
- separator: false
63
- }
64
-
65
- const default_inputs_chat = {
66
- separator: true
67
- }
68
-
69
- module.exports = {
70
- run,
71
- default_options
72
- }