testbeats 2.0.1 → 2.0.2
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 +10 -22
- package/package.json +1 -1
- package/src/beats/beats.api.js +40 -0
- package/src/beats/beats.js +151 -0
- package/src/beats/index.js +3 -164
- package/src/cli.js +6 -2
- package/src/commands/publish.js +14 -3
- package/src/extensions/index.js +4 -3
- package/src/extensions/report-portal-history.js +3 -2
- package/src/utils/logger.js +98 -0
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
>
|
|
1
|
+
> This npm package has been renamed from [test-results-reporter](https://www.npmjs.com/package/test-results-reporter) to [testbeats](https://www.npmjs.com/package/testbeats). test-results-reporter will soon be phased out, and users are encouraged to transition to testbeats.
|
|
2
2
|
|
|
3
3
|
<span align="center">
|
|
4
4
|
|
|
@@ -19,35 +19,23 @@
|
|
|
19
19
|
|
|
20
20
|
<hr>
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
<img height="48" style="margin: 6px;" src="./assets/slack.png" alt="slack" /> <img height="48" style="margin: 6px;" src="./assets/teams.png" alt="teams" /> <img height="48" style="margin: 6px;" src="./assets/chat.png" alt="chat" />
|
|
25
|
-
|
|
26
|
-
### Extensions
|
|
27
|
-
|
|
28
|
-
<img height="48" style="margin: 6px;" src="./assets/reportportal.jpeg" alt="reportportal" /> <img height="48" style="margin: 6px;" src="./assets/quickchart.png" alt="quickchart" /> <img height="48" style="margin: 6px;" src="./assets/hyperlink.png" alt="hyperlink" /> <img height="48" style="margin: 6px;" src="./assets/mentions.png" alt="mentions" />
|
|
29
|
-
|
|
30
|
-
### Test Results
|
|
31
|
-
|
|
32
|
-
<img height="48" style="margin: 6px;" src="./assets/testng.png" alt="testng" /> <img height="48" style="margin: 6px;" src="./assets/junit.png" alt="junit" /> <img height="48" style="margin: 6px;" src="./assets/cucumber.png" alt="cucumber" /> <img height="48" style="margin: 6px;" src="./assets/mocha.png" alt="mocha" /> <img height="48" style="margin: 6px;" src="./assets/xunit.png" alt="xunit" /> <img height="48" style="margin: 6px;" src="./assets/jmeter.png" alt="jmeter" />
|
|
22
|
+
</span>
|
|
33
23
|
|
|
34
|
-
|
|
24
|
+
### Get Started
|
|
35
25
|
|
|
36
|
-
|
|
26
|
+
TestBeats is a tool designed to streamline the process of publishing test results from various automation testing frameworks to communication platforms like **slack**, **teams** and more for easy access and collaboration. It unifies your test reporting to build quality insights and make faster decisions.
|
|
37
27
|
|
|
38
|
-
|
|
28
|
+
Read more about the project at [https://testbeats.com](https://testbeats.com)
|
|
39
29
|
|
|
40
|
-
|
|
30
|
+
### Sample Reports
|
|
41
31
|
|
|
42
|
-
|
|
32
|
+
#### Alerts in Slack
|
|
43
33
|
|
|
44
|
-
|
|
34
|
+

|
|
45
35
|
|
|
46
|
-
|
|
36
|
+
#### Results in Portal
|
|
47
37
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
</span>
|
|
38
|
+

|
|
51
39
|
|
|
52
40
|
<br />
|
|
53
41
|
|
package/package.json
CHANGED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
const request = require('phin-retry');
|
|
2
|
+
|
|
3
|
+
class BeatsApi {
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @param {import('../index').PublishReport} config
|
|
7
|
+
*/
|
|
8
|
+
constructor(config) {
|
|
9
|
+
this.config = config;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
postTestRun(payload) {
|
|
13
|
+
return request.post({
|
|
14
|
+
url: `${this.getBaseUrl()}/api/core/v1/test-runs`,
|
|
15
|
+
headers: {
|
|
16
|
+
'x-api-key': this.config.api_key
|
|
17
|
+
},
|
|
18
|
+
body: payload
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* @param {string} run_id
|
|
24
|
+
* @returns
|
|
25
|
+
*/
|
|
26
|
+
getTestRun(run_id) {
|
|
27
|
+
return request.get({
|
|
28
|
+
url: `${this.getBaseUrl()}/api/core/v1/test-runs/key?id=${run_id}`,
|
|
29
|
+
headers: {
|
|
30
|
+
'x-api-key': this.config.api_key
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
getBaseUrl() {
|
|
36
|
+
return process.env.TEST_BEATS_URL || "https://app.testbeats.com";
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
module.exports = { BeatsApi }
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
const { getCIInformation } = require('../helpers/ci');
|
|
2
|
+
const logger = require('../utils/logger');
|
|
3
|
+
const { BeatsApi } = require('./beats.api');
|
|
4
|
+
const { HOOK } = require('../helpers/constants');
|
|
5
|
+
|
|
6
|
+
class Beats {
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* @param {import('../index').PublishReport} config
|
|
10
|
+
* @param {TestResult} result
|
|
11
|
+
*/
|
|
12
|
+
constructor(config, result) {
|
|
13
|
+
this.config = config;
|
|
14
|
+
this.result = result;
|
|
15
|
+
this.api = new BeatsApi(config);
|
|
16
|
+
this.test_run_id = '';
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
async publish() {
|
|
20
|
+
this.#setCIInfo();
|
|
21
|
+
this.#setProjectName();
|
|
22
|
+
this.#setRunName();
|
|
23
|
+
this.#setApiKey();
|
|
24
|
+
await this.#publishTestResults();
|
|
25
|
+
this.#updateTitleLink();
|
|
26
|
+
await this.#attachFailureSummary();
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
#setCIInfo() {
|
|
30
|
+
this.ci = getCIInformation();
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
#setProjectName() {
|
|
34
|
+
this.config.project = this.config.project || process.env.TEST_BEATS_PROJECT || (this.ci && this.ci.repository_name) || 'demo-project';
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
#setApiKey() {
|
|
38
|
+
this.config.api_key = this.config.api_key || process.env.TEST_BEATS_API_KEY;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
#setRunName() {
|
|
42
|
+
this.config.run = this.config.run || process.env.TEST_BEATS_RUN || (this.ci && this.ci.build_name) || 'demo-run';
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async #publishTestResults() {
|
|
46
|
+
if (!this.config.api_key) {
|
|
47
|
+
logger.warn('😿 No API key provided, skipping publishing results to TestBeats Portal');
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
logger.info("🚀 Publishing results to TestBeats Portal...");
|
|
51
|
+
try {
|
|
52
|
+
const payload = this.#getPayload();
|
|
53
|
+
const response = await this.api.postTestRun(payload);
|
|
54
|
+
this.test_run_id = response.id;
|
|
55
|
+
} catch (error) {
|
|
56
|
+
logger.error(`❌ Unable to publish results to TestBeats Portal: ${error.message}`, error);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
#getPayload() {
|
|
61
|
+
const payload = {
|
|
62
|
+
project: this.config.project,
|
|
63
|
+
run: this.config.run,
|
|
64
|
+
...this.result
|
|
65
|
+
}
|
|
66
|
+
if (this.ci) {
|
|
67
|
+
payload.ci_details = [this.ci];
|
|
68
|
+
}
|
|
69
|
+
return payload;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
#updateTitleLink() {
|
|
73
|
+
if (!this.test_run_id) {
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
if (!this.config.targets) {
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
const link = `${this.api.getBaseUrl()}/reports/${this.test_run_id}`;
|
|
80
|
+
for (const target of this.config.targets) {
|
|
81
|
+
target.inputs.title_link = link;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
async #attachFailureSummary() {
|
|
86
|
+
if (!this.test_run_id) {
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
if (!this.config.targets) {
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
if (this.result.status !== 'FAIL') {
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
if (this.config.show_failure_summary === false) {
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
const text = await this.#getFailureSummary();
|
|
99
|
+
if (!text) {
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
const extension = this.#getAIFailureSummaryExtension(text);
|
|
103
|
+
for (const target of this.config.targets) {
|
|
104
|
+
target.extensions = target.extensions || [];
|
|
105
|
+
target.extensions.push(extension);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
async #getFailureSummary() {
|
|
110
|
+
logger.info('✨ Fetching AI Failure Summary...');
|
|
111
|
+
let retry = 3;
|
|
112
|
+
while (retry >= 0) {
|
|
113
|
+
retry = retry - 1;
|
|
114
|
+
await new Promise(resolve => setTimeout(resolve, this.#getDelay()));
|
|
115
|
+
const test_run = await this.api.getTestRun(this.test_run_id);
|
|
116
|
+
const status = test_run && test_run.failure_summary_status;
|
|
117
|
+
switch (status) {
|
|
118
|
+
case 'COMPLETED':
|
|
119
|
+
return test_run.execution_metrics[0].failure_summary;
|
|
120
|
+
case 'FAILED':
|
|
121
|
+
logger.error(`❌ Failed to generate AI Failure Summary`);
|
|
122
|
+
return;
|
|
123
|
+
case 'SKIPPED':
|
|
124
|
+
logger.warn(`❗ Skipped generating AI Failure Summary`);
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
logger.info(`🔄 AI Failure Summary not generated, retrying...`);
|
|
128
|
+
}
|
|
129
|
+
logger.warn(`🙈 AI Failure Summary not generated in given time`);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
#getDelay() {
|
|
133
|
+
if (process.env.TEST_BEATS_DELAY) {
|
|
134
|
+
return parseInt(process.env.TEST_BEATS_DELAY);
|
|
135
|
+
}
|
|
136
|
+
return 3000;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
#getAIFailureSummaryExtension(text) {
|
|
140
|
+
return {
|
|
141
|
+
name: 'ai-failure-summary',
|
|
142
|
+
hook: HOOK.AFTER_SUMMARY,
|
|
143
|
+
inputs: {
|
|
144
|
+
failure_summary: text
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
module.exports = { Beats }
|
package/src/beats/index.js
CHANGED
|
@@ -1,174 +1,13 @@
|
|
|
1
|
-
const request = require('phin-retry');
|
|
2
1
|
const TestResult = require('test-results-parser/src/models/TestResult');
|
|
3
|
-
const {
|
|
4
|
-
const { HOOK } = require('../helpers/constants');
|
|
5
|
-
|
|
6
|
-
function get_base_url() {
|
|
7
|
-
return process.env.TEST_BEATS_URL || "https://app.testbeats.com";
|
|
8
|
-
}
|
|
2
|
+
const { Beats } = require('./beats');
|
|
9
3
|
|
|
10
4
|
/**
|
|
11
5
|
* @param {import('../index').PublishReport} config
|
|
12
6
|
* @param {TestResult} result
|
|
13
7
|
*/
|
|
14
8
|
async function run(config, result) {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
const run_id = await publishTestResults(config, result);
|
|
18
|
-
if (run_id) {
|
|
19
|
-
attachTestBeatsReportHyperLink(config, run_id);
|
|
20
|
-
await attachTestBeatsFailureSummary(config, result, run_id);
|
|
21
|
-
}
|
|
22
|
-
} else {
|
|
23
|
-
console.warn('Missing testbeats config parameters');
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
*
|
|
29
|
-
* @param {import('../index').PublishReport} config
|
|
30
|
-
*/
|
|
31
|
-
function init(config) {
|
|
32
|
-
config.project = config.project || process.env.TEST_BEATS_PROJECT;
|
|
33
|
-
config.run = config.run || process.env.TEST_BEATS_RUN;
|
|
34
|
-
config.api_key = config.api_key || process.env.TEST_BEATS_API_KEY;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
*
|
|
39
|
-
* @param {import('../index').PublishReport} config
|
|
40
|
-
*/
|
|
41
|
-
function isValid(config) {
|
|
42
|
-
return config.project && config.run && config.api_key
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* @param {import('../index').PublishReport} config
|
|
47
|
-
* @param {TestResult} result
|
|
48
|
-
*/
|
|
49
|
-
async function publishTestResults(config, result) {
|
|
50
|
-
console.log("Publishing results to TestBeats");
|
|
51
|
-
try {
|
|
52
|
-
const payload = {
|
|
53
|
-
project: config.project,
|
|
54
|
-
run: config.run,
|
|
55
|
-
...result
|
|
56
|
-
}
|
|
57
|
-
const ci = getCIInformation();
|
|
58
|
-
if (ci) {
|
|
59
|
-
payload.ci_details = [ci];
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
const response = await request.post({
|
|
63
|
-
url: `${get_base_url()}/api/core/v1/test-runs`,
|
|
64
|
-
headers: {
|
|
65
|
-
'x-api-key': config.api_key
|
|
66
|
-
},
|
|
67
|
-
body: payload
|
|
68
|
-
});
|
|
69
|
-
return response.id;
|
|
70
|
-
} catch (error) {
|
|
71
|
-
console.log("Unable to publish results to TestBeats");
|
|
72
|
-
console.log(error);
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
* @param {import('../index').PublishReport} config
|
|
78
|
-
* @param {string} run_id
|
|
79
|
-
*/
|
|
80
|
-
function attachTestBeatsReportHyperLink(config, run_id) {
|
|
81
|
-
const beats_link = get_test_beats_report_link(run_id);
|
|
82
|
-
if (config.targets) {
|
|
83
|
-
for (const target of config.targets) {
|
|
84
|
-
target.inputs.title_link = beats_link;
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
/**
|
|
90
|
-
* @param {import('../index').PublishReport} config
|
|
91
|
-
* @param {TestResult} result
|
|
92
|
-
* @param {string} run_id
|
|
93
|
-
*/
|
|
94
|
-
async function attachTestBeatsFailureSummary(config, result, run_id) {
|
|
95
|
-
if (result.status !== 'FAIL') {
|
|
96
|
-
return;
|
|
97
|
-
}
|
|
98
|
-
if (config.show_failure_summary === false) {
|
|
99
|
-
return;
|
|
100
|
-
}
|
|
101
|
-
try {
|
|
102
|
-
await processFailureSummary(config, run_id);
|
|
103
|
-
} catch (error) {
|
|
104
|
-
console.log(error);
|
|
105
|
-
console.log("error processing failure summary");
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
async function processFailureSummary(config, run_id) {
|
|
110
|
-
let retry = 3;
|
|
111
|
-
while (retry > 0) {
|
|
112
|
-
const test_run = await getTestRun(config, run_id);
|
|
113
|
-
if (test_run && test_run.failure_summary_status) {
|
|
114
|
-
if (test_run.failure_summary_status === 'COMPLETED') {
|
|
115
|
-
addAIFailureSummaryExtension(config, test_run);
|
|
116
|
-
return;
|
|
117
|
-
} else if (test_run.failure_summary_status === 'FAILED') {
|
|
118
|
-
console.log(`Test run failure summary failed`);
|
|
119
|
-
return;
|
|
120
|
-
} else if (test_run.failure_summary_status === 'SKIPPED') {
|
|
121
|
-
console.log(`Test run failure summary failed`);
|
|
122
|
-
return;
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
console.log(`Test run failure summary not completed, retrying...`);
|
|
126
|
-
await new Promise(resolve => setTimeout(resolve, 3000));
|
|
127
|
-
retry = retry - 1;
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
/**
|
|
132
|
-
* @param {import('../index').PublishReport} config
|
|
133
|
-
* @param {string} run_id
|
|
134
|
-
*/
|
|
135
|
-
function getTestRun(config, run_id) {
|
|
136
|
-
return request.get({
|
|
137
|
-
url: `${get_base_url()}/api/core/v1/test-runs/key?id=${run_id}`,
|
|
138
|
-
headers: {
|
|
139
|
-
'x-api-key': config.api_key
|
|
140
|
-
}
|
|
141
|
-
});
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
function getAIFailureSummaryExtension(test_run) {
|
|
145
|
-
const execution_metric = test_run.execution_metrics[0];
|
|
146
|
-
return {
|
|
147
|
-
name: 'ai-failure-summary',
|
|
148
|
-
hook: HOOK.AFTER_SUMMARY,
|
|
149
|
-
inputs: {
|
|
150
|
-
failure_summary: execution_metric.failure_summary
|
|
151
|
-
}
|
|
152
|
-
};
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
function addAIFailureSummaryExtension(config, test_run) {
|
|
156
|
-
const extension = getAIFailureSummaryExtension(test_run);
|
|
157
|
-
if (config.targets) {
|
|
158
|
-
for (const target of config.targets) {
|
|
159
|
-
target.extensions = target.extensions || [];
|
|
160
|
-
target.extensions.push(extension);
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
/**
|
|
166
|
-
*
|
|
167
|
-
* @param {string} run_id
|
|
168
|
-
* @returns
|
|
169
|
-
*/
|
|
170
|
-
function get_test_beats_report_link(run_id) {
|
|
171
|
-
return `${get_base_url()}/reports/${run_id}`;
|
|
9
|
+
const beats = new Beats(config, result);
|
|
10
|
+
await beats.publish();
|
|
172
11
|
}
|
|
173
12
|
|
|
174
13
|
module.exports = { run }
|
package/src/cli.js
CHANGED
|
@@ -5,17 +5,21 @@ const sade = require('sade');
|
|
|
5
5
|
|
|
6
6
|
const prog = sade('testbeats');
|
|
7
7
|
const publish = require('./commands/publish');
|
|
8
|
+
const logger = require('./utils/logger');
|
|
8
9
|
|
|
9
10
|
prog
|
|
10
11
|
.version('2.0.1')
|
|
11
|
-
.option('-c, --config', 'Provide path to custom config', 'config.json')
|
|
12
|
+
.option('-c, --config', 'Provide path to custom config', 'config.json')
|
|
13
|
+
.option('-l, --logLevel', 'Log Level', "INFO");
|
|
12
14
|
|
|
13
15
|
prog.command('publish')
|
|
14
16
|
.action(async (opts) => {
|
|
15
17
|
try {
|
|
18
|
+
logger.setLevel(opts.logLevel);
|
|
19
|
+
logger.info(`Initiating...`);
|
|
16
20
|
await publish.run(opts);
|
|
17
21
|
} catch (error) {
|
|
18
|
-
|
|
22
|
+
logger.error(`Report publish failed: ${error.message}`);
|
|
19
23
|
process.exit(1);
|
|
20
24
|
}
|
|
21
25
|
});
|
package/src/commands/publish.js
CHANGED
|
@@ -2,14 +2,17 @@ const path = require('path');
|
|
|
2
2
|
const trp = require('test-results-parser');
|
|
3
3
|
const prp = require('performance-results-parser');
|
|
4
4
|
|
|
5
|
+
const pkg = require('../../package.json');
|
|
5
6
|
const { processData } = require('../helpers/helper');
|
|
6
7
|
const beats = require('../beats');
|
|
7
8
|
const target_manager = require('../targets');
|
|
9
|
+
const logger = require('../utils/logger');
|
|
8
10
|
|
|
9
11
|
/**
|
|
10
12
|
* @param {import('../index').PublishOptions} opts
|
|
11
13
|
*/
|
|
12
14
|
async function run(opts) {
|
|
15
|
+
logger.info(`💡 TestBeats v${pkg.version}`);
|
|
13
16
|
if (!opts) {
|
|
14
17
|
throw new Error('Missing publish options');
|
|
15
18
|
}
|
|
@@ -35,6 +38,7 @@ async function run(opts) {
|
|
|
35
38
|
validateConfig(config);
|
|
36
39
|
await processReport(config);
|
|
37
40
|
}
|
|
41
|
+
logger.info('✅ Results published successfully!');
|
|
38
42
|
}
|
|
39
43
|
|
|
40
44
|
/**
|
|
@@ -42,6 +46,7 @@ async function run(opts) {
|
|
|
42
46
|
* @param {import('../index').PublishReport} report
|
|
43
47
|
*/
|
|
44
48
|
async function processReport(report) {
|
|
49
|
+
logger.debug("processReport: Started")
|
|
45
50
|
const parsed_results = [];
|
|
46
51
|
for (const result_options of report.results) {
|
|
47
52
|
if (result_options.type === 'custom') {
|
|
@@ -61,9 +66,10 @@ async function processReport(report) {
|
|
|
61
66
|
await target_manager.run(target, result);
|
|
62
67
|
}
|
|
63
68
|
} else {
|
|
64
|
-
|
|
69
|
+
logger.warn('No targets defined, skipping sending results to targets');
|
|
65
70
|
}
|
|
66
71
|
}
|
|
72
|
+
logger.debug("processReport: Ended")
|
|
67
73
|
}
|
|
68
74
|
|
|
69
75
|
/**
|
|
@@ -71,8 +77,9 @@ async function processReport(report) {
|
|
|
71
77
|
* @param {import('../index').PublishReport} config
|
|
72
78
|
*/
|
|
73
79
|
function validateConfig(config) {
|
|
80
|
+
logger.info("🛠️ Validating configuration...")
|
|
74
81
|
if (!config) {
|
|
75
|
-
throw new Error('Missing
|
|
82
|
+
throw new Error('Missing configuration');
|
|
76
83
|
}
|
|
77
84
|
validateResults(config);
|
|
78
85
|
validateTargets(config);
|
|
@@ -83,6 +90,7 @@ function validateConfig(config) {
|
|
|
83
90
|
* @param {import('../index').PublishReport} config
|
|
84
91
|
*/
|
|
85
92
|
function validateResults(config) {
|
|
93
|
+
logger.debug("Validating results...")
|
|
86
94
|
if (!config.results) {
|
|
87
95
|
throw new Error('Missing results properties in config');
|
|
88
96
|
}
|
|
@@ -112,6 +120,7 @@ function validateResults(config) {
|
|
|
112
120
|
}
|
|
113
121
|
}
|
|
114
122
|
}
|
|
123
|
+
logger.debug("Validating results - Successful!")
|
|
115
124
|
}
|
|
116
125
|
|
|
117
126
|
/**
|
|
@@ -119,8 +128,9 @@ function validateResults(config) {
|
|
|
119
128
|
* @param {import('../index').PublishReport} config
|
|
120
129
|
*/
|
|
121
130
|
function validateTargets(config) {
|
|
131
|
+
logger.debug("Validating targets...")
|
|
122
132
|
if (!config.targets) {
|
|
123
|
-
|
|
133
|
+
logger.warn('⚠️ Targets are not defined in config');
|
|
124
134
|
return;
|
|
125
135
|
}
|
|
126
136
|
if (!Array.isArray(config.targets)) {
|
|
@@ -150,6 +160,7 @@ function validateTargets(config) {
|
|
|
150
160
|
}
|
|
151
161
|
}
|
|
152
162
|
}
|
|
163
|
+
logger.debug("Validating targets - Successful!")
|
|
153
164
|
}
|
|
154
165
|
|
|
155
166
|
module.exports = {
|
package/src/extensions/index.js
CHANGED
|
@@ -10,6 +10,7 @@ const ci_info = require('./ci-info');
|
|
|
10
10
|
const ai_failure_summary = require('./ai-failure-summary');
|
|
11
11
|
const { EXTENSION } = require('../helpers/constants');
|
|
12
12
|
const { checkCondition } = require('../helpers/helper');
|
|
13
|
+
const logger = require('../utils/logger');
|
|
13
14
|
|
|
14
15
|
async function run(options) {
|
|
15
16
|
const { target, result, hook } = options;
|
|
@@ -25,9 +26,9 @@ async function run(options) {
|
|
|
25
26
|
try {
|
|
26
27
|
await extension_runner.run(options);
|
|
27
28
|
} catch (error) {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
29
|
+
logger.error(`Failed to run extension: ${error.message}`);
|
|
30
|
+
logger.debug(`Extension details`, extension);
|
|
31
|
+
logger.debug(`Error: `, error);
|
|
31
32
|
}
|
|
32
33
|
}
|
|
33
34
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const { getSuiteHistory, getLastLaunchByName, getLaunchDetails } = require('../helpers/report-portal');
|
|
2
2
|
const { addChatExtension, addSlackExtension, addTeamsExtension } = require('../helpers/extension.helper');
|
|
3
3
|
const { HOOK, STATUS } = require('../helpers/constants');
|
|
4
|
+
const logger = require('../utils/logger');
|
|
4
5
|
|
|
5
6
|
async function getLaunchHistory(extension) {
|
|
6
7
|
const { inputs, outputs } = extension;
|
|
@@ -71,8 +72,8 @@ async function run({ extension, target, payload }) {
|
|
|
71
72
|
}
|
|
72
73
|
}
|
|
73
74
|
} catch (error) {
|
|
74
|
-
|
|
75
|
-
|
|
75
|
+
logger.error(`Failed to get report portal history: ${error.message}`);
|
|
76
|
+
logger.debug(`Error: ${error}`);
|
|
76
77
|
}
|
|
77
78
|
}
|
|
78
79
|
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
const trm = console;
|
|
2
|
+
|
|
3
|
+
const LEVEL_VERBOSE = 2;
|
|
4
|
+
const LEVEL_TRACE = 3;
|
|
5
|
+
const LEVEL_DEBUG = 4;
|
|
6
|
+
const LEVEL_INFO = 5;
|
|
7
|
+
const LEVEL_WARN = 6;
|
|
8
|
+
const LEVEL_ERROR = 7;
|
|
9
|
+
const LEVEL_SILENT = 8;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* returns log level value
|
|
13
|
+
* @param {string} level - log level
|
|
14
|
+
*/
|
|
15
|
+
function getLevelValue(level) {
|
|
16
|
+
const logLevel = level.toUpperCase();
|
|
17
|
+
switch (logLevel) {
|
|
18
|
+
case 'TRACE':
|
|
19
|
+
return LEVEL_TRACE;
|
|
20
|
+
case 'DEBUG':
|
|
21
|
+
return LEVEL_DEBUG;
|
|
22
|
+
case 'INFO':
|
|
23
|
+
return LEVEL_INFO;
|
|
24
|
+
case 'WARN':
|
|
25
|
+
return LEVEL_WARN;
|
|
26
|
+
case 'ERROR':
|
|
27
|
+
return LEVEL_ERROR;
|
|
28
|
+
case 'SILENT':
|
|
29
|
+
return LEVEL_SILENT;
|
|
30
|
+
case 'VERBOSE':
|
|
31
|
+
return LEVEL_VERBOSE;
|
|
32
|
+
default:
|
|
33
|
+
return LEVEL_INFO;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
class Logger {
|
|
38
|
+
|
|
39
|
+
constructor() {
|
|
40
|
+
this.level = process.env.TESTBEATS_LOG_LEVEL || 'INFO';
|
|
41
|
+
this.levelValue = getLevelValue(this.level);
|
|
42
|
+
if (process.env.TESTBEATS_DISABLE_LOG_COLORS === 'true') {
|
|
43
|
+
options.disableColors = true;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* sets log level
|
|
49
|
+
* @param {('TRACE'|'DEBUG'|'INFO'|'WARN'|'ERROR')} level - log level
|
|
50
|
+
*/
|
|
51
|
+
setLevel(level) {
|
|
52
|
+
this.level = level;
|
|
53
|
+
this.levelValue = getLevelValue(this.level);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
trace(...msg) {
|
|
57
|
+
if (this.levelValue <= LEVEL_TRACE) {
|
|
58
|
+
msg.forEach(m => trm.debug(m));
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
debug(...msg) {
|
|
63
|
+
if (this.levelValue <= LEVEL_DEBUG) {
|
|
64
|
+
msg.forEach(m => trm.debug(m));
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
info(...msg) {
|
|
69
|
+
if (this.levelValue <= LEVEL_INFO) {
|
|
70
|
+
msg.forEach(m => trm.info(m));
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
warn(...msg) {
|
|
75
|
+
if (this.levelValue <= LEVEL_WARN) {
|
|
76
|
+
msg.forEach(m => trm.warn(getMessage(m)));
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
error(...msg) {
|
|
81
|
+
if (this.levelValue <= LEVEL_ERROR) {
|
|
82
|
+
msg.forEach(m => trm.error(getMessage(m)));
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
function getMessage(msg) {
|
|
90
|
+
try {
|
|
91
|
+
return typeof msg === 'object' ? JSON.stringify(msg, null, 2) : msg;
|
|
92
|
+
} catch (_) {
|
|
93
|
+
return msg;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
module.exports = new Logger();
|