@allurereport/plugin-testops 3.3.0 → 3.4.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.
- package/README.md +8 -7
- package/dist/client.d.ts +38 -11
- package/dist/client.js +318 -67
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/logger.d.ts +24 -0
- package/dist/logger.js +129 -0
- package/dist/model.d.ts +80 -4
- package/dist/plugin.d.ts +9 -9
- package/dist/plugin.js +329 -101
- package/dist/uploadCategory.d.ts +3 -0
- package/dist/uploadCategory.js +20 -0
- package/dist/utils/categories.d.ts +4 -0
- package/dist/utils/categories.js +82 -0
- package/dist/utils.d.ts +5 -2
- package/dist/utils.js +34 -1
- package/package.json +22 -33
package/dist/plugin.js
CHANGED
|
@@ -9,155 +9,383 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
|
|
|
9
9
|
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
10
10
|
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
11
11
|
};
|
|
12
|
-
var
|
|
12
|
+
var _TestOpsPlugin_instances, _TestOpsPlugin_logger, _TestOpsPlugin_ci, _TestOpsPlugin_client, _TestOpsPlugin_enabled, _TestOpsPlugin_launchName, _TestOpsPlugin_launchTags, _TestOpsPlugin_uploadedTestResultsIds, _TestOpsPlugin_autocloseLaunch, _TestOpsPlugin_uploadQualityGateResults, _TestOpsPlugin_uploadGlobalErrors, _TestOpsPlugin_uploadGlobalAttachments, _TestOpsPlugin_uploadTestResults, _TestOpsPlugin_upload, _TestOpsPlugin_trsToUpload, _TestOpsPlugin_enrichWithCategories, _TestOpsPlugin_syncLaunchCategories, _TestOpsPlugin_collectCategoryNamesByExternalId, _TestOpsPlugin_assignCreatedCategoryIds, _TestOpsPlugin_startUpload, _TestOpsPlugin_stopUpload;
|
|
13
|
+
import { env } from "node:process";
|
|
13
14
|
import { detect } from "@allurereport/ci";
|
|
14
15
|
import { getWorstStatus } from "@allurereport/core-api";
|
|
15
|
-
import { createPluginSummary
|
|
16
|
-
import {
|
|
17
|
-
import
|
|
16
|
+
import { createPluginSummary } from "@allurereport/plugin-api";
|
|
17
|
+
import { uniqBy, stubTrue } from "lodash-es";
|
|
18
|
+
import { bold } from "yoctocolors";
|
|
18
19
|
import { TestOpsClient } from "./client.js";
|
|
19
|
-
import {
|
|
20
|
-
|
|
20
|
+
import { Logger } from "./logger.js";
|
|
21
|
+
import { toUploadCategory } from "./uploadCategory.js";
|
|
22
|
+
import { attachmentsResolverFactory, fixturesResolverFactory, resolvePluginOptions, unwrapStepsAttachments, } from "./utils.js";
|
|
23
|
+
const categoryDisplayName = (cat) => cat.name ?? cat.grouping?.[0]?.name ?? cat.grouping?.[0]?.value ?? cat.grouping?.[0]?.key ?? cat.externalId;
|
|
24
|
+
export class TestOpsPlugin {
|
|
21
25
|
constructor(options) {
|
|
22
|
-
|
|
26
|
+
_TestOpsPlugin_instances.add(this);
|
|
23
27
|
this.options = options;
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
28
|
+
_TestOpsPlugin_logger.set(this, new Logger("TestOpsPlugin"));
|
|
29
|
+
_TestOpsPlugin_ci.set(this, void 0);
|
|
30
|
+
_TestOpsPlugin_client.set(this, void 0);
|
|
31
|
+
_TestOpsPlugin_enabled.set(this, false);
|
|
32
|
+
_TestOpsPlugin_launchName.set(this, "");
|
|
33
|
+
_TestOpsPlugin_launchTags.set(this, []);
|
|
34
|
+
_TestOpsPlugin_uploadedTestResultsIds.set(this, new Set());
|
|
35
|
+
_TestOpsPlugin_autocloseLaunch.set(this, void 0);
|
|
36
|
+
const { accessToken, endpoint, projectId, launchName, launchTags, autocloseLaunch = true, } = resolvePluginOptions(options);
|
|
37
|
+
__classPrivateFieldSet(this, _TestOpsPlugin_ci, detect(), "f");
|
|
31
38
|
if ([accessToken, endpoint, projectId].every(Boolean)) {
|
|
32
|
-
__classPrivateFieldSet(this,
|
|
39
|
+
__classPrivateFieldSet(this, _TestOpsPlugin_enabled, true, "f");
|
|
40
|
+
__classPrivateFieldSet(this, _TestOpsPlugin_client, new TestOpsClient({
|
|
33
41
|
baseUrl: endpoint,
|
|
34
42
|
accessToken,
|
|
35
43
|
projectId,
|
|
36
44
|
}), "f");
|
|
37
|
-
__classPrivateFieldSet(this,
|
|
38
|
-
__classPrivateFieldSet(this,
|
|
45
|
+
__classPrivateFieldSet(this, _TestOpsPlugin_launchName, launchName, "f");
|
|
46
|
+
__classPrivateFieldSet(this, _TestOpsPlugin_launchTags, launchTags, "f");
|
|
39
47
|
}
|
|
48
|
+
__classPrivateFieldSet(this, _TestOpsPlugin_autocloseLaunch, autocloseLaunch, "f");
|
|
40
49
|
if (!accessToken) {
|
|
41
|
-
|
|
50
|
+
__classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").warn(`Allure TestOps ${bold("access token")} is missing. Please provide a valid access token in the plugin options.`);
|
|
42
51
|
}
|
|
43
52
|
if (!endpoint) {
|
|
44
|
-
|
|
53
|
+
__classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").warn(`Allure TestOps ${bold("endpoint")} is missing. Please provide a valid endpoint in the plugin options.`);
|
|
45
54
|
}
|
|
46
55
|
if (!projectId) {
|
|
47
|
-
|
|
56
|
+
__classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").warn(`Allure TestOps ${bold("project ID")} is missing. Please provide a valid project ID in the plugin options.`);
|
|
48
57
|
}
|
|
49
58
|
}
|
|
50
59
|
get ciMode() {
|
|
51
|
-
return __classPrivateFieldGet(this,
|
|
60
|
+
return __classPrivateFieldGet(this, _TestOpsPlugin_ci, "f") && __classPrivateFieldGet(this, _TestOpsPlugin_ci, "f").type !== "local";
|
|
52
61
|
}
|
|
53
|
-
async start(
|
|
54
|
-
if (!__classPrivateFieldGet(this,
|
|
62
|
+
async start(context, store) {
|
|
63
|
+
if (!__classPrivateFieldGet(this, _TestOpsPlugin_enabled, "f")) {
|
|
55
64
|
return;
|
|
56
65
|
}
|
|
57
|
-
|
|
58
|
-
await __classPrivateFieldGet(this,
|
|
59
|
-
|
|
66
|
+
__classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").verbose("Starting upload…");
|
|
67
|
+
await __classPrivateFieldGet(this, _TestOpsPlugin_instances, "m", _TestOpsPlugin_startUpload).call(this);
|
|
68
|
+
await __classPrivateFieldGet(this, _TestOpsPlugin_instances, "m", _TestOpsPlugin_upload).call(this, store, { issueNewToken: false, context, stage: "start" });
|
|
69
|
+
__classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").info(`Allure TestOps Launch: ${__classPrivateFieldGet(this, _TestOpsPlugin_client, "f").launchUrl}`);
|
|
60
70
|
}
|
|
61
|
-
async update(
|
|
62
|
-
if (!__classPrivateFieldGet(this,
|
|
71
|
+
async update(context, store) {
|
|
72
|
+
if (!__classPrivateFieldGet(this, _TestOpsPlugin_enabled, "f")) {
|
|
63
73
|
return;
|
|
64
74
|
}
|
|
65
|
-
|
|
75
|
+
__classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").verbose("Updating (uploading new results)…");
|
|
76
|
+
await __classPrivateFieldGet(this, _TestOpsPlugin_instances, "m", _TestOpsPlugin_upload).call(this, store, { context, stage: "update" });
|
|
66
77
|
}
|
|
67
|
-
async done(
|
|
68
|
-
if (!__classPrivateFieldGet(this,
|
|
78
|
+
async done(context, store) {
|
|
79
|
+
if (!__classPrivateFieldGet(this, _TestOpsPlugin_enabled, "f")) {
|
|
69
80
|
return;
|
|
70
81
|
}
|
|
71
|
-
const allTrs =
|
|
82
|
+
const allTrs = await store.allTestResults({
|
|
83
|
+
filter: this.options.filter,
|
|
84
|
+
includeHidden: false,
|
|
85
|
+
});
|
|
72
86
|
const worstStatus = getWorstStatus(allTrs.map(({ status }) => status));
|
|
73
|
-
|
|
74
|
-
await __classPrivateFieldGet(this,
|
|
87
|
+
__classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").verbose("Finalizing upload…");
|
|
88
|
+
await __classPrivateFieldGet(this, _TestOpsPlugin_instances, "m", _TestOpsPlugin_upload).call(this, store, { context, stage: "done" });
|
|
89
|
+
await __classPrivateFieldGet(this, _TestOpsPlugin_instances, "m", _TestOpsPlugin_stopUpload).call(this, worstStatus || "unknown");
|
|
90
|
+
const launchId = __classPrivateFieldGet(this, _TestOpsPlugin_client, "f").launchId;
|
|
91
|
+
if (typeof launchId !== "number") {
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
if (!__classPrivateFieldGet(this, _TestOpsPlugin_autocloseLaunch, "f")) {
|
|
95
|
+
__classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").info(`Upload finished. Allure TestOps Launch: ${__classPrivateFieldGet(this, _TestOpsPlugin_client, "f").launchUrl}`);
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
try {
|
|
99
|
+
await __classPrivateFieldGet(this, _TestOpsPlugin_client, "f").closeLaunch(launchId);
|
|
100
|
+
}
|
|
101
|
+
catch (err) {
|
|
102
|
+
if (err instanceof Error) {
|
|
103
|
+
__classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").debug(`Failed to close launch: ${err.message}`);
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
__classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").debug("Failed to close launch");
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
__classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").info(`Upload finished. Allure TestOps Launch: ${__classPrivateFieldGet(this, _TestOpsPlugin_client, "f").launchUrl}`);
|
|
75
110
|
}
|
|
76
111
|
async info(context, store) {
|
|
77
|
-
if (!__classPrivateFieldGet(this,
|
|
112
|
+
if (!__classPrivateFieldGet(this, _TestOpsPlugin_enabled, "f")) {
|
|
78
113
|
return undefined;
|
|
79
114
|
}
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
};
|
|
115
|
+
if (!__classPrivateFieldGet(this, _TestOpsPlugin_client, "f").launchUrl) {
|
|
116
|
+
return undefined;
|
|
117
|
+
}
|
|
118
|
+
const summary = await createPluginSummary({
|
|
119
|
+
name: __classPrivateFieldGet(this, _TestOpsPlugin_launchName, "f"),
|
|
120
|
+
plugin: "TestOps",
|
|
121
|
+
meta: {
|
|
122
|
+
reportId: context.reportUuid,
|
|
123
|
+
},
|
|
124
|
+
filter: this.options.filter,
|
|
125
|
+
history: context.history,
|
|
126
|
+
ci: context.ci,
|
|
127
|
+
store,
|
|
128
|
+
});
|
|
129
|
+
summary.remoteHref = __classPrivateFieldGet(this, _TestOpsPlugin_client, "f").launchUrl;
|
|
130
|
+
return summary;
|
|
94
131
|
}
|
|
95
132
|
}
|
|
96
|
-
|
|
97
|
-
const
|
|
98
|
-
const
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
133
|
+
_TestOpsPlugin_logger = new WeakMap(), _TestOpsPlugin_ci = new WeakMap(), _TestOpsPlugin_client = new WeakMap(), _TestOpsPlugin_enabled = new WeakMap(), _TestOpsPlugin_launchName = new WeakMap(), _TestOpsPlugin_launchTags = new WeakMap(), _TestOpsPlugin_uploadedTestResultsIds = new WeakMap(), _TestOpsPlugin_autocloseLaunch = new WeakMap(), _TestOpsPlugin_instances = new WeakSet(), _TestOpsPlugin_uploadQualityGateResults = async function _TestOpsPlugin_uploadQualityGateResults(store) {
|
|
134
|
+
const results = await store.qualityGateResults();
|
|
135
|
+
const uniqueResults = uniqBy(results.filter(({ success }) => !success), ({ rule, environment }) => `${rule}-${environment}`);
|
|
136
|
+
if (uniqueResults.length === 0) {
|
|
137
|
+
__classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").verbose("No quality gate results to upload");
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
const progressBar = __classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").progressBar("Uploading quality gate results");
|
|
141
|
+
try {
|
|
142
|
+
progressBar.update(0);
|
|
143
|
+
await __classPrivateFieldGet(this, _TestOpsPlugin_client, "f").uploadQualityGateResults(uniqueResults, (percent, total) => {
|
|
144
|
+
progressBar.update(percent / total);
|
|
145
|
+
});
|
|
146
|
+
progressBar.update(1);
|
|
147
|
+
progressBar.terminate();
|
|
148
|
+
}
|
|
149
|
+
catch (error) {
|
|
150
|
+
progressBar.terminate();
|
|
151
|
+
if (__classPrivateFieldGet(this, _TestOpsPlugin_client, "f").isTestOpsClientError(error)) {
|
|
152
|
+
__classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").error(`Failed to upload quality gate results: ${error.response.data.message}`);
|
|
153
|
+
__classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").debug(error.response?.data);
|
|
154
|
+
}
|
|
155
|
+
else if (error instanceof Error) {
|
|
156
|
+
__classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").error(`Failed to upload quality gate results: ${error.message}`);
|
|
157
|
+
}
|
|
158
|
+
else {
|
|
159
|
+
__classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").error("Failed to upload quality gate results");
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}, _TestOpsPlugin_uploadGlobalErrors = async function _TestOpsPlugin_uploadGlobalErrors(store) {
|
|
163
|
+
const results = await store.allGlobalErrors();
|
|
164
|
+
if (results.length === 0) {
|
|
165
|
+
__classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").verbose("No global errors to upload");
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
const progressBar = __classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").progressBar("Uploading global errors");
|
|
169
|
+
try {
|
|
170
|
+
progressBar.update(0);
|
|
171
|
+
await __classPrivateFieldGet(this, _TestOpsPlugin_client, "f").uploadGlobalErrors(results, (percent, total) => {
|
|
172
|
+
progressBar.update(percent / total);
|
|
173
|
+
});
|
|
174
|
+
progressBar.update(1);
|
|
175
|
+
progressBar.terminate();
|
|
176
|
+
}
|
|
177
|
+
catch (error) {
|
|
178
|
+
progressBar.terminate();
|
|
179
|
+
if (__classPrivateFieldGet(this, _TestOpsPlugin_client, "f").isTestOpsClientError(error)) {
|
|
180
|
+
__classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").error(`Failed to upload global errors: ${error.response.data.message}`);
|
|
181
|
+
__classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").debug(error.response?.data);
|
|
182
|
+
}
|
|
183
|
+
else if (error instanceof Error) {
|
|
184
|
+
__classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").error(`Failed to upload global errors: ${error.message}`);
|
|
185
|
+
}
|
|
186
|
+
else {
|
|
187
|
+
__classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").error("Failed to upload global errors");
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}, _TestOpsPlugin_uploadGlobalAttachments = async function _TestOpsPlugin_uploadGlobalAttachments(store) {
|
|
191
|
+
const attachments = await store.allGlobalAttachments();
|
|
192
|
+
if (attachments.length === 0) {
|
|
193
|
+
__classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").debug("No global attachments to upload");
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
const progressBar = __classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").progressBar("Uploading global attachments");
|
|
197
|
+
try {
|
|
198
|
+
progressBar.update(0);
|
|
199
|
+
await __classPrivateFieldGet(this, _TestOpsPlugin_client, "f").uploadGlobalAttachments({
|
|
200
|
+
attachments,
|
|
201
|
+
attachmentsResolver: async (attachmentLink) => {
|
|
202
|
+
const content = await store.attachmentContentById(attachmentLink.id);
|
|
203
|
+
const body = await content?.readContent(async (stream) => stream);
|
|
204
|
+
const attachmentName = attachmentLink.name || attachmentLink.originalFileName;
|
|
205
|
+
if (attachmentName === undefined || body === undefined) {
|
|
206
|
+
return undefined;
|
|
207
|
+
}
|
|
208
|
+
return {
|
|
209
|
+
originalFileName: attachmentName,
|
|
210
|
+
contentType: attachmentLink.contentType ?? "application/octet-stream",
|
|
211
|
+
content: body,
|
|
212
|
+
};
|
|
213
|
+
},
|
|
214
|
+
onProgress: (percent, total) => {
|
|
215
|
+
progressBar.update(percent / total);
|
|
216
|
+
},
|
|
217
|
+
});
|
|
218
|
+
progressBar.update(1);
|
|
219
|
+
progressBar.terminate();
|
|
220
|
+
}
|
|
221
|
+
catch (error) {
|
|
222
|
+
progressBar.terminate();
|
|
223
|
+
if (__classPrivateFieldGet(this, _TestOpsPlugin_client, "f").isTestOpsClientError(error)) {
|
|
224
|
+
__classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").error(`Failed to upload global attachments: ${error.response.data.message}`);
|
|
225
|
+
__classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").debug(error.response?.data);
|
|
226
|
+
}
|
|
227
|
+
else if (error instanceof Error) {
|
|
228
|
+
__classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").error(`Failed to upload global attachments: ${error.message}`);
|
|
229
|
+
}
|
|
230
|
+
else {
|
|
231
|
+
__classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").error("Failed to upload global attachments");
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}, _TestOpsPlugin_uploadTestResults = async function _TestOpsPlugin_uploadTestResults(store, trsToUpload, environments) {
|
|
235
|
+
const totalCount = trsToUpload.length;
|
|
236
|
+
__classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").info(`Preparing to upload ${bold(totalCount.toString())} ${totalCount > 1 ? "test results" : "test result"}`);
|
|
237
|
+
const trsProgressBar = __classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").progressBarCounter("Uploading test results", totalCount);
|
|
238
|
+
const uploadedTrs = await __classPrivateFieldGet(this, _TestOpsPlugin_client, "f").uploadTestResults({
|
|
239
|
+
attachmentsResolver: attachmentsResolverFactory(store),
|
|
240
|
+
fixturesResolver: fixturesResolverFactory(store),
|
|
241
|
+
environments,
|
|
242
|
+
trs: trsToUpload,
|
|
243
|
+
onProgress: () => trsProgressBar.tick(),
|
|
105
244
|
});
|
|
245
|
+
uploadedTrs.forEach((tr) => {
|
|
246
|
+
__classPrivateFieldGet(this, _TestOpsPlugin_uploadedTestResultsIds, "f").add(tr.id);
|
|
247
|
+
});
|
|
248
|
+
const uploadedCount = uploadedTrs.length;
|
|
249
|
+
trsProgressBar.update(uploadedCount / totalCount);
|
|
250
|
+
trsProgressBar.terminate();
|
|
251
|
+
if (uploadedCount === 0) {
|
|
252
|
+
__classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").warn("No test results were uploaded");
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
__classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").info(`Uploaded ${uploadedCount} ${uploadedCount > 1 ? "test results" : "test result"}`);
|
|
256
|
+
}, _TestOpsPlugin_upload = async function _TestOpsPlugin_upload(store, options = {}) {
|
|
257
|
+
const { issueNewToken = true, context, stage } = options;
|
|
258
|
+
const trsToUpload = await __classPrivateFieldGet(this, _TestOpsPlugin_instances, "m", _TestOpsPlugin_trsToUpload).call(this, store);
|
|
106
259
|
if (trsToUpload.length === 0) {
|
|
260
|
+
if (stage == "update") {
|
|
261
|
+
__classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").info("No new test results to upload");
|
|
262
|
+
}
|
|
263
|
+
if (stage === "done") {
|
|
264
|
+
__classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").info("No test results to upload");
|
|
265
|
+
}
|
|
107
266
|
return;
|
|
108
267
|
}
|
|
109
|
-
const allTrsWithAttachments = trsToUpload.map((tr) => {
|
|
110
|
-
return {
|
|
111
|
-
...tr,
|
|
112
|
-
steps: unwrapStepsAttachments(tr.steps),
|
|
113
|
-
};
|
|
114
|
-
});
|
|
115
268
|
if (issueNewToken) {
|
|
116
|
-
|
|
269
|
+
__classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").verbose("Issuing new OAuth token");
|
|
270
|
+
await __classPrivateFieldGet(this, _TestOpsPlugin_client, "f").issueOauthToken();
|
|
117
271
|
}
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
await __classPrivateFieldGet(this,
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
}));
|
|
137
|
-
},
|
|
138
|
-
fixturesResolver: async (tr) => {
|
|
139
|
-
const fxts = await store.fixturesByTrId(tr.id);
|
|
140
|
-
return fxts.map((fxt) => ({
|
|
141
|
-
...fxt,
|
|
142
|
-
type: fxt.type.toUpperCase(),
|
|
143
|
-
steps: unwrapStepsAttachments(fxt.steps),
|
|
144
|
-
}));
|
|
272
|
+
await __classPrivateFieldGet(this, _TestOpsPlugin_client, "f").createSession(env);
|
|
273
|
+
await __classPrivateFieldGet(this, _TestOpsPlugin_instances, "m", _TestOpsPlugin_uploadGlobalAttachments).call(this, store);
|
|
274
|
+
await __classPrivateFieldGet(this, _TestOpsPlugin_instances, "m", _TestOpsPlugin_uploadGlobalErrors).call(this, store);
|
|
275
|
+
await __classPrivateFieldGet(this, _TestOpsPlugin_instances, "m", _TestOpsPlugin_uploadQualityGateResults).call(this, store);
|
|
276
|
+
const environments = await store.allEnvironmentIdentities();
|
|
277
|
+
const contextCategories = context?.categories ?? [];
|
|
278
|
+
const trsEnrichedWithCategories = await __classPrivateFieldGet(this, _TestOpsPlugin_instances, "m", _TestOpsPlugin_enrichWithCategories).call(this, store, trsToUpload, contextCategories);
|
|
279
|
+
await __classPrivateFieldGet(this, _TestOpsPlugin_instances, "m", _TestOpsPlugin_syncLaunchCategories).call(this, trsEnrichedWithCategories, contextCategories);
|
|
280
|
+
await __classPrivateFieldGet(this, _TestOpsPlugin_instances, "m", _TestOpsPlugin_uploadTestResults).call(this, store, trsEnrichedWithCategories, environments);
|
|
281
|
+
}, _TestOpsPlugin_trsToUpload = async function _TestOpsPlugin_trsToUpload(store) {
|
|
282
|
+
const filter = this.options.filter ?? stubTrue;
|
|
283
|
+
const filteredTrs = await store.allTestResults({
|
|
284
|
+
filter: (tr) => {
|
|
285
|
+
const uploaded = __classPrivateFieldGet(this, _TestOpsPlugin_uploadedTestResultsIds, "f").has(tr.id);
|
|
286
|
+
if (uploaded) {
|
|
287
|
+
return false;
|
|
288
|
+
}
|
|
289
|
+
return filter(tr);
|
|
145
290
|
},
|
|
291
|
+
includeHidden: false,
|
|
146
292
|
});
|
|
147
|
-
|
|
148
|
-
},
|
|
149
|
-
|
|
293
|
+
return filteredTrs;
|
|
294
|
+
}, _TestOpsPlugin_enrichWithCategories = async function _TestOpsPlugin_enrichWithCategories(store, trs, contextCategories) {
|
|
295
|
+
return Promise.all(trs.map(async (tr) => {
|
|
296
|
+
const environmentId = await store.environmentIdByTrId(tr.id);
|
|
297
|
+
const base = {
|
|
298
|
+
...tr,
|
|
299
|
+
...(environmentId ? { environment: environmentId } : {}),
|
|
300
|
+
steps: unwrapStepsAttachments(tr.steps),
|
|
301
|
+
};
|
|
302
|
+
const category = toUploadCategory(base, contextCategories ?? []);
|
|
303
|
+
if (category) {
|
|
304
|
+
base.category = category;
|
|
305
|
+
}
|
|
306
|
+
return base;
|
|
307
|
+
}));
|
|
308
|
+
}, _TestOpsPlugin_syncLaunchCategories = async function _TestOpsPlugin_syncLaunchCategories(trs, contextCategories) {
|
|
309
|
+
const categoryNamesByExternalId = __classPrivateFieldGet(this, _TestOpsPlugin_instances, "m", _TestOpsPlugin_collectCategoryNamesByExternalId).call(this, trs);
|
|
310
|
+
if (categoryNamesByExternalId.size === 0) {
|
|
150
311
|
return;
|
|
151
312
|
}
|
|
152
|
-
|
|
153
|
-
|
|
313
|
+
const bulkItems = [];
|
|
314
|
+
const seenExternalIds = new Set();
|
|
315
|
+
for (const tr of trs) {
|
|
316
|
+
const cat = tr.category;
|
|
317
|
+
if (!cat?.externalId)
|
|
318
|
+
continue;
|
|
319
|
+
if (seenExternalIds.has(cat.externalId))
|
|
320
|
+
continue;
|
|
321
|
+
seenExternalIds.add(cat.externalId);
|
|
322
|
+
bulkItems.push({
|
|
323
|
+
externalId: cat.externalId,
|
|
324
|
+
name: categoryNamesByExternalId.get(cat.externalId) ?? categoryDisplayName(cat),
|
|
325
|
+
hide: cat.hide,
|
|
326
|
+
expand: cat.expand,
|
|
327
|
+
});
|
|
328
|
+
}
|
|
329
|
+
const rankByExternalId = new Map();
|
|
330
|
+
for (const c of contextCategories) {
|
|
331
|
+
if (!rankByExternalId.has(c.id)) {
|
|
332
|
+
rankByExternalId.set(c.id, c.index);
|
|
333
|
+
}
|
|
334
|
+
if (!rankByExternalId.has(c.name)) {
|
|
335
|
+
rankByExternalId.set(c.name, c.index);
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
const ranked = bulkItems.map((item, i) => ({
|
|
339
|
+
item,
|
|
340
|
+
i,
|
|
341
|
+
rank: rankByExternalId.get(item.externalId),
|
|
342
|
+
}));
|
|
343
|
+
ranked.sort((a, b) => {
|
|
344
|
+
const ar = a.rank ?? Number.POSITIVE_INFINITY;
|
|
345
|
+
const br = b.rank ?? Number.POSITIVE_INFINITY;
|
|
346
|
+
if (ar !== br)
|
|
347
|
+
return ar - br;
|
|
348
|
+
return a.i - b.i;
|
|
349
|
+
});
|
|
350
|
+
const orderedBulkItems = ranked.map((r) => r.item);
|
|
351
|
+
const launchId = __classPrivateFieldGet(this, _TestOpsPlugin_client, "f").launchId;
|
|
352
|
+
try {
|
|
353
|
+
const created = await __classPrivateFieldGet(this, _TestOpsPlugin_client, "f").createLaunchCategoriesBulk(launchId, orderedBulkItems);
|
|
354
|
+
const categoryIdByExternalId = new Map(created.map((r) => [r.externalId, r.id]));
|
|
355
|
+
__classPrivateFieldGet(this, _TestOpsPlugin_instances, "m", _TestOpsPlugin_assignCreatedCategoryIds).call(this, trs, categoryIdByExternalId);
|
|
356
|
+
}
|
|
357
|
+
catch {
|
|
358
|
+
}
|
|
359
|
+
}, _TestOpsPlugin_collectCategoryNamesByExternalId = function _TestOpsPlugin_collectCategoryNamesByExternalId(trs) {
|
|
360
|
+
const map = new Map();
|
|
361
|
+
for (const tr of trs) {
|
|
362
|
+
const cat = tr.category;
|
|
363
|
+
if (cat?.externalId) {
|
|
364
|
+
map.set(cat.externalId, categoryDisplayName(cat));
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
return map;
|
|
368
|
+
}, _TestOpsPlugin_assignCreatedCategoryIds = function _TestOpsPlugin_assignCreatedCategoryIds(trs, idByExternalId) {
|
|
369
|
+
for (const tr of trs) {
|
|
370
|
+
const cat = tr.category;
|
|
371
|
+
if (!cat?.externalId) {
|
|
372
|
+
continue;
|
|
373
|
+
}
|
|
374
|
+
const id = idByExternalId.get(cat.externalId);
|
|
375
|
+
if (typeof id === "number") {
|
|
376
|
+
tr.category = { ...cat, id };
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
}, _TestOpsPlugin_startUpload = async function _TestOpsPlugin_startUpload() {
|
|
380
|
+
await __classPrivateFieldGet(this, _TestOpsPlugin_client, "f").issueOauthToken();
|
|
381
|
+
await __classPrivateFieldGet(this, _TestOpsPlugin_client, "f").createLaunch(__classPrivateFieldGet(this, _TestOpsPlugin_launchName, "f"), __classPrivateFieldGet(this, _TestOpsPlugin_launchTags, "f"));
|
|
154
382
|
if (!this.ciMode) {
|
|
155
383
|
return;
|
|
156
384
|
}
|
|
157
|
-
await __classPrivateFieldGet(this,
|
|
158
|
-
},
|
|
159
|
-
if (!this.ciMode
|
|
385
|
+
await __classPrivateFieldGet(this, _TestOpsPlugin_client, "f").startUpload(__classPrivateFieldGet(this, _TestOpsPlugin_ci, "f"));
|
|
386
|
+
}, _TestOpsPlugin_stopUpload = async function _TestOpsPlugin_stopUpload(status) {
|
|
387
|
+
if (!this.ciMode) {
|
|
160
388
|
return;
|
|
161
389
|
}
|
|
162
|
-
await __classPrivateFieldGet(this,
|
|
390
|
+
await __classPrivateFieldGet(this, _TestOpsPlugin_client, "f").stopUpload(__classPrivateFieldGet(this, _TestOpsPlugin_ci, "f"), status);
|
|
163
391
|
};
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { CategoryDefinition } from "@allurereport/core-api";
|
|
2
|
+
import type { TestResultWithCategories, UploadCategory } from "./model.js";
|
|
3
|
+
export declare const toUploadCategory: (tr: TestResultWithCategories, contextCategories: CategoryDefinition[]) => UploadCategory | undefined;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { buildUploadCategoryGrouping, toUploadCategoryFromContext } from "./utils/categories.js";
|
|
2
|
+
export const toUploadCategory = (tr, contextCategories) => {
|
|
3
|
+
const c = tr.categories?.[0];
|
|
4
|
+
if (c?.name) {
|
|
5
|
+
const externalId = c.id ?? c.name;
|
|
6
|
+
const { hide, expand } = c || {};
|
|
7
|
+
const groupingFromTestResult = c.grouping?.length ? c.grouping : undefined;
|
|
8
|
+
const contextCategory = groupingFromTestResult === undefined
|
|
9
|
+
? contextCategories.find((category) => category.id === externalId || category.name === c.name)
|
|
10
|
+
: undefined;
|
|
11
|
+
return {
|
|
12
|
+
externalId,
|
|
13
|
+
name: c.name,
|
|
14
|
+
grouping: groupingFromTestResult ?? (contextCategory && buildUploadCategoryGrouping(tr, contextCategory)),
|
|
15
|
+
hide,
|
|
16
|
+
expand,
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
return toUploadCategoryFromContext(tr, contextCategories);
|
|
20
|
+
};
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { CategoryDefinition } from "@allurereport/core-api";
|
|
2
|
+
import type { TestResultWithCategories, UploadCategory } from "../model.js";
|
|
3
|
+
export declare const toUploadCategoryFromContext: (tr: TestResultWithCategories, categories: CategoryDefinition[]) => UploadCategory | undefined;
|
|
4
|
+
export declare const buildUploadCategoryGrouping: (tr: TestResultWithCategories, category: CategoryDefinition) => UploadCategory["grouping"];
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { EMPTY_VALUE, extractErrorMatchingData, findLastByLabelName, matchCategory } from "@allurereport/core-api";
|
|
2
|
+
const formatGroupName = (key, value) => `${key}: ${value === EMPTY_VALUE ? `No ${key}` : value}`;
|
|
3
|
+
const groupValue = (selector, tr) => {
|
|
4
|
+
const labels = tr.labels ?? [];
|
|
5
|
+
if (selector === "flaky") {
|
|
6
|
+
const value = tr.flaky ? "true" : "false";
|
|
7
|
+
return { key: "flaky", value, name: formatGroupName("flaky", value) };
|
|
8
|
+
}
|
|
9
|
+
if (selector === "transition") {
|
|
10
|
+
const value = tr.transition ?? EMPTY_VALUE;
|
|
11
|
+
return { key: "transition", value, name: formatGroupName("transition", value) };
|
|
12
|
+
}
|
|
13
|
+
if (selector === "status") {
|
|
14
|
+
const value = tr.status ?? "unknown";
|
|
15
|
+
return { key: "status", value, name: formatGroupName("status", value) };
|
|
16
|
+
}
|
|
17
|
+
if (selector === "environment") {
|
|
18
|
+
const value = tr.environment?.trim() ? tr.environment : EMPTY_VALUE;
|
|
19
|
+
return { key: "environment", value, name: formatGroupName("environment", value) };
|
|
20
|
+
}
|
|
21
|
+
if (selector === "owner") {
|
|
22
|
+
const value = findLastByLabelName(labels, "owner") ?? EMPTY_VALUE;
|
|
23
|
+
return { key: "owner", value, name: formatGroupName("owner", value) };
|
|
24
|
+
}
|
|
25
|
+
if (selector === "severity") {
|
|
26
|
+
const value = findLastByLabelName(labels, "severity") ?? "normal";
|
|
27
|
+
return { key: "severity", value, name: formatGroupName("severity", value) };
|
|
28
|
+
}
|
|
29
|
+
if (selector === "layer") {
|
|
30
|
+
const value = findLastByLabelName(labels, "layer") ?? EMPTY_VALUE;
|
|
31
|
+
return { key: "layer", value, name: formatGroupName("layer", value) };
|
|
32
|
+
}
|
|
33
|
+
const labelName = selector.label;
|
|
34
|
+
const labelValue = findLastByLabelName(labels, labelName) ?? EMPTY_VALUE;
|
|
35
|
+
return { key: labelName, value: labelValue, name: formatGroupName(labelName, labelValue) };
|
|
36
|
+
};
|
|
37
|
+
const buildGrouping = (tr, category) => {
|
|
38
|
+
const grouping = category.groupBy?.map((selector) => groupValue(selector, tr)) ?? [];
|
|
39
|
+
if (category.groupByMessage) {
|
|
40
|
+
const messageValue = tr.error?.message?.trim() ? tr.error.message : EMPTY_VALUE;
|
|
41
|
+
grouping.push({
|
|
42
|
+
key: "message",
|
|
43
|
+
value: messageValue,
|
|
44
|
+
name: formatGroupName("message", messageValue),
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
if (category.groupEnvironments) {
|
|
48
|
+
const historyValue = tr.historyId ?? tr.id ?? EMPTY_VALUE;
|
|
49
|
+
const historyName = tr.name?.trim() ? tr.name : historyValue;
|
|
50
|
+
grouping.push({
|
|
51
|
+
key: "historyId",
|
|
52
|
+
value: historyValue,
|
|
53
|
+
name: historyName,
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
if (category.groupEnvironments && !category.groupBy?.some((selector) => selector === "environment")) {
|
|
57
|
+
const environmentValue = tr.environment?.trim() ? tr.environment : EMPTY_VALUE;
|
|
58
|
+
grouping.push({
|
|
59
|
+
key: "environment",
|
|
60
|
+
value: environmentValue,
|
|
61
|
+
name: formatGroupName("environment", environmentValue),
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
return grouping.length > 0 ? grouping : undefined;
|
|
65
|
+
};
|
|
66
|
+
export const toUploadCategoryFromContext = (tr, categories) => {
|
|
67
|
+
if (!categories.length) {
|
|
68
|
+
return undefined;
|
|
69
|
+
}
|
|
70
|
+
const matched = matchCategory(categories, extractErrorMatchingData(tr));
|
|
71
|
+
if (!matched) {
|
|
72
|
+
return undefined;
|
|
73
|
+
}
|
|
74
|
+
return {
|
|
75
|
+
externalId: matched.id,
|
|
76
|
+
name: matched.name,
|
|
77
|
+
grouping: buildGrouping(tr, matched),
|
|
78
|
+
hide: matched.hide,
|
|
79
|
+
expand: matched.expand,
|
|
80
|
+
};
|
|
81
|
+
};
|
|
82
|
+
export const buildUploadCategoryGrouping = (tr, category) => buildGrouping(tr, category);
|
package/dist/utils.d.ts
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import type { TestStepResult } from "@allurereport/core-api";
|
|
2
|
-
import
|
|
2
|
+
import { AllureStore } from "@allurereport/plugin-api";
|
|
3
|
+
import type { AttachmentsResolver, FixtureResolver, TestOpsPluginOptions } from "./model.js";
|
|
3
4
|
export declare const unwrapStepsAttachments: (steps: TestStepResult[]) => TestStepResult[];
|
|
4
|
-
export declare const resolvePluginOptions: (options:
|
|
5
|
+
export declare const resolvePluginOptions: (options: TestOpsPluginOptions) => Omit<TestOpsPluginOptions, "filter">;
|
|
6
|
+
export declare function attachmentsResolverFactory(store: AllureStore): AttachmentsResolver;
|
|
7
|
+
export declare function fixturesResolverFactory(store: AllureStore): FixtureResolver;
|