@defra-fish/gafl-webapp-service 1.64.0-rc.2 → 1.64.0-rc.20

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@defra-fish/gafl-webapp-service",
3
- "version": "1.64.0-rc.2",
3
+ "version": "1.64.0-rc.20",
4
4
  "description": "The websales frontend for the GAFL service",
5
5
  "type": "module",
6
6
  "engines": {
@@ -36,48 +36,48 @@
36
36
  "prepare": "gulp --gulpfile build/gulpfile.cjs"
37
37
  },
38
38
  "dependencies": {
39
- "@defra-fish/business-rules-lib": "1.64.0-rc.2",
40
- "@defra-fish/connectors-lib": "1.64.0-rc.2",
41
- "@defra/hapi-gapi": "^2.0.0",
42
- "@hapi/boom": "^9.1.2",
43
- "@hapi/catbox-redis": "^6.0.2",
44
- "@hapi/cookie": "^11.0.2",
45
- "@hapi/crumb": "^8.0.1",
46
- "@hapi/hapi": "^20.2.1",
47
- "@hapi/inert": "^6.0.5",
48
- "@hapi/joi-date": "^2.0.1",
49
- "@hapi/scooter": "^6.0.1",
50
- "@hapi/vision": "^6.1.0",
51
- "blankie": "^5.0.0",
52
- "debug": "^4.3.3",
53
- "disinfect": "^1.1.0",
54
- "find": "^0.3.0",
55
- "govuk-frontend": "^5.10.2",
56
- "hapi-i18n": "^3.0.1",
57
- "joi": "^17.6.0",
58
- "moment": "^2.29.1",
59
- "moment-timezone": "^0.5.34",
60
- "node-fetch": "^2.7.0",
61
- "nunjucks": "^3.2.3",
62
- "openid-client": "^4.9.1",
63
- "semver": "^7.3.5",
64
- "uuid": "^8.3.2"
39
+ "@defra-fish/business-rules-lib": "1.64.0-rc.20",
40
+ "@defra-fish/connectors-lib": "1.64.0-rc.20",
41
+ "@defra/hapi-gapi": "2.0.0",
42
+ "@hapi/boom": "9.1.2",
43
+ "@hapi/catbox-redis": "6.0.2",
44
+ "@hapi/cookie": "11.0.2",
45
+ "@hapi/crumb": "8.0.1",
46
+ "@hapi/hapi": "20.2.1",
47
+ "@hapi/inert": "6.0.5",
48
+ "@hapi/joi-date": "2.0.1",
49
+ "@hapi/scooter": "6.0.1",
50
+ "@hapi/vision": "6.1.0",
51
+ "blankie": "5.0.0",
52
+ "debug": "4.3.3",
53
+ "disinfect": "1.1.0",
54
+ "find": "0.3.0",
55
+ "govuk-frontend": "5.10.2",
56
+ "hapi-i18n": "3.0.1",
57
+ "joi": "17.6.0",
58
+ "moment": "2.29.1",
59
+ "moment-timezone": "0.5.34",
60
+ "node-fetch": "2.7.0",
61
+ "nunjucks": "3.2.3",
62
+ "openid-client": "4.9.1",
63
+ "semver": "7.3.5",
64
+ "uuid": "8.3.2"
65
65
  },
66
66
  "devDependencies": {
67
- "@hapi/catbox-memory": "^5.0.1",
68
- "del": "^6.0.0",
69
- "gulp": "^4.0.2",
70
- "gulp-concat": "^2.6.1",
71
- "gulp-merge": "^0.1.1",
72
- "gulp-minify": "^3.1.0",
73
- "gulp-sass": "^5.1.0",
74
- "gulp-sourcemaps": "^3.0.0",
75
- "sass": "^1.69.3"
67
+ "@hapi/catbox-memory": "5.0.1",
68
+ "del": "6.0.0",
69
+ "gulp": "4.0.2",
70
+ "gulp-concat": "2.6.1",
71
+ "gulp-merge": "0.1.1",
72
+ "gulp-minify": "3.1.0",
73
+ "gulp-sass": "5.1.0",
74
+ "gulp-sourcemaps": "3.0.0",
75
+ "sass": "1.69.3"
76
76
  },
77
77
  "jest": {
78
78
  "setupFilesAfterEnv": [
79
79
  "./gafl-jest-matchers.js"
80
80
  ]
81
81
  },
82
- "gitHead": "b901cb67affda78c747af57f88753b8039bd2172"
82
+ "gitHead": "853e2b2a4d2e9ce0160d941b3e108e972a108ae5"
83
83
  }
@@ -1,18 +1,207 @@
1
- import cancelRpAuthenticationHandler from '../cancel-rp-authentication-handler'
2
- import { CONTROLLER } from '../../uri.js'
1
+ import handler from '../cancel-rp-authentication-handler'
2
+ import { CANCEL_RP_IDENTIFY, CANCEL_RP_DETAILS } from '../../uri.js'
3
+ import { addLanguageCodeToUri } from '../../processors/uri-helper.js'
4
+ import { salesApi } from '@defra-fish/connectors-lib'
3
5
 
6
+ jest.mock('../../processors/uri-helper.js')
7
+ jest.mock('@defra-fish/connectors-lib')
8
+ jest.mock('../../processors/renewals-write-cache.js', () => ({
9
+ setUpCacheFromAuthenticationResult: jest.fn().mockResolvedValue(undefined),
10
+ setUpPayloads: jest.fn().mockResolvedValue(undefined)
11
+ }))
12
+ jest.mock('@defra-fish/business-rules-lib', () => ({
13
+ validation: {
14
+ contact: {
15
+ createBirthDateValidator: () => ({ validateAsync: async () => '1970-01-01' }),
16
+ createOverseasPostcodeValidator: () => ({ validateAsync: async () => 'AA1 1AA' })
17
+ }
18
+ }
19
+ }))
4
20
  jest.mock('../../uri.js', () => ({
5
- CONTROLLER: { uri: Symbol('controller-uri') }
21
+ CANCEL_RP_IDENTIFY: { page: 'cancel-rp-identify page', uri: Symbol('cancel-rp-identify-uri') },
22
+ CANCEL_RP_DETAILS: { uri: Symbol('cancel-rp-details-uri') }
6
23
  }))
7
24
 
