Skip to content

Commit

Permalink
Implement tests for all basic functionality
Browse files Browse the repository at this point in the history
Also fix sendMail's options to accept the template and context properties.
  • Loading branch information
dantman committed Mar 26, 2019
1 parent d19eb7c commit 0ab00b0
Show file tree
Hide file tree
Showing 8 changed files with 4,670 additions and 79 deletions.
16 changes: 7 additions & 9 deletions jest.config.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,17 @@
require('ts-node/register');
require('./server/polyfills');

module.exports = {
'moduleFileExtensions': [
'ts',
'js',
'json'
'json',
'ts',
],
'transform': {
'^.+\\.ts$': 'ts-jest'
},
'testRegex': '\/lib\/.*\\.spec\\.(ts|js)$',
'rootDir': 'lib',
'testRegex': '/lib/.*\\.spec\\.(ts|js)$',
'globals': {
'ts-jest': {
'tsConfigFile': 'tsconfig.json'
'tsConfig': 'tsconfig.json'
}
}
},
'preset': 'ts-jest',
};
7 changes: 7 additions & 0 deletions lib/interfaces/mailer-send-mail-options.interface.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/** Dependencies **/
import { SendMailOptions } from 'nodemailer';

export interface MailerSendMailOptions extends SendMailOptions {
template?: string;
context?: any;
}
195 changes: 195 additions & 0 deletions lib/mailer.service.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
import { Test, TestingModule } from "@nestjs/testing";
import MailMessage = require('nodemailer/lib/mailer/mail-message');
import SMTPTransport = require('nodemailer/lib/smtp-transport');
import { MAILER_OPTIONS } from "./constants/mailer-options.constant";
import { MailerOptions } from "./interfaces/mailer-options.interface";
import { MailerService } from "./mailer.service";
import { HandlebarsAdapter } from './adapters/handlebars.adapter';
import { PugAdapter } from './adapters/pug.adapter';

/**
* Common testing code for testing up a testing module and MailerService
*/
async function getMailerServiceForOptions(
options: MailerOptions
): Promise<MailerService> {
const module: TestingModule = await Test.createTestingModule({
providers: [
{
name: MAILER_OPTIONS,
provide: MAILER_OPTIONS,
useValue: options
},
MailerService
]
}).compile();

const service = module.get<MailerService>(MailerService);

return service;
}

/**
* Common testing code for spying on the SMTPTransport's send() implementation
*/
function spyOnSmtpSend(onMail: (mail: MailMessage) => void) {
return jest.spyOn(SMTPTransport.prototype, 'send').mockImplementation(function (mail: MailMessage, callback: (err: Error | null, info: SMTPTransport.SentMessageInfo) => void): void {
onMail(mail);
callback(null, {
envelope: {
from: mail.data.from as string,
to: [mail.data.to as string]
},
messageId: 'ABCD'
});
});
}

describe("MailerService", () => {
it("should not be defined if a transport is not provided", async () => {
await expect(getMailerServiceForOptions({})).rejects.toMatchInlineSnapshot(
`[Error: Make sure to provide a nodemailer transport configuration object, connection url or a transport plugin instance.]`
);
});

it("should accept a smtp transport string", async () => {
const service = await getMailerServiceForOptions({
transport: "smtps://[email protected]:[email protected]"
});

expect(service).toBeDefined();
expect((service as any).transporter.transporter).toBeInstanceOf(SMTPTransport);
});

it("should accept smtp transport options", async () => {
const service = await getMailerServiceForOptions({
transport: {
secure: true,
auth: {
user: '[email protected]',
pass: 'pass',
},
options: {
host: 'smtp.domain.com',
},
}
});

expect(service).toBeDefined();
expect((service as any).transporter.transporter).toBeInstanceOf(SMTPTransport);
});


it("should accept a smtp transport instance", async () => {
const transport = new SMTPTransport({})
const service = await getMailerServiceForOptions({
transport: transport
});

expect(service).toBeDefined();
expect((service as any).transporter.transporter).toBe(transport);
});

it('should send emails with nodemailer', async () => {
let lastMail: MailMessage;
const send = spyOnSmtpSend((mail: MailMessage) => {
lastMail = mail;
});

const service = await getMailerServiceForOptions({
transport: "smtps://[email protected]:[email protected]"
});

await service.sendMail({
from: '[email protected]',
to: '[email protected]',
subject: 'Test',
html: 'This is test.'
});

expect(send).toHaveBeenCalled();
expect(lastMail.data.from).toBe('[email protected]');
expect(lastMail.data.to).toBe('[email protected]');
expect(lastMail.data.subject).toBe('Test');
expect(lastMail.data.html).toBe('This is test.');
});

it('should use mailerOptions.defaults when send emails', async () => {
let lastMail: MailMessage;
const send = spyOnSmtpSend((mail: MailMessage) => {
lastMail = mail;
});

const service = await getMailerServiceForOptions({
transport: "smtps://[email protected]:[email protected]",
defaults: {
from: '[email protected]'
}
});

await service.sendMail({
to: '[email protected]',
subject: 'Test',
html: 'This is test.'
});

expect(send).toHaveBeenCalled();
expect(lastMail.data.from).toBe('[email protected]');
});

it('should compile template with the handlebars adapter', async () => {
let lastMail: MailMessage;
const send = spyOnSmtpSend((mail: MailMessage) => {
lastMail = mail;
});

const service = await getMailerServiceForOptions({
transport: new SMTPTransport({}),
template: {
adapter: new HandlebarsAdapter(),
}
});

await service.sendMail({
from: '[email protected]',
to: '[email protected]',
subject: 'Test',
template: __dirname + '/test-templates/handlebars-template',
context: {
world: 'World',
},
});

expect(send).toHaveBeenCalled();
expect(lastMail.data.from).toBe('[email protected]');
expect(lastMail.data.html).toBe('<p>Handlebars test template.</p>\n<p>Hello World!</p>\n');
});

it('should compile template with the pug adapter', async () => {
let lastMail: MailMessage;
const send = spyOnSmtpSend((mail: MailMessage) => {
lastMail = mail;
});

const service = await getMailerServiceForOptions({
transport: new SMTPTransport({}),
template: {
adapter: new PugAdapter(),
}
});

await service.sendMail({
from: '[email protected]',
to: '[email protected]',
subject: 'Test',
template: __dirname + '/test-templates/pug-template',
context: {
world: 'World',
},
});

expect(send).toHaveBeenCalled();
expect(lastMail.data.from).toBe('[email protected]');
expect(lastMail.data.html).toBe('<p>Pug test template.</p><p>Hello World!</p>');
});
});
5 changes: 3 additions & 2 deletions lib/mailer.service.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
/** Dependencies **/
import { get } from 'lodash';
import { Injectable, Inject } from '@nestjs/common';
import { createTransport, SentMessageInfo, Transporter, SendMailOptions } from 'nodemailer';
import { createTransport, SentMessageInfo, Transporter } from 'nodemailer';
import { MailerSendMailOptions } from './interfaces/mailer-send-mail-options.interface';

/** Constants **/
import { MAILER_OPTIONS } from './constants/mailer-options.constant';
Expand Down Expand Up @@ -36,7 +37,7 @@ export class MailerService {
}
}

public async sendMail(sendMailOptions: SendMailOptions): Promise<SentMessageInfo> {
public async sendMail(sendMailOptions: MailerSendMailOptions): Promise<SentMessageInfo> {
return await this.transporter.sendMail(sendMailOptions);
}
}
2 changes: 2 additions & 0 deletions lib/test-templates/handlebars-template.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
<p>Handlebars test template.</p>
<p>Hello {{world}}!</p>
2 changes: 2 additions & 0 deletions lib/test-templates/pug-template.pug
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
p Pug test template.
p= "Hello " + world + "!"
Loading

0 comments on commit 0ab00b0

Please sign in to comment.