e2e-mail 0.0.1

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Eric Gregoire
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,36 @@
1
+ # e2e-mail
2
+ <p align="center">
3
+ <img width="256" height="256" alt="E2E Mail Logo" src="https://github.com/user-attachments/assets/7954e1d3-2be0-46f0-8c80-194b82bc0156" />
4
+ </p>
5
+
6
+ <h2 align="center">
7
+ Zero-config email testing for major E2E frameworks.
8
+ </h3>
9
+
10
+ <p align="center">
11
+ ๐Ÿšซ API Key | ๐Ÿšซ Monthly Bill | ๐Ÿšซ Domain Purchase | ๐Ÿšซ Self-Destructing Inboxes
12
+ </p>
13
+
14
+ <p align="center">
15
+ โœ… Simply Free Email Testing
16
+ </p>
17
+
18
+ E2E Mail is on a mission to make email testing as simple, reliable, and accessible as the rest of your test suite.
19
+
20
+ Most email testing solutions require paid APIs, custom domains, inbox management, vendor lock-in, or usage limits that make scaling difficult for open source projects, startups, and individual developers. E2E Mail takes a different approach.
21
+
22
+ By leveraging free temporary email infrastructure behind a developer-friendly API, E2E Mail provides persistent, testable inboxes without the setup overhead. No accounts to create. No billing to configure. No DNS records to manage.
23
+
24
+ Whether you're testing account verification flows, password resets, magic links, invitations, or transactional emails, E2E Mail helps you validate real email delivery inside your end-to-end tests with just a few lines of code.
25
+
26
+ ### Why E2E Mail?
27
+
28
+ ๐Ÿ†“ Free to use
29
+
30
+ ๐Ÿงช Built for automated testing
31
+
32
+ ๐Ÿ”„ Works with major E2E frameworks
33
+
34
+ ๐Ÿ“ฌ Real inboxes for real email flows
35
+
36
+ Email testing shouldn't require a subscription. It should just work.
@@ -0,0 +1,32 @@
1
+ import { E as s } from "./index-DalPChOM.js";
2
+ let i = null;
3
+ Cypress.Commands.add("initializeMailbox", (t, a) => (cy.log(`Creating mailbox ${t}`), cy.then(async () => {
4
+ i = new s(t, a), await i.initialize();
5
+ })));
6
+ Cypress.Commands.add("removeMailbox", () => cy.then(async () => {
7
+ if (!i)
8
+ throw new Error(
9
+ "Mailbox client not initialized. Call cy.initializeMailbox() first."
10
+ );
11
+ await i.dispose();
12
+ }));
13
+ Cypress.Commands.add("searchMailbox", (t, a = {}) => {
14
+ const {
15
+ timeout: l = Cypress.config().defaultCommandTimeout ?? 4e3,
16
+ autoDelete: r
17
+ } = a;
18
+ return cy.then({ timeout: l + 1e3 }, async () => {
19
+ if (!i)
20
+ throw new Error(
21
+ "Mailbox client not initialized. Call cy.initializeMailbox() first."
22
+ );
23
+ const e = await i.pollMessages(t, {
24
+ timeout: l,
25
+ autoDelete: r
26
+ }), o = Array.isArray(e.html) ? e.html[0] : e.html;
27
+ return o && cy.document().then((n) => {
28
+ n.open(), n.write(String(o)), n.close();
29
+ }), e;
30
+ });
31
+ });
32
+ //# sourceMappingURL=cypress.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cypress.js","sources":["../src/cypress/index.ts"],"sourcesContent":["import type { PollingOptions, SearchFilters } from \"../core/mailtm/mailtm\";\nimport type { components } from \"../core/api/mailtm-api.d\";\nimport { E2EMailClient } from \"../core/mailtm\";\n\nexport type { SearchFilters } from \"../core/mailtm/mailtm\";\n\ntype Message = components[\"schemas\"][\"Message-message.read\"];\n\ndeclare global {\n namespace Cypress {\n interface Chainable {\n /**\n * Initialize mailbox session\n * @param address Email address\n * @param password Account password\n */\n initializeMailbox(address: string, password: string): void;\n\n /**\n * Get most recent inbox match for an existing or new mail account\n * @param filters Optional search filters\n * @param options Configuration for message polling\n */\n searchMailbox(\n filters?: SearchFilters,\n options?: PollingOptions,\n ): Chainable<Message>;\n\n /**\n * Delete the account mailbox and all its emails from the server\n */\n removeMailbox(): Chainable<void>;\n }\n }\n}\n\nlet mailClient: E2EMailClient | null = null;\n\nCypress.Commands.add(\"initializeMailbox\", (address, password) => {\n cy.log(`Creating mailbox ${address}`);\n\n return cy.then(async () => {\n mailClient = new E2EMailClient(address, password);\n await mailClient.initialize();\n });\n});\n\nCypress.Commands.add(\"removeMailbox\", () => {\n return cy.then(async () => {\n if (!mailClient) {\n throw new Error(\n \"Mailbox client not initialized. Call cy.initializeMailbox() first.\",\n );\n }\n\n await mailClient.dispose();\n });\n});\n\nCypress.Commands.add(\"searchMailbox\", (filters, options = {}) => {\n const {\n timeout = Cypress.config().defaultCommandTimeout ?? 4000,\n autoDelete,\n } = options;\n\n return cy.then({ timeout: timeout + 1000 }, async () => {\n if (!mailClient) {\n throw new Error(\n \"Mailbox client not initialized. Call cy.initializeMailbox() first.\",\n );\n }\n\n // Get most recent match\n const message = await mailClient.pollMessages(filters, {\n timeout,\n autoDelete,\n });\n const html = Array.isArray(message.html) ? message.html[0] : message.html;\n\n // Write HTML to Cypress DOM\n if (html) {\n cy.document().then((doc) => {\n doc.open();\n doc.write(String(html));\n doc.close();\n });\n }\n\n return message;\n });\n});\n"],"names":["mailClient","address","password","E2EMailClient","filters","options","timeout","autoDelete","message","html","doc"],"mappings":";AAoCA,IAAIA,IAAmC;AAEvC,QAAQ,SAAS,IAAI,qBAAqB,CAACC,GAASC,OAClD,GAAG,IAAI,oBAAoBD,CAAO,EAAE,GAE7B,GAAG,KAAK,YAAY;AACzB,EAAAD,IAAa,IAAIG,EAAcF,GAASC,CAAQ,GAChD,MAAMF,EAAW,WAAA;AACnB,CAAC,EACF;AAED,QAAQ,SAAS,IAAI,iBAAiB,MAC7B,GAAG,KAAK,YAAY;AACzB,MAAI,CAACA;AACH,UAAM,IAAI;AAAA,MACR;AAAA,IAAA;AAIJ,QAAMA,EAAW,QAAA;AACnB,CAAC,CACF;AAED,QAAQ,SAAS,IAAI,iBAAiB,CAACI,GAASC,IAAU,OAAO;AAC/D,QAAM;AAAA,IACJ,SAAAC,IAAU,QAAQ,OAAA,EAAS,yBAAyB;AAAA,IACpD,YAAAC;AAAA,EAAA,IACEF;AAEJ,SAAO,GAAG,KAAK,EAAE,SAASC,IAAU,IAAA,GAAQ,YAAY;AACtD,QAAI,CAACN;AACH,YAAM,IAAI;AAAA,QACR;AAAA,MAAA;AAKJ,UAAMQ,IAAU,MAAMR,EAAW,aAAaI,GAAS;AAAA,MACrD,SAAAE;AAAA,MACA,YAAAC;AAAA,IAAA,CACD,GACKE,IAAO,MAAM,QAAQD,EAAQ,IAAI,IAAIA,EAAQ,KAAK,CAAC,IAAIA,EAAQ;AAGrE,WAAIC,KACF,GAAG,SAAA,EAAW,KAAK,CAACC,MAAQ;AAC1B,MAAAA,EAAI,KAAA,GACJA,EAAI,MAAM,OAAOD,CAAI,CAAC,GACtBC,EAAI,MAAA;AAAA,IACN,CAAC,GAGIF;AAAA,EACT,CAAC;AACH,CAAC;"}
@@ -0,0 +1,214 @@
1
+ import h from "openapi-fetch";
2
+ const o = h({
3
+ baseUrl: "https://api.mail.tm"
4
+ });
5
+ async function p() {
6
+ const { data: s = [], error: t } = await o.GET("/domains", {
7
+ headers: {
8
+ Accept: "application/json"
9
+ }
10
+ });
11
+ return { data: s, error: t };
12
+ }
13
+ async function u({
14
+ address: s,
15
+ password: t
16
+ }) {
17
+ const { data: a, error: e } = await o.POST("/accounts", {
18
+ headers: {
19
+ Accept: "application/json"
20
+ },
21
+ body: {
22
+ address: s,
23
+ password: t
24
+ }
25
+ });
26
+ return { data: a, error: e };
27
+ }
28
+ async function w(s) {
29
+ const { data: t, error: a } = await o.GET("/me", {
30
+ headers: {
31
+ Accept: "application/json",
32
+ Authorization: `Bearer ${s}`
33
+ }
34
+ });
35
+ return { data: t, error: a };
36
+ }
37
+ async function l(s, t) {
38
+ const { data: a, error: e } = await o.DELETE("/accounts/{id}", {
39
+ params: {
40
+ path: {
41
+ id: t
42
+ }
43
+ },
44
+ headers: {
45
+ Accept: "application/json",
46
+ Authorization: `Bearer ${s}`
47
+ }
48
+ });
49
+ return { data: a, error: e };
50
+ }
51
+ async function m({
52
+ address: s,
53
+ password: t
54
+ }) {
55
+ const { data: a, error: e } = await o.POST("/token", {
56
+ headers: {
57
+ Accept: "application/json"
58
+ },
59
+ body: {
60
+ address: s,
61
+ password: t
62
+ }
63
+ });
64
+ return { data: a, error: e };
65
+ }
66
+ async function f(s) {
67
+ const { data: t, error: a } = await o.GET("/messages", {
68
+ headers: {
69
+ Accept: "application/json",
70
+ Authorization: `Bearer ${s}`
71
+ }
72
+ });
73
+ return { data: t, error: a };
74
+ }
75
+ async function g(s, t) {
76
+ const { data: a, error: e } = await o.GET("/messages/{id}", {
77
+ params: {
78
+ path: {
79
+ id: t
80
+ }
81
+ },
82
+ headers: {
83
+ Accept: "application/json",
84
+ Authorization: `Bearer ${s}`
85
+ }
86
+ });
87
+ return { data: a, error: e };
88
+ }
89
+ async function E(s, t) {
90
+ const { data: a, error: e } = await o.DELETE("/messages/{id}", {
91
+ params: {
92
+ path: {
93
+ id: t
94
+ }
95
+ },
96
+ headers: {
97
+ Accept: "application/json",
98
+ Authorization: `Bearer ${s}`
99
+ }
100
+ });
101
+ return { data: a, error: e };
102
+ }
103
+ const A = async (s) => {
104
+ const { data: t } = await p(), a = s.split("@")[1], e = t.filter((n) => n.isActive).map((n) => n.domain), i = e.find((n) => n === a);
105
+ if (!i)
106
+ throw new Error(
107
+ `E2E Mail - Domain @${a} cannot be used to initialize mailbox.
108
+
109
+ Try again with one of the following domains: ${e.join(", ")}`
110
+ );
111
+ return i;
112
+ };
113
+ function y(s = [], t) {
114
+ if (!t) return s;
115
+ const a = [];
116
+ if (t.recipient && a.push(
117
+ (e) => {
118
+ var i;
119
+ return (i = e.to) == null ? void 0 : i.some(
120
+ ({ address: n }) => n === t.recipient
121
+ );
122
+ }
123
+ ), t.sender && a.push(
124
+ (e) => {
125
+ var i;
126
+ return ((i = e.from) == null ? void 0 : i.address) === t.sender;
127
+ }
128
+ ), t.subject) {
129
+ const e = new RegExp(t.subject, "i");
130
+ a.push((i) => e.test(i.subject ?? ""));
131
+ }
132
+ if (t.createdAfter) {
133
+ const e = Date.parse(t.createdAfter);
134
+ a.push(
135
+ (i) => Date.parse(i.createdAt ?? "") > e
136
+ );
137
+ }
138
+ return s.filter(
139
+ (e) => a.every((i) => i(e))
140
+ );
141
+ }
142
+ const r = class r {
143
+ constructor(t, a) {
144
+ this.address = t, this.password = a;
145
+ }
146
+ async login() {
147
+ const { data: t } = await m({
148
+ address: this.address,
149
+ password: this.password
150
+ });
151
+ if (t != null && t.token) {
152
+ const { data: a } = await w(t.token);
153
+ this.token = t.token, this.accountId = a == null ? void 0 : a.id;
154
+ }
155
+ }
156
+ /** Initializes mailbox with existing credentials. If new and available account, it will be created automatically */
157
+ async initialize() {
158
+ if (await this.login(), !this.token) {
159
+ await A(this.address);
160
+ const { error: { detail: t = "" } = {} } = await u({
161
+ address: this.address,
162
+ password: this.password
163
+ });
164
+ if (t)
165
+ throw new Error(`E2E Mail - Failed to create Account.
166
+ ${t}`);
167
+ await this.login();
168
+ }
169
+ }
170
+ async dispose() {
171
+ if (!this.token || !this.accountId)
172
+ throw new Error(
173
+ `E2EMail - Deleting mailbox ${this.address} failed.
174
+
175
+ Password is incorrect or account doesn't exist.`
176
+ );
177
+ return await l(this.token, this.accountId);
178
+ }
179
+ /** Fetch and filter emails for the initialized mailbox and return most recent match */
180
+ async queryMessages(t) {
181
+ if (!this.token)
182
+ throw new Error(
183
+ `E2EMail - Searching mailbox ${this.address} failed.
184
+
185
+ Password is incorrect.`
186
+ );
187
+ const { data: a } = await f(this.token), [e] = y(a, t);
188
+ if (e) {
189
+ const { data: i } = await g(this.token, (e == null ? void 0 : e.id) || "");
190
+ return i;
191
+ }
192
+ }
193
+ /** Poll for messages in the initialized mailbox and timeout */
194
+ async pollMessages(t, { timeout: a = 3e4, autoDelete: e = !0 } = {}) {
195
+ const i = Date.now();
196
+ for (; Date.now() - i < a; ) {
197
+ const n = await this.queryMessages(t);
198
+ if (n)
199
+ return e && this.token && n.id && await E(this.token, n.id), n;
200
+ await new Promise(
201
+ (d) => setTimeout(d, r.POLL_INTERVAL)
202
+ );
203
+ }
204
+ throw new Error(
205
+ `E2E Mail - No message matching your criteria found found after ${a}ms.`
206
+ );
207
+ }
208
+ };
209
+ r.POLL_INTERVAL = 1e3;
210
+ let c = r;
211
+ export {
212
+ c as E
213
+ };
214
+ //# sourceMappingURL=index-DalPChOM.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index-DalPChOM.js","sources":["../src/core/api/mailtm-api.ts","../src/core/mailtm/utils.ts","../src/core/mailtm/index.ts"],"sourcesContent":["import createClient from \"openapi-fetch\";\nimport type { paths } from \"../api/mailtm-api.d\";\nimport type { RequestPayload } from \".\";\n\n/** OpenAPI client for MailTM */\nconst client = createClient<paths>({\n baseUrl: \"https://api.mail.tm\",\n});\n\n/**********\n * DOMAIN *\n *********/\nexport async function getDomains() {\n const { data = [], error } = await client.GET(\"/domains\", {\n headers: {\n Accept: \"application/json\",\n },\n });\n\n return { data, error };\n}\n\n/***********\n * ACCOUNT *\n **********/\nexport async function createAccount({\n address,\n password,\n}: RequestPayload<\"api_accounts_post\">) {\n const { data, error } = await client.POST(\"/accounts\", {\n headers: {\n Accept: \"application/json\",\n },\n body: {\n address,\n password,\n },\n });\n\n return { data, error };\n}\n\nexport async function getMe(token: string) {\n const { data, error } = await client.GET(\"/me\", {\n headers: {\n Accept: \"application/json\",\n Authorization: `Bearer ${token}`,\n },\n });\n\n return { data, error };\n}\n\nexport async function deleteAccount(token: string, id: string) {\n const { data, error } = await client.DELETE(\"/accounts/{id}\", {\n params: {\n path: {\n id,\n },\n },\n headers: {\n Accept: \"application/json\",\n Authorization: `Bearer ${token}`,\n },\n });\n\n return { data, error };\n}\n\n/*********\n * TOKEN *\n ********/\nexport async function createToken({\n address,\n password,\n}: RequestPayload<\"login_check_post\">) {\n const { data, error } = await client.POST(\"/token\", {\n headers: {\n Accept: \"application/json\",\n },\n body: {\n address,\n password,\n },\n });\n\n return { data, error };\n}\n\n/************\n * MESSAGES *\n ***********/\nexport async function getMessages(token: string) {\n const { data, error } = await client.GET(\"/messages\", {\n headers: {\n Accept: \"application/json\",\n Authorization: `Bearer ${token}`,\n },\n });\n\n return { data, error };\n}\n\nexport async function getMessage(token: string, id: string) {\n const { data, error } = await client.GET(\"/messages/{id}\", {\n params: {\n path: {\n id,\n },\n },\n headers: {\n Accept: \"application/json\",\n Authorization: `Bearer ${token}`,\n },\n });\n\n return { data, error };\n}\n\nexport async function deleteMessage(token: string, id: string) {\n const { data, error } = await client.DELETE(\"/messages/{id}\", {\n params: {\n path: {\n id,\n },\n },\n headers: {\n Accept: \"application/json\",\n Authorization: `Bearer ${token}`,\n },\n });\n\n return { data, error };\n}\n\n/**********\n * SOURCE *\n *********/\nexport async function getSource(token: string, id: string) {\n const { data, error } = await client.GET(\"/sources/{id}\", {\n params: {\n path: {\n id,\n },\n },\n headers: {\n Accept: \"application/json\",\n Authorization: `Bearer ${token}`,\n },\n });\n\n return { data, error };\n}\n","import { ResponseData } from \"../api\";\nimport { getDomains } from \"../api/mailtm-api\";\nimport type { SearchFilters } from \"./mailtm.d\";\n\n/** Determines if passed email domain matches any active domains from mail.tm, fail if not\n * @remarks This is intentionally bypassed if a token was successfully created for a previously established account whose domain was deactivated\n * @param address The email address for which to check domain validitity\n */\nexport const validateEmailDomain = async (address: string) => {\n const { data: domains } = await getDomains();\n const domain = address.split(\"@\")[1];\n const activeDomains = domains.filter((d) => d.isActive).map((d) => d.domain);\n const domainMatch = activeDomains.find((d) => d === domain);\n\n if (!domainMatch) {\n throw new Error(\n `E2E Mail - Domain @${domain} cannot be used to initialize mailbox.\\n\\nTry again with one of the following domains: ${activeDomains.join(\", \")}`,\n );\n }\n\n return domainMatch;\n};\n\n/** Search and filter account messages by message metadata or content\n * @param messages The messages returned from the account to filter\n * @param filters The filtering criteria to apply to the messages\n */\nexport function filterMessages(\n messages: ResponseData<\"api_messages_get_collection\", 200> = [],\n filters?: SearchFilters,\n) {\n if (!filters) return messages;\n\n const predicates: Array<\n (message: ResponseData<\"api_messages_id_get\", 200>) => boolean\n > = [];\n\n if (filters.recipient) {\n predicates.push((message) =>\n (message.to as any[])?.some(\n ({ address }) => address === filters.recipient,\n ),\n );\n }\n\n if (filters.sender) {\n predicates.push(\n (message) => (message.from as any)?.address === filters.sender,\n );\n }\n\n if (filters.subject) {\n const regex = new RegExp(filters.subject, \"i\");\n predicates.push((message) => regex.test(message.subject ?? \"\"));\n }\n\n if (filters.createdAfter) {\n const startTime = Date.parse(filters.createdAfter);\n predicates.push(\n (message) => Date.parse(message.createdAt ?? \"\") > startTime,\n );\n }\n\n return messages.filter((message) =>\n predicates.every((predicate) => predicate(message)),\n );\n}\n","import {\n createAccount,\n deleteAccount,\n createToken,\n getMessage,\n getMessages,\n getMe,\n deleteMessage,\n} from \"../api/mailtm-api\";\nimport type { PollingOptions, SearchFilters } from \"./mailtm.d\";\nimport { filterMessages, validateEmailDomain } from \"./utils\";\n\nexport class E2EMailClient {\n private static readonly POLL_INTERVAL = 1000;\n private address: string;\n private password: string;\n private token: string | undefined;\n private accountId: string | undefined;\n\n constructor(address: string, password: string) {\n this.address = address;\n this.password = password;\n }\n\n private async login() {\n const { data: auth } = await createToken({\n address: this.address,\n password: this.password,\n });\n\n if (auth?.token) {\n const { data: account } = await getMe(auth.token);\n\n this.token = auth.token;\n this.accountId = account?.id;\n }\n }\n\n /** Initializes mailbox with existing credentials. If new and available account, it will be created automatically */\n public async initialize() {\n await this.login();\n\n if (!this.token) {\n await validateEmailDomain(this.address);\n const { error: { detail = \"\" } = {} } = await createAccount({\n address: this.address,\n password: this.password,\n });\n\n if (detail)\n throw new Error(`E2E Mail - Failed to create Account.\\n${detail}`);\n\n await this.login();\n }\n }\n\n public async dispose() {\n if (!this.token || !this.accountId)\n throw new Error(\n `E2EMail - Deleting mailbox ${this.address} failed.\\n\\nPassword is incorrect or account doesn't exist.`,\n );\n\n return await deleteAccount(this.token, this.accountId);\n }\n\n /** Fetch and filter emails for the initialized mailbox and return most recent match */\n private async queryMessages(filters?: SearchFilters) {\n if (!this.token)\n throw new Error(\n `E2EMail - Searching mailbox ${this.address} failed.\\n\\nPassword is incorrect.`,\n );\n\n const { data: allMessages } = await getMessages(this.token);\n const [firstMatch] = filterMessages(allMessages, filters);\n\n if (firstMatch) {\n const { data } = await getMessage(this.token, firstMatch?.id || \"\");\n return data;\n }\n\n return;\n }\n\n /** Poll for messages in the initialized mailbox and timeout */\n public async pollMessages(\n filters?: SearchFilters,\n { timeout = 30000, autoDelete = true }: PollingOptions = {},\n ) {\n const start = Date.now();\n\n while (Date.now() - start < timeout) {\n const message = await this.queryMessages(filters);\n\n if (message) {\n if (autoDelete && this.token && message.id) {\n await deleteMessage(this.token, message.id);\n }\n\n return message;\n }\n\n await new Promise((resolve) =>\n setTimeout(resolve, E2EMailClient.POLL_INTERVAL),\n );\n }\n\n throw new Error(\n `E2E Mail - No message matching your criteria found found after ${timeout}ms.`,\n );\n }\n}\n"],"names":["client","createClient","getDomains","data","error","createAccount","address","password","getMe","token","deleteAccount","id","createToken","getMessages","getMessage","deleteMessage","validateEmailDomain","domains","domain","activeDomains","d","domainMatch","filterMessages","messages","filters","predicates","message","_a","regex","startTime","predicate","_E2EMailClient","auth","account","detail","allMessages","firstMatch","timeout","autoDelete","start","resolve","E2EMailClient"],"mappings":"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"}
package/dist/index.js ADDED
@@ -0,0 +1,5 @@
1
+ import { E as e } from "./index-DalPChOM.js";
2
+ export {
3
+ e as E2EMailClient
4
+ };
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";"}
@@ -0,0 +1,43 @@
1
+ import { test as m } from "@playwright/test";
2
+ import { expect as h } from "@playwright/test";
3
+ import { E as w } from "./index-DalPChOM.js";
4
+ let i;
5
+ const x = m.extend({
6
+ initializeMailbox: async ({}, t) => {
7
+ await t(async (a, e) => {
8
+ i = new w(a, e), await i.initialize();
9
+ });
10
+ },
11
+ removeMailbox: async ({}, t) => {
12
+ await t(async () => {
13
+ if (!i)
14
+ throw new Error(
15
+ "Mailbox client not initialized. Call initializeMailbox() first."
16
+ );
17
+ await i.dispose();
18
+ });
19
+ },
20
+ searchMailbox: async ({ page: t }, a) => {
21
+ await a(async (e, s = {}) => {
22
+ var l;
23
+ const {
24
+ timeout: r = x.info().project.use.actionTimeout ?? 4e3,
25
+ autoDelete: c
26
+ } = s;
27
+ if (!i)
28
+ throw new Error(
29
+ "Mailbox client not initialized. Call initializeMailbox() first."
30
+ );
31
+ const o = await i.pollMessages(e, {
32
+ timeout: r,
33
+ autoDelete: c
34
+ }), n = ((l = o.html) == null ? void 0 : l[0]) ?? o.html ?? "";
35
+ return n && await t.setContent(String(n)), o;
36
+ });
37
+ }
38
+ });
39
+ export {
40
+ h as expect,
41
+ x as test
42
+ };
43
+ //# sourceMappingURL=playwright.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"playwright.js","sources":["../src/playwright/index.ts"],"sourcesContent":["import { test as base } from \"@playwright/test\";\nimport type { PollingOptions, SearchFilters } from \"../core/mailtm/mailtm\";\nimport type { components } from \"../core/api/mailtm-api.d\";\nimport { E2EMailClient } from \"../core/mailtm\";\n\ntype Message = components[\"schemas\"][\"Message-message.read\"];\n\nexport type MailFixtures = {\n /**\n * Initialize mailbox session\n * @param address Email address\n * @param password Account password\n */\n initializeMailbox: (address: string, password: string) => void;\n\n /**\n * Get most recent inbox match for an existing or new mail account\n * @param filters Optional search filters\n * @param options Configuration for message polling\n */\n searchMailbox: (\n filters?: SearchFilters,\n options?: PollingOptions,\n ) => Promise<Message>;\n\n /**\n * Delete the account mailbox and all its emails from the server\n */\n removeMailbox: () => void;\n};\n\nlet mailClient: E2EMailClient | undefined;\n\nexport const test = base.extend<MailFixtures>({\n initializeMailbox: async ({}, use) => {\n await use(async (address, password) => {\n mailClient = new E2EMailClient(address, password);\n await mailClient.initialize();\n });\n },\n\n removeMailbox: async ({}, use) => {\n await use(async () => {\n if (!mailClient) {\n throw new Error(\n \"Mailbox client not initialized. Call initializeMailbox() first.\",\n );\n }\n\n await mailClient.dispose();\n });\n },\n\n searchMailbox: async ({ page }, use) => {\n await use(async (filters, options = {}) => {\n const {\n timeout = test.info().project.use.actionTimeout ?? 4000,\n autoDelete,\n } = options;\n\n if (!mailClient) {\n throw new Error(\n \"Mailbox client not initialized. Call initializeMailbox() first.\",\n );\n }\n\n const message = await mailClient.pollMessages(filters, {\n timeout,\n autoDelete,\n });\n const html = message.html?.[0] ?? message.html ?? \"\";\n\n if (html) {\n await page.setContent(String(html));\n }\n\n return message;\n });\n },\n});\n\nexport { expect } from \"@playwright/test\";\n"],"names":["mailClient","test","base","use","address","password","E2EMailClient","page","filters","options","timeout","autoDelete","message","html","_a"],"mappings":";;;AA+BA,IAAIA;AAEG,MAAMC,IAAOC,EAAK,OAAqB;AAAA,EAC5C,mBAAmB,OAAO,CAAA,GAAIC,MAAQ;AACpC,UAAMA,EAAI,OAAOC,GAASC,MAAa;AACrC,MAAAL,IAAa,IAAIM,EAAcF,GAASC,CAAQ,GAChD,MAAML,EAAW,WAAA;AAAA,IACnB,CAAC;AAAA,EACH;AAAA,EAEA,eAAe,OAAO,CAAA,GAAIG,MAAQ;AAChC,UAAMA,EAAI,YAAY;AACpB,UAAI,CAACH;AACH,cAAM,IAAI;AAAA,UACR;AAAA,QAAA;AAIJ,YAAMA,EAAW,QAAA;AAAA,IACnB,CAAC;AAAA,EACH;AAAA,EAEA,eAAe,OAAO,EAAE,MAAAO,EAAA,GAAQJ,MAAQ;AACtC,UAAMA,EAAI,OAAOK,GAASC,IAAU,CAAA,MAAO;;AACzC,YAAM;AAAA,QACJ,SAAAC,IAAUT,EAAK,KAAA,EAAO,QAAQ,IAAI,iBAAiB;AAAA,QACnD,YAAAU;AAAA,MAAA,IACEF;AAEJ,UAAI,CAACT;AACH,cAAM,IAAI;AAAA,UACR;AAAA,QAAA;AAIJ,YAAMY,IAAU,MAAMZ,EAAW,aAAaQ,GAAS;AAAA,QACrD,SAAAE;AAAA,QACA,YAAAC;AAAA,MAAA,CACD,GACKE,MAAOC,IAAAF,EAAQ,SAAR,gBAAAE,EAAe,OAAMF,EAAQ,QAAQ;AAElD,aAAIC,KACF,MAAMN,EAAK,WAAW,OAAOM,CAAI,CAAC,GAG7BD;AAAA,IACT,CAAC;AAAA,EACH;AACF,CAAC;"}
package/package.json ADDED
@@ -0,0 +1,60 @@
1
+ {
2
+ "name": "e2e-mail",
3
+ "version": "0.0.1",
4
+ "description": "A zero-config solution for testing email in major testing frameworks.",
5
+ "main": "dist/index.js",
6
+ "type": "module",
7
+ "exports": {
8
+ ".": {
9
+ "import": "./dist/index.js"
10
+ },
11
+ "./cypress": {
12
+ "import": "./dist/cypress/index.js"
13
+ },
14
+ "./playwright": {
15
+ "import": "./dist/playwright/index.js"
16
+ }
17
+ },
18
+ "files": [
19
+ "dist",
20
+ "src/cypress/README.md",
21
+ "src/playwright/README.md"
22
+ ],
23
+ "scripts": {
24
+ "start": "tsx src/main.ts",
25
+ "schema": "tsx scripts/generate-types.ts application/json https://api.mail.tm/docs.jsonopenapi ./src/core/api/mailtm-api.d.ts",
26
+ "build": "vite build",
27
+ "build:watch": "vite build --watch",
28
+ "e2e:cypress": "cypress open",
29
+ "e2e:playwright": "playwright test --ui"
30
+ },
31
+ "keywords": [
32
+ "Cypress",
33
+ "Playwright",
34
+ "Testing",
35
+ "Email"
36
+ ],
37
+ "author": "Eric Gregoire",
38
+ "license": "MIT",
39
+ "devDependencies": {
40
+ "@playwright/test": "^1.49.0",
41
+ "@types/node": "^25.9.2",
42
+ "openapi-fetch": "^0.17.0",
43
+ "openapi-typescript": "^7.13.0",
44
+ "tsx": "^4.22.4",
45
+ "typescript": "^5.9.3",
46
+ "vite": "^6.3.0"
47
+ },
48
+ "peerDependencies": {
49
+ "cypress": "^15",
50
+ "playwright": "^1.60"
51
+ },
52
+ "peerDependenciesMeta": {
53
+ "cypress": {
54
+ "optional": true
55
+ },
56
+ "playwright": {
57
+ "optional": true
58
+ }
59
+ }
60
+ }