@empiricalrun/playwright-utils 0.11.0 → 0.13.2

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
@@ -1,5 +1,33 @@
1
1
  # @empiricalrun/playwright-utils
2
2
 
3
+ ## 0.13.2
4
+
5
+ ### Patch Changes
6
+
7
+ - 73a40d9: fix: throw if captcha env var is missing
8
+
9
+ ## 0.13.1
10
+
11
+ ### Patch Changes
12
+
13
+ - 5882a20: feat: add inline master agent support
14
+
15
+ ## 0.13.0
16
+
17
+ ### Minor Changes
18
+
19
+ - cff4d1c: feat: add third party captcha solver
20
+
21
+ ## 0.12.0
22
+
23
+ ### Minor Changes
24
+
25
+ - 3c94c63: feat: EmailClient support for email otp logins
26
+
27
+ ### Patch Changes
28
+
29
+ - ab045ff: fix: set dynamic maxQueueSize and clearer error logs
30
+
3
31
  ## 0.11.0
4
32
 
5
33
  ### Minor Changes
package/README.md CHANGED
@@ -1,3 +1,60 @@
1
1
  # playwright-utils
2
2
 
3
- Playwright utils for test code repos of our customers
3
+ Playwright utils for test code repos of our customers
4
+
5
+ ## Utilities
6
+
7
+ - Default config for Playwright projects
8
+ - Custom reporter
9
+ - Highlight user actions
10
+ - Custom fixture to wrap around that
11
+ - Vision capabilities
12
+ - `locator.query` API
13
+ - Captcha
14
+ - Email automation
15
+
16
+ ## Email automation
17
+
18
+ ### Example usage
19
+
20
+ #### Dynamic email
21
+
22
+ This dynamically generates a random email address that can
23
+ be used for the test (e.g. invite a new user).
24
+
25
+ ```ts
26
+ import { EmailClient } from "@empiricalrun/playwright-utils";
27
+
28
+ const client = new EmailClient();
29
+ const address = client.getAddress();
30
+
31
+ // Input the `address` in the application
32
+ // that sends the email.
33
+
34
+ // Get email received on the `address`
35
+ const email = await client.waitForEmail();
36
+ ```
37
+
38
+ #### Static email
39
+
40
+ This uses a known (static) email that can be used to login
41
+ into an application.
42
+
43
+ This needs an email id (e.g. `test-login-user`). The email id
44
+ is appended with the domain (managed internally) to get the full
45
+ email address.
46
+
47
+ ```ts
48
+ import { EmailClient } from "@empiricalrun/playwright-utils";
49
+
50
+ const emailId = `test-login-user`;
51
+
52
+ const client = new EmailClient({ emailId });
53
+ const address = client.getAddress(); // Returns full address with domain
54
+
55
+ // Get email received on the `address`
56
+ const email = await client.waitForEmail();
57
+
58
+ // Get login OTP
59
+ const loginCode = email.codes[0];
60
+ ```
@@ -0,0 +1,7 @@
1
+ import { Page } from "@playwright/test";
2
+ type CaptchaSolveOptions = {
3
+ provider: "2captcha" | "llm-vision";
4
+ };
5
+ export declare function solveRecaptcha(page: Page, options?: CaptchaSolveOptions): Promise<void>;
6
+ export {};
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/captcha/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAKxC,KAAK,mBAAmB,GAAG;IACzB,QAAQ,EAAE,UAAU,GAAG,YAAY,CAAC;CACrC,CAAC;AAMF,wBAAsB,cAAc,CAClC,IAAI,EAAE,IAAI,EACV,OAAO,GAAE,mBAAiC,iBAmB3C"}
@@ -0,0 +1,32 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.solveRecaptcha = void 0;
7
+ const puppeteer_extra_plugin_recaptcha_1 = __importDefault(require("puppeteer-extra-plugin-recaptcha"));
8
+ const vision_1 = require("./vision");
9
+ const defaultOpts = {
10
+ provider: "2captcha",
11
+ };
12
+ async function solveRecaptcha(page, options = defaultOpts) {
13
+ const { provider } = options;
14
+ if (provider === "2captcha") {
15
+ const token = process.env.TWOCAPTCHA_API_KEY;
16
+ if (!token) {
17
+ throw new Error("Missing environment variable: TWOCAPTCHA_API_KEY");
18
+ }
19
+ const rcPlugin = (0, puppeteer_extra_plugin_recaptcha_1.default)({
20
+ provider: {
21
+ id: "2captcha",
22
+ token,
23
+ },
24
+ visualFeedback: true,
25
+ });
26
+ await rcPlugin.solveRecaptchas(page);
27
+ }
28
+ else if (provider === "llm-vision") {
29
+ return (0, vision_1.solveRecaptcha)(page);
30
+ }
31
+ }
32
+ exports.solveRecaptcha = solveRecaptcha;
@@ -1,3 +1,3 @@
1
1
  import { Page } from "@playwright/test";