8
- const getSampleResponseToolkit = () => ({
9
- redirectWithLanguageCode: jest.fn()
25
+ const getSampleRequest = (payloadOverride = {}) => {
26
+ const page = {
27
+ getCurrentPermission: jest.fn().mockResolvedValue({
28
+ payload: {
29
+ referenceNumber: 'ABC123',
30
+ 'date-of-birth-day': '01',
31
+ 'date-of-birth-month': '01',
32
+ 'date-of-birth-year': '1970',
33
+ postcode: 'AA1 1AA',
34
+ ...payloadOverride
35
+ }
36
+ }),
37
+ setCurrentPermission: jest.fn()
38
+ }
39
+ const status = {
40
+ getCurrentPermission: jest.fn().mockResolvedValue({}),
41
+ setCurrentPermission: jest.fn()
42
+ }
43
+ const helpers = { page, status }
44
+ const cache = () => ({ helpers })
45
+ return { cache }
46
+ }
47
+
48
+ const getSampleResponseTooklkit = () => ({
49
+ redirectWithLanguageCode: jest.fn().mockReturnValue('redirected')
50
+ })
51
+
52
+ const invokeHandlerWithMocks = async ({ salesApiResponse, decoratedIdentifyUri } = {}) => {
53
+ if (typeof salesApiResponse !== 'undefined') {
54
+ salesApi.authenticateRecurringPayment.mockResolvedValueOnce(salesApiResponse)
55
+ }
56
+ if (decoratedIdentifyUri) {
57
+ addLanguageCodeToUri.mockReturnValueOnce(decoratedIdentifyUri)
58
+ }
59
+ const request = getSampleRequest()
60
+ const h = getSampleResponseTooklkit()
61
+ if (decoratedIdentifyUri) {
62
+ h.redirect = jest.fn().mockReturnValue('redirect-response')
63
+ }
64
+ const result = await handler(request, h)
65
+ return { request, h, result }
66
+ }
67
+
68
+ const mockSuccessResponse = () => ({
69
+ permission: { id: 'perm-id' },
70
+ recurringPayment: { id: 'rcp-id', status: 0, cancelledDate: null }
10
71
  })
11
72
 
12
73
  describe('Cancel RP Authentication Handler', () => {
13
- it('redirects with language code to CONTROLLER.uri', () => {
14
- const sampleResponseToolkit = getSampleResponseToolkit()
15
- cancelRpAuthenticationHandler(undefined, sampleResponseToolkit)
16
- expect(sampleResponseToolkit.redirectWithLanguageCode).toHaveBeenCalledWith(CONTROLLER.uri)
74
+ beforeEach(() => {
75
+ jest.resetAllMocks()
76
+ })
77
+
78
+ describe('Successful authentication', () => {
79
+ it('returns the redirect result', async () => {
80
+ const { result } = await invokeHandlerWithMocks({
81
+ salesApiResponse: mockSuccessResponse()
82
+ })
83
+ expect(result).toBe('redirected')
84
+ })
85
+
86
+ it('redirects to details', async () => {
87
+ const { h } = await invokeHandlerWithMocks({
88
+ salesApiResponse: mockSuccessResponse()
89
+ })
90
+ expect(h.redirectWithLanguageCode).toHaveBeenCalledWith(CANCEL_RP_DETAILS.uri)
91
+ })
92
+
93
+ it('marks status as authorised', async () => {
94
+ const { request } = await invokeHandlerWithMocks({
95
+ salesApiResponse: mockSuccessResponse()
96
+ })
97
+ expect(request.cache().helpers.status.setCurrentPermission).toHaveBeenCalledWith({ authentication: { authorised: true } })
98
+ })
99
+ })
100
+
101
+ describe('Unsuccessful authentication - no match', () => {
102
+ it('redirects to the decorated identify URI', async () => {
103
+ const { h } = await invokeHandlerWithMocks({ salesApiResponse: null, decoratedIdentifyUri: 'decorated-identify-uri' })
104
+ expect(h.redirect).toHaveBeenCalledWith('decorated-identify-uri')
105
+ })
106
+
107
+ it('sets page cache error and preserves payload', async () => {
108
+ const { request } = await invokeHandlerWithMocks({ salesApiResponse: null, decoratedIdentifyUri: 'decorated-identify-uri' })
109
+ expect(request.cache().helpers.page.setCurrentPermission).toHaveBeenCalledWith(
110
+ CANCEL_RP_IDENTIFY.page,
111
+ expect.objectContaining({
112
+ payload: expect.any(Object),
113
+ error: { referenceNumber: 'not-found' }
114
+ })
115
+ )
116
+ })
117
+
118
+ it('marks status as unauthorised', async () => {
119
+ const { request } = await invokeHandlerWithMocks({ salesApiResponse: null, decoratedIdentifyUri: 'decorated-identify-uri' })
120
+ expect(request.cache().helpers.status.setCurrentPermission).toHaveBeenCalledWith(
121
+ expect.objectContaining({ authentication: { authorised: false } })
122
+ )
123
+ })
124
+ })
125
+
126
+ describe('Unsuccessful authentication - no recurring payment agreement', () => {
127
+ it('redirects to the decorated identify URI', async () => {
128
+ const { h } = await invokeHandlerWithMocks({
129
+ salesApiResponse: { permission: { id: 'perm-id' }, recurringPayment: null },
130
+ decoratedIdentifyUri: 'decorated-identify-uri'
131
+ })
132
+ expect(h.redirect).toHaveBeenCalledWith('decorated-identify-uri')
133
+ })
134
+
135
+ it('sets page cache error for no RCP setup', async () => {
136
+ const { request } = await invokeHandlerWithMocks({
137
+ salesApiResponse: { permission: { id: 'perm-id' }, recurringPayment: null },
138
+ decoratedIdentifyUri: 'decorated-identify-uri'
139
+ })
140
+ expect(request.cache().helpers.page.setCurrentPermission).toHaveBeenCalledWith(
141
+ CANCEL_RP_IDENTIFY.page,
142
+ expect.objectContaining({
143
+ payload: expect.any(Object),
144
+ error: { recurringPayment: 'not-set-up' }
145
+ })
146
+ )
147
+ })
148
+
149
+ it('marks status as unauthorised', async () => {
150
+ const { request } = await invokeHandlerWithMocks({
151
+ salesApiResponse: { permission: { id: 'perm-id' }, recurringPayment: null },
152
+ decoratedIdentifyUri: 'decorated-identify-uri'
153
+ })
154
+ expect(request.cache().helpers.status.setCurrentPermission).toHaveBeenCalledWith(
155
+ expect.objectContaining({ authentication: { authorised: false } })
156
+ )
157
+ })
158
+ })
159
+
160
+ describe('Unsuccessful authentication - RCP cancelled', () => {
161
+ it('redirects to the decorated identify URI', async () => {
162
+ const { h } = await invokeHandlerWithMocks({
163
+ salesApiResponse: { permission: { id: 'perm-id' }, recurringPayment: { id: 'rcp-id', status: 1, cancelledDate: '2024-01-01' } },
164
+ decoratedIdentifyUri: 'decorated-identify-uri'
165
+ })
166
+ expect(h.redirect).toHaveBeenCalledWith('decorated-identify-uri')
167
+ })
168
+
169
+ it('sets page cache error for RCP cancelled', async () => {
170
+ const { request } = await invokeHandlerWithMocks({
171
+ salesApiResponse: { permission: { id: 'perm-id' }, recurringPayment: { id: 'rcp-id', status: 1, cancelledDate: '2024-01-01' } },
172
+ decoratedIdentifyUri: 'decorated-identify-uri'
173
+ })
174
+ expect(request.cache().helpers.page.setCurrentPermission).toHaveBeenCalledWith(
175
+ CANCEL_RP_IDENTIFY.page,
176
+ expect.objectContaining({
177
+ payload: expect.any(Object),
178
+ error: { recurringPayment: 'rcp-cancelled' }
179
+ })
180
+ )
181
+ })
182
+
183
+ it('marks status as unauthorised', async () => {
184
+ const { request } = await invokeHandlerWithMocks({
185
+ salesApiResponse: { permission: { id: 'perm-id' }, recurringPayment: { id: 'rcp-id', status: 1, cancelledDate: '2024-01-01' } },
186
+ decoratedIdentifyUri: 'decorated-identify-uri'
187
+ })
188
+ expect(request.cache().helpers.status.setCurrentPermission).toHaveBeenCalledWith(
189
+ expect.objectContaining({ authentication: { authorised: false } })
190
+ )
191
+ })
192
+ })
193
+
194
+ it('uses referenceNumber from status when payload is missing', async () => {
195
+ salesApi.authenticateRecurringPayment.mockResolvedValueOnce({
196
+ permission: { id: 'perm-id' },
197
+ recurringPayment: { id: 'rcp-id', status: 0, cancelledDate: null }
198
+ })
199
+ const request = getSampleRequest({ referenceNumber: undefined })
200
+ request.cache().helpers.status.getCurrentPermission.mockResolvedValueOnce({
201
+ referenceNumber: 'A1B2C3'
202
+ })
203
+ const h = getSampleResponseTooklkit()
204
+ await handler(request, h)
205
+ expect(salesApi.authenticateRecurringPayment).toHaveBeenCalledWith('A1B2C3', expect.anything(), expect.anything())
17
206
  })
18
207
  })
@@ -1,6 +1,62 @@
1
- import { CONTROLLER } from '../uri.js'
1
+ import { CANCEL_RP_IDENTIFY, CANCEL_RP_DETAILS } from '../../src/uri.js'
2
+ import { addLanguageCodeToUri } from '../processors/uri-helper.js'
3
+ import { salesApi } from '@defra-fish/connectors-lib'
4
+ import { validation } from '@defra-fish/business-rules-lib'
5
+ import { setUpCacheFromAuthenticationResult, setUpPayloads } from '../processors/renewals-write-cache.js'
6
+ import Joi from 'joi'
2
7
 
3
- export default async (_request, h) => {
4
- console.log('cancel rp authenticate handler', h.redirectWithLanguageCode)
5
- return h.redirectWithLanguageCode(CONTROLLER.uri)
8
+ const buildAuthFailure = (referenceNumber, payload, error) => ({
9
+ page: {
10
+ page: CANCEL_RP_IDENTIFY.page,
11
+ data: { payload, error }
12
+ },
13
+ status: {
14
+ referenceNumber,
15
+ authentication: { authorised: false }
16
+ },
17
+ redirectPath: CANCEL_RP_IDENTIFY.uri
18
+ })
19
+
20
+ const applyAuthFailure = async (request, h, failure) => {
21
+ await request.cache().helpers.page.setCurrentPermission(failure.page.page, failure.page.data)
22
+ await request.cache().helpers.status.setCurrentPermission(failure.status)
23
+ return h.redirect(addLanguageCodeToUri(request, failure.redirectPath))
6
24
  }
25
+
26
+ const cancelRpAuthenticationHandler = async (request, h) => {
27
+ const { payload } = await request.cache().helpers.page.getCurrentPermission(CANCEL_RP_IDENTIFY.page)
28
+ const permission = await request.cache().helpers.status.getCurrentPermission()
29
+
30
+ const referenceNumber = payload.referenceNumber || permission.referenceNumber
31
+
32
+ const dateOfBirth = await validation.contact
33
+ .createBirthDateValidator(Joi)
34
+ .validateAsync(`${payload['date-of-birth-year']}-${payload['date-of-birth-month']}-${payload['date-of-birth-day']}`)
35
+ const postcode = await validation.contact.createOverseasPostcodeValidator(Joi).validateAsync(payload.postcode)
36
+
37
+ const authenticationResult = await salesApi.authenticateRecurringPayment(referenceNumber, dateOfBirth, postcode)
38
+
39
+ const failures = error => applyAuthFailure(request, h, buildAuthFailure(referenceNumber, payload, error))
40
+
41
+ if (!authenticationResult) {
42
+ return failures({ referenceNumber: 'not-found' })
43
+ }
44
+
45
+ if (!authenticationResult.recurringPayment) {
46
+ return failures({ recurringPayment: 'not-set-up' })
47
+ }
48
+
49
+ if (authenticationResult.recurringPayment?.status === 1 || authenticationResult.recurringPayment?.cancelledDate) {
50
+ return failures({ recurringPayment: 'rcp-cancelled' })
51
+ }
52
+
53
+ await setUpCacheFromAuthenticationResult(request, authenticationResult)
54
+ await setUpPayloads(request)
55
+ await request.cache().helpers.status.setCurrentPermission({
56
+ authentication: { authorised: true }
57
+ })
58
+
59
+ return h.redirectWithLanguageCode(CANCEL_RP_DETAILS.uri)
60
+ }
61
+
62
+ export default cancelRpAuthenticationHandler
@@ -140,6 +140,19 @@
140
140
  "back": "Yn ôl",
141
141
  "buy_another_licence": "Prynu trwydded arall",
142
142
  "buy_different_licence": "Prynu trwydded wahanol",
143
+ "cancel_rp_identify_caption": "Cancel your recurring card payment agreement",
144
+ "cancel_rp_identify_title": "Enter your details",
145
+ "cancel_rp_identify_body": "Cancelling your recurring card payment agreement will not cancel or refund your current licence. You can still fish until your current licence expires but your licence will not renew automatically.",
146
+ "cancel_rp_identify_ref_number": "Last 6 characters of your licence number",
147
+ "cancel_rp_identify_ref_number_hint": "For example, F4A315",
148
+ "cancel_rp_identify_ref_number_error": "Enter the last 6 characters of your licence number",
149
+ "cancel_rp_identify_ref_pattern": "The last six characters of your licence number don’t look right. Check and enter again",
150
+ "cancel_rp_identify_dob": "Date of birth",
151
+ "cancel_rp_identify_dob_hint": "For example, 31 3 1980",
152
+ "cancel_rp_identify_postcode": "Postcode",
153
+ "cancel_rp_identify_postcode_hint": "For example, WA4 1AB",
154
+ "cancel_rp_identify_postcode_error": "Enter a postcode",
155
+ "cancel_rp_identify_postcode_pattern": "Your postcode doesn’t look right. Check and enter again",
143
156
  "change_licence_details_you": "Adolygu neu newid manylion eich trwydded",
144
157
  "change_licence_details_other": "Adolygu neu newid manylion y drwydded",
145
158
  "change_licence_number": "Newid rhif y drwydded",
@@ -775,7 +788,6 @@
775
788
  "rp_cancel_complete_title": "Cancel your recurring card payment agreement - complete",
776
789
  "rp_cancel_confirm_title": "Cancel your recurring card payment agreement - confirm",
777
790
  "rp_cancel_details_title": "Cancel your recurring card payment agreement - details",
778
- "rp_cancel_identify_title": "Cancel your recurring card payment agreement - identify",
779
791
  "save_changes": "Cadw newidiadau",
780
792
  "server_error_title_suffix": " - GOV.UK",
781
793
  "server_error_bulletpoint_1": "Os ydych chi wedi gofyn am hysbysiad dros e-bost neu neges destun, dylai’r hysbysiad gyrraedd o fewn 3 awr os bydd y taliad wedi cael ei gymryd.",
@@ -140,6 +140,19 @@
140
140
  "back": "Back",
141
141
  "buy_another_licence": "Buy another licence",
142
142
  "buy_different_licence": "Buy a different licence",
143
+ "cancel_rp_identify_caption": "Cancel your recurring card payment agreement",
144
+ "cancel_rp_identify_title": "Enter your details",
145
+ "cancel_rp_identify_body": "Cancelling your recurring card payment agreement will not cancel or refund your current licence. You can still fish until your current licence expires but your licence will not renew automatically.",
146
+ "cancel_rp_identify_ref_number": "Last 6 characters of your licence number",
147
+ "cancel_rp_identify_ref_number_hint": "For example, F4A315",
148
+ "cancel_rp_identify_ref_number_error": "Enter the last 6 characters of your licence number",
149
+ "cancel_rp_identify_ref_pattern": "The last six characters of your licence number don’t look right. Check and enter again",
150
+ "cancel_rp_identify_dob": "Date of birth",
151
+ "cancel_rp_identify_dob_hint": "For example, 31 3 1980",
152
+ "cancel_rp_identify_postcode": "Postcode",
153
+ "cancel_rp_identify_postcode_hint": "For example, WA4 1AB",
154
+ "cancel_rp_identify_postcode_error": "Enter a postcode",
155
+ "cancel_rp_identify_postcode_pattern": "Your postcode doesn’t look right. Check and enter again",
143
156
  "change_licence_details_you": "Review or change the licence details",
144
157
  "change_licence_details_other": "Review or change the licence details",
145
158
  "change_licence_number": "Change licence number",
@@ -775,7 +788,6 @@
775
788
  "rp_cancel_complete_title": "Cancel your recurring card payment agreement - complete",
776
789
  "rp_cancel_confirm_title": "Cancel your recurring card payment agreement - confirm",
777
790
  "rp_cancel_details_title": "Cancel your recurring card payment agreement - details",
778
- "rp_cancel_identify_title": "Cancel your recurring card payment agreement - identify",
779
791
  "save_changes": "Save changes",
780
792
  "server_error_title_suffix": " - GOV.UK",
781
793
  "server_error_bulletpoint_1": "If you have requested an email or text notification, this should be delivered in 3 hours if a payment has been taken.",
@@ -59,17 +59,17 @@
59
59
  <p class="govuk-body">{{ mssgs.access_statement_tech_body }}</p>
60
60
 
61
61
  <h2 class="govuk-heading-m">{{ mssgs.access_statement_compliance_heading }}</h2>
62
- <p class="govuk-body">{{ mssgs.access_statement_compliance_body_1 }}<a href="https://www.w3.org/TR/WCAG21/" class="govuk-link" target="_blank">{{ mssgs.access_statement_compliance_body_link_1 }}</a>{{ mssgs.full_stop }}</p>
63
- <p class="govuk-body">{{ mssgs.access_statement_compliance_body_2 }}<a href="https://www.w3.org/TR/WCAG21/" class="govuk-link" target="_blank">{{ mssgs.access_statement_compliance_body_link_2 }}</a>{{ mssgs.full_stop }}</p>
62
+ <p class="govuk-body">{{ mssgs.access_statement_compliance_body_1 }}<a href="https://www.w3.org/TR/WCAG22/" class="govuk-link" target="_blank">{{ mssgs.access_statement_compliance_body_link_1 }}</a>{{ mssgs.full_stop }}</p>
63
+ <p class="govuk-body">{{ mssgs.access_statement_compliance_body_2 }}<a href="https://www.w3.org/TR/WCAG22/" class="govuk-link" target="_blank">{{ mssgs.access_statement_compliance_body_link_2 }}</a>{{ mssgs.full_stop }}</p>
64
64
 
65
65
  <h2 class="govuk-heading-m">{{ mssgs.access_statement_nonaccess_heading }}</h2>
66
66
  <p class="govuk-body">{{ mssgs.access_statement_nonaccess_body }}</p>
67
67
 
68
68
  <h2 class="govuk-heading-m">{{ mssgs.access_statement_noncompliance_heading }}</h2>
69
69
  <ul class="govuk-list govuk-list--bullet">
70
- <li> {{ mssgs.access_statement_noncompliance_body_1 }}<a href="https://www.w3.org/WAI/WCAG21/Understanding/keyboard-no-exception.html" class="govuk-link" target="_blank">{{ mssgs.access_statement_noncompliance_body_link_1 }}</a>{{ mssgs.access_statement_noncompliance_body_2 }}</li>
71
- <li> {{ mssgs.access_statement_noncompliance_body_3 }}<a href="https://www.w3.org/WAI/WCAG21/Understanding/contrast-enhanced.html" class="govuk-link" target="_blank">{{ mssgs.access_statement_noncompliance_body_link_2 }}</a>{{ mssgs.access_statement_noncompliance_body_4 }} </li>
72
- <li> {{ mssgs.access_statement_noncompliance_body_5 }}<a href="https://www.w3.org/WAI/WCAG21/Understanding/link-purpose-link-only.html" class="govuk-link" target="_blank">{{ mssgs.access_statement_noncompliance_body_link_3 }}</a>{{ mssgs.access_statement_noncompliance_body_6 }} </li>
70
+ <li> {{ mssgs.access_statement_noncompliance_body_1 }}<a href="https://www.w3.org/WAI/WCAG22/Understanding/keyboard-no-exception.html" class="govuk-link" target="_blank">{{ mssgs.access_statement_noncompliance_body_link_1 }}</a>{{ mssgs.access_statement_noncompliance_body_2 }}</li>
71
+ <li> {{ mssgs.access_statement_noncompliance_body_3 }}<a href="https://www.w3.org/WAI/WCAG22/Understanding/contrast-enhanced.html" class="govuk-link" target="_blank">{{ mssgs.access_statement_noncompliance_body_link_2 }}</a>{{ mssgs.access_statement_noncompliance_body_4 }} </li>
72
+ <li> {{ mssgs.access_statement_noncompliance_body_5 }}<a href="https://www.w3.org/WAI/WCAG22/Understanding/link-purpose-link-only.html" class="govuk-link" target="_blank">{{ mssgs.access_statement_noncompliance_body_link_3 }}</a>{{ mssgs.access_statement_noncompliance_body_6 }} </li>
73
73
  </ul>
74
74
 
75
75
  <h2 class="govuk-heading-m">{{ mssgs.access_statement_burden_title }}</h2>
@@ -0,0 +1,174 @@
1
+ import pageRoute from '../../../../../routes/page-route.js'
2
+ import { CANCEL_RP_IDENTIFY } from '../../../../../uri.js'
3
+ import { addLanguageCodeToUri } from '../../../../../processors/uri-helper.js'
4
+ import { getData, validator } from '../route.js'
5
+ import { dateOfBirthValidator, getDateErrorFlags } from '../../../../../schema/validators/validators.js'
6
+ import { validation } from '@defra-fish/business-rules-lib'
7
+ import GetDataRedirect from '../../../../../handlers/get-data-redirect.js'
8
+
9
+ require('../route.js')
10
+
11
+ jest.mock('../../../../../routes/page-route.js')
12
+ jest.mock('../../../../../uri.js', () => ({
13
+ ...jest.requireActual('../../../../../uri.js'),
14
+ CANCEL_RP_IDENTIFY: { page: 'cancel-rp identify page', uri: Symbol('cancel-rp identify uri') },
15
+ CANCEL_RP_AUTHENTICATE: { uri: 'cancel-rp-authenticate uri' }
16
+ }))
17
+ jest.mock('../../../../../processors/uri-helper.js')
18
+ jest.mock('../../../../../schema/validators/validators.js')
19
+
20
+ describe('cancel recurring payment identify route', () => {
21
+ afterEach(() => {
22
+ jest.restoreAllMocks()
23
+ })
24
+
25
+ describe('getData', () => {
26
+ const getMockRequest = (referenceNumber, pageGet = async () => ({})) => ({
27
+ cache: () => ({
28
+ helpers: {
29
+ status: {
30
+ getCurrentPermission: () => ({
31
+ referenceNumber
32
+ }),
33
+ setCurrentPermission: jest.fn()
34
+ },
35
+ page: {
36
+ getCurrentPermission: pageGet
37
+ }
38
+ }
39
+ }),
40
+ i18n: {
41
+ getCatalog: () => [],
42
+ getLocales: () => []
43
+ }
44
+ })
45
+
46
+ it('passes correct page name when getting page cache', async () => {
47
+ const pageGet = jest.fn(() => ({}))
48
+ await getData(getMockRequest(undefined, pageGet))
49
+ expect(pageGet).toHaveBeenCalledWith(CANCEL_RP_IDENTIFY.page)
50
+ })
51
+
52
+ it.each([['09F6VF'], ['013AH6'], ['LK563F']])('returns referenceNumber when permission includes %s', async referenceNumber => {
53
+ const result = await getData(getMockRequest(referenceNumber))
54
+ expect(result.referenceNumber).toEqual(referenceNumber)
55
+ })
56
+
57
+ it('throws redirect when permission number fails validation', async () => {
58
+ const spy = jest
59
+ .spyOn(validation.permission, 'permissionNumberUniqueComponentValidator')
60
+ .mockReturnValue({ validate: () => ({ error: true }) })
61
+ const request = getMockRequest('BAD123')
62
+ request.cache().helpers.status.setCurrentPermission = jest.fn()
63
+ await expect(getData(request)).rejects.toBeInstanceOf(GetDataRedirect)
64
+ spy.mockRestore()
65
+ })
66
+
67
+ it('adds return value of getErrorFlags to the page data', async () => {
68
+ const errorFlags = { unique: Symbol('error-flags') }
69
+ getDateErrorFlags.mockReturnValueOnce(errorFlags)
70
+ const result = await getData(getMockRequest())
71
+ expect(result).toEqual(expect.objectContaining(errorFlags))
72
+ })
73
+
74
+ it('passes error to getErrorFlags', async () => {
75
+ const error = Symbol('error')
76
+ await getData(getMockRequest(undefined, async () => ({ error })))
77
+ expect(getDateErrorFlags).toHaveBeenCalledWith(error)
78
+ })
79
+
80
+ it.each([
81
+ ['full-date', 'object.missing'],
82
+ ['day', 'any.required']
83
+ ])('adds error details (%s: %s) to the page data', async (errorKey, errorValue) => {
84
+ const pageGet = async () => ({
85
+ error: { [errorKey]: errorValue }
86
+ })
87
+ const result = await getData(getMockRequest(undefined, pageGet))
88
+ expect(result.error).toEqual({ errorKey, errorValue })
89
+ })
90
+
91
+ it('omits error if there is no error', async () => {
92
+ const result = await getData(getMockRequest())
93
+ expect(result.error).toBeUndefined()
94
+ })
95
+ })
96
+
97
+ describe('pageRoute', () => {
98
+ it('passes CANCEL_RP_IDENTIFY.page as the first argument to pageRoute', () => {
99
+ expect(pageRoute.mock.calls[0][0]).toBe(CANCEL_RP_IDENTIFY.page)
100
+ })
101
+
102
+ it('passes CANCEL_RP_IDENTIFY.uri as the second argument to pageRoute', () => {
103
+ expect(pageRoute.mock.calls[0][1]).toBe(CANCEL_RP_IDENTIFY.uri)
104
+ })
105
+
106
+ it('calls pageRoute with validator, nextPage function, and getData', () => {
107
+ expect(pageRoute).toBeCalledWith(CANCEL_RP_IDENTIFY.page, CANCEL_RP_IDENTIFY.uri, validator, expect.any(Function), getData)
108
+ })
109
+ })
110
+
111
+ describe('page route next', () => {
112
+ const getNextPage = () => pageRoute.mock.calls[0][3]
113
+
114
+ it('passes a function', () => {
115
+ const nextPage = getNextPage()
116
+ expect(typeof nextPage).toBe('function')
117
+ })
118
+
119
+ it('calls addLanguageCodeToUri', () => {
120
+ const nextPage = getNextPage()
121
+ nextPage()
122
+ expect(addLanguageCodeToUri).toHaveBeenCalled()
123
+ })
124
+
125
+ it('passes request to addLanguageCodeToUri', () => {
126
+ const request = Symbol('request')
127
+ const nextPage = getNextPage()
128
+ nextPage(request)
129
+ expect(addLanguageCodeToUri).toHaveBeenCalledWith(request, expect.anything())
130
+ })
131
+
132
+ it('returns result of addLanguageCodeToUri', () => {
133
+ const expectedResult = Symbol('add language code to uri')
134
+ const nextPage = getNextPage()
135
+ addLanguageCodeToUri.mockReturnValueOnce(expectedResult)
136
+ expect(nextPage()).toBe(expectedResult)
137
+ })
138
+ })
139
+
140
+ describe('validator', () => {
141
+ const getMockPayload = (postcode = 'AA1 1AA', referenceNumber = 'A1B2C3') => ({
142
+ postcode,
143
+ referenceNumber
144
+ })
145
+
146
+ it('fails if dateOfBirthValidator throws', () => {
147
+ const expectedError = new Error('expected error')
148
+ dateOfBirthValidator.mockImplementationOnce(() => {
149
+ throw expectedError
150
+ })
151
+ expect(() => validator(getMockPayload())).toThrow(expectedError)
152
+ })
153
+
154
+ it('passes if dateOfBirthValidator succeeds', () => {
155
+ expect(() => validator(getMockPayload())).not.toThrow()
156
+ })
157
+
158
+ it('passes payload to dateOfBirthValidator', () => {
159
+ const p = getMockPayload()
160
+ validator(p)
161
+ expect(dateOfBirthValidator).toHaveBeenCalledWith(p)
162
+ })
163
+
164
+ it('fails if permission number is invalid', () => {
165
+ jest.spyOn(validation.permission, 'permissionNumberUniqueComponentValidator').mockReturnValue({ validate: () => ({ error: 'bad' }) })
166
+ expect(() => validator({ referenceNumber: 'BAD', postcode: 'AA1 1AA' })).toThrow()
167
+ })
168
+
169
+ it('fails if postcode is invalid', () => {
170
+ jest.spyOn(validation.contact, 'createOverseasPostcodeValidator').mockReturnValue({ validate: () => ({ error: 'bad' }) })
171
+ expect(() => validator({ referenceNumber: 'ABC123', postcode: 'ZZZ' })).toThrow()
172
+ })
173
+ })
174
+ })
@@ -1,15 +1,175 @@
1
1
  {% extends "layout.njk" %}
2
2
 
3
+ {% from "fieldset/macro.njk" import govukFieldset %}
3
4
  {% from "button/macro.njk" import govukButton %}
4
5
  {% from "page-title.njk" import pageTitle %}
6
+ {% from "date-input/macro.njk" import govukDateInput %}
7
+ {% from "input/macro.njk" import govukInput %}
8
+ {% from "error-summary.njk" import errorSummary %}
9
+
10
+ {% set title = mssgs.cancel_rp_identify_title %}
5
11
 
6
- {% set title = mssgs.rp_cancel_identify_title %}
7
12
  {% block pageTitle %}{{ pageTitle(title, error, mssgs) }}{% endblock %}
8
13
 
14
+ {%
15
+ set errorMap = {
16
+ 'referenceNumber' : {
17
+ 'string.empty': { text: mssgs.cancel_rp_identify_ref_number_error, ref: '#ref' },
18
+ 'string.pattern.base': { text: mssgs.cancel_rp_identify_ref_pattern, ref: '#ref' }
19
+ },
20
+ 'postcode': {
21
+ 'string.empty': { ref: '#postcode', text: mssgs.cancel_rp_identify_postcode_error },
22
+ 'string.pattern.base': { ref: '#postcode', text: mssgs.cancel_rp_identify_postcode_pattern }
23
+ },
24
+ 'full-date': {
25
+ 'object.missing': { ref: '#date-of-birth-day', text: mssgs.dob_error }
26
+ },
27
+ 'day-and-month': {
28
+ 'object.missing': { ref: '#date-of-birth-day', text: mssgs.dob_error_missing_day_and_month }
29
+ },
30
+ 'day-and-year': {
31
+ 'object.missing': { ref: '#date-of-birth-day', text: mssgs.dob_error_missing_day_and_year }
32
+ },
33
+ 'month-and-year': {
34
+ 'object.missing': { ref: '#date-of-birth-month', text: mssgs.dob_error_missing_month_and_year }
35
+ },
36
+ 'day': {
37
+ 'any.required': { ref: '#date-of-birth-day', text: mssgs.dob_error_missing_day }
38
+ },
39
+ 'month': {
40
+ 'any.required': { ref: '#date-of-birth-month', text: mssgs.dob_error_missing_month }
41
+ },
42
+ 'year': {
43
+ 'any.required': { ref: '#date-of-birth-year', text: mssgs.dob_error_missing_year }
44
+ },
45
+ 'non-numeric': {
46
+ 'number.base': { ref: '#date-of-birth-day', text: mssgs.dob_error_non_numeric }
47
+ },
48
+ 'invalid-date': {
49
+ 'any.custom': { ref: '#date-of-birth-day', text: mssgs.dob_error_date_real }
50
+ },
51
+ 'date-range': {
52
+ 'date.min': { ref: '#date-of-birth-day', text: mssgs.dob_error_year_min },
53
+ 'date.max': { ref: '#date-of-birth-day', text: mssgs.dob_error_year_max }
54
+ }
55
+ }
56
+ %}
57
+
58
+ {% set dateInputItems = [
59
+ {
60
+ label: mssgs.date_day,
61
+ name: "day",
62
+ classes: "govuk-input--width-2 govuk-input--error" if data.isDayError else "govuk-input--width-2",
63
+ value: payload['date-of-birth-day'],
64
+ attributes: { maxlength : 2 }
65
+ },
66
+ {
67
+ label: mssgs.date_month,
68
+ name: "month",
69
+ classes: "govuk-input--width-2 govuk-input--error" if data.isMonthError else "govuk-input--width-2",
70
+ value: payload['date-of-birth-month'],
71
+ attributes: { maxlength : 2 }
72
+ },
73
+ {
74
+ label: mssgs.date_year,
75
+ name: "year",
76
+ classes: "govuk-input--width-4 govuk-input--error" if data.isYearError else "govuk-input--width-4",
77
+ value: payload['date-of-birth-year'],
78
+ attributes: { maxlength : 4 }
79
+ }
80
+ ]
81
+ %}
82
+
9
83
  {% block content %}
10
84
  <div class="govuk-grid-row">
11
- <div class="govuk-grid-column-two-thirds">
12
- <form method="post" class="govuk-!-margin-bottom-6">
85
+ <div class="govuk-grid-column-two-thirds">
86
+ {{ errorSummary(error, errorMap, mssgs.there_is_a_problem) }}
87
+ <form method="post" class="govuk-!-margin-bottom-6">
88
+
89
+ {% set legendHtml %}
90
+ <span class="govuk-caption-l">{{ mssgs.cancel_rp_identify_caption }}</span>
91
+ {{ title }}
92
+ {% endset %}
93
+
94
+ {% call govukFieldset({
95
+ describedBy: "ref-hint date-of-birth-hint postcode-hint",
96
+ legend: {
97
+ html: legendHtml,
98
+ classes: "govuk-fieldset__legend--l govuk-!-margin-bottom-3",
99
+ isPageHeading: true
100
+ }
101
+ }) %}
102
+
103
+ <p class="govuk-body-m">{{ mssgs.cancel_rp_identify_body }}</p>
104
+
105
+ {{ govukInput({
106
+ id: "ref",
107
+ name: "referenceNumber",
108
+ type: "text",
109
+ classes: "govuk-input--width-10",
110
+ errorMessage: { text: mssgs.cancel_rp_identify_ref_number_error } if error['referenceNumber'],
111
+ label: {
112
+ text: mssgs.cancel_rp_identify_ref_number,
113
+ classes: "govuk-!-font-weight-bold"
114
+ },
115
+ hint: {
116
+ text: mssgs.cancel_rp_identify_ref_number_hint
117
+ },
118
+ attributes: {
119
+ spellcheck: "false",
120
+ maxlength: "6"
121
+ },
122
+ value: payload.referenceNumber
123
+ }) }}
124
+
125
+ {{ govukDateInput({
126
+ id: "date-of-birth",
127
+ namePrefix: "date-of-birth",
128
+ items: dateInputItems,
129
+ fieldset: {
130
+ legend: {
131
+ text: mssgs.cancel_rp_identify_dob,
132
+ isPageHeading: false,
133
+ classes: "govuk-!-font-weight-bold govuk-label"
134
+ }
135
+ },
136
+ errorMessage: ({
137
+ text: errorMap[data.error.errorKey][data.error.errorValue].text }
138
+ if data.error and data.error.errorKey in [
139
+ 'full-date',
140
+ 'day-and-month',
141
+ 'day-and-year',
142
+ 'month-and-year',
143
+ 'day',
144
+ 'month',
145
+ 'year',
146
+ 'non-numeric',
147
+ 'invalid-date',
148
+ 'date-range'
149
+ ]
150
+ ),
151
+ hint: {
152
+ text: mssgs.cancel_rp_identify_dob_hint
153
+ }
154
+ }) }}
155
+
156
+ {{ govukInput({
157
+ label: {
158
+ text: mssgs.cancel_rp_identify_postcode,
159
+ classes: "govuk-!-font-weight-bold"
160
+ },
161
+ id: "postcode",
162
+ name: "postcode",
163
+ hint: { text: mssgs.cancel_rp_identify_postcode_hint },
164
+ value: payload['postcode'],
165
+ autocomplete: 'postal-code',
166
+ classes: "govuk-input--width-10",
167
+ attributes: { maxlength: 10 },
168
+ errorMessage: { text: mssgs.cancel_rp_identify_postcode_error } if error['postcode']
169
+ }) }}
170
+
171
+ {% endcall %}
172
+
13
173
  {{ govukButton({
14
174
  attributes: { id: 'continue' },
15
175
  preventDoubleClick: true,
@@ -21,4 +181,11 @@
21
181
  </form>
22
182
  </div>
23
183
  </div>
24
- {% endblock %}
184
+ {% endblock %}
185
+
186
+ {% block bodyEnd %}
187
+ <script type="module" nonce="{{ nonce }}">
188
+ import { initAll } from '/public/javascript/govuk-frontend-min.js'
189
+ initAll()
190
+ </script>
191
+ {% endblock %}
@@ -1,13 +1,58 @@
1
- import pageRoute from '../../../../routes/page-route.js'
2
1
  import { CANCEL_RP_AUTHENTICATE, CANCEL_RP_IDENTIFY } from '../../../../uri.js'
2
+ import pageRoute from '../../../../routes/page-route.js'
3
+ import Joi from 'joi'
4
+ import { validation } from '@defra-fish/business-rules-lib'
3
5
  import { addLanguageCodeToUri } from '../../../../processors/uri-helper.js'
6
+ import GetDataRedirect from '../../../../handlers/get-data-redirect.js'
7
+ import { dateOfBirthValidator, getDateErrorFlags } from '../../../../schema/validators/validators.js'
8
+
9
+ export const getData = async request => {
10
+ const permission = await request.cache().helpers.status.getCurrentPermission()
11
+ const page = await request.cache().helpers.page.getCurrentPermission(CANCEL_RP_IDENTIFY.page)
12
+
13
+ if (permission.referenceNumber) {
14
+ const validatePermissionNumber = validation.permission
15
+ .permissionNumberUniqueComponentValidator(Joi)
16
+ .validate(permission.referenceNumber)
17
+ if (validatePermissionNumber.error) {
18
+ await request.cache().helpers.status.setCurrentPermission({ referenceNumber: null })
19
+ throw new GetDataRedirect(addLanguageCodeToUri(request, CANCEL_RP_IDENTIFY.uri))
20
+ }
21
+ }
22
+
23
+ const pageData = {
24
+ referenceNumber: permission.referenceNumber,
25
+ ...getDateErrorFlags(page?.error)
26
+ }
27
+
28
+ if (page?.error) {
29
+ const [errorKey] = Object.keys(page.error)
30
+ const errorValue = page.error[errorKey]
31
+ pageData.error = { errorKey, errorValue }
32
+ }
33
+
34
+ return pageData
35
+ }
36
+
37
+ export const validator = payload => {
38
+ dateOfBirthValidator(payload)
39
+
40
+ Joi.assert(
41
+ {
42
+ postcode: payload.postcode,
43
+ referenceNumber: payload.referenceNumber
44
+ },
45
+ Joi.object({
46
+ referenceNumber: validation.permission.permissionNumberUniqueComponentValidator(Joi),
47
+ postcode: validation.contact.createOverseasPostcodeValidator(Joi)
48
+ }).options({ abortEarly: false })
49
+ )
50
+ }
4
51
 
5
52
  export default pageRoute(
6
53
  CANCEL_RP_IDENTIFY.page,
7
54
  CANCEL_RP_IDENTIFY.uri,
8
- () => {},
9
- request => {
10
- return addLanguageCodeToUri(request, CANCEL_RP_AUTHENTICATE.uri)
11
- },
12
- () => {}
55
+ validator,
56
+ request => addLanguageCodeToUri(request, CANCEL_RP_AUTHENTICATE.uri),
57
+ getData
13
58
  )
@@ -1,106 +0,0 @@
1
- import pageRoute from '../../../../../routes/page-route.js'
2
- import { CANCEL_RP_AUTHENTICATE, CANCEL_RP_IDENTIFY } from '../../../../../uri.js'
3
- import { addLanguageCodeToUri } from '../../../../../processors/uri-helper.js'
4
-
5
- require('../route.js')
6
- // eslint-disable-next-line no-unused-vars
7
- const [[_v, _p, validator, completion, getData]] = pageRoute.mock.calls
8
-
9
- jest.mock('../../../../../routes/page-route.js')
10
- jest.mock('../../../../../uri.js', () => ({
11
- ...jest.requireActual('../../../../../uri.js'),
12
- CANCEL_RP_IDENTIFY: { page: Symbol('cancel-rp-identify'), uri: Symbol('cancel-rp-identify-uri') },
13
- CANCEL_RP_AUTHENTICATE: { uri: Symbol('cancel-rp-authenticate-uri') }
14
- }))
15
- jest.mock('../../../../../processors/uri-helper.js')
16
-
17
- describe('pageRoute receives expected arguments', () => {
18
- it('passes CANCEL_RP_IDENTIFY.page as the view name', () => {
19
- jest.isolateModules(() => {
20
- require('../route.js')
21
- expect(pageRoute).toHaveBeenCalledWith(
22
- CANCEL_RP_IDENTIFY.page,
23
- expect.anything(),
24
- expect.anything(),
25
- expect.anything(),
26
- expect.anything()
27
- )
28
- })
29
- })
30
-
31
- it('passes CANCEL_RP_IDENTIFY.uri as the path', () => {
32
- jest.isolateModules(() => {
33
- require('../route.js')
34
- expect(pageRoute).toHaveBeenCalledWith(
35
- expect.anything(),
36
- CANCEL_RP_IDENTIFY.uri,
37
- expect.anything(),
38
- expect.anything(),
39
- expect.anything()
40
- )
41
- })
42
- })
43
-
44
- it('passes a function as the validator', () => {
45
- jest.isolateModules(() => {
46
- require('../route.js')
47
- expect(pageRoute).toHaveBeenCalledWith(
48
- expect.anything(),
49
- expect.anything(),
50
- expect.any(Function),
51
- expect.anything(),
52
- expect.anything()
53
- )
54
- })
55
- })
56
-
57
- it('passes a function to generate redirect location on completion', () => {
58
- jest.isolateModules(() => {
59
- require('../route.js')
60
- expect(pageRoute).toHaveBeenCalledWith(
61
- expect.anything(),
62
- expect.anything(),
63
- expect.anything(),
64
- expect.any(Function),
65
- expect.anything()
66
- )
67
- })
68
- })
69
-
70
- it('passes a function to get the page data', () => {
71
- jest.isolateModules(() => {
72
- require('../route.js')
73
- expect(pageRoute).toHaveBeenCalledWith(
74
- expect.anything(),
75
- expect.anything(),
76
- expect.anything(),
77
- expect.anything(),
78
- expect.any(Function)
79
- )
80
- })
81
- })
82
- })
83
-
84
- describe('completion function', () => {
85
- beforeEach(jest.clearAllMocks)
86
-
87
- it('calls addLanguageCodeToUri with request', () => {
88
- const sampleRequest = Symbol('sample request')
89
- completion(sampleRequest)
90
- expect(addLanguageCodeToUri).toHaveBeenCalledWith(sampleRequest, expect.anything())
91
- })
92
-
93
- it('calls addLanguageCodeToUri with CANCEL_RP_AUTHENTICATE uri', () => {
94
- completion({})
95
- expect(addLanguageCodeToUri).toHaveBeenCalledWith(expect.anything(), CANCEL_RP_AUTHENTICATE.uri)
96
- })
97
-
98
- it('returns the value of addLanguageCodeToUri', () => {
99
- const expectedCompletionRedirect = Symbol('expected-completion-redirect')
100
- addLanguageCodeToUri.mockReturnValueOnce(expectedCompletionRedirect)
101
-
102
- const completionRedirect = completion({})
103
-
104
- expect(completionRedirect).toBe(expectedCompletionRedirect)
105
- })
106
- })