@supatest/cypress-reporter 0.0.1
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 +44 -0
- package/dist/index.cjs +919 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +109 -0
- package/dist/index.d.ts +109 -0
- package/dist/index.js +884 -0
- package/dist/index.js.map +1 -0
- package/package.json +43 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,919 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/index.ts
|
|
31
|
+
var index_exports = {};
|
|
32
|
+
__export(index_exports, {
|
|
33
|
+
ErrorCollector: () => ErrorCollector,
|
|
34
|
+
default: () => supatestPlugin
|
|
35
|
+
});
|
|
36
|
+
module.exports = __toCommonJS(index_exports);
|
|
37
|
+
var import_node_fs = __toESM(require("fs"), 1);
|
|
38
|
+
var import_node_os = __toESM(require("os"), 1);
|
|
39
|
+
var import_node_path = __toESM(require("path"), 1);
|
|
40
|
+
|
|
41
|
+
// ../reporter-core/dist/index.js
|
|
42
|
+
var import_simple_git = require("simple-git");
|
|
43
|
+
var import_fs = __toESM(require("fs"), 1);
|
|
44
|
+
var import_crypto = require("crypto");
|
|
45
|
+
var import_fs2 = __toESM(require("fs"), 1);
|
|
46
|
+
var defaultRetryConfig = {
|
|
47
|
+
maxAttempts: 3,
|
|
48
|
+
baseDelayMs: 1e3,
|
|
49
|
+
maxDelayMs: 1e4,
|
|
50
|
+
retryOnStatusCodes: [408, 429, 500, 502, 503, 504]
|
|
51
|
+
};
|
|
52
|
+
function sleep(ms) {
|
|
53
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
54
|
+
}
|
|
55
|
+
function shouldRetry(error, config) {
|
|
56
|
+
if (error instanceof Error && "statusCode" in error) {
|
|
57
|
+
const statusCode = error.statusCode;
|
|
58
|
+
return config.retryOnStatusCodes.includes(statusCode);
|
|
59
|
+
}
|
|
60
|
+
if (error instanceof Error) {
|
|
61
|
+
const networkErrors = ["ECONNRESET", "ETIMEDOUT", "ECONNREFUSED", "ENOTFOUND"];
|
|
62
|
+
return networkErrors.some((e) => error.message.includes(e));
|
|
63
|
+
}
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
async function withRetry(fn, config = defaultRetryConfig) {
|
|
67
|
+
let lastError;
|
|
68
|
+
for (let attempt = 1; attempt <= config.maxAttempts; attempt++) {
|
|
69
|
+
try {
|
|
70
|
+
return await fn();
|
|
71
|
+
} catch (error) {
|
|
72
|
+
lastError = error;
|
|
73
|
+
if (attempt === config.maxAttempts) {
|
|
74
|
+
throw error;
|
|
75
|
+
}
|
|
76
|
+
if (!shouldRetry(error, config)) {
|
|
77
|
+
throw error;
|
|
78
|
+
}
|
|
79
|
+
const delay = Math.min(
|
|
80
|
+
config.baseDelayMs * Math.pow(2, attempt - 1),
|
|
81
|
+
config.maxDelayMs
|
|
82
|
+
);
|
|
83
|
+
await sleep(delay);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
throw lastError;
|
|
87
|
+
}
|
|
88
|
+
var SupatestApiClient = class {
|
|
89
|
+
options;
|
|
90
|
+
retryConfig;
|
|
91
|
+
constructor(options) {
|
|
92
|
+
this.options = options;
|
|
93
|
+
this.retryConfig = {
|
|
94
|
+
...defaultRetryConfig,
|
|
95
|
+
maxAttempts: options.retryAttempts
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
async createRun(data) {
|
|
99
|
+
if (this.options.dryRun) {
|
|
100
|
+
this.logPayload("POST /v1/runs", data);
|
|
101
|
+
return {
|
|
102
|
+
runId: `mock_run_${Date.now()}`,
|
|
103
|
+
status: "running"
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
return this.request("POST", "/v1/runs", data);
|
|
107
|
+
}
|
|
108
|
+
async submitTest(runId, data) {
|
|
109
|
+
if (this.options.dryRun) {
|
|
110
|
+
this.logPayload(`POST /v1/runs/${runId}/tests`, data);
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
await this.request("POST", `/v1/runs/${runId}/tests`, data);
|
|
114
|
+
}
|
|
115
|
+
async signAttachments(runId, data) {
|
|
116
|
+
if (this.options.dryRun) {
|
|
117
|
+
this.logPayload(`POST /v1/runs/${runId}/attachments/sign`, data);
|
|
118
|
+
return {
|
|
119
|
+
uploads: data.attachments.map((att, i) => ({
|
|
120
|
+
attachmentId: `mock_att_${i}_${Date.now()}`,
|
|
121
|
+
signedUrl: `https://mock-s3.example.com/uploads/${att.filename}`,
|
|
122
|
+
expiresAt: new Date(Date.now() + 15 * 60 * 1e3).toISOString()
|
|
123
|
+
}))
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
return this.request(
|
|
127
|
+
"POST",
|
|
128
|
+
`/v1/runs/${runId}/attachments/sign`,
|
|
129
|
+
data
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
async completeRun(runId, data) {
|
|
133
|
+
if (this.options.dryRun) {
|
|
134
|
+
this.logPayload(`POST /v1/runs/${runId}/complete`, data);
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
await this.request("POST", `/v1/runs/${runId}/complete`, data);
|
|
138
|
+
}
|
|
139
|
+
async request(method, path2, body) {
|
|
140
|
+
const url = `${this.options.apiUrl}${path2}`;
|
|
141
|
+
return withRetry(async () => {
|
|
142
|
+
const controller = new AbortController();
|
|
143
|
+
const timeoutId = setTimeout(
|
|
144
|
+
() => controller.abort(),
|
|
145
|
+
this.options.timeoutMs
|
|
146
|
+
);
|
|
147
|
+
try {
|
|
148
|
+
const response = await fetch(url, {
|
|
149
|
+
method,
|
|
150
|
+
headers: {
|
|
151
|
+
"Content-Type": "application/json",
|
|
152
|
+
Authorization: `Bearer ${this.options.apiKey}`
|
|
153
|
+
},
|
|
154
|
+
body: body ? JSON.stringify(body) : void 0,
|
|
155
|
+
signal: controller.signal
|
|
156
|
+
});
|
|
157
|
+
if (!response.ok) {
|
|
158
|
+
const error = new Error(
|
|
159
|
+
`API request failed: ${response.status} ${response.statusText}`
|
|
160
|
+
);
|
|
161
|
+
error.statusCode = response.status;
|
|
162
|
+
throw error;
|
|
163
|
+
}
|
|
164
|
+
const text = await response.text();
|
|
165
|
+
return text ? JSON.parse(text) : {};
|
|
166
|
+
} finally {
|
|
167
|
+
clearTimeout(timeoutId);
|
|
168
|
+
}
|
|
169
|
+
}, this.retryConfig);
|
|
170
|
+
}
|
|
171
|
+
logPayload(endpoint, data) {
|
|
172
|
+
console.log(`
|
|
173
|
+
[supatest][dry-run] ${endpoint}`);
|
|
174
|
+
console.log(JSON.stringify(data, null, 2));
|
|
175
|
+
}
|
|
176
|
+
};
|
|
177
|
+
var ErrorCollector = class {
|
|
178
|
+
errors = [];
|
|
179
|
+
recordError(category, message, context) {
|
|
180
|
+
const error = {
|
|
181
|
+
category,
|
|
182
|
+
message,
|
|
183
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
184
|
+
testId: context?.testId,
|
|
185
|
+
testTitle: context?.testTitle,
|
|
186
|
+
attachmentName: context?.attachmentName,
|
|
187
|
+
filePath: context?.filePath,
|
|
188
|
+
originalError: context?.error instanceof Error ? context.error.stack : void 0
|
|
189
|
+
};
|
|
190
|
+
this.errors.push(error);
|
|
191
|
+
}
|
|
192
|
+
getSummary() {
|
|
193
|
+
const byCategory = {
|
|
194
|
+
RUN_CREATE: 0,
|
|
195
|
+
TEST_SUBMISSION: 0,
|
|
196
|
+
ATTACHMENT_SIGN: 0,
|
|
197
|
+
ATTACHMENT_UPLOAD: 0,
|
|
198
|
+
FILE_READ: 0,
|
|
199
|
+
RUN_COMPLETE: 0
|
|
200
|
+
};
|
|
201
|
+
for (const error of this.errors) {
|
|
202
|
+
byCategory[error.category]++;
|
|
203
|
+
}
|
|
204
|
+
return {
|
|
205
|
+
totalErrors: this.errors.length,
|
|
206
|
+
byCategory,
|
|
207
|
+
errors: this.errors
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
hasErrors() {
|
|
211
|
+
return this.errors.length > 0;
|
|
212
|
+
}
|
|
213
|
+
formatSummary() {
|
|
214
|
+
if (this.errors.length === 0) {
|
|
215
|
+
return "";
|
|
216
|
+
}
|
|
217
|
+
const summary = this.getSummary();
|
|
218
|
+
const lines = [
|
|
219
|
+
"",
|
|
220
|
+
"\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501",
|
|
221
|
+
"\u26A0\uFE0F Supatest Reporter - Error Summary",
|
|
222
|
+
"\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501",
|
|
223
|
+
`Total Errors: ${summary.totalErrors}`,
|
|
224
|
+
""
|
|
225
|
+
];
|
|
226
|
+
for (const [category, count] of Object.entries(summary.byCategory)) {
|
|
227
|
+
if (count === 0) continue;
|
|
228
|
+
const icon = this.getCategoryIcon(category);
|
|
229
|
+
const label = this.getCategoryLabel(category);
|
|
230
|
+
lines.push(`${icon} ${label}: ${count}`);
|
|
231
|
+
const categoryErrors = this.errors.filter((e) => e.category === category).slice(0, 3);
|
|
232
|
+
for (const error of categoryErrors) {
|
|
233
|
+
lines.push(` \u2022 ${this.formatError(error)}`);
|
|
234
|
+
}
|
|
235
|
+
const remaining = count - categoryErrors.length;
|
|
236
|
+
if (remaining > 0) {
|
|
237
|
+
lines.push(` ... and ${remaining} more`);
|
|
238
|
+
}
|
|
239
|
+
lines.push("");
|
|
240
|
+
}
|
|
241
|
+
lines.push(
|
|
242
|
+
"\u2139\uFE0F Note: Test execution was not interrupted. Some results may not be visible in the dashboard."
|
|
243
|
+
);
|
|
244
|
+
lines.push(
|
|
245
|
+
"\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501"
|
|
246
|
+
);
|
|
247
|
+
return lines.join("\n");
|
|
248
|
+
}
|
|
249
|
+
getCategoryIcon(category) {
|
|
250
|
+
const icons = {
|
|
251
|
+
RUN_CREATE: "\u{1F680}",
|
|
252
|
+
TEST_SUBMISSION: "\u{1F4DD}",
|
|
253
|
+
ATTACHMENT_SIGN: "\u{1F510}",
|
|
254
|
+
ATTACHMENT_UPLOAD: "\u{1F4CE}",
|
|
255
|
+
FILE_READ: "\u{1F4C1}",
|
|
256
|
+
RUN_COMPLETE: "\u{1F3C1}"
|
|
257
|
+
};
|
|
258
|
+
return icons[category];
|
|
259
|
+
}
|
|
260
|
+
getCategoryLabel(category) {
|
|
261
|
+
const labels = {
|
|
262
|
+
RUN_CREATE: "Run Initialization",
|
|
263
|
+
TEST_SUBMISSION: "Test Result Submission",
|
|
264
|
+
ATTACHMENT_SIGN: "Attachment Signing",
|
|
265
|
+
ATTACHMENT_UPLOAD: "Attachment Upload",
|
|
266
|
+
FILE_READ: "File Access",
|
|
267
|
+
RUN_COMPLETE: "Run Completion"
|
|
268
|
+
};
|
|
269
|
+
return labels[category];
|
|
270
|
+
}
|
|
271
|
+
formatError(error) {
|
|
272
|
+
const parts = [];
|
|
273
|
+
if (error.testTitle) {
|
|
274
|
+
parts.push(`Test: "${error.testTitle}"`);
|
|
275
|
+
}
|
|
276
|
+
if (error.attachmentName) {
|
|
277
|
+
parts.push(`Attachment: "${error.attachmentName}"`);
|
|
278
|
+
}
|
|
279
|
+
if (error.filePath) {
|
|
280
|
+
parts.push(`File: ${error.filePath}`);
|
|
281
|
+
}
|
|
282
|
+
parts.push(error.message);
|
|
283
|
+
return parts.join(" - ");
|
|
284
|
+
}
|
|
285
|
+
};
|
|
286
|
+
async function getLocalGitInfo(rootDir) {
|
|
287
|
+
try {
|
|
288
|
+
const git = (0, import_simple_git.simpleGit)(rootDir || process.cwd());
|
|
289
|
+
const isRepo = await git.checkIsRepo();
|
|
290
|
+
if (!isRepo) {
|
|
291
|
+
return {};
|
|
292
|
+
}
|
|
293
|
+
const [branchResult, commitResult, status, remotes] = await Promise.all([
|
|
294
|
+
git.revparse(["--abbrev-ref", "HEAD"]).catch(() => void 0),
|
|
295
|
+
git.revparse(["HEAD"]).catch(() => void 0),
|
|
296
|
+
git.status().catch(() => void 0),
|
|
297
|
+
git.getRemotes(true).catch(() => [])
|
|
298
|
+
]);
|
|
299
|
+
const branch = typeof branchResult === "string" ? branchResult.trim() : void 0;
|
|
300
|
+
const commit = typeof commitResult === "string" ? commitResult.trim() : void 0;
|
|
301
|
+
let commitMessage;
|
|
302
|
+
try {
|
|
303
|
+
const logOutput = await git.raw(["log", "-1", "--format=%s"]);
|
|
304
|
+
commitMessage = typeof logOutput === "string" ? logOutput.trim() : void 0;
|
|
305
|
+
} catch {
|
|
306
|
+
}
|
|
307
|
+
let author;
|
|
308
|
+
let authorEmail;
|
|
309
|
+
try {
|
|
310
|
+
const authorConfig = await git.getConfig("user.name").catch(() => void 0);
|
|
311
|
+
const emailConfig = await git.getConfig("user.email").catch(() => void 0);
|
|
312
|
+
author = authorConfig && typeof authorConfig.value === "string" ? authorConfig.value.trim() : void 0;
|
|
313
|
+
authorEmail = emailConfig && typeof emailConfig.value === "string" ? emailConfig.value.trim() : void 0;
|
|
314
|
+
} catch {
|
|
315
|
+
}
|
|
316
|
+
let tag;
|
|
317
|
+
try {
|
|
318
|
+
const tagResult = await git.raw(["describe", "--tags", "--exact-match"]).catch(() => void 0);
|
|
319
|
+
tag = typeof tagResult === "string" ? tagResult.trim() : void 0;
|
|
320
|
+
} catch {
|
|
321
|
+
}
|
|
322
|
+
let repo;
|
|
323
|
+
const remote = remotes && remotes.length > 0 ? remotes[0] : null;
|
|
324
|
+
if (remote && remote.refs && remote.refs.fetch) {
|
|
325
|
+
repo = remote.refs.fetch;
|
|
326
|
+
}
|
|
327
|
+
const dirty = status ? status.modified.length > 0 || status.created.length > 0 || status.deleted.length > 0 : false;
|
|
328
|
+
return {
|
|
329
|
+
branch,
|
|
330
|
+
commit,
|
|
331
|
+
commitMessage,
|
|
332
|
+
repo,
|
|
333
|
+
author,
|
|
334
|
+
authorEmail,
|
|
335
|
+
tag,
|
|
336
|
+
dirty
|
|
337
|
+
};
|
|
338
|
+
} catch (error) {
|
|
339
|
+
return {};
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
var AttachmentUploader = class {
|
|
343
|
+
options;
|
|
344
|
+
constructor(options) {
|
|
345
|
+
this.options = options;
|
|
346
|
+
}
|
|
347
|
+
async upload(signedUrl, filePath, contentType) {
|
|
348
|
+
if (this.options.dryRun) {
|
|
349
|
+
try {
|
|
350
|
+
const stats = import_fs.default.statSync(filePath);
|
|
351
|
+
console.log(
|
|
352
|
+
`[supatest][dry-run] Would upload ${filePath} (${stats.size} bytes) to S3`
|
|
353
|
+
);
|
|
354
|
+
} catch (error) {
|
|
355
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
356
|
+
console.warn(
|
|
357
|
+
`[supatest][dry-run] Cannot access file ${filePath}: ${message}`
|
|
358
|
+
);
|
|
359
|
+
}
|
|
360
|
+
return;
|
|
361
|
+
}
|
|
362
|
+
let fileBuffer;
|
|
363
|
+
try {
|
|
364
|
+
fileBuffer = await import_fs.default.promises.readFile(filePath);
|
|
365
|
+
} catch (error) {
|
|
366
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
367
|
+
throw new Error(`Failed to read file ${filePath}: ${message}`);
|
|
368
|
+
}
|
|
369
|
+
await withRetry(async () => {
|
|
370
|
+
const controller = new AbortController();
|
|
371
|
+
const timeoutId = setTimeout(
|
|
372
|
+
() => controller.abort(),
|
|
373
|
+
this.options.timeoutMs
|
|
374
|
+
);
|
|
375
|
+
try {
|
|
376
|
+
const response = await fetch(signedUrl, {
|
|
377
|
+
method: "PUT",
|
|
378
|
+
headers: {
|
|
379
|
+
"Content-Type": contentType,
|
|
380
|
+
"Content-Length": String(fileBuffer.length)
|
|
381
|
+
},
|
|
382
|
+
body: new Uint8Array(fileBuffer),
|
|
383
|
+
signal: controller.signal
|
|
384
|
+
});
|
|
385
|
+
if (!response.ok) {
|
|
386
|
+
const error = new Error(
|
|
387
|
+
`S3 upload failed: ${response.status} ${response.statusText}`
|
|
388
|
+
);
|
|
389
|
+
error.statusCode = response.status;
|
|
390
|
+
throw error;
|
|
391
|
+
}
|
|
392
|
+
} finally {
|
|
393
|
+
clearTimeout(timeoutId);
|
|
394
|
+
}
|
|
395
|
+
}, defaultRetryConfig);
|
|
396
|
+
}
|
|
397
|
+
async uploadBatch(items, signedUploads) {
|
|
398
|
+
const pLimit = (await import("p-limit")).default;
|
|
399
|
+
const limit = pLimit(this.options.maxConcurrent);
|
|
400
|
+
const results = await Promise.allSettled(
|
|
401
|
+
items.map(
|
|
402
|
+
(item, index) => limit(async () => {
|
|
403
|
+
try {
|
|
404
|
+
await this.upload(item.signedUrl, item.filePath, item.contentType);
|
|
405
|
+
return {
|
|
406
|
+
success: true,
|
|
407
|
+
attachmentId: signedUploads[index]?.attachmentId
|
|
408
|
+
};
|
|
409
|
+
} catch (error) {
|
|
410
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
411
|
+
return {
|
|
412
|
+
success: false,
|
|
413
|
+
attachmentId: signedUploads[index]?.attachmentId,
|
|
414
|
+
error: `${item.filePath}: ${message}`
|
|
415
|
+
};
|
|
416
|
+
}
|
|
417
|
+
})
|
|
418
|
+
)
|
|
419
|
+
);
|
|
420
|
+
return results.map((result, index) => {
|
|
421
|
+
if (result.status === "fulfilled") {
|
|
422
|
+
return result.value;
|
|
423
|
+
}
|
|
424
|
+
const message = result.reason instanceof Error ? result.reason.message : String(result.reason);
|
|
425
|
+
return {
|
|
426
|
+
success: false,
|
|
427
|
+
attachmentId: signedUploads[index]?.attachmentId,
|
|
428
|
+
error: message
|
|
429
|
+
};
|
|
430
|
+
});
|
|
431
|
+
}
|
|
432
|
+
};
|
|
433
|
+
function hashKey(value) {
|
|
434
|
+
return (0, import_crypto.createHash)("sha256").update(value).digest("hex").slice(0, 12);
|
|
435
|
+
}
|
|
436
|
+
function getFileSize(filePath) {
|
|
437
|
+
try {
|
|
438
|
+
return import_fs2.default.statSync(filePath).size;
|
|
439
|
+
} catch {
|
|
440
|
+
return 0;
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
function getErrorMessage(error) {
|
|
444
|
+
if (error instanceof Error) return error.message;
|
|
445
|
+
return String(error);
|
|
446
|
+
}
|
|
447
|
+
function logInfo(message) {
|
|
448
|
+
console.log(`[supatest] ${message}`);
|
|
449
|
+
}
|
|
450
|
+
function logWarn(message) {
|
|
451
|
+
console.warn(`[supatest] ${message}`);
|
|
452
|
+
}
|
|
453
|
+
function getCIInfo() {
|
|
454
|
+
if (process.env.GITHUB_ACTIONS) {
|
|
455
|
+
return {
|
|
456
|
+
provider: "github-actions",
|
|
457
|
+
runId: process.env.GITHUB_RUN_ID,
|
|
458
|
+
jobId: process.env.GITHUB_JOB,
|
|
459
|
+
jobUrl: `${process.env.GITHUB_SERVER_URL}/${process.env.GITHUB_REPOSITORY}/actions/runs/${process.env.GITHUB_RUN_ID}`,
|
|
460
|
+
buildNumber: process.env.GITHUB_RUN_NUMBER,
|
|
461
|
+
branch: process.env.GITHUB_REF_NAME,
|
|
462
|
+
pullRequest: process.env.GITHUB_EVENT_NAME === "pull_request" ? {
|
|
463
|
+
number: process.env.GITHUB_PR_NUMBER ?? "",
|
|
464
|
+
url: `${process.env.GITHUB_SERVER_URL}/${process.env.GITHUB_REPOSITORY}/pull/${process.env.GITHUB_PR_NUMBER}`
|
|
465
|
+
} : void 0
|
|
466
|
+
};
|
|
467
|
+
}
|
|
468
|
+
if (process.env.GITLAB_CI) {
|
|
469
|
+
return {
|
|
470
|
+
provider: "gitlab-ci",
|
|
471
|
+
runId: process.env.CI_PIPELINE_ID,
|
|
472
|
+
jobId: process.env.CI_JOB_ID,
|
|
473
|
+
jobUrl: process.env.CI_JOB_URL,
|
|
474
|
+
buildNumber: process.env.CI_PIPELINE_IID,
|
|
475
|
+
branch: process.env.CI_COMMIT_BRANCH,
|
|
476
|
+
pullRequest: process.env.CI_MERGE_REQUEST_IID ? {
|
|
477
|
+
number: process.env.CI_MERGE_REQUEST_IID,
|
|
478
|
+
url: process.env.CI_MERGE_REQUEST_PROJECT_URL + "/-/merge_requests/" + process.env.CI_MERGE_REQUEST_IID,
|
|
479
|
+
title: process.env.CI_MERGE_REQUEST_TITLE
|
|
480
|
+
} : void 0
|
|
481
|
+
};
|
|
482
|
+
}
|
|
483
|
+
if (process.env.JENKINS_URL) {
|
|
484
|
+
return {
|
|
485
|
+
provider: "jenkins",
|
|
486
|
+
runId: process.env.BUILD_ID,
|
|
487
|
+
jobUrl: process.env.BUILD_URL,
|
|
488
|
+
buildNumber: process.env.BUILD_NUMBER,
|
|
489
|
+
branch: process.env.GIT_BRANCH
|
|
490
|
+
};
|
|
491
|
+
}
|
|
492
|
+
if (process.env.CIRCLECI) {
|
|
493
|
+
return {
|
|
494
|
+
provider: "circleci",
|
|
495
|
+
runId: process.env.CIRCLE_WORKFLOW_ID,
|
|
496
|
+
jobId: process.env.CIRCLE_JOB,
|
|
497
|
+
jobUrl: process.env.CIRCLE_BUILD_URL,
|
|
498
|
+
buildNumber: process.env.CIRCLE_BUILD_NUM,
|
|
499
|
+
branch: process.env.CIRCLE_BRANCH,
|
|
500
|
+
pullRequest: process.env.CIRCLE_PULL_REQUEST ? {
|
|
501
|
+
number: process.env.CIRCLE_PR_NUMBER ?? "",
|
|
502
|
+
url: process.env.CIRCLE_PULL_REQUEST
|
|
503
|
+
} : void 0
|
|
504
|
+
};
|
|
505
|
+
}
|
|
506
|
+
if (process.env.TRAVIS) {
|
|
507
|
+
return {
|
|
508
|
+
provider: "travis",
|
|
509
|
+
runId: process.env.TRAVIS_BUILD_ID,
|
|
510
|
+
jobId: process.env.TRAVIS_JOB_ID,
|
|
511
|
+
jobUrl: process.env.TRAVIS_JOB_WEB_URL,
|
|
512
|
+
buildNumber: process.env.TRAVIS_BUILD_NUMBER,
|
|
513
|
+
branch: process.env.TRAVIS_BRANCH,
|
|
514
|
+
pullRequest: process.env.TRAVIS_PULL_REQUEST !== "false" ? {
|
|
515
|
+
number: process.env.TRAVIS_PULL_REQUEST ?? "",
|
|
516
|
+
url: `https://github.com/${process.env.TRAVIS_REPO_SLUG}/pull/${process.env.TRAVIS_PULL_REQUEST}`
|
|
517
|
+
} : void 0
|
|
518
|
+
};
|
|
519
|
+
}
|
|
520
|
+
if (process.env.BUILDKITE) {
|
|
521
|
+
return {
|
|
522
|
+
provider: "buildkite",
|
|
523
|
+
runId: process.env.BUILDKITE_BUILD_ID,
|
|
524
|
+
jobId: process.env.BUILDKITE_JOB_ID,
|
|
525
|
+
jobUrl: process.env.BUILDKITE_BUILD_URL,
|
|
526
|
+
buildNumber: process.env.BUILDKITE_BUILD_NUMBER,
|
|
527
|
+
branch: process.env.BUILDKITE_BRANCH
|
|
528
|
+
};
|
|
529
|
+
}
|
|
530
|
+
if (process.env.AZURE_PIPELINES || process.env.TF_BUILD) {
|
|
531
|
+
return {
|
|
532
|
+
provider: "azure-pipelines",
|
|
533
|
+
runId: process.env.BUILD_BUILDID,
|
|
534
|
+
jobUrl: `${process.env.SYSTEM_COLLECTIONURI}${process.env.SYSTEM_TEAMPROJECT}/_build/results?buildId=${process.env.BUILD_BUILDID}`,
|
|
535
|
+
buildNumber: process.env.BUILD_BUILDNUMBER,
|
|
536
|
+
branch: process.env.BUILD_SOURCEBRANCH
|
|
537
|
+
};
|
|
538
|
+
}
|
|
539
|
+
return void 0;
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
// src/index.ts
|
|
543
|
+
var DEFAULT_API_URL = "https://code-api.supatest.ai";
|
|
544
|
+
var SupatestCypressReporter = class {
|
|
545
|
+
options;
|
|
546
|
+
client;
|
|
547
|
+
uploader;
|
|
548
|
+
errorCollector = new ErrorCollector();
|
|
549
|
+
runId;
|
|
550
|
+
uploadQueue = [];
|
|
551
|
+
startedAt;
|
|
552
|
+
firstTestStartTime;
|
|
553
|
+
firstFailureTime;
|
|
554
|
+
testsProcessed = /* @__PURE__ */ new Map();
|
|
555
|
+
disabled = false;
|
|
556
|
+
rootDir;
|
|
557
|
+
stepIdCounter = 0;
|
|
558
|
+
cypressVersion;
|
|
559
|
+
browserName;
|
|
560
|
+
browserVersion;
|
|
561
|
+
constructor(options = {}) {
|
|
562
|
+
this.options = {
|
|
563
|
+
projectId: options.projectId || process.env.SUPATEST_PROJECT_ID || "",
|
|
564
|
+
apiKey: options.apiKey || process.env.SUPATEST_API_KEY || "",
|
|
565
|
+
apiUrl: options.apiUrl || process.env.SUPATEST_API_URL || DEFAULT_API_URL,
|
|
566
|
+
uploadAssets: options.uploadAssets ?? true,
|
|
567
|
+
maxConcurrentUploads: options.maxConcurrentUploads ?? 5,
|
|
568
|
+
retryAttempts: options.retryAttempts ?? 3,
|
|
569
|
+
timeoutMs: options.timeoutMs ?? 3e4,
|
|
570
|
+
dryRun: options.dryRun ?? process.env.SUPATEST_DRY_RUN === "true"
|
|
571
|
+
};
|
|
572
|
+
}
|
|
573
|
+
async onBeforeRun(details) {
|
|
574
|
+
this.rootDir = details.config.projectRoot;
|
|
575
|
+
this.cypressVersion = details.config.version;
|
|
576
|
+
this.browserName = details.browser.name;
|
|
577
|
+
this.browserVersion = details.browser.version;
|
|
578
|
+
if (!this.options.projectId || !this.options.apiKey) {
|
|
579
|
+
if (!this.options.dryRun) {
|
|
580
|
+
logWarn("Missing projectId or apiKey. Set SUPATEST_PROJECT_ID and SUPATEST_API_KEY.");
|
|
581
|
+
this.disabled = true;
|
|
582
|
+
return;
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
this.client = new SupatestApiClient({
|
|
586
|
+
apiKey: this.options.apiKey,
|
|
587
|
+
apiUrl: this.options.apiUrl,
|
|
588
|
+
timeoutMs: this.options.timeoutMs,
|
|
589
|
+
retryAttempts: this.options.retryAttempts,
|
|
590
|
+
dryRun: this.options.dryRun
|
|
591
|
+
});
|
|
592
|
+
this.uploader = new AttachmentUploader({
|
|
593
|
+
maxConcurrent: this.options.maxConcurrentUploads,
|
|
594
|
+
timeoutMs: this.options.timeoutMs,
|
|
595
|
+
dryRun: this.options.dryRun
|
|
596
|
+
});
|
|
597
|
+
this.startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
598
|
+
try {
|
|
599
|
+
const runRequest = {
|
|
600
|
+
projectId: this.options.projectId,
|
|
601
|
+
startedAt: this.startedAt,
|
|
602
|
+
cypress: {
|
|
603
|
+
version: this.cypressVersion || "unknown",
|
|
604
|
+
browser: this.browserName || "electron"
|
|
605
|
+
},
|
|
606
|
+
projects: [{
|
|
607
|
+
name: this.browserName || "electron",
|
|
608
|
+
browserName: this.browserName
|
|
609
|
+
}],
|
|
610
|
+
testStats: {
|
|
611
|
+
totalFiles: details.specs.length,
|
|
612
|
+
totalTests: 0,
|
|
613
|
+
// Unknown at this point
|
|
614
|
+
totalProjects: 1
|
|
615
|
+
},
|
|
616
|
+
environment: this.getEnvironmentInfo(),
|
|
617
|
+
git: await this.getGitInfo(),
|
|
618
|
+
rootDir: this.rootDir
|
|
619
|
+
};
|
|
620
|
+
const response = await this.client.createRun(runRequest);
|
|
621
|
+
this.runId = response.runId;
|
|
622
|
+
logInfo(`Run ${this.runId} started (${details.specs.length} spec files)`);
|
|
623
|
+
} catch (error) {
|
|
624
|
+
this.errorCollector.recordError("RUN_CREATE", getErrorMessage(error), { error });
|
|
625
|
+
this.disabled = true;
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
async onAfterSpec(spec, results) {
|
|
629
|
+
if (this.disabled || !this.runId) return;
|
|
630
|
+
for (const test of results.tests) {
|
|
631
|
+
await this.processTestResult(spec, test, results);
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
async onAfterRun(results) {
|
|
635
|
+
if (this.disabled || !this.runId) {
|
|
636
|
+
if (this.errorCollector.hasErrors()) {
|
|
637
|
+
console.log(this.errorCollector.formatSummary());
|
|
638
|
+
}
|
|
639
|
+
return;
|
|
640
|
+
}
|
|
641
|
+
await Promise.allSettled(this.uploadQueue);
|
|
642
|
+
const summary = {
|
|
643
|
+
total: results.totalTests,
|
|
644
|
+
passed: results.totalPassed,
|
|
645
|
+
failed: results.totalFailed,
|
|
646
|
+
flaky: 0,
|
|
647
|
+
skipped: results.totalSkipped + results.totalPending,
|
|
648
|
+
timedOut: 0,
|
|
649
|
+
interrupted: 0,
|
|
650
|
+
durationMs: results.totalDuration
|
|
651
|
+
};
|
|
652
|
+
try {
|
|
653
|
+
await this.client.completeRun(this.runId, {
|
|
654
|
+
status: results.status === "finished" ? "complete" : "errored",
|
|
655
|
+
endedAt: results.endedTestsAt,
|
|
656
|
+
summary,
|
|
657
|
+
timing: {
|
|
658
|
+
totalDurationMs: results.totalDuration,
|
|
659
|
+
timeToFirstTest: this.firstTestStartTime ? this.firstTestStartTime - new Date(this.startedAt).getTime() : void 0,
|
|
660
|
+
timeToFirstFailure: this.firstFailureTime ? this.firstFailureTime - new Date(this.startedAt).getTime() : void 0
|
|
661
|
+
}
|
|
662
|
+
});
|
|
663
|
+
logInfo(`Run ${this.runId} completed`);
|
|
664
|
+
} catch (error) {
|
|
665
|
+
this.errorCollector.recordError("RUN_COMPLETE", getErrorMessage(error), { error });
|
|
666
|
+
}
|
|
667
|
+
if (this.errorCollector.hasErrors()) {
|
|
668
|
+
console.log(this.errorCollector.formatSummary());
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
async processTestResult(spec, test, specResults) {
|
|
672
|
+
const testId = this.getTestId(spec.relative, test.title);
|
|
673
|
+
const status = this.mapCypressStatus(test.state);
|
|
674
|
+
if (!this.firstTestStartTime && test.attempts[0]) {
|
|
675
|
+
this.firstTestStartTime = new Date(test.attempts[0].wallClockStartedAt).getTime();
|
|
676
|
+
}
|
|
677
|
+
if (!this.firstFailureTime && status === "failed") {
|
|
678
|
+
this.firstFailureTime = Date.now();
|
|
679
|
+
}
|
|
680
|
+
const lastAttempt = test.attempts[test.attempts.length - 1];
|
|
681
|
+
const resultEntry = {
|
|
682
|
+
resultId: hashKey(`${testId}:${test.attempts.length - 1}`),
|
|
683
|
+
retry: test.attempts.length - 1,
|
|
684
|
+
status,
|
|
685
|
+
startTime: lastAttempt?.wallClockStartedAt,
|
|
686
|
+
durationMs: lastAttempt?.wallClockDuration || 0,
|
|
687
|
+
errors: this.extractErrors(test),
|
|
688
|
+
steps: [],
|
|
689
|
+
// Cypress doesn't expose steps like Playwright
|
|
690
|
+
attachments: this.buildAttachmentMeta(test, specResults)
|
|
691
|
+
};
|
|
692
|
+
const tags = this.extractTags(test.title.join(" "));
|
|
693
|
+
const metadata = this.parseTestMetadata(tags);
|
|
694
|
+
const payload = {
|
|
695
|
+
testId,
|
|
696
|
+
cypressId: testId,
|
|
697
|
+
file: spec.relative,
|
|
698
|
+
location: { file: spec.relative, line: 0, column: 0 },
|
|
699
|
+
title: test.title[test.title.length - 1],
|
|
700
|
+
titlePath: test.title,
|
|
701
|
+
tags,
|
|
702
|
+
annotations: [],
|
|
703
|
+
expectedStatus: "passed",
|
|
704
|
+
outcome: this.getOutcome(test),
|
|
705
|
+
timeout: 0,
|
|
706
|
+
retries: test.attempts.length - 1,
|
|
707
|
+
status,
|
|
708
|
+
durationMs: test.attempts.reduce((sum, a) => sum + (a.wallClockDuration || 0), 0),
|
|
709
|
+
retryCount: test.attempts.length - 1,
|
|
710
|
+
results: [resultEntry],
|
|
711
|
+
projectName: this.browserName || "electron",
|
|
712
|
+
metadata
|
|
713
|
+
};
|
|
714
|
+
try {
|
|
715
|
+
await this.client.submitTest(this.runId, payload);
|
|
716
|
+
if (this.options.uploadAssets) {
|
|
717
|
+
await this.uploadAttachments(testId, resultEntry.resultId, specResults, test);
|
|
718
|
+
}
|
|
719
|
+
} catch (error) {
|
|
720
|
+
this.errorCollector.recordError("TEST_SUBMISSION", getErrorMessage(error), {
|
|
721
|
+
testId,
|
|
722
|
+
testTitle: test.title.join(" > "),
|
|
723
|
+
error
|
|
724
|
+
});
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
getTestId(file, titlePath) {
|
|
728
|
+
const fullTitle = titlePath.join(" ");
|
|
729
|
+
const idMatch = fullTitle.match(/@id:([^\s@]+)/);
|
|
730
|
+
if (idMatch) return idMatch[1];
|
|
731
|
+
return hashKey(`${file}::${titlePath.join("::")}`);
|
|
732
|
+
}
|
|
733
|
+
mapCypressStatus(state) {
|
|
734
|
+
switch (state) {
|
|
735
|
+
case "passed":
|
|
736
|
+
return "passed";
|
|
737
|
+
case "failed":
|
|
738
|
+
return "failed";
|
|
739
|
+
case "pending":
|
|
740
|
+
case "skipped":
|
|
741
|
+
return "skipped";
|
|
742
|
+
default:
|
|
743
|
+
return "failed";
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
getOutcome(test) {
|
|
747
|
+
if (test.state === "skipped" || test.state === "pending") return "skipped";
|
|
748
|
+
if (test.state === "passed") {
|
|
749
|
+
if (test.attempts.length > 1 && test.attempts.some((a) => a.state === "failed")) {
|
|
750
|
+
return "flaky";
|
|
751
|
+
}
|
|
752
|
+
return "expected";
|
|
753
|
+
}
|
|
754
|
+
return "unexpected";
|
|
755
|
+
}
|
|
756
|
+
extractErrors(test) {
|
|
757
|
+
const errors = [];
|
|
758
|
+
for (const attempt of test.attempts) {
|
|
759
|
+
if (attempt.error) {
|
|
760
|
+
errors.push({
|
|
761
|
+
message: attempt.error.message,
|
|
762
|
+
stack: attempt.error.stack
|
|
763
|
+
});
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
if (test.displayError) {
|
|
767
|
+
errors.push({ message: test.displayError });
|
|
768
|
+
}
|
|
769
|
+
return errors;
|
|
770
|
+
}
|
|
771
|
+
extractTags(title) {
|
|
772
|
+
const tagRegex = /@([a-zA-Z][a-zA-Z0-9_:-]*)/g;
|
|
773
|
+
const tags = [];
|
|
774
|
+
let match;
|
|
775
|
+
while ((match = tagRegex.exec(title)) !== null) {
|
|
776
|
+
tags.push(`@${match[1]}`);
|
|
777
|
+
}
|
|
778
|
+
return tags;
|
|
779
|
+
}
|
|
780
|
+
parseTestMetadata(tags) {
|
|
781
|
+
const metadata = {
|
|
782
|
+
isSlow: false,
|
|
783
|
+
isFlakyTagged: false,
|
|
784
|
+
customMetadata: {}
|
|
785
|
+
};
|
|
786
|
+
for (const tag of tags) {
|
|
787
|
+
const lower = tag.toLowerCase();
|
|
788
|
+
if (lower === "@slow") metadata.isSlow = true;
|
|
789
|
+
if (lower === "@flaky") metadata.isFlakyTagged = true;
|
|
790
|
+
const ownerMatch = tag.match(/@owner:([^\s@]+)/i);
|
|
791
|
+
if (ownerMatch) metadata.owner = ownerMatch[1];
|
|
792
|
+
const priorityMatch = tag.match(/@priority:(critical|high|medium|low)/i);
|
|
793
|
+
if (priorityMatch) metadata.priority = priorityMatch[1].toLowerCase();
|
|
794
|
+
const featureMatch = tag.match(/@feature:([^\s@]+)/i);
|
|
795
|
+
if (featureMatch) metadata.feature = featureMatch[1];
|
|
796
|
+
const typeMatch = tag.match(/@test_type:(smoke|e2e|regression|integration|unit)/i);
|
|
797
|
+
if (typeMatch) metadata.testType = typeMatch[1].toLowerCase();
|
|
798
|
+
}
|
|
799
|
+
return metadata;
|
|
800
|
+
}
|
|
801
|
+
buildAttachmentMeta(test, specResults) {
|
|
802
|
+
const attachments = [];
|
|
803
|
+
for (const screenshot of specResults.screenshots) {
|
|
804
|
+
if (screenshot.path) {
|
|
805
|
+
attachments.push({
|
|
806
|
+
name: screenshot.name || "screenshot",
|
|
807
|
+
filename: import_node_path.default.basename(screenshot.path),
|
|
808
|
+
contentType: "image/png",
|
|
809
|
+
sizeBytes: getFileSize(screenshot.path),
|
|
810
|
+
kind: "screenshot"
|
|
811
|
+
});
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
if (specResults.video) {
|
|
815
|
+
attachments.push({
|
|
816
|
+
name: "video",
|
|
817
|
+
filename: import_node_path.default.basename(specResults.video),
|
|
818
|
+
contentType: "video/mp4",
|
|
819
|
+
sizeBytes: getFileSize(specResults.video),
|
|
820
|
+
kind: "video"
|
|
821
|
+
});
|
|
822
|
+
}
|
|
823
|
+
return attachments;
|
|
824
|
+
}
|
|
825
|
+
async uploadAttachments(testId, testResultId, specResults, test) {
|
|
826
|
+
const attachments = [];
|
|
827
|
+
for (const screenshot of specResults.screenshots) {
|
|
828
|
+
if (screenshot.path && import_node_fs.default.existsSync(screenshot.path)) {
|
|
829
|
+
attachments.push({
|
|
830
|
+
path: screenshot.path,
|
|
831
|
+
meta: {
|
|
832
|
+
name: screenshot.name || "screenshot",
|
|
833
|
+
filename: import_node_path.default.basename(screenshot.path),
|
|
834
|
+
contentType: "image/png",
|
|
835
|
+
sizeBytes: getFileSize(screenshot.path),
|
|
836
|
+
kind: "screenshot"
|
|
837
|
+
}
|
|
838
|
+
});
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
if (specResults.video && import_node_fs.default.existsSync(specResults.video)) {
|
|
842
|
+
attachments.push({
|
|
843
|
+
path: specResults.video,
|
|
844
|
+
meta: {
|
|
845
|
+
name: "video",
|
|
846
|
+
filename: import_node_path.default.basename(specResults.video),
|
|
847
|
+
contentType: "video/mp4",
|
|
848
|
+
sizeBytes: getFileSize(specResults.video),
|
|
849
|
+
kind: "video"
|
|
850
|
+
}
|
|
851
|
+
});
|
|
852
|
+
}
|
|
853
|
+
if (attachments.length === 0) return;
|
|
854
|
+
try {
|
|
855
|
+
const { uploads } = await this.client.signAttachments(this.runId, {
|
|
856
|
+
testResultId,
|
|
857
|
+
attachments: attachments.map((a) => a.meta)
|
|
858
|
+
});
|
|
859
|
+
const uploadItems = uploads.map((u, i) => ({
|
|
860
|
+
signedUrl: u.signedUrl,
|
|
861
|
+
filePath: attachments[i].path,
|
|
862
|
+
contentType: attachments[i].meta.contentType
|
|
863
|
+
}));
|
|
864
|
+
await this.uploader.uploadBatch(uploadItems, uploads);
|
|
865
|
+
} catch (error) {
|
|
866
|
+
this.errorCollector.recordError("ATTACHMENT_SIGN", getErrorMessage(error), { error });
|
|
867
|
+
}
|
|
868
|
+
}
|
|
869
|
+
getEnvironmentInfo() {
|
|
870
|
+
return {
|
|
871
|
+
os: {
|
|
872
|
+
platform: import_node_os.default.platform(),
|
|
873
|
+
release: import_node_os.default.release(),
|
|
874
|
+
arch: import_node_os.default.arch()
|
|
875
|
+
},
|
|
876
|
+
node: {
|
|
877
|
+
version: process.version
|
|
878
|
+
},
|
|
879
|
+
machine: {
|
|
880
|
+
cpus: import_node_os.default.cpus().length,
|
|
881
|
+
memory: import_node_os.default.totalmem(),
|
|
882
|
+
hostname: import_node_os.default.hostname()
|
|
883
|
+
},
|
|
884
|
+
cypress: {
|
|
885
|
+
version: this.cypressVersion || "unknown"
|
|
886
|
+
},
|
|
887
|
+
ci: getCIInfo()
|
|
888
|
+
};
|
|
889
|
+
}
|
|
890
|
+
async getGitInfo() {
|
|
891
|
+
const ciGitInfo = {
|
|
892
|
+
branch: process.env.GITHUB_REF_NAME ?? process.env.CI_COMMIT_BRANCH ?? process.env.GIT_BRANCH,
|
|
893
|
+
commit: process.env.GITHUB_SHA ?? process.env.CI_COMMIT_SHA ?? process.env.GIT_COMMIT,
|
|
894
|
+
repo: process.env.GITHUB_REPOSITORY ?? process.env.CI_PROJECT_PATH,
|
|
895
|
+
author: process.env.GITHUB_ACTOR ?? process.env.GITLAB_USER_NAME
|
|
896
|
+
};
|
|
897
|
+
if (ciGitInfo.branch || ciGitInfo.commit) {
|
|
898
|
+
return ciGitInfo;
|
|
899
|
+
}
|
|
900
|
+
return await getLocalGitInfo(this.rootDir);
|
|
901
|
+
}
|
|
902
|
+
};
|
|
903
|
+
function supatestPlugin(on, config, options = {}) {
|
|
904
|
+
const reporter = new SupatestCypressReporter(options);
|
|
905
|
+
on("before:run", async (details) => {
|
|
906
|
+
await reporter.onBeforeRun(details);
|
|
907
|
+
});
|
|
908
|
+
on("after:spec", async (spec, results) => {
|
|
909
|
+
await reporter.onAfterSpec(spec, results);
|
|
910
|
+
});
|
|
911
|
+
on("after:run", async (results) => {
|
|
912
|
+
await reporter.onAfterRun(results);
|
|
913
|
+
});
|
|
914
|
+
}
|
|
915
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
916
|
+
0 && (module.exports = {
|
|
917
|
+
ErrorCollector
|
|
918
|
+
});
|
|
919
|
+
//# sourceMappingURL=index.cjs.map
|