2
2
  export declare function solveRecaptcha(page: Page): Promise<void>;
3
- //# sourceMappingURL=captcha.d.ts.map
3
+ //# sourceMappingURL=vision.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vision.d.ts","sourceRoot":"","sources":["../../src/captcha/vision.ts"],"names":[],"mappings":"AACA,OAAO,EAAkB,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAyExD,wBAAsB,cAAc,CAAC,IAAI,EAAE,IAAI,iBAiB9C"}
package/dist/email.d.ts CHANGED
@@ -8,11 +8,18 @@ export type Email = {
8
8
  text?: string;
9
9
  html?: string;
10
10
  links: Link[];
11
+ codes: string[];
12
+ };
13
+ type EmailClientOptions = {
14
+ timeout?: number;
15
+ emailId?: string;
11
16
  };
12
17
  export declare class EmailClient {
13
18
  address: string;
14
19
  client: MailosaurClient;
15
- constructor();
20
+ timeout: number;
21
+ serverId: string;
22
+ constructor(opts?: EmailClientOptions);
16
23
  getAddress(): string;
17
24
  waitForEmail(): Promise<Email | undefined>;
18
25
  }
@@ -1 +1 @@
1
- {"version":3,"file":"email.d.ts","sourceRoot":"","sources":["../src/email.ts"],"names":[],"mappings":"AAAA,OAAO,eAAe,MAAM,WAAW,CAAC;AAExC,KAAK,IAAI,GAAG;IACV,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,MAAM,MAAM,KAAK,GAAG;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,IAAI,EAAE,CAAC;CACf,CAAC;AAIF,qBAAa,WAAW;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,eAAe,CAAC;;IAUxB,UAAU;IAIJ,YAAY,IAAI,OAAO,CAAC,KAAK,GAAG,SAAS,CAAC;CAqBjD"}
1
+ {"version":3,"file":"email.d.ts","sourceRoot":"","sources":["../src/email.ts"],"names":[],"mappings":"AAAA,OAAO,eAAe,MAAM,WAAW,CAAC;AAGxC,KAAK,IAAI,GAAG;IACV,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,MAAM,MAAM,KAAK,GAAG;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,IAAI,EAAE,CAAC;IACd,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB,CAAC;AAIF,KAAK,kBAAkB,GAAG;IACxB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,qBAAa,WAAW;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,eAAe,CAAC;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,SAAc;gBAEV,IAAI,GAAE,kBAAuB;IAWzC,UAAU;IAIJ,YAAY,IAAI,OAAO,CAAC,KAAK,GAAG,SAAS,CAAC;CAkCjD"}
package/dist/email.js CHANGED
@@ -5,36 +5,53 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.EmailClient = void 0;
7
7
  const mailosaur_1 = __importDefault(require("mailosaur"));
