Created
September 8, 2023 01:04
-
-
Save mandemeskel/48d2933bcab59f81a61907cc27a3f2d9 to your computer and use it in GitHub Desktop.
Example of test organization, using gnerators for test data and mocks.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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