Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -548,7 +548,7 @@ describe('send transactional email', () => {
})
})

it('should send to all recipients in test runs', async () => {
it('skips partial retry and sends only to the test runner in test runs', async () => {
const recipients = [
'recipient1@open.gov.sg',
'recipient2@open.gov.sg',
Expand All @@ -573,11 +573,11 @@ describe('send transactional email', () => {
})
$.execution.testRun = true
await expect(sendTransactionalEmail.run($)).resolves.not.toThrow()
expect($.http.post).toBeCalledTimes(5)
expect($.http.post).toBeCalledTimes(1)
expect($.setActionItem).toHaveBeenCalledWith({
raw: {
status: ['ACCEPTED', 'ACCEPTED', 'ACCEPTED', 'ACCEPTED', 'ACCEPTED'],
recipient: recipients,
status: ['ACCEPTED'],
recipient: [$.user.email],
subject: 'test subject',
body: 'test body',
from: 'jack',
Expand Down Expand Up @@ -623,6 +623,70 @@ describe('send transactional email', () => {
})
})

describe('test-run recipient override', () => {
it("redirects email to the test runner's address and drops CCs when testRun is true", async () => {
$.step.parameters.destinationEmail = 'recipient@example.com'
$.step.parameters.destinationEmailCc = 'cc@example.com'
$.user.email = 'me@example.com'
$.execution.testRun = true

await expect(sendTransactionalEmail.run($)).resolves.not.toThrow()
expect($.http.post).toBeCalledTimes(1)
expect($.setActionItem).toHaveBeenCalledWith({
raw: {
status: ['ACCEPTED'],
recipient: ['me@example.com'],
subject: 'test subject',
body: 'test body',
from: 'jack',
reply_to: 'replyTo@open.gov.sg',
},
})
})

it('does not override recipients on a normal (non-test) run', async () => {
const recipients = ['recipient1@open.gov.sg', 'recipient2@open.gov.sg']
const ccRecipients = ['cc1@open.gov.sg', 'cc2@open.gov.sg']
$.step.parameters.destinationEmail = recipients.join(',')
$.step.parameters.destinationEmailCc = ccRecipients.join(',')
$.user.email = 'me@example.com'
$.execution.testRun = false

await expect(sendTransactionalEmail.run($)).resolves.not.toThrow()
expect($.setActionItem).toHaveBeenCalledWith({
raw: {
status: ['ACCEPTED', 'ACCEPTED'],
recipient: recipients,
subject: 'test subject',
body: 'test body',
cc: ccRecipients,
from: 'jack',
reply_to: 'replyTo@open.gov.sg',
},
})
})

it("collapses multiple configured recipients to the test runner's address in test mode", async () => {
$.step.parameters.destinationEmail = 'a@x.com, b@x.com, c@x.com'
$.step.parameters.destinationEmailCc = 'cc1@x.com, cc2@x.com'
$.user.email = 'me@example.com'
$.execution.testRun = true

await expect(sendTransactionalEmail.run($)).resolves.not.toThrow()
expect($.http.post).toBeCalledTimes(1)
expect($.setActionItem).toHaveBeenCalledWith({
raw: {
status: ['ACCEPTED'],
recipient: ['me@example.com'],
subject: 'test subject',
body: 'test body',
from: 'jack',
reply_to: 'replyTo@open.gov.sg',
},
})
})
})

it('should send two emails if there are blacklisted recipients and invalid attachments', async () => {
const recipients = ['recipient1@open.gov.sg', 'recipient2@open.gov.sg']
$.step.parameters.destinationEmail = recipients.join(',')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,9 +93,18 @@ const action: IRawAction = {
replyTo,
attachments = [],
} = $.step.parameters
// During test runs, redirect all recipients to the test runner's own
// email so test sends never spam unrelated addresses configured on the
// step.
const effectiveDestinationEmail = $.execution.testRun
? $.user.email
: destinationEmail
const effectiveDestinationEmailCc = $.execution.testRun
? undefined
: destinationEmailCc
const result = transactionalEmailSchema.safeParse({
destinationEmail,
destinationEmailCc,
destinationEmail: effectiveDestinationEmail,
destinationEmailCc: effectiveDestinationEmailCc,
senderName,
subject,
body,
Expand Down
9 changes: 7 additions & 2 deletions packages/frontend/src/app-extensions/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
import postmanExtensions from './postman'
import type { FrontEndAppExtension } from './types'

// Nothing for now
const APP_EXTENSIONS: Record<string, FrontEndAppExtension> = {}
/**
* Keys are `${appKey}-${stepKey}`.
*/
const APP_EXTENSIONS: Record<string, FrontEndAppExtension> = {
...postmanExtensions,
}

export function getExtension(
appKey?: string,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { Tooltip } from '@chakra-ui/react'

import type { CheckStepButtonExtensionProps } from '@/app-extensions/types'

function CheckStepButtonExtension({ children }: CheckStepButtonExtensionProps) {
return (
<Tooltip
label="Test email will only be sent to you."
aria-label="Check step tooltip"
hasArrow
shouldWrapChildren
>
{children}
</Tooltip>
)
}

export default CheckStepButtonExtension
11 changes: 11 additions & 0 deletions packages/frontend/src/app-extensions/postman/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import type { FrontEndAppExtension } from '@/app-extensions/types'

import CheckStepButton from './actions/send-transactional-email/check-step-button'

const extensions = {
'postman-sendTransactionalEmail': {
CheckStepButton: CheckStepButton,
},
} satisfies Record<string, FrontEndAppExtension>

export default extensions
Loading