@impruthvi/nodemail 0.4.0 โ†’ 0.6.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.
Files changed (50) hide show
  1. package/README.md +123 -78
  2. package/dist/core/MailFacade.d.ts +49 -5
  3. package/dist/core/MailFacade.d.ts.map +1 -1
  4. package/dist/core/MailFacade.js +286 -4
  5. package/dist/core/MailFacade.js.map +1 -1
  6. package/dist/core/MailManager.d.ts +13 -1
  7. package/dist/core/MailManager.d.ts.map +1 -1
  8. package/dist/core/MailManager.js +64 -0
  9. package/dist/core/MailManager.js.map +1 -1
  10. package/dist/core/Mailable.d.ts +1 -0
  11. package/dist/core/Mailable.d.ts.map +1 -1
  12. package/dist/core/Mailable.js +4 -0
  13. package/dist/core/Mailable.js.map +1 -1
  14. package/dist/index.d.ts +4 -1
  15. package/dist/index.d.ts.map +1 -1
  16. package/dist/index.js +9 -2
  17. package/dist/index.js.map +1 -1
  18. package/dist/queue/QueueManager.d.ts +21 -0
  19. package/dist/queue/QueueManager.d.ts.map +1 -0
  20. package/dist/queue/QueueManager.js +131 -0
  21. package/dist/queue/QueueManager.js.map +1 -0
  22. package/dist/queue/drivers/BullDriver.d.ts +16 -0
  23. package/dist/queue/drivers/BullDriver.d.ts.map +1 -0
  24. package/dist/queue/drivers/BullDriver.js +137 -0
  25. package/dist/queue/drivers/BullDriver.js.map +1 -0
  26. package/dist/queue/drivers/BullMQDriver.d.ts +18 -0
  27. package/dist/queue/drivers/BullMQDriver.d.ts.map +1 -0
  28. package/dist/queue/drivers/BullMQDriver.js +150 -0
  29. package/dist/queue/drivers/BullMQDriver.js.map +1 -0
  30. package/dist/queue/index.d.ts +4 -0
  31. package/dist/queue/index.d.ts.map +1 -0
  32. package/dist/queue/index.js +10 -0
  33. package/dist/queue/index.js.map +1 -0
  34. package/dist/testing/AssertableMessage.d.ts +40 -0
  35. package/dist/testing/AssertableMessage.d.ts.map +1 -0
  36. package/dist/testing/AssertableMessage.js +158 -0
  37. package/dist/testing/AssertableMessage.js.map +1 -0
  38. package/dist/testing/MailFake.d.ts +34 -0
  39. package/dist/testing/MailFake.d.ts.map +1 -0
  40. package/dist/testing/MailFake.js +161 -0
  41. package/dist/testing/MailFake.js.map +1 -0
  42. package/dist/testing/index.d.ts +4 -0
  43. package/dist/testing/index.d.ts.map +1 -0
  44. package/dist/testing/index.js +8 -0
  45. package/dist/testing/index.js.map +1 -0
  46. package/dist/types/index.d.ts +45 -0
  47. package/dist/types/index.d.ts.map +1 -1
  48. package/examples/queue-example.ts +313 -0
  49. package/examples/testing-example.ts +303 -0
  50. package/package.json +1 -1
