testbeats 2.1.3 → 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 +6 -6
- package/src/beats/beats.api.js +14 -0
- package/src/beats/beats.js +34 -18
- package/src/beats/beats.types.d.ts +6 -0
- package/src/cli.js +3 -2
- package/src/commands/publish.command.js +16 -1
- package/src/extensions/base.extension.js +2 -2
- package/src/extensions/failure-analysis.extension.js +59 -0
- package/src/extensions/index.js +9 -0
- package/src/extensions/metadata.js +1 -1
- package/src/extensions/smart-analysis.extension.js +2 -2
- package/src/helpers/ci.js +53 -0
- package/src/helpers/constants.js +9 -0
- package/src/helpers/extension.helper.js +6 -6
- package/src/helpers/metadata.helper.js +6 -6
- package/src/index.d.ts +34 -27
- package/src/platforms/base.platform.js +3 -3
- package/src/targets/custom.js +3 -3
- package/src/targets/influx.js +19 -19
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "testbeats",
|
|
3
|
-
"version": "2.1.
|
|
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",
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
"homepage": "https://testbeats.com",
|
|
48
48
|
"dependencies": {
|
|
49
49
|
"async-retry": "^1.3.3",
|
|
50
|
-
"dotenv": "^
|
|
50
|
+
"dotenv": "^16.4.5",
|
|
51
51
|
"form-data-lite": "^1.0.3",
|
|
52
52
|
"influxdb-lite": "^1.0.0",
|
|
53
53
|
"performance-results-parser": "latest",
|
|
@@ -58,12 +58,12 @@
|
|
|
58
58
|
"test-results-parser": "0.2.5"
|
|
59
59
|
},
|
|
60
60
|
"devDependencies": {
|
|
61
|
-
"c8": "^
|
|
62
|
-
"mocha": "^10.
|
|
61
|
+
"c8": "^10.1.2",
|
|
62
|
+
"mocha": "^10.7.3",
|
|
63
63
|
"mocha-junit-reporter": "^2.2.1",
|
|
64
64
|
"mocha-multi-reporters": "^1.5.1",
|
|
65
|
-
"pactum": "^3.
|
|
66
|
-
"pkg": "^5.8.
|
|
65
|
+
"pactum": "^3.7.1",
|
|
66
|
+
"pkg": "^5.8.1"
|
|
67
67
|
},
|
|
68
68
|
"engines": {
|
|
69
69
|
"node": ">=14.0.0"
|
package/src/beats/beats.api.js
CHANGED
|
@@ -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 }
|
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 } = require('../helpers/constants');
|
|
4
|
+
const { HOOK, PROCESS_STATUS } = require('../helpers/constants');
|
|
5
5
|
const TestResult = require('test-results-parser/src/models/TestResult');
|
|
6
6
|
const { BeatsAttachments } = require('./beats.attachments');
|
|
7
7
|
|
|
@@ -31,9 +31,7 @@ class Beats {
|
|
|
31
31
|
await this.#publishTestResults();
|
|
32
32
|
await this.#uploadAttachments();
|
|
33
33
|
this.#updateTitleLink();
|
|
34
|
-
await this.#
|
|
35
|
-
await this.#attachSmartAnalysis();
|
|
36
|
-
await this.#attachErrorClusters();
|
|
34
|
+
await this.#attachExtensions();
|
|
37
35
|
}
|
|
38
36
|
|
|
39
37
|
#setCIInfo() {
|
|
@@ -104,13 +102,20 @@ class Beats {
|
|
|
104
102
|
}
|
|
105
103
|
}
|
|
106
104
|
|
|
107
|
-
async #
|
|
105
|
+
async #attachExtensions() {
|
|
108
106
|
if (!this.test_run_id) {
|
|
109
107
|
return;
|
|
110
108
|
}
|
|
111
109
|
if (!this.config.targets) {
|
|
112
110
|
return;
|
|
113
111
|
}
|
|
112
|
+
await this.#attachFailureSummary();
|
|
113
|
+
await this.#attachFailureAnalysis();
|
|
114
|
+
await this.#attachSmartAnalysis();
|
|
115
|
+
await this.#attachErrorClusters();
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
async #attachFailureSummary() {
|
|
114
119
|
if (this.result.status !== 'FAIL') {
|
|
115
120
|
return;
|
|
116
121
|
}
|
|
@@ -132,13 +137,30 @@ class Beats {
|
|
|
132
137
|
}
|
|
133
138
|
}
|
|
134
139
|
|
|
135
|
-
async #
|
|
136
|
-
if (
|
|
140
|
+
async #attachFailureAnalysis() {
|
|
141
|
+
if (this.result.status !== 'FAIL') {
|
|
137
142
|
return;
|
|
138
143
|
}
|
|
139
|
-
if (
|
|
144
|
+
if (this.config.show_failure_analysis === false) {
|
|
140
145
|
return;
|
|
141
146
|
}
|
|
147
|
+
try {
|
|
148
|
+
logger.info('🪄 Fetching Failure Analysis...');
|
|
149
|
+
await this.#setTestRun('Failure Analysis Status', 'failure_analysis_status');
|
|
150
|
+
const metrics = await this.api.getFailureAnalysis(this.test_run_id);
|
|
151
|
+
this.config.extensions.push({
|
|
152
|
+
name: 'failure-analysis',
|
|
153
|
+
hook: HOOK.AFTER_SUMMARY,
|
|
154
|
+
inputs: {
|
|
155
|
+
data: metrics
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
} catch (error) {
|
|
159
|
+
logger.error(`❌ Unable to attach failure analysis: ${error.message}`, error);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
async #attachSmartAnalysis() {
|
|
142
164
|
if (this.config.show_smart_analysis === false) {
|
|
143
165
|
return;
|
|
144
166
|
}
|
|
@@ -165,7 +187,7 @@ class Beats {
|
|
|
165
187
|
}
|
|
166
188
|
|
|
167
189
|
async #setTestRun(text, wait_for = 'smart_analysis_status') {
|
|
168
|
-
if (this.test_run && this.test_run[wait_for] ===
|
|
190
|
+
if (this.test_run && this.test_run[wait_for] === PROCESS_STATUS.COMPLETED) {
|
|
169
191
|
return;
|
|
170
192
|
}
|
|
171
193
|
let retry = 3;
|
|
@@ -175,13 +197,13 @@ class Beats {
|
|
|
175
197
|
this.test_run = await this.api.getTestRun(this.test_run_id);
|
|
176
198
|
const status = this.test_run && this.test_run[wait_for];
|
|
177
199
|
switch (status) {
|
|
178
|
-
case
|
|
200
|
+
case PROCESS_STATUS.COMPLETED:
|
|
179
201
|
logger.debug(`☑️ ${text} generated successfully`);
|
|
180
202
|
return;
|
|
181
|
-
case
|
|
203
|
+
case PROCESS_STATUS.FAILED:
|
|
182
204
|
logger.error(`❌ Failed to generate ${text}`);
|
|
183
205
|
return;
|
|
184
|
-
case
|
|
206
|
+
case PROCESS_STATUS.SKIPPED:
|
|
185
207
|
logger.warn(`❗ Skipped generating ${text}`);
|
|
186
208
|
return;
|
|
187
209
|
}
|
|
@@ -191,12 +213,6 @@ class Beats {
|
|
|
191
213
|
}
|
|
192
214
|
|
|
193
215
|
async #attachErrorClusters() {
|
|
194
|
-
if (!this.test_run_id) {
|
|
195
|
-
return;
|
|
196
|
-
}
|
|
197
|
-
if (!this.config.targets) {
|
|
198
|
-
return;
|
|
199
|
-
}
|
|
200
216
|
if (this.result.status !== 'FAIL') {
|
|
201
217
|
return;
|
|
202
218
|
}
|
package/src/cli.js
CHANGED
|
@@ -6,9 +6,10 @@ const sade = require('sade');
|
|
|
6
6
|
const prog = sade('testbeats');
|
|
7
7
|
const { PublishCommand } = require('./commands/publish.command');
|
|
8
8
|
const logger = require('./utils/logger');
|
|
9
|
+
const pkg = require('../package.json');
|
|
9
10
|
|
|
10
11
|
prog
|
|
11
|
-
.version(
|
|
12
|
+
.version(pkg.version)
|
|
12
13
|
.option('-c, --config', 'path to config file')
|
|
13
14
|
.option('-l, --logLevel', 'Log Level', "INFO")
|
|
14
15
|
.option('--api-key', 'api key')
|
|
@@ -40,4 +41,4 @@ prog.command('publish')
|
|
|
40
41
|
}
|
|
41
42
|
});
|
|
42
43
|
|
|
43
|
-
prog.parse(process.argv);
|
|
44
|
+
prog.parse(process.argv);
|
|
@@ -14,7 +14,7 @@ const { MIN_NODE_VERSION } = require('../helpers/constants');
|
|
|
14
14
|
class PublishCommand {
|
|
15
15
|
|
|
16
16
|
/**
|
|
17
|
-
* @param {import('../index').
|
|
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('..').
|
|
9
|
-
* @param {import('..').
|
|
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
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
const { BaseExtension } = require('./base.extension');
|
|
2
|
+
const { STATUS, HOOK } = require("../helpers/constants");
|
|
3
|
+
|
|
4
|
+
class FailureAnalysisExtension 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
|
+
#setDefaultOptions() {
|
|
14
|
+
this.default_options.hook = HOOK.AFTER_SUMMARY,
|
|
15
|
+
this.default_options.condition = STATUS.PASS_OR_FAIL;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
#setDefaultInputs() {
|
|
19
|
+
this.default_inputs.title = '';
|
|
20
|
+
this.default_inputs.title_link = '';
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
run() {
|
|
24
|
+
this.#setText();
|
|
25
|
+
this.attach();
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
#setText() {
|
|
29
|
+
/**
|
|
30
|
+
* @type {import('../beats/beats.types').IFailureAnalysisMetric[]}
|
|
31
|
+
*/
|
|
32
|
+
const metrics = this.extension.inputs.data;
|
|
33
|
+
if (!metrics || metrics.length === 0) {
|
|
34
|
+
logger.warn('⚠️ No failure analysis metrics found. Skipping.');
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const to_investigate = metrics.find(metric => metric.name === 'To Investigate');
|
|
39
|
+
const auto_analysed = metrics.find(metric => metric.name === 'Auto Analysed');
|
|
40
|
+
|
|
41
|
+
const failure_analysis = [];
|
|
42
|
+
|
|
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
|
+
}
|
|
49
|
+
|
|
50
|
+
if (failure_analysis.length === 0) {
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
this.text = failure_analysis.join(' ');
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
module.exports = { FailureAnalysisExtension };
|
package/src/extensions/index.js
CHANGED
|
@@ -13,12 +13,19 @@ 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 { FailureAnalysisExtension } = require('./failure-analysis.extension');
|
|
16
17
|
|
|
17
18
|
async function run(options) {
|
|
18
19
|
const { target, result, hook } = options;
|
|
20
|
+
/**
|
|
21
|
+
* @type {import("..").IExtension[]}
|
|
22
|
+
*/
|
|
19
23
|
const extensions = target.extensions || [];
|
|
20
24
|
for (let i = 0; i < extensions.length; i++) {
|
|
21
25
|
const extension = extensions[i];
|
|
26
|
+
if (extension.enable === false || extension.enable === 'false') {
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
22
29
|
const extension_runner = getExtensionRunner(extension, options);
|
|
23
30
|
const extension_options = Object.assign({}, extension_runner.default_options, extension);
|
|
24
31
|
if (extension_options.hook === hook) {
|
|
@@ -59,6 +66,8 @@ function getExtensionRunner(extension, options) {
|
|
|
59
66
|
return new CIInfoExtension(options.target, extension, options.result, options.payload, options.root_payload);
|
|
60
67
|
case EXTENSION.AI_FAILURE_SUMMARY:
|
|
61
68
|
return new AIFailureSummaryExtension(options.target, extension, options.result, options.payload, options.root_payload);
|
|
69
|
+
case EXTENSION.FAILURE_ANALYSIS:
|
|
70
|
+
return new FailureAnalysisExtension(options.target, extension, options.result, options.payload, options.root_payload);
|
|
62
71
|
case EXTENSION.SMART_ANALYSIS:
|
|
63
72
|
return new SmartAnalysisExtension(options.target, extension, options.result, options.payload, options.root_payload);
|
|
64
73
|
case EXTENSION.ERROR_CLUSTERS:
|
|
@@ -4,7 +4,7 @@ const { getTeamsMetaDataText, getSlackMetaDataText, getChatMetaDataText } = requ
|
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* @param {object} param0
|
|
7
|
-
* @param {import('..').
|
|
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);
|
package/src/helpers/ci.js
CHANGED
|
@@ -1,9 +1,21 @@
|
|
|
1
|
+
const os = require('os');
|
|
2
|
+
const pkg = require('../../package.json');
|
|
3
|
+
|
|
1
4
|
const ENV = process.env;
|
|
2
5
|
|
|
3
6
|
/**
|
|
4
7
|
* @returns {import('../extensions/extensions').ICIInfo}
|
|
5
8
|
*/
|
|
6
9
|
function getCIInformation() {
|
|
10
|
+
const ci_info = getBaseCIInfo();
|
|
11
|
+
const system_info = getSystemInfo();
|
|
12
|
+
return {
|
|
13
|
+
...ci_info,
|
|
14
|
+
...system_info
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function getBaseCIInfo() {
|
|
7
19
|
if (ENV.GITHUB_ACTIONS) {
|
|
8
20
|
return getGitHubActionsInformation();
|
|
9
21
|
}
|
|
@@ -16,6 +28,7 @@ function getCIInformation() {
|
|
|
16
28
|
if (ENV.SYSTEM_TEAMFOUNDATIONCOLLECTIONURI) {
|
|
17
29
|
return getAzureDevOpsInformation();
|
|
18
30
|
}
|
|
31
|
+
return getDefaultInformation();
|
|
19
32
|
}
|
|
20
33
|
|
|
21
34
|
function getGitHubActionsInformation() {
|
|
@@ -82,6 +95,46 @@ function getGitLabInformation() {
|
|
|
82
95
|
}
|
|
83
96
|
}
|
|
84
97
|
|
|
98
|
+
function getDefaultInformation() {
|
|
99
|
+
return {
|
|
100
|
+
ci: ENV.TEST_BEATS_CI_NAME,
|
|
101
|
+
git: ENV.TEST_BEATS_CI_GIT,
|
|
102
|
+
repository_url: ENV.TEST_BEATS_CI_REPOSITORY_URL,
|
|
103
|
+
repository_name: ENV.TEST_BEATS_CI_REPOSITORY_NAME,
|
|
104
|
+
repository_ref: ENV.TEST_BEATS_CI_REPOSITORY_REF,
|
|
105
|
+
repository_commit_sha: ENV.TEST_BEATS_CI_REPOSITORY_COMMIT_SHA,
|
|
106
|
+
build_url: ENV.TEST_BEATS_CI_BUILD_URL,
|
|
107
|
+
build_number: ENV.TEST_BEATS_CI_BUILD_NUMBER,
|
|
108
|
+
build_name: ENV.TEST_BEATS_CI_BUILD_NAME,
|
|
109
|
+
build_reason: ENV.TEST_BEATS_CI_BUILD_REASON,
|
|
110
|
+
user: ENV.TEST_BEATS_CI_USER || os.userInfo().username
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function getSystemInfo() {
|
|
115
|
+
function getRuntimeInfo() {
|
|
116
|
+
if (typeof process !== 'undefined' && process.versions && process.versions.node) {
|
|
117
|
+
return { name: 'node', version: process.versions.node };
|
|
118
|
+
} else if (typeof Deno !== 'undefined') {
|
|
119
|
+
return { name: 'deno', version: Deno.version.deno };
|
|
120
|
+
} else if (typeof Bun !== 'undefined') {
|
|
121
|
+
return { name: 'bun', version: Bun.version };
|
|
122
|
+
} else {
|
|
123
|
+
return { name: 'unknown', version: 'unknown' };
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const runtime = getRuntimeInfo();
|
|
128
|
+
|
|
129
|
+
return {
|
|
130
|
+
runtime: runtime.name,
|
|
131
|
+
runtime_version: runtime.version,
|
|
132
|
+
os: os.platform(),
|
|
133
|
+
os_version: os.release(),
|
|
134
|
+
testbeats_version: pkg.version
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
85
138
|
module.exports = {
|
|
86
139
|
getCIInformation
|
|
87
140
|
}
|
package/src/helpers/constants.js
CHANGED
|
@@ -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
|
+
FAILURE_ANALYSIS: 'failure-analysis',
|
|
24
25
|
SMART_ANALYSIS: 'smart-analysis',
|
|
25
26
|
ERROR_CLUSTERS: 'error-clusters',
|
|
26
27
|
HYPERLINKS: 'hyperlinks',
|
|
@@ -39,6 +40,13 @@ const URLS = Object.freeze({
|
|
|
39
40
|
QUICK_CHART: 'https://quickchart.io'
|
|
40
41
|
});
|
|
41
42
|
|
|
43
|
+
const PROCESS_STATUS = Object.freeze({
|
|
44
|
+
RUNNING: 'RUNNING',
|
|
45
|
+
COMPLETED: 'COMPLETED',
|
|
46
|
+
FAILED: 'FAILED',
|
|
47
|
+
SKIPPED: 'SKIPPED',
|
|
48
|
+
});
|
|
49
|
+
|
|
42
50
|
const MIN_NODE_VERSION = 14;
|
|
43
51
|
|
|
44
52
|
module.exports = Object.freeze({
|
|
@@ -47,5 +55,6 @@ module.exports = Object.freeze({
|
|
|
47
55
|
TARGET,
|
|
48
56
|
EXTENSION,
|
|
49
57
|
URLS,
|
|
58
|
+
PROCESS_STATUS,
|
|
50
59
|
MIN_NODE_VERSION
|
|
51
60
|
});
|
|
@@ -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("..").
|
|
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("..").
|
|
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("..").
|
|
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('..').
|
|
22
|
-
* @param {import('..').
|
|
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('..').
|
|
54
|
-
* @param {import('..').
|
|
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('..').
|
|
86
|
-
* @param {import('..').
|
|
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:
|
|
14
|
-
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
|
-
|
|
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
|
|
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:
|
|
100
|
-
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
|
|
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:
|
|
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
|
|
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
|
|
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:
|
|
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
|
-
|
|
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;
|
|
@@ -229,10 +235,11 @@ export interface PublishReport {
|
|
|
229
235
|
project?: string;
|
|
230
236
|
run?: string;
|
|
231
237
|
show_failure_summary?: boolean;
|
|
238
|
+
show_failure_analysis?: boolean;
|
|
232
239
|
show_smart_analysis?: boolean;
|
|
233
240
|
show_error_clusters?: boolean;
|
|
234
|
-
targets?:
|
|
235
|
-
extensions?:
|
|
241
|
+
targets?: ITarget[];
|
|
242
|
+
extensions?: IExtension[];
|
|
236
243
|
results?: ParseOptions[] | PerformanceParseOptions[] | CustomResultOptions[];
|
|
237
244
|
}
|
|
238
245
|
|
|
@@ -240,8 +247,8 @@ export interface PublishConfig {
|
|
|
240
247
|
api_key?: string;
|
|
241
248
|
project?: string;
|
|
242
249
|
run?: string;
|
|
243
|
-
targets?:
|
|
244
|
-
extensions?:
|
|
250
|
+
targets?: ITarget[];
|
|
251
|
+
extensions?: IExtension[];
|
|
245
252
|
results?: ParseOptions[] | PerformanceParseOptions[] | CustomResultOptions[];
|
|
246
253
|
}
|
|
247
254
|
|
|
@@ -250,7 +257,7 @@ export interface PublishOptions {
|
|
|
250
257
|
}
|
|
251
258
|
|
|
252
259
|
export interface CommandLineOptions {
|
|
253
|
-
config?: string;
|
|
260
|
+
config?: string | PublishConfig;
|
|
254
261
|
project?: string;
|
|
255
262
|
run?: string;
|
|
256
263
|
'api-key'?: string;
|
|
@@ -15,7 +15,7 @@ class BasePlatform {
|
|
|
15
15
|
|
|
16
16
|
/**
|
|
17
17
|
*
|
|
18
|
-
* @param {import('..').
|
|
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('..').
|
|
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('..').
|
|
87
|
+
* @param {import('..').ITarget} target
|
|
88
88
|
* @param {import('test-results-parser').ITestSuite} suite
|
|
89
89
|
*/
|
|
90
90
|
getSuiteDuration(target, suite) {
|
package/src/targets/custom.js
CHANGED
|
@@ -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').
|
|
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') {
|
package/src/targets/influx.js
CHANGED
|
@@ -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('..').
|
|
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('..').
|
|
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('..').
|
|
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('..').
|
|
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('..').
|
|
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);
|