@allurereport/plugin-testops 3.14.3 → 3.15.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/dist/client.js +18 -4
- package/dist/logger.d.ts +0 -10
- package/dist/logger.js +0 -28
- package/dist/model.d.ts +3 -3
- package/dist/plugin.js +159 -49
- package/dist/utils/attachments.d.ts +5 -0
- package/dist/utils/attachments.js +27 -0
- package/dist/utils/categories.d.ts +1 -0
- package/dist/utils/categories.js +18 -0
- package/dist/utils/fixtures.d.ts +4 -0
- package/dist/utils/fixtures.js +30 -0
- package/dist/utils/index.d.ts +7 -7
- package/dist/utils/index.js +7 -68
- package/dist/utils/options.d.ts +2 -0
- package/dist/utils/options.js +18 -0
- package/dist/utils/resolvers.d.ts +6 -0
- package/dist/utils/resolvers.js +58 -0
- package/dist/utils/testResults.d.ts +6 -0
- package/dist/utils/{uploaderDto.js → testResults.js} +31 -58
- package/dist/utils/validation.d.ts +1 -0
- package/dist/utils/validation.js +22 -0
- package/package.json +8 -9
- package/dist/utils/uploadCategory.d.ts +0 -3
- package/dist/utils/uploadCategory.js +0 -20
- package/dist/utils/uploaderDto.d.ts +0 -9
package/dist/client.js
CHANGED
|
@@ -17,8 +17,10 @@ import { chunk } from "lodash-es";
|
|
|
17
17
|
import pLimit from "p-limit";
|
|
18
18
|
import { bold } from "yoctocolors";
|
|
19
19
|
import { Logger } from "./logger.js";
|
|
20
|
+
import { toUploadFixturesResultsDto } from "./utils/fixtures.js";
|
|
20
21
|
import { testStatusToLaunchStatus } from "./utils/launches.js";
|
|
21
|
-
import {
|
|
22
|
+
import { normalizeTestStepsResults, toUploadResultsDto } from "./utils/testResults.js";
|
|
23
|
+
import { validateExecutableName } from "./utils/validation.js";
|
|
22
24
|
class TestOpsClientError extends AxiosError {
|
|
23
25
|
}
|
|
24
26
|
const CHUNK_SIZE = 100;
|
|
@@ -291,7 +293,11 @@ export class TestOpsClient {
|
|
|
291
293
|
throw new Error("Session isn't created! Call createSession first");
|
|
292
294
|
}
|
|
293
295
|
const { trs, environments, attachmentsResolver, fixturesResolver, onProgress } = params;
|
|
294
|
-
const
|
|
296
|
+
const projectedTrs = trs.map((tr) => ({
|
|
297
|
+
...tr,
|
|
298
|
+
...(tr.steps ? { steps: normalizeTestStepsResults(tr.steps) } : {}),
|
|
299
|
+
}));
|
|
300
|
+
const trsChunks = chunk(projectedTrs, CHUNK_SIZE);
|
|
295
301
|
const uploadLimitFn = pLimit(__classPrivateFieldGet(this, _TestOpsClient_uploadLimit, "f"));
|
|
296
302
|
const uploadedTrs = [];
|
|
297
303
|
const envNamesById = new Map(environments.map(({ id, name }) => [id, name]));
|
|
@@ -311,8 +317,8 @@ export class TestOpsClient {
|
|
|
311
317
|
await this.createNamedEnvs(Array.from(chunkEnvs.values()));
|
|
312
318
|
}
|
|
313
319
|
const reportIdsToTestOpsIds = await __classPrivateFieldGet(this, _TestOpsClient_instances, "m", _TestOpsClient_postTestResultsChunk).call(this, trsChunk);
|
|
320
|
+
uploadedTrs.push(...trsChunk.filter((tr) => typeof reportIdsToTestOpsIds[tr.id] === "number"));
|
|
314
321
|
await __classPrivateFieldGet(this, _TestOpsClient_instances, "m", _TestOpsClient_uploadChunkAttachmentsAndFixtures).call(this, trsChunk, reportIdsToTestOpsIds, attachmentsResolver, fixturesResolver, uploadLimitFn, onProgress);
|
|
315
|
-
uploadedTrs.push(...trsChunk);
|
|
316
322
|
}
|
|
317
323
|
__classPrivateFieldGet(this, _TestOpsClient_logger, "f").verbose("Test results upload completed");
|
|
318
324
|
}
|
|
@@ -395,8 +401,16 @@ _TestOpsClient_baseUrl = new WeakMap(), _TestOpsClient_logger = new WeakMap(), _
|
|
|
395
401
|
}, _TestOpsClient_uploadChunkAttachmentsAndFixtures = async function _TestOpsClient_uploadChunkAttachmentsAndFixtures(trsChunk, reportIdsToTestOpsIds, attachmentsResolver, fixturesResolver, uploadLimitFn, onProgress) {
|
|
396
402
|
await Promise.all(trsChunk.map((tr) => uploadLimitFn(async () => {
|
|
397
403
|
const testOpsId = reportIdsToTestOpsIds[tr.id];
|
|
404
|
+
if (typeof testOpsId !== "number") {
|
|
405
|
+
return;
|
|
406
|
+
}
|
|
398
407
|
const attachments = await attachmentsResolver(tr);
|
|
399
|
-
const fixtures = await fixturesResolver(tr)
|
|
408
|
+
const fixtures = (await fixturesResolver(tr))
|
|
409
|
+
.filter((fixture) => validateExecutableName(fixture.name))
|
|
410
|
+
.map((fixture) => ({
|
|
411
|
+
...fixture,
|
|
412
|
+
...(fixture.steps ? { steps: normalizeTestStepsResults(fixture.steps) } : {}),
|
|
413
|
+
}));
|
|
400
414
|
await __classPrivateFieldGet(this, _TestOpsClient_instances, "m", _TestOpsClient_uploadAttachmentsForResult).call(this, testOpsId, attachments);
|
|
401
415
|
await __classPrivateFieldGet(this, _TestOpsClient_instances, "m", _TestOpsClient_uploadFixturesForResult).call(this, testOpsId, fixtures);
|
|
402
416
|
onProgress?.();
|
package/dist/logger.d.ts
CHANGED
|
@@ -1,18 +1,8 @@
|
|
|
1
|
-
import ProgressBar from "progress";
|
|
2
1
|
type JSONMessage = Record<string, unknown> | Array<unknown>;
|
|
3
2
|
export declare class Logger {
|
|
4
3
|
#private;
|
|
5
4
|
private readonly loggerName;
|
|
6
5
|
constructor(loggerName: string);
|
|
7
|
-
progressBarCounter(message: string, total: number): ProgressBar | {
|
|
8
|
-
tick: () => void;
|
|
9
|
-
update: () => void;
|
|
10
|
-
terminate: () => void;
|
|
11
|
-
};
|
|
12
|
-
progressBar(message: string): ProgressBar | {
|
|
13
|
-
update: () => void;
|
|
14
|
-
terminate: () => void;
|
|
15
|
-
};
|
|
16
6
|
verbose(message: string | JSONMessage): void;
|
|
17
7
|
debug(message: string | JSONMessage): void;
|
|
18
8
|
newLine(): void;
|
package/dist/logger.js
CHANGED
|
@@ -13,7 +13,6 @@ var _Logger_instances, _Logger_level, _Logger_prefix, _Logger_isLogLevel, _Logge
|
|
|
13
13
|
import console from "node:console";
|
|
14
14
|
import { env } from "node:process";
|
|
15
15
|
import { inspect } from "node:util";
|
|
16
|
-
import ProgressBar from "progress";
|
|
17
16
|
import { blue, bold, cyan, dim, gray, red, yellow } from "yoctocolors";
|
|
18
17
|
const logLevelsPriority = {
|
|
19
18
|
silent: Number.MAX_SAFE_INTEGER,
|
|
@@ -43,33 +42,6 @@ export class Logger {
|
|
|
43
42
|
_Logger_prefix.set(this, void 0);
|
|
44
43
|
__classPrivateFieldSet(this, _Logger_prefix, cyan(bold(`[${this.loggerName}]:`)), "f");
|
|
45
44
|
}
|
|
46
|
-
progressBarCounter(message, total) {
|
|
47
|
-
if (__classPrivateFieldGet(this, _Logger_instances, "m", _Logger_isSilent).call(this)) {
|
|
48
|
-
return {
|
|
49
|
-
tick: () => { },
|
|
50
|
-
update: () => { },
|
|
51
|
-
terminate: () => { },
|
|
52
|
-
};
|
|
53
|
-
}
|
|
54
|
-
const progressBar = new ProgressBar(`${__classPrivateFieldGet(this, _Logger_prefix, "f")} ${blue(message)} [:bar] :current/:total`, {
|
|
55
|
-
total,
|
|
56
|
-
width: 20,
|
|
57
|
-
});
|
|
58
|
-
return progressBar;
|
|
59
|
-
}
|
|
60
|
-
progressBar(message) {
|
|
61
|
-
if (__classPrivateFieldGet(this, _Logger_instances, "m", _Logger_isSilent).call(this)) {
|
|
62
|
-
return {
|
|
63
|
-
update: () => { },
|
|
64
|
-
terminate: () => { },
|
|
65
|
-
};
|
|
66
|
-
}
|
|
67
|
-
const progressBar = new ProgressBar(`${__classPrivateFieldGet(this, _Logger_prefix, "f")} ${blue(message)} [:bar] :percent`, {
|
|
68
|
-
total: 100,
|
|
69
|
-
width: 20,
|
|
70
|
-
});
|
|
71
|
-
return progressBar;
|
|
72
|
-
}
|
|
73
45
|
verbose(message) {
|
|
74
46
|
if (!__classPrivateFieldGet(this, _Logger_instances, "m", _Logger_isLogLevel).call(this, "verbose")) {
|
|
75
47
|
return;
|
package/dist/model.d.ts
CHANGED
|
@@ -53,7 +53,7 @@ export type UploadLabelDto = {
|
|
|
53
53
|
};
|
|
54
54
|
export type UploadTestResultBodyStepDto = {
|
|
55
55
|
type: "body";
|
|
56
|
-
body
|
|
56
|
+
body: string;
|
|
57
57
|
bodyJson?: unknown;
|
|
58
58
|
status?: UploadTestStatus;
|
|
59
59
|
start?: number;
|
|
@@ -91,7 +91,7 @@ export type UploadTestResultDto = {
|
|
|
91
91
|
uuid?: string;
|
|
92
92
|
historyId?: string;
|
|
93
93
|
testCaseId?: string;
|
|
94
|
-
name
|
|
94
|
+
name: string;
|
|
95
95
|
fullName?: string;
|
|
96
96
|
description?: string;
|
|
97
97
|
descriptionHtml?: string;
|
|
@@ -131,7 +131,7 @@ export type UploadTestFixtureType = "BEFORE" | "AFTER";
|
|
|
131
131
|
export type UploadTestFixtureResultDto = {
|
|
132
132
|
type?: UploadTestFixtureType;
|
|
133
133
|
uuid?: string;
|
|
134
|
-
name
|
|
134
|
+
name: string;
|
|
135
135
|
start?: number;
|
|
136
136
|
stop?: number;
|
|
137
137
|
duration?: number;
|
package/dist/plugin.js
CHANGED
|
@@ -9,9 +9,10 @@ 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 _TestOpsPlugin_instances, _TestOpsPlugin_logger, _TestOpsPlugin_ci, _TestOpsPlugin_client, _TestOpsPlugin_launchName, _TestOpsPlugin_launchTags, _TestOpsPlugin_uploadedTestResultsIds, _TestOpsPlugin_autocloseLaunch, _TestOpsPlugin_gitFlow, _TestOpsPlugin_enabledByConfig, _TestOpsPlugin_uploadQualityGateResults, _TestOpsPlugin_uploadGlobalErrors, _TestOpsPlugin_uploadGlobalAttachments, _TestOpsPlugin_uploadTestResults, _TestOpsPlugin_upload, _TestOpsPlugin_trsToUpload, _TestOpsPlugin_enrichWithCategories, _TestOpsPlugin_syncLaunchCategories, _TestOpsPlugin_collectCategoryNamesByExternalId, _TestOpsPlugin_assignCreatedCategoryIds, _TestOpsPlugin_startUpload, _TestOpsPlugin_stopUpload;
|
|
12
|
+
var _TestOpsPlugin_instances, _TestOpsPlugin_logger, _TestOpsPlugin_ci, _TestOpsPlugin_client, _TestOpsPlugin_launchName, _TestOpsPlugin_launchTags, _TestOpsPlugin_uploadedTestResultsIds, _TestOpsPlugin_autocloseLaunch, _TestOpsPlugin_launchStarted, _TestOpsPlugin_gitFlow, _TestOpsPlugin_enabledByConfig, _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
13
|
import { env } from "node:process";
|
|
14
14
|
import { detect, isLocalCiDescriptor } from "@allurereport/ci";
|
|
15
|
+
import { createProgressLogger } from "@allurereport/cli-commons";
|
|
15
16
|
import { getWorstStatus } from "@allurereport/core-api";
|
|
16
17
|
import { createPluginSummary, } from "@allurereport/plugin-api";
|
|
17
18
|
import { uniqBy, stubTrue } from "lodash-es";
|
|
@@ -19,9 +20,11 @@ import { bold } from "yoctocolors";
|
|
|
19
20
|
import { TestOpsClient } from "./client.js";
|
|
20
21
|
import { LaunchGitFlow, resolveGitFlowOptions } from "./gitFlow/index.js";
|
|
21
22
|
import { Logger } from "./logger.js";
|
|
22
|
-
import {
|
|
23
|
-
import { toUploadCategory } from "./utils/
|
|
24
|
-
import {
|
|
23
|
+
import { uploadFilenameForLink } from "./utils/attachments.js";
|
|
24
|
+
import { toUploadCategory } from "./utils/categories.js";
|
|
25
|
+
import { resolvePluginOptions } from "./utils/options.js";
|
|
26
|
+
import { attachmentsResolverFactory, fixturesResolverFactory, unwrapStepsAttachments } from "./utils/resolvers.js";
|
|
27
|
+
import { validateExecutableName } from "./utils/validation.js";
|
|
25
28
|
const LAUNCH_PROGRESS_POLL_DELAY_MS = 500;
|
|
26
29
|
const LAUNCH_PROGRESS_ATTEMPTS_LIMIT = 10;
|
|
27
30
|
const categoryDisplayName = (cat) => cat.name ?? cat.grouping?.[0]?.name ?? cat.grouping?.[0]?.value ?? cat.grouping?.[0]?.key ?? cat.externalId;
|
|
@@ -36,6 +39,7 @@ export class TestOpsPlugin {
|
|
|
36
39
|
_TestOpsPlugin_launchTags.set(this, []);
|
|
37
40
|
_TestOpsPlugin_uploadedTestResultsIds.set(this, new Set());
|
|
38
41
|
_TestOpsPlugin_autocloseLaunch.set(this, false);
|
|
42
|
+
_TestOpsPlugin_launchStarted.set(this, false);
|
|
39
43
|
_TestOpsPlugin_gitFlow.set(this, void 0);
|
|
40
44
|
_TestOpsPlugin_enabledByConfig.set(this, false);
|
|
41
45
|
__classPrivateFieldSet(this, _TestOpsPlugin_enabledByConfig, context.enabled === true, "f");
|
|
@@ -103,7 +107,9 @@ export class TestOpsPlugin {
|
|
|
103
107
|
return;
|
|
104
108
|
}
|
|
105
109
|
__classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").verbose("Starting upload…");
|
|
106
|
-
await __classPrivateFieldGet(this, _TestOpsPlugin_instances, "m", _TestOpsPlugin_startUpload).call(this)
|
|
110
|
+
if (!(await __classPrivateFieldGet(this, _TestOpsPlugin_instances, "m", _TestOpsPlugin_startUpload).call(this))) {
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
107
113
|
await __classPrivateFieldGet(this, _TestOpsPlugin_instances, "m", _TestOpsPlugin_upload).call(this, store, { context, stage: "start" });
|
|
108
114
|
__classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").info(`Allure TestOps Launch: ${__classPrivateFieldGet(this, _TestOpsPlugin_client, "f").launchUrl}`);
|
|
109
115
|
}
|
|
@@ -111,6 +117,10 @@ export class TestOpsPlugin {
|
|
|
111
117
|
if (!this.enabled) {
|
|
112
118
|
return;
|
|
113
119
|
}
|
|
120
|
+
if (!__classPrivateFieldGet(this, _TestOpsPlugin_launchStarted, "f")) {
|
|
121
|
+
__classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").verbose("Skipping update: the TestOps launch was never started");
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
114
124
|
__classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").verbose("Updating (uploading new results)…");
|
|
115
125
|
await __classPrivateFieldGet(this, _TestOpsPlugin_instances, "m", _TestOpsPlugin_upload).call(this, store, { context, stage: "update" });
|
|
116
126
|
}
|
|
@@ -118,6 +128,10 @@ export class TestOpsPlugin {
|
|
|
118
128
|
if (!this.enabled) {
|
|
119
129
|
return;
|
|
120
130
|
}
|
|
131
|
+
if (!__classPrivateFieldGet(this, _TestOpsPlugin_launchStarted, "f")) {
|
|
132
|
+
__classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").verbose("Skipping finalization: the TestOps launch was never started");
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
121
135
|
const allTrs = await store.allTestResults({
|
|
122
136
|
filter: this.options.filter,
|
|
123
137
|
includeRetries: false,
|
|
@@ -125,7 +139,21 @@ export class TestOpsPlugin {
|
|
|
125
139
|
const worstStatus = getWorstStatus(allTrs.map(({ status }) => status));
|
|
126
140
|
__classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").verbose("Finalizing upload…");
|
|
127
141
|
await __classPrivateFieldGet(this, _TestOpsPlugin_instances, "m", _TestOpsPlugin_upload).call(this, store, { context, stage: "done" });
|
|
128
|
-
|
|
142
|
+
try {
|
|
143
|
+
await __classPrivateFieldGet(this, _TestOpsPlugin_instances, "m", _TestOpsPlugin_stopUpload).call(this, worstStatus || "unknown");
|
|
144
|
+
}
|
|
145
|
+
catch (error) {
|
|
146
|
+
if (__classPrivateFieldGet(this, _TestOpsPlugin_client, "f").isTestOpsClientError(error)) {
|
|
147
|
+
__classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").error(`Failed to stop TestOps upload: ${error.response.data.message}`);
|
|
148
|
+
__classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").debug(error.response?.data);
|
|
149
|
+
}
|
|
150
|
+
else if (error instanceof Error) {
|
|
151
|
+
__classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").error(`Failed to stop TestOps upload: ${error.message}`);
|
|
152
|
+
}
|
|
153
|
+
else {
|
|
154
|
+
__classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").error("Failed to stop TestOps upload");
|
|
155
|
+
}
|
|
156
|
+
}
|
|
129
157
|
const launchId = __classPrivateFieldGet(this, _TestOpsPlugin_client, "f").launchId;
|
|
130
158
|
if (typeof launchId !== "number") {
|
|
131
159
|
return;
|
|
@@ -181,24 +209,34 @@ export class TestOpsPlugin {
|
|
|
181
209
|
return summary;
|
|
182
210
|
}
|
|
183
211
|
}
|
|
184
|
-
_TestOpsPlugin_logger = new WeakMap(), _TestOpsPlugin_ci = new WeakMap(), _TestOpsPlugin_client = new WeakMap(), _TestOpsPlugin_launchName = new WeakMap(), _TestOpsPlugin_launchTags = new WeakMap(), _TestOpsPlugin_uploadedTestResultsIds = new WeakMap(), _TestOpsPlugin_autocloseLaunch = new WeakMap(), _TestOpsPlugin_gitFlow = new WeakMap(), _TestOpsPlugin_enabledByConfig = new WeakMap(), _TestOpsPlugin_instances = new WeakSet(), _TestOpsPlugin_uploadQualityGateResults = async function _TestOpsPlugin_uploadQualityGateResults(store) {
|
|
212
|
+
_TestOpsPlugin_logger = new WeakMap(), _TestOpsPlugin_ci = new WeakMap(), _TestOpsPlugin_client = new WeakMap(), _TestOpsPlugin_launchName = new WeakMap(), _TestOpsPlugin_launchTags = new WeakMap(), _TestOpsPlugin_uploadedTestResultsIds = new WeakMap(), _TestOpsPlugin_autocloseLaunch = new WeakMap(), _TestOpsPlugin_launchStarted = new WeakMap(), _TestOpsPlugin_gitFlow = new WeakMap(), _TestOpsPlugin_enabledByConfig = new WeakMap(), _TestOpsPlugin_instances = new WeakSet(), _TestOpsPlugin_uploadQualityGateResults = async function _TestOpsPlugin_uploadQualityGateResults(store) {
|
|
185
213
|
const results = await store.qualityGateResults();
|
|
186
214
|
const uniqueResults = uniqBy(results.filter(({ success }) => !success), ({ rule, environment }) => `${rule}-${environment}`);
|
|
187
215
|
if (uniqueResults.length === 0) {
|
|
188
216
|
__classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").verbose("No quality gate results to upload");
|
|
189
217
|
return;
|
|
190
218
|
}
|
|
191
|
-
const
|
|
219
|
+
const progressLogger = createProgressLogger({
|
|
220
|
+
total: 1,
|
|
221
|
+
message: "Uploading quality gate results",
|
|
222
|
+
unitLabel: "request uploaded",
|
|
223
|
+
prefix: "[TestOpsPlugin]",
|
|
224
|
+
});
|
|
225
|
+
let completed = false;
|
|
192
226
|
try {
|
|
193
|
-
|
|
194
|
-
await __classPrivateFieldGet(this, _TestOpsPlugin_client, "f").uploadQualityGateResults(uniqueResults, (percent
|
|
195
|
-
|
|
227
|
+
progressLogger.log(true);
|
|
228
|
+
await __classPrivateFieldGet(this, _TestOpsPlugin_client, "f").uploadQualityGateResults(uniqueResults, (percent) => {
|
|
229
|
+
if (!completed && percent >= 100) {
|
|
230
|
+
completed = true;
|
|
231
|
+
progressLogger.increment();
|
|
232
|
+
}
|
|
196
233
|
});
|
|
197
|
-
|
|
198
|
-
|
|
234
|
+
if (!completed) {
|
|
235
|
+
progressLogger.increment();
|
|
236
|
+
}
|
|
237
|
+
progressLogger.log(true);
|
|
199
238
|
}
|
|
200
239
|
catch (error) {
|
|
201
|
-
progressBar.terminate();
|
|
202
240
|
if (__classPrivateFieldGet(this, _TestOpsPlugin_client, "f").isTestOpsClientError(error)) {
|
|
203
241
|
__classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").error(`Failed to upload quality gate results: ${error.response.data.message}`);
|
|
204
242
|
__classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").debug(error.response?.data);
|
|
@@ -210,23 +248,36 @@ _TestOpsPlugin_logger = new WeakMap(), _TestOpsPlugin_ci = new WeakMap(), _TestO
|
|
|
210
248
|
__classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").error("Failed to upload quality gate results");
|
|
211
249
|
}
|
|
212
250
|
}
|
|
251
|
+
finally {
|
|
252
|
+
progressLogger.cancel?.();
|
|
253
|
+
}
|
|
213
254
|
}, _TestOpsPlugin_uploadGlobalErrors = async function _TestOpsPlugin_uploadGlobalErrors(store) {
|
|
214
255
|
const results = await store.allGlobalErrors();
|
|
215
256
|
if (results.length === 0) {
|
|
216
257
|
__classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").verbose("No global errors to upload");
|
|
217
258
|
return;
|
|
218
259
|
}
|
|
219
|
-
const
|
|
260
|
+
const progressLogger = createProgressLogger({
|
|
261
|
+
total: 1,
|
|
262
|
+
message: "Uploading global errors",
|
|
263
|
+
unitLabel: "request uploaded",
|
|
264
|
+
prefix: "[TestOpsPlugin]",
|
|
265
|
+
});
|
|
266
|
+
let completed = false;
|
|
220
267
|
try {
|
|
221
|
-
|
|
222
|
-
await __classPrivateFieldGet(this, _TestOpsPlugin_client, "f").uploadGlobalErrors(results, (percent
|
|
223
|
-
|
|
268
|
+
progressLogger.log(true);
|
|
269
|
+
await __classPrivateFieldGet(this, _TestOpsPlugin_client, "f").uploadGlobalErrors(results, (percent) => {
|
|
270
|
+
if (!completed && percent >= 100) {
|
|
271
|
+
completed = true;
|
|
272
|
+
progressLogger.increment();
|
|
273
|
+
}
|
|
224
274
|
});
|
|
225
|
-
|
|
226
|
-
|
|
275
|
+
if (!completed) {
|
|
276
|
+
progressLogger.increment();
|
|
277
|
+
}
|
|
278
|
+
progressLogger.log(true);
|
|
227
279
|
}
|
|
228
280
|
catch (error) {
|
|
229
|
-
progressBar.terminate();
|
|
230
281
|
if (__classPrivateFieldGet(this, _TestOpsPlugin_client, "f").isTestOpsClientError(error)) {
|
|
231
282
|
__classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").error(`Failed to upload global errors: ${error.response.data.message}`);
|
|
232
283
|
__classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").debug(error.response?.data);
|
|
@@ -238,15 +289,24 @@ _TestOpsPlugin_logger = new WeakMap(), _TestOpsPlugin_ci = new WeakMap(), _TestO
|
|
|
238
289
|
__classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").error("Failed to upload global errors");
|
|
239
290
|
}
|
|
240
291
|
}
|
|
292
|
+
finally {
|
|
293
|
+
progressLogger.cancel?.();
|
|
294
|
+
}
|
|
241
295
|
}, _TestOpsPlugin_uploadGlobalAttachments = async function _TestOpsPlugin_uploadGlobalAttachments(store) {
|
|
242
296
|
const attachments = await store.allGlobalAttachments();
|
|
243
297
|
if (attachments.length === 0) {
|
|
244
298
|
__classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").debug("No global attachments to upload");
|
|
245
299
|
return;
|
|
246
300
|
}
|
|
247
|
-
const
|
|
301
|
+
const progressLogger = createProgressLogger({
|
|
302
|
+
total: 1,
|
|
303
|
+
message: "Uploading global attachments",
|
|
304
|
+
unitLabel: "request uploaded",
|
|
305
|
+
prefix: "[TestOpsPlugin]",
|
|
306
|
+
});
|
|
307
|
+
let completed = false;
|
|
248
308
|
try {
|
|
249
|
-
|
|
309
|
+
progressLogger.log(true);
|
|
250
310
|
await __classPrivateFieldGet(this, _TestOpsPlugin_client, "f").uploadGlobalAttachments({
|
|
251
311
|
attachments,
|
|
252
312
|
attachmentsResolver: async (attachmentLink) => {
|
|
@@ -262,15 +322,19 @@ _TestOpsPlugin_logger = new WeakMap(), _TestOpsPlugin_ci = new WeakMap(), _TestO
|
|
|
262
322
|
content: body,
|
|
263
323
|
};
|
|
264
324
|
},
|
|
265
|
-
onProgress: (percent
|
|
266
|
-
|
|
325
|
+
onProgress: (percent) => {
|
|
326
|
+
if (!completed && percent >= 100) {
|
|
327
|
+
completed = true;
|
|
328
|
+
progressLogger.increment();
|
|
329
|
+
}
|
|
267
330
|
},
|
|
268
331
|
});
|
|
269
|
-
|
|
270
|
-
|
|
332
|
+
if (!completed) {
|
|
333
|
+
progressLogger.increment();
|
|
334
|
+
}
|
|
335
|
+
progressLogger.log(true);
|
|
271
336
|
}
|
|
272
337
|
catch (error) {
|
|
273
|
-
progressBar.terminate();
|
|
274
338
|
if (__classPrivateFieldGet(this, _TestOpsPlugin_client, "f").isTestOpsClientError(error)) {
|
|
275
339
|
__classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").error(`Failed to upload global attachments: ${error.response.data.message}`);
|
|
276
340
|
__classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").debug(error.response?.data);
|
|
@@ -282,28 +346,43 @@ _TestOpsPlugin_logger = new WeakMap(), _TestOpsPlugin_ci = new WeakMap(), _TestO
|
|
|
282
346
|
__classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").error("Failed to upload global attachments");
|
|
283
347
|
}
|
|
284
348
|
}
|
|
349
|
+
finally {
|
|
350
|
+
progressLogger.cancel?.();
|
|
351
|
+
}
|
|
285
352
|
}, _TestOpsPlugin_uploadTestResults = async function _TestOpsPlugin_uploadTestResults(store, trsToUpload, environments) {
|
|
286
353
|
const totalCount = trsToUpload.length;
|
|
287
354
|
__classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").info(`Preparing to upload ${bold(totalCount.toString())} ${totalCount > 1 ? "test results" : "test result"}`);
|
|
288
|
-
const
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
trs: trsToUpload,
|
|
294
|
-
onProgress: () => trsProgressBar.tick(),
|
|
295
|
-
});
|
|
296
|
-
uploadedTrs.forEach((tr) => {
|
|
297
|
-
__classPrivateFieldGet(this, _TestOpsPlugin_uploadedTestResultsIds, "f").add(tr.id);
|
|
355
|
+
const progressLogger = createProgressLogger({
|
|
356
|
+
total: totalCount,
|
|
357
|
+
message: "Uploading test results",
|
|
358
|
+
unitLabel: totalCount === 1 ? "test result uploaded" : "test results uploaded",
|
|
359
|
+
prefix: "[TestOpsPlugin]",
|
|
298
360
|
});
|
|
299
|
-
const
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
__classPrivateFieldGet(this,
|
|
304
|
-
|
|
361
|
+
const logProgress = progressLogger.log;
|
|
362
|
+
const incrementProgress = progressLogger.increment;
|
|
363
|
+
try {
|
|
364
|
+
logProgress(true);
|
|
365
|
+
const uploadedTrs = await __classPrivateFieldGet(this, _TestOpsPlugin_client, "f").uploadTestResults({
|
|
366
|
+
attachmentsResolver: attachmentsResolverFactory(store),
|
|
367
|
+
fixturesResolver: fixturesResolverFactory(store),
|
|
368
|
+
environments,
|
|
369
|
+
trs: trsToUpload,
|
|
370
|
+
onProgress: () => incrementProgress(),
|
|
371
|
+
});
|
|
372
|
+
logProgress(true);
|
|
373
|
+
uploadedTrs.forEach((tr) => {
|
|
374
|
+
__classPrivateFieldGet(this, _TestOpsPlugin_uploadedTestResultsIds, "f").add(tr.id);
|
|
375
|
+
});
|
|
376
|
+
const uploadedCount = uploadedTrs.length;
|
|
377
|
+
if (uploadedCount === 0) {
|
|
378
|
+
__classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").warn("No test results were uploaded");
|
|
379
|
+
return;
|
|
380
|
+
}
|
|
381
|
+
__classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").info(`Uploaded ${uploadedCount} ${uploadedCount > 1 ? "test results" : "test result"}`);
|
|
382
|
+
}
|
|
383
|
+
finally {
|
|
384
|
+
progressLogger.cancel?.();
|
|
305
385
|
}
|
|
306
|
-
__classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").info(`Uploaded ${uploadedCount} ${uploadedCount > 1 ? "test results" : "test result"}`);
|
|
307
386
|
}, _TestOpsPlugin_upload = async function _TestOpsPlugin_upload(store, options = {}) {
|
|
308
387
|
const { context, stage } = options;
|
|
309
388
|
const trsToUpload = await __classPrivateFieldGet(this, _TestOpsPlugin_instances, "m", _TestOpsPlugin_trsToUpload).call(this, store);
|
|
@@ -316,7 +395,22 @@ _TestOpsPlugin_logger = new WeakMap(), _TestOpsPlugin_ci = new WeakMap(), _TestO
|
|
|
316
395
|
}
|
|
317
396
|
return;
|
|
318
397
|
}
|
|
319
|
-
|
|
398
|
+
try {
|
|
399
|
+
await __classPrivateFieldGet(this, _TestOpsPlugin_client, "f").createSession(env);
|
|
400
|
+
}
|
|
401
|
+
catch (error) {
|
|
402
|
+
if (__classPrivateFieldGet(this, _TestOpsPlugin_client, "f").isTestOpsClientError(error)) {
|
|
403
|
+
__classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").error(`Failed to create TestOps session: ${error.response.data.message}`);
|
|
404
|
+
__classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").debug(error.response?.data);
|
|
405
|
+
}
|
|
406
|
+
else if (error instanceof Error) {
|
|
407
|
+
__classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").error(`Failed to create TestOps session: ${error.message}`);
|
|
408
|
+
}
|
|
409
|
+
else {
|
|
410
|
+
__classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").error("Failed to create TestOps session");
|
|
411
|
+
}
|
|
412
|
+
return;
|
|
413
|
+
}
|
|
320
414
|
await __classPrivateFieldGet(this, _TestOpsPlugin_instances, "m", _TestOpsPlugin_uploadGlobalAttachments).call(this, store);
|
|
321
415
|
await __classPrivateFieldGet(this, _TestOpsPlugin_instances, "m", _TestOpsPlugin_uploadGlobalErrors).call(this, store);
|
|
322
416
|
await __classPrivateFieldGet(this, _TestOpsPlugin_instances, "m", _TestOpsPlugin_uploadQualityGateResults).call(this, store);
|
|
@@ -333,7 +427,7 @@ _TestOpsPlugin_logger = new WeakMap(), _TestOpsPlugin_ci = new WeakMap(), _TestO
|
|
|
333
427
|
if (uploaded) {
|
|
334
428
|
return false;
|
|
335
429
|
}
|
|
336
|
-
return filter(tr);
|
|
430
|
+
return validateExecutableName(tr.name) && filter(tr);
|
|
337
431
|
},
|
|
338
432
|
includeRetries: false,
|
|
339
433
|
});
|
|
@@ -425,8 +519,24 @@ _TestOpsPlugin_logger = new WeakMap(), _TestOpsPlugin_ci = new WeakMap(), _TestO
|
|
|
425
519
|
}
|
|
426
520
|
}, _TestOpsPlugin_startUpload = async function _TestOpsPlugin_startUpload() {
|
|
427
521
|
const launchGitContext = __classPrivateFieldGet(this, _TestOpsPlugin_gitFlow, "f").resolve();
|
|
428
|
-
|
|
429
|
-
|
|
522
|
+
try {
|
|
523
|
+
await __classPrivateFieldGet(this, _TestOpsPlugin_client, "f").createLaunch(__classPrivateFieldGet(this, _TestOpsPlugin_launchName, "f"), __classPrivateFieldGet(this, _TestOpsPlugin_launchTags, "f"), launchGitContext);
|
|
524
|
+
await __classPrivateFieldGet(this, _TestOpsPlugin_client, "f").startUpload(__classPrivateFieldGet(this, _TestOpsPlugin_ci, "f"));
|
|
525
|
+
__classPrivateFieldSet(this, _TestOpsPlugin_launchStarted, true, "f");
|
|
526
|
+
}
|
|
527
|
+
catch (error) {
|
|
528
|
+
if (__classPrivateFieldGet(this, _TestOpsPlugin_client, "f").isTestOpsClientError(error)) {
|
|
529
|
+
__classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").error(`Failed to create TestOps launch: ${error.response.data.message}`);
|
|
530
|
+
__classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").debug(error.response?.data);
|
|
531
|
+
}
|
|
532
|
+
else if (error instanceof Error) {
|
|
533
|
+
__classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").error(`Failed to create TestOps launch: ${error.message}`);
|
|
534
|
+
}
|
|
535
|
+
else {
|
|
536
|
+
__classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").error("Failed to create TestOps launch");
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
return __classPrivateFieldGet(this, _TestOpsPlugin_launchStarted, "f");
|
|
430
540
|
}, _TestOpsPlugin_stopUpload = async function _TestOpsPlugin_stopUpload(status) {
|
|
431
541
|
await __classPrivateFieldGet(this, _TestOpsPlugin_client, "f").stopUpload(__classPrivateFieldGet(this, _TestOpsPlugin_ci, "f"), status);
|
|
432
542
|
};
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { AttachmentLink, TestStepResult } from "@allurereport/core-api";
|
|
2
|
+
import type { UploadAttachmentDto } from "../model.js";
|
|
3
|
+
export declare const uploadFilenameForLink: (link: AttachmentLink) => string | undefined;
|
|
4
|
+
export declare const toUploadAttachmentDto: (link: AttachmentLink) => UploadAttachmentDto;
|
|
5
|
+
export declare const getAttachmentIdsFromTestStepsResults: (steps: TestStepResult[] | undefined) => Set<string>;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export const uploadFilenameForLink = (link) => {
|
|
2
|
+
if ("originalFileName" in link && typeof link.originalFileName === "string")
|
|
3
|
+
return link.originalFileName;
|
|
4
|
+
return undefined;
|
|
5
|
+
};
|
|
6
|
+
export const toUploadAttachmentDto = (link) => ({
|
|
7
|
+
name: "name" in link && typeof link.name === "string" ? link.name : undefined,
|
|
8
|
+
originalFileName: uploadFilenameForLink(link),
|
|
9
|
+
contentType: "contentType" in link && typeof link.contentType === "string" ? link.contentType : undefined,
|
|
10
|
+
contentLength: "contentLength" in link && typeof link.contentLength === "number" ? link.contentLength : undefined,
|
|
11
|
+
optional: "optional" in link && typeof link.optional === "boolean"
|
|
12
|
+
? link.optional
|
|
13
|
+
: undefined,
|
|
14
|
+
});
|
|
15
|
+
export const getAttachmentIdsFromTestStepsResults = (steps) => {
|
|
16
|
+
const ids = new Set();
|
|
17
|
+
for (const step of steps ?? []) {
|
|
18
|
+
if (step.type === "attachment") {
|
|
19
|
+
ids.add(step.link.id);
|
|
20
|
+
}
|
|
21
|
+
else {
|
|
22
|
+
for (const id of getAttachmentIdsFromTestStepsResults(step.steps))
|
|
23
|
+
ids.add(id);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return ids;
|
|
27
|
+
};
|
|
@@ -2,3 +2,4 @@ import type { CategoryDefinition } from "@allurereport/core-api";
|
|
|
2
2
|
import type { TestResultWithCategories, UploadCategory } from "../model.js";
|
|
3
3
|
export declare const toUploadCategoryFromContext: (tr: TestResultWithCategories, categories: CategoryDefinition[]) => UploadCategory | undefined;
|
|
4
4
|
export declare const buildUploadCategoryGrouping: (tr: TestResultWithCategories, category: CategoryDefinition) => UploadCategory["grouping"];
|
|
5
|
+
export declare const toUploadCategory: (tr: TestResultWithCategories, contextCategories: CategoryDefinition[]) => UploadCategory | undefined;
|
package/dist/utils/categories.js
CHANGED
|
@@ -80,3 +80,21 @@ export const toUploadCategoryFromContext = (tr, categories) => {
|
|
|
80
80
|
};
|
|
81
81
|
};
|
|
82
82
|
export const buildUploadCategoryGrouping = (tr, category) => buildGrouping(tr, category);
|
|
83
|
+
export const toUploadCategory = (tr, contextCategories) => {
|
|
84
|
+
const c = tr.categories?.[0];
|
|
85
|
+
if (c?.name) {
|
|
86
|
+
const externalId = c.id ?? c.name;
|
|
87
|
+
const groupingFromTestResult = c.grouping?.length ? c.grouping : undefined;
|
|
88
|
+
const contextCategory = groupingFromTestResult === undefined
|
|
89
|
+
? contextCategories.find((category) => category.id === externalId || category.name === c.name)
|
|
90
|
+
: undefined;
|
|
91
|
+
return {
|
|
92
|
+
externalId,
|
|
93
|
+
name: c.name,
|
|
94
|
+
grouping: groupingFromTestResult ?? (contextCategory && buildUploadCategoryGrouping(tr, contextCategory)),
|
|
95
|
+
hide: c.hide,
|
|
96
|
+
expand: c.expand,
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
return toUploadCategoryFromContext(tr, contextCategories);
|
|
100
|
+
};
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { UploadFixturesResultsDto, UploadTestFixtureResultDto } from "../model.js";
|
|
2
|
+
import type { TestOpsFixtureResult } from "../model.js";
|
|
3
|
+
export declare const toUploadFixtureResultDto: (fxt: TestOpsFixtureResult) => UploadTestFixtureResultDto;
|
|
4
|
+
export declare const toUploadFixturesResultsDto: (fixtures: TestOpsFixtureResult[]) => UploadFixturesResultsDto;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { toUploadAttachmentDto } from "./attachments.js";
|
|
2
|
+
const toUploadStepDto = (step) => step.type === "attachment"
|
|
3
|
+
? { type: "attachment", attachment: toUploadAttachmentDto(step.link) }
|
|
4
|
+
: {
|
|
5
|
+
type: "body",
|
|
6
|
+
body: step.name,
|
|
7
|
+
status: step.status,
|
|
8
|
+
start: step.start,
|
|
9
|
+
stop: step.stop,
|
|
10
|
+
duration: step.duration,
|
|
11
|
+
message: typeof step.message === "string" ? step.message : undefined,
|
|
12
|
+
trace: typeof step.trace === "string" ? step.trace : undefined,
|
|
13
|
+
parameters: step.parameters,
|
|
14
|
+
steps: step.steps?.map(toUploadStepDto),
|
|
15
|
+
};
|
|
16
|
+
export const toUploadFixtureResultDto = (fxt) => ({
|
|
17
|
+
type: fxt.type,
|
|
18
|
+
uuid: fxt.id,
|
|
19
|
+
name: fxt.name,
|
|
20
|
+
start: fxt.start,
|
|
21
|
+
stop: fxt.stop,
|
|
22
|
+
duration: fxt.duration,
|
|
23
|
+
status: fxt.status,
|
|
24
|
+
message: fxt.error?.message,
|
|
25
|
+
trace: fxt.error?.trace,
|
|
26
|
+
steps: fxt.steps?.map(toUploadStepDto),
|
|
27
|
+
});
|
|
28
|
+
export const toUploadFixturesResultsDto = (fixtures) => ({
|
|
29
|
+
fixtures: fixtures.map(toUploadFixtureResultDto),
|
|
30
|
+
});
|
package/dist/utils/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
export
|
|
5
|
-
export
|
|
6
|
-
export
|
|
7
|
-
export
|
|
1
|
+
export { toUploadAttachmentDto, uploadFilenameForLink } from "./attachments.js";
|
|
2
|
+
export { toUploadCategory, buildUploadCategoryGrouping, toUploadCategoryFromContext } from "./categories.js";
|
|
3
|
+
export { toUploadFixtureResultDto, toUploadFixturesResultsDto } from "./fixtures.js";
|
|
4
|
+
export { resolvePluginOptions } from "./options.js";
|
|
5
|
+
export { attachmentsResolverFactory, fixturesResolverFactory, unwrapStepsAttachments } from "./resolvers.js";
|
|
6
|
+
export { normalizeTestStepsResults, toUploadResultsDto, toUploadTestResultDto } from "./testResults.js";
|
|
7
|
+
export { validateExecutableName } from "./validation.js";
|
package/dist/utils/index.js
CHANGED
|
@@ -1,68 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
export
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
attachment: step.link,
|
|
9
|
-
};
|
|
10
|
-
}
|
|
11
|
-
if (step.steps) {
|
|
12
|
-
return {
|
|
13
|
-
...step,
|
|
14
|
-
steps: unwrapStepsAttachments(step.steps),
|
|
15
|
-
};
|
|
16
|
-
}
|
|
17
|
-
return step;
|
|
18
|
-
});
|
|
19
|
-
};
|
|
20
|
-
export const resolvePluginOptions = (options) => {
|
|
21
|
-
const { ALLURE_TOKEN, ALLURE_ENDPOINT, ALLURE_PROJECT_ID, ALLURE_LAUNCH_TAGS, ALLURE_LAUNCH_NAME } = env;
|
|
22
|
-
const { accessToken = ALLURE_TOKEN, endpoint = ALLURE_ENDPOINT, projectId = ALLURE_PROJECT_ID, launchTags = ALLURE_LAUNCH_TAGS, launchName = ALLURE_LAUNCH_NAME, autocloseLaunch, } = options;
|
|
23
|
-
const tags = !launchTags
|
|
24
|
-
? []
|
|
25
|
-
: Array.isArray(launchTags)
|
|
26
|
-
? launchTags
|
|
27
|
-
: launchTags.split(",").map((tag) => tag.trim());
|
|
28
|
-
return {
|
|
29
|
-
launchName: launchName || "Allure Report",
|
|
30
|
-
launchTags: tags,
|
|
31
|
-
accessToken: accessToken || "",
|
|
32
|
-
endpoint: endpoint || "",
|
|
33
|
-
projectId: projectId || "",
|
|
34
|
-
...(autocloseLaunch !== undefined ? { autocloseLaunch } : {}),
|
|
35
|
-
};
|
|
36
|
-
};
|
|
37
|
-
export function attachmentsResolverFactory(store) {
|
|
38
|
-
const attachmentsResolver = async (tr) => {
|
|
39
|
-
const attachments = await store.attachmentsByTrId(tr.id);
|
|
40
|
-
const result = [];
|
|
41
|
-
await Promise.all(attachments.map(async (attachment) => {
|
|
42
|
-
const content = await store.attachmentContentById(attachment.id);
|
|
43
|
-
const body = await content?.readContent(async (s) => s);
|
|
44
|
-
const filename = uploadFilenameForLink(attachment);
|
|
45
|
-
if (filename === undefined || body === undefined) {
|
|
46
|
-
return undefined;
|
|
47
|
-
}
|
|
48
|
-
result.push({
|
|
49
|
-
originalFileName: filename,
|
|
50
|
-
contentType: attachment.contentType ?? "application/octet-stream",
|
|
51
|
-
content: body,
|
|
52
|
-
});
|
|
53
|
-
}));
|
|
54
|
-
return result;
|
|
55
|
-
};
|
|
56
|
-
return attachmentsResolver;
|
|
57
|
-
}
|
|
58
|
-
export function fixturesResolverFactory(store) {
|
|
59
|
-
const fixturesResolver = async (tr) => {
|
|
60
|
-
const fixtures = await store.fixturesByTrId(tr.id);
|
|
61
|
-
return fixtures.map((fxt) => ({
|
|
62
|
-
...fxt,
|
|
63
|
-
type: fxt.type.toUpperCase(),
|
|
64
|
-
steps: unwrapStepsAttachments(fxt.steps),
|
|
65
|
-
}));
|
|
66
|
-
};
|
|
67
|
-
return fixturesResolver;
|
|
68
|
-
}
|
|
1
|
+
export { toUploadAttachmentDto, uploadFilenameForLink } from "./attachments.js";
|
|
2
|
+
export { toUploadCategory, buildUploadCategoryGrouping, toUploadCategoryFromContext } from "./categories.js";
|
|
3
|
+
export { toUploadFixtureResultDto, toUploadFixturesResultsDto } from "./fixtures.js";
|
|
4
|
+
export { resolvePluginOptions } from "./options.js";
|
|
5
|
+
export { attachmentsResolverFactory, fixturesResolverFactory, unwrapStepsAttachments } from "./resolvers.js";
|
|
6
|
+
export { normalizeTestStepsResults, toUploadResultsDto, toUploadTestResultDto } from "./testResults.js";
|
|
7
|
+
export { validateExecutableName } from "./validation.js";
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { env } from "node:process";
|
|
2
|
+
export const resolvePluginOptions = (options) => {
|
|
3
|
+
const { ALLURE_TOKEN, ALLURE_ENDPOINT, ALLURE_PROJECT_ID, ALLURE_LAUNCH_TAGS, ALLURE_LAUNCH_NAME } = env;
|
|
4
|
+
const { accessToken = ALLURE_TOKEN, endpoint = ALLURE_ENDPOINT, projectId = ALLURE_PROJECT_ID, launchTags = ALLURE_LAUNCH_TAGS, launchName = ALLURE_LAUNCH_NAME, autocloseLaunch, } = options;
|
|
5
|
+
const tags = !launchTags
|
|
6
|
+
? []
|
|
7
|
+
: Array.isArray(launchTags)
|
|
8
|
+
? launchTags
|
|
9
|
+
: launchTags.split(",").map((tag) => tag.trim());
|
|
10
|
+
return {
|
|
11
|
+
launchName: launchName || "Allure Report",
|
|
12
|
+
launchTags: tags,
|
|
13
|
+
accessToken: accessToken || "",
|
|
14
|
+
endpoint: endpoint || "",
|
|
15
|
+
projectId: projectId || "",
|
|
16
|
+
...(autocloseLaunch !== undefined ? { autocloseLaunch } : {}),
|
|
17
|
+
};
|
|
18
|
+
};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { TestStepResult } from "@allurereport/core-api";
|
|
2
|
+
import { AllureStore } from "@allurereport/plugin-api";
|
|
3
|
+
import type { AttachmentsResolver, FixtureResolver } from "../model.js";
|
|
4
|
+
export declare const unwrapStepsAttachments: (steps: TestStepResult[]) => TestStepResult[];
|
|
5
|
+
export declare function attachmentsResolverFactory(store: AllureStore): AttachmentsResolver;
|
|
6
|
+
export declare function fixturesResolverFactory(store: AllureStore): FixtureResolver;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { getAttachmentIdsFromTestStepsResults, uploadFilenameForLink } from "./attachments.js";
|
|
2
|
+
import { normalizeTestStepsResults } from "./testResults.js";
|
|
3
|
+
import { validateExecutableName } from "./validation.js";
|
|
4
|
+
export const unwrapStepsAttachments = (steps) => steps.map((step) => step.type === "attachment"
|
|
5
|
+
? { ...step, attachment: step.link }
|
|
6
|
+
: step.steps
|
|
7
|
+
? { ...step, steps: unwrapStepsAttachments(step.steps) }
|
|
8
|
+
: step);
|
|
9
|
+
export function attachmentsResolverFactory(store) {
|
|
10
|
+
const attachmentsResolver = async (tr) => {
|
|
11
|
+
const attachments = await store.attachmentsByTrId(tr.id);
|
|
12
|
+
const fixtures = await store.fixturesByTrId(tr.id);
|
|
13
|
+
const allowedAttachmentIds = getAttachmentIdsFromTestStepsResults(normalizeTestStepsResults(tr.steps));
|
|
14
|
+
for (const attachment of tr.attachments ?? []) {
|
|
15
|
+
allowedAttachmentIds.add(attachment.id);
|
|
16
|
+
}
|
|
17
|
+
for (const fixture of fixtures) {
|
|
18
|
+
if (!validateExecutableName(fixture.name)) {
|
|
19
|
+
continue;
|
|
20
|
+
}
|
|
21
|
+
for (const id of getAttachmentIdsFromTestStepsResults(normalizeTestStepsResults(fixture.steps))) {
|
|
22
|
+
allowedAttachmentIds.add(id);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
const result = [];
|
|
26
|
+
const attachmentsById = new Map(attachments.map((attachment) => [attachment.id, attachment]));
|
|
27
|
+
await Promise.all([...allowedAttachmentIds].map(async (id) => {
|
|
28
|
+
const attachment = (await store.attachmentById(id)) ?? attachmentsById.get(id);
|
|
29
|
+
if (!attachment) {
|
|
30
|
+
return undefined;
|
|
31
|
+
}
|
|
32
|
+
const content = await store.attachmentContentById(attachment.id);
|
|
33
|
+
const body = await content?.readContent(async (s) => s);
|
|
34
|
+
const filename = uploadFilenameForLink(attachment);
|
|
35
|
+
if (filename === undefined || body === undefined) {
|
|
36
|
+
return undefined;
|
|
37
|
+
}
|
|
38
|
+
result.push({
|
|
39
|
+
originalFileName: filename,
|
|
40
|
+
contentType: attachment.contentType ?? "application/octet-stream",
|
|
41
|
+
content: body,
|
|
42
|
+
});
|
|
43
|
+
}));
|
|
44
|
+
return result;
|
|
45
|
+
};
|
|
46
|
+
return attachmentsResolver;
|
|
47
|
+
}
|
|
48
|
+
export function fixturesResolverFactory(store) {
|
|
49
|
+
const fixturesResolver = async (tr) => {
|
|
50
|
+
const fixtures = await store.fixturesByTrId(tr.id);
|
|
51
|
+
return fixtures.map((fxt) => ({
|
|
52
|
+
...fxt,
|
|
53
|
+
type: fxt.type.toUpperCase(),
|
|
54
|
+
steps: unwrapStepsAttachments(normalizeTestStepsResults(fxt.steps) ?? []),
|
|
55
|
+
}));
|
|
56
|
+
};
|
|
57
|
+
return fixturesResolver;
|
|
58
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { TestStepResult } from "@allurereport/core-api";
|
|
2
|
+
import type { UploadResultsDto, UploadTestResultDto } from "../model.js";
|
|
3
|
+
import type { TestOpsPluginTestResult } from "../model.js";
|
|
4
|
+
export declare const normalizeTestStepsResults: (steps: TestStepResult[] | undefined) => TestStepResult[] | undefined;
|
|
5
|
+
export declare const toUploadTestResultDto: (tr: TestOpsPluginTestResult) => UploadTestResultDto;
|
|
6
|
+
export declare const toUploadResultsDto: (testSessionId: number, trs: TestOpsPluginTestResult[]) => UploadResultsDto;
|
|
@@ -1,27 +1,23 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
export const
|
|
4
|
-
if (
|
|
5
|
-
return
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
};
|
|
1
|
+
import { toUploadAttachmentDto } from "./attachments.js";
|
|
2
|
+
import { validateExecutableName } from "./validation.js";
|
|
3
|
+
export const normalizeTestStepsResults = (steps) => {
|
|
4
|
+
if (!steps) {
|
|
5
|
+
return undefined;
|
|
6
|
+
}
|
|
7
|
+
const projected = [];
|
|
8
|
+
for (const step of steps) {
|
|
9
|
+
if (step.type === "attachment") {
|
|
10
|
+
projected.push(step);
|
|
11
|
+
}
|
|
12
|
+
else if (validateExecutableName(step.name)) {
|
|
13
|
+
projected.push({ ...step, ...(step.steps ? { steps: normalizeTestStepsResults(step.steps) } : {}) });
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
return projected;
|
|
18
17
|
};
|
|
19
18
|
const toUploadStepDto = (step) => {
|
|
20
19
|
if (step.type === "attachment") {
|
|
21
|
-
return {
|
|
22
|
-
type: "attachment",
|
|
23
|
-
attachment: toUploadAttachmentDto(step.link),
|
|
24
|
-
};
|
|
20
|
+
return { type: "attachment", attachment: toUploadAttachmentDto(step.link) };
|
|
25
21
|
}
|
|
26
22
|
return {
|
|
27
23
|
type: "body",
|
|
@@ -36,6 +32,8 @@ const toUploadStepDto = (step) => {
|
|
|
36
32
|
steps: step.steps?.map(toUploadStepDto),
|
|
37
33
|
};
|
|
38
34
|
};
|
|
35
|
+
const isObject = (v) => typeof v === "object" && v !== null;
|
|
36
|
+
const hasString = (obj, key) => typeof obj[key] === "string";
|
|
39
37
|
const normalizeCategory = (category) => {
|
|
40
38
|
if (!category)
|
|
41
39
|
return undefined;
|
|
@@ -46,8 +44,9 @@ const normalizeCategory = (category) => {
|
|
|
46
44
|
: typeof externalIdUnknown === "number"
|
|
47
45
|
? String(externalIdUnknown)
|
|
48
46
|
: undefined;
|
|
49
|
-
if (!externalId)
|
|
47
|
+
if (!externalId) {
|
|
50
48
|
return undefined;
|
|
49
|
+
}
|
|
51
50
|
const grouping = Array.isArray(groupingUnknown)
|
|
52
51
|
? groupingUnknown
|
|
53
52
|
.filter(isObject)
|
|
@@ -58,25 +57,18 @@ const normalizeCategory = (category) => {
|
|
|
58
57
|
}))
|
|
59
58
|
.filter((g) => g.key || g.value || g.name)
|
|
60
59
|
: undefined;
|
|
61
|
-
return {
|
|
62
|
-
externalId,
|
|
63
|
-
...(grouping && grouping.length > 0 ? { grouping } : {}),
|
|
64
|
-
};
|
|
60
|
+
return { externalId, ...(grouping && grouping.length > 0 ? { grouping } : {}) };
|
|
65
61
|
};
|
|
66
62
|
const optionalArray = (v, isItem) => {
|
|
67
|
-
if (!Array.isArray(v))
|
|
63
|
+
if (!Array.isArray(v)) {
|
|
68
64
|
return undefined;
|
|
69
|
-
const out = [];
|
|
70
|
-
for (const item of v) {
|
|
71
|
-
if (isItem(item))
|
|
72
|
-
out.push(item);
|
|
73
65
|
}
|
|
74
|
-
return
|
|
66
|
+
return v.filter(isItem);
|
|
75
67
|
};
|
|
76
68
|
const isAttachmentLink = (v) => isObject(v) && hasString(v, "id");
|
|
77
69
|
export const toUploadTestResultDto = (tr) => {
|
|
78
|
-
const
|
|
79
|
-
const
|
|
70
|
+
const attachments = optionalArray(tr.attachments, isAttachmentLink)?.map(toUploadAttachmentDto);
|
|
71
|
+
const category = normalizeCategory(tr.category);
|
|
80
72
|
return {
|
|
81
73
|
uuid: tr.id,
|
|
82
74
|
historyId: tr.historyId,
|
|
@@ -98,7 +90,7 @@ export const toUploadTestResultDto = (tr) => {
|
|
|
98
90
|
hostId: tr.hostId,
|
|
99
91
|
threadId: tr.threadId,
|
|
100
92
|
environment: tr.environment,
|
|
101
|
-
...(
|
|
93
|
+
...(category ? { category } : {}),
|
|
102
94
|
...(typeof tr.namedEnv?.id === "number" ? { namedEnv: { id: tr.namedEnv.id } } : {}),
|
|
103
95
|
steps: tr.steps?.map(toUploadStepDto),
|
|
104
96
|
...(attachments ? { attachments } : {}),
|
|
@@ -107,26 +99,7 @@ export const toUploadTestResultDto = (tr) => {
|
|
|
107
99
|
labels: tr.labels,
|
|
108
100
|
};
|
|
109
101
|
};
|
|
110
|
-
export const toUploadResultsDto = (testSessionId, trs) => {
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
};
|
|
115
|
-
};
|
|
116
|
-
export const toUploadFixtureResultDto = (fxt) => {
|
|
117
|
-
return {
|
|
118
|
-
type: fxt.type,
|
|
119
|
-
uuid: fxt.id,
|
|
120
|
-
name: fxt.name,
|
|
121
|
-
start: fxt.start,
|
|
122
|
-
stop: fxt.stop,
|
|
123
|
-
duration: fxt.duration,
|
|
124
|
-
status: fxt.status,
|
|
125
|
-
message: fxt.error?.message,
|
|
126
|
-
trace: fxt.error?.trace,
|
|
127
|
-
steps: fxt.steps?.map(toUploadStepDto),
|
|
128
|
-
};
|
|
129
|
-
};
|
|
130
|
-
export const toUploadFixturesResultsDto = (fixtures) => {
|
|
131
|
-
return { fixtures: fixtures.map(toUploadFixtureResultDto) };
|
|
132
|
-
};
|
|
102
|
+
export const toUploadResultsDto = (testSessionId, trs) => ({
|
|
103
|
+
testSessionId,
|
|
104
|
+
results: trs.map(toUploadTestResultDto),
|
|
105
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const validateExecutableName: (name: unknown) => name is string;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export const validateExecutableName = (name) => {
|
|
2
|
+
if (typeof name !== "string" || name.length === 0 || name.trim().length === 0) {
|
|
3
|
+
return false;
|
|
4
|
+
}
|
|
5
|
+
for (let index = 0; index < name.length; index += 1) {
|
|
6
|
+
const code = name.charCodeAt(index);
|
|
7
|
+
if (code <= 0x1f || (code >= 0x7f && code <= 0x9f) || code === 0xfffd) {
|
|
8
|
+
return false;
|
|
9
|
+
}
|
|
10
|
+
if (code >= 0xd800 && code <= 0xdbff) {
|
|
11
|
+
const nextCode = name.charCodeAt(index + 1);
|
|
12
|
+
if (nextCode < 0xdc00 || nextCode > 0xdfff || Number.isNaN(nextCode)) {
|
|
13
|
+
return false;
|
|
14
|
+
}
|
|
15
|
+
index += 1;
|
|
16
|
+
}
|
|
17
|
+
else if (code >= 0xdc00 && code <= 0xdfff) {
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return true;
|
|
22
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@allurereport/plugin-testops",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.15.0",
|
|
4
4
|
"description": "Allure Plugin TestOps",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"allure",
|
|
@@ -32,24 +32,23 @@
|
|
|
32
32
|
"lint:fix": "oxlint --import-plugin --fix src test features stories"
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
|
-
"@allurereport/ci": "3.
|
|
36
|
-
"@allurereport/
|
|
37
|
-
"@allurereport/
|
|
38
|
-
"@allurereport/
|
|
39
|
-
"@allurereport/
|
|
40
|
-
"@allurereport/
|
|
35
|
+
"@allurereport/ci": "3.15.0",
|
|
36
|
+
"@allurereport/cli-commons": "3.15.0",
|
|
37
|
+
"@allurereport/core-api": "3.15.0",
|
|
38
|
+
"@allurereport/git": "3.15.0",
|
|
39
|
+
"@allurereport/plugin-api": "3.15.0",
|
|
40
|
+
"@allurereport/reader-api": "3.15.0",
|
|
41
|
+
"@allurereport/service": "3.15.0",
|
|
41
42
|
"axios": "^1.18.1",
|
|
42
43
|
"form-data": "^4.0.6",
|
|
43
44
|
"lodash-es": "^4.18.1",
|
|
44
45
|
"p-limit": "^7.3.0",
|
|
45
|
-
"progress": "^2.0.3",
|
|
46
46
|
"yoctocolors": "^2"
|
|
47
47
|
},
|
|
48
48
|
"devDependencies": {
|
|
49
49
|
"@types/d3-shape": "^3.1.8",
|
|
50
50
|
"@types/lodash-es": "^4",
|
|
51
51
|
"@types/node": "^20",
|
|
52
|
-
"@types/progress": "^2",
|
|
53
52
|
"@vitest/runner": "^2",
|
|
54
53
|
"@vitest/snapshot": "^2.1.9",
|
|
55
54
|
"allure-js-commons": "^3",
|
|
@@ -1,3 +0,0 @@
|
|
|
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;
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import { buildUploadCategoryGrouping, toUploadCategoryFromContext } from "./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
|
-
};
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import type { AttachmentLink } from "@allurereport/core-api";
|
|
2
|
-
import type { UploadAttachmentDto, UploadFixturesResultsDto, UploadResultsDto, UploadTestFixtureResultDto, UploadTestResultDto } from "../model.js";
|
|
3
|
-
import type { TestOpsFixtureResult, TestOpsPluginTestResult } from "../model.js";
|
|
4
|
-
export declare const uploadFilenameForLink: (link: AttachmentLink) => string | undefined;
|
|
5
|
-
export declare const toUploadAttachmentDto: (link: AttachmentLink) => UploadAttachmentDto;
|
|
6
|
-
export declare const toUploadTestResultDto: (tr: TestOpsPluginTestResult) => UploadTestResultDto;
|
|
7
|
-
export declare const toUploadResultsDto: (testSessionId: number, trs: TestOpsPluginTestResult[]) => UploadResultsDto;
|
|
8
|
-
export declare const toUploadFixtureResultDto: (fxt: TestOpsFixtureResult) => UploadTestFixtureResultDto;
|
|
9
|
-
export declare const toUploadFixturesResultsDto: (fixtures: TestOpsFixtureResult[]) => UploadFixturesResultsDto;
|