8
- const serverId = "pnyrwq5o";
8
+ const DEFAULT_TIMEOUT = 30000;
9
9
  class EmailClient {
10
10
  address;
11
11
  client;
12
- constructor() {
13
- const randomString = [...Array(7)]
14
- .map(() => Math.random().toString(36)[2])
15
- .join("");
16
- this.address = `${randomString}@${serverId}.mailosaur.net`;
12
+ timeout;
13
+ serverId = "pnyrwq5o";
14
+ constructor(opts = {}) {
17
15
  this.client = new mailosaur_1.default("NAEZtp2DVibTCzCxdYkZzhG4hrxSlBrV");
16
+ const { timeout = DEFAULT_TIMEOUT, emailId: knownEmailId } = opts;
17
+ this.timeout = timeout;
18
+ const emailId = knownEmailId ||
19
+ [...Array(7)].map(() => Math.random().toString(36)[2]).join("");
20
+ const emailDomain = `${this.serverId}.mailosaur.net`;
21
+ this.address = `${emailId}@${emailDomain}`;
18
22
  }
19
23
  getAddress() {
20
24
  return this.address;
21
25
  }
22
26
  async waitForEmail() {
23
27
  try {
24
- const email = await this.client.messages.get(serverId, {
28
+ const email = await this.client.messages.get(this.serverId, {
25
29
  sentTo: this.address,
26
- }, { timeout: 30000 });
27
- if (email) {
28
- return {
29
- subject: email.subject,
30
- text: email.text?.body,
31
- html: email.html?.body,
32
- links: email.html?.links || [],
33
- };
34
- }
30
+ }, { timeout: this.timeout });
31
+ const codesAsString = (email.html?.codes || []).map((code) => code.value);
32
+ return {
33
+ subject: email.subject,
34
+ text: email.text?.body,
35
+ html: email.html?.body,
36
+ links: email.html?.links || [],
37
+ codes: codesAsString,
38
+ };
35
39
  }
36
40
  catch (error) {
37
- console.log("Error receiving email");
41
+ if (error.errorType) {
42
+ const mailosaurError = error;
43
+ if (mailosaurError.errorType === "authentication_error") {
44
+ throw new Error("Email provider error: Authentication error");
45
+ }
46
+ else if (mailosaurError.errorType === "search_timeout") {
47
+ throw new Error(`Email not received within ${this.timeout}ms`);
48
+ }
49
+ else {
50
+ console.error(`Mailosaur error: ${mailosaurError.httpResponseBody}`);
51
+ throw new Error(`Unknown error: ${mailosaurError.errorType}`);
52
+ }
53
+ }
54
+ throw error;
38
55
  }
39
56
  }
40
57
  }
@@ -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,EAEV,KAAK,EACL,QAAQ,IAAI,cAAc,EAC1B,SAAS,EACT,UAAU,IAAI,gBAAgB,EAE/B,MAAM,2BAA2B,CAAC;AAGnC,OAAO,EAML,UAAU,EAMX,MAAM,2BAA2B,CAAC;AAgBnC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAiC/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;AAEF,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;IAClC,OAAO,CAAC,UAAU,CAAa;IAC/B,OAAO,CAAC,oBAAoB,CAAiC;IAC7D,OAAO,CAAC,kBAAkB,CAAkC;IAC5D,OAAO,CAAC,kBAAkB,CAAK;IAE/B,OAAO,CAAC,YAAY;YA8BN,uBAAuB;IAQrC,OAAO,CAAC,YAAY,CAEN;IACd,OAAO,CAAC,eAAe,CAAmB;gBAE9B,OAAO,EAAE,mBAAmB;IAIxC,aAAa;IAIb,QAAQ;IAER,QAAQ;IAER,WAAW;IAEX,SAAS;IAET,OAAO,IAAI,IAAI;IAIf,WAAW,CAAC,IAAI,EAAE,cAAc,GAAG,IAAI;IAQvC,SAAS,CAAC,IAAI,EAAE,cAAc,EAAE,MAAM,EAAE,gBAAgB;IAyDxD,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;IA8FxB,MAAM;CA8Bb;AAkCD,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;AAqiBD,eAAe,YAAY,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,EAEV,KAAK,EACL,QAAQ,IAAI,cAAc,EAC1B,SAAS,EACT,UAAU,IAAI,gBAAgB,EAE/B,MAAM,2BAA2B,CAAC;AAGnC,OAAO,EAML,UAAU,EAMX,MAAM,2BAA2B,CAAC;AAgBnC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAiC/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;AAEF,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;IAClC,OAAO,CAAC,UAAU,CAAa;IAC/B,OAAO,CAAC,oBAAoB,CAAiC;IAC7D,OAAO,CAAC,kBAAkB,CAAkC;IAC5D,OAAO,CAAC,kBAAkB,CAEpB;IAEN,OAAO,CAAC,YAAY;YA8BN,uBAAuB;IAQrC,OAAO,CAAC,YAAY,CAEN;IACd,OAAO,CAAC,eAAe,CAAmB;gBAE9B,OAAO,EAAE,mBAAmB;IAIxC,aAAa;IAIb,QAAQ;IAER,QAAQ;IAER,WAAW;IAEX,SAAS;IAET,OAAO,IAAI,IAAI;IAIf,WAAW,CAAC,IAAI,EAAE,cAAc,GAAG,IAAI;IAQvC,SAAS,CAAC,IAAI,EAAE,cAAc,EAAE,MAAM,EAAE,gBAAgB;IA4DxD,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;IA6FxB,MAAM;CA8Bb;AAkCD,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;AAqiBD,eAAe,YAAY,CAAC"}
@@ -49,25 +49,27 @@ class HtmlReporter {
49
49
  _testCount = 1;
50
50
  uploadExecutionQueue = [];
51
51
  uploadWaitingQueue = [];
52
- uploadMaxQueueSize = 2;
52
+ uploadMaxQueueSize = process.env.UPLOAD_MAX_QUEUE_SIZE
53
+ ? parseInt(process.env.UPLOAD_MAX_QUEUE_SIZE)
54
+ : 2;
53
55
  processQueue() {
54
56
  try {
55
- // console.log("Execution queue length: ", this.uploadExecutionQueue.length);
56
- // console.log("Waiting queue length: ", this.uploadWaitingQueue.length);
57
- while (this.uploadExecutionQueue.length < this.uploadMaxQueueSize &&
57
+ while (this.uploadExecutionQueue.length <= this.uploadMaxQueueSize &&
58
58
  this.uploadWaitingQueue.length > 0) {
59
59
  const task = this.uploadWaitingQueue.shift();
60
60
  if (task) {
61
61
  const promise = task()
62
- .catch((e) => {
63
- // this.retryTask(task); // Handle retries ?
64
- console.log("Upload failed ", e);
62
+ .catch(() => {
63
+ // this.retryTask(task); // Retries handled within task itself
64
+ // console.log("Upload failed", e); // error is already logged in the upload function
65
65
  })
66
66
  .finally(() => {
67
+ // console.log("Finished upload execution from waiting queue"); // we already log in the upload function on successful upload
67
68
  // eslint-disable-next-line @typescript-eslint/no-floating-promises
68
69
  this.uploadExecutionQueue = this.uploadExecutionQueue.filter((p) => p !== promise);
69
70
  this.processQueue();
70
71
  });
72
+ console.log("Executing task from queue");
71
73
  this.uploadExecutionQueue.push(promise);
72
74
  }
73
75
  }
@@ -126,21 +128,24 @@ class HtmlReporter {
126
128
  folderName,
127
129
  uploadBucket: "test-report",
128
130
  });
129
- if (this.uploadExecutionQueue.length < this.uploadMaxQueueSize) {
131
+ if (this.uploadExecutionQueue.length <= this.uploadMaxQueueSize) {
130
132
  const promise = uploadTask()
131
133
  .catch((e) => {
132
- console.error("R2 upload failed for", attachment.path, e);
134
+ console.error("Upload failed for", attachment.path, e);
133
135
  // this.retryTask(uploadTask); // Retry logic
134
136
  })
135
137
  .finally(() => {
136
- console.log("Upload finished for ", attachment.path);
138
+ // we already log in the upload function on successful upload
139
+ // console.log("Finished upload execution for ", attachment.path);
137
140
  // eslint-disable-next-line @typescript-eslint/no-floating-promises
138
141
  this.uploadExecutionQueue = this.uploadExecutionQueue.filter((p) => p !== promise);
139
142
  this.processQueue(); // Keep processing queue
140
143
  });
144
+ console.log("Executing upload task for ", attachment.path);
141
145
  this.uploadExecutionQueue.push(promise);
142
146
  }
143
147
  else {
148
+ console.log("Queuing upload task ", attachment.path);
144
149
  this.uploadWaitingQueue.push(uploadTask);
145
150
  }
146
151
  }
@@ -239,8 +244,9 @@ class HtmlReporter {
239
244
  this.uploadWaitingQueue.push(uploadTraceTask);
240
245
  this.uploadWaitingQueue.push(uploadIndexTask);
241
246
  this.uploadWaitingQueue.push(uploadSummaryTask);
242
- // await Promise.allSettled(this.uploadExecutionQueue);
247
+ console.log("Waiting for all uploads to finish");
243
248
  await this.waitForAllTasksToFinish();
249
+ console.log("All uploads finished");
244
250
  const endTime = new Date().getTime();
245
251
  console.log("Finished final report upload at: ", new Intl.DateTimeFormat("en-US", {
246
252
  hour: "2-digit",
@@ -288,8 +294,7 @@ function reportFolderFromEnv() {
288
294
  }
289
295
  function getHtmlReportOptionProcessEnv() {
290
296
  // Note: PW_TEST_HTML_REPORT_OPEN is for backwards compatibility.
291
- // const htmlOpenEnv = process.env.PLAYWRIGHT_HTML_OPEN || process.env.PW_TEST_HTML_REPORT_OPEN;
292
- const htmlOpenEnv = false;
297
+ const htmlOpenEnv = process.env.PLAYWRIGHT_HTML_OPEN || process.env.PW_TEST_HTML_REPORT_OPEN;
293
298
  if (!htmlOpenEnv)
294
299
  return undefined;
295
300
  if (!isHtmlReportOption(htmlOpenEnv)) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@empiricalrun/playwright-utils",
3
- "version": "0.11.0",
3
+ "version": "0.13.2",
4
4
  "publishConfig": {
5
5
  "registry": "https://registry.npmjs.org/",
6
6
  "access": "public"
@@ -36,6 +36,8 @@
36
36
  "md5": "^2.3.0",
37
37
  "mime": "3.0.0",
38
38
  "playwright-core": "^1.46.1",
39
+ "playwright-extra": "^4.3.6",
40
+ "puppeteer-extra-plugin-recaptcha": "^3.6.8",
39
41
  "@empiricalrun/llm": "^0.9.1",
40
42
  "@empiricalrun/r2-uploader": "^0.1.3"
41
43
  },
@@ -1 +0,0 @@
1
- {"version":3,"file":"captcha.d.ts","sourceRoot":"","sources":["../src/captcha.ts"],"names":[],"mappings":"AACA,OAAO,EAAkB,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAyExD,wBAAsB,cAAc,CAAC,IAAI,EAAE,IAAI,iBAiB9C"}
File without changes