How to email multiple recipients in sendgrid v3 node.js - node.js

Can someone help me send an email to multiple recipients in sendgrid v3 + node.js? I've noticed that when I enter several email addresses in the to field, only the first email address receives the email. The email addresses after the first one do not receive the email:
send: function(email, callback) {
var from_email = new helper.Email(email.from);
var to_email = new helper.Email('emailUser1#gmail.com,emailUser2#gmail.com,emailUser3#gmail.com');
var subject = email.subject;
var content = email.content
var mail = new helper.Mail(from_email, subject, to_email, content);
var sg = require('sendgrid')(process.env.SENDGRID_API_KEY);
var request = sg.emptyRequest({
method: 'POST',
path: '/v3/mail/send',
body: mail.toJSON(),
});
sg.API(request, function(err, res) {
console.log(res);
if(err) {
console.log('---error sending email:---');
console.log(err);
console.log(err.response.body);
callback(500);
} else {
callback(200);
}
});
}
In the example above, only emailUser1#gmail.com receives the email; emailUser2#gmail.com and emailUser3#gmail.com do not receive the email.
Can someone help?
Thanks in advance!

The node js mail helper allows you to send to multiple recipients by specifying the to property as an array. Then depending on whether you want the recipients to be able to see each other's addresses, you send the message in slightly different ways:
To allow seeing, use sgMail.send(msg):
const sgMail = require('#sendgrid/mail');
sgMail.setApiKey(process.env.SENDGRID_API_KEY);
const msg = {
to: ['recipient1#example.org', 'recipient2#example.org'],
from: 'sender#example.org',
subject: 'Hello world',
text: 'Hello plain world!',
html: '<p>Hello HTML world!</p>',
};
sgMail.send(msg);
To prevent seeing, use sgMail.sendMultiple(msg) or sgMail.send(msg, true)
const sgMail = require('#sendgrid/mail');
sgMail.setApiKey(process.env.SENDGRID_API_KEY);
const msg = {
to: ['recipient1#example.org', 'recipient2#example.org'],
from: 'sender#example.org',
subject: 'Hello world',
text: 'Hello plain world!',
html: '<p>Hello HTML world!</p>',
};
sgMail.sendMultiple(msg);
https://github.com/sendgrid/sendgrid-nodejs/blob/b3b2092b7a150ffc5d68c9bb6575810a5827f69e/docs/use-cases/single-email-multiple-recipients.md
Under the covers the helper uses Personalizations which you can use for greater control:
https://sendgrid.com/docs/for-developers/sending-email/personalizations/

const sgMail = require('#sendgrid/mail');
module.exports.send = function () {
sgMail.setApiKey('XYZ');
const msg = {
to: ['abc#gmal.com', 'xyz#gmail.com'],
cc: ['test#gmail.com', 'testing#gmail.com'],
from: 'no-reply#mail.total.fr',
subject: 'Subject of mail',
html: 'html body',
text: 'text message'
};
// console.log('message in mail :: ', msg);
sgMail.send(msg).catch(console.error);
};

Are you using SendGrid's Helper Library? You're going to want to leverage Personalizations.
If you want the recipients to see each other, you should name & populate each of the recipients within a single Personalization object. If you don't want them to see each other, and want them to each receive the message distinctly, you'll want to make a new Personalization object for each distinct recipient group.

Sendgrid API V3
Hope this helps.
https://www.npmjs.com/package/sendgrid-v3-node
Example:
https://runkit.com/embed/ne9asbfj59fr
var sendgrid = require("sendgrid-v3-node")
const mailOptions = {
sendgrid_key: 'SENDGRID_KEY',
from_email: 'FROM_EMAIL',
from_name: 'FROM_NAME',
to: ['TO_EMAIL1', 'TO_EMAIL2']
};
mailOptions.subject = 'SUBJECT';
mailOptions.content = 'CONTENT';
sendgrid.send_via_sendgrid(mailOptions).then(response => {
console.log(response);
});

{
"from": "sender#yourdomain.com",
"template_id": "YOUR TEMPLATE ID",
"personalizations": [
{
"to": [
{
"email": "john#example.com"
}
],
"send_at": 1600188812
},
{
"to": [
{
"email": "jane#example.com"
}
],
"send_at": 1600275471
}
]
}

Related

Nodejs and Mailgun: Send generated attachment *not* from file system

I generate attachments in a node app and would like to send them using Mailgun. I have no access to the file system (Netlify functions).
Is there an easy way to accomplish that?
The hole picture
It's a Jamstack (Gatsby/React, Netlify, Mailgun) web app. Customers configure and request offers. Browser generates and posts offers and images to a Netlify function (fetch api). The function send the offer mail with the PDF-offer and images attached.
Code I tested (edit: 02/25)
const path = require('path');
const fs = require('fs');
const mailgun = require('mailgun-js')
const FormData = require('form-data');
const { Readable } = require('stream');
const API_KEY = 'SECRET';
const DOMAIN = 'brasilius.de';
const mg = mailgun({apiKey: API_KEY, domain: DOMAIN, host: "api.eu.mailgun.net"});
const stream = fs.createReadStream('test.txt'); // This works
/* {
id: '<20210225115125.1.BF14CC322F8E0DAC#brasilius.de>',
message: 'Queued. Thank you.'
} */
/*
const stream = Readable.from(["Send this text as attachment."]) // Won't work
{ message: "'from' parameter is missing" }
*/
const data = {
from: 'Excited User <me#brasilius.de>',
to: 'test#user.de',
subject: 'Hello',
text: 'Testing some Mailgun awesomeness!',
attachment: stream
};
mg.messages().send(data, (error, body) => {
console.log(body);
});
The simplest solution I found here
// https://thecodebarbarian.com/sending-emails-using-the-mailgun-api.html
const mailgun = require('mailgun-js')
const mg = mailgun({apiKey: process.env.API_KEY, domain: process.env.DOMAIN, host: "api.eu.mailgun.net"});
const filename = 'test.txt';
const text = "Example test content."
const attch = new mg.Attachment({data: Buffer.from(text), filename: filename})
const data = {
from: process.env.FROM,
to: process.env.TO,
subject: 'Hello',
text: 'Testing Mailgun attachments.',
attachment: attch
};
mg.messages().send(data, (error, body) => {
console.log(body);
});
As #Joe recommended, a solution with Nodemailers Mailcomposer:
// See: https://documentation.mailgun.com/en/latest/api-sending.html#examples
// See: http://nodemailer.com/extras/mailcomposer/#attachments
const mailgun = require('mailgun-js')
const MailComposer = require('nodemailer/lib/mail-composer');
const mg = mailgun({apiKey: process.env.API_KEY, domain: process.env.DOMAIN, host: "api.eu.mailgun.net"});
const mailOptions = {
from: process.env.FROM,
subject: 'Hello',
text: 'Testing Mailgun attachments.',
attachments: [
{ // utf-8 string as an attachment
filename: 'text.txt',
content: 'For testing just a text file. This could be a ReadStream, Buffer or other.'
}
]
};
const mail = new MailComposer(mailOptions);
mail.compile().build(function(mailBuildError, message) {
var dataToSend = {
to: process.env.TO,
message: message.toString('ascii')
};
mg.messages().sendMime(dataToSend, function(sendError, body) {
if (sendError) {
console.log(sendError);
return;
}
});
});

GMail API Replying to Email Thread Using NodeJS

