Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Apply more specific date error messages #2022

Merged
merged 24 commits into from
Dec 9, 2024
Merged
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
cda7d6c
Apply more specific date error messages
irisfaraway Aug 19, 2024
5687ff7
Apply new error messages to DOB form
irisfaraway Aug 19, 2024
ec23329
Alternate message for BOBO journey
irisfaraway Aug 20, 2024
92983cc
Changed our minds, no alt message
irisfaraway Aug 20, 2024
b3ae1de
Highlight invalid fields in red
irisfaraway Aug 20, 2024
a29c006
Only validate whole date if fields are valid
irisfaraway Aug 20, 2024
e1d58f9
Update DOB validation on renewals
irisfaraway Sep 2, 2024
51b7796
Add new validation to licence start time
irisfaraway Sep 2, 2024
e7ddd68
Split up licence start validator
irisfaraway Sep 3, 2024
67d2e45
Refactor to address Sonarcloud issues
irisfaraway Sep 19, 2024
988783a
Refactor for Sonarcloud, clarify field name
irisfaraway Sep 19, 2024
89d8557
Remove extra line break, actually trying to force Sonar to re-analyse
jaucourt Oct 25, 2024
397ec2c
Generalise date validation and apply to date of birth page
jaucourt Nov 5, 2024
53b6fd0
Amended invalid date validation as it wasn't doing what was expected …
jaucourt Nov 8, 2024
3502c60
Lint fix
jaucourt Nov 8, 2024
0551751
Refactor dateSchemaInput to map zero-length strings to undefined valu…
jaucourt Nov 13, 2024
8a950cb
Added validation for licence to start page
jaucourt Nov 15, 2024
7f0c55e
Remove blank lines in english resource file
jaucourt Nov 15, 2024
63ef199
Refactor date of birth and start date validators to be reusable funct…
jaucourt Nov 15, 2024
77f0833
Amend templates to have correct error class
jaucourt Nov 15, 2024
b6fc2fe
Improve test for licence to start page
jaucourt Nov 15, 2024
d182510
Make the error shown by the date input the same as in the error summary
jaucourt Nov 20, 2024
f891b50
Add function to get error flags for a date, to pass through to the te…
jaucourt Dec 9, 2024
54bfb0c
Renamed getErrorFlags function to getDateErrorFlags
jaucourt Dec 9, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 26 additions & 7 deletions packages/gafl-webapp-service/src/locales/cy.json
Original file line number Diff line number Diff line change
Expand Up @@ -265,9 +265,16 @@
"disability_concession_title_you": "Ydych chi’n derbyn unrhyw un o’r canlynol?",
"dob_day": "Diwrnod",
"dob_entry_hint": "Er enghraifft, 23 11 1979",
"dob_error_format_max": "Mae'n rhaid i’r dyddiad geni fod yn y gorffennol",
"dob_error_format_min": "Nodwch eich dyddiad geni a chynnwys y diwrnod, y mis a’r flwyddyn",
"dob_error_format": "Nodwch ddyddiad geni deiliad y drwydded a chynnwys diwrnod, mis a blwyddyn",
"dob_error_date_real": "Mae’n rhaid i’r dyddiad geni fod yn ddyddiad dilys",
"dob_error_missing_day_and_month": "Mae’n rhaid i’r dyddiad geni gynnwys diwrnod a mis",
"dob_error_missing_day_and_year": "Mae’n rhaid i’r dyddiad geni gynnwys diwrnod a blwyddyn",
"dob_error_missing_month_and_year": "Mae’n rhaid i’r dyddiad geni gynnwys mis a blwyddyn",
"dob_error_missing_day": "Mae’n rhaid i’r dyddiad geni gynnwys diwrnod",
"dob_error_missing_month": "Mae’n rhaid i’r dyddiad geni gynnwys mis",
"dob_error_missing_year": "Mae’n rhaid i’r dyddiad geni gynnwys blwyddyn",
"dob_error_non_numeric": "Rhowch rifau yn unig",
"dob_error_year_min": "Mae’r dyddiad geni yn rhy bell yn ôl",
"dob_error_year_max": "Mae'n rhaid i’r dyddiad geni fod yn y gorffennol",
"dob_error": "Rhowch y dyddiad geni",
"dob_month": "Mis",
"dob_privacy_link_prefix": "Os nad ydych yn darparu dyddiad geni cywir, gallai hynny achosi oedi wrth adnewyddu trwydded, neu olygu nad yw’r drwydded yn ddilys. Darllenwch am ",
Expand Down Expand Up @@ -311,15 +318,19 @@
"header_service_name_title": " - Cael trwydded bysgota â gwialen",
"identification": "Rhif adnabod",
"identify_body_protect_info": "Er mwyn dod o hyd i fanylion eich trwydded, bydd angen i ni wybod pwy ydych chi. Mae hyn yn ein helpu i ddiogelu eich gwybodaeth bersonol.",
"identify_error_date_real": "Mae’n rhaid i’r dyddiad geni fod yn ddyddiad dilys",
"identify_error_empty_postcode": "Nid ydych wedi nodi cod post",
"identify_error_empty": "Rhowch chwe nodyn olaf eich trwydded",
"identify_error_enter_bday_max": "Mae’n rhaid i’ch dyddiad geni fod yn y gorffennol",
"identify_error_enter_bday_min": "Mae eich dyddiad geni yn rhy bell yn ôl",
"identify_error_enter_bday": "Nodwch eich dyddiad geni a chynnwys y diwrnod, y mis a’r flwyddyn",
"identify_error_invalid_1": "Nid oes gennym gofnod o rif trwydded sy'n gorffen gyda ",
"identify_error_invalid_2": " sy’n cyd-fynd â'r manylion hyn.",
"identify_error_missing_day": "Mae’n rhaid i’r dyddiad geni gynnwys diwrnod",
"identify_error_missing_month": "Mae’n rhaid i’r dyddiad geni gynnwys mis",
"identify_error_missing_year": "Mae’n rhaid i’r dyddiad geni gynnwys blwyddyn",
"identify_error_non_numeric": "Rhowch rifau yn unig",
"identify_error_pattern_postcode": "Your postcode doesn’t look right. Check and enter again",
"identify_error_pattern": "Nid yw chwe nodyn olaf eich trwydded yn edrych yn gywir. Gwiriwch a rhowch gynnig arall arni",
"identify_error_year_min": "Mae’r dyddiad geni yn rhy bell yn ôl",
"identify_error_year_max": "Mae'n rhaid i’r dyddiad geni fod yn y gorffennol",
"identify_label_last_six_hint": "Er enghraifft F4A315",
"identify_label_last_six": "Chwe nodyn olaf eich trwydded.",
"identify_label_licence_ending": "Rhif trwydded yn gorffen gyda’r canlynol",
Expand Down Expand Up @@ -397,8 +408,16 @@
"licence_num": "Rhif trwydded",
"licence_start_days": " diwrnod nesaf",
"licence_start_enter_todays_date": "Rhowch ddyddiad heddiw os ydych chi am i’r drwydded 1 diwrnod neu 8 diwrnod ddechrau yn hwyrach heddiw.",
"licence_start_error_date_real": "Mae’n rhaid i ddyddiad dechrau’r drwydded fod yn ddyddiad dilys",
"licence_start_error_missing_day_and_month": "Mae’n rhaid i ddyddiad dechrau’r drwydded gynnwys diwrnod a mis",
"licence_start_error_missing_day_and_year": "Mae’n rhaid i ddyddiad dechrau’r drwydded gynnwys diwrnod a blwyddyn",
"licence_start_error_missing_month_and_year": "Mae’n rhaid i ddyddiad dechrau’r drwydded gynnwys mis a blwyddyn",
"licence_start_error": "Rhowch ddyddiad dechrau’r drwydded",
"licence_start_error_missing_day": "Mae’n rhaid i ddyddiad dechrau’r drwydded gynnwys diwrnod",
"licence_start_error_missing_month": "Mae’n rhaid i ddyddiad dechrau’r drwydded gynnwys mis",
"licence_start_error_missing_year": "Mae’n rhaid i ddyddiad dechrau’r drwydded gynnwys blwyddyn",
"licence_start_error_non_numeric": "Rhowch rifau yn unig",
"licence_start_error_choose_when": "Dewiswch pryd y dylai'r drwydded ddechrau",
"licence_start_error_format": "Nodwch y dyddiad y mae angen i'r drwydded ddechrau a chynnwys diwrnod, mis a blwyddyn",
"licence_start_error_within": "Nodwch ddyddiad o fewn y ",
"licence_start_hint": "Rhowch ddyddiad hyd at a chan gynnwys ",
"licence_start_later": "Yn hwyrach",
Expand Down
37 changes: 29 additions & 8 deletions packages/gafl-webapp-service/src/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -265,10 +265,19 @@
"disability_concession_title_you": "Do you receive any of the following?",
"dob_day": "day",
"dob_entry_hint": "For example, 23 11 1979",
"dob_error_format_max": "The date of birth must be in the past",
"dob_error_format_min": "Enter the date of birth and include a day, month and year",
"dob_error_format": "Enter the licence holder’s date of birth and include a day, month and year",
"dob_error": "Enter the date of birth",

