@empiricalrun/playwright-utils 0.47.2 → 0.47.4
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 +14 -0
- package/dist/email.d.ts +4 -2
- package/dist/email.d.ts.map +1 -1
- package/dist/email.js +19 -27
- package/dist/inbox-client.d.ts +32 -0
- package/dist/inbox-client.d.ts.map +1 -0
- package/dist/inbox-client.js +102 -0
- package/dist/mailosaur-client.d.ts +4 -32
- package/dist/mailosaur-client.d.ts.map +1 -1
- package/dist/mailosaur-client.js +9 -34
- package/dist/types.d.ts +7 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/docs/email.md +31 -2
- package/package.json +3 -3
- package/tsconfig.tsbuildinfo +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# @empiricalrun/playwright-utils
|
|
2
2
|
|
|
3
|
+
## 0.47.4
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 2d6f146: fix: incorrect types bundled
|
|
8
|
+
- Updated dependencies [2d6f146]
|
|
9
|
+
- @empiricalrun/dashboard-client@0.2.1
|
|
10
|
+
|
|
11
|
+
## 0.47.3
|
|
12
|
+
|
|
13
|
+
### Patch Changes
|
|
14
|
+
|
|
15
|
+
- ceeb466: feat: new provider option in email client
|
|
16
|
+
|
|
3
17
|
## 0.47.2
|
|
4
18
|
|
|
5
19
|
### Patch Changes
|
package/dist/email.d.ts
CHANGED
|
@@ -1,18 +1,20 @@
|
|
|
1
|
+
import { type InboxEmail } from "./inbox-client";
|
|
1
2
|
import { type MailosaurEmail } from "./mailosaur-client";
|
|
2
|
-
export type Email = MailosaurEmail;
|
|
3
|
+
export type Email = MailosaurEmail | InboxEmail;
|
|
3
4
|
type QueryFilter = {
|
|
4
5
|
from?: string;
|
|
5
6
|
subject?: string;
|
|
6
7
|
body?: string;
|
|
7
8
|
};
|
|
9
|
+
type EmailProvider = "inbox" | "mailosaur";
|
|
8
10
|
type EmailClientOptions = {
|
|
9
11
|
timeout?: number;
|
|
10
12
|
emailId?: string;
|
|
13
|
+
provider?: EmailProvider;
|
|
11
14
|
};
|
|
12
15
|
export declare class EmailClient {
|
|
13
16
|
address: string;
|
|
14
17
|
timeout: number;
|
|
15
|
-
serverId: string;
|
|
16
18
|
private client;
|
|
17
19
|
constructor(opts?: EmailClientOptions);
|
|
18
20
|
getAddress(): string;
|
package/dist/email.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"email.d.ts","sourceRoot":"","sources":["../src/email.ts"],"names":[],"mappings":"AAEA,OAAO,EAAmB,KAAK,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAE1E,MAAM,MAAM,KAAK,GAAG,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"email.d.ts","sourceRoot":"","sources":["../src/email.ts"],"names":[],"mappings":"AAEA,OAAO,EAAe,KAAK,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAC9D,OAAO,EAAmB,KAAK,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAE1E,MAAM,MAAM,KAAK,GAAG,cAAc,GAAG,UAAU,CAAC;AAEhD,KAAK,WAAW,GAAG;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,CAAC;AAMF,KAAK,aAAa,GAAG,OAAO,GAAG,WAAW,CAAC;AAM3C,KAAK,kBAAkB,GAAG;IACxB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,aAAa,CAAC;CAC1B,CAAC;AAEF,qBAAa,WAAW;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,MAAM,CAAsB;gBAExB,IAAI,GAAE,kBAAuB;IA+BzC,UAAU,IAAI,MAAM;IAId,YAAY,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,KAAK,CAAC;CAGzD"}
|
package/dist/email.js
CHANGED
|
@@ -2,47 +2,39 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.EmailClient = void 0;
|
|
4
4
|
const dashboard_client_1 = require("@empiricalrun/dashboard-client");
|
|
5
|
+
const inbox_client_1 = require("./inbox-client");
|
|
5
6
|
const mailosaur_client_1 = require("./mailosaur-client");
|
|
6
7
|
const DEFAULT_TIMEOUT = 30_000;
|
|
7
|
-
const
|
|
8
|
+
const MAILOSAUR_SERVER_ID = "pnyrwq5o";
|
|
9
|
+
const INBOX_EMAIL_DOMAIN = "empiricalrun.email";
|
|
8
10
|
class EmailClient {
|
|
9
11
|
address;
|
|
10
12
|
timeout;
|
|
11
|
-
serverId = SERVER_ID;
|
|
12
13
|
client;
|
|
13
14
|
constructor(opts = {}) {
|
|
14
15
|
const { timeout = DEFAULT_TIMEOUT, emailId: knownEmailId } = opts;
|
|
15
16
|
this.timeout = timeout;
|
|
16
|
-
const
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
17
|
+
const provider = opts.provider || "mailosaur";
|
|
18
|
+
const apiClient = new dashboard_client_1.DashboardAPIClient({
|
|
19
|
+
authType: "project-api-key",
|
|
20
|
+
});
|
|
21
|
+
const emailId = knownEmailId ||
|
|
22
|
+
[...Array(7)].map(() => Math.random().toString(36)[2]).join("");
|
|
23
|
+
if (provider === "inbox") {
|
|
24
|
+
this.client = new inbox_client_1.InboxClient({ apiClient, timeout: this.timeout });
|
|
25
|
+
this.address = emailId.includes("@")
|
|
26
|
+
? emailId
|
|
27
|
+
: `${emailId}@${INBOX_EMAIL_DOMAIN}`;
|
|
26
28
|
}
|
|
27
29
|
else {
|
|
28
|
-
const apiKey = process.env.MAILOSAUR_API_KEY;
|
|
29
|
-
if (!apiKey) {
|
|
30
|
-
throw new Error("Either EMPIRICALRUN_API_KEY or MAILOSAUR_API_KEY must be set");
|
|
31
|
-
}
|
|
32
30
|
this.client = new mailosaur_client_1.MailosaurClient({
|
|
33
|
-
|
|
34
|
-
serverId:
|
|
31
|
+
apiClient,
|
|
32
|
+
serverId: MAILOSAUR_SERVER_ID,
|
|
35
33
|
timeout: this.timeout,
|
|
36
34
|
});
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
if (emailId.includes("@")) {
|
|
41
|
-
this.address = emailId;
|
|
42
|
-
}
|
|
43
|
-
else {
|
|
44
|
-
const emailDomain = `${this.serverId}.mailosaur.net`;
|
|
45
|
-
this.address = `${emailId}@${emailDomain}`;
|
|
35
|
+
this.address = emailId.includes("@")
|
|
36
|
+
? emailId
|
|
37
|
+
: `${emailId}@${MAILOSAUR_SERVER_ID}.mailosaur.net`;
|
|
46
38
|
}
|
|
47
39
|
}
|
|
48
40
|
getAddress() {
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { IEmailProxyClient } from "./types";
|
|
2
|
+
type Link = {
|
|
3
|
+
text?: string;
|
|
4
|
+
href?: string;
|
|
5
|
+
};
|
|
6
|
+
export type InboxEmail = {
|
|
7
|
+
subject?: string;
|
|
8
|
+
text?: string;
|
|
9
|
+
html?: string;
|
|
10
|
+
links: Link[];
|
|
11
|
+
codes: string[];
|
|
12
|
+
};
|
|
13
|
+
type InboxClientOptions = {
|
|
14
|
+
apiClient: IEmailProxyClient;
|
|
15
|
+
timeout: number;
|
|
16
|
+
};
|
|
17
|
+
export declare class InboxClient {
|
|
18
|
+
private apiClient;
|
|
19
|
+
private timeout;
|
|
20
|
+
constructor(opts: InboxClientOptions);
|
|
21
|
+
waitForEmail(address: string, filter?: {
|
|
22
|
+
from?: string;
|
|
23
|
+
subject?: string;
|
|
24
|
+
body?: string;
|
|
25
|
+
}): Promise<InboxEmail>;
|
|
26
|
+
private pollForMessage;
|
|
27
|
+
private parseMessage;
|
|
28
|
+
private extractLinks;
|
|
29
|
+
private extractCodes;
|
|
30
|
+
}
|
|
31
|
+
export {};
|
|
32
|
+
//# sourceMappingURL=inbox-client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"inbox-client.d.ts","sourceRoot":"","sources":["../src/inbox-client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAEjD,KAAK,IAAI,GAAG;IACV,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG;IACvB,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;AAmBF,KAAK,kBAAkB,GAAG;IACxB,SAAS,EAAE,iBAAiB,CAAC;IAC7B,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,qBAAa,WAAW;IACtB,OAAO,CAAC,SAAS,CAAoB;IACrC,OAAO,CAAC,OAAO,CAAS;gBAEZ,IAAI,EAAE,kBAAkB;IAK9B,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,UAAU,CAAC;YASR,cAAc;IAoD5B,OAAO,CAAC,YAAY;IAapB,OAAO,CAAC,YAAY;IAwBpB,OAAO,CAAC,YAAY;CAYrB"}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.InboxClient = void 0;
|
|
4
|
+
const DEFAULT_POLL_DELAY = 1000;
|
|
5
|
+
class InboxClient {
|
|
6
|
+
apiClient;
|
|
7
|
+
timeout;
|
|
8
|
+
constructor(opts) {
|
|
9
|
+
this.apiClient = opts.apiClient;
|
|
10
|
+
this.timeout = opts.timeout;
|
|
11
|
+
}
|
|
12
|
+
async waitForEmail(address, filter) {
|
|
13
|
+
const now = new Date();
|
|
14
|
+
const bufferPeriod = 5_000;
|
|
15
|
+
const receivedAfter = new Date(now.getTime() - bufferPeriod);
|
|
16
|
+
const message = await this.pollForMessage(address, receivedAfter, filter);
|
|
17
|
+
return this.parseMessage(message);
|
|
18
|
+
}
|
|
19
|
+
async pollForMessage(address, receivedAfter, filter) {
|
|
20
|
+
const startTime = Date.now();
|
|
21
|
+
while (true) {
|
|
22
|
+
const params = new URLSearchParams({
|
|
23
|
+
to: address,
|
|
24
|
+
received_after: receivedAfter.toISOString(),
|
|
25
|
+
});
|
|
26
|
+
if (filter?.from)
|
|
27
|
+
params.set("from", filter.from);
|
|
28
|
+
if (filter?.subject)
|
|
29
|
+
params.set("subject", filter.subject);
|
|
30
|
+
if (filter?.body)
|
|
31
|
+
params.set("body", filter.body);
|
|
32
|
+
const path = `/api/messages?${params.toString()}`;
|
|
33
|
+
let result;
|
|
34
|
+
try {
|
|
35
|
+
result = await this.apiClient.request("/api/inbox/proxy", {
|
|
36
|
+
method: "POST",
|
|
37
|
+
body: { method: "GET", path },
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
catch (err) {
|
|
41
|
+
console.error("Inbox network error during search:", err);
|
|
42
|
+
if (err instanceof Error && "status" in err && err.status === 401) {
|
|
43
|
+
throw new Error("Email provider error: Authentication error");
|
|
44
|
+
}
|
|
45
|
+
throw new Error("Email provider error: Network request failed");
|
|
46
|
+
}
|
|
47
|
+
const message = result.data?.data?.[0];
|
|
48
|
+
if (message) {
|
|
49
|
+
return message;
|
|
50
|
+
}
|
|
51
|
+
const elapsed = Date.now() - startTime;
|
|
52
|
+
if (elapsed + DEFAULT_POLL_DELAY > this.timeout) {
|
|
53
|
+
throw new Error(`Email not received at ${address} within ${this.timeout}ms`);
|
|
54
|
+
}
|
|
55
|
+
await new Promise((resolve) => setTimeout(resolve, DEFAULT_POLL_DELAY));
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
parseMessage(message) {
|
|
59
|
+
const links = this.extractLinks(message.html_body, message.text_body);
|
|
60
|
+
const codes = this.extractCodes(message.html_body, message.text_body);
|
|
61
|
+
return {
|
|
62
|
+
subject: message.subject || undefined,
|
|
63
|
+
text: message.text_body || undefined,
|
|
64
|
+
html: message.html_body || undefined,
|
|
65
|
+
links,
|
|
66
|
+
codes,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
extractLinks(html, text) {
|
|
70
|
+
const links = [];
|
|
71
|
+
if (html) {
|
|
72
|
+
const hrefRegex = /<a[^>]+href="([^"]*)"[^>]*>(.*?)<\/a>/gi;
|
|
73
|
+
let match;
|
|
74
|
+
while ((match = hrefRegex.exec(html)) !== null) {
|
|
75
|
+
links.push({ href: match[1], text: match[2]?.replace(/<[^>]*>/g, "") });
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
if (text) {
|
|
79
|
+
const urlRegex = /https?:\/\/[^\s<>"')\]]+/g;
|
|
80
|
+
let match;
|
|
81
|
+
while ((match = urlRegex.exec(text)) !== null) {
|
|
82
|
+
if (!links.some((l) => l.href === match[0])) {
|
|
83
|
+
links.push({ href: match[0] });
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return links;
|
|
88
|
+
}
|
|
89
|
+
extractCodes(html, text) {
|
|
90
|
+
const codes = [];
|
|
91
|
+
const content = text || html || "";
|
|
92
|
+
const codeRegex = /\b(\d{4,8})\b/g;
|
|
93
|
+
let match;
|
|
94
|
+
while ((match = codeRegex.exec(content)) !== null) {
|
|
95
|
+
if (!codes.includes(match[1])) {
|
|
96
|
+
codes.push(match[1]);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return codes;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
exports.InboxClient = InboxClient;
|
|
@@ -1,26 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
request<T>(endpoint: string, options: {
|
|
3
|
-
params?: Record<string, string>;
|
|
4
|
-
body?: Record<string, unknown>;
|
|
5
|
-
method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
|
|
6
|
-
}): Promise<T>;
|
|
7
|
-
callGitHubProxy<T>(opts: {
|
|
8
|
-
method: "GET" | "POST" | "PATCH" | "PUT" | "DELETE";
|
|
9
|
-
url: string;
|
|
10
|
-
body?: unknown;
|
|
11
|
-
}): Promise<T>;
|
|
12
|
-
callMailosaurProxy<T>(opts: {
|
|
13
|
-
method: "GET" | "POST" | "PATCH" | "PUT" | "DELETE";
|
|
14
|
-
path: string;
|
|
15
|
-
body?: unknown;
|
|
16
|
-
}): Promise<T>;
|
|
17
|
-
callWebhookSiteProxy<T>(opts: {
|
|
18
|
-
method: "GET" | "POST" | "PATCH" | "PUT" | "DELETE";
|
|
19
|
-
path: string;
|
|
20
|
-
body?: unknown;
|
|
21
|
-
}): Promise<T>;
|
|
22
|
-
getBaseUrl(): string;
|
|
23
|
-
}
|
|
1
|
+
import type { IEmailProxyClient } from "./types";
|
|
24
2
|
type Link = {
|
|
25
3
|
text?: string;
|
|
26
4
|
href?: string;
|
|
@@ -33,18 +11,12 @@ export type MailosaurEmail = {
|
|
|
33
11
|
codes: string[];
|
|
34
12
|
};
|
|
35
13
|
type MailosaurClientOptions = {
|
|
14
|
+
apiClient: IEmailProxyClient;
|
|
36
15
|
serverId: string;
|
|
37
16
|
timeout: number;
|
|
38
|
-
}
|
|
39
|
-
apiKey: string;
|
|
40
|
-
apiClient?: never;
|
|
41
|
-
} | {
|
|
42
|
-
apiKey?: never;
|
|
43
|
-
apiClient: IDashboardAPIClient;
|
|
44
|
-
});
|
|
17
|
+
};
|
|
45
18
|
export declare class MailosaurClient {
|
|
46
|
-
private
|
|
47
|
-
private apiClient?;
|
|
19
|
+
private apiClient;
|
|
48
20
|
private serverId;
|
|
49
21
|
private timeout;
|
|
50
22
|
constructor(opts: MailosaurClientOptions);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mailosaur-client.d.ts","sourceRoot":"","sources":["../src/mailosaur-client.ts"],"names":[],"mappings":"AAAA,
|
|
1
|
+
{"version":3,"file":"mailosaur-client.d.ts","sourceRoot":"","sources":["../src/mailosaur-client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAEjD,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;AAiCF,KAAK,sBAAsB,GAAG;IAC5B,SAAS,EAAE,iBAAiB,CAAC;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,qBAAa,eAAe;IAC1B,OAAO,CAAC,SAAS,CAAoB;IACrC,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,OAAO,CAAS;gBAEZ,IAAI,EAAE,sBAAsB;YAM1B,WAAW;IAuBnB,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;YA2EjB,cAAc;IA+B5B,OAAO,CAAC,kBAAkB;IAmB1B,OAAO,CAAC,KAAK;CAGd"}
|
package/dist/mailosaur-client.js
CHANGED
|
@@ -1,51 +1,26 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.MailosaurClient = void 0;
|
|
4
|
-
const MAILOSAUR_BASE_URL = "https://mailosaur.com/api";
|
|
5
4
|
const DEFAULT_POLL_DELAY = 1000;
|
|
6
5
|
class MailosaurClient {
|
|
7
|
-
apiKey;
|
|
8
6
|
apiClient;
|
|
9
7
|
serverId;
|
|
10
8
|
timeout;
|
|
11
9
|
constructor(opts) {
|
|
12
10
|
this.serverId = opts.serverId;
|
|
13
11
|
this.timeout = opts.timeout;
|
|
14
|
-
|
|
15
|
-
this.apiClient = opts.apiClient;
|
|
16
|
-
}
|
|
17
|
-
else {
|
|
18
|
-
this.apiKey = opts.apiKey;
|
|
19
|
-
}
|
|
12
|
+
this.apiClient = opts.apiClient;
|
|
20
13
|
}
|
|
21
14
|
async makeRequest(method, path, body) {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
path,
|
|
26
|
-
body,
|
|
27
|
-
});
|
|
28
|
-
return {
|
|
29
|
-
data: result.data,
|
|
30
|
-
headers: result.headers || {},
|
|
31
|
-
status: 200,
|
|
32
|
-
};
|
|
33
|
-
}
|
|
34
|
-
const url = `${MAILOSAUR_BASE_URL}${path}`;
|
|
35
|
-
const response = await fetch(url, {
|
|
36
|
-
method,
|
|
37
|
-
headers: {
|
|
38
|
-
Authorization: `Basic ${Buffer.from(`${this.apiKey}:`).toString("base64")}`,
|
|
39
|
-
"Content-Type": "application/json",
|
|
40
|
-
},
|
|
41
|
-
body: body ? JSON.stringify(body) : undefined,
|
|
15
|
+
const result = await this.apiClient.request("/api/mailosaur/proxy", {
|
|
16
|
+
method: "POST",
|
|
17
|
+
body: { method, path, body },
|
|
42
18
|
});
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
return { data, headers: responseHeaders, status: response.status };
|
|
19
|
+
return {
|
|
20
|
+
data: result.data,
|
|
21
|
+
headers: result.headers || {},
|
|
22
|
+
status: 200,
|
|
23
|
+
};
|
|
49
24
|
}
|
|
50
25
|
async waitForEmail(address, filter) {
|
|
51
26
|
const now = new Date();
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,iBAAiB;IAChC,OAAO,CAAC,CAAC,EACP,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE;QACP,MAAM,EAAE,KAAK,GAAG,MAAM,GAAG,KAAK,GAAG,OAAO,GAAG,QAAQ,CAAC;QACpD,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;KAChC,GACA,OAAO,CAAC,CAAC,CAAC,CAAC;CACf"}
|
package/dist/types.js
ADDED
package/docs/email.md
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
# Email automation
|
|
2
2
|
|
|
3
|
+
## Providers
|
|
4
|
+
|
|
5
|
+
The `EmailClient` supports two email providers:
|
|
6
|
+
|
|
7
|
+
- **`mailosaur`** (default) — Uses Mailosaur via the dashboard proxy
|
|
8
|
+
- **`inbox`** — Uses our self-hosted Cloudflare Worker on `empiricalrun.email`
|
|
9
|
+
|
|
10
|
+
Both providers are accessed through the dashboard proxy, so only `EMPIRICALRUN_API_KEY` is required.
|
|
11
|
+
|
|
3
12
|
## Example usage
|
|
4
13
|
|
|
5
14
|
### Dynamic email
|
|
@@ -12,7 +21,7 @@ import { EmailClient } from "@empiricalrun/playwright-utils";
|
|
|
12
21
|
import { expect } from "@playwright/test";
|
|
13
22
|
|
|
14
23
|
const client = new EmailClient();
|
|
15
|
-
const address = client.getAddress();
|
|
24
|
+
const address = client.getAddress(); // e.g. abc1234@empiricalrun.email
|
|
16
25
|
|
|
17
26
|
// Input the `address` in the application
|
|
18
27
|
// that sends the email.
|
|
@@ -30,7 +39,7 @@ This uses a known (static) email that can be used to login
|
|
|
30
39
|
into an application.
|
|
31
40
|
|
|
32
41
|
This needs an email id (e.g. `test-login-user`). The email id
|
|
33
|
-
is appended with the domain (
|
|
42
|
+
is appended with the domain (`empiricalrun.email`) to get the full
|
|
34
43
|
email address.
|
|
35
44
|
|
|
36
45
|
```ts
|
|
@@ -47,3 +56,23 @@ const email = await client.waitForEmail();
|
|
|
47
56
|
// Get login OTP
|
|
48
57
|
const loginCode = email.codes[0];
|
|
49
58
|
```
|
|
59
|
+
|
|
60
|
+
### Using the inbox provider
|
|
61
|
+
|
|
62
|
+
Pass `provider: "inbox"` to use the self-hosted inbox provider.
|
|
63
|
+
|
|
64
|
+
```ts
|
|
65
|
+
const client = new EmailClient({ provider: "inbox" });
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Filtering emails
|
|
69
|
+
|
|
70
|
+
You can filter emails by sender, subject, or body content:
|
|
71
|
+
|
|
72
|
+
```ts
|
|
73
|
+
const email = await client.waitForEmail({
|
|
74
|
+
from: "noreply@example.com",
|
|
75
|
+
subject: "Verify your email",
|
|
76
|
+
body: "verification code",
|
|
77
|
+
});
|
|
78
|
+
```
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@empiricalrun/playwright-utils",
|
|
3
|
-
"version": "0.47.
|
|
3
|
+
"version": "0.47.4",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"registry": "https://registry.npmjs.org/",
|
|
6
6
|
"access": "public"
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
"rimraf": "^6.0.1",
|
|
45
45
|
"ts-morph": "^23.0.0",
|
|
46
46
|
"@empiricalrun/cua": "^0.3.0",
|
|
47
|
-
"@empiricalrun/dashboard-client": "^0.2.
|
|
47
|
+
"@empiricalrun/dashboard-client": "^0.2.1",
|
|
48
48
|
"@empiricalrun/llm": "^0.26.0",
|
|
49
49
|
"@empiricalrun/r2-uploader": "^0.9.1",
|
|
50
50
|
"@empiricalrun/reporter": "^0.28.1"
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
"scripts": {
|
|
53
53
|
"dev": "tsc --build --watch",
|
|
54
54
|
"build": "tsc --build",
|
|
55
|
-
"clean": "tsc --build --clean",
|
|
55
|
+
"clean": "rm -rf dist && tsc --build --clean",
|
|
56
56
|
"lint": "biome check --unsafe",
|
|
57
57
|
"test": "vitest run",
|
|
58
58
|
"test-browser": "npx playwright test",
|
package/tsconfig.tsbuildinfo
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"root":["./src/email.ts","./src/index.ts","./src/kv.ts","./src/logger.ts","./src/mailosaur-client.ts","./src/playwright-extensions.ts","./src/postgres.ts","./src/telemetry.ts","./src/webhook.ts","./src/auth/google.ts","./src/auth/index.ts","./src/auth/types.ts","./src/captcha/index.ts","./src/config/index.ts","./src/config/proxy.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/attachment-cleanup-test-reporter.ts","./src/reporter/attachment-cleanup.ts","./src/reporter/blob-utils.ts","./src/reporter/empirical-reporter.ts","./src/reporter/failing-line.ts","./src/reporter/harness.ts","./src/reporter/ibr-utils.ts","./src/reporter/incremental-blob-reporter.ts","./src/reporter/lifecycle-events.ts","./src/reporter/local-test.ts","./src/reporter/reporter-state.ts","./src/reporter/uploader.ts","./src/reporter/util.ts","./src/test/constants.ts","./src/test/coverage.ts","./src/test/index.ts","./src/test/types.ts","./src/test/video-labels.ts","./src/test/expect/index.ts","./src/test/expect/types.ts","./src/test/expect/visual.ts","./src/test/expect/webhook.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/inbox-client.ts","./src/index.ts","./src/kv.ts","./src/logger.ts","./src/mailosaur-client.ts","./src/playwright-extensions.ts","./src/postgres.ts","./src/telemetry.ts","./src/types.ts","./src/webhook.ts","./src/auth/google.ts","./src/auth/index.ts","./src/auth/types.ts","./src/captcha/index.ts","./src/config/index.ts","./src/config/proxy.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/attachment-cleanup-test-reporter.ts","./src/reporter/attachment-cleanup.ts","./src/reporter/blob-utils.ts","./src/reporter/empirical-reporter.ts","./src/reporter/failing-line.ts","./src/reporter/harness.ts","./src/reporter/ibr-utils.ts","./src/reporter/incremental-blob-reporter.ts","./src/reporter/lifecycle-events.ts","./src/reporter/local-test.ts","./src/reporter/reporter-state.ts","./src/reporter/uploader.ts","./src/reporter/util.ts","./src/test/constants.ts","./src/test/coverage.ts","./src/test/index.ts","./src/test/types.ts","./src/test/video-labels.ts","./src/test/expect/index.ts","./src/test/expect/types.ts","./src/test/expect/visual.ts","./src/test/expect/webhook.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"}
|