Dear All: I am sure many of you have discussed above topic multiple times after I am going through all the example and references I have managed to write the code to reply to same email ThreadID. But Unfortunately while I am responding to same ThreadID emails it's going as new email. I have attached my complete NodeJS Code help me to review and let me know where should I have to make the changes.
const {google} = require('googleapis');
const mailComposer = require('nodemailer/lib/mail-composer');
var program_name = process.argv[0]; //value will be "node"
var script_path = process.argv[1]; //value will be "yourscript.js"
var Sender_Email = process.argv[2]; //value will be "Sender Email"
var Receiver_Email = process.argv[3]; //value will be "Email To"
//var CC_Email = process.argv[4]; //value will be "Email Cc"
var Email_Subject = process.argv[4]; //value will be "Email Subject"
var Email_Template = process.argv[5]; //value will be "Email Template"
var ThreadID = process.argv[6]; //Path to attach the file
var Dec_Message_ID = process.argv[7]; //Encoded messageid
var FileAttachment = process.argv[8]; //Path to attach the file
var dateFormat = require('dateformat');
var day=dateFormat(new Date(), "mmm dd, yyyy HH:MM tt");
class CreateMail{
constructor(auth, to, cc, sub, body, task, attachmentSrc = [FileAttachment]){
this.me = Sender_Email;
this.task = task;
this.auth = auth;
this.to = Receiver_Email;
//this.cc = CC_Email;
this.sub = Email_Subject;
var fs = require('fs');
this.body = fs.readFileSync(Email_Template,{encoding:'utf-8'});
this.gmail = google.gmail({version: 'v1', auth});
this.attachment = attachmentSrc;
}
//Creates the mail body and encodes it to base64 format.
makeBody(){
if(this.attachment.length>0){
var arr = [];
for(var i=0;i<this.attachment.length;i++){
arr[i] = {
path: this.attachment[i],
encoding: 'base64'
}
}
}
let mail;
//Mail Body is created.
if(this.attachment.length>0){
mail = new mailComposer({
from: "Arthanareeswaran Chandrasekaran <arthaaadhi#visha.page>",
//sender: this.me,
to: this.to,
//cc: this.cc,
replyTo: this.to,
inReplyTo: "<CAO29sXBTxmE8M=xyTkdFfsrxB_Mdr5e6N6vXiijwTY9rn1kzpQ#mail.gmail.com>",
references: "<CAF7UyHwMrUvy-ZLNyRfjDmX876EKi5T-oc8E_tXy2PwO19dZ_Q#mail.gmail.com> <CAO29sXBH_B0yG4G2p6tdW1uk_tq9qFXmc01CPO5HJopkvMbU4Q#mail.gmail.com> <CAO29sXCcHv4LQSumjht_5zHEYvSzjfYkGr+yCEHfjwnqRvt0=Q#mail.gmail.com> <CAO29sXCPAxzWG0dC-TKEi4cR3xM8hbHhSJQ0ZAhbXBjsp503oA#mail.gmail.com> <CAO29sXA2mpqx6qbEeB5ke_6kUTrwXsqMD8ku0Aq3E_R07YzCLg#mail.gmail.com> <CAO29sXBTxmE8M=xyTkdFfsrxB_Mdr5e6N6vXiijwTY9rn1kzpQ#mail.gmail.com>",
subject: this.sub,
html: this.body,
textEncoding: "base64",
attachments: arr
});
}
else{
mail = new mailComposer({
to: this.to,
cc: this.cc,
html: this.body,
subject: this.sub,
textEncoding: "base64"
});
}
//Compiles and encodes the mail.
mail.compile().build((err, msg) => {
if (err){
return console.log('Error compiling email ' + error);
}
const encodedMessage = Buffer.from(msg)
.toString('base64')
.replace(/\+/g, '-')
.replace(/\//g, '_')
.replace(/=+$/, '');
if(this.task === 'mail'){
this.sendMail(encodedMessage);
}
else{
this.saveDraft(encodedMessage);
}
});
}
//Send the message to specified receiver.
sendMail(encodedMessage){
this.gmail.users.messages.send({
userId: this.me,
resource: {
raw: encodedMessage,
threadId: ThreadID
}
}, (err, result) => {
if(err){
return console.log('GMail API - The API returned an error: ' + err);
}
console.log("GMail API Sending Email Reply from server:", result.data);
});
}
//Saves the draft.
saveDraft(encodedMessage){
this.gmail.users.drafts.create({
'userId': this.me,
'resource': {
'message': {
'raw': encodedMessage,
threadId: ThreadID
}
}
})
}
//Deletes the draft.
deleteDraft(id){
this.attachment.gmail.users.drafts.delete({
id: id,
userId: this.me
});
}
}
module.exports = CreateMail;
Thanks for your help...
I suggest you update your code to this and check that the References, In-Reply-To and Subjects headers match:
function replyToMessage(auth) {
const gmail = google.gmail({
version: 'v1',
auth
});
const messages = [
'From: NAME <email#email.com>',
'To: NAME <email#email.com>',
'References: <REFERENCE1> <REFERENCE2>',
'In-Reply-To: <IN_REPLY_TO>',
'Content-Type: text/html; charset=utf-8',
'MIME-Version: 1.0',
'Subject: Re: SUBJECT',
'',
'BODY_OF_THE_REPLY',
'',
];
const message = messages.join('\n');
const encodedMessage = Buffer.from(message)
.toString('base64')
.replace(/\+/g, '-')
.replace(/\//g, '_')
.replace(/=+$/, '');
gmail.users.messages.send({
auth: auth,
userId: 'me',
resource: {
raw: encodedMessage,
threadId: 'THREAD_ID'
}
});
}
Also please bear in mind that in order to see the replies accordingly, you will have to turn on the Conversation View from Gmail Settings.
According to the documentation:
You can choose whether replies to emails are grouped in conversations, or if each email shows up in your inbox separately.
Hence, if this setting is not turned on, the email will show up separately in your inbox.
Reference
Gmail Help.

AWS Lambda not firing Email via nodemailer, but does in the local development environment

I'm working on a aws-lambda which is supposed to shoot mail when an event is triggered. I using nodejs for this and below is the code:
"use strict";
exports.sendEmail = function(event, context, callback) {
var config = require('./config');
var fs = require('fs');
var _ = require('lodash');
if (_validSchema(event.payload)) {
var templatePath = config.schemaMapping[event.payload.emailDetails.emailType]["templatePath"]
var emailHTML = _getHTML(templatePath, event.payload.params)
if (emailHTML && templatePath) {
_sendSESEmail(_emailParams(event.payload.emailDetails), emailHTML)
context.succeed(JSON.stringify(_setResponse(200, [{
code: "11",
source: "Email template or Email params in payload",
message: "Please provide correct Email template and correct email params",
detail: "Template path is provided via config and Params via Payload"
}])));
} else
context.fail(JSON.stringify(_setResponse(400, [{
code: "01",
source: "Email template or Email params in payload",
message: "Please provide correct Email template and correct email params",
detail: "Template path is provided via config and Params via Payload"
}])));
} else {
context.fail(JSON.stringify(_setResponse(400, [{
code: "02",
source: "Payload schema",
message: "Please provide correct schema to validate and a payload validating it",
detail: "Payload is provided "
}])));
}
function _validSchema(payload) {
var schemaPath = config.schemaMapping[payload.emailDetails.emailType]["schemaPath"];
var payloadVerification = _verifyPayload(payload, schemaPath);
console.log(payloadVerification.valid);
return payloadVerification.valid;
}
function _emailParams(emailDetails) {
var details = {};
details.to = _.join(emailDetails.to, ',');
details.from = emailDetails.from;
details.cc = _.join(emailDetails.cc, ',');
details.bcc = _.join(emailDetails.bcc, ',');
details.attachments = emailDetails.attachments;
details.subject = emailDetails.subject;
return details;
}
function _verifyPayload(payload, schemaPath) {
var schema = JSON.parse(fs.readFileSync(schemaPath, 'utf8'));
var Validator = require('jsonschema').Validator;
var verifier = new Validator();
console.log(verifier.validate(payload, schema))
return verifier.validate(payload, schema);
}
function _setResponse(status_code, error_list) {
return {
status: status_code,
errors: error_list
};
}
function _sendSESEmail(email, emailHTML) {
var nodemailer = require('nodemailer');
var sesTransport = require('nodemailer-ses-transport');
var transporter = nodemailer.createTransport(sesTransport({
accessKeyId: config.SES.accessKeyId,
secretAccessKey: config.SES.secretAccessKey
}));
transporter.sendMail({
from: email.from,
to: email.to,
cc: email.cc,
bcc: email.bcc,
attachments: email.attachments,
subject: email.subject,
html: emailHTML
});
}
function _getHTML(templateFile, params) {
var ejs = require('ejs');
console.log({ params: params })
var baseHTML = fs.readFileSync(templateFile, 'ascii');
return ejs.render(baseHTML, { params: params });
}
}
Above code works fine when tested in the dev environment with the below code, but does not fire a mail when tested on aws-lamda.
"use strict";
var exports = require('./exports');
var bankDetailsSchemaSample = {
"payload": {
"emailDetails": {
"from": 'some#something.com',
"to": ['kunal#something.com'],
"subject": 'My Amazon SES Simple Email',
"html": '',
"cc": ['nimesh.verma#something.com'],
"bcc": ['lokesh.gour#something.com'],
"emailType": 'bankDetails',
"attachments": [{
"filename": 'test.md',
"path": 'https://raw.github.com/nodemailer/nodemailer/master/LICENSE'
}]
},
"params": {
"orderId": 1234567,
"firstName": "Nimesh",
}
}
}
var context = {
fail: function(x) { console.log(" Fail " + x) },
succeed: function(x) { console.log(" Success " + x) }
}
exports.sendEmail(bankDetailsSchemaSample, context, {})
I can't find out, why this is happening, I also tried it using nodemailer-smtp-transport instead of nodemailer-ses-transport but the same results were obtained. When nothing helped I tried using aws-sdk instead of nodemailer and nodemailer-ses-transport and the mail is fired in both dev environment as well via aws lamda testing.
// load aws sdk
exports.sendEmail = function(event, context, callback) {
var aws = require('aws-sdk');
// load aws config
aws.config.loadFromPath('config.json');
// load AWS SES
var ses = new aws.SES({ apiVersion: '2010-12-01' });
// send to list
var to = ['nimesh.verma#something.com']
// this must relate to a verified SES account
var from = 'some#something.com'
// this sends the email
// #todo - add HTML version
ses.sendEmail({
Source: from,
Destination: { ToAddresses: to },
Message: {
Subject: {
Data: 'A Message To You Rudy'
},
Body: {
Text: {
Data: 'Stop your messing around',
}
}
}
}, function(err, data) {
if (err) throw err
console.log('Email sent:');
});
}
Why is this happening?
The problem was that the context.succeed method should be placed in the callback of _sendSESEmail method.
The complete working code is present at: https://github.com/nimeshkverma/aws-lambda-node-mailer

How to use pdf output of PDFMake in the attachment mail options of NodeMailer?

Here is a one of PDFMake's code snippet to create pdf file:
var pdfDoc = printer.createPdfKitDocument(docDefinition);
pdfDoc.pipe(fs.createWriteStream('pdfs/absolute.pdf'));
pdfDoc.end();
and here is one of the attachment option in NodeMailer:
{
// stream as an attachment
filename: 'text4.txt',
content: fs.createReadStream('file.txt')
}
I am able to create pdf and save it to a file in a directory and attach that saved file in the e-mail but I want to directly send the output of pdf to the attachment content of e-mail without saving the pdf output to a file.
I tried to understand nodejs pipe and stream features but couldn't understand them to meet my requirement. I think it should be possible through stream and pipe but don't know how to achieve.
I want to prevent saving of pdf output to file because there can be multiple users using the functionality of creating pdf file and sending e-mail. There can be a situation when a pdf file created by one user will be overwritten by another user.
you could instead of pipe, pass the entire pdfdoc to the attachment
var pdfDoc = printer.createPdfKitDocument(docDefinition);
pdfDoc.end();
and then:
attachments: {
// stream as an attachment
filename: 'text4.pdf',
content: pdfDoc
}
I was able to get it to work using a buffer, and ending the buffer AFTER the email was sent with the attachment from PDFMake. If you end the buffer before the email is sent it will not work:
const printer = new pdfMakePrinter(fontDescriptors);
const doc = printer.createPdfKitDocument(docDefinition);
let buffers = [];
doc.on('data', buffers.push.bind(buffers));
doc.on('end', () => {
let pdfData = Buffer.concat(buffers);
let mailOptions = {
from: '"John Doe" <email#gmail.com>', // sender address
to: 'receiver#gmail.com', // list of receivers
subject: 'stuff' // Subject line
text: '',
html: '', // html body
attachments: [{
filename: 'attachment.pdf',
content: pdfData
}]
};
transporter.sendMail(mailOptions, (error, info) => {
if (error) {
return console.log(error);
}
console.log('Message sent: %s', info.messageId);
console.log('Preview URL: %s', nodemailer.getTestMessageUrl(info));
});
});
//end buffer
doc.end();
Here's what I did. It works in a Meteor app.
Client Side:
var docDefinition = {
content: [
'First paragraph'
]
};
var data;
pdfMake.createPdf(docDefinition).getBase64(function(encodedString) {
data = encodedString;
Meteor.call("sendPDFEmail", data);
});
Server Side
sendPDFEmail: function(thepdf){
Email.send({
to: "receiver",
from: "sender",
subject: "Test",
html: "text",
attachments: [
{
filename: "test.pdf",
type: "application/pdf",
content: Buffer.from(thepdf, 'base64')
}
]
});
},
var pdfMake = require('pdfmake/build/pdfmake.js');
var pdfFonts = require('pdfmake/build/vfs_fonts.js');
pdfMake.vfs = pdfFonts.pdfMake.vfs;
//var base64 = require('base64-js');
var docDefinition = function(json) {
return {
content: [
'PARAGRAPH 1',
'paragraph 2', {
columns: [
'first column is a simple text', {
stack: [
// second column consists of paragraphs
'paragraph A',
'paragraph B',
'these paragraphs will be rendered one below another inside the column'
],
fontSize: 15
}]}]};};
const pdfDocGenerator = pdfMake.createPdf(docDefinition);
pdfDocGenerator.getBuffer((pdfBuffer) => {
// HERE YOU HAVE THE PDF BUFFER
//var pdfbase64 = base64.fromByteArray(buffer);
//console.log("pdfDocGenerator.getBuffer OK" + pdfbase64);
fs.writeFileSync('foo.pdf', buffer);
}

Mocking email function in nodejs

I've got a mailer function I've built and trying to shore up the coverage. Trying to test parts of it have proven tricky, specifically this mailer.smtpTransport.sendMail
var nodemailer = require('nodemailer')
var mailer = {}
mailer.smtpTransport = nodemailer.createTransport('SMTP', {
'service': 'Gmail',
'auth': {
'XOAuth2': {
'user': 'test#test.com',
'clientId': 'googleClientID',
'clientSecret': 'superSekrit',
'refreshToken': '1/refreshYoSelf'
}
}
})
var mailOptions = {
from: 'Some Admin <test#tester.com>',
}
mailer.verify = function(email, hash) {
var emailhtml = 'Welcome to TestCo. Click this '+hash+''
var emailtxt = 'Welcome to TestCo. This is your hash: '+hash
mailOptions.to = email
mailOptions.subject = 'Welcome to TestCo!'
mailOptions.html = emailhtml
mailOptions.text = emailtxt
mailer.smtpTransport.sendMail(mailOptions, function(error, response){
if(error) {
console.log(error)
} else {
console.log('Message sent: '+response.message)
}
})
}
I'm unsure of how to go about testing, specifically ensuring that my mailer.smtpTransport.sendMail function is passing the correct parameters without actually sending the email. I'm trying to use https://github.com/whatser/mock-nodemailer/tree/master, but I'm probably doing it wrong. Should I be mocking out the method?
var _ = require('lodash')
var should = require('should')
var nodemailer = require('nodemailer')
var mockMailer = require('./helpers/mock-nodemailer')
var transport = nodemailer.createTransport('SMTP', '')
var mailer = require('../../../server/lib/account/mailer')
describe('Mailer', function() {
describe('.verify()', function() {
it('sends a verify email with a hashto an address when invoked', function(done) {
var email ={
'to': 'dave#testco.com',
'html': 'Welcome to Testco. Click this bleh',
'text': 'Welcome to Testco. This is your hash: bleh',
'subject': 'Welcome to Testco!'
}
mockMailer.expectEmail(function(sentEmail) {
return _.isEqual(email, sentEmail)
}, done)
mailer.verify('dave#testco.com','bleh')
transport.sendMail(email, function() {})
})
})
You can use a 'Stub' transport layer on your test instead of SMTP.
var stubMailer = require("nodemailer").createTransport("Stub"),
options = {
from: "from#email.com",
to: "to#email.com",
text: "My Message!"
};
stubMailer.sendMail(options, function(err, response){
var message = response.message;
})
So, in that case, 'message' will be the email in text format. Something like this:
MIME-Version: 1.0
X-Mailer: Nodemailer (0.3.43; +http://www.nodemailer.com/)
Date: Fri, 25 Feb 2014 11:11:48 GMT
Message-Id: <123412341234.e23232#Nodemailer>
From: from#email.com
To: to#email.com
Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: quoted-printable
My Message!
For more examples, take a look at nodemailer test suite:
https://github.com/andris9/Nodemailer/blob/master/test/nodemailer-test.js
You can directly mock the sendMail function but it's not obvious how to access it from the tests. A Mailer instance is returned when you create a transport so you need to directly import that class in to your test.
const Mailer = require('nodemailer/lib/mailer')
Then you can mock or stub the sendMail method on the prototype in the usual way. Using Jasmine, you can do it like this:
beforeEach(function () {
spyOn(Mailer.prototype, 'sendMail').and.callFake(function (mailOptions, cb) {
cb(null, true)
})
})
The callFake ensures that the sendMail's callback is still executed encase you need to test what happens next. You can easily simulate an error by passing a first argument to cb: cb(new Error('Email failed'))
Now that the mock is set up, you can check everything is working as intended:
expect(Mailer.prototype.sendMail).toHaveBeenCalled()
expectEmail simply hooks into the transport layer, and expects you to identify the email ( return true if this is the email you are expecting ) by looking at the sentEmail contents.
In this case, return sentEmail.to === 'dave#testco.com' should suffice.
Keep in mind however, this module was designed in an environment where tests are ran in a random order and concurrently. You should propably randomize your data heavily to prevent collisions and false positives. BTW we use something like: var to = Date.now().toString(36) + Faker.Internet.email();
This example works fine for me
======== myfile.js ========
// SOME CODE HERE
transporter.sendMail(mailOptions, (err, info) => {
// PROCESS RESULT HERE
});
======== myfile.spec.js (unit test file) ========
const sinon = require('sinon');
const nodemailer = require('nodemailer');
const sandbox = sinon.sandbox.create();
describe('XXX', () => {
afterEach(function() {
sandbox.restore();
});
it('XXX', done => {
const transport = {
sendMail: (data, callback) => {
const err = new Error('some error');
callback(err, null);
}
};
sandbox.stub(nodemailer, 'createTransport').returns(transport);
// CALL FUNCTION TO TEST
// EXPECT RESULT
});
});

Resources