@empiricalrun/playwright-utils 0.18.15 → 0.18.16
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/CHANGELOG.md
CHANGED
|
@@ -39,12 +39,7 @@ declare class HtmlReporter implements ReporterV2 {
|
|
|
39
39
|
private _open;
|
|
40
40
|
private _port;
|
|
41
41
|
private _host;
|
|
42
|
-
private uploadExecutionQueue;
|
|
43
|
-
private uploadWaitingQueue;
|
|
44
|
-
private uploadMaxQueueSize;
|
|
45
42
|
private haveSeenAttachments;
|
|
46
|
-
private processQueue;
|
|
47
|
-
private waitForAllTasksToFinish;
|
|
48
43
|
private _buildResult;
|
|
49
44
|
private _topLevelErrors;
|
|
50
45
|
constructor(options: HtmlReporterOptions);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"custom.d.ts","sourceRoot":"","sources":["../../src/reporter/custom.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAMH,OAAO,KAAK,EACV,UAAU,EACV,UAAU,EAMV,KAAK,EACL,QAAQ,IAAI,cAAc,EAC1B,SAAS,EACT,UAAU,IAAI,gBAAgB,EAE/B,MAAM,2BAA2B,CAAC;AAInC,OAAO,EAML,UAAU,EAMX,MAAM,2BAA2B,CAAC;
|
|
1
|
+
{"version":3,"file":"custom.d.ts","sourceRoot":"","sources":["../../src/reporter/custom.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAMH,OAAO,KAAK,EACV,UAAU,EACV,UAAU,EAMV,KAAK,EACL,QAAQ,IAAI,cAAc,EAC1B,SAAS,EACT,UAAU,IAAI,gBAAgB,EAE/B,MAAM,2BAA2B,CAAC;AAInC,OAAO,EAML,UAAU,EAMX,MAAM,2BAA2B,CAAC;AAkBnC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAsC/C,QAAA,MAAM,iBAAiB,UAAoC,CAAC;AAC5D,KAAK,oBAAoB,GAAG,CAAC,OAAO,iBAAiB,CAAC,CAAC,MAAM,CAAC,CAAC;AAM/D,KAAK,mBAAmB,GAAG;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,IAAI,CAAC,EAAE,oBAAoB,CAAC;IAC5B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACxB,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB,CAAC;AA+BF,cAAM,YAAa,YAAW,UAAU;IACtC,OAAO,CAAC,MAAM,CAAc;IAC5B,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,QAAQ,CAAsB;IACtC,OAAO,CAAC,aAAa,CAAU;IAC/B,OAAO,CAAC,mBAAmB,CAAU;IACrC,OAAO,CAAC,KAAK,CAAqB;IAClC,OAAO,CAAC,KAAK,CAAqB;IAClC,OAAO,CAAC,KAAK,CAAqB;IAKlC,OAAO,CAAC,mBAAmB,CAAqB;IAEhD,OAAO,CAAC,YAAY,CAEN;IACd,OAAO,CAAC,eAAe,CAAmB;gBAE9B,OAAO,EAAE,mBAAmB;IAIxC,aAAa;IAIb,QAAQ;IAER,QAAQ;IAER,WAAW;IAEX,SAAS;IAET,WAAW,CAAC,IAAI,EAAE,cAAc;IAIhC,OAAO,IAAI,IAAI;IAIf,SAAS,CAAC,IAAI,EAAE,cAAc,EAAE,MAAM,EAAE,gBAAgB;YA4D1C,qBAAqB;IA6CnC,WAAW,CAAC,MAAM,EAAE,UAAU;IAI9B,OAAO,CAAC,KAAK,EAAE,KAAK;IAYpB,eAAe,IAAI;QACjB,YAAY,EAAE,MAAM,CAAC;QACrB,IAAI,EAAE,oBAAoB,CAAC;QAC3B,kBAAkB,EAAE,MAAM,CAAC;QAC3B,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;QACzB,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;KAC1B;IAsBD,eAAe,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO;IASxD,OAAO,CAAC,KAAK,EAAE,SAAS,GAAG,IAAI;IAIzB,KAAK,CAAC,MAAM,EAAE,UAAU;IAkIxB,MAAM;CA8Bb;AAwED,wBAAsB,cAAc,CAClC,YAAY,EAAE,MAAM,GAAG,SAAS,EAChC,IAAI,GAAE,MAAoB,EAC1B,IAAI,CAAC,EAAE,MAAM,EACb,MAAM,CAAC,EAAE,MAAM,iBAqBhB;AAED,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,MAAM,GAAG,UAAU,CAsBhE;AAsiBD,eAAe,YAAY,CAAC"}
|
package/dist/reporter/custom.js
CHANGED
|
@@ -34,6 +34,7 @@ const zipBundle_1 = require("playwright-core/lib/zipBundle");
|
|
|
34
34
|
const stream_1 = require("stream");
|
|
35
35
|
const logger_1 = require("../logger");
|
|
36
36
|
const base_1 = require("./base");
|
|
37
|
+
const queue_1 = require("./queue");
|
|
37
38
|
const util_1 = require("./util");
|
|
38
39
|
const htmlReportOptions = ["always", "never", "on-failure"];
|
|
39
40
|
const isHtmlReportOption = (type) => {
|
|
@@ -62,6 +63,7 @@ function listFilesRecursively(dir) {
|
|
|
62
63
|
}
|
|
63
64
|
return files;
|
|
64
65
|
}
|
|
66
|
+
const R2_BUCKET_NAME = "test-report";
|
|
65
67
|
class HtmlReporter {
|
|
66
68
|
config;
|
|
67
69
|
suite;
|
|
@@ -71,43 +73,10 @@ class HtmlReporter {
|
|
|
71
73
|
_open;
|
|
72
74
|
_port;
|
|
73
75
|
_host;
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
? parseInt(process.env.UPLOAD_MAX_QUEUE_SIZE)
|
|
78
|
-
: 2;
|
|
76
|
+
// Appwright persistent device tests generate attachments after onTestEnd
|
|
77
|
+
// We use the summary.json to re-process all attachments and this set ensures
|
|
78
|
+
// we don't upload the same attachment multiple times
|
|
79
79
|
haveSeenAttachments = new Set();
|
|
80
|
-
processQueue() {
|
|
81
|
-
try {
|
|
82
|
-
while (this.uploadExecutionQueue.length <= this.uploadMaxQueueSize &&
|
|
83
|
-
this.uploadWaitingQueue.length > 0) {
|
|
84
|
-
const task = this.uploadWaitingQueue.shift();
|
|
85
|
-
if (task) {
|
|
86
|
-
const promise = task()
|
|
87
|
-
.catch((e) => {
|
|
88
|
-
logger_1.logger.error("Error in processing task", e);
|
|
89
|
-
})
|
|
90
|
-
.finally(() => {
|
|
91
|
-
// console.log("Finished upload execution from waiting queue"); // we already log in the upload function on successful upload
|
|
92
|
-
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
93
|
-
this.uploadExecutionQueue = this.uploadExecutionQueue.filter((p) => p !== promise);
|
|
94
|
-
this.processQueue();
|
|
95
|
-
});
|
|
96
|
-
this.uploadExecutionQueue.push(promise);
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
catch (e) {
|
|
101
|
-
logger_1.logger.error("Error while processing queue", e);
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
async waitForAllTasksToFinish() {
|
|
105
|
-
return Promise.allSettled(this.uploadExecutionQueue).then(async () => {
|
|
106
|
-
if (this.uploadExecutionQueue.length > 0) {
|
|
107
|
-
await this.waitForAllTasksToFinish();
|
|
108
|
-
}
|
|
109
|
-
});
|
|
110
|
-
}
|
|
111
80
|
_buildResult;
|
|
112
81
|
_topLevelErrors = [];
|
|
113
82
|
constructor(options) {
|
|
@@ -127,62 +96,68 @@ class HtmlReporter {
|
|
|
127
96
|
return "v2";
|
|
128
97
|
}
|
|
129
98
|
onTestEnd(test, result) {
|
|
99
|
+
if (!process.env.TEST_RUN_GITHUB_ACTION_ID) {
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
130
102
|
try {
|
|
131
103
|
const attachmentPromises = result.attachments.map((attachment) => {
|
|
132
104
|
return this.processTestAttachment(attachment);
|
|
133
105
|
});
|
|
134
|
-
|
|
135
|
-
.
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
106
|
+
const testCaseRunEventTask = async () => {
|
|
107
|
+
return Promise.all(attachmentPromises)
|
|
108
|
+
.then((uploadedAttachments) => {
|
|
109
|
+
logger_1.logger.info(`Attachments for test ${test.title} are uploaded:`, uploadedAttachments);
|
|
110
|
+
const { suites, projectName } = (0, util_1.suitesAndProjectForTest)(test);
|
|
111
|
+
let params = {
|
|
112
|
+
test,
|
|
113
|
+
result,
|
|
114
|
+
assetsURL: {
|
|
115
|
+
trace: "",
|
|
116
|
+
videos: [],
|
|
117
|
+
},
|
|
118
|
+
suites,
|
|
119
|
+
projectName,
|
|
120
|
+
runId: process.env.TEST_RUN_GITHUB_ACTION_ID || "",
|
|
121
|
+
};
|
|
122
|
+
uploadedAttachments.forEach((attachment) => {
|
|
123
|
+
if (attachment) {
|
|
124
|
+
Object.entries(attachment).forEach(([key, value]) => {
|
|
125
|
+
if (key.includes("video")) {
|
|
126
|
+
params.assetsURL.videos.push({
|
|
127
|
+
name: `test-${test.id}-slug-${(0, crypto_1.randomUUID)()}`,
|
|
128
|
+
url: value,
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
if (key.includes("trace")) {
|
|
132
|
+
params.assetsURL.trace = value;
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
return (0, util_1.sendTestCaseUpdateToDashboard)(params);
|
|
138
|
+
})
|
|
139
|
+
.catch((error) => {
|
|
140
|
+
logger_1.logger.error(`Error sending test case event for: ${test.title}:`, error);
|
|
166
141
|
});
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
.catch((error) => {
|
|
170
|
-
logger_1.logger.error(`Error processing attachments for test: ${test.title}:`, error);
|
|
171
|
-
});
|
|
142
|
+
};
|
|
143
|
+
void (0, queue_1.sendTaskToQueue)(testCaseRunEventTask);
|
|
172
144
|
}
|
|
173
145
|
catch (e) {
|
|
174
|
-
logger_1.logger.error(`Error
|
|
146
|
+
logger_1.logger.error(`Error in onTestEnd for ${test.title}:`, e);
|
|
175
147
|
}
|
|
176
148
|
}
|
|
177
149
|
async processTestAttachment(attachment) {
|
|
178
|
-
if (!attachment.path)
|
|
150
|
+
if (!attachment.path) {
|
|
179
151
|
return;
|
|
180
|
-
|
|
152
|
+
}
|
|
153
|
+
if (this.haveSeenAttachments.has(attachment.path)) {
|
|
181
154
|
return;
|
|
155
|
+
}
|
|
182
156
|
this.haveSeenAttachments.add(attachment.path);
|
|
183
157
|
const folder = path_1.default.relative(this._outputFolder + "/data", attachment.path);
|
|
184
158
|
const folderName = folder.split("/")[0];
|
|
185
|
-
// on test gen run, we don't have PROJECT_NAME or TEST_RUN_GITHUB_ACTION_ID so we
|
|
159
|
+
// on test gen run, we don't have PROJECT_NAME or TEST_RUN_GITHUB_ACTION_ID so we
|
|
160
|
+
// skip uploading since we anyways handle uploads for that
|
|
186
161
|
if (!process.env.PROJECT_NAME || !process.env.TEST_RUN_GITHUB_ACTION_ID) {
|
|
187
162
|
return;
|
|
188
163
|
}
|
|
@@ -196,33 +171,19 @@ class HtmlReporter {
|
|
|
196
171
|
process.env.TEST_RUN_GITHUB_ACTION_ID +
|
|
197
172
|
"/data/" +
|
|
198
173
|
folderName,
|
|
199
|
-
uploadBucket:
|
|
200
|
-
})
|
|
174
|
+
uploadBucket: R2_BUCKET_NAME,
|
|
175
|
+
})
|
|
176
|
+
.then((fileMap) => {
|
|
177
|
+
logger_1.logger.debug("Finished uploading test attachment", fileMap);
|
|
178
|
+
return fileMap;
|
|
179
|
+
})
|
|
180
|
+
.catch((e) => logger_1.logger.error("Error uploading test attachment", e));
|
|
201
181
|
return uploadedFiles;
|
|
202
182
|
};
|
|
203
|
-
|
|
204
|
-
const promise = uploadTask()
|
|
205
|
-
.catch((e) => {
|
|
206
|
-
logger_1.logger.error("Upload failed for", attachment.path, e);
|
|
207
|
-
// this.retryTask(uploadTask); // Retry logic
|
|
208
|
-
})
|
|
209
|
-
.finally(() => {
|
|
210
|
-
// this is a list of promises which we dont want to await right now
|
|
211
|
-
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
212
|
-
this.uploadExecutionQueue = this.uploadExecutionQueue.filter((p) => p !== promise);
|
|
213
|
-
this.processQueue(); // Keep processing queue
|
|
214
|
-
});
|
|
215
|
-
this.uploadExecutionQueue.push(promise);
|
|
216
|
-
return promise;
|
|
217
|
-
}
|
|
218
|
-
else {
|
|
219
|
-
logger_1.logger.debug("Queuing upload task ", attachment.path);
|
|
220
|
-
this.uploadWaitingQueue.push(uploadTask);
|
|
221
|
-
}
|
|
183
|
+
return (0, queue_1.sendTaskToQueue)(uploadTask);
|
|
222
184
|
}
|
|
223
185
|
catch (e) {
|
|
224
186
|
logger_1.logger.error("Error while uploading attachment", e);
|
|
225
|
-
// tests shouldn't stop no matter whatever error we get
|
|
226
187
|
}
|
|
227
188
|
}
|
|
228
189
|
onConfigure(config) {
|
|
@@ -269,11 +230,10 @@ class HtmlReporter {
|
|
|
269
230
|
// await removeFolders([this._outputFolder]);
|
|
270
231
|
const builder = new HtmlBuilder(this.config, this._outputFolder, this._attachmentsBaseURL);
|
|
271
232
|
this._buildResult = await builder.build(this.config.metadata, projectSuites, result, this._topLevelErrors);
|
|
272
|
-
const startTime = new Date().getTime();
|
|
273
233
|
if (!process.env.PROJECT_NAME || !process.env.TEST_RUN_GITHUB_ACTION_ID) {
|
|
274
|
-
// Skipping uploads since PROJECT_NAME or TEST_RUN_GITHUB_ACTION_ID is not set,
|
|
275
234
|
return;
|
|
276
235
|
}
|
|
236
|
+
const startTime = new Date().getTime();
|
|
277
237
|
logger_1.logger.info("Starting final report upload at: ", new Intl.DateTimeFormat("en-US", {
|
|
278
238
|
hour: "2-digit",
|
|
279
239
|
minute: "2-digit",
|
|
@@ -302,7 +262,7 @@ class HtmlReporter {
|
|
|
302
262
|
"/" +
|
|
303
263
|
process.env.TEST_RUN_GITHUB_ACTION_ID +
|
|
304
264
|
"/trace",
|
|
305
|
-
uploadBucket:
|
|
265
|
+
uploadBucket: R2_BUCKET_NAME,
|
|
306
266
|
})
|
|
307
267
|
.then((fileMap) => {
|
|
308
268
|
logger_1.logger.debug("Finished uploading playwright trace", fileMap);
|
|
@@ -318,7 +278,7 @@ class HtmlReporter {
|
|
|
318
278
|
destinationDir: process.env.PROJECT_NAME +
|
|
319
279
|
"/" +
|
|
320
280
|
process.env.TEST_RUN_GITHUB_ACTION_ID,
|
|
321
|
-
uploadBucket:
|
|
281
|
+
uploadBucket: R2_BUCKET_NAME,
|
|
322
282
|
})
|
|
323
283
|
.then((fileMap) => {
|
|
324
284
|
logger_1.logger.debug("Finished uploading html report", fileMap);
|
|
@@ -334,26 +294,20 @@ class HtmlReporter {
|
|
|
334
294
|
destinationDir: process.env.PROJECT_NAME +
|
|
335
295
|
"/" +
|
|
336
296
|
process.env.TEST_RUN_GITHUB_ACTION_ID,
|
|
337
|
-
uploadBucket:
|
|
297
|
+
uploadBucket: R2_BUCKET_NAME,
|
|
338
298
|
})
|
|
339
299
|
.then((fileMap) => {
|
|
340
300
|
logger_1.logger.debug("Finished uploading json report", fileMap);
|
|
341
301
|
})
|
|
342
302
|
.catch((e) => logger_1.logger.error("Error uploading json report", e));
|
|
343
303
|
};
|
|
344
|
-
logger_1.logger.debug("Uploading waiting queue", this.uploadWaitingQueue.length);
|
|
345
|
-
logger_1.logger.debug("Uploading execution queue", this.uploadExecutionQueue.length);
|
|
346
|
-
const processQueue = !this.uploadExecutionQueue.length;
|
|
347
304
|
if (traceExists) {
|
|
348
|
-
|
|
349
|
-
}
|
|
350
|
-
this.uploadWaitingQueue.push(uploadIndexTask);
|
|
351
|
-
this.uploadWaitingQueue.push(uploadSummaryTask);
|
|
352
|
-
if (processQueue) {
|
|
353
|
-
this.processQueue();
|
|
305
|
+
void (0, queue_1.sendTaskToQueue)(uploadTraceTask);
|
|
354
306
|
}
|
|
307
|
+
void (0, queue_1.sendTaskToQueue)(uploadIndexTask);
|
|
308
|
+
void (0, queue_1.sendTaskToQueue)(uploadSummaryTask);
|
|
355
309
|
logger_1.logger.info("Waiting for all uploads to finish");
|
|
356
|
-
await
|
|
310
|
+
await (0, queue_1.waitForTaskQueueToFinish)();
|
|
357
311
|
logger_1.logger.info("All uploads finished");
|
|
358
312
|
const endTime = new Date().getTime();
|
|
359
313
|
logger_1.logger.info("Finished final report upload at: ", new Intl.DateTimeFormat("en-US", {
|
|
@@ -361,7 +315,6 @@ class HtmlReporter {
|
|
|
361
315
|
minute: "2-digit",
|
|
362
316
|
second: "2-digit",
|
|
363
317
|
}).format(endTime));
|
|
364
|
-
// time difference
|
|
365
318
|
const timeDiff = endTime - startTime;
|
|
366
319
|
logger_1.logger.info("Time taken to upload after tests ended: ", timeDiff, "ms");
|
|
367
320
|
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { FileMap } from "@empiricalrun/r2-uploader";
|
|
2
|
+
type AsyncTask = () => Promise<FileMap | void>;
|
|
3
|
+
type AsyncTaskReturnType = FileMap | void;
|
|
4
|
+
type AsyncTaskResult = Promise<AsyncTaskReturnType>;
|
|
5
|
+
export declare function sendTaskToQueue(task: AsyncTask): AsyncTaskResult;
|
|
6
|
+
export declare function waitForTaskQueueToFinish(): Promise<void>;
|
|
7
|
+
export {};
|
|
8
|
+
//# sourceMappingURL=queue.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"queue.d.ts","sourceRoot":"","sources":["../../src/reporter/queue.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,2BAA2B,CAAC;AAIpD,KAAK,SAAS,GAAG,MAAM,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAAC;AAC/C,KAAK,mBAAmB,GAAG,OAAO,GAAG,IAAI,CAAC;AAC1C,KAAK,eAAe,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAAC;AAoGpD,wBAAgB,eAAe,CAAC,IAAI,EAAE,SAAS,GAAG,eAAe,CAQhE;AAED,wBAAgB,wBAAwB,IAAI,OAAO,CAAC,IAAI,CAAC,CAOxD"}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.waitForTaskQueueToFinish = exports.sendTaskToQueue = void 0;
|
|
4
|
+
const logger_1 = require("../logger");
|
|
5
|
+
class TaskQueue {
|
|
6
|
+
waitingQueue = [];
|
|
7
|
+
executingTasks = [];
|
|
8
|
+
maxConcurrent;
|
|
9
|
+
constructor() {
|
|
10
|
+
this.maxConcurrent = process.env.UPLOAD_MAX_QUEUE_SIZE
|
|
11
|
+
? parseInt(process.env.UPLOAD_MAX_QUEUE_SIZE)
|
|
12
|
+
: 2;
|
|
13
|
+
}
|
|
14
|
+
async processNextTask() {
|
|
15
|
+
logger_1.logger.debug("[queue] processing next task in queue, with queue lengths:", taskQueue.queueLength, taskQueue.executingTasksCount);
|
|
16
|
+
if (this.waitingQueue.length === 0 ||
|
|
17
|
+
this.executingTasks.length >= this.maxConcurrent) {
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
const queuedTask = this.waitingQueue.shift();
|
|
21
|
+
if (!queuedTask)
|
|
22
|
+
return;
|
|
23
|
+
const { task, resolve, reject } = queuedTask;
|
|
24
|
+
// Create the promise first
|
|
25
|
+
let executingPromise;
|
|
26
|
+
const executeTask = async () => {
|
|
27
|
+
try {
|
|
28
|
+
const result = await task();
|
|
29
|
+
resolve(result);
|
|
30
|
+
}
|
|
31
|
+
catch (error) {
|
|
32
|
+
logger_1.logger.error("Error in processing task", error);
|
|
33
|
+
reject(error);
|
|
34
|
+
}
|
|
35
|
+
finally {
|
|
36
|
+
// Remove this task from executing tasks
|
|
37
|
+
const index = this.executingTasks.indexOf(executingPromise);
|
|
38
|
+
if (index > -1) {
|
|
39
|
+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
40
|
+
this.executingTasks.splice(index, 1);
|
|
41
|
+
}
|
|
42
|
+
// Try to process next task
|
|
43
|
+
await this.processNextTask();
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
// Assign the promise
|
|
47
|
+
executingPromise = executeTask();
|
|
48
|
+
this.executingTasks.push(executingPromise);
|
|
49
|
+
}
|
|
50
|
+
enqueue(task) {
|
|
51
|
+
return new Promise((resolve, reject) => {
|
|
52
|
+
this.waitingQueue.push({ task, resolve, reject });
|
|
53
|
+
this.processNextTask().catch((error) => {
|
|
54
|
+
logger_1.logger.error("Error processing queue", error);
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
async waitForCompletion() {
|
|
59
|
+
if (this.waitingQueue.length === 0 && this.executingTasks.length === 0) {
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
// First wait for all currently executing tasks
|
|
63
|
+
if (this.executingTasks.length > 0) {
|
|
64
|
+
await Promise.all(this.executingTasks);
|
|
65
|
+
}
|
|
66
|
+
// If there are still tasks in the waiting queue, process them
|
|
67
|
+
if (this.waitingQueue.length > 0) {
|
|
68
|
+
await this.processNextTask();
|
|
69
|
+
}
|
|
70
|
+
await this.waitForCompletion();
|
|
71
|
+
}
|
|
72
|
+
get queueLength() {
|
|
73
|
+
return this.waitingQueue.length;
|
|
74
|
+
}
|
|
75
|
+
get executingTasksCount() {
|
|
76
|
+
return this.executingTasks.length;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
const taskQueue = new TaskQueue();
|
|
80
|
+
function sendTaskToQueue(task) {
|
|
81
|
+
logger_1.logger.debug("[queue] sending task to queue, with queue lengths:", taskQueue.queueLength, taskQueue.executingTasksCount, task);
|
|
82
|
+
return taskQueue.enqueue(task);
|
|
83
|
+
}
|
|
84
|
+
exports.sendTaskToQueue = sendTaskToQueue;
|
|
85
|
+
function waitForTaskQueueToFinish() {
|
|
86
|
+
logger_1.logger.debug("[queue] waiting for queue to finish, with queue lengths:", taskQueue.queueLength, taskQueue.executingTasksCount);
|
|
87
|
+
return taskQueue.waitForCompletion();
|
|
88
|
+
}
|
|
89
|
+
exports.waitForTaskQueueToFinish = waitForTaskQueueToFinish;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@empiricalrun/playwright-utils",
|
|
3
|
-
"version": "0.18.
|
|
3
|
+
"version": "0.18.16",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"registry": "https://registry.npmjs.org/",
|
|
6
6
|
"access": "public"
|
|
@@ -43,9 +43,9 @@
|
|
|
43
43
|
"playwright-extra": "^4.3.6",
|
|
44
44
|
"puppeteer-extra-plugin-recaptcha": "^3.6.8",
|
|
45
45
|
"rimraf": "^6.0.1",
|
|
46
|
-
"@empiricalrun/
|
|
46
|
+
"@empiricalrun/r2-uploader": "^0.3.7",
|
|
47
47
|
"@empiricalrun/llm": "^0.9.28",
|
|
48
|
-
"@empiricalrun/
|
|
48
|
+
"@empiricalrun/test-gen": "^0.38.41"
|
|
49
49
|
},
|
|
50
50
|
"scripts": {
|
|
51
51
|
"dev": "tsc --build --watch",
|