-
Notifications
You must be signed in to change notification settings - Fork 87
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: move to react email, update copy (#7985)
- Loading branch information
Showing
6 changed files
with
119 additions
and
72 deletions.
There are no files selected for viewing
23 changes: 23 additions & 0 deletions
23
react-email-preview/emails/EmailAddressVerificationOtp.stories.tsx
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
import { Meta, StoryFn } from '@storybook/react' | ||
|
||
import EmailAddressVerificationOtp, { | ||
type EmailAddressVerificationOtpHtmlData, | ||
} from './EmailAddressVerificationOtp' | ||
|
||
export default { | ||
title: 'EmailPreview/EmailAddressVerificationOtp', | ||
component: EmailAddressVerificationOtp, | ||
decorators: [], | ||
} as Meta | ||
|
||
const Template: StoryFn<EmailAddressVerificationOtpHtmlData> = (args) => ( | ||
<EmailAddressVerificationOtp {...args} /> | ||
) | ||
|
||
export const Default = Template.bind({}) | ||
Default.args = { | ||
otpPrefix: 'ABC', | ||
otp: '123456', | ||
minutesToExpiry: 30, | ||
appName: 'FormSG', | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
import { EmailAddressVerificationOtp } from '../../src/app/views/templates/EmailAddressVerificationOtp' | ||
|
||
export type { EmailAddressVerificationOtpHtmlData } from '../../src/app/views/templates/EmailAddressVerificationOtp' | ||
|
||
export default EmailAddressVerificationOtp |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -20,8 +20,6 @@ import { | |
ISubmissionSchema, | ||
} from 'src/types' | ||
|
||
import { HASH_EXPIRE_AFTER_SECONDS } from '../../../../../shared/utils/verification' | ||
|
||
const MOCK_VALID_EMAIL = '[email protected]' | ||
const MOCK_VALID_EMAIL_2 = '[email protected]' | ||
const MOCK_VALID_EMAIL_3 = '[email protected]' | ||
|
@@ -81,31 +79,18 @@ describe('mail.service', () => { | |
describe('sendVerificationOtp', () => { | ||
const MOCK_OTP = '123456' | ||
|
||
const generateExpectedArg = async () => { | ||
return { | ||
to: MOCK_VALID_EMAIL, | ||
from: MOCK_SENDER_STRING, | ||
subject: `Your OTP for submitting a form on ${MOCK_APP_NAME}`, | ||
html: MailUtils.generateVerificationOtpHtml({ | ||
appName: MOCK_APP_NAME, | ||
otp: MOCK_OTP, | ||
minutesToExpiry: HASH_EXPIRE_AFTER_SECONDS / 60, | ||
otpPrefix: MOCK_OTP_PREFIX, | ||
}), | ||
headers: { | ||
// Hardcode in tests in case something changes this. | ||
'X-Formsg-Email-Type': 'Verification OTP', | ||
}, | ||
} | ||
} | ||
const expectedArg = expect.objectContaining({ | ||
to: MOCK_VALID_EMAIL, | ||
from: MOCK_SENDER_STRING, | ||
subject: `Your OTP for submitting a form on ${MOCK_APP_NAME}`, | ||
html: expect.stringMatching(MOCK_OTP), | ||
}) | ||
|
||
it('should send verification otp successfully', async () => { | ||
// Arrange | ||
// sendMail should return mocked success response | ||
sendMailSpy.mockResolvedValueOnce('mockedSuccessResponse') | ||
|
||
const expectedArgument = await generateExpectedArg() | ||
|
||
// Act | ||
const actualResult = await mailService.sendVerificationOtp( | ||
MOCK_VALID_EMAIL, | ||
|
@@ -117,7 +102,7 @@ describe('mail.service', () => { | |
expect(actualResult._unsafeUnwrap()).toEqual(true) | ||
// Check arguments passed to sendNodeMail | ||
expect(sendMailSpy).toHaveBeenCalledTimes(1) | ||
expect(sendMailSpy).toHaveBeenCalledWith(expectedArgument) | ||
expect(sendMailSpy).toHaveBeenCalledWith(expectedArg) | ||
}) | ||
|
||
it('should reject with error when email is invalid', async () => { | ||
|
@@ -148,8 +133,6 @@ describe('mail.service', () => { | |
.mockRejectedValueOnce(mock4xxReject) | ||
.mockResolvedValueOnce('mockedSuccessResponse') | ||
|
||
const expectedArgument = await generateExpectedArg() | ||
|
||
// Act | ||
const actualResult = await mailService.sendVerificationOtp( | ||
MOCK_VALID_EMAIL, | ||
|
@@ -163,7 +146,7 @@ describe('mail.service', () => { | |
// Should have been called two times since it rejected the first one and | ||
// resolved | ||
expect(sendMailSpy).toHaveBeenCalledTimes(2) | ||
expect(sendMailSpy).toHaveBeenCalledWith(expectedArgument) | ||
expect(sendMailSpy).toHaveBeenCalledWith(expectedArg) | ||
}) | ||
|
||
it('should autoretry MOCK_RETRY_COUNT times and return error when all retries fail with 4xx errors', async () => { | ||
|
@@ -174,8 +157,6 @@ describe('mail.service', () => { | |
} | ||
sendMailSpy.mockRejectedValue(mock4xxReject) | ||
|
||
const expectedArgument = await generateExpectedArg() | ||
|
||
// Act | ||
const actualResult = await mailService.sendVerificationOtp( | ||
MOCK_VALID_EMAIL, | ||
|
@@ -192,7 +173,7 @@ describe('mail.service', () => { | |
// Check arguments passed to sendNodeMail | ||
// Should have been called MOCK_RETRY_COUNT + 1 times | ||
expect(sendMailSpy).toHaveBeenCalledTimes(MOCK_RETRY_COUNT + 1) | ||
expect(sendMailSpy).toHaveBeenCalledWith(expectedArgument) | ||
expect(sendMailSpy).toHaveBeenCalledWith(expectedArg) | ||
}) | ||
|
||
it('should stop autoretrying when the returned error is not a 4xx error', async () => { | ||
|
@@ -206,8 +187,6 @@ describe('mail.service', () => { | |
.mockRejectedValueOnce(mock4xxReject) | ||
.mockRejectedValueOnce(mockError) | ||
|
||
const expectedArgument = await generateExpectedArg() | ||
|
||
// Act | ||
const actualResult = await mailService.sendVerificationOtp( | ||
MOCK_VALID_EMAIL, | ||
|
@@ -223,7 +202,7 @@ describe('mail.service', () => { | |
// Should retry two times and stop since the second rejected value is | ||
// non-4xx error. | ||
expect(sendMailSpy).toHaveBeenCalledTimes(2) | ||
expect(sendMailSpy).toHaveBeenCalledWith(expectedArgument) | ||
expect(sendMailSpy).toHaveBeenCalledWith(expectedArg) | ||
}) | ||
}) | ||
|
||
|
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
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
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
import { Body, Head, Html, Text, Link } from '@react-email/components' | ||
|
||
export type EmailAddressVerificationOtpHtmlData = { | ||
otpPrefix: string | ||
otp: string | ||
minutesToExpiry: number | ||
appName: string | ||
} | ||
|
||
export const EmailAddressVerificationOtp = ({ | ||
otpPrefix, | ||
otp, | ||
minutesToExpiry, | ||
appName, | ||
}: EmailAddressVerificationOtpHtmlData): JSX.Element => { | ||
return ( | ||
<Html> | ||
<Head /> | ||
<Body> | ||
<Text>You are currently submitting a form on {appName}.</Text> | ||
<Text> | ||
Your OTP is {otpPrefix}-<b>{otp}</b>. It will expire in{' '} | ||
{minutesToExpiry} minutes. Please use this to verify your submission. | ||
</Text> | ||
<Text> | ||
Never share your OTP with anyone else. If you did not request this | ||
OTP, you can safely ignore this email. | ||
</Text> | ||
<Text>The {appName} Support Team</Text> | ||
</Body> | ||
</Html> | ||
) | ||
} |