generated from ministryofjustice/hmpps-template-typescript
-
Notifications
You must be signed in to change notification settings - Fork 0
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
VB-3518, additional support page #58
Merged
Merged
Changes from 1 commit
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
169 changes: 169 additions & 0 deletions
169
server/routes/bookingJourney/selectAdditionalSupport.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,169 @@ | ||
import type { Express } from 'express' | ||
import request from 'supertest' | ||
import * as cheerio from 'cheerio' | ||
import { SessionData } from 'express-session' | ||
import { FieldValidationError } from 'express-validator' | ||
import { appWithAllRoutes, flashProvider } from '../testutils/appSetup' | ||
import { createMockBookerService, createMockPrisonService } from '../../services/testutils/mocks' | ||
import TestData from '../testutils/testData' | ||
import { FlashData } from '../../@types/bapv' | ||
|
||
let app: Express | ||
|
||
const bookerService = createMockBookerService() | ||
const prisonService = createMockPrisonService() | ||
let sessionData: SessionData | ||
|
||
const url = '/book-a-visit/select-additional-support' | ||
|
||
const bookerReference = TestData.bookerReference().value | ||
const prisoner = TestData.prisonerInfoDto() | ||
const prison = TestData.prisonDto() | ||
const visitors = [ | ||
hutcheonb-moj marked this conversation as resolved.
Show resolved
Hide resolved
|
||
TestData.visitorInfoDto({ personId: 1, firstName: 'Visitor', lastName: 'One', dateOfBirth: '1980-02-03' }), | ||
TestData.visitorInfoDto({ personId: 2, firstName: 'Visitor', lastName: 'Two', dateOfBirth: '1990-09-03' }), | ||
TestData.visitorInfoDto({ personId: 3, firstName: 'Visitor', lastName: 'Three', dateOfBirth: '2024-03-01' }), | ||
] | ||
|
||
afterEach(() => { | ||
jest.resetAllMocks() | ||
jest.useRealTimers() | ||
hutcheonb-moj marked this conversation as resolved.
Show resolved
Hide resolved
|
||
}) | ||
|
||
describe('Select additional support page', () => { | ||
let flashData: FlashData | ||
|
||
describe(`GET ${url}`, () => { | ||
beforeEach(() => { | ||
flashData = {} | ||
flashProvider.mockImplementation((key: keyof FlashData) => flashData[key]) | ||
|
||
sessionData = { | ||
booker: { reference: bookerReference, prisoners: [prisoner] }, | ||
bookingJourney: { allVisitors: visitors, selectedVisitors: visitors, prisoner, prison }, | ||
} as SessionData | ||
|
||
app = appWithAllRoutes({ services: { bookerService, prisonService }, sessionData }) | ||
hutcheonb-moj marked this conversation as resolved.
Show resolved
Hide resolved
|
||
}) | ||
|
||
it('should render additional support page', () => { | ||
return request(app) | ||
.get(url) | ||
.expect('Content-Type', /html/) | ||
.expect(res => { | ||
const $ = cheerio.load(res.text) | ||
expect($('title').text()).toMatch(/^Any additional support needs\? -/) | ||
expect($('[data-test="back-link"]').attr('href')).toBe('/') | ||
expect($('h1').text()).toBe('Is additional support needed for any of the visitors?') | ||
|
||
expect($('[data-test=prison-name]').text().trim()).toContain('Hewell (HMP)') | ||
|
||
expect($('form[method=POST]').attr('action')).toBe('/book-a-visit/select-additional-support') | ||
|
||
expect($('[data-test="continue-button"]').text().trim()).toBe('Continue') | ||
}) | ||
}) | ||
|
||
it('should render validation errors', () => { | ||
const validationError: FieldValidationError = { | ||
type: 'field', | ||
location: 'body', | ||
path: 'additionalSupport', | ||
value: [], | ||
msg: 'Enter details of the request', | ||
} | ||
|
||
flashData = { errors: [validationError], formValues: { visitorIds: [] } } | ||
|
||
return request(app) | ||
.get(url) | ||
.expect('Content-Type', /html/) | ||
.expect(res => { | ||
const $ = cheerio.load(res.text) | ||
expect($('.govuk-error-summary a[href="#additionalSupport-error"]').text()).toBe( | ||
'Enter details of the request', | ||
) | ||
expect($('#additionalSupport-error').text()).toContain('Enter details of the request') | ||
}) | ||
}) | ||
}) | ||
|
||
describe(`POST ${url}`, () => { | ||
beforeEach(() => { | ||
sessionData = { | ||
booker: { | ||
reference: bookerReference, | ||
prisoners: [prisoner], | ||
}, | ||
bookingJourney: { | ||
prisoner, | ||
prison, | ||
allVisitors: visitors, | ||
selectedVisitors: [visitors[0], visitors[2]], | ||
}, | ||
} as SessionData | ||
|
||
app = appWithAllRoutes({ sessionData }) | ||
}) | ||
|
||
it('should should save entered additional support to session and redirect to main contact page', () => { | ||
return request(app) | ||
.post(url) | ||
.send({ additionalSupportRequired: 'yes', additionalSupport: 'Wheelchair access' }) | ||
.expect(302) | ||
.expect('Location', '/book-a-visit/select-main-contact') | ||
.expect(() => { | ||
expect(sessionData).toStrictEqual({ | ||
booker: { | ||
reference: bookerReference, | ||
prisoners: [prisoner], | ||
}, | ||
bookingJourney: { | ||
prisoner, | ||
prison, | ||
allVisitors: visitors, | ||
selectedVisitors: [visitors[0], visitors[2]], | ||
visitorSupport: 'Wheelchair access', | ||
}, | ||
} as SessionData) | ||
}) | ||
}) | ||
|
||
it('should set a validation error and redirect to original page when no options selected', () => { | ||
const expectedFlashData: FlashData = { | ||
errors: [ | ||
{ | ||
type: 'field', | ||
location: 'body', | ||
path: 'additionalSupportRequired', | ||
value: undefined, | ||
msg: 'No answer selected', | ||
}, | ||
], | ||
formValues: { additionalSupport: '' }, | ||
} | ||
|
||
return request(app) | ||
.post(url) | ||
.expect(302) | ||
.expect('Location', url) | ||
.expect(() => { | ||
expect(flashProvider).toHaveBeenCalledWith('errors', expectedFlashData.errors) | ||
expect(flashProvider).toHaveBeenCalledWith('formValues', expectedFlashData.formValues) | ||
|
||
expect(sessionData).toStrictEqual({ | ||
booker: { | ||
reference: bookerReference, | ||
prisoners: [prisoner], | ||
}, | ||
bookingJourney: { | ||
prisoner, | ||
prison, | ||
allVisitors: visitors, | ||
selectedVisitors: [visitors[0], visitors[2]], | ||
}, | ||
} as SessionData) | ||
}) | ||
}) | ||
}) | ||
}) |
49 changes: 49 additions & 0 deletions
49
server/routes/bookingJourney/selectAdditionalSupportController.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
import type { RequestHandler } from 'express' | ||
import { ValidationChain, body, validationResult } from 'express-validator' | ||
import { BookerService } from '../../services' | ||
|
||
export default class AdditionalSupportController { | ||
hutcheonb-moj marked this conversation as resolved.
Show resolved
Hide resolved
|
||
public constructor(private readonly bookerService: BookerService) {} | ||
hutcheonb-moj marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
public view(): RequestHandler { | ||
return async (req, res) => { | ||
res.render('pages/bookingJourney/selectAdditionalSupport', { | ||
errors: req.flash('errors'), | ||
formValues: req.flash('formValues')?.[0] || {}, | ||
booker: req.session.booker, | ||
bookingJourney: req.session.bookingJourney, | ||
}) | ||
} | ||
} | ||
|
||
public submit(): RequestHandler { | ||
return async (req, res) => { | ||
const { bookingJourney } = req.session | ||
const errors = validationResult(req) | ||
|
||
if (!errors.isEmpty()) { | ||
req.flash('errors', errors.array() as []) | ||
hutcheonb-moj marked this conversation as resolved.
Show resolved
Hide resolved
|
||
req.flash('formValues', req.body) | ||
return res.redirect(`/book-a-visit/select-additional-support`) | ||
} | ||
|
||
bookingJourney.visitorSupport = req.body.additionalSupportRequired === 'no' ? '' : req.body.additionalSupport | ||
|
||
return res.redirect('/book-a-visit/select-main-contact') | ||
} | ||
} | ||
|
||
validate(): ValidationChain[] { | ||
return [ | ||
body('additionalSupportRequired').isIn(['yes', 'no']).withMessage('No answer selected'), | ||
body('additionalSupport') | ||
.trim() | ||
.if(body('additionalSupportRequired').equals('yes')) | ||
.notEmpty() | ||
.withMessage('Enter details of the request') | ||
.bail() | ||
.isLength({ min: 3, max: 512 }) | ||
.withMessage('Please enter at least 3 and no more than 512 characters'), | ||
] | ||
} | ||
} |
15 changes: 15 additions & 0 deletions
15
server/routes/bookingJourney/selectMainContactController.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
import type { RequestHandler } from 'express' | ||
import { BookerService } from '../../services' | ||
|
||
export default class MainContactController { | ||
hutcheonb-moj marked this conversation as resolved.
Show resolved
Hide resolved
|
||
public constructor(private readonly bookerService: BookerService) {} | ||
|
||
public view(): RequestHandler { | ||
return async (req, res) => { | ||
res.render('pages/bookingJourney/selectMainContact', { | ||
booker: req.session.booker, | ||
bookingJourney: req.session.bookingJourney, | ||
}) | ||
} | ||
} | ||
} |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We've got a bit of a mix in the code of
visitorSupport
vsadditionalSupport
: should we standardise on one?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we send it as visitor support - that's why I chose this naming