package/README.md CHANGED
@@ -3,12 +3,10 @@
3
3
  [![npm version](https://badge.fury.io/js/@impruthvi%2Fnodemail.svg)](https://www.npmjs.com/package/@impruthvi/nodemail)
4
4
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
5
  [![TypeScript](https://img.shields.io/badge/TypeScript-5.6-blue.svg)](https://www.typescriptlang.org/)
6
- [![Tests](https://img.shields.io/badge/tests-112%20passing-brightgreen)](https://github.com/impruthvi/nodemail)
6
+ [![Tests](https://img.shields.io/badge/tests-195%20passing-brightgreen)](https://github.com/impruthvi/nodemail)
7
7
  [![Coverage](https://img.shields.io/badge/coverage-85%25-brightgreen)](https://github.com/impruthvi/nodemail)
8
8
 
9
- > ๐Ÿšง **Work in Progress** - A unified mail service for Node.js/TypeScript inspired by Laravel's elegant Mail system.
10
-
11
- **@impruthvi/nodemail** aims to bring the simplicity and elegance of Laravel's Mail system to the Node.js ecosystem with full TypeScript support.
9
+ **@impruthvi/nodemail** brings the simplicity and elegance of Laravel's Mail system to the Node.js ecosystem with full TypeScript support.
12
10
 
13
11
  ## ๐ŸŽฏ Vision
14
12
 
@@ -22,10 +20,11 @@ Inspired by [Laravel's Mail system](https://laravel.com/docs/mail).
22
20
 
23
21
  ## โœจ Features
24
22
 
25
- ### โœ… Available Now (v0.4.0)
23
+ ### โœ… Available Now (v0.5.0)
26
24
  - ๐ŸŽฏ **Multiple Providers** - SMTP (Nodemailer), SendGrid, AWS SES, Mailgun, Resend, Postmark
27
25
  - ๐ŸŽจ **Template Engines** - Handlebars, EJS, Pug support with dynamic loading
28
26
  - ๐Ÿ“ **Mailable Classes** - Reusable email definitions with template support
27
+ - ๐Ÿงช **Testing Utilities** - Mail::fake() for testing (Laravel-style assertions)
29
28
  - ๐Ÿชถ **Lightweight** - Only ~25MB with SMTP, install additional providers as needed
30
29
  - ๐Ÿ”’ **Type-Safe** - Full TypeScript support with strict typing
31
30
  - โœจ **Complete Fluent API** - Chain to(), subject(), html(), template(), data(), cc(), bcc(), attachments(), headers()
@@ -35,7 +34,6 @@ Inspired by [Laravel's Mail system](https://laravel.com/docs/mail).
35
34
  ### ๐Ÿšง Coming Soon
36
35
  - ๐Ÿ”” **Notifications** - Multi-channel notification system
37
36
  - ๐Ÿ“‹ **Markdown Mail** - Beautiful emails from markdown
38
- - ๐Ÿงช **Testing Utilities** - Mail::fake() for testing
39
37
  - ๐Ÿ“ฆ **Queue Support** - Background email sending (Bull/BullMQ)
40
38
  - ๐ŸŒ **i18n Support** - Multi-language emails
41
39
  - ๐Ÿš€ **More Providers** - Mailtrap and others
@@ -43,12 +41,12 @@ Inspired by [Laravel's Mail system](https://laravel.com/docs/mail).
43
41
  ## ๐Ÿ“ฆ Installation
44
42
 
45
43
  ```bash
46
- npm install @impruthvi/nodemail@beta
44
+ npm install @impruthvi/nodemail
47
45
  ```
48
46
 
49
- Or for the latest stable (when v1.0.0 is released):
47
+ Or install a specific version:
50
48
  ```bash
51
- npm install @impruthvi/nodemail
49
+ npm install @impruthvi/nodemail@0.5.0
52
50
  ```
53
51
 
54
52
  **Lightweight by default!** Only includes SMTP support (~25MB).
@@ -338,79 +336,109 @@ await Mail.to('user@example.com').send(new WelcomeEmail(user, 'My App'));
338
336
  await new WelcomeEmail(user, 'My App').to('user@example.com').send();
339
337
  ```
340
338
 
341
- **Advanced usage (coming soon):**
339
+ ## ๐Ÿงช Testing with Mail::fake()
342
340
 
343
- ```typescript
344
- import { Mail } from 'nodemail';
345
-
346
- // Configure once
347
- Mail.configure({
348
- default: 'smtp',
349
- from: {
350
- address: 'noreply@example.com',
351
- name: 'My App',
352
- },
353
- mailers: {
354
- smtp: {
355
- driver: 'smtp',
356
- host: process.env.MAIL_HOST,
357
- port: 587,
358
- username: process.env.MAIL_USERNAME,
359
- password: process.env.MAIL_PASSWORD,
360
- },
361
- },
362
- });
363
-
364
- // Send emails
365
- await Mail.to('user@example.com')
366
- .subject('Welcome!')
367
- .html('<h1>Hello World!</h1>')
368
- .send();
369
- ```
370
-
371
- **Advanced usage (coming soon):**
341
+ Test your emails without actually sending them - just like Laravel's `Mail::fake()`:
372
342
 
373
343
  ```typescript
374
- import { Mail } from 'nodemail';
375
-
376
- // Configure once
377
- Mail.configure({
378
- default: 'smtp',
379
- from: {
380
- address: 'noreply@example.com',
381
- name: 'My App',
382
- },
383
- mailers: {
384
- smtp: {
385
- driver: 'smtp',
386
- host: process.env.MAIL_HOST,
387
- port: 587,
388
- username: process.env.MAIL_USERNAME,
389
- password: process.env.MAIL_PASSWORD,
390
- },
391
- },
392
- });
393
-
394
- // Send anywhere in your app
395
- await Mail.to('user@example.com')
396
- .subject('Welcome!')
397
- .html('<h1>Hello World!</h1>')
398
- .send();
344
+ import { Mail, Mailable } from '@impruthvi/nodemail';
399
345
 
400
- // Or use Mailable classes
346
+ // Your Mailable class
401
347
  class WelcomeEmail extends Mailable {
402
- constructor(private user: User) {
348
+ constructor(public userName: string) {
403
349
  super();
404
350
  }
405
351
 
406
352
  build() {
407
353
  return this
408
- .subject(`Welcome, ${this.user.name}!`)
409
- .view('emails.welcome', { user: this.user });
354
+ .subject(`Welcome, ${this.userName}!`)
355
+ .html(`<h1>Hello ${this.userName}!</h1>`);
410
356
  }
411
357
  }
412
358
 
413
- await Mail.to('user@example.com').send(new WelcomeEmail(user));
359
+ // In your tests
360
+ describe('User Registration', () => {
361
+ beforeEach(() => {
362
+ Mail.fake(); // Enable fake mode
363
+ });
364
+
365
+ afterEach(() => {
366
+ Mail.restore(); // Restore real mailer
367
+ });
368
+
369
+ it('sends welcome email on registration', async () => {
370
+ // Your application code that sends email
371
+ await Mail.to('user@example.com').send(new WelcomeEmail('John'));
372
+
373
+ // Assert email was sent
374
+ Mail.assertSent(WelcomeEmail);
375
+
376
+ // Assert with conditions
377
+ Mail.assertSent(WelcomeEmail, (mail) => {
378
+ return mail.hasTo('user@example.com') &&
379
+ mail.subjectContains('Welcome');
380
+ });
381
+
382
+ // Assert sent count
383
+ Mail.assertSentCount(WelcomeEmail, 1);
384
+
385
+ // Assert other mailables were NOT sent
386
+ Mail.assertNotSent(PasswordResetEmail);
387
+ });
388
+
389
+ it('does not send email when validation fails', async () => {
390
+ // Code that doesn't send email
391
+ Mail.assertNothingSent();
392
+ });
393
+ });
394
+ ```
395
+
396
+ ### Available Assertions
397
+
398
+ | Method | Description |
399
+ |--------|-------------|
400
+ | `Mail.fake()` | Enable fake mode (store emails instead of sending) |
401
+ | `Mail.restore()` | Restore real mailer |
402
+ | `Mail.assertSent(Mailable)` | Assert mailable was sent |
403
+ | `Mail.assertSent(Mailable, callback)` | Assert with custom conditions |
404
+ | `Mail.assertSentCount(Mailable, count)` | Assert sent exactly N times |
405
+ | `Mail.assertNotSent(Mailable)` | Assert mailable was NOT sent |
406
+ | `Mail.assertNothingSent()` | Assert no emails were sent |
407
+ | `Mail.assertQueued(Mailable)` | Assert mailable was queued |
408
+ | `Mail.assertNothingQueued()` | Assert nothing was queued |
409
+ | `Mail.sent()` | Get all sent messages |
410
+ | `Mail.sent(Mailable)` | Get sent messages of specific type |
411
+
412
+ ### AssertableMessage Methods
413
+
414
+ When inspecting sent messages, you can use these helper methods:
415
+
416
+ ```typescript
417
+ const sent = Mail.sent(WelcomeEmail)[0];
418
+
419
+ // Check recipients
420
+ sent.hasTo('user@example.com'); // Check TO
421
+ sent.hasCc('cc@example.com'); // Check CC
422
+ sent.hasBcc('bcc@example.com'); // Check BCC
423
+
424
+ // Check content
425
+ sent.hasSubject('Welcome!'); // Exact subject match
426
+ sent.subjectContains('Welcome'); // Subject contains
427
+ sent.htmlContains('Hello'); // HTML contains
428
+ sent.textContains('Hello'); // Plain text contains
429
+
430
+ // Check attachments
431
+ sent.hasAttachments(); // Has any attachments
432
+ sent.hasAttachment('file.pdf'); // Has specific attachment
433
+
434
+ // Check headers
435
+ sent.hasHeader('X-Custom'); // Has header
436
+ sent.hasHeader('X-Custom', 'value'); // Header with value
437
+
438
+ // Get values
439
+ sent.getTo(); // Get recipients array
440
+ sent.getSubject(); // Get subject
441
+ sent.getHtml(); // Get HTML content
414
442
  ```
415
443
 
416
444
  ## ๐Ÿ› ๏ธ Current Status
@@ -428,20 +456,37 @@ await Mail.to('user@example.com').send(new WelcomeEmail(user));
428
456
  - โœ… SMTP Provider (nodemailer)
429
457
  - โœ… SendGrid Provider (@sendgrid/mail)
430
458
  - โœ… AWS SES Provider (@aws-sdk/client-ses)
431
- - โœ… Message builder with fluent API
459
+ - โœ… Message builder with complete fluent API
432
460
  - โœ… Configuration system
433
461
  - โœ… Error handling & graceful degradation
434
- - ๐Ÿšง Other providers (Mailgun, Resend, Postmark) - coming soon
435
462
 
436
- **Phase 3: Advanced Features** ๐Ÿšง Next
437
- - Enhanced Mailable classes with template support
438
- - Additional providers (Mailgun, Resend, Postmark)
463
+ **Phase 3: Additional Providers** โœ… Complete
464
+ - โœ… Mailgun Provider (mailgun.js)
465
+ - โœ… Resend Provider (resend)
466
+ - โœ… Postmark Provider (postmark)
467
+ - โœ… Dynamic loading for all providers
468
+ - โœ… Comprehensive provider tests
469
+
470
+ **Phase 4: Template Engines & Mailable** โœ… Complete
471
+ - โœ… Template engines (Handlebars, EJS, Pug)
472
+ - โœ… Laravel-like Mailable classes with template support
473
+ - โœ… Complete fluent API (cc, bcc, replyTo, attachments, headers)
474
+ - โœ… Dynamic template loading with caching
475
+
476
+ **Phase 5: Testing Utilities** โœ… Complete (v0.5.0)
477
+ - โœ… Mail::fake() for testing
478
+ - โœ… assertSent(), assertNotSent(), assertNothingSent()
479
+ - โœ… assertQueued(), assertNothingQueued()
480
+ - โœ… AssertableMessage with inspection methods
481
+ - โœ… Comprehensive test suite (172 tests)
482
+ - โœ… 85%+ code coverage
483
+
484
+ **Phase 6: Advanced Features** ๐Ÿšง Coming Soon
439
485
  - Queue integration (Bull/BullMQ)
440
- - Template engines (Handlebars, EJS, Pug)
441
- - Testing utilities (Mail::fake(), assertSent())
442
- - Unit test coverage
443
486
  - CLI tools
444
487
  - Markdown mail support
488
+ - Multi-channel notifications
489
+ - i18n support
445
490
 
446
491
  ## ๐Ÿค Contributing
447
492
 
@@ -504,7 +549,7 @@ Unlike other packages that bundle everything:
504
549
  - **Type-Safe**: Full TypeScript support with strict typing
505
550
  - **Developer-Friendly**: Clean, intuitive API
506
551
  - **Production-Ready**: Built with best practices
507
- - **Well-Tested**: Comprehensive test coverage (coming soon)
552
+ - **Well-Tested**: 172 passing tests with 85%+ coverage
508
553
 
509
554
  ## ๐Ÿ“„ License
510
555
 
@@ -1,14 +1,58 @@
1
1
  import { MailManager } from './MailManager';
2
- import type { MailConfig } from '../types';
2
+ import type { MailConfig, MailOptions, MailResponse, QueueJobResult } from '../types';
3
+ import type { Mailable } from './Mailable';
4
+ import { MailFake } from '../testing/MailFake';
5
+ import type { AssertableMessage } from '../testing/AssertableMessage';
3
6
  declare class MailFacade {
4
7
  private static instance;
8
+ private static fakeInstance;
9
+ private static config;
5
10
  static configure(config: MailConfig): void;
6
11
  private static getInstance;
7
- static to(address: string | string[]): import("./MailManager").MessageBuilder;
12
+ private static isFaking;
13
+ static to(address: string | string[]): FakeableMessageBuilder;
8
14
  static mailer(name: string): MailManager;
9
- static send(mailable: import('./Mailable').Mailable): Promise<import('../types').MailResponse>;
10
- static fake(): void;
11
- static assertSent(): void;
15
+ static send(mailable: Mailable): Promise<MailResponse>;
16
+ static queue(mailable: Mailable): Promise<QueueJobResult>;
17
+ static later(mailable: Mailable, delaySeconds: number): Promise<QueueJobResult>;
18
+ static at(mailable: Mailable, date: Date): Promise<QueueJobResult>;
19
+ static processQueue(queueName?: string): Promise<void>;
20
+ static fake(): MailFake;
21
+ static restore(): void;
22
+ static getFake(): MailFake | null;
23
+ static assertSent<T extends Mailable>(mailableClass: new (...args: any[]) => T, callback?: (message: AssertableMessage) => boolean): void;
24
+ static assertSentCount<T extends Mailable>(mailableClass: new (...args: any[]) => T, count: number): void;
25
+ static assertNotSent<T extends Mailable>(mailableClass: new (...args: any[]) => T, callback?: (message: AssertableMessage) => boolean): void;
26
+ static assertNothingSent(): void;
27
+ static assertQueued<T extends Mailable>(mailableClass: new (...args: any[]) => T, callback?: (message: AssertableMessage) => boolean): void;
28
+ static assertQueuedCount<T extends Mailable>(mailableClass: new (...args: any[]) => T, count: number): void;
29
+ static assertNotQueued<T extends Mailable>(mailableClass: new (...args: any[]) => T, callback?: (message: AssertableMessage) => boolean): void;
30
+ static assertNothingQueued(): void;
31
+ static sent<T extends Mailable>(mailableClass?: new (...args: any[]) => T): AssertableMessage[];
32
+ static queued<T extends Mailable>(mailableClass?: new (...args: any[]) => T): AssertableMessage[];
33
+ static hasSent(): boolean;
34
+ static hasQueued(): boolean;
35
+ }
36
+ declare class FakeableMessageBuilder {
37
+ private manager;
38
+ options: Partial<MailOptions>;
39
+ constructor(manager: MailManager | MailFake, to: string | string[]);
40
+ subject(subject: string): this;
41
+ html(html: string): this;
42
+ text(text: string): this;
43
+ from(from: string): this;
44
+ cc(cc: string | string[]): this;
45
+ bcc(bcc: string | string[]): this;
46
+ replyTo(replyTo: string): this;
47
+ attachments(attachments: import('../types').Attachment[]): this;
48
+ headers(headers: Record<string, string>): this;
49
+ template(template: string): this;
50
+ data(data: Record<string, unknown>): this;
51
+ send(mailable?: Mailable): Promise<MailResponse>;
52
+ queue(mailable?: Mailable): Promise<QueueJobResult>;
53
+ later(delaySeconds: number, mailable?: Mailable): Promise<QueueJobResult>;
54
+ at(date: Date, mailable?: Mailable): Promise<QueueJobResult>;
55
+ private getMailOptions;
12
56
  }
13
57
  export { MailFacade as Mail };
14
58
  //# sourceMappingURL=MailFacade.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"MailFacade.d.ts","sourceRoot":"","sources":["../../src/core/MailFacade.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAE3C,cAAM,UAAU;IACd,OAAO,CAAC,MAAM,CAAC,QAAQ,CAA4B;IAKnD,MAAM,CAAC,SAAS,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI;IAO1C,OAAO,CAAC,MAAM,CAAC,WAAW;IAU1B,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE;IAOpC,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,MAAM;WAOb,IAAI,CAAC,QAAQ,EAAE,OAAO,YAAY,EAAE,QAAQ,GAAG,OAAO,CAAC,OAAO,UAAU,EAAE,YAAY,CAAC;IAQpG,MAAM,CAAC,IAAI,IAAI,IAAI;IAQnB,MAAM,CAAC,UAAU,IAAI,IAAI;CAI1B;AAED,OAAO,EAAE,UAAU,IAAI,IAAI,EAAE,CAAC"}
1
+ {"version":3,"file":"MailFacade.d.ts","sourceRoot":"","sources":["../../src/core/MailFacade.ts"],"names":[],"mappings":"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"}
@@ -2,9 +2,13 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.Mail = void 0;
4
4
  const MailManager_1 = require("./MailManager");
5
+ const MailFake_1 = require("../testing/MailFake");
5
6
  class MailFacade {
6
7
  static instance = null;
8
+ static fakeInstance = null;
9
+ static config = null;
7
10
  static configure(config) {
11
+ this.config = config;
8
12
  this.instance = new MailManager_1.MailManager(config);
9
13
  }
10
14
  static getInstance() {
@@ -13,22 +17,300 @@ class MailFacade {
13
17
  }
14
18
  return this.instance;
15
19
  }
20
+ static isFaking() {
21
+ return this.fakeInstance !== null;
22
+ }
16
23
  static to(address) {
17
- return this.getInstance().to(address);
24
+ if (this.isFaking()) {
25
+ return new FakeableMessageBuilder(this.fakeInstance, address);
26
+ }
27
+ return new FakeableMessageBuilder(this.getInstance(), address);
18
28
  }
19
29
  static mailer(name) {
20
30
  return this.getInstance().mailer(name);
21
31
  }
22
32
  static async send(mailable) {
33
+ if (this.isFaking()) {
34
+ const options = mailable.getMailOptions();
35
+ return this.fakeInstance.send(options, mailable);
36
+ }
23
37
  mailable.setMailManager(this.getInstance());
24
38
  return mailable.send();
25
39
  }
40
+ static async queue(mailable) {
41
+ if (this.isFaking()) {
42
+ const options = mailable.getMailOptions();
43
+ await this.fakeInstance.queue(options, mailable);
44
+ return { success: true, jobId: `fake-${Date.now()}`, queue: 'mail' };
45
+ }
46
+ mailable.setMailManager(this.getInstance());
47
+ return this.getInstance().queue(mailable.getMailOptions());
48
+ }
49
+ static async later(mailable, delaySeconds) {
50
+ if (this.isFaking()) {
51
+ const options = mailable.getMailOptions();
52
+ await this.fakeInstance.later(options, delaySeconds, mailable);
53
+ return { success: true, jobId: `fake-${Date.now()}`, queue: 'mail' };
54
+ }
55
+ mailable.setMailManager(this.getInstance());
56
+ return this.getInstance().later(mailable.getMailOptions(), delaySeconds);
57
+ }
58
+ static async at(mailable, date) {
59
+ if (this.isFaking()) {
60
+ const options = mailable.getMailOptions();
61
+ await this.fakeInstance.at(options, date, mailable);
62
+ return { success: true, jobId: `fake-${Date.now()}`, queue: 'mail' };
63
+ }
64
+ mailable.setMailManager(this.getInstance());
65
+ return this.getInstance().at(mailable.getMailOptions(), date);
66
+ }
67
+ static async processQueue(queueName) {
68
+ if (this.isFaking()) {
69
+ return;
70
+ }
71
+ return this.getInstance().processQueue(queueName);
72
+ }
26
73
  static fake() {
27
- throw new Error('Mail.fake() not yet implemented');
74
+ this.fakeInstance = new MailFake_1.MailFake(this.config || undefined);
75
+ return this.fakeInstance;
76
+ }
77
+ static restore() {
78
+ this.fakeInstance = null;
79
+ }
80
+ static getFake() {
81
+ return this.fakeInstance;
82
+ }
83
+ static assertSent(mailableClass, callback) {
84
+ if (!this.fakeInstance) {
85
+ throw new Error('Mail::fake() must be called before using assertions.');
86
+ }
87
+ this.fakeInstance.assertSent(mailableClass, callback);
28
88
  }
29
- static assertSent() {
30
- throw new Error('Mail.assertSent() not yet implemented');
89
+ static assertSentCount(mailableClass, count) {
90
+ if (!this.fakeInstance) {
91
+ throw new Error('Mail::fake() must be called before using assertions.');
92
+ }
93
+ this.fakeInstance.assertSentCount(mailableClass, count);
94
+ }
95
+ static assertNotSent(mailableClass, callback) {
96
+ if (!this.fakeInstance) {
97
+ throw new Error('Mail::fake() must be called before using assertions.');
98
+ }
99
+ this.fakeInstance.assertNotSent(mailableClass, callback);
100
+ }
101
+ static assertNothingSent() {
102
+ if (!this.fakeInstance) {
103
+ throw new Error('Mail::fake() must be called before using assertions.');
104
+ }
105
+ this.fakeInstance.assertNothingSent();
106
+ }
107
+ static assertQueued(mailableClass, callback) {
108
+ if (!this.fakeInstance) {
109
+ throw new Error('Mail::fake() must be called before using assertions.');
110
+ }
111
+ this.fakeInstance.assertQueued(mailableClass, callback);
112
+ }
113
+ static assertQueuedCount(mailableClass, count) {
114
+ if (!this.fakeInstance) {
115
+ throw new Error('Mail::fake() must be called before using assertions.');
116
+ }
117
+ this.fakeInstance.assertQueuedCount(mailableClass, count);
118
+ }
119
+ static assertNotQueued(mailableClass, callback) {
120
+ if (!this.fakeInstance) {
121
+ throw new Error('Mail::fake() must be called before using assertions.');
122
+ }
123
+ this.fakeInstance.assertNotQueued(mailableClass, callback);
124
+ }
125
+ static assertNothingQueued() {
126
+ if (!this.fakeInstance) {
127
+ throw new Error('Mail::fake() must be called before using assertions.');
128
+ }
129
+ this.fakeInstance.assertNothingQueued();
130
+ }
131
+ static sent(mailableClass) {
132
+ if (!this.fakeInstance) {
133
+ throw new Error('Mail::fake() must be called before using sent().');
134
+ }
135
+ return this.fakeInstance.sent(mailableClass);
136
+ }
137
+ static queued(mailableClass) {
138
+ if (!this.fakeInstance) {
139
+ throw new Error('Mail::fake() must be called before using queued().');
140
+ }
141
+ return this.fakeInstance.queued(mailableClass);
142
+ }
143
+ static hasSent() {
144
+ if (!this.fakeInstance) {
145
+ throw new Error('Mail::fake() must be called before using hasSent().');
146
+ }
147
+ return this.fakeInstance.hasSent();
148
+ }
149
+ static hasQueued() {
150
+ if (!this.fakeInstance) {
151
+ throw new Error('Mail::fake() must be called before using hasQueued().');
152
+ }
153
+ return this.fakeInstance.hasQueued();
31
154
  }
32
155
  }
33
156
  exports.Mail = MailFacade;
157
+ class FakeableMessageBuilder {
158
+ manager;
159
+ options = {};
160
+ constructor(manager, to) {
161
+ this.manager = manager;
162
+ this.options.to = to;
163
+ }
164
+ subject(subject) {
165
+ this.options.subject = subject;
166
+ return this;
167
+ }
168
+ html(html) {
169
+ this.options.html = html;
170
+ return this;
171
+ }
172
+ text(text) {
173
+ this.options.text = text;
174
+ return this;
175
+ }
176
+ from(from) {
177
+ this.options.from = from;
178
+ return this;
179
+ }
180
+ cc(cc) {
181
+ this.options.cc = cc;
182
+ return this;
183
+ }
184
+ bcc(bcc) {
185
+ this.options.bcc = bcc;
186
+ return this;
187
+ }
188
+ replyTo(replyTo) {
189
+ this.options.replyTo = replyTo;
190
+ return this;
191
+ }
192
+ attachments(attachments) {
193
+ this.options.attachments = attachments;
194
+ return this;
195
+ }
196
+ headers(headers) {
197
+ this.options.headers = headers;
198
+ return this;
199
+ }
200
+ template(template) {
201
+ this.options.template = template;
202
+ return this;
203
+ }
204
+ data(data) {
205
+ this.options.data = data;
206
+ return this;
207
+ }
208
+ async send(mailable) {
209
+ if (this.manager instanceof MailFake_1.MailFake) {
210
+ if (mailable) {
211
+ const mailOptions = mailable.getMailOptions();
212
+ return this.manager.send({ ...mailOptions, to: this.options.to }, mailable);
213
+ }
214
+ if (!this.options.subject) {
215
+ throw new Error('Email subject is required');
216
+ }
217
+ return this.manager.send(this.options);
218
+ }
219
+ const realManager = this.manager;
220
+ if (!(realManager instanceof MailManager_1.MailManager)) {
221
+ throw new Error('Invalid mail manager');
222
+ }
223
+ if (mailable) {
224
+ mailable.setMailManager(realManager);
225
+ const mailOptions = mailable.getMailOptions();
226
+ return realManager.send({
227
+ ...mailOptions,
228
+ to: this.options.to,
229
+ });
230
+ }
231
+ if (!this.options.subject) {
232
+ throw new Error('Email subject is required');
233
+ }
234
+ return realManager.send(this.options);
235
+ }
236
+ async queue(mailable) {
237
+ if (this.manager instanceof MailFake_1.MailFake) {
238
+ if (mailable) {
239
+ const mailOptions = mailable.getMailOptions();
240
+ await this.manager.queue({ ...mailOptions, to: this.options.to }, mailable);
241
+ }
242
+ else {
243
+ if (!this.options.subject) {
244
+ throw new Error('Email subject is required');
245
+ }
246
+ await this.manager.queue(this.options);
247
+ }
248
+ return { success: true, jobId: `fake-${Date.now()}`, queue: 'mail' };
249
+ }
250
+ const realManager = this.manager;
251
+ if (!(realManager instanceof MailManager_1.MailManager)) {
252
+ throw new Error('Invalid mail manager');
253
+ }
254
+ const mailOptions = this.getMailOptions(mailable);
255
+ return realManager.queue(mailOptions);
256
+ }
257
+ async later(delaySeconds, mailable) {
258
+ if (this.manager instanceof MailFake_1.MailFake) {
259
+ if (mailable) {
260
+ const mailOptions = mailable.getMailOptions();
261
+ await this.manager.later({ ...mailOptions, to: this.options.to }, delaySeconds, mailable);
262
+ }
263
+ else {
264
+ if (!this.options.subject) {
265
+ throw new Error('Email subject is required');
266
+ }
267
+ await this.manager.later(this.options, delaySeconds);
268
+ }
269
+ return { success: true, jobId: `fake-${Date.now()}`, queue: 'mail' };
270
+ }
271
+ const realManager = this.manager;
272
+ if (!(realManager instanceof MailManager_1.MailManager)) {
273
+ throw new Error('Invalid mail manager');
274
+ }
275
+ const mailOptions = this.getMailOptions(mailable);
276
+ return realManager.later(mailOptions, delaySeconds);
277
+ }
278
+ async at(date, mailable) {
279
+ if (this.manager instanceof MailFake_1.MailFake) {
280
+ if (mailable) {
281
+ const mailOptions = mailable.getMailOptions();
282
+ await this.manager.at({ ...mailOptions, to: this.options.to }, date, mailable);
283
+ }
284
+ else {
285
+ if (!this.options.subject) {
286
+ throw new Error('Email subject is required');
287
+ }
288
+ await this.manager.at(this.options, date);
289
+ }
290
+ return { success: true, jobId: `fake-${Date.now()}`, queue: 'mail' };
291
+ }
292
+ const realManager = this.manager;
293
+ if (!(realManager instanceof MailManager_1.MailManager)) {
294
+ throw new Error('Invalid mail manager');
295
+ }
296
+ const mailOptions = this.getMailOptions(mailable);
297
+ return realManager.at(mailOptions, date);
298
+ }
299
+ getMailOptions(mailable) {
300
+ if (mailable) {
301
+ if (this.manager instanceof MailManager_1.MailManager) {
302
+ mailable.setMailManager(this.manager);
303
+ }
304
+ const mailOptions = mailable.getMailOptions();
305
+ return {
306
+ ...mailOptions,
307
+ to: this.options.to,
308
+ };
309
+ }
310
+ if (!this.options.subject) {
311
+ throw new Error('Email subject is required');
312
+ }
313
+ return this.options;
314
+ }
315
+ }
34
316
  //# sourceMappingURL=MailFacade.js.map