Skip to content
Merged
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
96 changes: 77 additions & 19 deletions src/server/plugins/engine/components/PaymentField.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -952,26 +952,24 @@ describe('PaymentField', () => {
})

describe('dispatcher with email prepopulation', () => {
it('should pass valid email to createPayment', async () => {
const emailDef = {
title: 'Payment with email',
it('should pass state.userConfirmationEmailAddress to createPayment', async () => {
const def = {
title: 'Payment',
name: 'myPayment',
type: ComponentType.PaymentField,
options: {
amount: 50,
description: 'Test payment',
emailField: 'userEmail'
description: 'Test payment'
}
} satisfies PaymentFieldComponent

const mockYarSet = jest.fn()
const mockRequest = {
server: {
plugins: {
'forms-engine-plugin': { baseUrl: 'base-url' }
}
},
yar: { set: mockYarSet }
yar: { set: jest.fn() }
} as unknown as FormRequestPayload
const mockH = {
redirect: jest
Expand All @@ -988,10 +986,10 @@ describe('PaymentField', () => {
},
getState: jest.fn().mockResolvedValueOnce({
$$__referenceNumber: 'ref-123',
userEmail: 'test@example.com'
userConfirmationEmailAddress: 'cya@example.com'
})
},
component: emailDef,
component: def,
sourceUrl: 'http://localhost:3009/test',
isLive: false,
isPreview: true
Expand All @@ -1008,26 +1006,87 @@ describe('PaymentField', () => {

await PaymentField.dispatcher(mockRequest, mockH, args)

// Verify createPayment was called with the email as the 7th argument
expect(postJson).toHaveBeenCalledWith(
expect.any(String),
expect.objectContaining({
payload: expect.objectContaining({
email: 'test@example.com'
email: 'cya@example.com'
})
})
)
})

it('should not pass email when state.userConfirmationEmailAddress is missing', async () => {
const def = {
title: 'Payment',
name: 'myPayment',
type: ComponentType.PaymentField,
options: {
amount: 50,
description: 'Test payment'
}
} satisfies PaymentFieldComponent

const mockRequest = {
server: {
plugins: {
'forms-engine-plugin': { baseUrl: 'base-url' }
}
},
yar: { set: jest.fn() }
} as unknown as FormRequestPayload
const mockH = {
redirect: jest
.fn()
.mockReturnValueOnce({ code: jest.fn().mockReturnValueOnce('ok') })
} as unknown as FormResponseToolkit
const args = {
controller: {
model: {
formId: 'formid',
basePath: 'base-path',
services: mockServices,
conditions: {}
},
getState: jest.fn().mockResolvedValueOnce({
$$__referenceNumber: 'ref-123'
})
},
component: def,
sourceUrl: 'http://localhost:3009/test',
isLive: false,
isPreview: true
} as unknown as PaymentExternalArgs

// @ts-expect-error - partial mock
jest.mocked(postJson).mockResolvedValueOnce({
payload: {
state: { status: 'created' },
payment_id: 'pay-id',
_links: { next_url: { href: '/next' } }
}
})

await PaymentField.dispatcher(mockRequest, mockH, args)

expect(postJson).toHaveBeenCalledWith(
expect.any(String),
expect.objectContaining({
payload: expect.not.objectContaining({
email: expect.anything()
})
})
)
})

it('should not pass email when emailField value is empty', async () => {
const emailDef = {
title: 'Payment with empty email',
it('should not pass email when state.userConfirmationEmailAddress is not a string', async () => {
const def = {
title: 'Payment',
name: 'myPayment',
type: ComponentType.PaymentField,
options: {
amount: 50,
description: 'Test payment',
emailField: 'userEmail'
description: 'Test payment'
}
} satisfies PaymentFieldComponent

Expand All @@ -1054,10 +1113,10 @@ describe('PaymentField', () => {
},
getState: jest.fn().mockResolvedValueOnce({
$$__referenceNumber: 'ref-123',
userEmail: ''
userConfirmationEmailAddress: { not: 'a string' }
})
},
component: emailDef,
component: def,
sourceUrl: 'http://localhost:3009/test',
isLive: false,
isPreview: true
Expand All @@ -1074,7 +1133,6 @@ describe('PaymentField', () => {

await PaymentField.dispatcher(mockRequest, mockH, args)

// Verify createPayment was called WITHOUT an email field
expect(postJson).toHaveBeenCalledWith(
expect.any(String),
expect.objectContaining({
Expand Down
11 changes: 3 additions & 8 deletions src/server/plugins/engine/components/PaymentField.ts
Original file line number Diff line number Diff line change
Expand Up @@ -270,15 +270,10 @@ export class PaymentField extends FormComponent {
const payCallbackUrl = `${baseUrl}/payment-callback?uuid=${uuid}`
const paymentPageUrl = args.sourceUrl

// Prepopulate GOV.UK Pay email if emailField is configured.
// The referenced EmailAddressField validates with joi.string().email()
// at input time, so the value in state is already validated.
let prefilledEmail: string | undefined
if (options.emailField) {
const emailValue = state[options.emailField]
if (typeof emailValue === 'string' && emailValue) {
prefilledEmail = emailValue
}
const userConfirmationEmail = state.userConfirmationEmailAddress
if (typeof userConfirmationEmail === 'string' && userConfirmationEmail) {
prefilledEmail = userConfirmationEmail
}

const amountInPence = Math.round(resolvedAmount * 100)
Expand Down
Loading