Skip to content

Commit

Permalink
Go to drop out page if no eligible adult visitors
Browse files Browse the repository at this point in the history
  • Loading branch information
tpmcgowan committed Dec 20, 2024
1 parent d281ea1 commit 3f32d5f
Show file tree
Hide file tree
Showing 6 changed files with 140 additions and 77 deletions.
52 changes: 40 additions & 12 deletions integration_tests/e2e/bookingJourneyDropOuts.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,19 @@ context('Booking journey - drop-out points', () => {
const today = new Date()
const prison = TestData.prisonDto({ policyNoticeDaysMax: 36 }) // > 31 so always 2 months shown
const prisoner = TestData.bookerPrisonerInfoDto()
const visitors = [
TestData.visitorInfoDto({
visitorId: 1000,
firstName: 'Adult',
lastName: 'One',
dateOfBirth: format(subYears(today, 25), DateFormats.ISO_DATE), // 25-year-old
}),
]

const adultVisitor = TestData.visitorInfoDto({
visitorId: 1000,
firstName: 'Adult',
lastName: 'One',
dateOfBirth: format(subYears(today, 25), DateFormats.ISO_DATE), // 25-year-old
})
const childVisitor = TestData.visitorInfoDto({
visitorId: 1000,
firstName: 'Child',
lastName: 'One',
dateOfBirth: format(subYears(today, 5), DateFormats.ISO_DATE), // 5-year-old
})

const tomorrow = format(addDays(today, 1), DateFormats.ISO_DATE)
const in10Days = format(addDays(today, 10), DateFormats.ISO_DATE)
Expand Down Expand Up @@ -50,7 +55,7 @@ context('Booking journey - drop-out points', () => {

// Start booking journey
cy.task('stubGetPrison', prison)
cy.task('stubGetVisitors', { visitors })
cy.task('stubGetVisitors', { visitors: [adultVisitor] })
cy.task('stubValidatePrisonerPass')
homePage.startBooking()

Expand Down Expand Up @@ -87,7 +92,7 @@ context('Booking journey - drop-out points', () => {

// Start booking journey
cy.task('stubGetPrison', prison)
cy.task('stubGetVisitors', { visitors })
cy.task('stubGetVisitors', { visitors: [adultVisitor] })
cy.task('stubValidatePrisonerPass')
homePage.startBooking()

Expand Down Expand Up @@ -138,7 +143,7 @@ context('Booking journey - drop-out points', () => {

// Start booking journey
cy.task('stubGetPrison', prison)
cy.task('stubGetVisitors', { visitors })
cy.task('stubGetVisitors', { visitors: [adultVisitor] })
cy.task('stubValidatePrisonerPass')
homePage.startBooking()

Expand All @@ -165,7 +170,7 @@ context('Booking journey - drop-out points', () => {

// Start booking journey
cy.task('stubGetPrison', prison)
cy.task('stubGetVisitors', { visitors })
cy.task('stubGetVisitors', { visitors: [adultVisitor] })
cy.task('stubValidatePrisonerFail')
homePage.startBooking()

Expand All @@ -179,5 +184,28 @@ context('Booking journey - drop-out points', () => {
cannotBookPage.backLink().click()
Page.verifyOnPage(HomePage)
})

it('should show drop-out page when no eligible visitors over 18', () => {
cy.task('stubGetBookerReference')
cy.task('stubGetPrisoners', { prisoners: [prisoner] })
cy.signIn()

// Home page - prisoner shown
const homePage = Page.verifyOnPage(HomePage)

// Start booking journey
cy.task('stubGetPrison', prison)
cy.task('stubGetVisitors', { visitors: [childVisitor] })
cy.task('stubValidatePrisonerPass')
homePage.startBooking()

// Visit cannot be booked page
const cannotBookPage = Page.verifyOnPage(CannotBookPage)
cy.contains('One person on a visit must be 18 years old or older')

// Back link back to Home page
cannotBookPage.backLink().click()
Page.verifyOnPage(HomePage)
})
})
})
6 changes: 5 additions & 1 deletion server/@types/bapv.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,4 +60,8 @@ export type BookingCancelled = {
hasMobile: boolean
}

export type CannotBookReason = 'NO_VO_BALANCE' | 'TRANSFER_OR_RELEASE' | 'UNSUPPORTED_PRISON'
export type CannotBookReason =
| 'NO_VO_BALANCE'
| 'TRANSFER_OR_RELEASE'
| 'UNSUPPORTED_PRISON'
| 'NO_ELIGIBLE_ADULT_VISITOR'
18 changes: 18 additions & 0 deletions server/routes/bookVisit/cannotBookController.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,5 +109,23 @@ describe('A visit cannot be booked', () => {
expect(sessionData.bookingJourney).toBe(undefined)
})
})

it('should render cannot book page and clear bookingJourney data - NO_ELIGIBLE_ADULT_VISITOR', () => {
sessionData.bookingJourney.cannotBookReason = 'NO_ELIGIBLE_ADULT_VISITOR'

return request(app)
.get(paths.BOOK_VISIT.CANNOT_BOOK)
.expect('Content-Type', /html/)
.expect(res => {
const $ = cheerio.load(res.text)
expect($('title').text()).toMatch(/^A visit cannot be booked -/)
expect($('[data-test="back-link"]').attr('href')).toBe(paths.HOME)
expect($('h1').text()).toBe('A visit cannot be booked')

expect($('main p').eq(0).text()).toContain('One person on a visit must be 18 years old or older')

expect(sessionData.bookingJourney).toBe(undefined)
})
})
})
})
117 changes: 54 additions & 63 deletions server/routes/bookVisit/selectVisitorsController.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,41 +49,47 @@ const visitor3 = TestData.visitor({
firstName: 'Visitor',
lastName: 'Age 17y',
dateOfBirth: '2006-05-03', // 18 tomorrow
adult: false,
})
const visitor4 = TestData.visitor({
visitorDisplayId: randomUUID(),
visitorId: 4000,
firstName: 'Visitor',
lastName: 'Age 16y',
dateOfBirth: '2008-05-02', // 16 today
adult: false,
})
const visitor5 = TestData.visitor({
visitorDisplayId: randomUUID(),
visitorId: 5000,
firstName: 'Visitor',
lastName: 'Age 15y',
dateOfBirth: '2008-05-03', // 16 tomorrow
adult: false,
})
const visitor6 = TestData.visitor({
visitorDisplayId: randomUUID(),
visitorId: 6000,
firstName: 'Visitor',
lastName: 'Age 10y',
dateOfBirth: '2014-05-02',
adult: false,
})
const visitor7 = TestData.visitor({
visitorDisplayId: randomUUID(),
visitorId: 7000,
firstName: 'Visitor',
lastName: 'Age 1y',
dateOfBirth: '2023-05-02',
adult: false,
})
const visitor8 = TestData.visitor({
visitorDisplayId: randomUUID(),
visitorId: 8000,
firstName: 'Visitor',
lastName: 'Age 4m',
dateOfBirth: '2024-01-02',
adult: false,
})
const visitors = [visitor1, visitor2, visitor3, visitor4, visitor5, visitor6, visitor7, visitor8]

