@defra-fish/gafl-webapp-service 1.57.0-rc.1 → 1.57.0-rc.10
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 +4 -4
- package/src/locales/cy.json +27 -8
- package/src/locales/en.json +29 -8
- package/src/pages/concessions/date-of-birth/__tests__/route.spec.js +75 -20
- package/src/pages/concessions/date-of-birth/date-of-birth.njk +35 -9
- package/src/pages/concessions/date-of-birth/route.js +10 -14
- package/src/pages/licence-details/licence-to-start/__tests__/route.spec.js +65 -2
- package/src/pages/licence-details/licence-to-start/licence-to-start.njk +38 -12
- package/src/pages/licence-details/licence-to-start/route.js +14 -32
- package/src/pages/renewals/identify/__tests__/identity.spec.js +3 -0
- package/src/pages/renewals/identify/__tests__/route.spec.js +126 -0
- package/src/pages/renewals/identify/identify.njk +34 -8
- package/src/pages/renewals/identify/route.js +10 -10
- package/src/pages/summary/licence-summary/__tests__/__snapshots__/route.spec.js.snap +0 -12
- package/src/pages/summary/licence-summary/__tests__/route.spec.js +26 -12
- package/src/pages/summary/licence-summary/route.js +2 -1
- package/src/schema/__tests__/__snapshots__/date.schema.test.js.snap +32 -0
- package/src/schema/__tests__/date.schema.test.js +64 -0
- package/src/schema/date.schema.js +63 -0
- package/src/schema/validators/__tests__/validators.spec.js +208 -0
- package/src/schema/validators/validators.js +65 -0
- package/src/services/payment/__test__/govuk-pay-service.spec.js +8 -8
- package/src/services/payment/govuk-pay-service.js +3 -3
- package/src/pages/renewals/identify/__tests__/identity.next-page.spec.js +0 -36
- package/src/pages/renewals/identify/__tests__/route-spec.js +0 -38
|
@@ -1,49 +1,31 @@
|
|
|
1
|
-
import Joi from 'joi'
|
|
2
1
|
import moment from 'moment-timezone'
|
|
3
|
-
|
|
4
|
-
import JoiDate from '@hapi/joi-date'
|
|
5
2
|
import { START_AFTER_PAYMENT_MINUTES, ADVANCED_PURCHASE_MAX_DAYS, SERVICE_LOCAL_TIME } from '@defra-fish/business-rules-lib'
|
|
6
3
|
import { LICENCE_TO_START } from '../../../uri.js'
|
|
7
4
|
import pageRoute from '../../../routes/page-route.js'
|
|
8
|
-
import { dateFormats } from '../../../constants.js'
|
|
9
5
|
import { nextPage } from '../../../routes/next-page.js'
|
|
10
|
-
|
|
11
|
-
const JoiX = Joi.extend(JoiDate)
|
|
12
|
-
|
|
13
|
-
const validator = payload => {
|
|
14
|
-
const licenceStartDate = `${payload['licence-start-date-year']}-${payload['licence-start-date-month']}-${payload['licence-start-date-day']}`
|
|
15
|
-
Joi.assert(
|
|
16
|
-
{
|
|
17
|
-
'licence-start-date': licenceStartDate,
|
|
18
|
-
'licence-to-start': payload['licence-to-start']
|
|
19
|
-
},
|
|
20
|
-
Joi.object({
|
|
21
|
-
'licence-to-start': Joi.string().valid('after-payment', 'another-date').required(),
|
|
22
|
-
'licence-start-date': Joi.alternatives().conditional('licence-to-start', {
|
|
23
|
-
is: 'another-date',
|
|
24
|
-
then: JoiX.date()
|
|
25
|
-
.format(dateFormats)
|
|
26
|
-
.min(moment().tz(SERVICE_LOCAL_TIME).startOf('day'))
|
|
27
|
-
.max(moment().tz(SERVICE_LOCAL_TIME).add(ADVANCED_PURCHASE_MAX_DAYS, 'days'))
|
|
28
|
-
.required(),
|
|
29
|
-
otherwise: Joi.string().empty('')
|
|
30
|
-
})
|
|
31
|
-
}).options({ abortEarly: false, allowUnknown: true })
|
|
32
|
-
)
|
|
33
|
-
}
|
|
6
|
+
import { getDateErrorFlags, startDateValidator } from '../../../schema/validators/validators.js'
|
|
34
7
|
|
|
35
8
|
export const getData = async request => {
|
|
36
9
|
const fmt = 'DD MM YYYY'
|
|
37
10
|
const { isLicenceForYou } = await request.cache().helpers.transaction.getCurrentPermission()
|
|
38
|
-
|
|
39
|
-
|
|
11
|
+
const page = await request.cache().helpers.page.getCurrentPermission(LICENCE_TO_START.page)
|
|
12
|
+
const pageData = {
|
|
40
13
|
isLicenceForYou,
|
|
41
14
|
exampleStartDate: moment().tz(SERVICE_LOCAL_TIME).add(1, 'days').format(fmt),
|
|
42
15
|
minStartDate: moment().tz(SERVICE_LOCAL_TIME).format(fmt),
|
|
43
16
|
maxStartDate: moment().tz(SERVICE_LOCAL_TIME).add(ADVANCED_PURCHASE_MAX_DAYS, 'days').format(fmt),
|
|
44
17
|
advancedPurchaseMaxDays: ADVANCED_PURCHASE_MAX_DAYS,
|
|
45
|
-
startAfterPaymentMinutes: START_AFTER_PAYMENT_MINUTES
|
|
18
|
+
startAfterPaymentMinutes: START_AFTER_PAYMENT_MINUTES,
|
|
19
|
+
...getDateErrorFlags(page?.error)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (page?.error) {
|
|
23
|
+
const [errorKey] = Object.keys(page.error)
|
|
24
|
+
const errorValue = page.error[errorKey]
|
|
25
|
+
pageData.error = { errorKey, errorValue }
|
|
46
26
|
}
|
|
27
|
+
|
|
28
|
+
return pageData
|
|
47
29
|
}
|
|
48
30
|
|
|
49
|
-
export default pageRoute(LICENCE_TO_START.page, LICENCE_TO_START.uri,
|
|
31
|
+
export default pageRoute(LICENCE_TO_START.page, LICENCE_TO_START.uri, startDateValidator, nextPage, getData)
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import pageRoute from '../../../../routes/page-route.js'
|
|
2
|
+
import { addLanguageCodeToUri } from '../../../../processors/uri-helper.js'
|
|
3
|
+
import { getData, validator } from '../route.js'
|
|
4
|
+
import { IDENTIFY, NEW_TRANSACTION } from '../../../../uri.js'
|
|
5
|
+
import { dateOfBirthValidator, getDateErrorFlags } from '../../../../schema/validators/validators.js'
|
|
6
|
+
|
|
7
|
+
jest.mock('../../../../routes/page-route.js', () => jest.fn())
|
|
8
|
+
jest.mock('../../../../uri.js', () => ({
|
|
9
|
+
IDENTIFY: { page: 'identify page', uri: 'identify uri' },
|
|
10
|
+
AUTHENTICATE: { uri: Symbol('authenticate uri') },
|
|
11
|
+
NEW_TRANSACTION: { uri: Symbol('new transaction uri') }
|
|
12
|
+
}))
|
|
13
|
+
jest.mock('../../../../processors/uri-helper.js')
|
|
14
|
+
jest.mock('../../../../schema/validators/validators.js')
|
|
15
|
+
|
|
16
|
+
describe('getData', () => {
|
|
17
|
+
const getMockRequest = (referenceNumber, pageGet = async () => ({})) => ({
|
|
18
|
+
cache: () => ({
|
|
19
|
+
helpers: {
|
|
20
|
+
status: {
|
|
21
|
+
getCurrentPermission: () => ({
|
|
22
|
+
referenceNumber: referenceNumber
|
|
23
|
+
})
|
|
24
|
+
},
|
|
25
|
+
page: {
|
|
26
|
+
getCurrentPermission: pageGet
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
})
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
it('addLanguageCodeToUri is called with the expected arguments', async () => {
|
|
33
|
+
const request = getMockRequest('013AH6')
|
|
34
|
+
await getData(request)
|
|
35
|
+
expect(addLanguageCodeToUri).toHaveBeenCalledWith(request, NEW_TRANSACTION.uri)
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
it('getData returns correct URI', async () => {
|
|
39
|
+
const expectedUri = Symbol('decorated uri')
|
|
40
|
+
addLanguageCodeToUri.mockReturnValueOnce(expectedUri)
|
|
41
|
+
|
|
42
|
+
const result = await getData(getMockRequest('013AH6'))
|
|
43
|
+
expect(result.uri.new).toEqual(expectedUri)
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
it.each([['09F6VF'], ['013AH6'], ['LK563F']])('getData returns referenceNumber', async referenceNumber => {
|
|
47
|
+
const result = await getData(getMockRequest(referenceNumber))
|
|
48
|
+
expect(result.referenceNumber).toEqual(referenceNumber)
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
it('adds return value of getErrorFlags to the page data', async () => {
|
|
52
|
+
const errorFlags = { unique: Symbol('error-flags') }
|
|
53
|
+
getDateErrorFlags.mockReturnValueOnce(errorFlags)
|
|
54
|
+
const result = await getData(getMockRequest())
|
|
55
|
+
expect(result).toEqual(expect.objectContaining(errorFlags))
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
it('passes error to getErrorFlags', async () => {
|
|
59
|
+
const error = Symbol('error')
|
|
60
|
+
await getData(getMockRequest(undefined, async () => ({ error })))
|
|
61
|
+
expect(getDateErrorFlags).toHaveBeenCalledWith(error)
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
it('passes correct page name when getting page cache', async () => {
|
|
65
|
+
const pageGet = jest.fn(() => ({}))
|
|
66
|
+
await getData(getMockRequest(undefined, pageGet))
|
|
67
|
+
expect(pageGet).toHaveBeenCalledWith(IDENTIFY.page)
|
|
68
|
+
})
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
describe('default', () => {
|
|
72
|
+
it('should call the pageRoute with date-of-birth, /buy/date-of-birth, dateOfBirthValidator and nextPage', async () => {
|
|
73
|
+
expect(pageRoute).toBeCalledWith(IDENTIFY.page, IDENTIFY.uri, validator, expect.any(Function), getData)
|
|
74
|
+
})
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
describe('page route next', () => {
|
|
78
|
+
const nextPage = pageRoute.mock.calls[0][3]
|
|
79
|
+
beforeEach(jest.clearAllMocks)
|
|
80
|
+
|
|
81
|
+
it('passes a function as the nextPage argument', () => {
|
|
82
|
+
expect(typeof nextPage).toBe('function')
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
it('calls addLanguageCodeToUri', () => {
|
|
86
|
+
nextPage()
|
|
87
|
+
expect(addLanguageCodeToUri).toHaveBeenCalled()
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
it('passes request to addLanguageCodeToUri', () => {
|
|
91
|
+
const request = Symbol('request')
|
|
92
|
+
nextPage(request)
|
|
93
|
+
expect(addLanguageCodeToUri).toHaveBeenCalledWith(request, expect.anything())
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
it('next page returns result of addLanguageCodeToUri', () => {
|
|
97
|
+
const expectedResult = Symbol('add language code to uri')
|
|
98
|
+
addLanguageCodeToUri.mockReturnValueOnce(expectedResult)
|
|
99
|
+
expect(nextPage()).toBe(expectedResult)
|
|
100
|
+
})
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
describe('validator', () => {
|
|
104
|
+
const getMockRequest = (postcode = 'AA1 1AA', referenceNumber = 'A1B2C3') => ({
|
|
105
|
+
postcode,
|
|
106
|
+
referenceNumber
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
it('fails if dateOfBirth validator fails', () => {
|
|
110
|
+
const expectedError = new Error('expected error')
|
|
111
|
+
dateOfBirthValidator.mockImplementationOnce(() => {
|
|
112
|
+
throw expectedError
|
|
113
|
+
})
|
|
114
|
+
expect(() => validator(getMockRequest)).toThrow(expectedError)
|
|
115
|
+
})
|
|
116
|
+
|
|
117
|
+
it('passes if dateOfBirth validator passes', () => {
|
|
118
|
+
expect(() => validator(getMockRequest())).not.toThrow()
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
it('passes payload to dateOfBirth validator', () => {
|
|
122
|
+
const payload = getMockRequest()
|
|
123
|
+
validator(payload)
|
|
124
|
+
expect(dateOfBirthValidator).toHaveBeenCalledWith(payload)
|
|
125
|
+
})
|
|
126
|
+
})
|
|
@@ -21,14 +21,40 @@
|
|
|
21
21
|
ref: "#ref"
|
|
22
22
|
}
|
|
23
23
|
},
|
|
24
|
-
'date-of-birth': {
|
|
25
|
-
'date.format': { ref: '#date-of-birth-day', text: mssgs.identify_error_enter_bday },
|
|
26
|
-
'date.max': { ref: '#date-of-birth-day', text: mssgs.identify_error_enter_bday_max },
|
|
27
|
-
'date.min': { ref: '#date-of-birth-day', text: mssgs.identify_error_enter_bday_min }
|
|
28
|
-
},
|
|
29
24
|
'postcode': {
|
|
30
25
|
'string.empty': { ref: '#postcode', text: mssgs.identify_error_empty_postcode },
|
|
31
26
|
'string.pattern.base': { ref: '#postcode', text: mssgs.identify_error_pattern_postcode }
|
|
27
|
+
},
|
|
28
|
+
'full-date': {
|
|
29
|
+
'object.missing': { ref: '#date-of-birth-day', text: mssgs.dob_error }
|
|
30
|
+
},
|
|
31
|
+
'day-and-month': {
|
|
32
|
+
'object.missing': { ref: '#date-of-birth-day', text: mssgs.dob_error_missing_day_and_month }
|
|
33
|
+
},
|
|
34
|
+
'day-and-year': {
|
|
35
|
+
'object.missing': { ref: '#date-of-birth-day', text: mssgs.dob_error_missing_day_and_year }
|
|
36
|
+
},
|
|
37
|
+
'month-and-year': {
|
|
38
|
+
'object.missing': { ref: '#date-of-birth-month', text: mssgs.dob_error_missing_month_and_year }
|
|
39
|
+
},
|
|
40
|
+
'day': {
|
|
41
|
+
'any.required': { ref: '#date-of-birth-day', text: mssgs.dob_error_missing_day }
|
|
42
|
+
},
|
|
43
|
+
'month': {
|
|
44
|
+
'any.required': { ref: '#date-of-birth-month', text: mssgs.dob_error_missing_month }
|
|
45
|
+
},
|
|
46
|
+
'year': {
|
|
47
|
+
'any.required': { ref: '#date-of-birth-year', text: mssgs.dob_error_missing_year }
|
|
48
|
+
},
|
|
49
|
+
'non-numeric': {
|
|
50
|
+
'number.base': { ref: '#date-of-birth-day', text: mssgs.dob_error_non_numeric }
|
|
51
|
+
},
|
|
52
|
+
'invalid-date': {
|
|
53
|
+
'any.custom': { ref: '#date-of-birth-day', text: mssgs.dob_error_date_real }
|
|
54
|
+
},
|
|
55
|
+
'date-range': {
|
|
56
|
+
'date.min': { ref: '#date-of-birth-day', text: mssgs.dob_error_year_min },
|
|
57
|
+
'date.max': { ref: '#date-of-birth-day', text: mssgs.dob_error_year_max }
|
|
32
58
|
}
|
|
33
59
|
}
|
|
34
60
|
%}
|
|
@@ -37,21 +63,21 @@
|
|
|
37
63
|
{
|
|
38
64
|
label: mssgs.dob_day,
|
|
39
65
|
name: "day",
|
|
40
|
-
classes: "govuk-input--width-2",
|
|
66
|
+
classes: "govuk-input--width-2 govuk-input--error" if data.isDayError else "govuk-input--width-2",
|
|
41
67
|
value: payload['date-of-birth-day'],
|
|
42
68
|
attributes: { maxlength : 2 }
|
|
43
69
|
},
|
|
44
70
|
{
|
|
45
71
|
label: mssgs.dob_month,
|
|
46
72
|
name: "month",
|
|
47
|
-
classes: "govuk-input--width-2",
|
|
73
|
+
classes: "govuk-input--width-2 govuk-input--error" if data.isMonthError else "govuk-input--width-2",
|
|
48
74
|
value: payload['date-of-birth-month'],
|
|
49
75
|
attributes: { maxlength : 2 }
|
|
50
76
|
},
|
|
51
77
|
{
|
|
52
78
|
label: mssgs.dob_year,
|
|
53
79
|
name: "year",
|
|
54
|
-
classes: "govuk-input--width-4",
|
|
80
|
+
classes: "govuk-input--width-4 govuk-input--error" if data.isYearError else "govuk-input--width-4",
|
|
55
81
|
value: payload['date-of-birth-year'],
|
|
56
82
|
attributes: { maxlength : 4 }
|
|
57
83
|
}
|
|
@@ -4,10 +4,12 @@ import Joi from 'joi'
|
|
|
4
4
|
import { validation } from '@defra-fish/business-rules-lib'
|
|
5
5
|
import { addLanguageCodeToUri } from '../../../processors/uri-helper.js'
|
|
6
6
|
import GetDataRedirect from '../../../handlers/get-data-redirect.js'
|
|
7
|
+
import { dateOfBirthValidator, getDateErrorFlags } from '../../../schema/validators/validators.js'
|
|
7
8
|
|
|
8
9
|
export const getData = async request => {
|
|
9
10
|
// If we are supplied a permission number, validate it or throw 400
|
|
10
11
|
const permission = await request.cache().helpers.status.getCurrentPermission()
|
|
12
|
+
const page = await request.cache().helpers.page.getCurrentPermission(IDENTIFY.page)
|
|
11
13
|
|
|
12
14
|
if (permission.referenceNumber) {
|
|
13
15
|
const validatePermissionNumber = validation.permission
|
|
@@ -23,25 +25,23 @@ export const getData = async request => {
|
|
|
23
25
|
referenceNumber: permission.referenceNumber,
|
|
24
26
|
uri: {
|
|
25
27
|
new: addLanguageCodeToUri(request, NEW_TRANSACTION.uri)
|
|
26
|
-
}
|
|
28
|
+
},
|
|
29
|
+
...getDateErrorFlags(page?.error)
|
|
27
30
|
}
|
|
28
31
|
}
|
|
29
32
|
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
'date-of-birth': validation.contact.createBirthDateValidator(Joi),
|
|
33
|
-
postcode: validation.contact.createOverseasPostcodeValidator(Joi)
|
|
34
|
-
}).options({ abortEarly: false, allowUnknown: true })
|
|
33
|
+
export const validator = payload => {
|
|
34
|
+
dateOfBirthValidator(payload)
|
|
35
35
|
|
|
36
|
-
const validator = async payload => {
|
|
37
|
-
const dateOfBirth = `${payload['date-of-birth-year']}-${payload['date-of-birth-month']}-${payload['date-of-birth-day']}`
|
|
38
36
|
Joi.assert(
|
|
39
37
|
{
|
|
40
|
-
'date-of-birth': dateOfBirth,
|
|
41
38
|
postcode: payload.postcode,
|
|
42
39
|
referenceNumber: payload.referenceNumber
|
|
43
40
|
},
|
|
44
|
-
|
|
41
|
+
Joi.object({
|
|
42
|
+
referenceNumber: validation.permission.permissionNumberUniqueComponentValidator(Joi),
|
|
43
|
+
postcode: validation.contact.createOverseasPostcodeValidator(Joi)
|
|
44
|
+
}).options({ abortEarly: false })
|
|
45
45
|
)
|
|
46
46
|
}
|
|
47
47
|
|
|
@@ -1128,18 +1128,6 @@ Array [
|
|
|
1128
1128
|
},
|
|
1129
1129
|
},
|
|
1130
1130
|
Object {
|
|
1131
|
-
"actions": Object {
|
|
1132
|
-
"items": Array [
|
|
1133
|
-
Object {
|
|
1134
|
-
"attributes": Object {
|
|
1135
|
-
"id": "change-licence-length",
|
|
1136
|
-
},
|
|
1137
|
-
"href": "/buy/licence-length",
|
|
1138
|
-
"text": "contact_summary_change",
|
|
1139
|
-
"visuallyHiddenText": "licence_summary_length",
|
|
1140
|
-
},
|
|
1141
|
-
],
|
|
1142
|
-
},
|
|
1143
1131
|
"key": Object {
|
|
1144
1132
|
"text": "licence_summary_length",
|
|
1145
1133
|
},
|
|
@@ -7,7 +7,11 @@ import { licenceTypeDisplay } from '../../../../processors/licence-type-display.
|
|
|
7
7
|
import { addLanguageCodeToUri } from '../../../../processors/uri-helper.js'
|
|
8
8
|
import mappingConstants from '../../../../processors/mapping-constants.js'
|
|
9
9
|
import { displayPermissionPrice } from '../../../../processors/price-display.js'
|
|
10
|
+
import { hasJunior } from '../../../../processors/concession-helper.js'
|
|
10
11
|
|
|
12
|
+
jest.mock('../../../../processors/concession-helper.js', () => ({
|
|
13
|
+
hasJunior: jest.fn(() => false)
|
|
14
|
+
}))
|
|
11
15
|
jest.mock('../../../../processors/licence-type-display.js', () => ({
|
|
12
16
|
licenceTypeDisplay: jest.fn(() => 'Special Canal Licence, Shopping Trollies and Old Wellies')
|
|
13
17
|
}))
|
|
@@ -370,20 +374,30 @@ describe('licence-summary > route', () => {
|
|
|
370
374
|
)
|
|
371
375
|
})
|
|
372
376
|
|
|
377
|
+
it('calls hasJunior with permission', async () => {
|
|
378
|
+
const currentPermission = getMockNewPermission()
|
|
379
|
+
const mockRequest = getMockRequest({ currentPermission })
|
|
380
|
+
|
|
381
|
+
await getData(mockRequest)
|
|
382
|
+
|
|
383
|
+
expect(hasJunior).toHaveBeenCalledWith(currentPermission)
|
|
384
|
+
})
|
|
385
|
+
|
|
373
386
|
describe('licence summary rows', () => {
|
|
374
387
|
it.each`
|
|
375
|
-
desc | currentPermission
|
|
376
|
-
${'1 year renewal'} | ${getMockPermission()}
|
|
377
|
-
${'1 year new licence'} | ${getMockNewPermission()}
|
|
378
|
-
${'1 year senior renewal'} | ${getMockSeniorPermission()}
|
|
379
|
-
${'8 day licence'} | ${{ ...getMockNewPermission(), licenceLength: '8D' }}
|
|
380
|
-
${'1 day licence'} | ${{ ...getMockNewPermission(), licenceLength: '1D' }}
|
|
381
|
-
${'Junior licence'} | ${getMockJuniorPermission()}
|
|
382
|
-
${'Blue badge concession'} | ${getMockBlueBadgePermission()}
|
|
383
|
-
${'Continuing permission'} | ${getMockContinuingPermission()}
|
|
384
|
-
${'Another date permission'} | ${{ ...getMockPermission(), licenceToStart: 'another-date' }}
|
|
385
|
-
${'1 year new three rod licence '} | ${{ ...getMockNewPermission(), numberOfRods: '3' }}
|
|
386
|
-
`('creates licence summary name rows for $desc', async ({ currentPermission }) => {
|
|
388
|
+
desc | currentPermission | junior
|
|
389
|
+
${'1 year renewal'} | ${getMockPermission()} | ${false}
|
|
390
|
+
${'1 year new licence'} | ${getMockNewPermission()} | ${false}
|
|
391
|
+
${'1 year senior renewal'} | ${getMockSeniorPermission()} | ${false}
|
|
392
|
+
${'8 day licence'} | ${{ ...getMockNewPermission(), licenceLength: '8D' }} | ${false}
|
|
393
|
+
${'1 day licence'} | ${{ ...getMockNewPermission(), licenceLength: '1D' }} | ${false}
|
|
394
|
+
${'Junior licence'} | ${getMockJuniorPermission()} | ${true}
|
|
395
|
+
${'Blue badge concession'} | ${getMockBlueBadgePermission()} | ${false}
|
|
396
|
+
${'Continuing permission'} | ${getMockContinuingPermission()} | ${false}
|
|
397
|
+
${'Another date permission'} | ${{ ...getMockPermission(), licenceToStart: 'another-date' }} | ${false}
|
|
398
|
+
${'1 year new three rod licence '} | ${{ ...getMockNewPermission(), numberOfRods: '3' }} | ${false}
|
|
399
|
+
`('creates licence summary name rows for $desc', async ({ currentPermission, junior }) => {
|
|
400
|
+
hasJunior.mockReturnValueOnce(junior)
|
|
387
401
|
const mockRequest = getMockRequest({ currentPermission })
|
|
388
402
|
const data = await getData(mockRequest)
|
|
389
403
|
expect(data.licenceSummaryRows).toMatchSnapshot()
|
|
@@ -22,6 +22,7 @@ import { CONCESSION, CONCESSION_PROOF } from '../../../processors/mapping-consta
|
|
|
22
22
|
import { nextPage } from '../../../routes/next-page.js'
|
|
23
23
|
import { addLanguageCodeToUri } from '../../../processors/uri-helper.js'
|
|
24
24
|
import { displayPermissionPrice } from '../../../processors/price-display.js'
|
|
25
|
+
import { hasJunior } from '../../../processors/concession-helper.js'
|
|
25
26
|
import db from 'debug'
|
|
26
27
|
const debug = db('webapp:licence-summary')
|
|
27
28
|
|
|
@@ -115,7 +116,7 @@ class RowGenerator {
|
|
|
115
116
|
generateLicenceLengthRow () {
|
|
116
117
|
const args = ['licence_summary_length', this.labels[`licence_type_${this.permission.licenceLength.toLowerCase()}`]]
|
|
117
118
|
|
|
118
|
-
if (this.permission.numberOfRods !== '3') {
|
|
119
|
+
if (this.permission.numberOfRods !== '3' && !hasJunior(this.permission)) {
|
|
119
120
|
args.push(LICENCE_LENGTH.uri, 'change-licence-length')
|
|
120
121
|
}
|
|
121
122
|
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
2
|
+
|
|
3
|
+
exports[`dateSchemaInput matches expected format 1`] = `
|
|
4
|
+
Object {
|
|
5
|
+
"day": "1",
|
|
6
|
+
"day-and-month": Object {
|
|
7
|
+
"day": "1",
|
|
8
|
+
"month": "2",
|
|
9
|
+
},
|
|
10
|
+
"day-and-year": Object {
|
|
11
|
+
"day": "1",
|
|
12
|
+
"year": "2023",
|
|
13
|
+
},
|
|
14
|
+
"full-date": Object {
|
|
15
|
+
"day": "1",
|
|
16
|
+
"month": "2",
|
|
17
|
+
"year": "2023",
|
|
18
|
+
},
|
|
19
|
+
"invalid-date": "2023-02-01",
|
|
20
|
+
"month": "2",
|
|
21
|
+
"month-and-year": Object {
|
|
22
|
+
"month": "2",
|
|
23
|
+
"year": "2023",
|
|
24
|
+
},
|
|
25
|
+
"non-numeric": Object {
|
|
26
|
+
"day": "1",
|
|
27
|
+
"month": "2",
|
|
28
|
+
"year": "2023",
|
|
29
|
+
},
|
|
30
|
+
"year": "2023",
|
|
31
|
+
}
|
|
32
|
+
`;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import Joi from 'joi'
|
|
2
|
+
import { dateSchemaInput, dateSchema } from '../date.schema.js'
|
|
3
|
+
|
|
4
|
+
describe('dateSchemaInput', () => {
|
|
5
|
+
it('matches expected format', () => {
|
|
6
|
+
expect(dateSchemaInput('1', '2', '2023')).toMatchSnapshot()
|
|
7
|
+
})
|
|
8
|
+
|
|
9
|
+
it.each`
|
|
10
|
+
desc | day | month | year | result
|
|
11
|
+
${'all empty'} | ${''} | ${''} | ${''} | ${{ 'full-date': { day: undefined, month: undefined, year: undefined } }}
|
|
12
|
+
${'day and month empty'} | ${''} | ${''} | ${'2020'} | ${{ 'day-and-month': { day: undefined, month: undefined } }}
|
|
13
|
+
${'day and year empty'} | ${''} | ${'11'} | ${''} | ${{ 'day-and-year': { day: undefined, year: undefined } }}
|
|
14
|
+
${'month and year empty'} | ${'12'} | ${''} | ${''} | ${{ 'month-and-year': { month: undefined, year: undefined } }}
|
|
15
|
+
${'day empty'} | ${''} | ${'3'} | ${'2021'} | ${{ day: undefined }}
|
|
16
|
+
${'month empty'} | ${'4'} | ${''} | ${'2003'} | ${{ month: undefined }}
|
|
17
|
+
${'year empty'} | ${'15'} | ${'11'} | ${''} | ${{ year: undefined }}
|
|
18
|
+
`('maps empty strings to undefined values when $desc', ({ day, month, year, result }) => {
|
|
19
|
+
expect(dateSchemaInput(day, month, year)).toEqual(expect.objectContaining(result))
|
|
20
|
+
})
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
describe('dateSchema', () => {
|
|
24
|
+
it.each`
|
|
25
|
+
payload | expectedError | payloadDesc
|
|
26
|
+
${{}} | ${'full-date'} | ${'empty day, month and year'}
|
|
27
|
+
${{ year: '1' }} | ${'day-and-month'} | ${'empty day and month'}
|
|
28
|
+
${{ month: '2' }} | ${'day-and-year'} | ${'empty day and year'}
|
|
29
|
+
${{ day: '3' }} | ${'month-and-year'} | ${'empty month and year'}
|
|
30
|
+
${{ month: '5', year: '2023' }} | ${'day'} | ${'empty day'}
|
|
31
|
+
${{ day: '12', year: '2024' }} | ${'month'} | ${'empty month'}
|
|
32
|
+
${{ day: '15', month: '3' }} | ${'year'} | ${'empty year'}
|
|
33
|
+
${{ day: 'Ides', month: 'March', year: '44 B.C.' }} | ${'non-numeric.day'} | ${'non-numerics entered'}
|
|
34
|
+
${{ day: 'Thirteenth', month: '11', year: '1978' }} | ${'non-numeric.day'} | ${'non-numeric day'}
|
|
35
|
+
${{ day: '29', month: 'MAR', year: '2002' }} | ${'non-numeric.month'} | ${'non-numeric month '}
|
|
36
|
+
${{ day: '13', month: '1', year: 'Two thousand and five' }} | ${'non-numeric.year'} | ${'non-numeric year'}
|
|
37
|
+
${{ day: '30', month: '2', year: '1994' }} | ${'invalid-date'} | ${'an invalid date - 1994-02-40'}
|
|
38
|
+
${{ day: '1', month: '13', year: '2022' }} | ${'invalid-date'} | ${'an invalid date - 2022-13-01'}
|
|
39
|
+
${{ day: '29', month: '2', year: '2023' }} | ${'invalid-date'} | ${'an invalid date - 1994-02-40'}
|
|
40
|
+
${{ day: '-1.15', month: '18', year: '22.2222' }} | ${'invalid-date'} | ${'an invalid date - 22.2222-18-1.15'}
|
|
41
|
+
`('Error has $expectedError in details when payload has $payloadDesc', ({ payload: { day, month, year }, expectedError }) => {
|
|
42
|
+
expect(() => {
|
|
43
|
+
Joi.assert(dateSchemaInput(day, month, year), dateSchema)
|
|
44
|
+
}).toThrow(
|
|
45
|
+
expect.objectContaining({
|
|
46
|
+
details: expect.arrayContaining([
|
|
47
|
+
expect.objectContaining({
|
|
48
|
+
path: expectedError.split('.'),
|
|
49
|
+
context: expect.objectContaining({
|
|
50
|
+
label: expectedError,
|
|
51
|
+
key: expectedError.split('.').pop()
|
|
52
|
+
})
|
|
53
|
+
})
|
|
54
|
+
])
|
|
55
|
+
})
|
|
56
|
+
)
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
it('valid date passes validation', () => {
|
|
60
|
+
expect(() => {
|
|
61
|
+
Joi.assert(dateSchemaInput('12', '10', '1987'), dateSchema)
|
|
62
|
+
}).not.toThrow()
|
|
63
|
+
})
|
|
64
|
+
})
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
import Joi from 'joi'
|
|
3
|
+
|
|
4
|
+
export const dateSchemaInput = (unparsedDay, unparsedMonth, unparsedYear) => {
|
|
5
|
+
const day = unparsedDay === '' ? undefined : unparsedDay
|
|
6
|
+
const month = unparsedMonth === '' ? undefined : unparsedMonth
|
|
7
|
+
const year = unparsedYear === '' ? undefined : unparsedYear
|
|
8
|
+
|
|
9
|
+
return {
|
|
10
|
+
'full-date': { day, month, year },
|
|
11
|
+
'day-and-month': { day, month },
|
|
12
|
+
'day-and-year': { day, year },
|
|
13
|
+
'month-and-year': { month, year },
|
|
14
|
+
day,
|
|
15
|
+
month,
|
|
16
|
+
year,
|
|
17
|
+
'non-numeric': { day, month, year },
|
|
18
|
+
'invalid-date': `${year}-${(month || '').padStart(2, '0')}-${(day || '').padStart(2, '0')}`
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export const dateSchema = Joi.object({
|
|
23
|
+
'full-date': Joi.object()
|
|
24
|
+
.keys({
|
|
25
|
+
day: Joi.any(),
|
|
26
|
+
month: Joi.any(),
|
|
27
|
+
year: Joi.any()
|
|
28
|
+
})
|
|
29
|
+
.or('day', 'month', 'year'),
|
|
30
|
+
'day-and-month': Joi.object()
|
|
31
|
+
.keys({
|
|
32
|
+
day: Joi.any(),
|
|
33
|
+
month: Joi.any()
|
|
34
|
+
})
|
|
35
|
+
.or('day', 'month'),
|
|
36
|
+
'day-and-year': Joi.object()
|
|
37
|
+
.keys({
|
|
38
|
+
day: Joi.any(),
|
|
39
|
+
year: Joi.any()
|
|
40
|
+
})
|
|
41
|
+
.or('day', 'year'),
|
|
42
|
+
'month-and-year': Joi.object()
|
|
43
|
+
.keys({
|
|
44
|
+
month: Joi.any(),
|
|
45
|
+
year: Joi.any()
|
|
46
|
+
})
|
|
47
|
+
.or('month', 'year'),
|
|
48
|
+
day: Joi.any().required(),
|
|
49
|
+
month: Joi.any().required(),
|
|
50
|
+
year: Joi.any().required(),
|
|
51
|
+
'non-numeric': Joi.object().keys({
|
|
52
|
+
day: Joi.number(),
|
|
53
|
+
month: Joi.number(),
|
|
54
|
+
year: Joi.number()
|
|
55
|
+
}),
|
|
56
|
+
'invalid-date': Joi.custom((dateToValidate, helpers) => {
|
|
57
|
+
if (new Date(dateToValidate).toISOString() !== `${dateToValidate}T00:00:00.000Z`) {
|
|
58
|
+
throw helpers.error('invalid-date')
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return dateToValidate
|
|
62
|
+
})
|
|
63
|
+
}).options({ abortEarly: true })
|