@empiricalrun/playwright-utils 0.30.1 → 0.32.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +18 -0
- package/dist/email.d.ts +4 -14
- package/dist/email.d.ts.map +1 -1
- package/dist/email.js +14 -52
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/kv.d.ts +7 -0
- package/dist/kv.d.ts.map +1 -0
- package/dist/kv.js +35 -0
- package/dist/mailosaur-client.d.ts +34 -0
- package/dist/mailosaur-client.d.ts.map +1 -0
- package/dist/mailosaur-client.js +131 -0
- package/dist/test/index.d.ts +2 -0
- package/dist/test/index.d.ts.map +1 -1
- package/dist/test/index.js +5 -0
- package/docs/kv.md +26 -0
- package/package.json +3 -4
- package/tsconfig.tsbuildinfo +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# @empiricalrun/playwright-utils
|
|
2
2
|
|
|
3
|
+
## 0.32.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 74b2cc6: feat: add KV store API and playwright fixture
|
|
8
|
+
|
|
9
|
+
## 0.31.0
|
|
10
|
+
|
|
11
|
+
### Minor Changes
|
|
12
|
+
|
|
13
|
+
- 5639645: refactor: replace Mailosaur SDK with direct REST API calls
|
|
14
|
+
|
|
15
|
+
### Patch Changes
|
|
16
|
+
|
|
17
|
+
- Updated dependencies [25962dc]
|
|
18
|
+
- @empiricalrun/llm@0.25.1
|
|
19
|
+
- @empiricalrun/test-gen@0.78.2
|
|
20
|
+
|
|
3
21
|
## 0.30.1
|
|
4
22
|
|
|
5
23
|
### Patch Changes
|
package/dist/email.d.ts
CHANGED
|
@@ -1,15 +1,5 @@
|
|
|
1
|
-
import
|
|
2
|
-
type
|
|
3
|
-
text?: string;
|
|
4
|
-
href?: string;
|
|
5
|
-
};
|
|
6
|
-
export type Email = {
|
|
7
|
-
subject?: string;
|
|
8
|
-
text?: string;
|
|
9
|
-
html?: string;
|
|
10
|
-
links: Link[];
|
|
11
|
-
codes: string[];
|
|
12
|
-
};
|
|
1
|
+
import { type MailosaurEmail } from "./mailosaur-client";
|
|
2
|
+
export type Email = MailosaurEmail;
|
|
13
3
|
type QueryFilter = {
|
|
14
4
|
from?: string;
|
|
15
5
|
subject?: string;
|
|
@@ -21,12 +11,12 @@ type EmailClientOptions = {
|
|
|
21
11
|
};
|
|
22
12
|
export declare class EmailClient {
|
|
23
13
|
address: string;
|
|
24
|
-
client: MailosaurClient;
|
|
25
14
|
timeout: number;
|
|
26
15
|
serverId: string;
|
|
16
|
+
private client;
|
|
27
17
|
constructor(opts?: EmailClientOptions);
|
|
28
18
|
getAddress(): string;
|
|
29
|
-
waitForEmail(filter?: QueryFilter): Promise<Email
|
|
19
|
+
waitForEmail(filter?: QueryFilter): Promise<Email>;
|
|
30
20
|
}
|
|
31
21
|
export {};
|
|
32
22
|
//# sourceMappingURL=email.d.ts.map
|
package/dist/email.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"email.d.ts","sourceRoot":"","sources":["../src/email.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"email.d.ts","sourceRoot":"","sources":["../src/email.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmB,KAAK,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAE1E,MAAM,MAAM,KAAK,GAAG,cAAc,CAAC;AAEnC,KAAK,WAAW,GAAG;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,CAAC;AAKF,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,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,SAAa;IACrB,OAAO,CAAC,MAAM,CAAkB;gBAEpB,IAAI,GAAE,kBAAuB;IA2BzC,UAAU,IAAI,MAAM;IAId,YAAY,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,KAAK,CAAC;CAGzD"}
|
package/dist/email.js
CHANGED
|
@@ -1,26 +1,29 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
3
|
exports.EmailClient = void 0;
|
|
7
|
-
const
|
|
4
|
+
const mailosaur_client_1 = require("./mailosaur-client");
|
|
8
5
|
const DEFAULT_TIMEOUT = 30_000;
|
|
6
|
+
const SERVER_ID = "pnyrwq5o";
|
|
9
7
|
class EmailClient {
|
|
10
8
|
address;
|
|
11
|
-
client;
|
|
12
9
|
timeout;
|
|
13
|
-
serverId =
|
|
10
|
+
serverId = SERVER_ID;
|
|
11
|
+
client;
|
|
14
12
|
constructor(opts = {}) {
|
|
15
|
-
|
|
13
|
+
const apiKey = process.env.MAILOSAUR_API_KEY;
|
|
14
|
+
if (!apiKey) {
|
|
15
|
+
throw new Error("'apiKey' must be set");
|
|
16
|
+
}
|
|
16
17
|
const { timeout = DEFAULT_TIMEOUT, emailId: knownEmailId } = opts;
|
|
17
18
|
this.timeout = timeout;
|
|
19
|
+
this.client = new mailosaur_client_1.MailosaurClient({
|
|
20
|
+
apiKey,
|
|
21
|
+
serverId: this.serverId,
|
|
22
|
+
timeout: this.timeout,
|
|
23
|
+
});
|
|
18
24
|
const emailId = knownEmailId ||
|
|
19
25
|
[...Array(7)].map(() => Math.random().toString(36)[2]).join("");
|
|
20
26
|
if (emailId.includes("@")) {
|
|
21
|
-
// When EmailClient is used as a virtual SMTP server,
|
|
22
|
-
// we can read emails sent to any domain. These can be
|
|
23
|
-
// specified by setting the emailId to a full address.
|
|
24
27
|
this.address = emailId;
|
|
25
28
|
}
|
|
26
29
|
else {
|
|
@@ -32,48 +35,7 @@ class EmailClient {
|
|
|
32
35
|
return this.address;
|
|
33
36
|
}
|
|
34
37
|
async waitForEmail(filter) {
|
|
35
|
-
|
|
36
|
-
const bufferPeriod = 5_000;
|
|
37
|
-
const receivedAfter = new Date(now.getTime() - bufferPeriod);
|
|
38
|
-
try {
|
|
39
|
-
const email = await this.client.messages.get(this.serverId, {
|
|
40
|
-
sentTo: this.address,
|
|
41
|
-
sentFrom: filter?.from,
|
|
42
|
-
subject: filter?.subject,
|
|
43
|
-
body: filter?.body,
|
|
44
|
-
}, {
|
|
45
|
-
timeout: this.timeout,
|
|
46
|
-
receivedAfter,
|
|
47
|
-
});
|
|
48
|
-
const codesAsString = (email.html?.codes || []).map((code) => code.value);
|
|
49
|
-
const allLinks = [
|
|
50
|
-
...(email.html?.links || []),
|
|
51
|
-
...(email.text?.links || []),
|
|
52
|
-
];
|
|
53
|
-
return {
|
|
54
|
-
subject: email.subject,
|
|
55
|
-
text: email.text?.body,
|
|
56
|
-
html: email.html?.body,
|
|
57
|
-
links: allLinks,
|
|
58
|
-
codes: codesAsString,
|
|
59
|
-
};
|
|
60
|
-
}
|
|
61
|
-
catch (error) {
|
|
62
|
-
if (error.errorType) {
|
|
63
|
-
const mailosaurError = error;
|
|
64
|
-
if (mailosaurError.errorType === "authentication_error") {
|
|
65
|
-
throw new Error("Email provider error: Authentication error");
|
|
66
|
-
}
|
|
67
|
-
else if (mailosaurError.errorType === "search_timeout") {
|
|
68
|
-
throw new Error(`Email not received at ${this.address} within ${this.timeout}ms`);
|
|
69
|
-
}
|
|
70
|
-
else {
|
|
71
|
-
console.error(`Mailosaur error: ${mailosaurError.httpResponseBody}`);
|
|
72
|
-
throw new Error(`Unknown error: ${mailosaurError.errorType}`);
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
throw error;
|
|
76
|
-
}
|
|
38
|
+
return this.client.waitForEmail(this.address, filter);
|
|
77
39
|
}
|
|
78
40
|
}
|
|
79
41
|
exports.EmailClient = EmailClient;
|
package/dist/index.d.ts
CHANGED
|
@@ -3,5 +3,6 @@ export * from "./auth/google";
|
|
|
3
3
|
export { solveRecaptcha } from "./captcha";
|
|
4
4
|
export { baseConfig, chromeStablePath, devices } from "./config";
|
|
5
5
|
export * from "./email";
|
|
6
|
+
export * from "./kv";
|
|
6
7
|
export * from "./playwright-extensions";
|
|
7
8
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,QAAQ,CAAC;AACvB,cAAc,eAAe,CAAC;AAC9B,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAC3C,OAAO,EAAE,UAAU,EAAE,gBAAgB,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AACjE,cAAc,SAAS,CAAC;AACxB,cAAc,yBAAyB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,QAAQ,CAAC;AACvB,cAAc,eAAe,CAAC;AAC9B,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAC3C,OAAO,EAAE,UAAU,EAAE,gBAAgB,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AACjE,cAAc,SAAS,CAAC;AACxB,cAAc,MAAM,CAAC;AACrB,cAAc,yBAAyB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -24,4 +24,5 @@ Object.defineProperty(exports, "baseConfig", { enumerable: true, get: function (
|
|
|
24
24
|
Object.defineProperty(exports, "chromeStablePath", { enumerable: true, get: function () { return config_1.chromeStablePath; } });
|
|
25
25
|
Object.defineProperty(exports, "devices", { enumerable: true, get: function () { return config_1.devices; } });
|
|
26
26
|
__exportStar(require("./email"), exports);
|
|
27
|
+
__exportStar(require("./kv"), exports);
|
|
27
28
|
__exportStar(require("./playwright-extensions"), exports);
|
package/dist/kv.d.ts
ADDED
package/dist/kv.d.ts.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"kv.d.ts","sourceRoot":"","sources":["../src/kv.ts"],"names":[],"mappings":"AAYA,qBAAa,QAAQ;IACnB,OAAO,CAAC,MAAM,CAAqB;;IAW7B,GAAG,CAAC,CAAC,GAAG,OAAO,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC;IAgBhD,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAanE"}
|
package/dist/kv.js
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.KVClient = void 0;
|
|
4
|
+
const dashboard_1 = require("@empiricalrun/test-gen/dashboard");
|
|
5
|
+
class KVClient {
|
|
6
|
+
client;
|
|
7
|
+
constructor() {
|
|
8
|
+
if (!process.env.EMPIRICALRUN_API_KEY) {
|
|
9
|
+
throw new Error("EMPIRICALRUN_API_KEY environment variable is required");
|
|
10
|
+
}
|
|
11
|
+
this.client = new dashboard_1.DashboardAPIClient({
|
|
12
|
+
authType: "project-api-key",
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
async get(key) {
|
|
16
|
+
const response = await this.client.request("/api/projects/kv", {
|
|
17
|
+
method: "GET",
|
|
18
|
+
params: { key },
|
|
19
|
+
});
|
|
20
|
+
if (response.error) {
|
|
21
|
+
throw new Error(`KV get failed: ${response.error.message}`);
|
|
22
|
+
}
|
|
23
|
+
return response.data?.value ?? null;
|
|
24
|
+
}
|
|
25
|
+
async set(key, value, ttl) {
|
|
26
|
+
const response = await this.client.request("/api/projects/kv", {
|
|
27
|
+
method: "POST",
|
|
28
|
+
body: { key, value, ttl },
|
|
29
|
+
});
|
|
30
|
+
if (response.error) {
|
|
31
|
+
throw new Error(`KV set failed: ${response.error.message}`);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
exports.KVClient = KVClient;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
type Link = {
|
|
2
|
+
text?: string;
|
|
3
|
+
href?: string;
|
|
4
|
+
};
|
|
5
|
+
export type MailosaurEmail = {
|
|
6
|
+
subject?: string;
|
|
7
|
+
text?: string;
|
|
8
|
+
html?: string;
|
|
9
|
+
links: Link[];
|
|
10
|
+
codes: string[];
|
|
11
|
+
};
|
|
12
|
+
type MailosaurClientOptions = {
|
|
13
|
+
apiKey: string;
|
|
14
|
+
serverId: string;
|
|
15
|
+
timeout: number;
|
|
16
|
+
};
|
|
17
|
+
export declare class MailosaurClient {
|
|
18
|
+
private apiKey;
|
|
19
|
+
private serverId;
|
|
20
|
+
private timeout;
|
|
21
|
+
constructor(opts: MailosaurClientOptions);
|
|
22
|
+
waitForEmail(address: string, filter?: {
|
|
23
|
+
from?: string;
|
|
24
|
+
subject?: string;
|
|
25
|
+
body?: string;
|
|
26
|
+
}): Promise<MailosaurEmail>;
|
|
27
|
+
private searchWithPolling;
|
|
28
|
+
private getMessageById;
|
|
29
|
+
private handleMailosaurError;
|
|
30
|
+
private parseEmailResponse;
|
|
31
|
+
private sleep;
|
|
32
|
+
}
|
|
33
|
+
export {};
|
|
34
|
+
//# sourceMappingURL=mailosaur-client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mailosaur-client.d.ts","sourceRoot":"","sources":["../src/mailosaur-client.ts"],"names":[],"mappings":"AAAA,KAAK,IAAI,GAAG;IACV,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B,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;AA6BF,KAAK,sBAAsB,GAAG;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,qBAAa,eAAe;IAC1B,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,OAAO,CAAS;gBAEZ,IAAI,EAAE,sBAAsB;IAMlC,YAAY,CAChB,OAAO,EAAE,MAAM,EACf,MAAM,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,GAC1D,OAAO,CAAC,cAAc,CAAC;YAqBZ,iBAAiB;YAyEjB,cAAc;YAuBd,oBAAoB;IAqBlC,OAAO,CAAC,kBAAkB;IAmB1B,OAAO,CAAC,KAAK;CAGd"}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MailosaurClient = void 0;
|
|
4
|
+
const MAILOSAUR_BASE_URL = "https://mailosaur.com/api";
|
|
5
|
+
const DEFAULT_POLL_DELAY = 1000;
|
|
6
|
+
class MailosaurClient {
|
|
7
|
+
apiKey;
|
|
8
|
+
serverId;
|
|
9
|
+
timeout;
|
|
10
|
+
constructor(opts) {
|
|
11
|
+
this.apiKey = opts.apiKey;
|
|
12
|
+
this.serverId = opts.serverId;
|
|
13
|
+
this.timeout = opts.timeout;
|
|
14
|
+
}
|
|
15
|
+
async waitForEmail(address, filter) {
|
|
16
|
+
const now = new Date();
|
|
17
|
+
const bufferPeriod = 5_000;
|
|
18
|
+
const receivedAfter = new Date(now.getTime() - bufferPeriod);
|
|
19
|
+
const searchCriteria = {
|
|
20
|
+
sentTo: address,
|
|
21
|
+
...(filter?.from && { sentFrom: filter.from }),
|
|
22
|
+
...(filter?.subject && { subject: filter.subject }),
|
|
23
|
+
...(filter?.body && { body: filter.body }),
|
|
24
|
+
};
|
|
25
|
+
const messageId = await this.searchWithPolling(searchCriteria, receivedAfter, address);
|
|
26
|
+
const message = await this.getMessageById(messageId);
|
|
27
|
+
return this.parseEmailResponse(message);
|
|
28
|
+
}
|
|
29
|
+
async searchWithPolling(criteria, receivedAfter, address) {
|
|
30
|
+
const startTime = Date.now();
|
|
31
|
+
let pollCount = 0;
|
|
32
|
+
while (true) {
|
|
33
|
+
const searchParams = new URLSearchParams({
|
|
34
|
+
server: this.serverId,
|
|
35
|
+
page: "0",
|
|
36
|
+
itemsPerPage: "1",
|
|
37
|
+
receivedAfter: receivedAfter.toISOString(),
|
|
38
|
+
});
|
|
39
|
+
const url = `${MAILOSAUR_BASE_URL}/messages/search?${searchParams.toString()}`;
|
|
40
|
+
let response;
|
|
41
|
+
try {
|
|
42
|
+
response = await fetch(url, {
|
|
43
|
+
method: "POST",
|
|
44
|
+
headers: {
|
|
45
|
+
Authorization: `Basic ${Buffer.from(`${this.apiKey}:`).toString("base64")}`,
|
|
46
|
+
"Content-Type": "application/json",
|
|
47
|
+
},
|
|
48
|
+
body: JSON.stringify(criteria),
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
catch (err) {
|
|
52
|
+
console.error("Mailosaur network error during search:", err);
|
|
53
|
+
throw new Error("Email provider error: Network request failed");
|
|
54
|
+
}
|
|
55
|
+
if (!response.ok) {
|
|
56
|
+
await this.handleMailosaurError(response, "search");
|
|
57
|
+
}
|
|
58
|
+
const body = (await response.json());
|
|
59
|
+
if (body.items && body.items.length > 0 && body.items[0]?.id) {
|
|
60
|
+
return body.items[0].id;
|
|
61
|
+
}
|
|
62
|
+
// Parse delay from response header, filtering out invalid values
|
|
63
|
+
const header = response.headers.get("x-ms-delay");
|
|
64
|
+
let delay = DEFAULT_POLL_DELAY;
|
|
65
|
+
if (header) {
|
|
66
|
+
const pattern = header
|
|
67
|
+
.split(",")
|
|
68
|
+
.map((x) => parseInt(x, 10))
|
|
69
|
+
.filter((n) => Number.isFinite(n) && n > 0);
|
|
70
|
+
if (pattern.length > 0) {
|
|
71
|
+
const index = Math.min(pollCount, pattern.length - 1);
|
|
72
|
+
delay = pattern[index] ?? DEFAULT_POLL_DELAY;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
pollCount += 1;
|
|
76
|
+
// Check if we'll exceed timeout
|
|
77
|
+
const elapsed = Date.now() - startTime;
|
|
78
|
+
if (elapsed + delay > this.timeout) {
|
|
79
|
+
throw new Error(`Email not received at ${address} within ${this.timeout}ms`);
|
|
80
|
+
}
|
|
81
|
+
await this.sleep(delay);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
async getMessageById(messageId) {
|
|
85
|
+
const url = `${MAILOSAUR_BASE_URL}/messages/${messageId}`;
|
|
86
|
+
let response;
|
|
87
|
+
try {
|
|
88
|
+
response = await fetch(url, {
|
|
89
|
+
method: "GET",
|
|
90
|
+
headers: {
|
|
91
|
+
Authorization: `Basic ${Buffer.from(`${this.apiKey}:`).toString("base64")}`,
|
|
92
|
+
},
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
catch (err) {
|
|
96
|
+
console.error("Mailosaur network error during getMessageById:", err);
|
|
97
|
+
throw new Error("Email provider error: Network request failed");
|
|
98
|
+
}
|
|
99
|
+
if (!response.ok) {
|
|
100
|
+
await this.handleMailosaurError(response, "getMessageById");
|
|
101
|
+
}
|
|
102
|
+
return (await response.json());
|
|
103
|
+
}
|
|
104
|
+
async handleMailosaurError(response, context) {
|
|
105
|
+
const errorBody = await response.text().catch(() => "");
|
|
106
|
+
console.error(`Mailosaur error in ${context}:`, errorBody || response.statusText);
|
|
107
|
+
if (response.status === 401) {
|
|
108
|
+
throw new Error("Email provider error: Authentication error");
|
|
109
|
+
}
|
|
110
|
+
throw new Error(`Email provider error (${context}): HTTP ${response.status}${errorBody ? ` - ${errorBody}` : ""}`);
|
|
111
|
+
}
|
|
112
|
+
parseEmailResponse(email) {
|
|
113
|
+
const codesAsString = email.html?.codes
|
|
114
|
+
?.map((code) => code.value)
|
|
115
|
+
.filter((v) => typeof v === "string") ?? [];
|
|
116
|
+
const htmlLinks = email.html?.links ?? [];
|
|
117
|
+
const textLinks = email.text?.links ?? [];
|
|
118
|
+
const allLinks = [...htmlLinks, ...textLinks];
|
|
119
|
+
return {
|
|
120
|
+
subject: email.subject,
|
|
121
|
+
text: email.text?.body,
|
|
122
|
+
html: email.html?.body,
|
|
123
|
+
links: allLinks,
|
|
124
|
+
codes: codesAsString,
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
sleep(ms) {
|
|
128
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
exports.MailosaurClient = MailosaurClient;
|
package/dist/test/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { BrowserContext, BrowserContextOptions, expect, Page, PlaywrightTestArgs, PlaywrightTestOptions, PlaywrightWorkerArgs, PlaywrightWorkerOptions, TestType } from "@playwright/test";
|
|
2
|
+
import { KVClient } from "../kv";
|
|
2
3
|
import { injectLocatorHighlightScripts } from "./scripts";
|
|
3
4
|
import { HighlighterOpts } from "./types";
|
|
4
5
|
import { setVideoLabel } from "./video-labels";
|
|
@@ -12,6 +13,7 @@ declare global {
|
|
|
12
13
|
declare const extendExpect: (expectInstance: typeof expect) => import("@playwright/test").Expect<{}>;
|
|
13
14
|
type TestOptions = {
|
|
14
15
|
page: Page;
|
|
16
|
+
kv: KVClient;
|
|
15
17
|
customContextPageProvider: (options?: BrowserContextOptions) => Promise<{
|
|
16
18
|
context: BrowserContext;
|
|
17
19
|
page: Page;
|
package/dist/test/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/test/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,cAAc,EACd,qBAAqB,EACrB,MAAM,EACN,IAAI,EACJ,kBAAkB,EAClB,qBAAqB,EACrB,oBAAoB,EACpB,uBAAuB,EACvB,QAAQ,EACT,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/test/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,cAAc,EACd,qBAAqB,EACrB,MAAM,EACN,IAAI,EACJ,kBAAkB,EAClB,qBAAqB,EACrB,oBAAoB,EACpB,uBAAuB,EACvB,QAAQ,EACT,MAAM,kBAAkB,CAAC;AAG1B,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAEjC,OAAO,EAAE,6BAA6B,EAAE,MAAM,WAAW,CAAC;AAC1D,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAC1C,OAAO,EAA8B,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAE3E,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,cAAc,CAAC;QACvB,UAAU,QAAQ,CAAC,CAAC;YAClB,WAAW,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;SAC9C;KACF;CACF;AAED,QAAA,MAAM,YAAY,GAAa,gBAAgB,OAAO,MAAM,0CAG3D,CAAC;AAEF,KAAK,WAAW,GAAG;IACjB,IAAI,EAAE,IAAI,CAAC;IACX,EAAE,EAAE,QAAQ,CAAC;IACb,yBAAyB,EAAE,CACzB,OAAO,CAAC,EAAE,qBAAqB,KAC5B,OAAO,CAAC;QAAE,OAAO,EAAE,cAAc,CAAC;QAAC,IAAI,EAAE,IAAI,CAAA;KAAE,CAAC,CAAC;IACtD,UAAU,EAAE,IAAI,CAAC;CAClB,CAAC;AAYF,QAAA,MAAM,eAAe,GACnB,QAAQ,QAAQ,CACd,kBAAkB,GAAG,qBAAqB,EAC1C,oBAAoB,GAAG,uBAAuB,CAC/C,EACD,UAAS,eAAgC,uHAuG1C,CAAC;AAEF,OAAO,EACL,eAAe,EACf,YAAY,EACZ,6BAA6B,EAC7B,aAAa,GACd,CAAC"}
|
package/dist/test/index.js
CHANGED
|
@@ -5,6 +5,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.setVideoLabel = exports.injectLocatorHighlightScripts = exports.extendExpect = exports.baseTestFixture = void 0;
|
|
7
7
|
const path_1 = __importDefault(require("path"));
|
|
8
|
+
const kv_1 = require("../kv");
|
|
8
9
|
const expect_1 = require("./expect");
|
|
9
10
|
const scripts_1 = require("./scripts");
|
|
10
11
|
Object.defineProperty(exports, "injectLocatorHighlightScripts", { enumerable: true, get: function () { return scripts_1.injectLocatorHighlightScripts; } });
|
|
@@ -48,6 +49,10 @@ const baseTestFixture = function (testFn, options = defaultOptions) {
|
|
|
48
49
|
});
|
|
49
50
|
await use(page);
|
|
50
51
|
},
|
|
52
|
+
kv: async ({}, use) => {
|
|
53
|
+
const client = new kv_1.KVClient();
|
|
54
|
+
await use(client);
|
|
55
|
+
},
|
|
51
56
|
customContextPageProvider: async ({ browser }, use, testInfo) => {
|
|
52
57
|
const contexts = [];
|
|
53
58
|
async function createContext(contextOptions = {}) {
|
package/docs/kv.md
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# KV Store
|
|
2
|
+
|
|
3
|
+
A simple key-value store for sharing data across test runs.
|
|
4
|
+
|
|
5
|
+
## Fixture usage
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
test("example", async ({ page, kv }) => {
|
|
9
|
+
await kv.set("my-key", { some: "data" }, 3600);
|
|
10
|
+
const value = await kv.get<{ some: string }>("my-key");
|
|
11
|
+
});
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Standalone usage
|
|
15
|
+
|
|
16
|
+
```ts
|
|
17
|
+
import { KVClient } from "@empiricalrun/playwright-utils";
|
|
18
|
+
|
|
19
|
+
const kv = new KVClient();
|
|
20
|
+
await kv.set("my-key", { some: "data" }, 3600); // ttl in seconds, max 2 days
|
|
21
|
+
const value = await kv.get<{ some: string }>("my-key");
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Requirements
|
|
25
|
+
|
|
26
|
+
- `EMPIRICALRUN_API_KEY` environment variable must be set
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@empiricalrun/playwright-utils",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.32.0",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"registry": "https://registry.npmjs.org/",
|
|
6
6
|
"access": "public"
|
|
@@ -40,12 +40,11 @@
|
|
|
40
40
|
"async-retry": "^1.3.3",
|
|
41
41
|
"authenticator": "^1.1.5",
|
|
42
42
|
"console-log-level": "^1.4.1",
|
|
43
|
-
"mailosaur": "^8.6.1",
|
|
44
43
|
"puppeteer-extra-plugin-recaptcha": "^3.6.8",
|
|
45
44
|
"rimraf": "^6.0.1",
|
|
46
|
-
"@empiricalrun/llm": "^0.25.
|
|
45
|
+
"@empiricalrun/llm": "^0.25.1",
|
|
47
46
|
"@empiricalrun/r2-uploader": "^0.4.0",
|
|
48
|
-
"@empiricalrun/test-gen": "^0.78.
|
|
47
|
+
"@empiricalrun/test-gen": "^0.78.2"
|
|
49
48
|
},
|
|
50
49
|
"scripts": {
|
|
51
50
|
"dev": "tsc --build --watch",
|
package/tsconfig.tsbuildinfo
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"root":["./src/email.ts","./src/index.ts","./src/logger.ts","./src/playwright-extensions.ts","./src/auth/google.ts","./src/auth/index.ts","./src/auth/types.ts","./src/captcha/index.ts","./src/captcha/vision.ts","./src/config/index.ts","./src/config/devices/types.ts","./src/overlay-tests/cache.spec.ts","./src/overlay-tests/click.spec.ts","./src/overlay-tests/fixtures.ts","./src/overlay-tests/patch.spec.ts","./src/reporter/empirical-reporter.ts","./src/reporter/uploader.ts","./src/reporter/util.ts","./src/test/constants.ts","./src/test/expect.ts","./src/test/index.ts","./src/test/types.ts","./src/test/video-labels.ts","./src/test/scripts/agent-capabilities.ts","./src/test/scripts/index.ts","./src/test/scripts/locator-highlights.ts","./src/test/scripts/locator-vision.ts","./src/test/scripts/mouse-pointer.ts","./src/test/scripts/types.ts","./src/test/scripts/pw-locator-patch/dismiss-overlays/cache.ts","./src/test/scripts/pw-locator-patch/dismiss-overlays/index.ts","./src/test/scripts/pw-locator-patch/dismiss-overlays/prompt.ts","./src/test/scripts/pw-locator-patch/dismiss-overlays/types.ts","./src/test/scripts/pw-locator-patch/dismiss-overlays/utils.ts","./src/test/scripts/pw-locator-patch/highlight/click.ts","./src/test/scripts/pw-locator-patch/highlight/expect.ts","./src/test/scripts/pw-locator-patch/highlight/hover.ts","./src/test/scripts/pw-locator-patch/highlight/inner-text.ts","./src/test/scripts/pw-locator-patch/highlight/input-value.ts","./src/test/scripts/pw-locator-patch/highlight/is-checked.ts","./src/test/scripts/pw-locator-patch/highlight/is-disabled.ts","./src/test/scripts/pw-locator-patch/highlight/is-editable.ts","./src/test/scripts/pw-locator-patch/highlight/text-content.ts","./src/test/scripts/pw-locator-patch/utils/index.ts","./src/test/scripts/pw-locator-patch/vision/query.ts"],"version":"5.8.3"}
|
|
1
|
+
{"root":["./src/email.ts","./src/index.ts","./src/kv.ts","./src/logger.ts","./src/mailosaur-client.ts","./src/playwright-extensions.ts","./src/auth/google.ts","./src/auth/index.ts","./src/auth/types.ts","./src/captcha/index.ts","./src/captcha/vision.ts","./src/config/index.ts","./src/config/devices/types.ts","./src/overlay-tests/cache.spec.ts","./src/overlay-tests/click.spec.ts","./src/overlay-tests/fixtures.ts","./src/overlay-tests/patch.spec.ts","./src/reporter/empirical-reporter.ts","./src/reporter/uploader.ts","./src/reporter/util.ts","./src/test/constants.ts","./src/test/expect.ts","./src/test/index.ts","./src/test/types.ts","./src/test/video-labels.ts","./src/test/scripts/agent-capabilities.ts","./src/test/scripts/index.ts","./src/test/scripts/locator-highlights.ts","./src/test/scripts/locator-vision.ts","./src/test/scripts/mouse-pointer.ts","./src/test/scripts/types.ts","./src/test/scripts/pw-locator-patch/dismiss-overlays/cache.ts","./src/test/scripts/pw-locator-patch/dismiss-overlays/index.ts","./src/test/scripts/pw-locator-patch/dismiss-overlays/prompt.ts","./src/test/scripts/pw-locator-patch/dismiss-overlays/types.ts","./src/test/scripts/pw-locator-patch/dismiss-overlays/utils.ts","./src/test/scripts/pw-locator-patch/highlight/click.ts","./src/test/scripts/pw-locator-patch/highlight/expect.ts","./src/test/scripts/pw-locator-patch/highlight/hover.ts","./src/test/scripts/pw-locator-patch/highlight/inner-text.ts","./src/test/scripts/pw-locator-patch/highlight/input-value.ts","./src/test/scripts/pw-locator-patch/highlight/is-checked.ts","./src/test/scripts/pw-locator-patch/highlight/is-disabled.ts","./src/test/scripts/pw-locator-patch/highlight/is-editable.ts","./src/test/scripts/pw-locator-patch/highlight/text-content.ts","./src/test/scripts/pw-locator-patch/utils/index.ts","./src/test/scripts/pw-locator-patch/vision/query.ts"],"version":"5.8.3"}
|