Expand All @@ -108,7 +114,6 @@ describe('Select visitors', () => {
flashProvider.mockImplementation((key: keyof FlashData) => flashData[key])

sessionData = {
booker: { reference: bookerReference, prisoners: [prisoner] },
bookingJourney: { prisoner },
} as SessionData

Expand Down Expand Up @@ -178,17 +183,11 @@ describe('Select visitors', () => {
expect(bookerService.getEligibleVisitors).toHaveBeenCalledWith(bookerReference, prisoner.prisonerNumber)
expect(prisonService.getPrison).toHaveBeenCalledWith(prisoner.prisonId)

expect(sessionData).toStrictEqual({
booker: {
reference: bookerReference,
prisoners: [prisoner],
},
bookingJourney: {
prisoner,
prison,
eligibleVisitors: visitors,
},
} as SessionData)
expect(sessionData.bookingJourney).toStrictEqual({
prisoner,
prison,
eligibleVisitors: visitors,
} as SessionData['bookingJourney'])
})
})

Expand Down Expand Up @@ -271,17 +270,28 @@ describe('Select visitors', () => {
expect(bookerService.getEligibleVisitors).toHaveBeenCalledWith(bookerReference, prisoner.prisonerNumber)
expect(prisonService.getPrison).toHaveBeenCalledWith(prisoner.prisonId)

expect(sessionData).toStrictEqual({
booker: {
reference: bookerReference,
prisoners: [prisoner],
},
bookingJourney: {
prisoner,
prison,
eligibleVisitors: [],
},
} as SessionData)
expect(sessionData.bookingJourney).toStrictEqual({
prisoner,
prison,
eligibleVisitors: [],
} as SessionData['bookingJourney'])
})
})