"dob_error_date_real": "Date of birth must be a real date",
"dob_error_missing_day_and_month": "Date of birth must include a day and month",
"dob_error_missing_day_and_year": "Date of birth must include a day and year",
"dob_error_missing_month_and_year": "Date of birth must include a month and year",
"dob_error_missing_day": "Date of birth must include a day",
"dob_error_missing_month": "Date of birth must include a month",
"dob_error_missing_year": "Date of birth must include a year",
"dob_error_non_numeric": "Enter only numbers",
"dob_error_year_min": "Date of birth is too long ago",
"dob_error_year_max": "The date of birth must be in the past",
"dob_error": "Enter a date of birth",

"dob_month": "month",
"dob_privacy_link_prefix": "If you do not provide a correct date of birth, this may cause delays when a licence is renewed or mean that a licence is not valid. Read about ",
"dob_privacy_link": "how we use personal information (opens in new tab)",
Expand Down Expand Up @@ -311,15 +320,19 @@
"header_service_name_title": " - Get a rod fishing licence",
"identification": "Identification",
"identify_body_protect_info": "To find your licence details we first need to identify you. This helps us protect your personal information.",
"identify_error_date_real": "Date of birth must be a real date",
"identify_error_empty_postcode": "You did not enter a postcode",
"identify_error_empty": "Enter the last six characters of your licence number",
"identify_error_enter_bday_max": "Your date of birth must be in the past",
"identify_error_enter_bday_min": "Your date of birth is too long ago",
"identify_error_enter_bday": "Enter your date of birth and include a day, month and year",
"identify_error_invalid_1": "We do not have any record of a licence number ending ",
"identify_error_invalid_2": " matching these details.",
"identify_error_missing_day": "Date of birth must include a day",
"identify_error_missing_month": "Date of birth must include a month",
"identify_error_missing_year": "Date of birth must include a year",
"identify_error_non_numeric": "Enter only numbers",
"identify_error_pattern_postcode": "Your postcode doesn’t look right. Check and enter again",
"identify_error_pattern": "The last six characters of your licence number don’t look right. Check and enter again",
"identify_error_year_min": "Date of birth is too long ago",
"identify_error_year_max": "The date of birth must be in the past",
"identify_label_last_six_hint": "For example F4A315",
"identify_label_last_six": "The last six characters of your licence number",
"identify_label_licence_ending": "The licence number ending",
Expand Down Expand Up @@ -397,8 +410,16 @@
"licence_num": "Licence number",
"licence_start_days": " days",
"licence_start_enter_todays_date": "Enter today’s date if you want the 1-day or 8-day licence to start later today.",
"licence_start_error_date_real": "Licence start date must be a real date",
"licence_start_error_missing_day_and_month": "Licence start date must include a day and month",
"licence_start_error_missing_day_and_year": "Licence start date must include a day and year",
"licence_start_error_missing_month_and_year": "Licence start date must include a month and year",
"licence_start_error_missing_day": "Licence start date must include a day",
"licence_start_error_missing_month": "Licence start date must include a month",
"licence_start_error_missing_year": "Licence start date must include a year",
"licence_start_error_non_numeric": "Enter only numbers",
"licence_start_error": "Enter a licence start date",
"licence_start_error_choose_when": "Choose when the licence should start",
"licence_start_error_format": "Enter the date the licence needs to start, include a day, month and year",
"licence_start_error_within": "Enter a date within the next ",
"licence_start_hint": "Enter a date up to and including ",
"licence_start_later": "Later",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,75 +1,117 @@
import { getData, validator } from '../route'
import { getData } from '../route'
import pageRoute from '../../../../routes/page-route.js'
import { nextPage } from '../../../../routes/next-page.js'
import { LICENCE_FOR } from '../../../../uri.js'
import { DATE_OF_BIRTH, LICENCE_FOR } from '../../../../uri.js'
import { dateOfBirthValidator } from '../../../../schema/validators/validators.js'

