DefinitelyTyped/types/nodemailer/nodemailer-tests.ts
Piotr Roszatycki 63e78c08d1 nodemailer: custom authentication handlers (#36954)
* nodemailer: custom authentication handlers

* nodemailer: bump TypeScript version for depended libs

* nodemailer: handler returns unknown value
2019-07-22 12:48:38 -07:00

1641 lines
47 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import * as nodemailer from 'nodemailer';
import addressparser = require('nodemailer/lib/addressparser');
import base64 = require('nodemailer/lib/base64');
import DKIM = require('nodemailer/lib/dkim');
import fetch = require('nodemailer/lib/fetch');
import Cookies = require('nodemailer/lib/fetch/cookies');
import JSONTransport = require('nodemailer/lib/json-transport');
import Mail = require('nodemailer/lib/mailer');
import MailComposer = require('nodemailer/lib/mail-composer');
import MailMessage = require('nodemailer/lib/mailer/mail-message');
import mimeFuncs = require('nodemailer/lib/mime-funcs');
import mimeTypes = require('nodemailer/lib/mime-funcs/mime-types');
import MimeNode = require('nodemailer/lib/mime-node');
import qp = require('nodemailer/lib/qp');
import SendmailTransport = require('nodemailer/lib/sendmail-transport');
import SESTransport = require('nodemailer/lib/ses-transport');
import shared = require('nodemailer/lib/shared');
import SMTPConnection = require('nodemailer/lib/smtp-connection');
import SMTPPool = require('nodemailer/lib/smtp-pool');
import SMTPTransport = require('nodemailer/lib/smtp-transport');
import StreamTransport = require('nodemailer/lib/stream-transport');
import wellKnown = require('nodemailer/lib/well-known');
import XOAuth2 = require('nodemailer/lib/xoauth2');
import LeWindows = require('nodemailer/lib/sendmail-transport/le-windows');
import LeUnix = require('nodemailer/lib/sendmail-transport/le-unix');
import * as fs from 'fs';
import * as stream from 'stream';
// mock aws-sdk
const aws = {
SES: class MockSES {
constructor(options?: object) { }
},
config: {
loadFromPath: (path: string): void => { }
}
};
// 1. Nodemailer
function nodemailer_test() {
// Generate test SMTP service account from ethereal.email
// Only needed if you don't have a real mail account for testing
nodemailer.createTestAccount((err, account) => {
if (err) {
console.log(err);
return;
}
// create reusable transporter object using the default SMTP transport
const transporter = nodemailer.createTransport({
host: 'smtp.ethereal.email',
port: 587,
secure: false, // true for 465, false for other ports
auth: {
user: account.user, // generated ethereal user
pass: account.pass // generated ethereal password
}
});
// setup email data with unicode symbols
const mailOptions: Mail.Options = {
from: '"Fred Foo 👻" <foo@blurdybloop.com>', // sender address
to: 'bar@blurdybloop.com, baz@blurdybloop.com', // list of receivers
subject: 'Hello ✔', // Subject line
text: 'Hello world?', // plain text body
html: '<b>Hello world?</b>' // html body
};
// send mail with defined transport object
transporter.sendMail(mailOptions, (err, info: SMTPTransport.SentMessageInfo) => {
if (err) {
console.log(err);
return;
}
console.log('Message sent: %s', info.messageId);
// Preview only available when sending through an Ethereal account
console.log('Preview URL: %s', nodemailer.getTestMessageUrl(info));
// Message sent: <b658f8ca-6296-ccf4-8306-87d57a0b4321@blurdybloop.com>
// Preview URL: https://ethereal.email/message/WaQKMgKddxQDoou...
});
});
}
// 3. Message configuration
// Commmon fields
function message_common_fields_test() {
const message: Mail.Options = {
from: 'sender@server.com',
to: 'receiver@sender.com',
subject: 'Message title',
text: 'Plaintext version of the message',
html: '<p>HTML version of the message</p>'
};
}
// More advanced fields
function message_more_advanced_fields_test() {
const message: Mail.Options = {
headers: {
'My-Custom-Header': 'header value'
},
date: new Date('2000-01-01 00:00:00')
};
const htmlstream = fs.createReadStream('content.html');
const transport = nodemailer.createTransport();
transport.sendMail({ html: htmlstream }, (err) => {
if (err) {
// check if htmlstream is still open and close it to clean up
}
});
}
// 3. Attachments
function message_attachments_test() {
const message: Mail.Options = {
attachments: [
{ // utf-8 string as an attachment
filename: 'text1.txt',
content: 'hello world!',
},
{ // binary buffer as an attachment
filename: 'text2.txt',
content: new Buffer('hello world!', 'utf-8')
},
{ // file on disk as an attachment
filename: 'text3.txt',
path: '/path/to/file.txt' // stream this file
},
{ // filename and content type is derived from path
path: '/path/to/file.txt'
},
{ // stream as an attachment
filename: 'text4.txt',
content: fs.createReadStream('file.txt'),
contentTransferEncoding: 'quoted-printable'
},
{ // define custom content type for the attachment
filename: 'text.bin',
content: 'hello world!',
contentType: 'text/plain',
contentTransferEncoding: '7bit',
contentDisposition: 'attachment'
},
{ // use URL as an attachment
filename: 'license.txt',
path: 'https://raw.github.com/nodemailer/nodemailer/master/LICENSE'
},
{ // encoded string as an attachment
filename: 'text1.txt',
content: 'aGVsbG8gd29ybGQh',
encoding: 'base64',
contentTransferEncoding: 'base64'
},
{ // data uri as an attachment
path: 'data:text/plain;base64,aGVsbG8gd29ybGQ=',
contentDisposition: 'inline',
contentTransferEncoding: false
},
{
// use pregenerated MIME node
raw: 'Content-Type: text/plain\r\n' + // tslint:disable-line prefer-template
'Content-Disposition: attachment;\r\n' +
'\r\n' +
'Hello world!'
}
]
};
}
// 3. Alternatives
function message_alternatives_test() {
const message: Mail.Options = {
html: '<b>Hello world!</b>',
alternatives: [
{
contentType: 'text/x-web-markdown',
content: '**Hello world!**'
}
]
};
}
// 3. Address object
function message_address_object_test() {
const message: Mail.Options = {
to: 'foobar@blurdybloop.com, "Ноде Майлер" <bar@blurdybloop.com>, "Name, User" <baz@blurdybloop.com>',
cc: [
'foobar@blurdybloop.com',
'"Ноде Майлер" <bar@blurdybloop.com>',
'"Name, User" <baz@blurdybloop.com>'
],
bcc: [
'foobar@blurdybloop.com',
{
name: 'Майлер, Ноде',
address: 'foobar@blurdybloop.com'
}
]
};
}
// 3. Calendar events
// Send a REQUEST event as a string
function message_calendar_request_test() {
const content = 'BEGIN:VCALENDAR\r\nPRODID:-//ACME/DesktopCalendar//EN\r\nMETHOD:REQUEST\r\n...';
const message: Mail.Options = {
from: 'sender@example.com',
to: 'recipient@example.com',
subject: 'Appointment',
text: 'Please see the attached appointment',
icalEvent: {
filename: 'invitation.ics',
method: 'request',
content
}
};
}
// Send a PUBLISH event from a file
function message_calendar_publish_test() {
const message: Mail.Options = {
from: 'sender@example.com',
to: 'recipient@example.com',
subject: 'Appointment',
text: 'Please see the attached appointment',
icalEvent: {
method: 'PUBLISH',
path: '/path/to/file'
}
};
}
// Send a CANCEL event from an URL
function message_calendar_cancel_test() {
const message: Mail.Options = {
from: 'sender@example.com',
to: 'recipient@example.com',
subject: 'Appointment',
text: 'Please see the attached appointment',
icalEvent: {
method: 'CANCEL',
href: 'http://www.example.com/events?event=123'
}
};
}
// 3. Embedded images
function message_embedded_images_test() {
const message: Mail.Options = {
html: 'Embedded image: <img src="cid:unique@nodemailer.com"/>',
attachments: [{
filename: 'image.png',
path: '/path/to/file',
cid: 'unique@nodemailer.com' // same cid value as in the html img src
}]
};
}
// 3. List headers
// Setup different List-* headers
function message_list_headers_test() {
const message: Mail.Options = {
from: 'sender@example.com',
to: 'recipient@example.com',
subject: 'List Message',
text: 'I hope no-one unsubscribes from this list!',
list: {
// List-Help: <mailto:admin@example.com?subject=help>
help: 'admin@example.com?subject=help',
// List-Unsubscribe: <http://example.com> (Comment)
unsubscribe: {
url: 'http://example.com',
comment: 'Comment'
},
// List-Subscribe: <mailto:admin@example.com?subject=subscribe>
// List-Subscribe: <http://example.com> (Subscribe)
subscribe: [
'admin@example.com?subject=subscribe',
{
url: 'http://example.com',
comment: 'Subscribe'
}
],
// List-Post: <http://example.com/post>, <mailto:admin@example.com?subject=post> (Post)
post: [
[
'http://example.com/post',
{
url: 'admin@example.com?subject=post',
comment: 'Post'
}
]
]
}
};
}
// 3. Custom headers
// Set custom headers
function message_custom_headers_test() {
const message: Mail.Options = {
headers: {
'x-my-key': 'header value',
'x-another-key': 'another value'
}
};
}
// Multiple rows with the same key
function message_multiple_rows_with_the_same_key_test() {
const message: Mail.Options = {
headers: {
'x-my-key': [
'value for row 1',
'value for row 2',
'value for row 3'
]
}
};
}
// Prepared headers
function message_prepared_headers_test() {
const message: Mail.Options = {
headers: {
'x-processed': 'a really long header or value with non-ascii characters 👮',
'x-unprocessed': {
prepared: true,
value: 'a really long header or value with non-ascii characters 👮'
}
}
};
}
// 3. Custom source
// Use string as a message body
function message_string_body_test() {
const message: Mail.Options = {
envelope: {
from: 'sender@example.com',
to: ['recipient@example.com']
},
raw: `From: sender@example.com
To: recipient@example.com
Subject: test message
Hello world!`
};
}
// Set EML file as message body
function message_eml_file_test() {
const message: Mail.Options = {
envelope: {
from: 'sender@example.com',
to: ['recipient@example.com']
},
raw: {
path: '/path/to/message.eml'
}
};
}
// Set string as attachment body
function message_string_attachment_test() {
const message: Mail.Options = {
from: 'sender@example.com',
to: 'recipient@example.com',
subject: 'Custom attachment',
attachments: [{
raw: `Content-Type: text/plain
Content-Disposition: attachment
Attached text file`}]
};
}
// 4. SMTP transport
// Single connection
function smtp_single_connection_test() {
const smtpConfig: SMTPTransport.Options = {
host: 'smtp.example.com',
port: 587,
secure: false, // upgrade later with STARTTLS
auth: {
user: 'username',
pass: 'password'
}
};
const transporter = nodemailer.createTransport(smtpConfig);
}
// Pooled connection
function smtp_pooled_connection_test() {
const smtpConfig: SMTPPool.Options = {
pool: true,
host: 'smtp.example.com',
port: 465,
secure: true, // use TLS
auth: {
user: 'username',
pass: 'password'
}
};
const transporter = nodemailer.createTransport(smtpConfig);
}
// Allow self-signed certificates
function smtp_self_signed_test() {
const smtpConfig: SMTPTransport.Options = {
host: 'my.smtp.host',
port: 465,
secure: true, // use TLS
auth: {
user: 'username',
pass: 'pass'
},
tls: {
// do not fail on invalid certs
rejectUnauthorized: false
}
};
const transporter = nodemailer.createTransport(smtpConfig);
}
// Verify SMTP connection configuration
function smtp_verify_test() {
const transporter = nodemailer.createTransport();
transporter.verify((error, success) => {
if (error) {
console.log(error);
} else {
console.log('Server is ready to take our messages');
}
});
}
// 4. SMTP envelope
function smtp_envelope_test() {
const message: Mail.Options = {
from: 'mailer@nodemailer.com', // listed in rfc822 message header
to: 'daemon@nodemailer.com', // listed in rfc822 message header
envelope: {
from: 'Daemon <deamon@nodemailer.com>', // used as MAIL FROM: address for SMTP
to: 'mailer@nodemailer.com, Mailer <mailer2@nodemailer.com>' // used as RCPT TO: address for SMTP
}
};
}
// 4. Pooled SMTP
// transporter.close()
function smtp_pool_close_test() {
const transporter = nodemailer.createTransport({ pool: true });
transporter.close();
}
// Event:idle
function smtp_pool_idle_test() {
const messages = [{ raw: 'list of messages' }];
const transporter = nodemailer.createTransport({ pool: true });
transporter.on('idle', () => {
// send next message from the pending queue
while (transporter.isIdle() && messages.length) {
transporter.sendMail(messages.shift()!);
}
});
}
// 4. Testing SMTP
// Create a testing account on the fly
function smtp_test_account_test() {
nodemailer.createTestAccount((err, account) => {
if (!err) {
// create reusable transporter object using the default SMTP transport
const transporter = nodemailer.createTransport({
host: 'smtp.ethereal.email',
port: 587,
secure: false, // true for 465, false for other ports
auth: {
user: account.user, // generated ethereal user
pass: account.pass // generated ethereal password
}
});
}
});
}
// Use environment specific SMTP settings
function smtp_info_test() {
const transporter = nodemailer.createTransport();
transporter.sendMail({}).then((info: SMTPTransport.SentMessageInfo) => {
console.log('Preview URL: ' + nodemailer.getTestMessageUrl(info));
});
}
// 4. OAuth2
// Using custom token handling
function oauth2_token_handling_test() {
const transporter = nodemailer.createTransport();
const userTokens: { [key: string]: string; } = {};
transporter.set('oauth2_provision_cb', (user, renew, callback) => {
const accessToken = userTokens[user];
if (!accessToken) {
callback(new Error('Unknown user'));
} else {
callback(null, accessToken);
}
});
}
// Token update notifications
function oauth2_token_update_test() {
const transporter = nodemailer.createTransport();
transporter.on('token', token => {
console.log('A new access token was generated');
console.log('User: %s', token.user);
console.log('Access Token: %s', token.accessToken);
console.log('Expires: %s', new Date(token.expires));
});
}
// Authenticate using existing token
function oauth2_existing_token_test() {
const transporter = nodemailer.createTransport({
host: 'smtp.gmail.com',
port: 465,
secure: true,
auth: {
type: 'OAuth2',
user: 'user@example.com',
accessToken: 'ya29.Xx_XX0xxxxx-xX0X0XxXXxXxXXXxX0x'
}
});
}
// Custom handler
function oauth2_custom_handler_test() {
const transporter = nodemailer.createTransport({
host: 'smtp.gmail.com',
port: 465,
secure: true,
auth: {
type: 'OAuth2',
user: 'user@example.com'
}
});
const userTokens: { [key: string]: string; } = {};
transporter.set('oauth2_provision_cb', (user, renew, callback) => {
const accessToken = userTokens[user];
if (!accessToken) {
callback(new Error('Unknown user'));
} else {
callback(null, accessToken);
}
});
}
// Set up 3LO authentication
function oauth2_3lo_test() {
const transporter = nodemailer.createTransport({
host: 'smtp.gmail.com',
port: 465,
secure: true,
auth: {
type: 'OAuth2',
user: 'user@example.com',
clientId: '000000000000-xxx0.apps.googleusercontent.com',
clientSecret: 'XxxxxXXxX0xxxxxxxx0XXxX0',
refreshToken: '1/XXxXxsss-xxxXXXXXxXxx0XXXxxXXx0x00xxx',
accessToken: 'ya29.Xx_XX0xxxxx-xX0X0XxXXxXxXXXxX0x',
expires: 1484314697598
}
});
}
// Set up 2LO authentication
function oauth2_2lo_test() {
const transporter = nodemailer.createTransport({
host: 'smtp.gmail.com',
port: 465,
secure: true,
auth: {
type: 'OAuth2',
user: 'user@example.com',
clientId: '113600000000000000000',
clientSecret: '-----BEGIN PRIVATE KEY-----\nMIIEvgIBADANBg...',
accessToken: 'ya29.Xx_XX0xxxxx-xX0X0XxXXxXxXXXxX0x',
expires: 1484314697598
}
});
}
// Provide authentication details with message options
function oauth2_message_options_test() {
const transporter = nodemailer.createTransport({
host: 'smtp.gmail.com',
port: 465,
secure: true,
auth: {
type: 'OAuth2',
clientId: '000000000000-xxx.apps.googleusercontent.com',
clientSecret: 'XxxxxXXxX0xxxxxxxx0XXxX0'
}
});
const auth: SMTPConnection.AuthenticationTypeOAuth2 = {
user: 'user@example.com',
refreshToken: '1/XXxXxsss-xxxXXXXXxXxx0XXXxxXXx0x00xxx',
accessToken: 'ya29.Xx_XX0xxxxx-xX0X0XxXXxXxXXXxX0x',
expires: 1484314697598
};
const options: SMTPTransport.MailOptions = {
from: 'sender@example.com',
to: 'recipient@example.com',
subject: 'Message',
text: 'I hope this message gets through!',
auth: {
user: 'user@example.com',
refreshToken: '1/XXxXxsss-xxxXXXXXxXxx0XXXxxXXx0x00xxx',
accessToken: 'ya29.Xx_XX0xxxxx-xX0X0XxXXxXxXXXxX0x',
expires: 1484314697598
}
};
transporter.sendMail(options);
}
function oauth2_privision_cb_test() {
const transporter = nodemailer.createTransport({
host: 'smtp.gmail.com',
port: 465,
secure: true,
auth: {
type: 'OAuth2'
}
});
const userTokens: { [key: string]: string; } = {};
transporter.set('oauth2_provision_cb', (user, renew, callback) => {
const accessToken = userTokens[user];
if (!accessToken) {
callback(new Error('Unknown user'));
} else {
callback(null, accessToken);
}
});
const options: SMTPTransport.MailOptions = {
from: 'sender@example.com',
to: 'recipient@example.com',
subject: 'Message',
text: 'I hope this message gets through!',
auth: {
user: 'user@example.com'
}
};
transporter.sendMail(options);
}
// Set up XOAuth2 authentication
function oauth2_xoauth2_test() {
const xoauth2 = new XOAuth2({
user: 'test@example.com',
clientId: '{Client ID}',
clientSecret: '{Client Secret}',
refreshToken: 'saladus',
accessUrl: 'http://localhost:8993/',
accessToken: 'abc',
timeout: 3600
});
const transporter = nodemailer.createTransport({
service: 'gmail',
auth: {
type: 'OAUTH2',
user: 'user@example.com',
oauth2: xoauth2,
method: 'XOAUTH2'
}
});
}
// Set up custom authentication
async function custom_auth_async_test() {
const account = await nodemailer.createTestAccount();
const transporter = nodemailer.createTransport(
{
host: account.smtp.host,
port: account.smtp.port,
secure: account.smtp.secure,
auth: {
type: 'custom',
user: account.user,
pass: account.pass,
method: 'x-login'
},
logger: false,
debug: false,
customAuth: {
// can create multiple handlers
'x-login': async (ctx) => {
// This custom method implements AUTH LOGIN even though Nodemailer supports it natively.
// AUTH LOGIN mechanism includes multiple steps, so it's great for a demo nevertheless
console.log('Performing custom authentication for %s', ctx.auth.credentials.user);
console.log('Supported extensions: %s', ctx.extensions.join(', '));
console.log('Supported auth methods: %s', ctx.authMethods.join(', '));
if (!ctx.authMethods.includes('LOGIN')) {
console.log('Server does not support AUTH LOGIN');
throw new Error('Can not log in');
}
console.log('AUTH LOGIN is supported, proceeding with login...');
let cmd;
cmd = await ctx.sendCommand('AUTH LOGIN');
if (cmd.status !== 334) {
// expecting '334 VXNlcm5hbWU6'
throw new Error('Invalid login sequence while waiting for "334 VXNlcm5hbWU6"');
}
console.log('Sending username: %s', ctx.auth.credentials.user);
cmd = await ctx.sendCommand(Buffer.from(ctx.auth.credentials.user, 'utf-8').toString('base64'));
if (cmd.status !== 334) {
// expecting '334 UGFzc3dvcmQ6'
throw new Error('Invalid login sequence while waiting for "334 UGFzc3dvcmQ6"');
}
console.log('Sending password: %s', '*'.repeat(ctx.auth.credentials.pass.length));
cmd = await ctx.sendCommand(Buffer.from(ctx.auth.credentials.pass, 'utf-8').toString('base64'));
if (cmd.status < 200 || cmd.status >= 300) {
// expecting a 235 response, just in case allow everything in 2xx range
throw new Error('User failed to authenticate');
}
console.log('User authenticated! (%s)', cmd.response);
// all checks passed
return true;
}
}
},
{
// default message fields
// sender info
from: 'Pangalink <no-reply@pangalink.net>',
headers: {
'X-Laziness-level': '1000' // just an example header, no need to use this
}
}
);
}
async function custom_auth_cb_test() {
const account = await nodemailer.createTestAccount();
const transporter = nodemailer.createTransport(
{
host: account.smtp.host,
port: account.smtp.port,
secure: account.smtp.secure,
auth: {
type: 'custom',
user: account.user,
pass: account.pass,
method: 'x-login'
},
logger: false,
debug: false,
customAuth: {
// can create multiple handlers
'x-login': ctx => {
// This custom method implements AUTH LOGIN even though Nodemailer supports it natively.
// AUTH LOGIN mechanism includes multiple steps, so it's great for a demo nevertheless
console.log('Performing custom authentication for %s', ctx.auth.credentials.user);
console.log('Supported extensions: %s', ctx.extensions.join(', '));
console.log('Supported auth methods: %s', ctx.authMethods.join(', '));
if (!ctx.authMethods.includes('LOGIN')) {
console.log('Server does not support AUTH LOGIN');
return ctx.reject(new Error('Can not log in'));
}
console.log('AUTH LOGIN is supported, proceeding with login...');
ctx.sendCommand('AUTH LOGIN', (err, cmd) => {
if (err) {
return ctx.reject(err);
}
if (cmd.status !== 334) {
// expecting '334 VXNlcm5hbWU6'
return ctx.reject('Invalid login sequence while waiting for "334 VXNlcm5hbWU6"');
}
console.log('Sending username: %s', ctx.auth.credentials.user);
ctx.sendCommand(Buffer.from(ctx.auth.credentials.user, 'utf-8').toString('base64'), (err, cmd) => {
if (err) {
return ctx.reject(err);
}
if (cmd.status !== 334) {
// expecting '334 UGFzc3dvcmQ6'
return ctx.reject('Invalid login sequence while waiting for "334 UGFzc3dvcmQ6"');
}
console.log('Sending password: %s', '*'.repeat(ctx.auth.credentials.pass.length));
ctx.sendCommand(Buffer.from(ctx.auth.credentials.pass, 'utf-8').toString('base64'), (err, cmd) => {
if (err) {
return ctx.reject(err);
}
if (cmd.status < 200 || cmd.status >= 300) {
// expecting a 235 response, just in case allow everything in 2xx range
return ctx.reject('User failed to authenticate');
}
console.log('User authenticated! (%s)', cmd.response);
// all checks passed
return ctx.resolve();
});
});
});
}
}
},
{
// default message fields
// sender info
from: 'Pangalink <no-reply@pangalink.net>',
headers: {
'X-Laziness-level': '1000' // just an example header, no need to use this
}
}
);
}
// 5. Sendmail transport
// Send a message using specific binary
function sendmail_test() {
const transporter = nodemailer.createTransport({
sendmail: true,
newline: 'unix',
path: '/usr/sbin/sendmail'
});
transporter.sendMail({
from: 'sender@example.com',
to: 'recipient@example.com',
subject: 'Message',
text: 'I hope this message gets delivered!'
}, (err, info: SendmailTransport.SentMessageInfo) => {
if (!err) {
console.log(info.envelope);
console.log(info.messageId);
}
});
}
// line ending transforms using windows-style newlines
function sendmail_line_endings_windows_test() {
function process_le(mail: MailMessage) {
const input = mail.message.createReadStream();
input.pipe(new LeWindows());
}
}
// line ending transforms using unix-style newlines
function sendmail_line_endings_unix_test() {
function process_le(mail: MailMessage) {
const input = mail.message.createReadStream();
input.pipe(new LeUnix());
}
}
// 5. SES transport
// Send a message using SES transport
function ses_test() {
// configure AWS SDK
aws.config.loadFromPath('config.json');
// create Nodemailer SES transporter
const transporter = nodemailer.createTransport({
SES: new aws.SES({
apiVersion: '2010-12-01'
})
});
const options: SESTransport.MailOptions = {
from: 'sender@example.com',
to: 'recipient@example.com',
subject: 'Message',
text: 'I hope this message gets sent!',
ses: { // optional extra arguments for SendRawEmail
Tags: [{
Name: 'tag name',
Value: 'tag value'
}]
}
};
// send some mail
transporter.sendMail(options, (err, info: SESTransport.SentMessageInfo) => {
if (!err) {
console.log(info.envelope);
console.log(info.messageId);
}
});
}
// 5. Stream transport
// Stream a message with windows-style newlines
function stream_test() {
const transporter = nodemailer.createTransport({
streamTransport: true,
newline: 'windows'
});
transporter.sendMail({
from: 'sender@example.com',
to: 'recipient@example.com',
subject: 'Message',
text: 'I hope this message gets streamed!'
}, (err, info: StreamTransport.SentMessageInfo) => {
if (!err) {
console.log(info.envelope);
console.log(info.messageId);
// if ('pipe' in info.message) {
if (info.message instanceof stream.Readable) {
info.message.pipe(process.stdout);
}
}
});
}
// Create a buffer with unix-style newlines
function stream_buffer_unix_newlines_test() {
const transporter = nodemailer.createTransport({
streamTransport: true,
newline: 'unix',
buffer: true,
normalizeHeaderKey: (key) => key.toUpperCase()
});
transporter.sendMail({
from: 'sender@example.com',
to: 'recipient@example.com',
subject: 'Message',
text: 'I hope this message gets buffered!'
}, (err, info: StreamTransport.SentMessageInfo) => {
if (!err) {
console.log(info.envelope);
console.log(info.messageId);
console.log(info.message.toString());
}
});
}
// Create a JSON encoded message object
function json_test() {
const transporter = nodemailer.createTransport({
jsonTransport: true,
skipEncoding: true
});
transporter.sendMail({
from: 'sender@example.com',
to: 'recipient@example.com',
subject: 'Message',
text: 'I hope this message gets buffered!'
}, (err, info: JSONTransport.SentMessageInfo) => {
if (!err) {
console.log(info.envelope);
console.log(info.messageId);
console.log(info.message); // JSON string
}
});
}
// 6. Create plugins
// 'compile'
function plugin_compile_test() {
const transporter = nodemailer.createTransport();
function plugin(mail: typeof transporter.MailMessage, callback: (err?: Error | null) => void) {
// if mail.data.html is a file or an url, it is returned as a Buffer
mail.resolveContent(mail.data, 'html', (err, html) => {
if (err) {
callback(err);
return;
}
console.log('HTML contents: %s', html.toString());
callback();
});
}
transporter.use('compile', (mail, callback) => {
if (!mail.data.text && mail.data.html && typeof mail.data.html === 'string') {
mail.data.text = mail.data.html.replace(/<[^>]*>/g, ' ');
}
callback();
});
}
// 'stream'
function plugin_stream_test() {
const transformer: stream.Transform = new (require('stream').Transform)();
transformer._transform = function(chunk: Buffer, encoding, done) {
// replace all tabs with spaces in the stream chunk
for (let i = 0; i < chunk.length; i++) {
if (chunk[i] === 0x09) {
chunk[i] = 0x20;
}
}
this.push(chunk);
done();
};
const transporter = nodemailer.createTransport();
transporter.use('stream', (mail, callback) => {
// apply output transformer to the raw message stream
mail.message.transform(transformer);
callback();
});
transporter.use('stream', (mail, callback) => {
const addresses = mail.message.getAddresses();
console.log('From: %s', JSON.stringify(addresses.from));
console.log('To: %s', JSON.stringify(addresses.to));
console.log('Cc: %s', JSON.stringify(addresses.cc));
console.log('Bcc: %s', JSON.stringify(addresses.bcc));
callback();
});
}
// Transport Example
function plugin_transport_example_test() {
interface MailOptions extends Mail.Options {
mailOption?: 'foo';
}
interface Options extends MailOptions, nodemailer.TransportOptions {
transportOptions: 'bar';
}
interface SentMessageInfo {
SentMessageInfo: 'baz';
}
class Transport implements nodemailer.Transport {
name = 'minimal';
version = '0.1.0';
constructor(options: Options) { }
send(mail: MailMessage, callback: (err: Error | null, info: SentMessageInfo) => void): void {
const input = mail.message.createReadStream();
input.pipe(process.stdout);
input.on('end', () => {
callback(null, { SentMessageInfo: 'baz' });
});
}
}
const transporter = nodemailer.createTransport(new Transport({
transportOptions: 'bar'
}));
const options: MailOptions = {
from: 'sender',
to: 'receiver',
subject: 'hello',
text: 'hello world!',
mailOption: 'foo'
};
transporter.sendMail(options);
}
// 7. https://nodemailer.com/dkim/
// Sign all messages
function dkim_sign_all_test() {
const opts: SMTPTransport.Options = {
host: 'smtp.example.com',
port: 465,
secure: true,
dkim: {
domainName: 'example.com',
keySelector: '2017',
privateKey: '-----BEGIN PRIVATE KEY-----\nMIIEvgIBADANBg...'
}
};
}
// Sign all messages with multiple keys
function dkim_sign_multiple_keys_test() {
const transporter = nodemailer.createTransport({
host: 'smtp.example.com',
port: 465,
secure: true,
dkim: {
keys: [
{
domainName: 'example.com',
keySelector: '2017',
privateKey: '-----BEGIN PRIVATE KEY-----\nMIIEvgIBADANBg...'
},
{
domainName: 'example.com',
keySelector: '2016',
privateKey: '-----BEGIN PRIVATE KEY-----\nMIIEvgIBADANBg...'
}
],
cacheDir: false
}
});
}
// Sign a specific message
function dkim_sign_specific_message_test() {
const transporter = nodemailer.createTransport({
host: 'smtp.example.com',
port: 465,
secure: true
});
const message: Mail.Options = {
from: 'sender@example.com',
to: 'recipient@example.com',
subject: 'Message',
text: 'I hope this message gets read!',
dkim: {
domainName: 'example.com',
keySelector: '2017',
privateKey: '-----BEGIN PRIVATE KEY-----\nMIIEvgIBADANBg...'
}
};
}
// Cache large messages for signing
function dkim_cache_large_messages_test() {
const transporter = nodemailer.createTransport({
host: 'smtp.example.com',
port: 465,
secure: true,
dkim: {
domainName: 'example.com',
keySelector: '2017',
privateKey: '-----BEGIN PRIVATE KEY-----\nMIIEvgIBADANBg...',
cacheDir: '/tmp',
cacheTreshold: 100 * 1024
}
});
}
// Do not sign specific header keys
function dkim_specific_header_key_test() {
const transporter = nodemailer.createTransport({
host: 'smtp.example.com',
port: 465,
secure: true,
dkim: {
domainName: 'example.com',
keySelector: '2017',
privateKey: '-----BEGIN PRIVATE KEY-----\nMIIEvgIBADANBg...',
skipFields: 'message-id:date'
}
});
}
// 8. SMTP Connection
// SMTP Connection
function smtp_connection_test() {
const connection = new SMTPConnection();
connection.connect((err) => {
if (err) throw err;
connection.login({ user: 'user', pass: 'pass' }, (err) => {
if (err) throw err;
connection.send({ from: 'a@example.com', to: 'b@example.net' }, 'message', (err, info) => {
if (err) {
const code: string = err.code || '???';
const response: string = err.response || '???';
const responseCode: number = err.responseCode || 0;
const command: string = err.command || '???';
throw err;
}
connection.reset(() => {
if (err) throw err;
connection.quit();
connection.close();
});
});
});
});
}
// Mailcomposer
// createReadStream
function mailcomposer_createReadStream_test() {
const mail = new MailComposer({ from: '...' });
const stream = mail.compile().createReadStream();
stream.pipe(process.stdout);
}
// build
function mailcomposer_build_test() {
const mail = new MailComposer({ from: '...' });
mail.compile().build((err, message) => {
process.stdout.write(message);
});
}
// addressparser
function isAddress(addressOrGroup: addressparser.AddressOrGroup): addressOrGroup is addressparser.Address {
return (addressOrGroup as addressparser.Address).address !== undefined;
}
function isGroup(addressOrGroup: addressparser.AddressOrGroup): addressOrGroup is addressparser.Group {
return (addressOrGroup as addressparser.Group).group !== undefined;
}
function addressparser_test() {
const input = 'andris@tr.ee';
const results: addressparser.AddressOrGroup[] = addressparser(input);
const firstResult = results[0];
if (isAddress(firstResult)) {
const address: string = firstResult.address;
const name: string = firstResult.name;
} else if (isGroup(firstResult)) {
const group: addressparser.AddressOrGroup[] = firstResult.group;
const name: string = firstResult.name;
}
}
function addressparser_flatten_test() {
const input = 'andris@tr.ee';
const results = addressparser(input, { flatten: true });
const firstResult = results[0];
const address: string = firstResult.address;
const name: string = firstResult.name;
}
// base64
function base64_test() {
base64.encode('abcd= ÕÄÖÜ');
base64.encode(new Buffer([0x00, 0x01, 0x02, 0x20, 0x03]));
}
// dkim
function dkim_test_options() {
const dkim = new DKIM({
domainName: 'example.com',
keySelector: '2017',
privateKey: '-----BEGIN PRIVATE KEY-----\nMIIEvgIBADANBg...'
});
const stream = dkim.sign('Message');
stream.pipe(process.stdout);
}
function dkim_test_extra_options() {
const dkim = new DKIM();
const stream = dkim.sign('Message', {
domainName: 'example.com',
keySelector: '2017',
privateKey: '-----BEGIN PRIVATE KEY-----\nMIIEvgIBADANBg...'
});
stream.pipe(process.stdout);
}
// fetch
function fetch_test() {
const stream = fetch('http://localhost/');
const statusCode: number = stream.statusCode;
const headers = stream.headers;
const contentType: string | undefined = headers['content-type'];
fetch('http://localhost:/', {
allowErrorResponse: true,
method: 'post',
cookie: 'test=pest',
body: {
hello: 'world 😭',
another: 'value'
},
timeout: 1000,
tls: {
rejectUnauthorized: true
}
});
}
// fetch/cookies
function fetch_cookies_test() {
const biskviit = new Cookies();
biskviit.getPath('/');
biskviit.isExpired({
name: 'a',
value: 'b',
expires: new Date(Date.now() + 10000)
});
biskviit.compare(
{
name: 'zzz',
path: '/',
domain: 'example.com',
secure: false,
httponly: false
},
{
name: 'zzz',
path: '/',
domain: 'example.com',
secure: false,
httponly: false
}
);
biskviit.add({
name: 'zzz',
value: 'abc',
path: '/',
expires: new Date(Date.now() + 10000),
domain: 'example.com',
secure: false,
httponly: false
});
const cookie = {
name: 'zzz',
value: 'abc',
path: '/def/',
expires: new Date(Date.now() + 10000),
domain: 'example.com',
secure: false,
httponly: false
};
biskviit.match(cookie, 'http://example.com/def/');
biskviit.parse('theme=plain');
biskviit.list('https://www.foo.com');
biskviit.get('https://www.foo.com');
biskviit.set('theme=plain', 'https://foo.com/');
}
// mime-funcs
function mime_funcs_test() {
mimeFuncs.isPlainText('abc');
mimeFuncs.hasLongerLines('abc\ndef', 5);
mimeFuncs.encodeWord('See on õhin test');
mimeFuncs.encodeWord('See on õhin test', 'B');
mimeFuncs.encodeWords('метель" вьюга', 'Q', 52);
mimeFuncs.encodeWords('Jõgeva Jõgeva Jõgeva mugeva Jõgeva Jõgeva Jõgeva Jõgeva Jõgeva', 'Q', 16);
mimeFuncs.encodeWords('õõõõõ õõõõõ õõõõõ mugeva õõõõõ õõõõõ õõõõõ õõõõõ Jõgeva', 'B', 30);
mimeFuncs.buildHeaderParam('title', 'this is just a title', 500);
const parsedHeader = mimeFuncs.parseHeaderValue('content-disposition: attachment; filename=filename');
console.log(parsedHeader.params.filename);
mimeFuncs.buildHeaderValue({
value: 'test'
});
mimeFuncs.buildHeaderValue({
value: 'test',
params: {
a: 'b'
}
});
mimeFuncs.foldLines('Testin command line', 76, true);
mimeFuncs.foldLines('Testin command line', 76);
}
// mime-node
function mime_node_test() {
const mb = new MimeNode('text/plain', {
normalizeHeaderKey: (key) => key.toUpperCase()
});
const child = mb.createChild('multipart/mixed');
mb.appendChild(child);
child.replace(child);
mb.setHeader('key', 'value');
const value: string = mb.getHeader('key');
mb.addHeader({
key: 'value4',
key2: 'value5'
});
mb.setHeader([
{
key: 'key',
value: 'value2'
},
{
key: 'key2',
value: 'value3'
}
]);
mb.setHeader('key', ['value1', 'value2', 'value3']);
mb.setContent('abc');
mb.build((err, msg) => {
const msgAsString: string = msg.toString();
});
mb.processFunc((input) => {
const isReadable: boolean = input.readable;
return input;
});
}
// mime-types
function mime_types_test() {
mimeTypes.detectExtension(false);
mimeTypes.detectExtension('unknown');
mimeTypes.detectMimeType(false);
mimeTypes.detectMimeType('unknown');
}
// qp
function qp_test() {
qp.encode('abcd= ÕÄÖÜ');
qp.encode(new Buffer([0x00, 0x01, 0x02, 0x20, 0x03]));
}
// shared
function shared_getLogger_test() {
shared.getLogger({
logger: false
});
shared.getLogger();
const options = shared.parseConnectionUrl('smtps://user:pass@localhost:123?tls.rejectUnauthorized=false&name=horizon');
console.log(options.secure, options.auth!.user, options.tls!.rejectUnauthorized);
}
function shared_resolveContent_string_test() {
const mail = {
data: {
html: '<p>Tere, tere</p><p>vana kere!</p>\n'
}
};
shared.resolveContent(mail.data, 'html', (err, value) => {
if (!err) {
console.log(value);
}
});
shared.resolveContent(mail.data, 'html').then((value) => console.log(value));
}
function shared_resolveContent_buffer_test() {
const mail = {
data: {
html: new Buffer('<p>Tere, tere</p><p>vana kere!</p>\n')
}
};
shared.resolveContent(mail.data, 'html', (err, value) => {
if (!err) {
console.log(value);
}
});
shared.resolveContent(mail.data, 'html').then((value) => console.log(value));
}
function shared_assign_test() {
const target = {
a: 1,
b: 2,
c: 3
};
const arg1 = {
b: 5,
y: 66,
e: 33
};
const arg2 = {
y: 17,
qq: 98
};
shared.assign(target, arg1, arg2);
}
function shared_encodeXText_test() {
shared.encodeXText('teretere');
}
// well-known
function well_known_test() {
const options = wellKnown('Gmail');
if (options) {
console.log(options.host, options.port, options.secure);
}
}
// xoauth2
function xoauth2_test() {
const xoauth2 = new XOAuth2({
user: 'test@example.com',
clientId: '{Client ID}',
clientSecret: '{Client Secret}',
refreshToken: 'saladus',
accessUrl: 'http://localhost:8993/',
accessToken: 'abc',
timeout: 3600
});
xoauth2.getToken(false, (err, accessToken) => {
if (err) throw err;
const token: string = xoauth2.buildXOAuth2Token(accessToken);
});
}
function xoauth2_sign_payload_test() {
const xoauth2 = new XOAuth2({
user: 'test@example.com',
serviceClient: '{Client ID}',
accessUrl: 'http://localhost:8497/',
timeout: 3600,
privateKey: '-----BEGIN RSA PRIVATE KEY-----\n...'
});
xoauth2.jwtSignRS256({
some: 'payload'
});
}