it('should handle booker having no eligible adult visitors for this prisoner and redirect to cannot book page with reason', () => {
bookerService.getEligibleVisitors.mockResolvedValue([visitor6]) // only a child visitor

return request(app)
.get(paths.BOOK_VISIT.SELECT_VISITORS)
.expect(302)
.expect('location', paths.BOOK_VISIT.CANNOT_BOOK)
.expect(() => {
expect(sessionData.bookingJourney).toStrictEqual({
prisoner,
prison,
eligibleVisitors: [visitor6],
cannotBookReason: 'NO_ELIGIBLE_ADULT_VISITOR',
} as SessionData['bookingJourney'])
})
})
})
Expand All @@ -291,7 +301,6 @@ describe('Select visitors', () => {
visitSessionsService.getSessionRestriction.mockResolvedValue('OPEN')

sessionData = {
booker: { reference: bookerReference, prisoners: [prisoner] },
bookingJourney: { prisoner, prison, eligibleVisitors: visitors },
} as SessionData

Expand All @@ -306,19 +315,13 @@ describe('Select visitors', () => {
.expect('Location', paths.BOOK_VISIT.CHOOSE_TIME)
.expect(() => {
expect(flashProvider).not.toHaveBeenCalled()
expect(sessionData).toStrictEqual({
booker: {
reference: bookerReference,
prisoners: [prisoner],
},
bookingJourney: {
prisoner,
prison,
eligibleVisitors: visitors,
selectedVisitors: [visitor1, visitor3],
sessionRestriction: 'OPEN',
},
} as SessionData)
expect(sessionData.bookingJourney).toStrictEqual({
prisoner,
prison,
eligibleVisitors: visitors,
selectedVisitors: [visitor1, visitor3],
sessionRestriction: 'OPEN',
} as SessionData['bookingJourney'])
expect(visitSessionsService.getSessionRestriction).toHaveBeenCalledWith({
prisonerId: prisoner.prisonerNumber,
visitorIds: [visitor1.visitorId, visitor3.visitorId],
Expand All @@ -336,19 +339,13 @@ describe('Select visitors', () => {
.expect('Location', paths.BOOK_VISIT.CLOSED_VISIT)
.expect(() => {
expect(flashProvider).not.toHaveBeenCalled()
expect(sessionData).toStrictEqual({
booker: {
reference: bookerReference,
prisoners: [prisoner],
},
bookingJourney: {
prisoner,
prison,
eligibleVisitors: visitors,
selectedVisitors: [visitor1, visitor3],
sessionRestriction: 'CLOSED',
},
} as SessionData)
expect(sessionData.bookingJourney).toStrictEqual({
prisoner,
prison,
eligibleVisitors: visitors,
selectedVisitors: [visitor1, visitor3],
sessionRestriction: 'CLOSED',
} as SessionData['bookingJourney'])
expect(visitSessionsService.getSessionRestriction).toHaveBeenCalledWith({
prisonerId: prisoner.prisonerNumber,
visitorIds: [visitor1.visitorId, visitor3.visitorId],
Expand All @@ -371,19 +368,13 @@ describe('Select visitors', () => {
.expect('Location', paths.BOOK_VISIT.CHOOSE_TIME)
.expect(() => {
expect(flashProvider).not.toHaveBeenCalled()
expect(sessionData).toStrictEqual({
booker: {
reference: bookerReference,
prisoners: [prisoner],
},
bookingJourney: {
prisoner,
prison,
eligibleVisitors: visitors,
selectedVisitors: [visitors[0], visitors[2]], // duplicate '1' & unrecognised UUID filtered out
sessionRestriction: 'OPEN',
},
} as SessionData)
expect(sessionData.bookingJourney).toStrictEqual({
prisoner,
prison,
eligibleVisitors: visitors,
selectedVisitors: [visitors[0], visitors[2]], // duplicate '1' & unrecognised UUID filtered out
sessionRestriction: 'OPEN',
} as SessionData['bookingJourney'])
expect(visitSessionsService.getSessionRestriction).toHaveBeenCalledWith({
prisonerId: prisoner.prisonerNumber,
visitorIds: [visitor1.visitorId, visitor3.visitorId],
Expand Down
8 changes: 7 additions & 1 deletion server/routes/bookVisit/selectVisitorsController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,12 @@ export default class SelectVisitorsController {
])
}

const isAtLeastOneAdultVisitor = bookingJourney.eligibleVisitors.some(visitor => visitor.adult)
if (bookingJourney.eligibleVisitors.length && !isAtLeastOneAdultVisitor) {
req.session.bookingJourney.cannotBookReason = 'NO_ELIGIBLE_ADULT_VISITOR'
return res.redirect(paths.BOOK_VISIT.CANNOT_BOOK)
}

const selectedVisitorDisplayIds = {
visitorDisplayIds: bookingJourney.selectedVisitors?.map(visitor => visitor.visitorDisplayId) ?? [],
}
Expand All @@ -31,7 +37,7 @@ export default class SelectVisitorsController {
...req.flash('formValues')?.[0],
}

res.render('pages/bookVisit/selectVisitors', {
return res.render('pages/bookVisit/selectVisitors', {
errors: req.flash('errors'),
formValues,
prison: bookingJourney.prison,
Expand Down
16 changes: 16 additions & 0 deletions server/views/pages/bookVisit/cannotBook.njk
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,22 @@
<a href="https://www.gov.uk/government/collections/prisons-in-england-and-wales" target="_blank">how to book a visit at this prison</a>.
</p>
{% endif %}

{% if cannotBookReason == 'NO_ELIGIBLE_ADULT_VISITOR' %}
<p>
One person on a visit must be 18 years old or older. None of your adult visitors can be selected.
This may be because of a new restriction or a ban.
</p>

<h2 class="govuk-heading-m">Add a new visitor</h2>
<p>The person you want to book for must be on the prisoner’s visitor list.</p>

<p>
To make a request to book for someone new,
<a href="https://visit-someone-in-prison-add-visitor.form.service.justice.gov.uk/" target="_blank" rel="noopener noreferrer">complete the form (opens in a new tab)</a>.
We will respond within 5 working days.
</p>
{% endif %}
</div>
</div>
{% endblock %}

0 comments on commit 3f32d5f

Please sign in to comment.