testbeats 2.2.7 → 2.2.9
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 +3 -2
- package/src/beats/beats.attachments.js +1 -1
- package/src/beats/beats.js +2 -2
- package/src/cli.js +18 -0
- package/src/commands/manual-sync.command.js +79 -0
- package/src/helpers/constants.js +1 -0
- package/src/index.d.ts +8 -3
- package/src/manual/parsers/gherkin.js +237 -0
- package/src/manual/sync.helper.js +148 -0
- package/src/platforms/base.platform.js +13 -108
- package/src/platforms/chat.platform.js +4 -1
- package/src/platforms/slack.platform.js +14 -1
- package/src/targets/base.target.js +114 -1
- package/src/targets/chat.js +11 -4
- package/src/targets/custom.target.js +3 -2
- package/src/targets/github-output.target.js +64 -0
- package/src/targets/github.target.js +316 -0
- package/src/targets/index.js +5 -2
- package/src/targets/slack.js +32 -12
- package/src/targets/teams.js +14 -8
- package/src/utils/context.utils.js +5 -0
- package/src/targets/github.js +0 -310
|
@@ -6,7 +6,10 @@ class BasePlatform {
|
|
|
6
6
|
* @param {string|number} text
|
|
7
7
|
*/
|
|
8
8
|
bold(text) {
|
|
9
|
-
|
|
9
|
+
if (text) {
|
|
10
|
+
return `**${text}**`;
|
|
11
|
+
}
|
|
12
|
+
return text;
|
|
10
13
|
}
|
|
11
14
|
|
|
12
15
|
break() {
|
|
@@ -39,117 +42,19 @@ class BasePlatform {
|
|
|
39
42
|
return text;
|
|
40
43
|
}
|
|
41
44
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
*
|
|
46
|
-
* @param {import('..').ITarget} target
|
|
47
|
-
* @param {import('test-results-parser').ITestSuite} suite
|
|
48
|
-
*/
|
|
49
|
-
getSuiteSummaryText(target, suite) {
|
|
50
|
-
const suite_title = this.getSuiteTitle(suite);
|
|
51
|
-
const suite_results_text = this.#getSuiteResultsText(suite);
|
|
52
|
-
const duration_text = this.#getSuiteDurationText(target, suite);
|
|
53
|
-
|
|
54
|
-
const texts = [
|
|
55
|
-
this.bold(suite_title),
|
|
56
|
-
this.break(),
|
|
57
|
-
this.break(),
|
|
58
|
-
suite_results_text,
|
|
59
|
-
this.break(),
|
|
60
|
-
duration_text,
|
|
61
|
-
];
|
|
62
|
-
|
|
63
|
-
const metadata_text = this.getSuiteMetaDataText(suite);
|
|
64
|
-
|
|
65
|
-
if (metadata_text) {
|
|
66
|
-
texts.push(this.break());
|
|
67
|
-
texts.push(this.break());
|
|
68
|
-
texts.push(metadata_text);
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
return texts.join('');
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
*
|
|
76
|
-
* @param {import('test-results-parser').ITestSuite} suite
|
|
77
|
-
* @returns {string}
|
|
78
|
-
*/
|
|
79
|
-
getSuiteTitle(suite) {
|
|
80
|
-
const emoji = suite.status === 'PASS' ? '✅' : suite.total === suite.skipped ? '⏭️' : '❌';
|
|
81
|
-
return `${emoji} ${suite.name}`;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
45
|
/**
|
|
85
|
-
*
|
|
86
|
-
* @param {
|
|
87
|
-
* @returns {string}
|
|
88
|
-
*/
|
|
89
|
-
#getSuiteResultsText(suite) {
|
|
90
|
-
const suite_results = this.getSuiteResults(suite);
|
|
91
|
-
return `${this.bold('Results')}: ${suite_results}`;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
/**
|
|
95
|
-
*
|
|
96
|
-
* @param {import('test-results-parser').ITestSuite} suite
|
|
97
|
-
* @returns {string}
|
|
98
|
-
*/
|
|
99
|
-
getSuiteResults(suite) {
|
|
100
|
-
return `${suite.passed} / ${suite.total} Passed (${getPercentage(suite.passed, suite.total)}%)`;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
/**
|
|
104
|
-
*
|
|
105
|
-
* @param {import('..').ITarget} target
|
|
106
|
-
* @param {import('test-results-parser').ITestSuite} suite
|
|
107
|
-
*/
|
|
108
|
-
#getSuiteDurationText(target, suite) {
|
|
109
|
-
const duration = this.getSuiteDuration(target, suite);
|
|
110
|
-
return `${this.bold('Duration')}: ${duration}`
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
/**
|
|
114
|
-
*
|
|
115
|
-
* @param {import('..').ITarget} target
|
|
116
|
-
* @param {import('test-results-parser').ITestSuite} suite
|
|
117
|
-
*/
|
|
118
|
-
getSuiteDuration(target, suite) {
|
|
119
|
-
return getPrettyDuration(suite.duration, target.inputs.duration);
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
/**
|
|
123
|
-
*
|
|
124
|
-
* @param {import('test-results-parser').ITestSuite} suite
|
|
46
|
+
* @param {string} text
|
|
47
|
+
* @param {string} url
|
|
125
48
|
* @returns {string}
|
|
126
49
|
*/
|
|
127
|
-
|
|
128
|
-
if (
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
// webdriver io
|
|
135
|
-
if (suite.metadata.device && typeof suite.metadata.device === 'string') {
|
|
136
|
-
texts.push(`${suite.metadata.device}`);
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
if (suite.metadata.platform && suite.metadata.platform.name && suite.metadata.platform.version) {
|
|
140
|
-
texts.push(`${suite.metadata.platform.name} ${suite.metadata.platform.version}`);
|
|
50
|
+
link(text, url) {
|
|
51
|
+
if (url) {
|
|
52
|
+
if (!text) {
|
|
53
|
+
text = url;
|
|
54
|
+
}
|
|
55
|
+
return `[${text}](${url})`;
|
|
141
56
|
}
|
|
142
|
-
|
|
143
|
-
if (suite.metadata.browser && suite.metadata.browser.name && suite.metadata.browser.version) {
|
|
144
|
-
texts.push(`${suite.metadata.browser.name} ${suite.metadata.browser.version}`);
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
// playwright
|
|
148
|
-
if (suite.metadata.hostname && typeof suite.metadata.hostname === 'string') {
|
|
149
|
-
texts.push(`${suite.metadata.hostname}`);
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
return texts.join(' • ');
|
|
57
|
+
return url;
|
|
153
58
|
}
|
|
154
59
|
|
|
155
60
|
/**
|
|
@@ -6,7 +6,10 @@ class SlackPlatform extends BasePlatform {
|
|
|
6
6
|
* @param {string|number} text
|
|
7
7
|
*/
|
|
8
8
|
bold(text) {
|
|
9
|
-
|
|
9
|
+
if (text) {
|
|
10
|
+
return `*${text}*`;
|
|
11
|
+
}
|
|
12
|
+
return text;
|
|
10
13
|
}
|
|
11
14
|
|
|
12
15
|
/**
|
|
@@ -26,6 +29,16 @@ class SlackPlatform extends BasePlatform {
|
|
|
26
29
|
}
|
|
27
30
|
return text;
|
|
28
31
|
}
|
|
32
|
+
|
|
33
|
+
link(text, url) {
|
|
34
|
+
if (url) {
|
|
35
|
+
if (!text) {
|
|
36
|
+
text = url;
|
|
37
|
+
}
|
|
38
|
+
return `<${url}|${text}>`;
|
|
39
|
+
}
|
|
40
|
+
return text;
|
|
41
|
+
}
|
|
29
42
|
}
|
|
30
43
|
|
|
31
44
|
module.exports = { SlackPlatform }
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
const { getPlatform } = require('../platforms');
|
|
2
2
|
const { STATUS } = require('../helpers/constants');
|
|
3
|
+
const { getPercentage, getPrettyDuration } = require('../helpers/helper');
|
|
3
4
|
|
|
4
5
|
class BaseTarget {
|
|
5
6
|
|
|
@@ -34,12 +35,124 @@ class BaseTarget {
|
|
|
34
35
|
* @type {import('../platforms/base.platform').BasePlatform}
|
|
35
36
|
*/
|
|
36
37
|
this.platform = getPlatform(this.name);
|
|
38
|
+
|
|
37
39
|
}
|
|
38
40
|
|
|
39
41
|
async run({ result }) {
|
|
40
42
|
// throw new Error('Not implemented');
|
|
41
43
|
}
|
|
42
44
|
|
|
45
|
+
/**
|
|
46
|
+
*
|
|
47
|
+
* @param {import('..').ITarget} target
|
|
48
|
+
* @param {import('test-results-parser').ITestSuite} suite
|
|
49
|
+
*/
|
|
50
|
+
getSuiteSummaryText(target, suite) {
|
|
51
|
+
const suite_title = this.getSuiteTitle(suite);
|
|
52
|
+
const suite_results_text = this.#getSuiteResultsText(suite);
|
|
53
|
+
const duration_text = this.#getSuiteDurationText(target, suite);
|
|
54
|
+
|
|
55
|
+
const texts = [
|
|
56
|
+
this.platform.bold(suite_title),
|
|
57
|
+
this.platform.break(),
|
|
58
|
+
this.platform.break(),
|
|
59
|
+
suite_results_text,
|
|
60
|
+
this.platform.break(),
|
|
61
|
+
duration_text,
|
|
62
|
+
];
|
|
63
|
+
|
|
64
|
+
const metadata_text = this.getSuiteMetaDataText(suite);
|
|
65
|
+
|
|
66
|
+
if (metadata_text) {
|
|
67
|
+
texts.push(this.platform.break());
|
|
68
|
+
texts.push(this.platform.break());
|
|
69
|
+
texts.push(metadata_text);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return texts.join('');
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
*
|
|
77
|
+
* @param {import('test-results-parser').ITestSuite} suite
|
|
78
|
+
* @returns {string}
|
|
79
|
+
*/
|
|
80
|
+
getSuiteTitle(suite) {
|
|
81
|
+
const emoji = suite.status === 'PASS' ? '✅' : suite.total === suite.skipped ? '⏭️' : '❌';
|
|
82
|
+
return `${emoji} ${suite.name}`;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
*
|
|
87
|
+
* @param {import('test-results-parser').ITestSuite} suite
|
|
88
|
+
* @returns {string}
|
|
89
|
+
*/
|
|
90
|
+
#getSuiteResultsText(suite) {
|
|
91
|
+
const suite_results = this.getSuiteResults(suite);
|
|
92
|
+
return `${this.platform.bold('Results')}: ${suite_results}`;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
*
|
|
97
|
+
* @param {import('test-results-parser').ITestSuite} suite
|
|
98
|
+
* @returns {string}
|
|
99
|
+
*/
|
|
100
|
+
getSuiteResults(suite) {
|
|
101
|
+
return `${suite.passed} / ${suite.total} Passed (${getPercentage(suite.passed, suite.total)}%)`;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
*
|
|
106
|
+
* @param {import('..').ITarget} target
|
|
107
|
+
* @param {import('test-results-parser').ITestSuite} suite
|
|
108
|
+
*/
|
|
109
|
+
#getSuiteDurationText(target, suite) {
|
|
110
|
+
const duration = this.getSuiteDuration(target, suite);
|
|
111
|
+
return `${this.platform.bold('Duration')}: ${duration}`
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
*
|
|
116
|
+
* @param {import('..').ITarget} target
|
|
117
|
+
* @param {import('test-results-parser').ITestSuite} suite
|
|
118
|
+
*/
|
|
119
|
+
getSuiteDuration(target, suite) {
|
|
120
|
+
return getPrettyDuration(suite.duration, target.inputs.duration);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
*
|
|
125
|
+
* @param {import('test-results-parser').ITestSuite} suite
|
|
126
|
+
* @returns {string}
|
|
127
|
+
*/
|
|
128
|
+
getSuiteMetaDataText(suite) {
|
|
129
|
+
if (!suite || !suite.metadata) {
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const texts = [];
|
|
134
|
+
|
|
135
|
+
// webdriver io
|
|
136
|
+
if (suite.metadata.device && typeof suite.metadata.device === 'string') {
|
|
137
|
+
texts.push(`${suite.metadata.device}`);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
if (suite.metadata.platform && suite.metadata.platform.name && suite.metadata.platform.version) {
|
|
141
|
+
texts.push(`${suite.metadata.platform.name} ${suite.metadata.platform.version}`);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (suite.metadata.browser && suite.metadata.browser.name && suite.metadata.browser.version) {
|
|
145
|
+
texts.push(`${suite.metadata.browser.name} ${suite.metadata.browser.version}`);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// playwright
|
|
149
|
+
if (suite.metadata.hostname && typeof suite.metadata.hostname === 'string') {
|
|
150
|
+
texts.push(`${suite.metadata.hostname}`);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
return texts.join(' • ');
|
|
154
|
+
}
|
|
155
|
+
|
|
43
156
|
}
|
|
44
157
|
|
|
45
|
-
|
|
158
|
+
module.exports = { BaseTarget };
|
package/src/targets/chat.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
const request = require('phin-retry');
|
|
2
2
|
const { getTitleText, getResultText, truncate, getPrettyDuration } = require('../helpers/helper');
|
|
3
3
|
const extension_manager = require('../extensions');
|
|
4
|
-
const { HOOK, STATUS
|
|
4
|
+
const { HOOK, STATUS } = require('../helpers/constants');
|
|
5
5
|
const PerformanceTestResult = require('performance-results-parser/src/models/PerformanceTestResult');
|
|
6
6
|
const { getValidMetrics, getMetricValuesText } = require('../helpers/performance');
|
|
7
7
|
const logger = require('../utils/logger');
|
|
8
|
-
const {
|
|
8
|
+
const { BaseTarget } = require('./base.target');
|
|
9
9
|
|
|
10
10
|
async function run({ result, target }) {
|
|
11
11
|
setTargetInputs(target);
|
|
@@ -101,8 +101,9 @@ function setSuiteBlock({ result, target, payload }) {
|
|
|
101
101
|
}
|
|
102
102
|
|
|
103
103
|
function getSuiteSummary({ target, suite }) {
|
|
104
|
-
const
|
|
105
|
-
const
|
|
104
|
+
const tg = new ChatTarget({ target });
|
|
105
|
+
// const platform = getPlatform(TARGET.CHAT);
|
|
106
|
+
const text = tg.getSuiteSummaryText(target, suite);
|
|
106
107
|
return text;
|
|
107
108
|
}
|
|
108
109
|
|
|
@@ -260,6 +261,12 @@ async function handleErrors({ target, errors }) {
|
|
|
260
261
|
});
|
|
261
262
|
}
|
|
262
263
|
|
|
264
|
+
class ChatTarget extends BaseTarget {
|
|
265
|
+
constructor({ target }) {
|
|
266
|
+
super({ target });
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
263
270
|
module.exports = {
|
|
264
271
|
run,
|
|
265
272
|
handleErrors,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
const { BaseTarget } = require('./base.target');
|
|
2
2
|
const path = require('path');
|
|
3
|
+
const ctx = require('../utils/context.utils');
|
|
3
4
|
|
|
4
5
|
const DEFAULT_INPUTS = {};
|
|
5
6
|
|
|
@@ -18,9 +19,9 @@ class CustomTarget extends BaseTarget {
|
|
|
18
19
|
if (typeof this.inputs.load === 'string') {
|
|
19
20
|
const cwd = process.cwd();
|
|
20
21
|
const target_runner = require(path.join(cwd, this.inputs.load));
|
|
21
|
-
await target_runner.run({ target: this.target, result });
|
|
22
|
+
await target_runner.run({ target: this.target, result, ctx });
|
|
22
23
|
} else if (typeof this.inputs.load === 'function') {
|
|
23
|
-
await this.inputs.load({ target: this.target, result });
|
|
24
|
+
await this.inputs.load({ target: this.target, result, ctx });
|
|
24
25
|
} else {
|
|
25
26
|
throw `Invalid 'load' input in custom target - ${this.inputs.load}`;
|
|
26
27
|
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const logger = require('../utils/logger');
|
|
4
|
+
const { BaseTarget } = require('./base.target');
|
|
5
|
+
const context = require('../utils/context.utils');
|
|
6
|
+
|
|
7
|
+
const DEFAULT_INPUTS = {
|
|
8
|
+
output_file: process.env.GITHUB_OUTPUT, // Path to output file, defaults to GITHUB_OUTPUT env var
|
|
9
|
+
key: 'testbeats' // Key name for the output
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
const default_options = {
|
|
13
|
+
condition: 'passOrFail'
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
class GitHubOutputTarget extends BaseTarget {
|
|
17
|
+
constructor({ target }) {
|
|
18
|
+
super({ target });
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
async run({ result, target }) {
|
|
22
|
+
this.result = result;
|
|
23
|
+
this.setTargetInputs(target);
|
|
24
|
+
|
|
25
|
+
logger.info(`🔔 Writing results to GitHub Actions outputs...`);
|
|
26
|
+
return await this.writeToGitHubOutput({ target, result });
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
setTargetInputs(target) {
|
|
30
|
+
target.inputs = Object.assign({}, DEFAULT_INPUTS, target.inputs);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
async writeToGitHubOutput({ target, result }) {
|
|
34
|
+
const outputFile = target.inputs.output_file || process.env.GITHUB_OUTPUT;
|
|
35
|
+
|
|
36
|
+
if (!outputFile) {
|
|
37
|
+
throw new Error('GitHub output file path is required. Set GITHUB_OUTPUT environment variable or provide output_file in target inputs.');
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Ensure the directory exists
|
|
41
|
+
const outputDir = path.dirname(outputFile);
|
|
42
|
+
if (!fs.existsSync(outputDir)) {
|
|
43
|
+
fs.mkdirSync(outputDir, { recursive: true });
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const lines = []
|
|
47
|
+
lines.push(`${target.inputs.key}_results=${JSON.stringify(result)}`)
|
|
48
|
+
lines.push(`${target.inputs.key}_stores=${JSON.stringify(context.stores)}`)
|
|
49
|
+
const outputContent = lines.join('\n');
|
|
50
|
+
|
|
51
|
+
fs.appendFileSync(outputFile, outputContent);
|
|
52
|
+
|
|
53
|
+
logger.info(`✅ Successfully wrote results to ${outputFile}`);
|
|
54
|
+
return { success: true, key: target.inputs.key };
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
async handleErrors({ target, errors }) {
|
|
58
|
+
logger.error('GitHub Output target errors:', errors);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
module.exports = {
|
|
63
|
+
GitHubOutputTarget
|
|
64
|
+
};
|