Skip to content

Instantly share code, notes, and snippets.

@mandemeskel
Created September 8, 2023 01:04
Show Gist options
  • Save mandemeskel/48d2933bcab59f81a61907cc27a3f2d9 to your computer and use it in GitHub Desktop.
Save mandemeskel/48d2933bcab59f81a61907cc27a3f2d9 to your computer and use it in GitHub Desktop.
Example of test organization, using gnerators for test data and mocks.
import { createErrorServiceErrorMock } from "@tests/shared/error_service_mock"
const {
setApiKey,
send
} = createSendGridMock()
createErrorServiceErrorMock()
import EmailTypes from "@app/email_types"
import EmailSender from "@/use_cases/email/email_sender"
import { createEmailWithTextBody, createEmailWithHtmlBody } from "@tests/test_data_generators"
describe('EmailSender.send', () => {
const APPROVED_SENDER = 'invoices@omnimerchant.app'
const valid_email = createEmailWithTextBody()
beforeEach(() => {
send.mockClear()
send.mockResolvedValue(undefined)
setApiKey.mockClear()
})
it('sets the correct API key', async () => {
await EmailSender.send(valid_email)
expect(setApiKey).toBeCalledWith(
EmailSender.API_KEY,
)
})
it('sends the email only once', async () => {
await EmailSender.send(valid_email)
expect(send).toHaveBeenCalledTimes(1)
})
it('uses the correct recepient address', async () => {
await EmailSender.send(valid_email)
expect(send).toHaveBeenCalledWith(
expect.objectContaining({
to: valid_email.to,
subject: valid_email.subject,
})
)
})
it('uses the correct subject', async () => {
await EmailSender.send(valid_email)
expect(send).toHaveBeenCalledWith(
expect.objectContaining({
subject: valid_email.subject,
})
)
})
it('uses the correct from address so email provider recognizes sender', async () => {
await EmailSender.send(valid_email)
expect(send).toHaveBeenCalledWith(
expect.objectContaining({
from: APPROVED_SENDER,
})
)
})
it('uses the correct reply_to address so replies go to the merchant not us', async () => {
await EmailSender.send(valid_email)
expect(send).toHaveBeenCalledWith(
expect.objectContaining({
// NOT a type, send-grid uses camel-case here
replyTo: valid_email.reply_to,
})
)
})
it('sends to multiple addresses', async () => {
const email = createEmailWithMultipleAddresses()
await EmailSender.send(email)
expect(send).toHaveBeenCalledWith(
expect.objectContaining({
to: email.to
})
)
})
it('when provided a text body, sends a text email', async () => {
const email = createEmailWithTextBody()
await EmailSender.send(email)
expect(send).toHaveBeenCalledWith(
expect.objectContaining({
text: email.body
})
)
})
it('when provided an HTML body, sends an HTML email', async () => {
const email = createEmailWithHtmlBody()
await EmailSender.send(email)
expect(send).toHaveBeenCalledWith(
expect.objectContaining({
html: email.body
})
)
})
it('when provided an attachment, sends the attachment', async () => {
const email = createEmailWithAttachment()
await EmailSender.send(email)
const expected_attachment = {
content: email.attachment!.data.toString(),
filename: email.attachment!.filename,
type: email.attachment!.mime,
disposition: 'attachment',
contentId: 'attachment1',
}
expect(send).toHaveBeenCalledWith(
expect.objectContaining({
attachments: [expected_attachment]
})
)
})
it('when provided a to and reply_to email that are the same, omits the reply_to email', async () => {
const email = createEmailWithInvalidReplyTo()
await EmailSender.send(email)
expect(send).not.toHaveBeenCalledWith(
expect.objectContaining({
replyTo: email.reply_to,
})
)
})
it('when provided to addresses that contain the reply_to email, omits the reply_to email', async () => {
const email = createEmailWithInvalidReplyToAndMultipleTos()
await EmailSender.send(email)
expect(send).not.toHaveBeenCalledWith(
expect.objectContaining({
replyTo: email.reply_to,
})
)
})
})
function createEmailWithInvalidReplyTo() : EmailTypes.Email {
const email = createEmailWithTextBody()
return {
...email,
// NOTE: to, cc, and reply can't have the same emails
to: 'edd@email.com',
// emails are case insentive so this tests the
// comparison is done in case insenstive wave
reply_to: 'EDD@email.com',
}
}
function createEmailWithInvalidReplyToAndMultipleTos() : EmailTypes.Email {
const email = createEmailWithTextBody()
return {
...email,
// NOTE: to, cc, and reply can't have the same emails
to: ['ed@gmail.com', 'edd@email.com'],
// emails are case insentive so this tests the
// comparison is done in case insenstive wave
reply_to: 'EDD@email.com',
}
}
function createEmailWithMultipleAddresses() : EmailTypes.Email {
const email = createEmailWithTextBody()
return {
...email,
to: ['rolf@culde.sac', 'jimmy@thewood.boy', 'noneckeddy@culde.sac'],
}
}
function createEmailWithAttachment() : EmailTypes.Email {
return {
...createEmailWithTextBody(),
attachment: {
data: 'fadskfjlasdflsadfjldjflk',
filename: 'invoice.pdf',
mime: 'application/pdf',
}
}
}
function createSendGridMock() {
const sendMock = jest.fn()
const setApiKeyMock = jest.fn()
jest.mock(
'@sendgrid/mail',
() => {
return {
setApiKey: setApiKeyMock,
send: sendMock,
}
}
)
return {
setApiKey: setApiKeyMock,
send: sendMock,
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment