@testdino/playwright 1.0.9 → 1.0.11
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/bin/tdpw.js +2 -2
- package/dist/cli/index.mjs +44 -21
- package/dist/cli/index.mjs.map +1 -1
- package/dist/index.d.mts +9 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +304 -162
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +204 -77
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/dist/cli/index.d.ts +0 -1
- package/dist/cli/index.js +0 -929
- package/dist/cli/index.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,37 +1,48 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
Object.defineProperty
|
|
4
|
-
|
|
5
|
-
var
|
|
6
|
-
var
|
|
7
|
-
var
|
|
8
|
-
var
|
|
9
|
-
var
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
var
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
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);
|
|
20
29
|
|
|
21
|
-
|
|
30
|
+
// src/index.ts
|
|
31
|
+
var src_exports = {};
|
|
32
|
+
__export(src_exports, {
|
|
33
|
+
coverageFixtures: () => coverageFixtures,
|
|
34
|
+
default: () => TestdinoReporter,
|
|
35
|
+
expect: () => import_test.expect,
|
|
36
|
+
test: () => test
|
|
37
|
+
});
|
|
38
|
+
module.exports = __toCommonJS(src_exports);
|
|
22
39
|
|
|
23
|
-
|
|
24
|
-
var
|
|
25
|
-
var
|
|
26
|
-
var picomatch__default = /*#__PURE__*/_interopDefault(picomatch);
|
|
27
|
-
var chalk__default = /*#__PURE__*/_interopDefault(chalk);
|
|
40
|
+
// src/reporter/index.ts
|
|
41
|
+
var import_crypto2 = require("crypto");
|
|
42
|
+
var import_fs2 = require("fs");
|
|
28
43
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
}) : x)(function(x) {
|
|
32
|
-
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
33
|
-
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
34
|
-
});
|
|
44
|
+
// src/streaming/websocket.ts
|
|
45
|
+
var import_ws = __toESM(require("ws"));
|
|
35
46
|
|
|
36
47
|
// src/reporter/errors.ts
|
|
37
48
|
var TestDinoServerError = class extends Error {
|
|
@@ -103,7 +114,7 @@ var WebSocketClient = class {
|
|
|
103
114
|
* Passes sessionId from HTTP auth so the server reuses the existing session.
|
|
104
115
|
*/
|
|
105
116
|
async connect() {
|
|
106
|
-
if (this.isConnecting || this.ws?.readyState ===
|
|
117
|
+
if (this.isConnecting || this.ws?.readyState === import_ws.default.OPEN) {
|
|
107
118
|
return;
|
|
108
119
|
}
|
|
109
120
|
this.isConnecting = true;
|
|
@@ -113,7 +124,7 @@ var WebSocketClient = class {
|
|
|
113
124
|
if (this.options.sessionId) {
|
|
114
125
|
wsUrl += `&sessionId=${this.options.sessionId}`;
|
|
115
126
|
}
|
|
116
|
-
this.ws = new
|
|
127
|
+
this.ws = new import_ws.default(wsUrl);
|
|
117
128
|
let serverReady = false;
|
|
118
129
|
const handshakeTimeout = setTimeout(() => {
|
|
119
130
|
if (!serverReady) {
|
|
@@ -177,7 +188,7 @@ var WebSocketClient = class {
|
|
|
177
188
|
* Send event through WebSocket
|
|
178
189
|
*/
|
|
179
190
|
async send(event) {
|
|
180
|
-
if (!this.ws || this.ws.readyState !==
|
|
191
|
+
if (!this.ws || this.ws.readyState !== import_ws.default.OPEN) {
|
|
181
192
|
throw new Error("WebSocket is not connected");
|
|
182
193
|
}
|
|
183
194
|
return new Promise((resolve, reject) => {
|
|
@@ -195,7 +206,7 @@ var WebSocketClient = class {
|
|
|
195
206
|
* Note: Uses Promise.all intentionally - if one send fails, connection is broken
|
|
196
207
|
*/
|
|
197
208
|
async sendBatch(events) {
|
|
198
|
-
if (!this.ws || this.ws.readyState !==
|
|
209
|
+
if (!this.ws || this.ws.readyState !== import_ws.default.OPEN) {
|
|
199
210
|
throw new Error("WebSocket is not connected");
|
|
200
211
|
}
|
|
201
212
|
await Promise.all(events.map((event) => this.send(event)));
|
|
@@ -205,7 +216,7 @@ var WebSocketClient = class {
|
|
|
205
216
|
* Use this for critical events like run:end where delivery must be confirmed
|
|
206
217
|
*/
|
|
207
218
|
async sendAndWaitForAck(event, timeout = this.ackTimeout) {
|
|
208
|
-
if (!this.ws || this.ws.readyState !==
|
|
219
|
+
if (!this.ws || this.ws.readyState !== import_ws.default.OPEN) {
|
|
209
220
|
throw new Error("WebSocket is not connected");
|
|
210
221
|
}
|
|
211
222
|
const sequence = event.sequence;
|
|
@@ -234,7 +245,7 @@ var WebSocketClient = class {
|
|
|
234
245
|
* Check if WebSocket is connected
|
|
235
246
|
*/
|
|
236
247
|
isConnected() {
|
|
237
|
-
return this.ws?.readyState ===
|
|
248
|
+
return this.ws?.readyState === import_ws.default.OPEN;
|
|
238
249
|
}
|
|
239
250
|
/**
|
|
240
251
|
* Close WebSocket connection
|
|
@@ -295,7 +306,11 @@ var WebSocketClient = class {
|
|
|
295
306
|
total: Number(details.total) || 0,
|
|
296
307
|
resetDate: details.resetDate?.toString(),
|
|
297
308
|
canPartialSubmit: Boolean(details.canPartialSubmit),
|
|
298
|
-
allowedCount: Number(details.allowedCount) || 0
|
|
309
|
+
allowedCount: Number(details.allowedCount) || 0,
|
|
310
|
+
projectName: details.projectName?.toString(),
|
|
311
|
+
projectLimit: details.projectLimit != null ? Number(details.projectLimit) : void 0,
|
|
312
|
+
projectUsed: details.projectUsed != null ? Number(details.projectUsed) : void 0,
|
|
313
|
+
projectBorrowed: details.projectBorrowed != null ? Number(details.projectBorrowed) : void 0
|
|
299
314
|
})
|
|
300
315
|
);
|
|
301
316
|
} else if (errorCode === "QUOTA_EXHAUSTED" && errorObj.details && typeof errorObj.details === "object") {
|
|
@@ -369,7 +384,7 @@ var WebSocketClient = class {
|
|
|
369
384
|
startPing() {
|
|
370
385
|
this.stopPing();
|
|
371
386
|
this.pingInterval = setInterval(() => {
|
|
372
|
-
if (this.ws?.readyState ===
|
|
387
|
+
if (this.ws?.readyState === import_ws.default.OPEN) {
|
|
373
388
|
this.ws.ping();
|
|
374
389
|
}
|
|
375
390
|
}, 3e4);
|
|
@@ -384,9 +399,15 @@ var WebSocketClient = class {
|
|
|
384
399
|
}
|
|
385
400
|
}
|
|
386
401
|
};
|
|
402
|
+
|
|
403
|
+
// src/streaming/http.ts
|
|
404
|
+
var import_axios = __toESM(require("axios"));
|
|
405
|
+
|
|
406
|
+
// src/utils/index.ts
|
|
407
|
+
var import_path = require("path");
|
|
387
408
|
function normalizePath(filePath, rootDir) {
|
|
388
409
|
if (rootDir && filePath.startsWith(rootDir)) {
|
|
389
|
-
return
|
|
410
|
+
return (0, import_path.relative)(rootDir, filePath);
|
|
390
411
|
}
|
|
391
412
|
return filePath;
|
|
392
413
|
}
|
|
@@ -407,7 +428,7 @@ var HttpClient = class {
|
|
|
407
428
|
retryDelay: 1e3,
|
|
408
429
|
...options
|
|
409
430
|
};
|
|
410
|
-
this.client =
|
|
431
|
+
this.client = import_axios.default.create({
|
|
411
432
|
baseURL: this.options.serverUrl,
|
|
412
433
|
headers: {
|
|
413
434
|
"Content-Type": "application/json",
|
|
@@ -424,7 +445,7 @@ var HttpClient = class {
|
|
|
424
445
|
const response = await this.client.post("/auth");
|
|
425
446
|
return response.data;
|
|
426
447
|
} catch (error) {
|
|
427
|
-
if (
|
|
448
|
+
if (import_axios.default.isAxiosError(error) && error.response?.status === 402) {
|
|
428
449
|
const quotaError = error.response.data;
|
|
429
450
|
throw new QuotaExhaustedError(quotaError.message, quotaError.details);
|
|
430
451
|
}
|
|
@@ -441,6 +462,14 @@ var HttpClient = class {
|
|
|
441
462
|
await this.client.post("/events", { events });
|
|
442
463
|
return;
|
|
443
464
|
} catch (error) {
|
|
465
|
+
if (import_axios.default.isAxiosError(error) && error.response?.status === 402) {
|
|
466
|
+
const data = error.response.data;
|
|
467
|
+
const details = data?.details;
|
|
468
|
+
if (data?.error === "QUOTA_EXCEEDED" && details) {
|
|
469
|
+
throw new QuotaExceededError(data.message || "Quota exceeded", details);
|
|
470
|
+
}
|
|
471
|
+
throw new QuotaExhaustedError(data?.message || "Quota exceeded", details);
|
|
472
|
+
}
|
|
444
473
|
lastError = new Error(this.getErrorMessage(error));
|
|
445
474
|
if (attempt < this.options.maxRetries - 1) {
|
|
446
475
|
const delay = this.options.retryDelay * Math.pow(2, attempt);
|
|
@@ -460,7 +489,7 @@ var HttpClient = class {
|
|
|
460
489
|
* Extract error message from various error types
|
|
461
490
|
*/
|
|
462
491
|
getErrorMessage(error) {
|
|
463
|
-
if (
|
|
492
|
+
if (import_axios.default.isAxiosError(error)) {
|
|
464
493
|
return error.response?.data?.message || error.message;
|
|
465
494
|
}
|
|
466
495
|
if (error instanceof Error) {
|
|
@@ -552,6 +581,9 @@ var EventBuffer = class {
|
|
|
552
581
|
}
|
|
553
582
|
};
|
|
554
583
|
|
|
584
|
+
// src/metadata/git.ts
|
|
585
|
+
var import_promises = require("fs/promises");
|
|
586
|
+
|
|
555
587
|
// src/metadata/base.ts
|
|
556
588
|
var BaseMetadataCollector = class {
|
|
557
589
|
name;
|
|
@@ -637,6 +669,16 @@ var BaseMetadataCollector = class {
|
|
|
637
669
|
};
|
|
638
670
|
|
|
639
671
|
// src/metadata/git.ts
|
|
672
|
+
var execaPromise = null;
|
|
673
|
+
async function getExeca() {
|
|
674
|
+
if (!execaPromise) {
|
|
675
|
+
execaPromise = import("execa").then((m) => m.execa).catch((error) => {
|
|
676
|
+
console.error("Failed to import execa:", error.message);
|
|
677
|
+
throw new Error("Failed to load execa. Ensure execa is installed: npm install execa");
|
|
678
|
+
});
|
|
679
|
+
}
|
|
680
|
+
return execaPromise;
|
|
681
|
+
}
|
|
640
682
|
var GitMetadataCollector = class extends BaseMetadataCollector {
|
|
641
683
|
options;
|
|
642
684
|
constructor(options = {}) {
|
|
@@ -729,7 +771,8 @@ var GitMetadataCollector = class extends BaseMetadataCollector {
|
|
|
729
771
|
const isCI = process.env.CI === "true" || process.env.GITHUB_ACTIONS === "true";
|
|
730
772
|
if (!isCI) return;
|
|
731
773
|
try {
|
|
732
|
-
|
|
774
|
+
const execa = await getExeca();
|
|
775
|
+
await execa("git", ["config", "--global", "--add", "safe.directory", this.options.cwd], {
|
|
733
776
|
timeout: this.options.timeout,
|
|
734
777
|
reject: true
|
|
735
778
|
});
|
|
@@ -744,7 +787,7 @@ var GitMetadataCollector = class extends BaseMetadataCollector {
|
|
|
744
787
|
if (!eventPath) return void 0;
|
|
745
788
|
try {
|
|
746
789
|
const content = await this.withTimeout(
|
|
747
|
-
|
|
790
|
+
(0, import_promises.readFile)(eventPath, "utf-8"),
|
|
748
791
|
this.options.timeout,
|
|
749
792
|
"GitHub event file read"
|
|
750
793
|
);
|
|
@@ -1028,8 +1071,9 @@ var GitMetadataCollector = class extends BaseMetadataCollector {
|
|
|
1028
1071
|
* Execute git command with timeout
|
|
1029
1072
|
*/
|
|
1030
1073
|
async execGit(args) {
|
|
1074
|
+
const execa = await getExeca();
|
|
1031
1075
|
const { stdout } = await this.withTimeout(
|
|
1032
|
-
execa
|
|
1076
|
+
execa("git", args, {
|
|
1033
1077
|
cwd: this.options.cwd,
|
|
1034
1078
|
timeout: this.options.timeout,
|
|
1035
1079
|
reject: true
|
|
@@ -1040,6 +1084,9 @@ var GitMetadataCollector = class extends BaseMetadataCollector {
|
|
|
1040
1084
|
return stdout.trim();
|
|
1041
1085
|
}
|
|
1042
1086
|
};
|
|
1087
|
+
|
|
1088
|
+
// src/metadata/ci.ts
|
|
1089
|
+
var import_os = require("os");
|
|
1043
1090
|
var CIMetadataCollector = class extends BaseMetadataCollector {
|
|
1044
1091
|
constructor(_options = {}) {
|
|
1045
1092
|
super("ci");
|
|
@@ -1117,9 +1164,9 @@ var CIMetadataCollector = class extends BaseMetadataCollector {
|
|
|
1117
1164
|
collectEnvironment() {
|
|
1118
1165
|
try {
|
|
1119
1166
|
return {
|
|
1120
|
-
name:
|
|
1167
|
+
name: (0, import_os.type)(),
|
|
1121
1168
|
type: process.platform,
|
|
1122
|
-
os: `${
|
|
1169
|
+
os: `${(0, import_os.type)()} ${(0, import_os.release)()}`,
|
|
1123
1170
|
node: process.version
|
|
1124
1171
|
};
|
|
1125
1172
|
} catch {
|
|
@@ -1127,6 +1174,10 @@ var CIMetadataCollector = class extends BaseMetadataCollector {
|
|
|
1127
1174
|
}
|
|
1128
1175
|
}
|
|
1129
1176
|
};
|
|
1177
|
+
|
|
1178
|
+
// src/metadata/system.ts
|
|
1179
|
+
var import_os2 = require("os");
|
|
1180
|
+
var import_process = require("process");
|
|
1130
1181
|
var SystemMetadataCollector = class extends BaseMetadataCollector {
|
|
1131
1182
|
constructor(_options = {}) {
|
|
1132
1183
|
super("system");
|
|
@@ -1161,11 +1212,11 @@ var SystemMetadataCollector = class extends BaseMetadataCollector {
|
|
|
1161
1212
|
let platformName = "unknown";
|
|
1162
1213
|
let releaseVersion = "unknown";
|
|
1163
1214
|
try {
|
|
1164
|
-
platformName =
|
|
1215
|
+
platformName = (0, import_os2.platform)();
|
|
1165
1216
|
} catch {
|
|
1166
1217
|
}
|
|
1167
1218
|
try {
|
|
1168
|
-
releaseVersion =
|
|
1219
|
+
releaseVersion = (0, import_os2.release)();
|
|
1169
1220
|
} catch {
|
|
1170
1221
|
}
|
|
1171
1222
|
return `${platformName} ${releaseVersion}`;
|
|
@@ -1176,7 +1227,7 @@ var SystemMetadataCollector = class extends BaseMetadataCollector {
|
|
|
1176
1227
|
*/
|
|
1177
1228
|
getCpuInfo() {
|
|
1178
1229
|
try {
|
|
1179
|
-
const cpuList =
|
|
1230
|
+
const cpuList = (0, import_os2.cpus)();
|
|
1180
1231
|
if (cpuList.length === 0) {
|
|
1181
1232
|
return "unknown";
|
|
1182
1233
|
}
|
|
@@ -1193,7 +1244,7 @@ var SystemMetadataCollector = class extends BaseMetadataCollector {
|
|
|
1193
1244
|
*/
|
|
1194
1245
|
getMemoryInfo() {
|
|
1195
1246
|
try {
|
|
1196
|
-
const totalBytes =
|
|
1247
|
+
const totalBytes = (0, import_os2.totalmem)();
|
|
1197
1248
|
const totalGB = totalBytes / (1024 * 1024 * 1024);
|
|
1198
1249
|
return `${totalGB.toFixed(1)} GB`;
|
|
1199
1250
|
} catch {
|
|
@@ -1206,7 +1257,7 @@ var SystemMetadataCollector = class extends BaseMetadataCollector {
|
|
|
1206
1257
|
*/
|
|
1207
1258
|
getNodeVersion() {
|
|
1208
1259
|
try {
|
|
1209
|
-
return
|
|
1260
|
+
return import_process.version;
|
|
1210
1261
|
} catch {
|
|
1211
1262
|
return "unknown";
|
|
1212
1263
|
}
|
|
@@ -1217,7 +1268,7 @@ var SystemMetadataCollector = class extends BaseMetadataCollector {
|
|
|
1217
1268
|
*/
|
|
1218
1269
|
getPlatform() {
|
|
1219
1270
|
try {
|
|
1220
|
-
return
|
|
1271
|
+
return (0, import_os2.platform)();
|
|
1221
1272
|
} catch {
|
|
1222
1273
|
return "unknown";
|
|
1223
1274
|
}
|
|
@@ -1228,12 +1279,15 @@ var SystemMetadataCollector = class extends BaseMetadataCollector {
|
|
|
1228
1279
|
*/
|
|
1229
1280
|
getHostname() {
|
|
1230
1281
|
try {
|
|
1231
|
-
return
|
|
1282
|
+
return (0, import_os2.hostname)();
|
|
1232
1283
|
} catch {
|
|
1233
1284
|
return "unknown";
|
|
1234
1285
|
}
|
|
1235
1286
|
}
|
|
1236
1287
|
};
|
|
1288
|
+
|
|
1289
|
+
// src/metadata/playwright.ts
|
|
1290
|
+
var import_promises2 = require("fs/promises");
|
|
1237
1291
|
var PlaywrightMetadataCollector = class extends BaseMetadataCollector {
|
|
1238
1292
|
options;
|
|
1239
1293
|
constructor(options = {}) {
|
|
@@ -1283,7 +1337,7 @@ var PlaywrightMetadataCollector = class extends BaseMetadataCollector {
|
|
|
1283
1337
|
return void 0;
|
|
1284
1338
|
}
|
|
1285
1339
|
const packageJsonContent = await this.withTimeout(
|
|
1286
|
-
|
|
1340
|
+
(0, import_promises2.readFile)(packageJsonPath, "utf-8"),
|
|
1287
1341
|
this.options.timeout,
|
|
1288
1342
|
"Playwright package.json read"
|
|
1289
1343
|
);
|
|
@@ -1302,7 +1356,7 @@ var PlaywrightMetadataCollector = class extends BaseMetadataCollector {
|
|
|
1302
1356
|
*/
|
|
1303
1357
|
resolvePlaywrightPackageJson() {
|
|
1304
1358
|
try {
|
|
1305
|
-
return
|
|
1359
|
+
return require.resolve("@playwright/test/package.json");
|
|
1306
1360
|
} catch {
|
|
1307
1361
|
return void 0;
|
|
1308
1362
|
}
|
|
@@ -1706,6 +1760,9 @@ function createMetadataCollector(playwrightConfig, playwrightSuite) {
|
|
|
1706
1760
|
);
|
|
1707
1761
|
return aggregator;
|
|
1708
1762
|
}
|
|
1763
|
+
|
|
1764
|
+
// src/uploads/sas-token-client.ts
|
|
1765
|
+
var import_axios2 = __toESM(require("axios"));
|
|
1709
1766
|
var SASTokenClient = class {
|
|
1710
1767
|
client;
|
|
1711
1768
|
options;
|
|
@@ -1715,7 +1772,7 @@ var SASTokenClient = class {
|
|
|
1715
1772
|
retryDelay: 1e3,
|
|
1716
1773
|
...options
|
|
1717
1774
|
};
|
|
1718
|
-
this.client =
|
|
1775
|
+
this.client = import_axios2.default.create({
|
|
1719
1776
|
baseURL: this.options.serverUrl,
|
|
1720
1777
|
headers: {
|
|
1721
1778
|
"Content-Type": "application/json",
|
|
@@ -1757,7 +1814,7 @@ var SASTokenClient = class {
|
|
|
1757
1814
|
* Extract error message from various error types
|
|
1758
1815
|
*/
|
|
1759
1816
|
getErrorMessage(error) {
|
|
1760
|
-
if (
|
|
1817
|
+
if (import_axios2.default.isAxiosError(error)) {
|
|
1761
1818
|
if (error.response?.status === 401) {
|
|
1762
1819
|
return "Invalid API key for artifact uploads";
|
|
1763
1820
|
}
|
|
@@ -1775,6 +1832,11 @@ var SASTokenClient = class {
|
|
|
1775
1832
|
return String(error);
|
|
1776
1833
|
}
|
|
1777
1834
|
};
|
|
1835
|
+
|
|
1836
|
+
// src/uploads/artifact-uploader.ts
|
|
1837
|
+
var import_fs = require("fs");
|
|
1838
|
+
var import_path2 = require("path");
|
|
1839
|
+
var import_axios3 = __toESM(require("axios"));
|
|
1778
1840
|
var ArtifactUploader = class {
|
|
1779
1841
|
sasToken;
|
|
1780
1842
|
options;
|
|
@@ -1795,9 +1857,9 @@ var ArtifactUploader = class {
|
|
|
1795
1857
|
*/
|
|
1796
1858
|
async uploadFile(attachment, testId) {
|
|
1797
1859
|
const startTime = Date.now();
|
|
1798
|
-
const fileName =
|
|
1860
|
+
const fileName = (0, import_path2.basename)(attachment.path);
|
|
1799
1861
|
try {
|
|
1800
|
-
const stats =
|
|
1862
|
+
const stats = (0, import_fs.statSync)(attachment.path);
|
|
1801
1863
|
const fileSize = stats.size;
|
|
1802
1864
|
if (fileSize > this.sasToken.maxSize) {
|
|
1803
1865
|
return {
|
|
@@ -1808,7 +1870,7 @@ var ArtifactUploader = class {
|
|
|
1808
1870
|
duration: Date.now() - startTime
|
|
1809
1871
|
};
|
|
1810
1872
|
}
|
|
1811
|
-
const extension =
|
|
1873
|
+
const extension = (0, import_path2.extname)(fileName).slice(1).toLowerCase();
|
|
1812
1874
|
const allowedTypes = this.sasToken.allowedFileTypes;
|
|
1813
1875
|
if (allowedTypes.length > 0 && !allowedTypes.includes(extension)) {
|
|
1814
1876
|
return {
|
|
@@ -1918,9 +1980,9 @@ var ArtifactUploader = class {
|
|
|
1918
1980
|
* Perform actual file upload to Azure
|
|
1919
1981
|
*/
|
|
1920
1982
|
async doUpload(filePath, uploadUrl, contentType) {
|
|
1921
|
-
const fileStream =
|
|
1922
|
-
const stats =
|
|
1923
|
-
await
|
|
1983
|
+
const fileStream = (0, import_fs.createReadStream)(filePath);
|
|
1984
|
+
const stats = (0, import_fs.statSync)(filePath);
|
|
1985
|
+
await import_axios3.default.put(uploadUrl, fileStream, {
|
|
1924
1986
|
headers: {
|
|
1925
1987
|
"Content-Type": contentType,
|
|
1926
1988
|
"Content-Length": stats.size,
|
|
@@ -1946,6 +2008,10 @@ var ArtifactUploader = class {
|
|
|
1946
2008
|
return this.sasToken.uniqueId;
|
|
1947
2009
|
}
|
|
1948
2010
|
};
|
|
2011
|
+
|
|
2012
|
+
// src/code-coverage/merger.ts
|
|
2013
|
+
var import_istanbul_lib_coverage = __toESM(require("istanbul-lib-coverage"));
|
|
2014
|
+
var import_picomatch = __toESM(require("picomatch"));
|
|
1949
2015
|
function toIstanbulMapData(map) {
|
|
1950
2016
|
return map;
|
|
1951
2017
|
}
|
|
@@ -1953,7 +2019,7 @@ function fromIstanbulMapData(data) {
|
|
|
1953
2019
|
return data;
|
|
1954
2020
|
}
|
|
1955
2021
|
var CoverageMerger = class {
|
|
1956
|
-
coverageMap =
|
|
2022
|
+
coverageMap = import_istanbul_lib_coverage.default.createCoverageMap({});
|
|
1957
2023
|
hasData = false;
|
|
1958
2024
|
includePatterns;
|
|
1959
2025
|
excludePatterns;
|
|
@@ -1991,8 +2057,8 @@ var CoverageMerger = class {
|
|
|
1991
2057
|
const hasInclude = this.includePatterns && this.includePatterns.length > 0;
|
|
1992
2058
|
const hasExclude = this.excludePatterns && this.excludePatterns.length > 0;
|
|
1993
2059
|
if (!hasInclude && !hasExclude) return coverageMap;
|
|
1994
|
-
const isExcluded = hasExclude ?
|
|
1995
|
-
const isIncluded = hasInclude ?
|
|
2060
|
+
const isExcluded = hasExclude ? (0, import_picomatch.default)(this.excludePatterns) : void 0;
|
|
2061
|
+
const isIncluded = hasInclude ? (0, import_picomatch.default)(this.includePatterns) : void 0;
|
|
1996
2062
|
const filtered = {};
|
|
1997
2063
|
for (const [filePath, fileCoverage] of Object.entries(coverageMap)) {
|
|
1998
2064
|
if (isExcluded && isExcluded(filePath)) {
|
|
@@ -2062,6 +2128,9 @@ function extractMetric(metric) {
|
|
|
2062
2128
|
pct: metric.pct
|
|
2063
2129
|
};
|
|
2064
2130
|
}
|
|
2131
|
+
|
|
2132
|
+
// src/code-coverage/compact.ts
|
|
2133
|
+
var import_crypto = require("crypto");
|
|
2065
2134
|
function extractCompactCounts(coverageMapJSON, gitRoot) {
|
|
2066
2135
|
const files = {};
|
|
2067
2136
|
let fileCount = 0;
|
|
@@ -2097,14 +2166,19 @@ function computeShapeHash(fileCoverage) {
|
|
|
2097
2166
|
b: countBranchPaths(branchMap),
|
|
2098
2167
|
bp: Object.values(branchMap).map((b) => (b.locations || []).length)
|
|
2099
2168
|
};
|
|
2100
|
-
return
|
|
2169
|
+
return (0, import_crypto.createHash)("sha256").update(JSON.stringify(shape)).digest("hex").slice(0, 12);
|
|
2101
2170
|
}
|
|
2171
|
+
|
|
2172
|
+
// src/code-coverage/html-report.ts
|
|
2173
|
+
var import_istanbul_lib_report = require("istanbul-lib-report");
|
|
2174
|
+
var import_istanbul_reports = require("istanbul-reports");
|
|
2175
|
+
var import_promises3 = require("fs/promises");
|
|
2102
2176
|
async function generateIstanbulHtmlReport(coverageMerger, options) {
|
|
2103
|
-
await
|
|
2177
|
+
await (0, import_promises3.rm)(options.outputDir, { recursive: true, force: true }).catch(() => {
|
|
2104
2178
|
});
|
|
2105
|
-
await
|
|
2179
|
+
await (0, import_promises3.mkdir)(options.outputDir, { recursive: true });
|
|
2106
2180
|
const coverageMap = coverageMerger.getRawCoverageMap();
|
|
2107
|
-
const context =
|
|
2181
|
+
const context = (0, import_istanbul_lib_report.createContext)({
|
|
2108
2182
|
dir: options.outputDir,
|
|
2109
2183
|
watermarks: {
|
|
2110
2184
|
statements: [50, 80],
|
|
@@ -2114,13 +2188,16 @@ async function generateIstanbulHtmlReport(coverageMerger, options) {
|
|
|
2114
2188
|
},
|
|
2115
2189
|
coverageMap
|
|
2116
2190
|
});
|
|
2117
|
-
const reporter =
|
|
2191
|
+
const reporter = (0, import_istanbul_reports.create)("html", {
|
|
2118
2192
|
skipEmpty: false,
|
|
2119
2193
|
subdir: ""
|
|
2120
2194
|
});
|
|
2121
2195
|
reporter.execute(context);
|
|
2122
2196
|
return `${options.outputDir}/index.html`;
|
|
2123
2197
|
}
|
|
2198
|
+
|
|
2199
|
+
// src/code-coverage/fixtures.ts
|
|
2200
|
+
var import_test = require("@playwright/test");
|
|
2124
2201
|
var COVERAGE_EXTRACT_TIMEOUT_MS = 3e4;
|
|
2125
2202
|
async function extractCoverageFromPage(page, timeoutMs = COVERAGE_EXTRACT_TIMEOUT_MS) {
|
|
2126
2203
|
return Promise.race([
|
|
@@ -2149,9 +2226,30 @@ var coverageFixtures = {
|
|
|
2149
2226
|
{ auto: true }
|
|
2150
2227
|
]
|
|
2151
2228
|
};
|
|
2152
|
-
var test =
|
|
2229
|
+
var test = import_test.test.extend(
|
|
2153
2230
|
coverageFixtures
|
|
2154
2231
|
);
|
|
2232
|
+
|
|
2233
|
+
// src/reporter/log.ts
|
|
2234
|
+
var chalkPromise = null;
|
|
2235
|
+
async function getChalk() {
|
|
2236
|
+
if (!chalkPromise) {
|
|
2237
|
+
chalkPromise = import("chalk").then((m) => m.default).catch((error) => {
|
|
2238
|
+
console.warn("\u26A0\uFE0F TestDino: Failed to load chalk, using plain text output");
|
|
2239
|
+
console.debug("Chalk import error:", error.message);
|
|
2240
|
+
return {
|
|
2241
|
+
green: (s) => s,
|
|
2242
|
+
yellow: (s) => s,
|
|
2243
|
+
red: (s) => s,
|
|
2244
|
+
blue: (s) => s,
|
|
2245
|
+
cyan: (s) => s,
|
|
2246
|
+
dim: (s) => s,
|
|
2247
|
+
bold: (s) => s
|
|
2248
|
+
};
|
|
2249
|
+
});
|
|
2250
|
+
}
|
|
2251
|
+
return chalkPromise;
|
|
2252
|
+
}
|
|
2155
2253
|
function stripAnsi(str) {
|
|
2156
2254
|
return str.replace(/\u001b\[[0-9;]*m/g, "");
|
|
2157
2255
|
}
|
|
@@ -2180,20 +2278,20 @@ function shortenPath(filePath) {
|
|
|
2180
2278
|
const parts = filePath.split("/");
|
|
2181
2279
|
return parts.length > 2 ? parts.slice(-2).join("/") : filePath;
|
|
2182
2280
|
}
|
|
2183
|
-
function colorPct(pct) {
|
|
2184
|
-
const color = pct >= 80 ?
|
|
2281
|
+
function colorPct(pct, chalk) {
|
|
2282
|
+
const color = pct >= 80 ? chalk.green : pct >= 50 ? chalk.yellow : chalk.red;
|
|
2185
2283
|
return color(pct === 100 ? "100%" : `${pct.toFixed(1)}%`);
|
|
2186
2284
|
}
|
|
2187
|
-
function
|
|
2285
|
+
function printCoverageTableWithChalk(event, chalk) {
|
|
2188
2286
|
const { summary, files } = event;
|
|
2189
|
-
const row = (content) => ` ${
|
|
2287
|
+
const row = (content) => ` ${chalk.dim("\u2502")} ${content}`;
|
|
2190
2288
|
const nameW = 30;
|
|
2191
2289
|
const colW = 10;
|
|
2192
|
-
console.log(row(`${
|
|
2290
|
+
console.log(row(`${chalk.bold("Coverage")} ${chalk.dim(`${files.length} files`)}`));
|
|
2193
2291
|
console.log(row(""));
|
|
2194
2292
|
console.log(
|
|
2195
2293
|
row(
|
|
2196
|
-
` ${
|
|
2294
|
+
` ${chalk.dim(`${pad("File", nameW)}${padStart("Stmts", colW)}${padStart("Branch", colW)}${padStart("Funcs", colW)}${padStart("Lines", colW)}`)}`
|
|
2197
2295
|
)
|
|
2198
2296
|
);
|
|
2199
2297
|
for (const file of files) {
|
|
@@ -2201,67 +2299,67 @@ function printCoverageTable(event) {
|
|
|
2201
2299
|
const short = name.length > nameW ? name.slice(0, nameW - 1) + "~" : name;
|
|
2202
2300
|
console.log(
|
|
2203
2301
|
row(
|
|
2204
|
-
` ${pad(short, nameW)}` + padStart(colorPct(file.statements.pct), colW) + padStart(colorPct(file.branches.pct), colW) + padStart(colorPct(file.functions.pct), colW) + padStart(colorPct(file.lines.pct), colW)
|
|
2302
|
+
` ${pad(short, nameW)}` + padStart(colorPct(file.statements.pct, chalk), colW) + padStart(colorPct(file.branches.pct, chalk), colW) + padStart(colorPct(file.functions.pct, chalk), colW) + padStart(colorPct(file.lines.pct, chalk), colW)
|
|
2205
2303
|
)
|
|
2206
2304
|
);
|
|
2207
2305
|
}
|
|
2208
|
-
console.log(row(` ${
|
|
2306
|
+
console.log(row(` ${chalk.dim("\u2500".repeat(nameW + colW * 4))}`));
|
|
2209
2307
|
console.log(
|
|
2210
2308
|
row(
|
|
2211
|
-
` ${
|
|
2309
|
+
` ${chalk.bold(pad("All files", nameW))}` + padStart(colorPct(summary.statements.pct, chalk), colW) + padStart(colorPct(summary.branches.pct, chalk), colW) + padStart(colorPct(summary.functions.pct, chalk), colW) + padStart(colorPct(summary.lines.pct, chalk), colW)
|
|
2212
2310
|
)
|
|
2213
2311
|
);
|
|
2214
2312
|
}
|
|
2215
|
-
function
|
|
2313
|
+
function printRunSummaryWithChalk(result, streamingSuccess, data, chalk) {
|
|
2216
2314
|
const W = 72;
|
|
2217
|
-
const topBorder = ` ${
|
|
2218
|
-
const bottomBorder = ` ${
|
|
2219
|
-
const divider = ` ${
|
|
2220
|
-
const row = (content) => ` ${
|
|
2315
|
+
const topBorder = ` ${chalk.dim(`\u250C${"\u2500".repeat(W)}\u2510`)}`;
|
|
2316
|
+
const bottomBorder = ` ${chalk.dim(`\u2514${"\u2500".repeat(W)}\u2518`)}`;
|
|
2317
|
+
const divider = ` ${chalk.dim(`\u251C${"\u2500".repeat(W)}\u2524`)}`;
|
|
2318
|
+
const row = (content) => ` ${chalk.dim("\u2502")} ${content}`;
|
|
2221
2319
|
console.log("");
|
|
2222
2320
|
console.log(topBorder);
|
|
2223
|
-
console.log(row(
|
|
2321
|
+
console.log(row(chalk.bold("TestDino Run Summary")));
|
|
2224
2322
|
console.log(divider);
|
|
2225
|
-
console.log(row(`${
|
|
2323
|
+
console.log(row(`${chalk.dim("Run")} ${data.runId}`));
|
|
2226
2324
|
const git = data.runMetadata?.git;
|
|
2227
2325
|
if (git?.branch || git?.commit?.hash) {
|
|
2228
|
-
const branch = git.branch ?
|
|
2229
|
-
const sha = git.commit?.hash ?
|
|
2230
|
-
const sep = branch && sha ? ` ${
|
|
2231
|
-
const msg = git.commit?.message ? ` ${
|
|
2232
|
-
console.log(row(`${
|
|
2326
|
+
const branch = git.branch ? chalk.cyan(git.branch) : "";
|
|
2327
|
+
const sha = git.commit?.hash ? chalk.dim(git.commit.hash.slice(0, 7)) : "";
|
|
2328
|
+
const sep = branch && sha ? ` ${chalk.dim("@")} ` : "";
|
|
2329
|
+
const msg = git.commit?.message ? ` ${chalk.dim(git.commit.message.split("\n")[0].slice(0, 50))}` : "";
|
|
2330
|
+
console.log(row(`${chalk.dim("Git")} ${branch}${sep}${sha}${msg}`));
|
|
2233
2331
|
}
|
|
2234
2332
|
console.log(divider);
|
|
2235
|
-
const statusColor = result.status === "passed" ?
|
|
2333
|
+
const statusColor = result.status === "passed" ? chalk.green : result.status === "failed" ? chalk.red : chalk.yellow;
|
|
2236
2334
|
const statusLabel = result.status === "timedout" ? "timed out" : result.status;
|
|
2237
2335
|
console.log(
|
|
2238
2336
|
row(
|
|
2239
|
-
`${
|
|
2337
|
+
`${chalk.bold("Tests")} ${statusColor(statusLabel.toUpperCase())} ${chalk.dim(formatDuration(result.duration))}`
|
|
2240
2338
|
)
|
|
2241
2339
|
);
|
|
2242
2340
|
const counts = [];
|
|
2243
|
-
if (data.testCounts.passed > 0) counts.push(
|
|
2244
|
-
if (data.testCounts.failed > 0) counts.push(
|
|
2245
|
-
if (data.testCounts.flaky > 0) counts.push(
|
|
2246
|
-
if (data.testCounts.skipped > 0) counts.push(
|
|
2247
|
-
if (data.testCounts.timedOut > 0) counts.push(
|
|
2248
|
-
if (data.testCounts.interrupted > 0) counts.push(
|
|
2249
|
-
const retriedStr = data.testCounts.retried > 0 ? ` ${
|
|
2250
|
-
console.log(row(` ${counts.join(
|
|
2341
|
+
if (data.testCounts.passed > 0) counts.push(chalk.green(`${data.testCounts.passed} passed`));
|
|
2342
|
+
if (data.testCounts.failed > 0) counts.push(chalk.red(`${data.testCounts.failed} failed`));
|
|
2343
|
+
if (data.testCounts.flaky > 0) counts.push(chalk.yellow(`${data.testCounts.flaky} flaky`));
|
|
2344
|
+
if (data.testCounts.skipped > 0) counts.push(chalk.dim(`${data.testCounts.skipped} skipped`));
|
|
2345
|
+
if (data.testCounts.timedOut > 0) counts.push(chalk.red(`${data.testCounts.timedOut} timed out`));
|
|
2346
|
+
if (data.testCounts.interrupted > 0) counts.push(chalk.yellow(`${data.testCounts.interrupted} interrupted`));
|
|
2347
|
+
const retriedStr = data.testCounts.retried > 0 ? ` ${chalk.dim(`(${data.testCounts.retried} retries)`)}` : "";
|
|
2348
|
+
console.log(row(` ${counts.join(chalk.dim(" \xB7 "))} ${chalk.dim(`of ${data.totalTests}`)}${retriedStr}`));
|
|
2251
2349
|
console.log(divider);
|
|
2252
2350
|
const shardStr = data.shardInfo ? `${data.shardInfo.current}/${data.shardInfo.total}` : "\u2014";
|
|
2253
2351
|
console.log(
|
|
2254
2352
|
row(
|
|
2255
|
-
`${pad(`${
|
|
2353
|
+
`${pad(`${chalk.dim("Workers")} ${data.workerCount > 0 ? data.workerCount : "\u2014"}`, 28)}${pad(`${chalk.dim("Shard")} ${shardStr}`, 28)}${chalk.dim("Projects")} ${data.projectNames.size > 0 ? Array.from(data.projectNames).join(", ") : "\u2014"}`
|
|
2256
2354
|
)
|
|
2257
2355
|
);
|
|
2258
2356
|
console.log(divider);
|
|
2259
2357
|
const transport = data.useHttpFallback ? "HTTP" : "WebSocket";
|
|
2260
|
-
const streamIcon = streamingSuccess ?
|
|
2261
|
-
console.log(row(`${
|
|
2358
|
+
const streamIcon = streamingSuccess ? chalk.green("sent") : chalk.red("failed");
|
|
2359
|
+
console.log(row(`${chalk.bold("Stream")} ${streamIcon} ${chalk.dim(`via ${transport}`)}`));
|
|
2262
2360
|
if (data.lastCoverageEvent) {
|
|
2263
2361
|
console.log(divider);
|
|
2264
|
-
|
|
2362
|
+
printCoverageTableWithChalk(data.lastCoverageEvent, chalk);
|
|
2265
2363
|
}
|
|
2266
2364
|
console.log(bottomBorder);
|
|
2267
2365
|
console.log("");
|
|
@@ -2276,8 +2374,14 @@ var createReporterLog = (options) => ({
|
|
|
2276
2374
|
console.log(`\u{1F50D} TestDino: ${msg}`);
|
|
2277
2375
|
}
|
|
2278
2376
|
},
|
|
2279
|
-
printRunSummary,
|
|
2280
|
-
|
|
2377
|
+
printRunSummary: async (result, streamingSuccess, data) => {
|
|
2378
|
+
const chalk = await getChalk();
|
|
2379
|
+
printRunSummaryWithChalk(result, streamingSuccess, data, chalk);
|
|
2380
|
+
},
|
|
2381
|
+
printCoverageTable: async (event) => {
|
|
2382
|
+
const chalk = await getChalk();
|
|
2383
|
+
printCoverageTableWithChalk(event, chalk);
|
|
2384
|
+
}
|
|
2281
2385
|
});
|
|
2282
2386
|
|
|
2283
2387
|
// src/reporter/index.ts
|
|
@@ -2301,6 +2405,7 @@ var TestdinoReporter = class {
|
|
|
2301
2405
|
isShuttingDown = false;
|
|
2302
2406
|
// Quota tracking
|
|
2303
2407
|
quotaExceeded = false;
|
|
2408
|
+
pendingQuotaError = null;
|
|
2304
2409
|
// Session ID from HTTP auth, passed to WebSocket for session reuse
|
|
2305
2410
|
sessionId = null;
|
|
2306
2411
|
// Artifact upload
|
|
@@ -2330,7 +2435,7 @@ var TestdinoReporter = class {
|
|
|
2330
2435
|
constructor(config = {}) {
|
|
2331
2436
|
const cliConfig = this.loadCliConfig();
|
|
2332
2437
|
this.config = { ...config, ...cliConfig };
|
|
2333
|
-
this.runId =
|
|
2438
|
+
this.runId = (0, import_crypto2.randomUUID)();
|
|
2334
2439
|
this.log = createReporterLog({ debug: this.config.debug ?? false });
|
|
2335
2440
|
this.coverageEnabled = this.config.coverage?.enabled ?? false;
|
|
2336
2441
|
if (this.coverageEnabled) {
|
|
@@ -2360,10 +2465,10 @@ var TestdinoReporter = class {
|
|
|
2360
2465
|
return {};
|
|
2361
2466
|
}
|
|
2362
2467
|
try {
|
|
2363
|
-
if (!
|
|
2468
|
+
if (!(0, import_fs2.existsSync)(cliConfigPath)) {
|
|
2364
2469
|
return {};
|
|
2365
2470
|
}
|
|
2366
|
-
const configContent =
|
|
2471
|
+
const configContent = (0, import_fs2.readFileSync)(cliConfigPath, "utf-8");
|
|
2367
2472
|
const cliConfig = JSON.parse(configContent);
|
|
2368
2473
|
const mappedConfig = {};
|
|
2369
2474
|
if (cliConfig.token !== void 0 && typeof cliConfig.token === "string") {
|
|
@@ -2463,7 +2568,7 @@ var TestdinoReporter = class {
|
|
|
2463
2568
|
if (!this.quotaExceeded) {
|
|
2464
2569
|
this.quotaExceeded = true;
|
|
2465
2570
|
this.initFailed = true;
|
|
2466
|
-
this.
|
|
2571
|
+
this.pendingQuotaError = error;
|
|
2467
2572
|
}
|
|
2468
2573
|
} else {
|
|
2469
2574
|
this.log.error(`WebSocket error: ${error.message}`);
|
|
@@ -2503,7 +2608,7 @@ var TestdinoReporter = class {
|
|
|
2503
2608
|
this.initFailed = true;
|
|
2504
2609
|
if (error instanceof Error && "code" in error && isQuotaError(error)) {
|
|
2505
2610
|
this.quotaExceeded = true;
|
|
2506
|
-
this.
|
|
2611
|
+
this.pendingQuotaError = error;
|
|
2507
2612
|
} else if (errorMessage.includes("Authentication failed") || errorMessage.includes("401") || errorMessage.includes("Unauthorized")) {
|
|
2508
2613
|
this.printConfigurationError("Authentication failed - Invalid or expired token", [
|
|
2509
2614
|
"Verify your token is correct",
|
|
@@ -2727,6 +2832,9 @@ var TestdinoReporter = class {
|
|
|
2727
2832
|
if (this.pendingTestEndPromises.size > 0) {
|
|
2728
2833
|
await Promise.allSettled(Array.from(this.pendingTestEndPromises));
|
|
2729
2834
|
}
|
|
2835
|
+
if (this.pendingQuotaError) {
|
|
2836
|
+
this.printQuotaError(this.pendingQuotaError);
|
|
2837
|
+
}
|
|
2730
2838
|
this.log.success("Tests completed (quota limit reached; not streamed to TestDino)");
|
|
2731
2839
|
this.wsClient?.close();
|
|
2732
2840
|
this.removeSignalHandlers();
|
|
@@ -2815,8 +2923,15 @@ var TestdinoReporter = class {
|
|
|
2815
2923
|
this.log.success("All events sent (HTTP fallback)");
|
|
2816
2924
|
delivered = true;
|
|
2817
2925
|
} catch (httpError) {
|
|
2818
|
-
const
|
|
2819
|
-
|
|
2926
|
+
const isQuota = isQuotaError(httpError) || httpError instanceof Error && "code" in httpError && (httpError.code === "QUOTA_EXCEEDED" || httpError.code === "QUOTA_EXHAUSTED");
|
|
2927
|
+
if (isQuota) {
|
|
2928
|
+
this.quotaExceeded = true;
|
|
2929
|
+
this.pendingQuotaError = httpError;
|
|
2930
|
+
this.printQuotaError(httpError);
|
|
2931
|
+
} else {
|
|
2932
|
+
const httpErrorMessage = httpError instanceof Error ? httpError.message : String(httpError);
|
|
2933
|
+
this.log.error(`HTTP fallback also failed: ${httpErrorMessage}`);
|
|
2934
|
+
}
|
|
2820
2935
|
}
|
|
2821
2936
|
} else if (!delivered) {
|
|
2822
2937
|
this.log.warn("Server did not acknowledge run:end in time, events may be pending");
|
|
@@ -2836,7 +2951,7 @@ var TestdinoReporter = class {
|
|
|
2836
2951
|
useHttpFallback: this.useHttpFallback,
|
|
2837
2952
|
lastCoverageEvent: this.lastCoverageEvent
|
|
2838
2953
|
};
|
|
2839
|
-
this.log.printRunSummary(result, delivered, summaryData);
|
|
2954
|
+
await this.log.printRunSummary(result, delivered, summaryData);
|
|
2840
2955
|
if (this.coverageThresholdFailed && this.lastCoverageEvent) {
|
|
2841
2956
|
const failures = this.checkCoverageThresholds(this.lastCoverageEvent.summary);
|
|
2842
2957
|
this.log.error("Coverage thresholds not met:");
|
|
@@ -3123,45 +3238,75 @@ var TestdinoReporter = class {
|
|
|
3123
3238
|
const details = errorData.details;
|
|
3124
3239
|
const planName = details?.planName || "Unknown";
|
|
3125
3240
|
const resetDate = details?.resetDate;
|
|
3126
|
-
|
|
3127
|
-
|
|
3128
|
-
|
|
3129
|
-
|
|
3130
|
-
|
|
3131
|
-
|
|
3132
|
-
|
|
3133
|
-
|
|
3134
|
-
Used: ${details.used || "Unknown"} executions`;
|
|
3135
|
-
if (resetDate) {
|
|
3136
|
-
message += `
|
|
3137
|
-
Limit Resets: ${new Date(resetDate).toLocaleDateString()}`;
|
|
3138
|
-
}
|
|
3139
|
-
} else if (errorData.code === "QUOTA_EXCEEDED") {
|
|
3241
|
+
const formattedResetDate = resetDate ? new Date(resetDate).toLocaleDateString("en-US", {
|
|
3242
|
+
day: "2-digit",
|
|
3243
|
+
month: "short",
|
|
3244
|
+
year: "numeric"
|
|
3245
|
+
}) : void 0;
|
|
3246
|
+
console.error("");
|
|
3247
|
+
console.error(border);
|
|
3248
|
+
if (errorData.code === "QUOTA_EXCEEDED") {
|
|
3140
3249
|
const exceeded = details;
|
|
3141
|
-
|
|
3142
|
-
|
|
3143
|
-
|
|
3144
|
-
|
|
3145
|
-
|
|
3146
|
-
|
|
3147
|
-
|
|
3148
|
-
|
|
3149
|
-
|
|
3150
|
-
|
|
3151
|
-
|
|
3152
|
-
|
|
3250
|
+
const orgRemaining = (exceeded.total ?? 0) - (exceeded.used ?? 0);
|
|
3251
|
+
const effectiveLimit = (exceeded.projectLimit ?? 0) + (exceeded.projectBorrowed ?? 0);
|
|
3252
|
+
console.error(" \u274C TestDino Project Execution Limit Reached");
|
|
3253
|
+
console.error(border);
|
|
3254
|
+
console.error("");
|
|
3255
|
+
console.error(" The test case limit allocated to this project has been exceeded.");
|
|
3256
|
+
console.error("");
|
|
3257
|
+
console.error(" Project Usage:");
|
|
3258
|
+
if (exceeded.projectName) {
|
|
3259
|
+
console.error(` Project: ${exceeded.projectName}`);
|
|
3260
|
+
}
|
|
3261
|
+
if (exceeded.projectLimit != null) {
|
|
3262
|
+
console.error(
|
|
3263
|
+
` Limit: ${effectiveLimit} test cases${exceeded.projectBorrowed ? ` (${exceeded.projectLimit} allocated + ${exceeded.projectBorrowed} borrowed)` : ""}`
|
|
3264
|
+
);
|
|
3153
3265
|
}
|
|
3266
|
+
if (exceeded.projectUsed != null) {
|
|
3267
|
+
console.error(` Used: ${exceeded.projectUsed}`);
|
|
3268
|
+
}
|
|
3269
|
+
console.error(` This run: ${exceeded.totalTests || "Unknown"} test cases`);
|
|
3270
|
+
console.error(" Status: Project quota exhausted");
|
|
3271
|
+
console.error("");
|
|
3272
|
+
console.error(" Organization Usage:");
|
|
3273
|
+
console.error(` Plan: ${planName} (${exceeded.total || "Unknown"} test cases / month)`);
|
|
3274
|
+
console.error(` Used: ${exceeded.used || "Unknown"}`);
|
|
3275
|
+
console.error(` Remaining: ${orgRemaining}`);
|
|
3276
|
+
if (orgRemaining > 0) {
|
|
3277
|
+
console.error("");
|
|
3278
|
+
console.error(" Note:");
|
|
3279
|
+
console.error(` Your organization still has ${orgRemaining} test cases available,`);
|
|
3280
|
+
console.error(" but they are not allocated to this project.");
|
|
3281
|
+
}
|
|
3282
|
+
console.error("");
|
|
3283
|
+
console.error(" Solutions:");
|
|
3284
|
+
console.error(" 1. Allocate more test case quota to this project in");
|
|
3285
|
+
console.error(" Settings \u2192 Billing & Usage \u2192 Test Limits");
|
|
3286
|
+
console.error(" 2. Enable Auto Allocation to automatically distribute");
|
|
3287
|
+
console.error(" remaining organization quota");
|
|
3288
|
+
} else if (errorData.code === "QUOTA_EXHAUSTED") {
|
|
3289
|
+
console.error(" \u274C TestDino Organization Execution Limit Reached");
|
|
3290
|
+
console.error(border);
|
|
3291
|
+
console.error("");
|
|
3292
|
+
console.error(" Your organization has exhausted its monthly test case limit.");
|
|
3293
|
+
console.error("");
|
|
3294
|
+
console.error(" Organization Usage:");
|
|
3295
|
+
console.error(` Plan: ${planName} (${details.totalLimit || "Unknown"} test cases / month)`);
|
|
3296
|
+
console.error(` Used: ${details.used || "Unknown"}`);
|
|
3297
|
+
console.error(" Remaining: 0");
|
|
3298
|
+
console.error("");
|
|
3299
|
+
console.error(" Solutions:");
|
|
3300
|
+
console.error(" 1. Upgrade your plan to increase monthly test case limit");
|
|
3301
|
+
}
|
|
3302
|
+
if (formattedResetDate) {
|
|
3303
|
+
console.error("");
|
|
3304
|
+
console.error(" Monthly Reset:");
|
|
3305
|
+
console.error(` ${formattedResetDate}`);
|
|
3154
3306
|
}
|
|
3155
3307
|
console.error("");
|
|
3156
|
-
console.error(
|
|
3157
|
-
console.error("
|
|
3158
|
-
console.error(border);
|
|
3159
|
-
console.error(` ${message}`);
|
|
3160
|
-
console.error("");
|
|
3161
|
-
console.error(" Solutions:");
|
|
3162
|
-
console.error(" 1. Upgrade your plan to increase monthly limit");
|
|
3163
|
-
console.error(" 2. Wait for monthly limit reset");
|
|
3164
|
-
console.error(" 3. Visit https://testdino.com/pricing for plan options");
|
|
3308
|
+
console.error(" Docs: https://docs.testdino.com/platform/billing-and-usage/test-limits");
|
|
3309
|
+
console.error(" Pricing: https://testdino.com/pricing");
|
|
3165
3310
|
console.error(border);
|
|
3166
3311
|
console.error("");
|
|
3167
3312
|
}
|
|
@@ -3541,13 +3686,10 @@ var TestdinoReporter = class {
|
|
|
3541
3686
|
return failures;
|
|
3542
3687
|
}
|
|
3543
3688
|
};
|
|
3544
|
-
|
|
3545
|
-
|
|
3546
|
-
|
|
3547
|
-
|
|
3689
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
3690
|
+
0 && (module.exports = {
|
|
3691
|
+
coverageFixtures,
|
|
3692
|
+
expect,
|
|
3693
|
+
test
|
|
3548
3694
|
});
|
|
3549
|
-
exports.coverageFixtures = coverageFixtures;
|
|
3550
|
-
exports.default = TestdinoReporter;
|
|
3551
|
-
exports.test = test;
|
|
3552
|
-
//# sourceMappingURL=index.js.map
|
|
3553
3695
|
//# sourceMappingURL=index.js.map
|