@defra/forms-engine-plugin 4.0.43 → 4.0.44
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.public/stylesheets/application.min.css +1 -1
- package/.public/stylesheets/application.min.css.map +1 -1
- package/.server/client/stylesheets/_payment-field.scss +8 -0
- package/.server/client/stylesheets/application.scss +2 -0
- package/.server/index.js +3 -1
- package/.server/index.js.map +1 -1
- package/.server/server/constants.d.ts +1 -0
- package/.server/server/constants.js +1 -0
- package/.server/server/constants.js.map +1 -1
- package/.server/server/forms/payment-test.yaml +42 -0
- package/.server/server/forms/register-as-a-unicorn-breeder.yaml +14 -0
- package/.server/server/plugins/engine/components/FormComponent.d.ts +1 -0
- package/.server/server/plugins/engine/components/FormComponent.js +1 -0
- package/.server/server/plugins/engine/components/FormComponent.js.map +1 -1
- package/.server/server/plugins/engine/components/PaymentField.d.ts +135 -0
- package/.server/server/plugins/engine/components/PaymentField.js +228 -0
- package/.server/server/plugins/engine/components/PaymentField.js.map +1 -0
- package/.server/server/plugins/engine/components/PaymentField.types.d.ts +21 -0
- package/.server/server/plugins/engine/components/PaymentField.types.js +2 -0
- package/.server/server/plugins/engine/components/PaymentField.types.js.map +1 -0
- package/.server/server/plugins/engine/components/UkAddressField.d.ts +1 -1
- package/.server/server/plugins/engine/components/UkAddressField.js +3 -1
- package/.server/server/plugins/engine/components/UkAddressField.js.map +1 -1
- package/.server/server/plugins/engine/components/helpers/components.d.ts +1 -1
- package/.server/server/plugins/engine/components/helpers/components.js +3 -0
- package/.server/server/plugins/engine/components/helpers/components.js.map +1 -1
- package/.server/server/plugins/engine/components/index.d.ts +1 -0
- package/.server/server/plugins/engine/components/index.js +1 -0
- package/.server/server/plugins/engine/components/index.js.map +1 -1
- package/.server/server/plugins/engine/helpers.d.ts +1 -0
- package/.server/server/plugins/engine/models/SummaryViewModel.d.ts +3 -0
- package/.server/server/plugins/engine/models/SummaryViewModel.js +7 -0
- package/.server/server/plugins/engine/models/SummaryViewModel.js.map +1 -1
- package/.server/server/plugins/engine/outputFormatters/human/v1.js +34 -1
- package/.server/server/plugins/engine/outputFormatters/human/v1.js.map +1 -1
- package/.server/server/plugins/engine/outputFormatters/machine/v2.d.ts +22 -0
- package/.server/server/plugins/engine/outputFormatters/machine/v2.js +43 -1
- package/.server/server/plugins/engine/outputFormatters/machine/v2.js.map +1 -1
- package/.server/server/plugins/engine/pageControllers/QuestionPageController.js +29 -8
- package/.server/server/plugins/engine/pageControllers/QuestionPageController.js.map +1 -1
- package/.server/server/plugins/engine/pageControllers/StartPageController.d.ts +2 -0
- package/.server/server/plugins/engine/pageControllers/SummaryPageController.d.ts +17 -0
- package/.server/server/plugins/engine/pageControllers/SummaryPageController.js +173 -51
- package/.server/server/plugins/engine/pageControllers/SummaryPageController.js.map +1 -1
- package/.server/server/plugins/engine/pageControllers/errors.d.ts +31 -0
- package/.server/server/plugins/engine/pageControllers/errors.js +59 -2
- package/.server/server/plugins/engine/pageControllers/errors.js.map +1 -1
- package/.server/server/plugins/engine/pageControllers/helpers/submission.d.ts +27 -0
- package/.server/server/plugins/engine/pageControllers/helpers/submission.js +77 -0
- package/.server/server/plugins/engine/pageControllers/helpers/submission.js.map +1 -0
- package/.server/server/plugins/engine/plugin.js +4 -1
- package/.server/server/plugins/engine/plugin.js.map +1 -1
- package/.server/server/plugins/engine/routes/index.js +8 -4
- package/.server/server/plugins/engine/routes/index.js.map +1 -1
- package/.server/server/plugins/engine/routes/payment-helper.d.ts +14 -0
- package/.server/server/plugins/engine/routes/payment-helper.js +41 -0
- package/.server/server/plugins/engine/routes/payment-helper.js.map +1 -0
- package/.server/server/plugins/engine/routes/payment-helper.test.js +81 -0
- package/.server/server/plugins/engine/routes/payment-helper.test.js.map +1 -0
- package/.server/server/plugins/engine/routes/payment.d.ts +8 -0
- package/.server/server/plugins/engine/routes/payment.js +140 -0
- package/.server/server/plugins/engine/routes/payment.js.map +1 -0
- package/.server/server/plugins/engine/routes/payment.test.js +187 -0
- package/.server/server/plugins/engine/routes/payment.test.js.map +1 -0
- package/.server/server/plugins/engine/services/localFormsService.js +6 -0
- package/.server/server/plugins/engine/services/localFormsService.js.map +1 -1
- package/.server/server/plugins/engine/types/schema.js +7 -0
- package/.server/server/plugins/engine/types/schema.js.map +1 -1
- package/.server/server/plugins/engine/types.d.ts +19 -1
- package/.server/server/plugins/engine/types.js +4 -0
- package/.server/server/plugins/engine/types.js.map +1 -1
- package/.server/server/plugins/engine/validationHelpers.d.ts +1 -1
- package/.server/server/plugins/engine/validationHelpers.js.map +1 -1
- package/.server/server/plugins/engine/views/components/paymentfield.html +42 -0
- package/.server/server/plugins/engine/views/index.html +9 -1
- package/.server/server/plugins/engine/views/partials/form.html +20 -5
- package/.server/server/plugins/engine/views/summary.html +17 -1
- package/.server/server/plugins/nunjucks/filters/field.d.ts +1 -1
- package/.server/server/plugins/payment/helper.d.ts +30 -0
- package/.server/server/plugins/payment/helper.js +49 -0
- package/.server/server/plugins/payment/helper.js.map +1 -0
- package/.server/server/plugins/payment/helper.test.js +37 -0
- package/.server/server/plugins/payment/helper.test.js.map +1 -0
- package/.server/server/plugins/payment/service.d.ts +40 -0
- package/.server/server/plugins/payment/service.js +129 -0
- package/.server/server/plugins/payment/service.js.map +1 -0
- package/.server/server/plugins/payment/service.test.js +162 -0
- package/.server/server/plugins/payment/service.test.js.map +1 -0
- package/.server/server/plugins/payment/types.d.ts +172 -0
- package/.server/server/plugins/payment/types.js +78 -0
- package/.server/server/plugins/payment/types.js.map +1 -0
- package/.server/server/types.d.ts +2 -0
- package/.server/server/types.js.map +1 -1
- package/.server/typings/hapi/index.d.js.map +1 -1
- package/README.md +12 -9
- package/package.json +2 -2
- package/src/client/stylesheets/_payment-field.scss +8 -0
- package/src/client/stylesheets/application.scss +2 -0
- package/src/index.ts +5 -1
- package/src/server/constants.js +1 -0
- package/src/server/forms/payment-test.yaml +42 -0
- package/src/server/forms/register-as-a-unicorn-breeder.yaml +14 -0
- package/src/server/plugins/engine/components/FormComponent.ts +1 -0
- package/src/server/plugins/engine/components/PaymentField.test.ts +611 -0
- package/src/server/plugins/engine/components/PaymentField.ts +367 -0
- package/src/server/plugins/engine/components/PaymentField.types.ts +21 -0
- package/src/server/plugins/engine/components/UkAddressField.ts +2 -1
- package/src/server/plugins/engine/components/helpers/components.ts +5 -0
- package/src/server/plugins/engine/components/index.ts +1 -0
- package/src/server/plugins/engine/models/SummaryViewModel.ts +8 -0
- package/src/server/plugins/engine/outputFormatters/human/v1.payment.test.ts +147 -0
- package/src/server/plugins/engine/outputFormatters/human/v1.test.ts +105 -103
- package/src/server/plugins/engine/outputFormatters/human/v1.ts +61 -2
- package/src/server/plugins/engine/outputFormatters/machine/v2.payment.test.ts +115 -0
- package/src/server/plugins/engine/outputFormatters/machine/v2.ts +60 -1
- package/src/server/plugins/engine/pageControllers/QuestionPageController.ts +32 -6
- package/src/server/plugins/engine/pageControllers/SummaryPageController.ts +247 -72
- package/src/server/plugins/engine/pageControllers/errors.test.ts +13 -1
- package/src/server/plugins/engine/pageControllers/errors.ts +79 -4
- package/src/server/plugins/engine/pageControllers/helpers/submission.test.ts +299 -0
- package/src/server/plugins/engine/pageControllers/helpers/submission.ts +110 -0
- package/src/server/plugins/engine/plugin.ts +11 -6
- package/src/server/plugins/engine/routes/index.ts +17 -16
- package/src/server/plugins/engine/routes/payment-helper.js +39 -0
- package/src/server/plugins/engine/routes/payment-helper.test.js +90 -0
- package/src/server/plugins/engine/routes/payment.js +151 -0
- package/src/server/plugins/engine/routes/payment.test.js +180 -0
- package/src/server/plugins/engine/services/localFormsService.js +7 -0
- package/src/server/plugins/engine/types/schema.ts +9 -0
- package/src/server/plugins/engine/types.ts +24 -1
- package/src/server/plugins/engine/validationHelpers.ts +1 -1
- package/src/server/plugins/engine/views/components/paymentfield.html +42 -0
- package/src/server/plugins/engine/views/index.html +9 -1
- package/src/server/plugins/engine/views/partials/form.html +20 -5
- package/src/server/plugins/engine/views/summary.html +17 -1
- package/src/server/plugins/payment/helper.js +56 -0
- package/src/server/plugins/payment/helper.test.js +52 -0
- package/src/server/plugins/payment/service.js +171 -0
- package/src/server/plugins/payment/service.test.js +205 -0
- package/src/server/plugins/payment/types.js +77 -0
- package/src/server/types.ts +2 -0
- package/src/typings/hapi/index.d.ts +1 -0
|
@@ -0,0 +1,611 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ComponentType,
|
|
3
|
+
type FormMetadata,
|
|
4
|
+
type PaymentFieldComponent
|
|
5
|
+
} from '@defra/forms-model'
|
|
6
|
+
|
|
7
|
+
import { ComponentCollection } from '~/src/server/plugins/engine/components/ComponentCollection.js'
|
|
8
|
+
import { PaymentField } from '~/src/server/plugins/engine/components/PaymentField.js'
|
|
9
|
+
import {
|
|
10
|
+
getAnswer,
|
|
11
|
+
type Field
|
|
12
|
+
} from '~/src/server/plugins/engine/components/helpers/components.js'
|
|
13
|
+
import { FormModel } from '~/src/server/plugins/engine/models/FormModel.js'
|
|
14
|
+
import { PaymentPreAuthError } from '~/src/server/plugins/engine/pageControllers/errors.js'
|
|
15
|
+
import {
|
|
16
|
+
type FormContext,
|
|
17
|
+
type FormValue
|
|
18
|
+
} from '~/src/server/plugins/engine/types.js'
|
|
19
|
+
import {
|
|
20
|
+
type FormRequestPayload,
|
|
21
|
+
type FormResponseToolkit
|
|
22
|
+
} from '~/src/server/routes/types.js'
|
|
23
|
+
import { get, post, postJson } from '~/src/server/services/httpService.js'
|
|
24
|
+
import definition from '~/test/form/definitions/blank.js'
|
|
25
|
+
import { getFormData, getFormState } from '~/test/helpers/component-helpers.js'
|
|
26
|
+
|
|
27
|
+
jest.mock('~/src/server/services/httpService.ts')
|
|
28
|
+
|
|
29
|
+
describe('PaymentField', () => {
|
|
30
|
+
let model: FormModel
|
|
31
|
+
|
|
32
|
+
beforeEach(() => {
|
|
33
|
+
model = new FormModel(definition, {
|
|
34
|
+
basePath: 'test'
|
|
35
|
+
})
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
describe('Defaults', () => {
|
|
39
|
+
let def: PaymentFieldComponent
|
|
40
|
+
let collection: ComponentCollection
|
|
41
|
+
let field: Field
|
|
42
|
+
|
|
43
|
+
beforeEach(() => {
|
|
44
|
+
def = {
|
|
45
|
+
title: 'Example payment field',
|
|
46
|
+
name: 'myComponent',
|
|
47
|
+
type: ComponentType.PaymentField,
|
|
48
|
+
options: {
|
|
49
|
+
amount: 100,
|
|
50
|
+
description: 'Test payment description'
|
|
51
|
+
}
|
|
52
|
+
} satisfies PaymentFieldComponent
|
|
53
|
+
|
|
54
|
+
collection = new ComponentCollection([def], { model })
|
|
55
|
+
field = collection.fields[0]
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
describe('Schema', () => {
|
|
59
|
+
it('uses component title as label as default', () => {
|
|
60
|
+
const { formSchema } = collection
|
|
61
|
+
const { keys } = formSchema.describe()
|
|
62
|
+
|
|
63
|
+
expect(keys).toHaveProperty(
|
|
64
|
+
'myComponent',
|
|
65
|
+
expect.objectContaining({
|
|
66
|
+
flags: expect.objectContaining({
|
|
67
|
+
label: 'Example payment field'
|
|
68
|
+
})
|
|
69
|
+
})
|
|
70
|
+
)
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
it('uses component name as keys', () => {
|
|
74
|
+
const { formSchema } = collection
|
|
75
|
+
const { keys } = formSchema.describe()
|
|
76
|
+
|
|
77
|
+
expect(field.keys).toEqual(['myComponent'])
|
|
78
|
+
expect(field.collection).toBeUndefined()
|
|
79
|
+
|
|
80
|
+
for (const key of field.keys) {
|
|
81
|
+
expect(keys).toHaveProperty(key)
|
|
82
|
+
}
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
it('is required by default', () => {
|
|
86
|
+
const { formSchema } = collection
|
|
87
|
+
const { keys } = formSchema.describe()
|
|
88
|
+
|
|
89
|
+
expect(keys).toHaveProperty(
|
|
90
|
+
'myComponent',
|
|
91
|
+
expect.objectContaining({
|
|
92
|
+
keys: expect.objectContaining({
|
|
93
|
+
amount: expect.objectContaining({
|
|
94
|
+
flags: expect.objectContaining({
|
|
95
|
+
presence: 'required'
|
|
96
|
+
})
|
|
97
|
+
})
|
|
98
|
+
})
|
|
99
|
+
})
|
|
100
|
+
)
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
it('adds errors for empty value', () => {
|
|
104
|
+
const payment = {
|
|
105
|
+
paymentId: '',
|
|
106
|
+
reference: '',
|
|
107
|
+
amount: 0,
|
|
108
|
+
description: '',
|
|
109
|
+
uuid: '',
|
|
110
|
+
formId: '',
|
|
111
|
+
isLivePayment: false
|
|
112
|
+
}
|
|
113
|
+
const result = collection.validate(
|
|
114
|
+
getFormData(payment as unknown as FormValue)
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
const errors = result.errors ?? []
|
|
118
|
+
|
|
119
|
+
expect(errors[0]).toEqual(
|
|
120
|
+
expect.objectContaining({
|
|
121
|
+
text: 'Enter myComponent.paymentId'
|
|
122
|
+
})
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
expect(errors[1]).toEqual(
|
|
126
|
+
expect.objectContaining({
|
|
127
|
+
text: 'Enter myComponent.reference'
|
|
128
|
+
})
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
expect(errors[2]).toEqual(
|
|
132
|
+
expect.objectContaining({
|
|
133
|
+
text: 'Enter myComponent.description'
|
|
134
|
+
})
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
expect(errors[3]).toEqual(
|
|
138
|
+
expect.objectContaining({
|
|
139
|
+
text: 'Enter myComponent.uuid'
|
|
140
|
+
})
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
expect(errors[4]).toEqual(
|
|
144
|
+
expect.objectContaining({
|
|
145
|
+
text: 'Enter myComponent.formId'
|
|
146
|
+
})
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
expect(errors[5]).toEqual(
|
|
150
|
+
expect.objectContaining({
|
|
151
|
+
text: 'Select myComponent.preAuth'
|
|
152
|
+
})
|
|
153
|
+
)
|
|
154
|
+
})
|
|
155
|
+
|
|
156
|
+
it('adds errors for invalid values', () => {
|
|
157
|
+
const result1 = collection.validate(getFormData(['invalid']))
|
|
158
|
+
const result2 = collection.validate(
|
|
159
|
+
// @ts-expect-error - Allow invalid param for test
|
|
160
|
+
getFormData({ unknown: 'invalid' })
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
expect(result1.errors).toBeTruthy()
|
|
164
|
+
expect(result2.errors).toBeTruthy()
|
|
165
|
+
})
|
|
166
|
+
})
|
|
167
|
+
|
|
168
|
+
describe('State', () => {
|
|
169
|
+
const paymentForState = {
|
|
170
|
+
paymentId: 'payment-id',
|
|
171
|
+
reference: 'payment-ref',
|
|
172
|
+
amount: 150,
|
|
173
|
+
description: 'payment description',
|
|
174
|
+
uuid: 'ee501106-4ce1-4947-91a7-7cc1a335ccd8',
|
|
175
|
+
formId: 'formid',
|
|
176
|
+
isLivePayment: false
|
|
177
|
+
}
|
|
178
|
+
it('returns text from state', () => {
|
|
179
|
+
const state1 = getFormState(paymentForState as unknown as FormValue)
|
|
180
|
+
const state2 = getFormState(null)
|
|
181
|
+
|
|
182
|
+
const answer1 = getAnswer(field, state1)
|
|
183
|
+
const answer2 = getAnswer(field, state2)
|
|
184
|
+
|
|
185
|
+
expect(answer1).toBe('£150.00 - payment description')
|
|
186
|
+
expect(answer2).toBe('')
|
|
187
|
+
})
|
|
188
|
+
})
|
|
189
|
+
|
|
190
|
+
describe('View model', () => {
|
|
191
|
+
it('sets Nunjucks component defaults', () => {
|
|
192
|
+
const viewModel = field.getViewModel(getFormData(undefined))
|
|
193
|
+
|
|
194
|
+
expect(viewModel).toEqual(
|
|
195
|
+
expect.objectContaining({
|
|
196
|
+
label: { text: def.title },
|
|
197
|
+
name: 'myComponent',
|
|
198
|
+
id: 'myComponent',
|
|
199
|
+
amount: '100.00',
|
|
200
|
+
attributes: {},
|
|
201
|
+
description: 'Test payment description'
|
|
202
|
+
})
|
|
203
|
+
)
|
|
204
|
+
})
|
|
205
|
+
|
|
206
|
+
it('sets Nunjucks component values', () => {
|
|
207
|
+
const paymentForViewModel = {
|
|
208
|
+
paymentId: 'payment-id',
|
|
209
|
+
reference: 'payment-ref',
|
|
210
|
+
uuid: 'ee501106-4ce1-4947-91a7-7cc1a335ccd8',
|
|
211
|
+
formId: 'formid',
|
|
212
|
+
amount: 100,
|
|
213
|
+
description: 'Test payment description',
|
|
214
|
+
isLivePayment: false
|
|
215
|
+
} as unknown as FormValue
|
|
216
|
+
const viewModel = field.getViewModel(getFormData(paymentForViewModel))
|
|
217
|
+
|
|
218
|
+
expect(viewModel).toEqual(
|
|
219
|
+
expect.objectContaining({
|
|
220
|
+
label: { text: def.title },
|
|
221
|
+
name: 'myComponent',
|
|
222
|
+
id: 'myComponent',
|
|
223
|
+
amount: '100.00',
|
|
224
|
+
attributes: {},
|
|
225
|
+
description: 'Test payment description'
|
|
226
|
+
})
|
|
227
|
+
)
|
|
228
|
+
})
|
|
229
|
+
})
|
|
230
|
+
|
|
231
|
+
describe('AllPossibleErrors', () => {
|
|
232
|
+
it('should return errors', () => {
|
|
233
|
+
const errors = field.getAllPossibleErrors()
|
|
234
|
+
expect(errors.baseErrors).not.toBeEmpty()
|
|
235
|
+
expect(errors.advancedSettingsErrors).toBeEmpty()
|
|
236
|
+
})
|
|
237
|
+
})
|
|
238
|
+
})
|
|
239
|
+
|
|
240
|
+
describe('dispatcher and onSubmit', () => {
|
|
241
|
+
const def = {
|
|
242
|
+
title: 'Example payment field',
|
|
243
|
+
name: 'myComponent',
|
|
244
|
+
type: ComponentType.PaymentField,
|
|
245
|
+
options: {
|
|
246
|
+
amount: 100,
|
|
247
|
+
description: 'Test payment description'
|
|
248
|
+
}
|
|
249
|
+
} satisfies PaymentFieldComponent
|
|
250
|
+
|
|
251
|
+
const collection = new ComponentCollection([def], { model })
|
|
252
|
+
const paymentField = collection.fields[0] as PaymentField
|
|
253
|
+
|
|
254
|
+
describe('dispatcher', () => {
|
|
255
|
+
it('should create payment and redirect to gov pay', async () => {
|
|
256
|
+
const mockYarSet = jest.fn()
|
|
257
|
+
const mockRequest = {
|
|
258
|
+
server: {
|
|
259
|
+
plugins: {
|
|
260
|
+
// eslint-disable-next-line no-useless-computed-key
|
|
261
|
+
['forms-engine-plugin']: {
|
|
262
|
+
baseUrl: 'base-url'
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
},
|
|
266
|
+
yar: {
|
|
267
|
+
set: mockYarSet
|
|
268
|
+
}
|
|
269
|
+
} as unknown as FormRequestPayload
|
|
270
|
+
const mockH = {
|
|
271
|
+
redirect: jest
|
|
272
|
+
.fn()
|
|
273
|
+
.mockReturnValueOnce({ code: jest.fn().mockReturnValueOnce('ok') })
|
|
274
|
+
} as unknown as FormResponseToolkit
|
|
275
|
+
const args = {
|
|
276
|
+
controller: {
|
|
277
|
+
model: {
|
|
278
|
+
formId: 'formid',
|
|
279
|
+
basePath: 'base-path',
|
|
280
|
+
name: 'PaymentModel'
|
|
281
|
+
},
|
|
282
|
+
getState: jest
|
|
283
|
+
.fn()
|
|
284
|
+
.mockResolvedValueOnce({ $$__referenceNumber: 'pay-ref-123' })
|
|
285
|
+
},
|
|
286
|
+
component: paymentField,
|
|
287
|
+
sourceUrl: 'http://localhost:3009/test-payment',
|
|
288
|
+
isLive: false,
|
|
289
|
+
isPreview: true
|
|
290
|
+
}
|
|
291
|
+
// @ts-expect-error - partial mock
|
|
292
|
+
jest.mocked(postJson).mockResolvedValueOnce({
|
|
293
|
+
payload: {
|
|
294
|
+
state: {
|
|
295
|
+
status: 'created'
|
|
296
|
+
},
|
|
297
|
+
payment_id: 'new-payment-id',
|
|
298
|
+
_links: {
|
|
299
|
+
next_url: {
|
|
300
|
+
href: '/next-url'
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
})
|
|
305
|
+
|
|
306
|
+
const res = await PaymentField.dispatcher(mockRequest, mockH, args)
|
|
307
|
+
expect(res).toBe('ok')
|
|
308
|
+
expect(mockYarSet).toHaveBeenCalledWith(expect.any(String), {
|
|
309
|
+
amount: 100,
|
|
310
|
+
componentName: 'myComponent',
|
|
311
|
+
description: 'Test payment description',
|
|
312
|
+
failureUrl: 'http://localhost:3009/test-payment',
|
|
313
|
+
formId: 'formid',
|
|
314
|
+
isLivePayment: false,
|
|
315
|
+
paymentId: 'new-payment-id',
|
|
316
|
+
reference: 'pay-ref-123',
|
|
317
|
+
returnUrl: 'base-url/base-path/summary',
|
|
318
|
+
uuid: expect.any(String)
|
|
319
|
+
})
|
|
320
|
+
})
|
|
321
|
+
|
|
322
|
+
it('should redirect to summary if payment is already pre-authorised', async () => {
|
|
323
|
+
const mockRedirectCode = jest.fn().mockReturnValueOnce('redirected')
|
|
324
|
+
const mockH = {
|
|
325
|
+
redirect: jest.fn().mockReturnValueOnce({ code: mockRedirectCode })
|
|
326
|
+
} as unknown as FormResponseToolkit
|
|
327
|
+
const mockRequest = {
|
|
328
|
+
server: {
|
|
329
|
+
plugins: {
|
|
330
|
+
// eslint-disable-next-line no-useless-computed-key
|
|
331
|
+
['forms-engine-plugin']: {
|
|
332
|
+
baseUrl: 'base-url'
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
},
|
|
336
|
+
yar: {
|
|
337
|
+
set: jest.fn()
|
|
338
|
+
}
|
|
339
|
+
} as unknown as FormRequestPayload
|
|
340
|
+
const args = {
|
|
341
|
+
controller: {
|
|
342
|
+
model: {
|
|
343
|
+
formId: 'formid',
|
|
344
|
+
basePath: 'base-path',
|
|
345
|
+
name: 'PaymentModel'
|
|
346
|
+
},
|
|
347
|
+
getState: jest.fn().mockResolvedValueOnce({
|
|
348
|
+
$$__referenceNumber: 'pay-ref-123',
|
|
349
|
+
myComponent: {
|
|
350
|
+
paymentId: 'existing-payment-id',
|
|
351
|
+
amount: 100,
|
|
352
|
+
description: 'Test payment',
|
|
353
|
+
preAuth: {
|
|
354
|
+
status: 'success',
|
|
355
|
+
createdAt: '2026-01-29T12:00:00.000Z'
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
})
|
|
359
|
+
},
|
|
360
|
+
component: paymentField,
|
|
361
|
+
sourceUrl: 'http://localhost:3009/test-payment',
|
|
362
|
+
isLive: false,
|
|
363
|
+
isPreview: true
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
const res = await PaymentField.dispatcher(mockRequest, mockH, args)
|
|
367
|
+
|
|
368
|
+
expect(res).toBe('redirected')
|
|
369
|
+
expect(mockH.redirect).toHaveBeenCalledWith(
|
|
370
|
+
'base-url/base-path/summary'
|
|
371
|
+
)
|
|
372
|
+
expect(mockRedirectCode).toHaveBeenCalledWith(303)
|
|
373
|
+
expect(postJson).not.toHaveBeenCalled()
|
|
374
|
+
})
|
|
375
|
+
})
|
|
376
|
+
|
|
377
|
+
describe('onSubmit', () => {
|
|
378
|
+
it('should throw if missing state', async () => {
|
|
379
|
+
const mockRequest = {} as unknown as FormRequestPayload
|
|
380
|
+
|
|
381
|
+
const error = await paymentField
|
|
382
|
+
.onSubmit(
|
|
383
|
+
mockRequest,
|
|
384
|
+
{} as FormMetadata,
|
|
385
|
+
{ state: {} } as FormContext
|
|
386
|
+
)
|
|
387
|
+
.catch((e: unknown) => e)
|
|
388
|
+
|
|
389
|
+
expect(error).toBeInstanceOf(PaymentPreAuthError)
|
|
390
|
+
expect((error as PaymentPreAuthError).component).toBe(paymentField)
|
|
391
|
+
expect((error as PaymentPreAuthError).userMessage).toBe(
|
|
392
|
+
'Complete the payment to continue'
|
|
393
|
+
)
|
|
394
|
+
})
|
|
395
|
+
|
|
396
|
+
it('should ignore if our state says payment already captured', async () => {
|
|
397
|
+
const mockRequest = {} as unknown as FormRequestPayload
|
|
398
|
+
|
|
399
|
+
await paymentField.onSubmit(
|
|
400
|
+
mockRequest,
|
|
401
|
+
{} as FormMetadata,
|
|
402
|
+
{
|
|
403
|
+
state: {
|
|
404
|
+
myComponent: {
|
|
405
|
+
capture: {
|
|
406
|
+
status: 'success'
|
|
407
|
+
},
|
|
408
|
+
paymentId: 'payment-id',
|
|
409
|
+
amount: 123,
|
|
410
|
+
description: 'Payment desc'
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
} as unknown as FormContext
|
|
414
|
+
)
|
|
415
|
+
expect(get).not.toHaveBeenCalled()
|
|
416
|
+
expect(post).not.toHaveBeenCalled()
|
|
417
|
+
})
|
|
418
|
+
|
|
419
|
+
it('should mark payment already captured according to gov pay', async () => {
|
|
420
|
+
const mockRequest = {} as unknown as FormRequestPayload
|
|
421
|
+
jest
|
|
422
|
+
.mocked(get)
|
|
423
|
+
// @ts-expect-error - partial mock
|
|
424
|
+
.mockResolvedValueOnce({
|
|
425
|
+
payload: { amount: 10000, state: { status: 'success' } }
|
|
426
|
+
})
|
|
427
|
+
await paymentField.onSubmit(
|
|
428
|
+
mockRequest,
|
|
429
|
+
{} as FormMetadata,
|
|
430
|
+
{
|
|
431
|
+
state: {
|
|
432
|
+
myComponent: {
|
|
433
|
+
paymentId: 'payment-id',
|
|
434
|
+
amount: 100,
|
|
435
|
+
description: 'Payment desc',
|
|
436
|
+
isLivePayment: false,
|
|
437
|
+
formId: 'formid'
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
} as unknown as FormContext
|
|
441
|
+
)
|
|
442
|
+
expect(get).toHaveBeenCalled()
|
|
443
|
+
expect(post).not.toHaveBeenCalled()
|
|
444
|
+
})
|
|
445
|
+
|
|
446
|
+
it('should throw if bad status', async () => {
|
|
447
|
+
const mockRequest = {} as unknown as FormRequestPayload
|
|
448
|
+
jest
|
|
449
|
+
.mocked(get)
|
|
450
|
+
// @ts-expect-error - partial mock
|
|
451
|
+
.mockResolvedValueOnce({
|
|
452
|
+
payload: { amount: 10000, state: { status: 'bad' } }
|
|
453
|
+
})
|
|
454
|
+
const error = await paymentField
|
|
455
|
+
.onSubmit(
|
|
456
|
+
mockRequest,
|
|
457
|
+
{} as FormMetadata,
|
|
458
|
+
{
|
|
459
|
+
state: {
|
|
460
|
+
myComponent: {
|
|
461
|
+
paymentId: 'payment-id',
|
|
462
|
+
amount: 100,
|
|
463
|
+
description: 'Payment desc',
|
|
464
|
+
isLivePayment: false,
|
|
465
|
+
formId: 'formid'
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
} as unknown as FormContext
|
|
469
|
+
)
|
|
470
|
+
.catch((e: unknown) => e)
|
|
471
|
+
|
|
472
|
+
expect(error).toBeInstanceOf(PaymentPreAuthError)
|
|
473
|
+
expect((error as PaymentPreAuthError).component).toBe(paymentField)
|
|
474
|
+
expect((error as PaymentPreAuthError).userMessage).toBe(
|
|
475
|
+
'Your payment authorisation has expired. Please add your payment details again.'
|
|
476
|
+
)
|
|
477
|
+
})
|
|
478
|
+
|
|
479
|
+
it('should throw if error during capture', async () => {
|
|
480
|
+
const mockRequest = {} as unknown as FormRequestPayload
|
|
481
|
+
jest
|
|
482
|
+
.mocked(get)
|
|
483
|
+
// @ts-expect-error - partial mock
|
|
484
|
+
.mockResolvedValueOnce({
|
|
485
|
+
payload: { amount: 10000, state: { status: 'capturable' } }
|
|
486
|
+
})
|
|
487
|
+
// @ts-expect-error - partial mock
|
|
488
|
+
jest.mocked(post).mockResolvedValueOnce({ res: { statusCode: 400 } })
|
|
489
|
+
const error = await paymentField
|
|
490
|
+
.onSubmit(
|
|
491
|
+
mockRequest,
|
|
492
|
+
{} as FormMetadata,
|
|
493
|
+
{
|
|
494
|
+
state: {
|
|
495
|
+
myComponent: {
|
|
496
|
+
paymentId: 'payment-id',
|
|
497
|
+
amount: 123,
|
|
498
|
+
description: 'Payment desc',
|
|
499
|
+
isLivePayment: false,
|
|
500
|
+
formId: 'formid'
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
} as unknown as FormContext
|
|
504
|
+
)
|
|
505
|
+
.catch((e: unknown) => e)
|
|
506
|
+
|
|
507
|
+
expect(error).toBeInstanceOf(PaymentPreAuthError)
|
|
508
|
+
expect((error as PaymentPreAuthError).component).toBe(paymentField)
|
|
509
|
+
expect((error as PaymentPreAuthError).userMessage).toBe(
|
|
510
|
+
'There was a problem and your form was not submitted. Try submitting the form again.'
|
|
511
|
+
)
|
|
512
|
+
})
|
|
513
|
+
|
|
514
|
+
it('should throw if amount mismatch', async () => {
|
|
515
|
+
const mockRequest = {} as unknown as FormRequestPayload
|
|
516
|
+
jest
|
|
517
|
+
.mocked(get)
|
|
518
|
+
// @ts-expect-error - partial mock
|
|
519
|
+
.mockResolvedValueOnce({
|
|
520
|
+
payload: { amount: 5000, state: { status: 'capturable' } }
|
|
521
|
+
})
|
|
522
|
+
// @ts-expect-error - partial mock
|
|
523
|
+
jest.mocked(post).mockResolvedValueOnce({ res: { statusCode: 200 } })
|
|
524
|
+
const error = await paymentField
|
|
525
|
+
.onSubmit(
|
|
526
|
+
mockRequest,
|
|
527
|
+
{} as FormMetadata,
|
|
528
|
+
{
|
|
529
|
+
state: {
|
|
530
|
+
myComponent: {
|
|
531
|
+
paymentId: 'payment-id',
|
|
532
|
+
amount: 123,
|
|
533
|
+
description: 'Payment desc',
|
|
534
|
+
isLivePayment: false,
|
|
535
|
+
formId: 'formid'
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
} as unknown as FormContext
|
|
539
|
+
)
|
|
540
|
+
.catch((e: unknown) => e)
|
|
541
|
+
|
|
542
|
+
expect(error).toBeInstanceOf(PaymentPreAuthError)
|
|
543
|
+
expect((error as PaymentPreAuthError).component).toBe(paymentField)
|
|
544
|
+
expect((error as PaymentPreAuthError).userMessage).toBe(
|
|
545
|
+
'The pre-authorised payment amount is somehow different from that requested. Try adding payment details again.'
|
|
546
|
+
)
|
|
547
|
+
})
|
|
548
|
+
|
|
549
|
+
it('should capture payment if no errors', async () => {
|
|
550
|
+
const mockRequest = {} as unknown as FormRequestPayload
|
|
551
|
+
jest
|
|
552
|
+
.mocked(get)
|
|
553
|
+
// @ts-expect-error - partial mock
|
|
554
|
+
.mockResolvedValueOnce({
|
|
555
|
+
payload: { amount: 10000, state: { status: 'capturable' } }
|
|
556
|
+
})
|
|
557
|
+
// @ts-expect-error - partial mock
|
|
558
|
+
jest.mocked(post).mockResolvedValueOnce({ res: { statusCode: 200 } })
|
|
559
|
+
await paymentField.onSubmit(
|
|
560
|
+
mockRequest,
|
|
561
|
+
{} as FormMetadata,
|
|
562
|
+
{
|
|
563
|
+
state: {
|
|
564
|
+
myComponent: {
|
|
565
|
+
paymentId: 'payment-id',
|
|
566
|
+
amount: 123,
|
|
567
|
+
description: 'Payment desc',
|
|
568
|
+
isLivePayment: false,
|
|
569
|
+
formId: 'formid'
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
} as unknown as FormContext
|
|
573
|
+
)
|
|
574
|
+
expect(get).toHaveBeenCalled()
|
|
575
|
+
expect(post).toHaveBeenCalled()
|
|
576
|
+
})
|
|
577
|
+
})
|
|
578
|
+
|
|
579
|
+
describe('getFormValue', () => {
|
|
580
|
+
it('should return undefined', () => {
|
|
581
|
+
expect(paymentField.getFormValue({})).toBeUndefined()
|
|
582
|
+
})
|
|
583
|
+
it('should return value', () => {
|
|
584
|
+
const payment = {
|
|
585
|
+
paymentId: 'payment-id',
|
|
586
|
+
amount: 123,
|
|
587
|
+
description: 'Payment desc',
|
|
588
|
+
isLivePayment: false,
|
|
589
|
+
formId: 'formid'
|
|
590
|
+
}
|
|
591
|
+
expect(paymentField.getFormValue(payment)).toEqual(payment)
|
|
592
|
+
})
|
|
593
|
+
})
|
|
594
|
+
|
|
595
|
+
describe('isState', () => {
|
|
596
|
+
it('should return false if not valid state', () => {
|
|
597
|
+
expect(paymentField.isState({})).toBe(false)
|
|
598
|
+
})
|
|
599
|
+
it('should return value', () => {
|
|
600
|
+
const payment = {
|
|
601
|
+
paymentId: 'payment-id',
|
|
602
|
+
amount: 123,
|
|
603
|
+
description: 'Payment desc',
|
|
604
|
+
isLivePayment: false,
|
|
605
|
+
formId: 'formid'
|
|
606
|
+
}
|
|
607
|
+
expect(paymentField.isState(payment)).toBe(true)
|
|
608
|
+
})
|
|
609
|
+
})
|
|
610
|
+
})
|
|
611
|
+
})
|