Merge pull request #2669 from mozilla/MNTOR-249-email-coverage
MNTOR-249: Improve test coverage of EmailUtils
This commit is contained in:
Коммит
0ae5f8b061
|
@ -99,6 +99,7 @@ const EmailUtils = {
|
|||
},
|
||||
|
||||
getVerificationUrl (subscriber) {
|
||||
if (!subscriber.verification_token) throw new Error('subscriber has no verification_token')
|
||||
let url = new URL(`${AppConstants.SERVER_URL}/user/verify`)
|
||||
url = this.appendUtmParams(url, 'verified-subscribers', 'account-verification-email')
|
||||
url.searchParams.append('token', encodeURIComponent(subscriber.verification_token))
|
||||
|
@ -108,8 +109,8 @@ const EmailUtils = {
|
|||
getUnsubscribeUrl (subscriber, emailType) {
|
||||
// TODO: email unsubscribe is broken for most emails
|
||||
let url = new URL(`${AppConstants.SERVER_URL}/user/unsubscribe`)
|
||||
const token = (subscriber.hasOwnProperty('verification_token')) ? subscriber.verification_token : subscriber.primary_verification_token
|
||||
const hash = (subscriber.hasOwnProperty('sha1')) ? subscriber.sha1 : subscriber.primary_sha1
|
||||
const token = (Object.prototype.hasOwnProperty.call(subscriber, 'verification_token')) ? subscriber.verification_token : subscriber.primary_verification_token
|
||||
const hash = (Object.prototype.hasOwnProperty.call(subscriber, 'sha1')) ? subscriber.sha1 : subscriber.primary_sha1
|
||||
url.searchParams.append('token', encodeURIComponent(token))
|
||||
url.searchParams.append('hash', encodeURIComponent(hash))
|
||||
url = this.appendUtmParams(url, 'unsubscribe', emailType)
|
||||
|
@ -118,6 +119,7 @@ const EmailUtils = {
|
|||
|
||||
getMonthlyUnsubscribeUrl (subscriber, campaign, content) {
|
||||
// TODO: create new subscriptions section in settings to manage all emails and avoid one-off routes like this
|
||||
if (!subscriber.primary_verification_token) throw new Error('subscriber has no primary verification_token')
|
||||
let url = new URL('user/unsubscribe-monthly/', AppConstants.SERVER_URL)
|
||||
|
||||
url = this.appendUtmParams(url, campaign, content)
|
||||
|
|
|
@ -3,40 +3,149 @@
|
|||
const nodemailer = require('nodemailer')
|
||||
|
||||
const EmailUtils = require('../email-utils')
|
||||
const { EMAIL_FROM, SERVER_URL, SES_CONFIG_SET } = require('../app-constants')
|
||||
const { TEST_SUBSCRIBERS, TEST_EMAIL_ADDRESSES } = require('../db/seeds/test_subscribers')
|
||||
|
||||
jest.mock('nodemailer')
|
||||
|
||||
test('EmailUtils.init with empty host doesnt invoke nodemailer', () => {
|
||||
nodemailer.createTransport = jest.fn()
|
||||
|
||||
EmailUtils.init('')
|
||||
|
||||
const mockCreateTransport = nodemailer.createTransport.mock
|
||||
expect(mockCreateTransport.calls.length).toBe(1)
|
||||
expect(mockCreateTransport.calls[0][0]).toEqual({ jsonTransport: true })
|
||||
test('EmailUtils.sendEmail before .init() fails', async () => {
|
||||
const sendMailArgs = ['test@example.com', 'subject', 'template.hbs', { breach: 'Test' }]
|
||||
const expectedError = new Error('SMTP transport not initialized')
|
||||
await expect(EmailUtils.sendEmail(...sendMailArgs)).rejects.toEqual(expectedError)
|
||||
})
|
||||
|
||||
test.skip('EmailUtils.init with user, pass, host, port invokes nodemailer.createTransport', () => {
|
||||
test('EmailUtils.init with empty host uses jsonTransport', async () => {
|
||||
nodemailer.createTransport = jest.fn()
|
||||
|
||||
await expect(EmailUtils.init('')).resolves.toBe(true)
|
||||
|
||||
expect(nodemailer.createTransport).toHaveBeenCalledWith({ jsonTransport: true })
|
||||
})
|
||||
|
||||
test('EmailUtils.init with SMTP URL invokes nodemailer.createTransport', async () => {
|
||||
const testSmtpUrl = 'smtps://test:test@test:1'
|
||||
nodemailer.createTransport = jest.fn()
|
||||
const mockTransporter = {
|
||||
verify: jest.fn().mockReturnValueOnce("✓"),
|
||||
use: jest.fn(),
|
||||
}
|
||||
nodemailer.createTransport = jest.fn().mockReturnValueOnce(mockTransporter)
|
||||
|
||||
EmailUtils.init(testSmtpUrl)
|
||||
await expect(EmailUtils.init(testSmtpUrl)).resolves.toBe("✓")
|
||||
|
||||
const mockCreateTransport = nodemailer.createTransport.mock
|
||||
expect(mockCreateTransport.calls.length).toBe(1)
|
||||
expect(mockCreateTransport.calls[0][0]).toBe(testSmtpUrl)
|
||||
expect(nodemailer.createTransport).toHaveBeenCalledWith(testSmtpUrl)
|
||||
expect(mockTransporter.verify).toHaveBeenCalledWith()
|
||||
expect(mockTransporter.use.mock.calls.length).toBe(1)
|
||||
expect(mockTransporter.use.mock.calls[0].length).toEqual(2)
|
||||
expect(mockTransporter.use.mock.calls[0][0]).toBe('compile')
|
||||
})
|
||||
|
||||
test.skip('EmailUtils.sendEmail with recipient, subject, template, context calls gTransporter.sendMail', () => {
|
||||
|
||||
test('EmailUtils.sendEmail with recipient, subject, template, context calls gTransporter.sendMail', async () => {
|
||||
const testSmtpUrl = 'smtps://test:test@test:1'
|
||||
const sendMailArgs = ['test@example.com', 'subject', 'template.hbs', { breach: 'Test' }]
|
||||
nodemailer.createTransport = jest.fn()
|
||||
const mockTransporter = {
|
||||
verify: jest.fn().mockReturnValueOnce("verified"),
|
||||
use: jest.fn(),
|
||||
sendMail: jest.fn((options, cb) => cb(null, "sent")),
|
||||
transporter: {name: 'MockTransporter'}
|
||||
}
|
||||
nodemailer.createTransport = jest.fn().mockReturnValueOnce(mockTransporter)
|
||||
|
||||
EmailUtils.init(testSmtpUrl)
|
||||
EmailUtils.sendEmail(...sendMailArgs)
|
||||
await expect(EmailUtils.init(testSmtpUrl)).resolves.toBe("verified")
|
||||
await expect(EmailUtils.sendEmail(...sendMailArgs)).resolves.toBe("sent")
|
||||
|
||||
// TODO: find a way to expect gTransporter.sendMail
|
||||
expect(mockTransporter.sendMail.mock.calls.length).toBe(1)
|
||||
expect(mockTransporter.sendMail.mock.calls[0].length).toBe(2)
|
||||
expect(mockTransporter.sendMail.mock.calls[0][0]).toEqual(
|
||||
{
|
||||
context: {
|
||||
SERVER_URL,
|
||||
layout: 'template.hbs',
|
||||
breach: 'Test',
|
||||
},
|
||||
from: EMAIL_FROM,
|
||||
headers: {"x-ses-configuration-set": SES_CONFIG_SET},
|
||||
subject: "subject",
|
||||
template: "template.hbs",
|
||||
to: "test@example.com",
|
||||
})
|
||||
})
|
||||
|
||||
test('EmailUtils.sendEmail rejects with error', async () => {
|
||||
const testSmtpUrl = 'smtps://test:test@test:1'
|
||||
const sendMailArgs = ['test@example.com', 'subject', 'template.hbs', { breach: 'Test' }]
|
||||
const mockTransporter = {
|
||||
verify: jest.fn().mockReturnValueOnce("verified"),
|
||||
use: jest.fn(),
|
||||
sendMail: jest.fn((options, cb) => cb("error", null)),
|
||||
transporter: {name: 'MockTransporter'}
|
||||
}
|
||||
nodemailer.createTransport = jest.fn().mockReturnValueOnce(mockTransporter)
|
||||
|
||||
await expect(EmailUtils.init('smtps://test:test@test:1')).resolves.toBe("verified")
|
||||
await expect(EmailUtils.sendEmail(...sendMailArgs)).rejects.toBe("error")
|
||||
})
|
||||
|
||||
test('EmailUtils.init with empty host uses jsonTransport. logs messages', async () => {
|
||||
const sendMailArgs = ['test@example.com', 'subject', 'template.hbs', { breach: 'Test' }]
|
||||
const sendMailInfo = {message: "sent"}
|
||||
const mockTransporter = {
|
||||
sendMail: jest.fn((options, cb) => cb(null, sendMailInfo)),
|
||||
transporter: {name: 'JSONTransport'}
|
||||
}
|
||||
nodemailer.createTransport = jest.fn().mockReturnValueOnce(mockTransporter)
|
||||
|
||||
await expect(EmailUtils.init('')).resolves.toBe(true)
|
||||
expect(nodemailer.createTransport).toHaveBeenCalledWith({ jsonTransport: true })
|
||||
await expect(EmailUtils.sendEmail(...sendMailArgs)).resolves.toEqual(sendMailInfo)
|
||||
})
|
||||
|
||||
test('EmailUtils.getEmailCtaHref works without a subscriber ID', () => {
|
||||
const emailCtaHref = EmailUtils.getEmailCtaHref('email-type', 'content')
|
||||
expect(emailCtaHref.pathname).toBe('/')
|
||||
emailCtaHref.searchParams.sort()
|
||||
expect(Array.from(emailCtaHref.searchParams.entries())).toEqual(
|
||||
[
|
||||
['utm_campaign', 'email-type'],
|
||||
['utm_content', 'content'],
|
||||
['utm_medium', 'email'],
|
||||
['utm_source', 'fx-monitor'],
|
||||
])
|
||||
})
|
||||
|
||||
test('EmailUtils.getEmailCtaHref works with a subscriber ID', () => {
|
||||
const emailCtaHref = EmailUtils.getEmailCtaHref('email-type-2', 'content-2', 1234)
|
||||
expect(emailCtaHref.pathname).toBe('/')
|
||||
emailCtaHref.searchParams.sort()
|
||||
expect(Array.from(emailCtaHref.searchParams.entries())).toEqual(
|
||||
[
|
||||
['subscriber_id', '1234'],
|
||||
['utm_campaign', 'email-type-2'],
|
||||
['utm_content', 'content-2'],
|
||||
['utm_medium', 'email'],
|
||||
['utm_source', 'fx-monitor'],
|
||||
])
|
||||
})
|
||||
|
||||
test('EmailUtils.getVerificationUrl returns a URL', () => {
|
||||
const fakeSubscriber = {"verification_token": "SubscriberVerificationToken"}
|
||||
const verificationUrl = EmailUtils.getVerificationUrl(fakeSubscriber)
|
||||
expect(verificationUrl.pathname).toBe('/user/verify')
|
||||
verificationUrl.searchParams.sort()
|
||||
expect(Array.from(verificationUrl.searchParams.entries())).toEqual(
|
||||
[
|
||||
['token', 'SubscriberVerificationToken'],
|
||||
['utm_campaign', 'verified-subscribers'],
|
||||
['utm_content', 'account-verification-email'],
|
||||
['utm_medium', 'email'],
|
||||
['utm_source', 'fx-monitor'],
|
||||
])
|
||||
})
|
||||
|
||||
test('EmailUtils.getVerificationUrl throws when subscriber has no token', () => {
|
||||
const fakeSubscriber = {"verification_token": null}
|
||||
const expected = 'subscriber has no verification_token'
|
||||
expect(() => EmailUtils.getVerificationUrl(fakeSubscriber)).toThrow(expected)
|
||||
})
|
||||
|
||||
test('EmailUtils.getUnsubscribeUrl works with subscriber record', () => {
|
||||
|
@ -56,3 +165,25 @@ test('EmailUtils.getUnsubscribeUrl works with email_address record', () => {
|
|||
expect(unsubUrl).toMatch(emailAddressRecord.sha1)
|
||||
expect(unsubUrl).toMatch(emailAddressRecord.verification_token)
|
||||
})
|
||||
|
||||
test('EmailUtils.getMonthlyUnsubscribeUrl returns unsubscribe URL', () => {
|
||||
const fakeSubscriber = {'primary_verification_token': 'PrimaryVerificationToken'}
|
||||
|
||||
const unsubUrl = EmailUtils.getMonthlyUnsubscribeUrl(fakeSubscriber, 'campaign', 'content')
|
||||
expect(unsubUrl.pathname).toBe('/user/unsubscribe-monthly/')
|
||||
unsubUrl.searchParams.sort()
|
||||
expect(Array.from(unsubUrl.searchParams.entries())).toEqual(
|
||||
[
|
||||
['token', 'PrimaryVerificationToken'],
|
||||
['utm_campaign', 'campaign'],
|
||||
['utm_content', 'content'],
|
||||
['utm_medium', 'email'],
|
||||
['utm_source', 'fx-monitor'],
|
||||
])
|
||||
})
|
||||
|
||||
test('EmailUtils.getMonthlyUnsubscribeUrl throws when subscriber has no token', () => {
|
||||
const fakeSubscriber = {'primary_verification_token': null}
|
||||
const expected = 'subscriber has no primary verification_token'
|
||||
expect(() => EmailUtils.getMonthlyUnsubscribeUrl(fakeSubscriber, 'campaign', 'content')).toThrow(expected)
|
||||
})
|
||||
|
|
Загрузка…
Ссылка в новой задаче