testbeats 2.0.0

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.
@@ -0,0 +1,304 @@
1
+ const request = require('phin-retry');
2
+ const { getPercentage, truncate, getPrettyDuration } = require('../helpers/helper');
3
+ const { getValidMetrics, getMetricValuesText } = require('../helpers/performance');
4
+ const extension_manager = require('../extensions');
5
+ const { HOOK, STATUS } = require('../helpers/constants');
6
+
7
+ const TestResult = require('test-results-parser/src/models/TestResult');
8
+ const PerformanceTestResult = require('performance-results-parser/src/models/PerformanceTestResult');
9
+
10
+ /**
11
+ * @param {object} param0
12
+ * @param {PerformanceTestResult | TestResult} param0.result
13
+ * @returns
14
+ */
15
+ async function run({ result, target }) {
16
+ setTargetInputs(target);
17
+ const root_payload = getRootPayload();
18
+ const payload = getMainPayload(target);
19
+ if (result instanceof PerformanceTestResult) {
20
+ await setPerformancePayload({ result, target, payload, root_payload });
21
+ } else {
22
+ await setFunctionalPayload({ result, target, payload, root_payload });
23
+ }
24
+ setRootPayload(root_payload, payload);
25
+ return request.post({
26
+ url: target.inputs.url,
27
+ body: root_payload
28
+ });
29
+ }
30
+
31
+ async function setPerformancePayload({ result, target, payload, root_payload }) {
32
+ await extension_manager.run({ result, target, payload, root_payload, hook: HOOK.START });
33
+ setTitleBlock({ result, target, payload });
34
+ await setMainBlockForPerformance({ result, target, payload });
35
+ await extension_manager.run({ result, target, payload, root_payload, hook: HOOK.AFTER_SUMMARY });
36
+ await setTransactionBlock({ result, target, payload });
37
+ await extension_manager.run({ result, target, payload, root_payload, hook: HOOK.END });
38
+ }
39
+
40
+ async function setFunctionalPayload({ result, target, payload, root_payload }) {
41
+ await extension_manager.run({ result, target, payload, root_payload, hook: HOOK.START });
42
+ setTitleBlock({ result, target, payload });
43
+ setMainBlock({ result, target, payload });
44
+ await extension_manager.run({ result, target, payload, root_payload, hook: HOOK.AFTER_SUMMARY });
45
+ setSuiteBlock({ result, target, payload });
46
+ await extension_manager.run({ result, target, payload, root_payload, hook: HOOK.END });
47
+ }
48
+
49
+ function setTargetInputs(target) {
50
+ target.inputs = Object.assign({}, default_inputs, target.inputs);
51
+ if (target.inputs.publish === 'test-summary-slim') {
52
+ target.inputs.include_suites = false;
53
+ }
54
+ if (target.inputs.publish === 'failure-details') {
55
+ target.inputs.include_failure_details = true;
56
+ }
57
+ }
58
+
59
+ function getMainPayload(target) {
60
+ const main = {
61
+ "$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
62
+ "type": "AdaptiveCard",
63
+ "version": "1.0",
64
+ "body": [],
65
+ "actions": []
66
+ };
67
+ if (target.inputs.width) {
68
+ if (!main.msteams) {
69
+ main.msteams = {};
70
+ }
71
+ main.msteams.width = target.inputs.width;
72
+ }
73
+ return main;
74
+ }
75
+
76
+ function getTitleText(result, target) {
77
+ const title = target.inputs.title ? target.inputs.title : result.name;
78
+ if (target.inputs.title_suffix) {
79
+ return `${title} ${target.inputs.title_suffix}`;
80
+ }
81
+ return `${title}`;
82
+ }
83
+
84
+ function setTitleBlock({ result, target, payload }) {
85
+ const title = getTitleText(result, target);
86
+ const emoji = result.status === 'PASS' ? '✅' : '❌';
87
+ let text = '';
88
+ if (target.inputs.include_suites === false) {
89
+ text = `${emoji} ${title}`;
90
+ } else if (result.suites && result.suites.length > 1 || result.transactions && result.transactions.length > 1) {
91
+ text = title;
92
+ } else {
93
+ text = `${emoji} ${title}`;
94
+ }
95
+ if (target.inputs.title_link) {
96
+ text = `[${text}](${target.inputs.title_link})`
97
+ }
98
+ payload.body.push({
99
+ "type": "TextBlock",
100
+ "text": text,
101
+ "size": "medium",
102
+ "weight": "bolder",
103
+ "wrap": true
104
+ });
105
+ }
106
+
107
+ function setMainBlock({ result, target, payload }) {
108
+ const facts = [];
109
+ const percentage = getPercentage(result.passed, result.total);
110
+ facts.push({
111
+ "title": "Results:",
112
+ "value": `${result.passed} / ${result.total} Passed (${percentage}%)`
113
+ });
114
+ facts.push({
115
+ "title": "Duration:",
116
+ "value": `${getPrettyDuration(result.duration, target.inputs.duration)}`
117
+ });
118
+ payload.body.push({
119
+ "type": "FactSet",
120
+ "facts": facts
121
+ });
122
+ }
123
+
124
+ function setSuiteBlock({ result, target, payload }) {
125
+ let suite_attachments_length = 0;
126
+ if (target.inputs.include_suites) {
127
+ for (let i = 0; i < result.suites.length && suite_attachments_length < target.inputs.max_suites; i++) {
128
+ const suite = result.suites[i];
129
+ if (target.inputs.only_failures && suite.status !== 'FAIL') {
130
+ continue;
131
+ }
132
+ // if suites length eq to 1 then main block will include suite summary
133
+ if (result.suites.length > 1) {
134
+ payload.body.push(...getSuiteSummary({ suite, target }));
135
+ suite_attachments_length += 1;
136
+ }
137
+ if (target.inputs.include_failure_details) {
138
+ payload.body.push(...getFailureDetailsFactSets(suite));
139
+ }
140
+ }
141
+ }
142
+ }
143
+
144
+ function getSuiteSummary({ suite, target }) {
145
+ const percentage = getPercentage(suite.passed, suite.total);
146
+ const emoji = suite.status === 'PASS' ? '✅' : '❌';
147
+ return [
148
+ {
149
+ "type": "TextBlock",
150
+ "text": `${emoji} ${suite.name}`,
151
+ "isSubtle": true,
152
+ "weight": "bolder",
153
+ "wrap": true
154
+ },
155
+ {
156
+ "type": "FactSet",
157
+ "facts": [
158
+ {
159
+ "title": "Results:",
160
+ "value": `${suite.passed} / ${suite.total} Passed (${percentage}%)`
161
+ },
162
+ {
163
+ "title": "Duration:",
164
+ "value": `${getPrettyDuration(suite.duration, target.inputs.duration)}`
165
+ }
166
+ ]
167
+ }
168
+ ]
169
+ }
170
+
171
+ function getFailureDetailsFactSets(suite) {
172
+ const fact_sets = [];
173
+ const cases = suite.cases;
174
+ for (let i = 0; i < cases.length; i++) {
175
+ const test_case = cases[i];
176
+ if (test_case.status === 'FAIL') {
177
+ fact_sets.push({
178
+ "type": "FactSet",
179
+ "facts": [
180
+ {
181
+ "title": "Test:",
182
+ "value": test_case.name
183
+ },
184
+ {
185
+ "title": "Error:",
186
+ "value": truncate(test_case.failure, 150)
187
+ }
188
+ ]
189
+ });
190
+ }
191
+ }
192
+ return fact_sets;
193
+ }
194
+
195
+ function getRootPayload() {
196
+ return {
197
+ "type": "message",
198
+ "attachments": [
199
+ {
200
+ "contentType": "application/vnd.microsoft.card.adaptive",
201
+ "content": ''
202
+ }
203
+ ]
204
+ };
205
+ }
206
+
207
+ function setRootPayload(root_payload, payload) {
208
+ root_payload.attachments[0].content = payload;
209
+ }
210
+
211
+ /**
212
+ * @param {object} param0
213
+ * @param {PerformanceTestResult} param0.result
214
+ */
215
+ async function setMainBlockForPerformance({ result, target, payload }) {
216
+ const total = result.transactions.length;
217
+ const passed = result.transactions.filter(_transaction => _transaction.status === 'PASS').length;
218
+ const percentage = getPercentage(passed, total);
219
+ payload.body.push({
220
+ "type": "FactSet",
221
+ "facts": [
222
+ {
223
+ "title": "Results:",
224
+ "value": `${passed} / ${total} Passed (${percentage}%)`
225
+ }
226
+ ]
227
+ });
228
+ payload.body.push({
229
+ "type": "FactSet",
230
+ "facts": await getFactMetrics({ metrics: result.metrics, result, target })
231
+ });
232
+ }
233
+
234
+ async function getFactMetrics({ metrics, target, result }) {
235
+ const facts = [];
236
+ const valid_metrics = await getValidMetrics({ metrics, target, result });
237
+ for (let i = 0; i < valid_metrics.length; i++) {
238
+ const metric = valid_metrics[i];
239
+ facts.push({
240
+ "title": `${metric.name}:`,
241
+ "value": getMetricValuesText({ metrics, metric, target, result })
242
+ })
243
+ }
244
+ return facts;
245
+ }
246
+
247
+ /**
248
+ * @param {object} param0
249
+ * @param {PerformanceTestResult} param0.result
250
+ */
251
+ async function setTransactionBlock({ result, target, payload }) {
252
+ if (target.inputs.include_suites) {
253
+ for (let i = 0; i < result.transactions.length; i++) {
254
+ const transaction = result.transactions[i];
255
+ if (target.inputs.only_failures && transaction.status !== 'FAIL') {
256
+ continue;
257
+ }
258
+ // if transactions length eq to 1 then main block will include transaction summary
259
+ if (result.transactions.length > 1) {
260
+ const emoji = transaction.status === 'PASS' ? '✅' : '❌';
261
+ payload.body.push({
262
+ "type": "TextBlock",
263
+ "text": `${emoji} ${transaction.name}`,
264
+ "isSubtle": true,
265
+ "weight": "bolder",
266
+ "wrap": true
267
+ });
268
+ payload.body.push({
269
+ "type": "FactSet",
270
+ "facts": await getFactMetrics({ metrics: transaction.metrics, result, target })
271
+ });
272
+ }
273
+ }
274
+ }
275
+ }
276
+
277
+ const default_options = {
278
+ condition: STATUS.PASS_OR_FAIL
279
+ }
280
+
281
+ const default_inputs = {
282
+ publish: 'test-summary',
283
+ include_suites: true,
284
+ max_suites: 10,
285
+ only_failures: false,
286
+ include_failure_details: false,
287
+ width: '',
288
+ duration: '',
289
+ metrics: [
290
+ {
291
+ "name": "Samples",
292
+ },
293
+ {
294
+ "name": "Duration",
295
+ "condition": "always",
296
+ "fields": ["avg", "p95"]
297
+ }
298
+ ]
299
+ }
300
+
301
+ module.exports = {
302
+ run,
303
+ default_options
304
+ }