jest.mock('../../../../routes/next-page.js', () => ({
nextPage: jest.fn()
}))
jest.mock('../../../../routes/next-page.js')
jest.mock('../../../../routes/page-route.js')
jest.mock('../../../../schema/validators/validators.js')
jest.mock('../../../../uri.js', () => ({
...jest.requireActual('../../../../uri.js'),
DATE_OF_BIRTH: {
page: Symbol('date-of-birth-page'),
uri: Symbol('/date-of-birth')
},
LICENCE_TO_START: {
page: Symbol('licence-to-start-page'),
uri: Symbol('/licence-to-start')
}
}))

describe('name > route', () => {
const mockRequest = (statusGet = () => {}, transactionGet = () => {}) => ({
const mockRequest = ({
pageGet = async () => {},
statusGet = async () => ({ [LICENCE_FOR.page]: true }),
transactionGet = async () => ({ isLicenceForYou: null })
} = {}) => ({
cache: () => ({
helpers: {
transaction: {
getCurrentPermission: transactionGet
},
status: {
getCurrentPermission: statusGet
},
page: {
getCurrentPermission: pageGet
}
}
})
})

describe('getData', () => {
it('should return isLicenceForYou as true, if isLicenceForYou is true on the transaction cache', async () => {
const transaction = () => ({
const transactionGet = async () => ({
isLicenceForYou: true
})
const status = () => ({
const statusGet = async () => ({
[LICENCE_FOR.page]: true
})
const result = await getData(mockRequest(status, transaction))

const result = await getData(mockRequest({ statusGet, transactionGet }))
expect(result.isLicenceForYou).toBeTruthy()
})

it('should return isLicenceForYou as false, if isLicenceForYou is false on the transaction cache', async () => {
const transaction = () => ({
const transactionGet = async () => ({
isLicenceForYou: false
})
const status = () => ({
const statusGet = async () => ({
[LICENCE_FOR.page]: true
})
const result = await getData(mockRequest(status, transaction))
const result = await getData(mockRequest({ statusGet, transactionGet }))
expect(result.isLicenceForYou).toBeFalsy()
})

it.each([
['full-date', 'object.missing'],
['day', 'any.required']
])('should add error details ({%s: %s}) to the page data', async (errorKey, errorValue) => {
const pageGet = async () => ({
error: { [errorKey]: errorValue }
})

const result = await getData(mockRequest({ pageGet }))
expect(result.error).toEqual({ errorKey, errorValue })
})

it('omits error if there is no error', async () => {
const result = await getData(mockRequest())
expect(result.error).toBeUndefined()
})

it('passes correct page name when getting page cache', async () => {
const pageGet = jest.fn(() => ({}))
await getData(mockRequest({ pageGet }))
expect(pageGet).toHaveBeenCalledWith(DATE_OF_BIRTH.page)
})
})

describe('redirectToStartOfJourney', () => {
it('should throw a redirect if not been to LICENCE_FOR page', async () => {
const transaction = () => ({
const transactionGet = async () => ({
isLicenceForYou: true
})
const status = () => ({
const statusGet = async () => ({
[LICENCE_FOR.page]: false
})
const func = () => getData(mockRequest(status, transaction))
const func = () => getData(mockRequest({ statusGet, transactionGet }))
await expect(func).rejects.toThrowRedirectTo(LICENCE_FOR.uri)
})

it('should not throw a redirect if not been to LICENCE_FOR page', async () => {
const transaction = () => ({
const transactionGet = async () => ({
isLicenceForYou: true
})
const status = () => ({
const statusGet = async () => ({
[LICENCE_FOR.page]: true
})

let error

try {
await getData(mockRequest(status, transaction))
await getData(mockRequest({ statusGet, transactionGet }))
} catch (e) {
error = e
}
Expand All @@ -79,8 +121,8 @@ describe('name > route', () => {
})

describe('default', () => {
it('should call the pageRoute with date-of-birth, /buy/date-of-birth, validator and nextPage', async () => {
expect(pageRoute).toBeCalledWith('date-of-birth', '/buy/date-of-birth', validator, nextPage, getData)
it('should call the pageRoute with date-of-birth, /buy/date-of-birth, dateOfBirthValidator and nextPage', async () => {
expect(pageRoute).toBeCalledWith(DATE_OF_BIRTH.page, DATE_OF_BIRTH.uri, dateOfBirthValidator, nextPage, getData)
})
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -9,33 +9,59 @@

{%
set errorMap = {
'date-of-birth': {
'date.format': { ref: '#date-of-birth-day', text: mssgs.dob_error_format },
'date.max': { ref: '#date-of-birth-day', text: mssgs.dob_error_format_max },
'date.min': { ref: '#date-of-birth-day', text: mssgs.dob_error_format_min }
}
'full-date': {
'object.missing': { ref: '#date-of-birth-day', text: mssgs.dob_error }
},
'day-and-month': {
'object.missing': { ref: '#date-of-birth-day', text: mssgs.dob_error_missing_day_and_month }
},
'day-and-year': {
'object.missing': { ref: '#date-of-birth-day', text: mssgs.dob_error_missing_day_and_year }
},
'month-and-year': {
'object.missing': { ref: '#date-of-birth-month', text: mssgs.dob_error_missing_month_and_year }
},
'day': {
'any.required': { ref: '#date-of-birth-day', text: mssgs.dob_error_missing_day }
},
'month': {
'any.required': { ref: '#date-of-birth-month', text: mssgs.dob_error_missing_month }
},
'year': {
'any.required': { ref: '#date-of-birth-year', text: mssgs.dob_error_missing_year }
},
'non-numeric': {
'number.base': { ref: '#date-of-birth-day', text: mssgs.dob_error_non_numeric }
},
'invalid-date': {
'any.custom': { ref: '#date-of-birth-day', text: mssgs.dob_error_date_real }
},
'birthDate': {
'date.min': { ref: '#date-of-birth-day', text: mssgs.dob_error_year_min },
'date.max': { ref: '#date-of-birth-day', text: mssgs.dob_error_year_max }
}
}
%}

{% set dateInputItems = [
{
label: mssgs.dob_day,
name: 'day',
classes: "govuk-input--width-2",
classes: "govuk-input--width-2 govuk-input--error" if error['full-date'] or error['day-and-month'] or error['day-and-year'] or error['day'] or error['invalid-date'] or error['birthDate'] else "govuk-input--width-2",
jaucourt marked this conversation as resolved.
Show resolved Hide resolved
value: payload['date-of-birth-day'],
attributes: { maxlength : 2 }
},
{
label: mssgs.dob_month,
name: 'month',
classes: "govuk-input--width-2",
classes: "govuk-input--width-2 govuk-input--error" if error['full-date'] or error['day-and-month'] or error['month-and-year'] or error['month'] or error['invalid-date'] or error['birthDate'] else "govuk-input--width-2",
value: payload['date-of-birth-month'],
attributes: { maxlength : 2 }
},
{
label: mssgs.dob_year,
name: 'year',
classes: "govuk-input--width-4",
classes: "govuk-input--width-4 govuk-input--error" if error['full-date'] or error['month-and-year'] or error['day-and-year'] or error['year'] or error['invalid-date'] or error['birthDate'] else "govuk-input--width-4",
value: payload['date-of-birth-year'],
attributes: { maxlength : 4 }
}
Expand All @@ -56,6 +82,6 @@
id: "date-of-birth",
namePrefix: "date-of-birth",
items: dateInputItems,
errorMessage: { text: mssgs.dob_error } if error
errorMessage: { text: errorMap[data.error.errorKey][data.error.errorValue].text } if data.error
}) }}
{% endblock %}
Original file line number Diff line number Diff line change
@@ -1,19 +1,8 @@
import { DATE_OF_BIRTH, LICENCE_FOR } from '../../../uri.js'
import Joi from 'joi'
import pageRoute from '../../../routes/page-route.js'
import { validation } from '@defra-fish/business-rules-lib'
import { nextPage } from '../../../routes/next-page.js'
import GetDataRedirect from '../../../handlers/get-data-redirect.js'

export const validator = payload => {
const dateOfBirth = `${payload['date-of-birth-year']}-${payload['date-of-birth-month']}-${payload['date-of-birth-day']}`
Joi.assert(
{ 'date-of-birth': dateOfBirth },
Joi.object({
'date-of-birth': validation.contact.createBirthDateValidator(Joi)
})
)
}
import { dateOfBirthValidator } from '../../../schema/validators/validators.js'

const redirectToStartOfJourney = status => {
if (!status[LICENCE_FOR.page]) {
Expand All @@ -24,10 +13,17 @@ const redirectToStartOfJourney = status => {
export const getData = async request => {
const { isLicenceForYou } = await request.cache().helpers.transaction.getCurrentPermission()
const status = await request.cache().helpers.status.getCurrentPermission()
const page = await request.cache().helpers.page.getCurrentPermission(DATE_OF_BIRTH.page)

redirectToStartOfJourney(status)

if (page?.error) {
const [errorKey] = Object.keys(page.error)
const errorValue = page.error[errorKey]

return { isLicenceForYou, error: { errorKey, errorValue } }
}
return { isLicenceForYou }
}

export default pageRoute(DATE_OF_BIRTH.page, DATE_OF_BIRTH.uri, validator, nextPage, getData)
export default pageRoute(DATE_OF_BIRTH.page, DATE_OF_BIRTH.uri, dateOfBirthValidator, nextPage, getData)
Loading
Loading