@tempyemail/e2e-testing 1.0.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/LICENSE +21 -0
- package/README.md +695 -0
- package/dist/client.d.ts +19 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +50 -0
- package/dist/client.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +29 -0
- package/dist/index.js.map +1 -0
- package/dist/mailbox.d.ts +46 -0
- package/dist/mailbox.d.ts.map +1 -0
- package/dist/mailbox.js +146 -0
- package/dist/mailbox.js.map +1 -0
- package/dist/parsers/links.d.ts +20 -0
- package/dist/parsers/links.d.ts.map +1 -0
- package/dist/parsers/links.js +77 -0
- package/dist/parsers/links.js.map +1 -0
- package/dist/parsers/otp.d.ts +28 -0
- package/dist/parsers/otp.d.ts.map +1 -0
- package/dist/parsers/otp.js +71 -0
- package/dist/parsers/otp.js.map +1 -0
- package/dist/types.d.ts +64 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +6 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/polling.d.ts +11 -0
- package/dist/utils/polling.d.ts.map +1 -0
- package/dist/utils/polling.js +36 -0
- package/dist/utils/polling.js.map +1 -0
- package/package.json +52 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 TempyEmail
|
|
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,695 @@
|
|
|
1
|
+
# @tempyemail/e2e-testing
|
|
2
|
+
|
|
3
|
+
> JavaScript/TypeScript client for [tempy.email](https://tempy.email) - Temporary email addresses for automated E2E testing
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/@tempyemail/e2e-testing)
|
|
6
|
+
[](https://opensource.org/licenses/MIT)
|
|
7
|
+
|
|
8
|
+
**Perfect for testing:**
|
|
9
|
+
- Email verification flows (signup, 2FA, password reset)
|
|
10
|
+
- OTP/verification code extraction
|
|
11
|
+
- Magic link authentication
|
|
12
|
+
- Email-triggered workflows
|
|
13
|
+
- Notification systems
|
|
14
|
+
|
|
15
|
+
**No authentication required** - fully public API with automatic cleanup.
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## 📦 Installation
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
npm install @tempyemail/e2e-testing
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Or with other package managers:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
yarn add @tempyemail/e2e-testing
|
|
29
|
+
pnpm add @tempyemail/e2e-testing
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## 🚀 Quick Start
|
|
35
|
+
|
|
36
|
+
```typescript
|
|
37
|
+
import { TempyEmail } from '@tempyemail/e2e-testing';
|
|
38
|
+
|
|
39
|
+
// Create client
|
|
40
|
+
const client = new TempyEmail();
|
|
41
|
+
|
|
42
|
+
// Create a temporary mailbox
|
|
43
|
+
const mailbox = await client.createMailbox();
|
|
44
|
+
console.log(`Email: ${mailbox.address}`);
|
|
45
|
+
|
|
46
|
+
// Wait for an email
|
|
47
|
+
const email = await mailbox.waitForEmail({ timeout: 30000 });
|
|
48
|
+
console.log(`Received: ${email.subject}`);
|
|
49
|
+
|
|
50
|
+
// Extract OTP code
|
|
51
|
+
const otp = await mailbox.waitForOTP({ timeout: 30000 });
|
|
52
|
+
console.log(`OTP: ${otp}`);
|
|
53
|
+
|
|
54
|
+
// Cleanup
|
|
55
|
+
await mailbox.delete();
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
## 📚 API Reference
|
|
61
|
+
|
|
62
|
+
### TempyEmail
|
|
63
|
+
|
|
64
|
+
Main client class for interacting with the API.
|
|
65
|
+
|
|
66
|
+
#### Constructor
|
|
67
|
+
|
|
68
|
+
```typescript
|
|
69
|
+
new TempyEmail(config?: {
|
|
70
|
+
baseUrl?: string; // Default: 'https://tempy.email/api/v1'
|
|
71
|
+
timeout?: number; // Default: 30000 (30 seconds)
|
|
72
|
+
})
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
#### Methods
|
|
76
|
+
|
|
77
|
+
##### `createMailbox(options?)`
|
|
78
|
+
|
|
79
|
+
Create a new temporary mailbox.
|
|
80
|
+
|
|
81
|
+
```typescript
|
|
82
|
+
const mailbox = await client.createMailbox({
|
|
83
|
+
webhookUrl?: string; // Optional webhook URL
|
|
84
|
+
webhookFormat?: 'json' | 'xml'; // Default: 'json'
|
|
85
|
+
});
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
**Returns:** `Promise<Mailbox>`
|
|
89
|
+
|
|
90
|
+
##### `getMailbox(address)`
|
|
91
|
+
|
|
92
|
+
Get an existing mailbox by email address.
|
|
93
|
+
|
|
94
|
+
```typescript
|
|
95
|
+
const mailbox = await client.getMailbox('abc123@tempy.email');
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
**Returns:** `Promise<Mailbox>`
|
|
99
|
+
|
|
100
|
+
---
|
|
101
|
+
|
|
102
|
+
### Mailbox
|
|
103
|
+
|
|
104
|
+
Represents a temporary email inbox with helper methods.
|
|
105
|
+
|
|
106
|
+
#### Properties
|
|
107
|
+
|
|
108
|
+
- `address: string` - The email address
|
|
109
|
+
- `expiresAt: Date` - When the mailbox expires
|
|
110
|
+
- `webhookUrl?: string` - Webhook URL if configured
|
|
111
|
+
|
|
112
|
+
#### Methods
|
|
113
|
+
|
|
114
|
+
##### `getMessages()`
|
|
115
|
+
|
|
116
|
+
Get all messages in the mailbox.
|
|
117
|
+
|
|
118
|
+
```typescript
|
|
119
|
+
const messages = await mailbox.getMessages();
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
**Returns:** `Promise<Email[]>`
|
|
123
|
+
|
|
124
|
+
##### `waitForEmail(options?)`
|
|
125
|
+
|
|
126
|
+
Wait for a new email matching the specified criteria.
|
|
127
|
+
|
|
128
|
+
```typescript
|
|
129
|
+
const email = await mailbox.waitForEmail({
|
|
130
|
+
timeout?: number; // Default: 30000ms
|
|
131
|
+
subject?: string | RegExp; // Filter by subject
|
|
132
|
+
from?: string | RegExp; // Filter by sender
|
|
133
|
+
pollInterval?: number; // Default: 1000ms
|
|
134
|
+
});
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
**Returns:** `Promise<Email>`
|
|
138
|
+
|
|
139
|
+
**Example:**
|
|
140
|
+
|
|
141
|
+
```typescript
|
|
142
|
+
// Wait for any email
|
|
143
|
+
const email = await mailbox.waitForEmail();
|
|
144
|
+
|
|
145
|
+
// Wait for email from specific sender
|
|
146
|
+
const email = await mailbox.waitForEmail({
|
|
147
|
+
from: /noreply@example\.com/
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
// Wait for email with specific subject
|
|
151
|
+
const email = await mailbox.waitForEmail({
|
|
152
|
+
subject: 'Welcome',
|
|
153
|
+
timeout: 60000
|
|
154
|
+
});
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
##### `waitForOTP(options?)`
|
|
158
|
+
|
|
159
|
+
Wait for an email and extract an OTP code.
|
|
160
|
+
|
|
161
|
+
```typescript
|
|
162
|
+
const otp = await mailbox.waitForOTP({
|
|
163
|
+
timeout?: number; // Default: 30000ms
|
|
164
|
+
pattern?: RegExp; // Custom OTP pattern
|
|
165
|
+
from?: string | RegExp; // Filter by sender
|
|
166
|
+
});
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
**Returns:** `Promise<string>`
|
|
170
|
+
|
|
171
|
+
**Automatically extracts:**
|
|
172
|
+
- 6-digit codes (e.g., `123456`)
|
|
173
|
+
- 4-8 digit codes
|
|
174
|
+
- Alphanumeric codes (e.g., `ABC123`)
|
|
175
|
+
- UUID tokens
|
|
176
|
+
|
|
177
|
+
**Example:**
|
|
178
|
+
|
|
179
|
+
```typescript
|
|
180
|
+
// Extract any common OTP format
|
|
181
|
+
const otp = await mailbox.waitForOTP({ timeout: 30000 });
|
|
182
|
+
|
|
183
|
+
// Custom pattern
|
|
184
|
+
const otp = await mailbox.waitForOTP({
|
|
185
|
+
pattern: /PIN:\s*(\d{4})/
|
|
186
|
+
});
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
##### `waitForLink(options?)`
|
|
190
|
+
|
|
191
|
+
Wait for an email and extract a verification/magic link.
|
|
192
|
+
|
|
193
|
+
```typescript
|
|
194
|
+
const link = await mailbox.waitForLink({
|
|
195
|
+
timeout?: number; // Default: 30000ms
|
|
196
|
+
pattern?: RegExp; // Custom link pattern
|
|
197
|
+
from?: string | RegExp; // Filter by sender
|
|
198
|
+
});
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
**Returns:** `Promise<string>`
|
|
202
|
+
|
|
203
|
+
**Example:**
|
|
204
|
+
|
|
205
|
+
```typescript
|
|
206
|
+
// Extract verification link
|
|
207
|
+
const link = await mailbox.waitForLink({
|
|
208
|
+
pattern: /verify/i
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
// Extract password reset link
|
|
212
|
+
const resetLink = await mailbox.waitForLink({
|
|
213
|
+
pattern: /reset-password/,
|
|
214
|
+
from: /security@/
|
|
215
|
+
});
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
##### `markAsRead(emailIds)`
|
|
219
|
+
|
|
220
|
+
Mark emails as read.
|
|
221
|
+
|
|
222
|
+
```typescript
|
|
223
|
+
await mailbox.markAsRead(['email-id-1', 'email-id-2']);
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
##### `delete()`
|
|
227
|
+
|
|
228
|
+
Delete the mailbox.
|
|
229
|
+
|
|
230
|
+
```typescript
|
|
231
|
+
await mailbox.delete();
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
##### `getStatus()`
|
|
235
|
+
|
|
236
|
+
Get mailbox status.
|
|
237
|
+
|
|
238
|
+
```typescript
|
|
239
|
+
const status = await mailbox.getStatus();
|
|
240
|
+
console.log(`Expires: ${status.expiresAt}`);
|
|
241
|
+
console.log(`Time remaining: ${status.secondsRemaining}s`);
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
##### `isExpired()`
|
|
245
|
+
|
|
246
|
+
Check if mailbox has expired.
|
|
247
|
+
|
|
248
|
+
```typescript
|
|
249
|
+
if (mailbox.isExpired()) {
|
|
250
|
+
console.log('Mailbox expired');
|
|
251
|
+
}
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
##### `secondsRemaining()`
|
|
255
|
+
|
|
256
|
+
Get seconds remaining until expiration.
|
|
257
|
+
|
|
258
|
+
```typescript
|
|
259
|
+
const remaining = mailbox.secondsRemaining();
|
|
260
|
+
console.log(`${remaining}s remaining`);
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
---
|
|
264
|
+
|
|
265
|
+
## 🎭 Framework Integration
|
|
266
|
+
|
|
267
|
+
### Playwright
|
|
268
|
+
|
|
269
|
+
```typescript
|
|
270
|
+
import { test, expect } from '@playwright/test';
|
|
271
|
+
import { TempyEmail } from '@tempyemail/e2e-testing';
|
|
272
|
+
|
|
273
|
+
test('user signup with verification', async ({ page }) => {
|
|
274
|
+
const client = new TempyEmail();
|
|
275
|
+
const mailbox = await client.createMailbox();
|
|
276
|
+
|
|
277
|
+
// Fill signup form
|
|
278
|
+
await page.goto('https://example.com/signup');
|
|
279
|
+
await page.fill('[name="email"]', mailbox.address);
|
|
280
|
+
await page.fill('[name="password"]', 'Password123!');
|
|
281
|
+
await page.click('button[type="submit"]');
|
|
282
|
+
|
|
283
|
+
// Wait for OTP
|
|
284
|
+
const otp = await mailbox.waitForOTP({ timeout: 30000 });
|
|
285
|
+
|
|
286
|
+
// Enter OTP
|
|
287
|
+
await page.fill('[name="code"]', otp);
|
|
288
|
+
await page.click('button[type="submit"]');
|
|
289
|
+
|
|
290
|
+
// Verify success
|
|
291
|
+
await expect(page.locator('.success')).toBeVisible();
|
|
292
|
+
|
|
293
|
+
await mailbox.delete();
|
|
294
|
+
});
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
### Cypress
|
|
298
|
+
|
|
299
|
+
```typescript
|
|
300
|
+
import { TempyEmail } from '@tempyemail/e2e-testing';
|
|
301
|
+
|
|
302
|
+
describe('Signup', () => {
|
|
303
|
+
it('completes email verification', () => {
|
|
304
|
+
const client = new TempyEmail();
|
|
305
|
+
|
|
306
|
+
cy.wrap(client.createMailbox()).then((mailbox) => {
|
|
307
|
+
cy.visit('/signup');
|
|
308
|
+
cy.get('[name="email"]').type(mailbox.address);
|
|
309
|
+
cy.get('[name="password"]').type('Password123!');
|
|
310
|
+
cy.get('button[type="submit"]').click();
|
|
311
|
+
|
|
312
|
+
// Wait for OTP
|
|
313
|
+
cy.wrap(mailbox.waitForOTP({ timeout: 30000 })).then((otp) => {
|
|
314
|
+
cy.get('[name="code"]').type(otp);
|
|
315
|
+
cy.get('button[type="submit"]').click();
|
|
316
|
+
cy.get('.success').should('be.visible');
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
cy.wrap(mailbox.delete());
|
|
320
|
+
});
|
|
321
|
+
});
|
|
322
|
+
});
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
### Jest
|
|
326
|
+
|
|
327
|
+
```typescript
|
|
328
|
+
import { TempyEmail } from '@tempyemail/e2e-testing';
|
|
329
|
+
|
|
330
|
+
describe('Email Integration', () => {
|
|
331
|
+
let client: TempyEmail;
|
|
332
|
+
let mailbox: Mailbox;
|
|
333
|
+
|
|
334
|
+
beforeAll(() => {
|
|
335
|
+
client = new TempyEmail();
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
beforeEach(async () => {
|
|
339
|
+
mailbox = await client.createMailbox();
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
afterEach(async () => {
|
|
343
|
+
await mailbox.delete();
|
|
344
|
+
});
|
|
345
|
+
|
|
346
|
+
it('receives welcome email', async () => {
|
|
347
|
+
// Trigger email in your app
|
|
348
|
+
await yourApp.sendWelcomeEmail(mailbox.address);
|
|
349
|
+
|
|
350
|
+
const email = await mailbox.waitForEmail({
|
|
351
|
+
subject: /welcome/i,
|
|
352
|
+
timeout: 30000
|
|
353
|
+
});
|
|
354
|
+
|
|
355
|
+
expect(email.subject).toContain('Welcome');
|
|
356
|
+
});
|
|
357
|
+
});
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
### Vitest
|
|
361
|
+
|
|
362
|
+
```typescript
|
|
363
|
+
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
364
|
+
import { TempyEmail, Mailbox } from '@tempyemail/e2e-testing';
|
|
365
|
+
|
|
366
|
+
describe('Password Reset', () => {
|
|
367
|
+
let mailbox: Mailbox;
|
|
368
|
+
|
|
369
|
+
beforeEach(async () => {
|
|
370
|
+
const client = new TempyEmail();
|
|
371
|
+
mailbox = await client.createMailbox();
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
afterEach(async () => {
|
|
375
|
+
await mailbox.delete();
|
|
376
|
+
});
|
|
377
|
+
|
|
378
|
+
it('sends reset link', async () => {
|
|
379
|
+
await yourApp.requestPasswordReset(mailbox.address);
|
|
380
|
+
|
|
381
|
+
const link = await mailbox.waitForLink({
|
|
382
|
+
pattern: /reset-password/,
|
|
383
|
+
timeout: 30000
|
|
384
|
+
});
|
|
385
|
+
|
|
386
|
+
expect(link).toMatch(/^https:\/\//);
|
|
387
|
+
});
|
|
388
|
+
});
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
---
|
|
392
|
+
|
|
393
|
+
## 🔍 Extracting OTP Codes
|
|
394
|
+
|
|
395
|
+
The library automatically extracts common OTP formats:
|
|
396
|
+
|
|
397
|
+
```typescript
|
|
398
|
+
// 6-digit codes
|
|
399
|
+
"Your code is 123456" → "123456"
|
|
400
|
+
|
|
401
|
+
// Codes with formatting
|
|
402
|
+
"Code: 987-654" → "987654"
|
|
403
|
+
|
|
404
|
+
// Alphanumeric codes
|
|
405
|
+
"Token: ABC123" → "ABC123"
|
|
406
|
+
|
|
407
|
+
// UUID tokens
|
|
408
|
+
"Token: 550e8400-e29b-41d4-a716-446655440000" → "550e8400-..."
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
### Custom OTP Patterns
|
|
412
|
+
|
|
413
|
+
```typescript
|
|
414
|
+
// Extract 4-digit PIN
|
|
415
|
+
const pin = await mailbox.waitForOTP({
|
|
416
|
+
pattern: /PIN:\s*(\d{4})/
|
|
417
|
+
});
|
|
418
|
+
|
|
419
|
+
// Extract specific format
|
|
420
|
+
const code = await mailbox.waitForOTP({
|
|
421
|
+
pattern: /CODE:\s*([A-Z0-9]{8})/
|
|
422
|
+
});
|
|
423
|
+
```
|
|
424
|
+
|
|
425
|
+
### Manual Extraction
|
|
426
|
+
|
|
427
|
+
```typescript
|
|
428
|
+
import {
|
|
429
|
+
extract6DigitOTP,
|
|
430
|
+
extractNumericOTP,
|
|
431
|
+
extractAlphanumericOTP,
|
|
432
|
+
extractUUID,
|
|
433
|
+
extractByPattern
|
|
434
|
+
} from '@tempyemail/e2e-testing';
|
|
435
|
+
|
|
436
|
+
// From email text
|
|
437
|
+
const email = await mailbox.waitForEmail();
|
|
438
|
+
const otp = extract6DigitOTP(email.bodyText);
|
|
439
|
+
```
|
|
440
|
+
|
|
441
|
+
---
|
|
442
|
+
|
|
443
|
+
## 🔗 Extracting Links
|
|
444
|
+
|
|
445
|
+
Extract verification and magic links from emails:
|
|
446
|
+
|
|
447
|
+
```typescript
|
|
448
|
+
// Wait for verification link
|
|
449
|
+
const link = await mailbox.waitForLink();
|
|
450
|
+
|
|
451
|
+
// Custom pattern
|
|
452
|
+
const magicLink = await mailbox.waitForLink({
|
|
453
|
+
pattern: /magic-link/
|
|
454
|
+
});
|
|
455
|
+
|
|
456
|
+
// From specific sender
|
|
457
|
+
const resetLink = await mailbox.waitForLink({
|
|
458
|
+
pattern: /reset/,
|
|
459
|
+
from: /security@example\.com/
|
|
460
|
+
});
|
|
461
|
+
```
|
|
462
|
+
|
|
463
|
+
### Manual Link Extraction
|
|
464
|
+
|
|
465
|
+
```typescript
|
|
466
|
+
import {
|
|
467
|
+
extractLinks,
|
|
468
|
+
extractVerificationLink,
|
|
469
|
+
extractLinksByDomain,
|
|
470
|
+
extractFirstLink
|
|
471
|
+
} from '@tempyemail/e2e-testing';
|
|
472
|
+
|
|
473
|
+
const email = await mailbox.waitForEmail();
|
|
474
|
+
|
|
475
|
+
// All links
|
|
476
|
+
const allLinks = extractLinks(email.bodyHtml);
|
|
477
|
+
|
|
478
|
+
// Verification links only
|
|
479
|
+
const verifyLink = extractVerificationLink(email.bodyHtml);
|
|
480
|
+
|
|
481
|
+
// Links from specific domain
|
|
482
|
+
const appLinks = extractLinksByDomain(email.bodyHtml, 'example.com');
|
|
483
|
+
|
|
484
|
+
// First link
|
|
485
|
+
const firstLink = extractFirstLink(email.bodyText);
|
|
486
|
+
```
|
|
487
|
+
|
|
488
|
+
---
|
|
489
|
+
|
|
490
|
+
## 🪝 Using Webhooks
|
|
491
|
+
|
|
492
|
+
Receive real-time notifications when emails arrive:
|
|
493
|
+
|
|
494
|
+
```typescript
|
|
495
|
+
const mailbox = await client.createMailbox({
|
|
496
|
+
webhookUrl: 'https://your-server.com/webhook',
|
|
497
|
+
webhookFormat: 'json'
|
|
498
|
+
});
|
|
499
|
+
|
|
500
|
+
console.log(`Webhook configured: ${mailbox.webhookUrl}`);
|
|
501
|
+
```
|
|
502
|
+
|
|
503
|
+
**Webhook payload:**
|
|
504
|
+
|
|
505
|
+
```json
|
|
506
|
+
{
|
|
507
|
+
"id": "msg_abc123",
|
|
508
|
+
"from": "sender@example.com",
|
|
509
|
+
"to": "abc123@tempy.email",
|
|
510
|
+
"subject": "Verification Code",
|
|
511
|
+
"bodyText": "Your code is 123456",
|
|
512
|
+
"bodyHtml": "<p>Your code is <b>123456</b></p>",
|
|
513
|
+
"receivedAt": "2025-01-15T10:30:00Z",
|
|
514
|
+
"direction": "inbound",
|
|
515
|
+
"isRead": false,
|
|
516
|
+
"allowReply": true
|
|
517
|
+
}
|
|
518
|
+
```
|
|
519
|
+
|
|
520
|
+
**Testing webhooks locally:**
|
|
521
|
+
|
|
522
|
+
```bash
|
|
523
|
+
# Expose local server with ngrok
|
|
524
|
+
npx ngrok http 3000
|
|
525
|
+
|
|
526
|
+
# Use ngrok URL as webhook
|
|
527
|
+
const mailbox = await client.createMailbox({
|
|
528
|
+
webhookUrl: 'https://abc123.ngrok.io/webhook'
|
|
529
|
+
});
|
|
530
|
+
```
|
|
531
|
+
|
|
532
|
+
---
|
|
533
|
+
|
|
534
|
+
## 💡 Best Practices
|
|
535
|
+
|
|
536
|
+
### 1. Always Clean Up
|
|
537
|
+
|
|
538
|
+
```typescript
|
|
539
|
+
let mailbox: Mailbox;
|
|
540
|
+
|
|
541
|
+
try {
|
|
542
|
+
mailbox = await client.createMailbox();
|
|
543
|
+
// ... your test ...
|
|
544
|
+
} finally {
|
|
545
|
+
if (mailbox) {
|
|
546
|
+
await mailbox.delete();
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
```
|
|
550
|
+
|
|
551
|
+
### 2. Use Appropriate Timeouts
|
|
552
|
+
|
|
553
|
+
```typescript
|
|
554
|
+
// Quick operations
|
|
555
|
+
const email = await mailbox.waitForEmail({ timeout: 10000 });
|
|
556
|
+
|
|
557
|
+
// Slower email services
|
|
558
|
+
const email = await mailbox.waitForEmail({ timeout: 60000 });
|
|
559
|
+
```
|
|
560
|
+
|
|
561
|
+
### 3. Filter by Sender
|
|
562
|
+
|
|
563
|
+
```typescript
|
|
564
|
+
// Only accept emails from your app
|
|
565
|
+
const otp = await mailbox.waitForOTP({
|
|
566
|
+
from: /noreply@yourapp\.com/
|
|
567
|
+
});
|
|
568
|
+
```
|
|
569
|
+
|
|
570
|
+
### 4. Handle Timeouts Gracefully
|
|
571
|
+
|
|
572
|
+
```typescript
|
|
573
|
+
try {
|
|
574
|
+
const otp = await mailbox.waitForOTP({ timeout: 30000 });
|
|
575
|
+
} catch (error) {
|
|
576
|
+
if (error.message.includes('timeout')) {
|
|
577
|
+
console.log('Email not received - check email service');
|
|
578
|
+
}
|
|
579
|
+
throw error;
|
|
580
|
+
}
|
|
581
|
+
```
|
|
582
|
+
|
|
583
|
+
### 5. Reuse Mailboxes When Possible
|
|
584
|
+
|
|
585
|
+
```typescript
|
|
586
|
+
// Good: One mailbox for entire test
|
|
587
|
+
const mailbox = await client.createMailbox();
|
|
588
|
+
await testSignup(mailbox);
|
|
589
|
+
await testVerification(mailbox);
|
|
590
|
+
await mailbox.delete();
|
|
591
|
+
|
|
592
|
+
// Avoid: Creating multiple mailboxes unnecessarily
|
|
593
|
+
```
|
|
594
|
+
|
|
595
|
+
---
|
|
596
|
+
|
|
597
|
+
## ⚡ Rate Limits
|
|
598
|
+
|
|
599
|
+
The tempy.email API has the following limits:
|
|
600
|
+
|
|
601
|
+
- **Mailbox creation:** Unlimited
|
|
602
|
+
- **API requests:** 100 per minute per IP
|
|
603
|
+
- **Mailbox lifetime:** 1 hour (automatic cleanup)
|
|
604
|
+
- **Message retention:** Deleted when mailbox expires
|
|
605
|
+
|
|
606
|
+
No authentication required - fully public API.
|
|
607
|
+
|
|
608
|
+
---
|
|
609
|
+
|
|
610
|
+
## 🐛 Troubleshooting
|
|
611
|
+
|
|
612
|
+
### "Polling timeout" error
|
|
613
|
+
|
|
614
|
+
```typescript
|
|
615
|
+
// Increase timeout
|
|
616
|
+
const email = await mailbox.waitForEmail({ timeout: 60000 });
|
|
617
|
+
|
|
618
|
+
// Check if email was actually sent
|
|
619
|
+
const messages = await mailbox.getMessages();
|
|
620
|
+
console.log(`${messages.length} messages in mailbox`);
|
|
621
|
+
```
|
|
622
|
+
|
|
623
|
+
### "No OTP code found" error
|
|
624
|
+
|
|
625
|
+
```typescript
|
|
626
|
+
// Use custom pattern
|
|
627
|
+
const otp = await mailbox.waitForOTP({
|
|
628
|
+
pattern: /code:\s*(\d+)/i
|
|
629
|
+
});
|
|
630
|
+
|
|
631
|
+
// Or extract manually
|
|
632
|
+
const email = await mailbox.waitForEmail();
|
|
633
|
+
console.log('Email body:', email.bodyText);
|
|
634
|
+
```
|
|
635
|
+
|
|
636
|
+
### "Failed to create mailbox" error
|
|
637
|
+
|
|
638
|
+
- Check your internet connection
|
|
639
|
+
- Verify tempy.email is accessible
|
|
640
|
+
- Check for rate limiting (100 requests/minute)
|
|
641
|
+
|
|
642
|
+
### Emails not arriving
|
|
643
|
+
|
|
644
|
+
- Wait longer (increase timeout)
|
|
645
|
+
- Check spam/junk folder in your email service
|
|
646
|
+
- Verify the email was actually sent from your app
|
|
647
|
+
- Check mailbox hasn't expired (`mailbox.isExpired()`)
|
|
648
|
+
|
|
649
|
+
---
|
|
650
|
+
|
|
651
|
+
## 📖 Examples
|
|
652
|
+
|
|
653
|
+
Complete working examples are available in the [`examples/`](./examples) directory:
|
|
654
|
+
|
|
655
|
+
- **[Basic](./examples/basic/)** - Simple Node.js examples
|
|
656
|
+
- **[Playwright](./examples/playwright/)** - Browser testing with Playwright
|
|
657
|
+
- **[Cypress](./examples/cypress/)** - E2E testing with Cypress
|
|
658
|
+
- **[Jest](./examples/jest/)** - Unit/integration testing with Jest
|
|
659
|
+
- **[Vitest](./examples/vitest/)** - Testing with Vitest
|
|
660
|
+
|
|
661
|
+
---
|
|
662
|
+
|
|
663
|
+
## 🤝 Contributing
|
|
664
|
+
|
|
665
|
+
Contributions are welcome! Please open an issue or submit a pull request on [GitHub](https://github.com/TempyEmail/e2e-testing).
|
|
666
|
+
|
|
667
|
+
---
|
|
668
|
+
|
|
669
|
+
## 📄 License
|
|
670
|
+
|
|
671
|
+
MIT © TempyEmail
|
|
672
|
+
|
|
673
|
+
---
|
|
674
|
+
|
|
675
|
+
## 🔗 Links
|
|
676
|
+
|
|
677
|
+
- **Website:** https://tempy.email
|
|
678
|
+
- **GitHub:** https://github.com/TempyEmail/e2e-testing
|
|
679
|
+
- **npm:** https://www.npmjs.com/package/@tempyemail/e2e-testing
|
|
680
|
+
- **Issues:** https://github.com/TempyEmail/e2e-testing/issues
|
|
681
|
+
|
|
682
|
+
---
|
|
683
|
+
|
|
684
|
+
## 🌟 Why tempy.email?
|
|
685
|
+
|
|
686
|
+
- ✅ **No authentication** - Just create and use
|
|
687
|
+
- ✅ **Automatic cleanup** - Mailboxes expire after 1 hour
|
|
688
|
+
- ✅ **Real emails** - Full SMTP support, not mocked
|
|
689
|
+
- ✅ **Webhook support** - Real-time notifications
|
|
690
|
+
- ✅ **Smart parsing** - Automatic OTP and link extraction
|
|
691
|
+
- ✅ **Framework agnostic** - Works with any testing framework
|
|
692
|
+
- ✅ **TypeScript native** - Full type safety
|
|
693
|
+
- ✅ **Zero configuration** - Works out of the box
|
|
694
|
+
|
|
695
|
+
Perfect for CI/CD pipelines, automated testing, and development workflows.
|