diff --git a/backend/api/whereaboutsApi.ts b/backend/api/whereaboutsApi.ts index 452e795b7..85b7eeade 100644 --- a/backend/api/whereaboutsApi.ts +++ b/backend/api/whereaboutsApi.ts @@ -59,10 +59,6 @@ export const whereaboutsApiFactory = (client) => { `/attendances/offender/${offenderNo}/unacceptable-absences?fromDate=${fromDate}&toDate=${toDate}&page=${page}` ) - const getCourtLocations = (context) => get(context, '/court/courts') - - const addVideoLinkBooking = (context, body) => post(context, '/court/video-link-bookings', body) - const getAttendanceChanges = (context, { fromDateTime, toDateTime }, agencyId) => { const path = `/attendances/changes?fromDateTime=${fromDateTime}&toDateTime=${toDateTime}` + @@ -122,8 +118,6 @@ export const whereaboutsApiFactory = (client) => { getAbsences, getUnacceptableAbsences, getUnacceptableAbsenceDetail, - getCourtLocations, - addVideoLinkBooking, getAttendanceChanges, getCellsWithCapacity, moveToCell, diff --git a/backend/controllers/appointments/addAppointment.ts b/backend/controllers/appointments/addAppointment.ts index 9270f8917..e46c38d32 100644 --- a/backend/controllers/appointments/addAppointment.ts +++ b/backend/controllers/appointments/addAppointment.ts @@ -276,8 +276,6 @@ export const addAppointmentFactory = ( bookingId, }) - if (appointmentType === 'VLB') return res.redirect(`/offenders/${offenderNo}/prepost-appointments`) - await createAppointments({ locals: res.locals, comments, diff --git a/backend/controllers/appointments/prepostAppoinments.ts b/backend/controllers/appointments/prepostAppoinments.ts deleted file mode 100644 index 793100a6f..000000000 --- a/backend/controllers/appointments/prepostAppoinments.ts +++ /dev/null @@ -1,435 +0,0 @@ -import moment from 'moment' -import { DATE_TIME_FORMAT_SPEC, DAY_MONTH_YEAR, Time } from '../../../common/dateHelpers' -import { notifications } from '../../config' -import { properCaseName } from '../../utils' - -const { confirmBookingPrisonTemplateId, emails } = notifications - -const APPOINTMENT_DURATION_MINS = 15 - -const unpackAppointmentDetails = (req) => { - const appointmentDetails = req.flash('appointmentDetails') - if (!appointmentDetails || !appointmentDetails.length) throw new Error('Appointment details are missing') - - return appointmentDetails.reduce( - (acc, current) => ({ - ...acc, - ...current, - }), - {} - ) -} - -const packAppointmentDetails = (req, details) => { - req.flash('appointmentDetails', details) -} - -const validate = ({ - postAppointment, - preAppointment, - preAppointmentLocation, - postAppointmentLocation, - court, - otherCourtForm, - otherCourt, -}) => { - const errors = [] - - if (preAppointment === 'yes' && !preAppointmentLocation) - errors.push({ text: 'Select a room for the pre-court hearing briefing', href: '#preAppointmentLocation' }) - - if (postAppointment === 'yes' && !postAppointmentLocation) - errors.push({ text: 'Select a room for the post-court hearing briefing', href: '#postAppointmentLocation' }) - - if (!otherCourtForm && !court) errors.push({ text: 'Select which court the hearing is for', href: '#court' }) - - if (otherCourtForm && !otherCourt) errors.push({ text: 'Enter the name of the court', href: '#otherCourt' }) - - return errors -} - -const getLinks = (offenderNo) => ({ - postAppointments: `/offenders/${offenderNo}/prepost-appointments`, - cancel: `/offenders/${offenderNo}/prepost-appointments/cancel`, -}) - -export const prepostAppointmentsFactory = ({ - prisonApi, - hmppsManageUsersApi, - whereaboutsApi, - notifyClient, - appointmentsService, - existingEventsService, - raiseAnalyticsEvent, -}) => { - const cancel = async (req, res) => { - unpackAppointmentDetails(req) - res.redirect(`/prisoner/${req.params.offenderNo}`) - } - - const getLocationEvents = async (context, { activeCaseLoadId, locationId, date }) => { - const [locationDetails, locationEvents] = await Promise.all([ - prisonApi.getLocation(context, Number(locationId)), - existingEventsService.getExistingEventsForLocation(context, activeCaseLoadId, Number(locationId), date), - ]) - - return { - locationName: locationDetails && locationDetails.userDescription, - events: locationEvents, - } - } - - const getCourts = async (locals) => { - const items = await whereaboutsApi.getCourtLocations(locals) - const formattedLocations = Object.fromEntries(items.map(({ id, name }) => [id, name])) - - return { - ...formattedLocations, - other: 'Other', - } - } - const getCourtDropdownValues = async (locals) => { - const courts = await getCourts(locals) - return Object.keys(courts).map((key) => ({ value: key, text: courts[key] })) - } - - const handleLocationEventsIfRequired = async ( - locals, - { activeCaseLoadId, preAppointmentLocation, postAppointmentLocation, date } - ) => { - const locationEvents = {} - if (preAppointmentLocation) { - const { locationName, events } = await getLocationEvents(locals, { - activeCaseLoadId, - locationId: preAppointmentLocation, - date, - }) - // @ts-expect-error ts-migrate(2339) FIXME: Property 'preAppointment' does not exist on type '... Remove this comment to see the full error message - locationEvents.preAppointment = { locationName, events } - } - - if (postAppointmentLocation) { - const { locationName, events } = await getLocationEvents(locals, { - activeCaseLoadId, - locationId: postAppointmentLocation, - date, - }) - // @ts-expect-error ts-migrate(2339) FIXME: Property 'postAppointment' does not exist on type ... Remove this comment to see the full error message - locationEvents.postAppointment = { locationName, events } - } - - return locationEvents - } - - const index = async (req, res) => { - const { offenderNo } = req.params - const { activeCaseLoadId, authSource } = req.session.userDetails - - try { - const appointmentDetails = unpackAppointmentDetails(req) - const { locationId, appointmentType, startTime, endTime, postAppointment, preAppointment } = appointmentDetails - - const { appointmentTypes, locationTypes } = await appointmentsService.getAppointmentOptions( - res.locals, - activeCaseLoadId - ) - const { text: locationDescription } = locationTypes.find((loc) => loc.value === Number(locationId)) - const { text: appointmentTypeDescription } = appointmentTypes.find((app) => app.value === appointmentType) - - const { firstName, lastName, bookingId } = await prisonApi.getDetails(res.locals, offenderNo) - - const date = moment(startTime, DATE_TIME_FORMAT_SPEC).format(DAY_MONTH_YEAR) - - const courts = await getCourtDropdownValues(res.locals) - - const locationEvents = await handleLocationEventsIfRequired(res.locals, { - activeCaseLoadId, - preAppointmentLocation: preAppointment && preAppointment.locationId, - postAppointmentLocation: postAppointment && postAppointment.locationId, - date, - }) - - packAppointmentDetails(req, { - ...appointmentDetails, - locationDescription, - appointmentTypeDescription, - locationTypes, - firstName, - lastName, - bookingId, - date, - }) - - res.render('prepostAppointments.njk', { - links: getLinks(offenderNo), - locations: locationTypes, - locationEvents, - courts, - formValues: { - postAppointment: postAppointment?.required, - preAppointment: preAppointment?.required, - preAppointmentLocation: preAppointment && Number(preAppointment.locationId), - postAppointmentLocation: postAppointment && Number(postAppointment.locationId), - }, - date, - details: { - name: `${properCaseName(lastName)}, ${properCaseName(firstName)}`, - location: locationDescription, - date: moment(startTime, DATE_TIME_FORMAT_SPEC).format('D MMMM YYYY'), - courtHearingStartTime: Time(startTime), - courtHearingEndTime: endTime && Time(endTime), - }, - }) - } catch (error) { - res.locals.redirectUrl = - authSource === 'nomis' ? `/offenders/${offenderNo}/add-appointment` : '/videolink/prisoner-search' - throw error - } - } - const makeVideoLinkBooking = async (context, main, pre, post) => { - const { comment, bookingId, courtName, courtId } = main - - await whereaboutsApi.addVideoLinkBooking(context, { - bookingId, - court: courtName, - courtId, - comment, - madeByTheCourt: false, - ...(pre && { - pre: { - locationId: Number(pre.locationId), - startTime: pre.startTime, - endTime: pre.endTime, - }, - }), - main: { - locationId: Number(main.locationId), - startTime: main.startTime, - endTime: main.endTime, - }, - ...(post && { - post: { - locationId: Number(post.locationId), - startTime: post.startTime, - endTime: post.endTime, - }, - }), - }) - } - - const toPreAppointment = ({ startTime, preAppointmentLocation }) => { - const preStartTime = moment(startTime, DATE_TIME_FORMAT_SPEC).subtract(APPOINTMENT_DURATION_MINS, 'minutes') - const preEndTime = moment(preStartTime, DATE_TIME_FORMAT_SPEC).add(APPOINTMENT_DURATION_MINS, 'minutes') - return { - required: 'yes', - startTime: preStartTime.format(DATE_TIME_FORMAT_SPEC), - endTime: preEndTime.format(DATE_TIME_FORMAT_SPEC), - locationId: Number(preAppointmentLocation), - } - } - - const toPostAppointment = ({ endTime, postAppointmentLocation }) => { - const postEndTime = moment(endTime, DATE_TIME_FORMAT_SPEC).add(APPOINTMENT_DURATION_MINS, 'minutes') - - return { - required: 'yes', - startTime: endTime, - endTime: postEndTime.format(DATE_TIME_FORMAT_SPEC), - locationId: Number(postAppointmentLocation), - } - } - - const post = async (req, res) => { - const { offenderNo } = req.params - const { activeCaseLoadId, username, authSource } = req.session.userDetails - - const { - postAppointment, - preAppointment, - preAppointmentLocation, - postAppointmentLocation, - court: courtId, - otherCourt, - otherCourtForm, - } = req.body - - try { - const appointmentDetails = unpackAppointmentDetails(req) - const { startTime, endTime, comment, locationDescription, locationTypes, firstName, lastName, date } = - appointmentDetails - - const errors = validate({ - preAppointment, - postAppointment, - preAppointmentLocation, - postAppointmentLocation, - court: courtId, - otherCourt, - otherCourtForm, - }) - - const locationEvents = await handleLocationEventsIfRequired(res.locals, { - activeCaseLoadId, - preAppointmentLocation, - postAppointmentLocation, - date, - }) - - const courts = await getCourts(res.locals) - const courtName = otherCourt || courts[courtId] - - const preDetails = (preAppointment === 'yes' && - toPreAppointment({ - startTime, - preAppointmentLocation, - })) || { required: 'no' } - - const postDetails = (postAppointment === 'yes' && - toPostAppointment({ - endTime, - postAppointmentLocation, - })) || { required: 'no' } - - packAppointmentDetails(req, { - ...appointmentDetails, - preAppointment: preDetails, - postAppointment: postDetails, - court: courtName, - }) - - if (errors.length) { - packAppointmentDetails(req, appointmentDetails) - - const courtDropDownValues = await getCourtDropdownValues(res.locals) - - if (otherCourtForm) { - return res.render('enterCustomCourt.njk', { - cancel: `/offenders/${offenderNo}/prepost-appointments`, - formValues: { - postAppointment, - preAppointment, - preAppointmentLocation, - postAppointmentLocation, - court: courtId, - }, - errors, - }) - } - - return res.render('prepostAppointments.njk', { - locationEvents, - links: getLinks(offenderNo), - locations: locationTypes, - courts: courtDropDownValues, - formValues: { - postAppointment, - preAppointment, - preAppointmentLocation: preAppointmentLocation && Number(preAppointmentLocation), - postAppointmentLocation: postAppointmentLocation && Number(postAppointmentLocation), - court: courtId, - }, - errors, - date, - details: { - name: `${properCaseName(lastName)}, ${properCaseName(firstName)}`, - location: locationDescription, - date: moment(startTime, DATE_TIME_FORMAT_SPEC).format('D MMMM YYYY'), - courtHearingStartTime: Time(startTime), - courtHearingEndTime: endTime && Time(endTime), - }, - }) - } - - if (courtId === 'other') { - return res.render('enterCustomCourt.njk', { - cancel: `/offenders/${offenderNo}/prepost-appointments`, - formValues: { - postAppointment, - preAppointment, - preAppointmentLocation: preAppointmentLocation && Number(preAppointmentLocation), - postAppointmentLocation: postAppointmentLocation && Number(postAppointmentLocation), - court: courtId, - }, - errors, - date, - }) - } - - await makeVideoLinkBooking( - res.locals, - { - ...appointmentDetails, - // Pass court name if 'other' as it means free text, else send court ID - courtName: otherCourt ? courtName : undefined, - courtId: !otherCourt ? courtId : undefined, - }, - preAppointment === 'yes' && preDetails, - postAppointment === 'yes' && postDetails - ) - - const agencyDetails = await prisonApi.getAgencyDetails(res.locals, activeCaseLoadId) - const userEmailData = await hmppsManageUsersApi.userEmail(res.locals, username) - - raiseAnalyticsEvent('VLB Appointments', 'Video link booked', `${agencyDetails.description} - ${courtName}`) - - const preAppointmentInfo = - preAppointment === 'yes' - ? // @ts-expect-error ts-migrate(2339) FIXME: Property 'preAppointment' does not exist on type '... Remove this comment to see the full error message - `${locationEvents.preAppointment.locationName}, ${Time( - moment(startTime, DATE_TIME_FORMAT_SPEC).subtract(APPOINTMENT_DURATION_MINS, 'minutes') - )} to ${Time(startTime)}` - : 'None requested' - - const postAppointmentInfo = - postAppointment === 'yes' - ? // @ts-expect-error ts-migrate(2339) FIXME: Property 'postAppointment' does not exist on type ... Remove this comment to see the full error message - `${locationEvents.postAppointment.locationName}, ${Time(endTime)} to ${Time( - moment(endTime, DATE_TIME_FORMAT_SPEC).add(APPOINTMENT_DURATION_MINS, 'minutes') - )}` - : 'None requested' - - if (userEmailData && userEmailData.email) { - const personalisation = { - startTime: Time(startTime), - endTime: Time(endTime), - comments: comment || 'None entered.', - firstName: properCaseName(firstName), - lastName: properCaseName(lastName), - offenderNo, - prison: agencyDetails.description, - date: moment(date, DAY_MONTH_YEAR).format('D MMMM YYYY'), - location: locationDescription, - preAppointmentInfo, - postAppointmentInfo, - } - - notifyClient.sendEmail(confirmBookingPrisonTemplateId, userEmailData.email, { - personalisation, - reference: null, - }) - - if (emails[activeCaseLoadId] && emails[activeCaseLoadId].omu) { - notifyClient.sendEmail(confirmBookingPrisonTemplateId, emails[activeCaseLoadId].omu, { - personalisation, - reference: null, - }) - } - } - return res.redirect(`/offenders/${offenderNo}/confirm-appointment`) - } catch (error) { - res.locals.redirectUrl = - authSource === 'nomis' ? `/offenders/${offenderNo}/add-appointment` : '/videolink/prisoner-search' - throw error - } - } - - return { - index, - post, - cancel, - } -} - -export default { - prepostAppointmentsFactory, -} diff --git a/backend/routes.ts b/backend/routes.ts index 0fccf085e..84c616656 100644 --- a/backend/routes.ts +++ b/backend/routes.ts @@ -17,7 +17,6 @@ import bulkAppointmentsUploadRouter from './routes/appointments/bulkAppointments import bulkAppointmentsClashesRouter from './routes/appointments/bulkAppointmentsClashesRouter' import changeCaseloadRouter from './routes/changeCaseloadRouter' import addAppointmentRouter from './routes/appointments/addAppointmentRouter' -import prepostAppointmentsRouter from './routes/appointments/prepostAppointmentsRouter' import viewAppointments from './controllers/appointments/viewAppointments' import confirmAppointmentRouter from './routes/appointments/confirmAppointmentRouter' import prisonerProfileRouter from './routes/prisonerProfileRouter' @@ -37,8 +36,6 @@ import notificationDismiss from './controllers/notificationDismiss' import contentfulClient from './contentfulClient' import notificationBar from './middleware/notificationHandler' import systemOauthClient from './api/systemOauthClient' -import { notifyClient } from './shared/notifyClient' -import { raiseAnalyticsEvent } from './raiseAnalyticsEvent' import backToStart from './controllers/backToStart' import permit from './controllers/permit' import appointmentDetailsServiceFactory from './services/appointmentDetailsService' @@ -307,11 +304,6 @@ const setup = ({ isAppointmentsRolledOut, maintenancePage('Appointment details') ) - router.use( - '/offenders/:offenderNo/prepost-appointments', - isAppointmentsRolledOut, - maintenancePage('Appointment details') - ) } else { router.use( '/offenders/:offenderNo/add-appointment', @@ -319,19 +311,6 @@ const setup = ({ isPrisonerProfileAppointmentsRolledOut, addAppointmentRouter({ systemOauthClient, prisonApi, whereaboutsApi, logError }) ) - - router.use( - '/offenders/:offenderNo/prepost-appointments', - isAppointmentsRolledOut, - prepostAppointmentsRouter({ - systemOauthClient, - prisonApi, - hmppsManageUsersApi, - whereaboutsApi, - notifyClient, - raiseAnalyticsEvent, - }) - ) } router.use( diff --git a/backend/routes/appointments/prepostAppointmentsRouter.ts b/backend/routes/appointments/prepostAppointmentsRouter.ts deleted file mode 100644 index 3674c99d8..000000000 --- a/backend/routes/appointments/prepostAppointmentsRouter.ts +++ /dev/null @@ -1,35 +0,0 @@ -import express from 'express' -import prepostAppointments from '../../controllers/appointments/prepostAppoinments' -import { appointmentsServiceFactory } from '../../services/appointmentsService' -import existingEventsServiceFactory from '../../services/existingEventsService' - -const router = express.Router({ mergeParams: true }) - -const controller = ({ - systemOauthClient, - prisonApi, - hmppsManageUsersApi, - whereaboutsApi, - notifyClient, - raiseAnalyticsEvent, -}) => { - const appointmentsService = appointmentsServiceFactory(prisonApi) - const existingEventsService = existingEventsServiceFactory(systemOauthClient.getClientCredentialsTokens, prisonApi) - const { index, post, cancel } = prepostAppointments.prepostAppointmentsFactory({ - prisonApi, - hmppsManageUsersApi, - whereaboutsApi, - notifyClient, - appointmentsService, - existingEventsService, - raiseAnalyticsEvent, - }) - - router.get('/', index) - router.post('/', post) - router.get('/cancel', cancel) - - return router -} - -export default (dependencies) => controller(dependencies) diff --git a/backend/tests/addAppointment.test.ts b/backend/tests/addAppointment.test.ts index acb01ea9e..512c85244 100644 --- a/backend/tests/addAppointment.test.ts +++ b/backend/tests/addAppointment.test.ts @@ -169,18 +169,6 @@ describe('Add appointment', () => { const spy = jest.spyOn(Date, 'now') spy.mockRestore() }) - - it('should redirect to the prepost appointment page when the appointment type is "VLB"', async () => { - jest.spyOn(Date, 'now').mockImplementation(() => 33103209600000) // Friday 3019-01-01T00:00:00.000Z - req.body = { ...validBody, appointmentType: 'VLB', date: moment().format(DAY_MONTH_YEAR) } - - await controller.post(req, res) - - expect(res.redirect).toHaveBeenCalledWith(`/offenders/${offenderNo}/prepost-appointments`) - - const spy = jest.spyOn(Date, 'now') - spy.mockRestore() - }) }) describe('when there are API errors', () => { diff --git a/backend/tests/prepostAppointments.test.ts b/backend/tests/prepostAppointments.test.ts deleted file mode 100644 index ac228e26c..000000000 --- a/backend/tests/prepostAppointments.test.ts +++ /dev/null @@ -1,945 +0,0 @@ -import { prepostAppointmentsFactory } from '../controllers/appointments/prepostAppoinments' -import { Time } from '../../common/dateHelpers' -import { notifyClient } from '../shared/notifyClient' -import config from '../config' - -describe('Pre post appointments', () => { - let body - const prisonApi = {} - const hmppsManageUsersApi = {} - const whereaboutsApi = {} - const appointmentsService = {} - const existingEventsService = {} - - const req = { - originalUrl: 'http://localhost', - params: { offenderNo: 'A12345' }, - session: { userDetails: { activeCaseLoadId: 'LEI', authSource: 'nomis' } }, - } - const res = { locals: {} } - - const bookingId = 1 - const appointmentDetails = { - bookingId, - offenderNo: 'A12345', - firstName: 'john', - lastName: 'doe', - appointmentType: 'VLB', - locationId: 1, - startTime: '2017-10-10T11:00', - endTime: '2017-10-10T14:00', - recurring: 'No', - comment: 'Test', - locationDescription: 'Room 3', - appointmentTypeDescription: 'Videolink', - locationTypes: [{ value: 1, text: 'Room 3' }], - date: '10/10/2019', - court: 'london', - } - - const locationEvents = [ - { locationId: 3, eventDescription: 'Doctors - An appointment', startTime: '12:00', endTime: '13:00' }, - ] - - beforeEach(() => { - // @ts-expect-error ts-migrate(2339) FIXME: Property 'getDetails' does not exist on type '{}'. - prisonApi.getDetails = jest.fn() - // @ts-expect-error ts-migrate(2339) FIXME: Property 'addSingleAppointment' does not exist on ... Remove this comment to see the full error message - prisonApi.addSingleAppointment = jest.fn() - // @ts-expect-error ts-migrate(2339) FIXME: Property 'getLocation' does not exist on type '{}'... Remove this comment to see the full error message - prisonApi.getLocation = jest.fn() - // @ts-expect-error ts-migrate(2339) FIXME: Property 'getAgencyDetails' does not exist on type... Remove this comment to see the full error message - prisonApi.getAgencyDetails = jest.fn() - // @ts-expect-error ts-migrate(2339) FIXME: Property 'userEmail' does not exist on type '{}'. - hmppsManageUsersApi.userEmail = jest.fn() - // @ts-expect-error ts-migrate(2339) FIXME: Property 'getAppointmentOptions' does not exist on... Remove this comment to see the full error message - appointmentsService.getAppointmentOptions = jest.fn() - // @ts-expect-error ts-migrate(2339) FIXME: Property 'getExistingEventsForLocation' does not e... Remove this comment to see the full error message - existingEventsService.getExistingEventsForLocation = jest.fn() - // @ts-expect-error ts-migrate(2339) FIXME: Property 'getCourtLocations' does not exist on typ... Remove this comment to see the full error message - whereaboutsApi.getCourtLocations = jest.fn() - // @ts-expect-error ts-migrate(2339) FIXME: Property 'addVideoLinkBooking' does not exist on t... Remove this comment to see the full error message - whereaboutsApi.addVideoLinkBooking = jest.fn() - - // @ts-expect-error ts-migrate(2339) FIXME: Property 'flash' does not exist on type '{ origina... Remove this comment to see the full error message - req.flash = jest.fn() - // @ts-expect-error ts-migrate(2339) FIXME: Property 'render' does not exist on type '{ locals... Remove this comment to see the full error message - res.render = jest.fn() - res.locals = {} - - // @ts-expect-error ts-migrate(2339) FIXME: Property 'getAppointmentOptions' does not exist on... Remove this comment to see the full error message - appointmentsService.getAppointmentOptions.mockReturnValue({ - appointmentTypes: [{ value: 'VLB', text: 'Videolink' }], - locationTypes: [{ value: 1, text: 'Room 3' }], - }) - - // @ts-expect-error ts-migrate(2339) FIXME: Property 'getDetails' does not exist on type '{}'. - prisonApi.getDetails.mockReturnValue({ - bookingId, - offenderNo: 'A12345', - firstName: 'john', - lastName: 'doe', - assignedLivingUnitDesc: 'Cell 1', - }) - - // @ts-expect-error ts-migrate(2339) FIXME: Property 'getAgencyDetails' does not exist on type... Remove this comment to see the full error message - prisonApi.getAgencyDetails.mockReturnValue({ - description: 'Moorland', - }) - - // @ts-expect-error ts-migrate(2339) FIXME: Property 'getLocation' does not exist on type '{}'... Remove this comment to see the full error message - prisonApi.getLocation.mockReturnValue({ userDescription: 'Test location' }) - // @ts-expect-error ts-migrate(2339) FIXME: Property 'getCourtLocations' does not exist on typ... Remove this comment to see the full error message - whereaboutsApi.getCourtLocations.mockReturnValue([ - { id: 'LEICOURT-1', name: 'Leeds' }, - { id: 'LDNCOURT-1', name: 'London' }, - ]) - // @ts-expect-error ts-migrate(2339) FIXME: Property 'getExistingEventsForLocation' does not e... Remove this comment to see the full error message - existingEventsService.getExistingEventsForLocation.mockReturnValue(locationEvents) - - // @ts-expect-error ts-migrate(2339) FIXME: Property 'flash' does not exist on type '{ origina... Remove this comment to see the full error message - req.flash.mockImplementation(() => [appointmentDetails]) - - body = { - postAppointment: 'yes', - preAppointment: 'no', - court: 'LDNCOURT-1', - } - }) - - describe('index', () => { - it('should return correct links', async () => { - const { index } = prepostAppointmentsFactory({ - prisonApi, - appointmentsService, - whereaboutsApi, - // @ts-expect-error ts-migrate(2345) FIXME: Argument of type '{ prisonApi: {}; appointmentsSer... Remove this comment to see the full error message - logError: () => {}, - }) - - await index(req, res) - - // @ts-expect-error ts-migrate(2339) FIXME: Property 'render' does not exist on type '{ locals... Remove this comment to see the full error message - expect(res.render).toHaveBeenCalledWith( - 'prepostAppointments.njk', - expect.objectContaining({ - links: { - postAppointments: '/offenders/A12345/prepost-appointments', - cancel: '/offenders/A12345/prepost-appointments/cancel', - }, - }) - ) - }) - - it('should return locations', async () => { - const { index } = prepostAppointmentsFactory({ - prisonApi, - appointmentsService, - whereaboutsApi, - // @ts-expect-error ts-migrate(2345) FIXME: Argument of type '{ prisonApi: {}; appointmentsSer... Remove this comment to see the full error message - logError: () => {}, - }) - - await index(req, res) - - // @ts-expect-error ts-migrate(2339) FIXME: Property 'render' does not exist on type '{ locals... Remove this comment to see the full error message - expect(res.render).toHaveBeenCalledWith( - 'prepostAppointments.njk', - expect.objectContaining({ - locations: [{ value: 1, text: 'Room 3' }], - }) - ) - }) - - it('should return court locations', async () => { - const { index } = prepostAppointmentsFactory({ - prisonApi, - appointmentsService, - whereaboutsApi, - // @ts-expect-error ts-migrate(2345) FIXME: Argument of type '{ prisonApi: {}; appointmentsSer... Remove this comment to see the full error message - logError: () => {}, - }) - - await index(req, res) - - // @ts-expect-error ts-migrate(2339) FIXME: Property 'render' does not exist on type '{ locals... Remove this comment to see the full error message - expect(res.render).toHaveBeenCalledWith( - 'prepostAppointments.njk', - expect.objectContaining({ - courts: [ - { text: 'Leeds', value: 'LEICOURT-1' }, - { text: 'London', value: 'LDNCOURT-1' }, - { text: 'Other', value: 'other' }, - ], - }) - ) - }) - - it('should return no default form values', async () => { - const { index } = prepostAppointmentsFactory({ - prisonApi, - appointmentsService, - whereaboutsApi, - // @ts-expect-error ts-migrate(2345) FIXME: Argument of type '{ prisonApi: {}; appointmentsSer... Remove this comment to see the full error message - logError: () => {}, - }) - - await index(req, res) - - // @ts-expect-error ts-migrate(2339) FIXME: Property 'render' does not exist on type '{ locals... Remove this comment to see the full error message - expect(res.render).toHaveBeenCalledWith( - 'prepostAppointments.njk', - expect.objectContaining({ - formValues: {}, - }) - ) - }) - - it('should extract appointment details', async () => { - const { index } = prepostAppointmentsFactory({ - prisonApi, - appointmentsService, - whereaboutsApi, - // @ts-expect-error ts-migrate(2345) FIXME: Argument of type '{ prisonApi: {}; appointmentsSer... Remove this comment to see the full error message - logError: () => {}, - }) - - await index(req, res) - - // @ts-expect-error ts-migrate(2339) FIXME: Property 'render' does not exist on type '{ locals... Remove this comment to see the full error message - expect(res.render).toHaveBeenCalledWith( - 'prepostAppointments.njk', - expect.objectContaining({ - date: '10/10/2017', - details: { - courtHearingEndTime: '14:00', - courtHearingStartTime: '11:00', - date: '10 October 2017', - location: 'Room 3', - name: 'Doe, John', - }, - }) - ) - }) - - it('should throw and log an error when appointment details are missing from flash', async () => { - const logError = jest.fn() - // @ts-expect-error ts-migrate(2345) FIXME: Argument of type '{ prisonApi: {}; appointmentsSer... Remove this comment to see the full error message - const { index } = prepostAppointmentsFactory({ prisonApi, appointmentsService, logError }) - - // @ts-expect-error ts-migrate(2339) FIXME: Property 'flash' does not exist on type '{ origina... Remove this comment to see the full error message - req.flash.mockImplementation(() => []) - // @ts-expect-error ts-migrate(2339) FIXME: Property 'status' does not exist on type '{ locals... Remove this comment to see the full error message - res.status = jest.fn() - // @ts-expect-error ts-migrate(2339) FIXME: Property 'body' does not exist on type '{ original... Remove this comment to see the full error message - req.body = body - - await expect(index(req, res)).rejects.toThrowError(new Error('Appointment details are missing')) - // @ts-expect-error ts-migrate(2339) FIXME: Property 'redirectUrl' does not exist on type '{}'... Remove this comment to see the full error message - expect(res.locals.redirectUrl).toBe('/offenders/A12345/add-appointment') - }) - - it('should display locationEvents is present in flash', async () => { - // @ts-expect-error ts-migrate(2339) FIXME: Property 'flash' does not exist on type '{ origina... Remove this comment to see the full error message - req.flash.mockImplementation(() => [ - { - ...appointmentDetails, - preAppointment: { - locationId: 1, - }, - postAppointment: { - locationId: 1, - }, - }, - ]) - - const { index } = prepostAppointmentsFactory({ - prisonApi, - appointmentsService, - existingEventsService, - whereaboutsApi, - // @ts-expect-error ts-migrate(2345) FIXME: Argument of type '{ prisonApi: {}; appointmentsSer... Remove this comment to see the full error message - logError: () => {}, - }) - - await index(req, res) - - // @ts-expect-error ts-migrate(2339) FIXME: Property 'render' does not exist on type '{ locals... Remove this comment to see the full error message - expect(res.render).toHaveBeenCalledWith( - 'prepostAppointments.njk', - expect.objectContaining({ - locationEvents: { - postAppointment: { - events: [ - { endTime: '13:00', eventDescription: 'Doctors - An appointment', locationId: 3, startTime: '12:00' }, - ], - locationName: 'Test location', - }, - preAppointment: { - events: [ - { endTime: '13:00', eventDescription: 'Doctors - An appointment', locationId: 3, startTime: '12:00' }, - ], - locationName: 'Test location', - }, - }, - }) - ) - }) - }) - - describe('post', () => { - it('should redirect to the court location page when other has been selected', async () => { - const { post } = prepostAppointmentsFactory({ - prisonApi, - appointmentsService, - whereaboutsApi, - // @ts-expect-error ts-migrate(2345) FIXME: Argument of type '{ prisonApi: {}; appointmentsSer... Remove this comment to see the full error message - logError: () => {}, - }) - - // @ts-expect-error ts-migrate(2339) FIXME: Property 'body' does not exist on type '{ original... Remove this comment to see the full error message - req.body = { - court: 'other', - postAppointment: 'no', - preAppointment: 'no', - } - - await post(req, res) - - // @ts-expect-error ts-migrate(2339) FIXME: Property 'flash' does not exist on type '{ origina... Remove this comment to see the full error message - expect(req.flash).toHaveBeenCalledWith('appointmentDetails', { - appointmentType: 'VLB', - appointmentTypeDescription: 'Videolink', - bookingId: 1, - comment: 'Test', - court: 'Other', - date: '10/10/2019', - endTime: '2017-10-10T14:00', - firstName: 'john', - lastName: 'doe', - locationDescription: 'Room 3', - locationId: 1, - locationTypes: [{ text: 'Room 3', value: 1 }], - offenderNo: 'A12345', - postAppointment: { - required: 'no', - }, - preAppointment: { - required: 'no', - }, - recurring: 'No', - startTime: '2017-10-10T11:00', - }) - // @ts-expect-error ts-migrate(2339) FIXME: Property 'render' does not exist on type '{ locals... Remove this comment to see the full error message - expect(res.render).toHaveBeenCalledWith('enterCustomCourt.njk', { - date: '10/10/2019', - errors: [], - formValues: { - court: 'other', - postAppointment: 'no', - preAppointment: 'no', - }, - cancel: '/offenders/A12345/prepost-appointments', - }) - }) - - it('should validate the presence of other court', async () => { - const { post } = prepostAppointmentsFactory({ - prisonApi, - appointmentsService, - whereaboutsApi, - // @ts-expect-error ts-migrate(2345) FIXME: Argument of type '{ prisonApi: {}; appointmentsSer... Remove this comment to see the full error message - logError: () => {}, - }) - - // @ts-expect-error ts-migrate(2339) FIXME: Property 'body' does not exist on type '{ original... Remove this comment to see the full error message - req.body = { - otherCourtForm: 'true', - postAppointment: 'no', - preAppointment: 'no', - } - - await post(req, res) - - // @ts-expect-error ts-migrate(2339) FIXME: Property 'render' does not exist on type '{ locals... Remove this comment to see the full error message - expect(res.render).toHaveBeenCalledWith( - 'enterCustomCourt.njk', - expect.objectContaining({ - errors: [{ href: '#otherCourt', text: 'Enter the name of the court' }], - }) - ) - }) - - it('should validate presence of room locations', async () => { - const { post } = prepostAppointmentsFactory({ - prisonApi, - appointmentsService, - whereaboutsApi, - // @ts-expect-error ts-migrate(2345) FIXME: Argument of type '{ prisonApi: {}; appointmentsSer... Remove this comment to see the full error message - logError: () => {}, - }) - - // @ts-expect-error ts-migrate(2339) FIXME: Property 'body' does not exist on type '{ original... Remove this comment to see the full error message - req.body = { - ...body, - preAppointmentLocation: null, - postAppointmentLocation: undefined, - postAppointment: 'yes', - preAppointment: 'yes', - } - - await post(req, res) - - // @ts-expect-error ts-migrate(2339) FIXME: Property 'render' does not exist on type '{ locals... Remove this comment to see the full error message - expect(res.render).toHaveBeenCalledWith( - 'prepostAppointments.njk', - expect.objectContaining({ - errors: [ - { text: 'Select a room for the pre-court hearing briefing', href: '#preAppointmentLocation' }, - { text: 'Select a room for the post-court hearing briefing', href: '#postAppointmentLocation' }, - ], - }) - ) - }) - - it('should validate presence of court', async () => { - const { post } = prepostAppointmentsFactory({ - prisonApi, - appointmentsService, - existingEventsService, - whereaboutsApi, - // @ts-expect-error ts-migrate(2345) FIXME: Argument of type '{ prisonApi: {}; appointmentsSer... Remove this comment to see the full error message - logError: () => {}, - }) - - // @ts-expect-error ts-migrate(2339) FIXME: Property 'body' does not exist on type '{ original... Remove this comment to see the full error message - req.body = { - ...body, - postAppointment: 'no', - preAppointment: 'no', - court: null, - } - - await post(req, res) - - // @ts-expect-error ts-migrate(2339) FIXME: Property 'render' does not exist on type '{ locals... Remove this comment to see the full error message - expect(res.render).toHaveBeenCalledWith( - 'prepostAppointments.njk', - expect.objectContaining({ - errors: [{ href: '#court', text: 'Select which court the hearing is for' }], - }) - ) - }) - - it('should validate presence of room locations when "no" have been selected', async () => { - const { post } = prepostAppointmentsFactory({ - prisonApi, - appointmentsService, - whereaboutsApi, - // @ts-expect-error ts-migrate(2345) FIXME: Argument of type '{ prisonApi: {}; appointmentsSer... Remove this comment to see the full error message - logError: () => {}, - }) - - // @ts-expect-error ts-migrate(2339) FIXME: Property 'body' does not exist on type '{ original... Remove this comment to see the full error message - req.body = { - ...body, - preAppointmentLocation: null, - postAppointment: 'no', - preAppointment: 'yes', - } - - await post(req, res) - - // @ts-expect-error ts-migrate(2339) FIXME: Property 'render' does not exist on type '{ locals... Remove this comment to see the full error message - expect(res.render).toHaveBeenCalledWith( - 'prepostAppointments.njk', - expect.objectContaining({ - errors: [{ text: 'Select a room for the pre-court hearing briefing', href: '#preAppointmentLocation' }], - }) - ) - }) - - it('should return selected form values on validation errors', async () => { - const { post } = prepostAppointmentsFactory({ - prisonApi, - appointmentsService, - existingEventsService, - whereaboutsApi, - // @ts-expect-error ts-migrate(2345) FIXME: Argument of type '{ prisonApi: {}; appointmentsSer... Remove this comment to see the full error message - logError: () => {}, - }) - - // @ts-expect-error ts-migrate(2339) FIXME: Property 'body' does not exist on type '{ original... Remove this comment to see the full error message - req.body = { - preAppointment: 'yes', - preAppointmentLocation: 2, - postAppointment: 'yes', - court: 'london', - } - - await post(req, res) - - // @ts-expect-error ts-migrate(2339) FIXME: Property 'render' does not exist on type '{ locals... Remove this comment to see the full error message - expect(res.render).toHaveBeenCalledWith( - 'prepostAppointments.njk', - expect.objectContaining({ - formValues: { - preAppointment: 'yes', - preAppointmentLocation: 2, - postAppointment: 'yes', - court: 'london', - }, - }) - ) - }) - - it('should return locations, links and summary details on validation errors', async () => { - const { post } = prepostAppointmentsFactory({ - prisonApi, - appointmentsService, - existingEventsService, - whereaboutsApi, - // @ts-expect-error ts-migrate(2345) FIXME: Argument of type '{ prisonApi: {}; appointmentsSer... Remove this comment to see the full error message - logError: () => {}, - }) - - // @ts-expect-error ts-migrate(2339) FIXME: Property 'body' does not exist on type '{ original... Remove this comment to see the full error message - req.body = body - - await post(req, res) - - // @ts-expect-error ts-migrate(2339) FIXME: Property 'render' does not exist on type '{ locals... Remove this comment to see the full error message - expect(res.render).toHaveBeenCalledWith( - 'prepostAppointments.njk', - expect.objectContaining({ - links: { - postAppointments: `/offenders/A12345/prepost-appointments`, - cancel: '/offenders/A12345/prepost-appointments/cancel', - }, - locations: [{ value: 1, text: 'Room 3' }], - details: { - courtHearingEndTime: '14:00', - courtHearingStartTime: '11:00', - date: '10 October 2017', - location: 'Room 3', - name: 'Doe, John', - }, - }) - ) - }) - - it('should throw and log an error when appointment details are missing from flash', async () => { - const logError = jest.fn() - // @ts-expect-error ts-migrate(2345) FIXME: Argument of type '{ prisonApi: {}; appointmentsSer... Remove this comment to see the full error message - const { post } = prepostAppointmentsFactory({ prisonApi, appointmentsService, logError }) - - // @ts-expect-error ts-migrate(2339) FIXME: Property 'flash' does not exist on type '{ origina... Remove this comment to see the full error message - req.flash.mockImplementation(() => []) - // @ts-expect-error ts-migrate(2339) FIXME: Property 'body' does not exist on type '{ original... Remove this comment to see the full error message - req.body = body - - await expect(post(req, res)).rejects.toThrowError(new Error('Appointment details are missing')) - - // @ts-expect-error ts-migrate(2339) FIXME: Property 'redirectUrl' does not exist on type '{}'... Remove this comment to see the full error message - expect(res.locals.redirectUrl).toBe('/offenders/A12345/add-appointment') - }) - - it('should pack appointment details back into flash before rendering', async () => { - const { post } = prepostAppointmentsFactory({ - prisonApi, - appointmentsService, - whereaboutsApi, - existingEventsService, - // @ts-expect-error ts-migrate(2345) FIXME: Argument of type '{ prisonApi: {}; appointmentsSer... Remove this comment to see the full error message - logError: () => {}, - }) - // @ts-expect-error ts-migrate(2339) FIXME: Property 'body' does not exist on type '{ original... Remove this comment to see the full error message - req.body = body - - await post(req, res) - - // @ts-expect-error ts-migrate(2339) FIXME: Property 'flash' does not exist on type '{ origina... Remove this comment to see the full error message - expect(req.flash).toHaveBeenCalledWith('appointmentDetails', appointmentDetails) - }) - - it('should raise a telemetry event on appointment creation', async () => { - // @ts-expect-error ts-migrate(2339) FIXME: Property 'getAgencyDetails' does not exist on type... Remove this comment to see the full error message - prisonApi.getAgencyDetails.mockReturnValue({ - description: 'Leeds', - }) - - const raiseAnalyticsEvent = jest.fn() - - const { post } = prepostAppointmentsFactory({ - prisonApi, - hmppsManageUsersApi, - appointmentsService, - whereaboutsApi, - existingEventsService, - raiseAnalyticsEvent, - // @ts-expect-error ts-migrate(2345) FIXME: Argument of type '{ prisonApi: {}; hmppsManageUsersApi: {}; a... Remove this comment to see the full error message - logError: () => {}, - }) - - // @ts-expect-error ts-migrate(2339) FIXME: Property 'body' does not exist on type '{ original... Remove this comment to see the full error message - req.body = { - ...body, - preAppointment: 'no', - postAppointment: 'no', - } - // @ts-expect-error ts-migrate(2339) FIXME: Property 'redirect' does not exist on type '{ loca... Remove this comment to see the full error message - res.redirect = () => {} - - await post(req, res) - - // @ts-expect-error ts-migrate(2339) FIXME: Property 'getAgencyDetails' does not exist on type... Remove this comment to see the full error message - expect(prisonApi.getAgencyDetails).toHaveBeenCalledWith({}, 'LEI') - expect(raiseAnalyticsEvent).toHaveBeenCalledWith('VLB Appointments', 'Video link booked', 'Leeds - London') - }) - - describe('Events at location', () => { - it('should return events at the pre appointment location on validation errors', async () => { - const { post } = prepostAppointmentsFactory({ - prisonApi, - appointmentsService, - existingEventsService, - whereaboutsApi, - // @ts-expect-error ts-migrate(2345) FIXME: Argument of type '{ prisonApi: {}; appointmentsSer... Remove this comment to see the full error message - logError: () => {}, - }) - - // @ts-expect-error ts-migrate(2339) FIXME: Property 'body' does not exist on type '{ original... Remove this comment to see the full error message - req.body = { - ...body, - preAppointmentLocation: 1, - } - - await post(req, res) - - // @ts-expect-error ts-migrate(2339) FIXME: Property 'getExistingEventsForLocation' does not e... Remove this comment to see the full error message - expect(existingEventsService.getExistingEventsForLocation).toHaveBeenCalledWith({}, 'LEI', 1, '10/10/2019') - - // @ts-expect-error ts-migrate(2339) FIXME: Property 'render' does not exist on type '{ locals... Remove this comment to see the full error message - expect(res.render).toHaveBeenCalledWith( - 'prepostAppointments.njk', - expect.objectContaining({ - locationEvents: { - preAppointment: { - locationName: 'Test location', - events: locationEvents, - }, - }, - }) - ) - }) - - it('should return events at the post appointment location on validation errors', async () => { - const { post } = prepostAppointmentsFactory({ - prisonApi, - appointmentsService, - existingEventsService, - whereaboutsApi, - // @ts-expect-error ts-migrate(2345) FIXME: Argument of type '{ prisonApi: {}; appointmentsSer... Remove this comment to see the full error message - logError: () => {}, - }) - - // @ts-expect-error ts-migrate(2339) FIXME: Property 'body' does not exist on type '{ original... Remove this comment to see the full error message - req.body = { - ...body, - preAppointmentLocation: null, - postAppointmentLocation: 1, - postAppointment: 'yes', - preAppointment: 'yes', - } - - await post(req, res) - - // @ts-expect-error ts-migrate(2339) FIXME: Property 'getExistingEventsForLocation' does not e... Remove this comment to see the full error message - expect(existingEventsService.getExistingEventsForLocation).toHaveBeenCalledWith({}, 'LEI', 1, '10/10/2019') - - // @ts-expect-error ts-migrate(2339) FIXME: Property 'render' does not exist on type '{ locals... Remove this comment to see the full error message - expect(res.render).toHaveBeenCalledWith( - 'prepostAppointments.njk', - expect.objectContaining({ - locationEvents: { - postAppointment: { - locationName: 'Test location', - events: locationEvents, - }, - }, - }) - ) - }) - }) - - describe('Create appointments', () => { - beforeEach(() => { - // @ts-expect-error ts-migrate(2339) FIXME: Property 'flash' does not exist on type '{ origina... Remove this comment to see the full error message - req.flash.mockImplementation(() => [ - { - ...appointmentDetails, - startTime: '2017-10-10T11:00', - endTime: '2017-10-10T14:00', - }, - ]) - - // @ts-expect-error ts-migrate(2339) FIXME: Property 'body' does not exist on type '{ original... Remove this comment to see the full error message - req.body = { - postAppointment: 'yes', - preAppointment: 'yes', - preAppointmentLocation: '2', - postAppointmentLocation: '3', - court: 'LDNCOURT-1', - } - - // @ts-expect-error ts-migrate(2339) FIXME: Property 'redirect' does not exist on type '{ loca... Remove this comment to see the full error message - res.redirect = jest.fn() - }) - - it('should create booking', async () => { - const { post } = prepostAppointmentsFactory({ - prisonApi, - hmppsManageUsersApi, - notifyClient, - appointmentsService, - existingEventsService, - whereaboutsApi, - raiseAnalyticsEvent: () => {}, - }) - - await post(req, res) - - // @ts-expect-error ts-migrate(2339) FIXME: Property 'addVideoLinkBooking' does not exist on t... Remove this comment to see the full error message - expect(whereaboutsApi.addVideoLinkBooking).toHaveBeenCalledWith( - {}, - { - bookingId: 1, - comment: 'Test', - court: undefined, - courtId: 'LDNCOURT-1', - madeByTheCourt: false, - pre: { - startTime: '2017-10-10T10:45:00', - endTime: '2017-10-10T11:00:00', - locationId: 2, - }, - main: { - startTime: '2017-10-10T11:00', - endTime: '2017-10-10T14:00', - locationId: 1, - }, - post: { - startTime: '2017-10-10T14:00', - endTime: '2017-10-10T14:15:00', - locationId: 3, - }, - } - ) - }) - - it('when other court selected it should create booking', async () => { - const { post } = prepostAppointmentsFactory({ - prisonApi, - hmppsManageUsersApi, - notifyClient, - appointmentsService, - existingEventsService, - whereaboutsApi, - raiseAnalyticsEvent: () => {}, - }) - - // @ts-expect-error ts-migrate(2339) FIXME: Property 'body' does not exist on type '{ original... Remove this comment to see the full error message - await post({ ...req, body: { ...req.body, otherCourt: 'Mega Court' } }, res) - - // @ts-expect-error ts-migrate(2339) FIXME: Property 'addVideoLinkBooking' does not exist on t... Remove this comment to see the full error message - expect(whereaboutsApi.addVideoLinkBooking).toHaveBeenCalledWith( - {}, - { - bookingId: 1, - comment: 'Test', - court: 'Mega Court', - courtId: undefined, - madeByTheCourt: false, - pre: { - startTime: '2017-10-10T10:45:00', - endTime: '2017-10-10T11:00:00', - locationId: 2, - }, - main: { - startTime: '2017-10-10T11:00', - endTime: '2017-10-10T14:00', - locationId: 1, - }, - post: { - startTime: '2017-10-10T14:00', - endTime: '2017-10-10T14:15:00', - locationId: 3, - }, - } - ) - }) - - it('should not request pre or post appointments when no has been selected', async () => { - const { post } = prepostAppointmentsFactory({ - prisonApi, - hmppsManageUsersApi, - notifyClient, - appointmentsService, - existingEventsService, - whereaboutsApi, - raiseAnalyticsEvent: () => {}, - }) - - // @ts-expect-error ts-migrate(2339) FIXME: Property 'body' does not exist on type '{ original... Remove this comment to see the full error message - req.body = { - postAppointment: 'no', - preAppointment: 'no', - court: 'LDNCOURT-1', - } - await post(req, res) - - // @ts-expect-error ts-migrate(2339) FIXME: Property 'addVideoLinkBooking' does not exist on t... Remove this comment to see the full error message - expect(whereaboutsApi.addVideoLinkBooking.mock.calls.length).toBe(1) - - // @ts-expect-error ts-migrate(2339) FIXME: Property 'addVideoLinkBooking' does not exist on t... Remove this comment to see the full error message - expect(whereaboutsApi.addVideoLinkBooking).toHaveBeenCalledWith( - {}, - { - bookingId: 1, - comment: 'Test', - court: undefined, - courtId: 'LDNCOURT-1', - madeByTheCourt: false, - main: { - startTime: '2017-10-10T11:00', - endTime: '2017-10-10T14:00', - locationId: 1, - }, - } - ) - }) - - it('should place pre and post appointment details into flash', async () => { - const { post } = prepostAppointmentsFactory({ - prisonApi, - hmppsManageUsersApi, - notifyClient, - appointmentsService, - existingEventsService, - whereaboutsApi, - raiseAnalyticsEvent: () => {}, - }) - - // @ts-expect-error ts-migrate(2339) FIXME: Property 'body' does not exist on type '{ original... Remove this comment to see the full error message - req.body = { - postAppointment: 'yes', - preAppointment: 'yes', - preAppointmentLocation: 1, - postAppointmentLocation: 2, - court: 'london', - } - - await post(req, res) - - // @ts-expect-error ts-migrate(2339) FIXME: Property 'flash' does not exist on type '{ origina... Remove this comment to see the full error message - expect(req.flash).toHaveBeenCalledWith( - 'appointmentDetails', - expect.objectContaining({ - preAppointment: { - required: 'yes', - endTime: '2017-10-10T11:00:00', - locationId: 1, - startTime: '2017-10-10T10:45:00', - }, - postAppointment: { - required: 'yes', - endTime: '2017-10-10T14:15:00', - locationId: 2, - startTime: '2017-10-10T14:00', - }, - }) - ) - }) - - it('should redirect to confirmation page', async () => { - notifyClient.sendEmail = jest.fn() - - const { post } = prepostAppointmentsFactory({ - prisonApi, - hmppsManageUsersApi, - appointmentsService, - whereaboutsApi, - // @ts-expect-error ts-migrate(2345) FIXME: Argument of type '{ prisonApi: {}; hmppsManageUsersApi: {}; a... Remove this comment to see the full error message - logError: () => {}, - raiseAnalyticsEvent: () => {}, - }) - - // @ts-expect-error ts-migrate(2339) FIXME: Property 'body' does not exist on type '{ original... Remove this comment to see the full error message - req.body = { - postAppointment: 'no', - preAppointment: 'no', - court: 'london', - } - await post(req, res) - - // @ts-expect-error ts-migrate(2339) FIXME: Property 'redirect' does not exist on type '{ loca... Remove this comment to see the full error message - expect(res.redirect).toHaveBeenCalledWith('/offenders/A12345/confirm-appointment') - expect(notifyClient.sendEmail).not.toHaveBeenCalled() - }) - - it('should try to send email with prison template when prison user has email', async () => { - notifyClient.sendEmail = jest.fn() - - // @ts-expect-error ts-migrate(2339) FIXME: Property 'userEmail' does not exist on type '{}'. - hmppsManageUsersApi.userEmail.mockReturnValue({ - email: 'test@example.com', - }) - - notifyClient.sendEmail = jest.fn() - - const { post } = prepostAppointmentsFactory({ - prisonApi, - hmppsManageUsersApi, - notifyClient, - appointmentsService, - existingEventsService, - whereaboutsApi, - // @ts-expect-error ts-migrate(2345) FIXME: Argument of type '{ prisonApi: {}; hmppsManageUsersApi: {}; n... Remove this comment to see the full error message - logError: () => {}, - raiseAnalyticsEvent: () => {}, - }) - - // @ts-expect-error ts-migrate(2339) FIXME: Property 'body' does not exist on type '{ original... Remove this comment to see the full error message - req.body = { - postAppointment: 'no', - preAppointment: 'no', - court: 'london', - } - await post(req, res) - - const personalisation = { - startTime: Time(appointmentDetails.startTime), - endTime: Time(appointmentDetails.endTime), - comments: appointmentDetails.comment, - date: '10 October 2019', - firstName: 'John', - lastName: 'Doe', - offenderNo: appointmentDetails.offenderNo, - location: 'Room 3', - postAppointmentInfo: 'None requested', - preAppointmentInfo: 'None requested', - prison: 'Moorland', - } - - expect(notifyClient.sendEmail).toHaveBeenCalledWith( - config.notifications.confirmBookingPrisonTemplateId, - 'test@example.com', - { - personalisation, - reference: null, - } - ) - }) - }) - }) -}) diff --git a/backend/tests/whereaboutsMaintenanceMode.test.ts b/backend/tests/whereaboutsMaintenanceMode.test.ts index 6fa605ac5..e2a07d764 100644 --- a/backend/tests/whereaboutsMaintenanceMode.test.ts +++ b/backend/tests/whereaboutsMaintenanceMode.test.ts @@ -95,20 +95,6 @@ describe('Whereabouts maintenance mode flag is true', () => { }) }) - it('should display maintenance page when user navigates to /offenders/:offenderNo/prepost-appointments', () => { - return supertest(app) - .get('/offenders/ABC123/prepost-appointments') - .expect(200) - .expect(() => { - expect(res.render).toHaveBeenCalledWith( - 'maintenancePage.njk', - expect.objectContaining({ - title: 'Appointment details', - }) - ) - }) - }) - it('should display maintenance page when user navigates to /appointment-details/:id/confirm-deletion', () => { return supertest(app) .get('/appointment-details/123/confirm-deletion') diff --git a/integration-tests/integration/appointments/addAppointment.cy.js b/integration-tests/integration/appointments/addAppointment.cy.js index 7c03ad924..70a5b7776 100644 --- a/integration-tests/integration/appointments/addAppointment.cy.js +++ b/integration-tests/integration/appointments/addAppointment.cy.js @@ -3,9 +3,6 @@ const moment = require('moment') const offenderBasicDetails = require('../../mockApis/responses/offenderBasicDetails.json') const AddAppointmentPage = require('../../pages/appointments/addAppointmentPage') const ConfirmSingleAppointmentPage = require('../../pages/appointments/confirmSingleAppointmentPage') -const PrePostAppointmentsPage = require('../../pages/appointments/prePostAppointmentsPage') -const OtherCourtPage = require('../../pages/appointments/otherCourtPage') -const ConfirmVideoLinkPrisonPage = require('../../pages/appointments/confirmVideoLinkPrisonPage') context('A user can add an appointment', () => { beforeEach(() => { @@ -17,10 +14,7 @@ context('A user can add an appointment', () => { }) const offenderNo = 'A12345' cy.task('stubOffenderBasicDetails', offenderBasicDetails) - cy.task('stubAppointmentTypes', [ - { code: 'ACTI', description: 'Activities' }, - { code: 'VLB', description: 'Video Link Booking' }, - ]) + cy.task('stubAppointmentTypes', [{ code: 'ACTI', description: 'Activities' }]) cy.task('stubAppointmentsAtAgency', 'MDI', []) cy.task('stubVisitsAtAgency', 'MDI', []) cy.task('stubCreateAppointment') @@ -150,214 +144,4 @@ context('A user can add an appointment', () => { form.submitButton().click() ConfirmSingleAppointmentPage.verifyOnPage(`John Smith’s`) }) - - it('A user can successfully add a video link booking', () => { - const addAppointmentPage = AddAppointmentPage.verifyOnPage('John Smith') - const form = addAppointmentPage.form() - - form.appointmentType().select('VLB') - form.location().select('1') - form.startTimeHours().select('22') - form.startTimeMinutes().select('55') - form.endTimeHours().select('23') - form.endTimeMinutes().select('55') - form.recurringNo().click() - form.comments().type('Test comment') - form.date().click() - addAppointmentPage.todaysDate().click() - form.submitButton().click() - - const prePostAppointmentsPage = PrePostAppointmentsPage.verifyOnPage() - const prePostForm = prePostAppointmentsPage.form() - - prePostForm.preAppointmentYes().click() - prePostForm.preAppointmentLocation().select('1') - prePostForm.postAppointmentNo().click() - prePostForm.court().select('Leeds') - prePostForm.submitButton().click() - - const confirmVideoLinkPrisonPage = ConfirmVideoLinkPrisonPage.verifyOnPage() - confirmVideoLinkPrisonPage.courtLocation().contains('Leeds') - confirmVideoLinkPrisonPage.searchForAnotherPrisoner().exists() - confirmVideoLinkPrisonPage.backToPrisonerProfile().exists('A12345') - - cy.task('getBookingRequest').then((request) => { - expect(request).to.deep.equal({ - bookingId: 14, - courtId: 'LDS', - comment: 'Test comment', - madeByTheCourt: false, - pre: { - locationId: 1, - startTime: moment().format(`YYYY-MM-DD[T22:40:00]`), - endTime: moment().format(`YYYY-MM-DD[T22:55:00]`), - }, - main: { - locationId: 1, - startTime: moment().format(`YYYY-MM-DD[T22:55:00]`), - endTime: moment().format(`YYYY-MM-DD[T23:55:00]`), - }, - }) - }) - }) - - it('Correct validation errors for video link bookings', () => { - const addAppointmentPage = AddAppointmentPage.verifyOnPage('John Smith') - const form = addAppointmentPage.form() - - form.appointmentType().select('VLB') - form.location().select('1') - form.startTimeHours().select('22') - form.startTimeMinutes().select('55') - form.endTimeHours().select('23') - form.endTimeMinutes().select('55') - form.recurringNo().click() - form.comments().type('Test comment') - form.date().click() - addAppointmentPage.todaysDate().click() - form.submitButton().click() - - const prePostAppointmentsPage = PrePostAppointmentsPage.verifyOnPage() - const prePostForm = prePostAppointmentsPage.form() - - prePostForm.preAppointmentYes().click() - prePostForm.postAppointmentYes().click() - prePostForm.submitButton().click() - prePostAppointmentsPage - .errorSummary() - .find('li') - .then(($errorItems) => { - expect($errorItems.get(0).innerText).to.contain('Select a room for the pre-court hearing briefing') - expect($errorItems.get(1).innerText).to.contain('Select a room for the post-court hearing briefing') - expect($errorItems.get(2).innerText).to.contain('Select which court the hearing is for') - }) - }) - - it('Should allow the user to enter custom court entry', () => { - const addAppointmentPage = AddAppointmentPage.verifyOnPage('John Smith') - const form = addAppointmentPage.form() - - form.appointmentType().select('VLB') - form.location().select('1') - form.startTimeHours().select('22') - form.startTimeMinutes().select('55') - form.endTimeHours().select('23') - form.endTimeMinutes().select('55') - form.recurringNo().click() - form.comments().type('Test comment') - form.date().click() - addAppointmentPage.todaysDate().click() - form.submitButton().click() - - const prePostAppointmentsPage = PrePostAppointmentsPage.verifyOnPage() - const prePostForm = prePostAppointmentsPage.form() - - prePostForm.preAppointmentYes().click() - prePostForm.preAppointmentLocation().select('1') - prePostForm.postAppointmentNo().click() - prePostForm.court().select('Other') - prePostForm.submitButton().click() - - const otherCourtPage = OtherCourtPage.verifyOnPage() - const otherCourtForm = otherCourtPage.form() - - otherCourtForm.otherCourt().type('test') - otherCourtForm.submitButton().click() - - const confirmVideoLinkPrisonPage = ConfirmVideoLinkPrisonPage.verifyOnPage() - confirmVideoLinkPrisonPage.courtLocation().contains('test') - - cy.task('getBookingRequest').then((request) => { - expect(request).to.deep.equal({ - bookingId: 14, - court: 'test', - comment: 'Test comment', - madeByTheCourt: false, - pre: { - locationId: 1, - startTime: moment().format(`YYYY-MM-DD[T22:40:00]`), - endTime: moment().format(`YYYY-MM-DD[T22:55:00]`), - }, - main: { - locationId: 1, - startTime: moment().format(`YYYY-MM-DD[T22:55:00]`), - endTime: moment().format(`YYYY-MM-DD[T23:55:00]`), - }, - }) - }) - }) - - it('Should display correct error messages on other court form page', () => { - const addAppointmentPage = AddAppointmentPage.verifyOnPage('John Smith') - const form = addAppointmentPage.form() - - form.appointmentType().select('VLB') - form.location().select('1') - form.startTimeHours().select('22') - form.startTimeMinutes().select('55') - form.endTimeHours().select('23') - form.endTimeMinutes().select('55') - form.recurringNo().click() - form.comments().type('Test comment') - form.date().click() - addAppointmentPage.todaysDate().click() - form.submitButton().click() - - const prePostAppointmentsPage = PrePostAppointmentsPage.verifyOnPage() - const prePostForm = prePostAppointmentsPage.form() - - prePostForm.preAppointmentYes().click() - prePostForm.preAppointmentLocation().select('1') - prePostForm.postAppointmentNo().click() - prePostForm.court().select('Other') - prePostForm.submitButton().click() - - const otherCourtPage = OtherCourtPage.verifyOnPage() - const otherCourtForm = otherCourtPage.form() - - otherCourtForm.submitButton().click() - - otherCourtPage - .errorSummary() - .find('li') - .then(($errorItems) => { - expect($errorItems.get(0).innerText).to.contain('Enter the name of the court') - }) - }) - - it('Should retain previously entered information', () => { - const addAppointmentPage = AddAppointmentPage.verifyOnPage('John Smith') - const form = addAppointmentPage.form() - - form.appointmentType().select('VLB') - form.location().select('1') - form.startTimeHours().select('22') - form.startTimeMinutes().select('55') - form.endTimeHours().select('23') - form.endTimeMinutes().select('55') - form.recurringNo().click() - form.comments().type('Test comment') - form.date().click() - addAppointmentPage.todaysDate().click() - form.submitButton().click() - - const prePostAppointmentsPage = PrePostAppointmentsPage.verifyOnPage() - const prePostForm = prePostAppointmentsPage.form() - - prePostForm.preAppointmentYes().click() - prePostForm.preAppointmentLocation().select('1') - prePostForm.postAppointmentNo().click() - prePostForm.court().select('Other') - prePostForm.submitButton().click() - - const otherCourtPage = OtherCourtPage.verifyOnPage() - const otherCourtForm = otherCourtPage.form() - - otherCourtForm.cancelButton().click() - const returnPrePostAppointmentsPage = PrePostAppointmentsPage.verifyOnPage() - - returnPrePostAppointmentsPage.form().preAppointmentYes().should('be.checked') - returnPrePostAppointmentsPage.form().preAppointmentLocation().contains('1') - returnPrePostAppointmentsPage.form().postAppointmentNo().should('be.checked') - }) }) diff --git a/integration-tests/integration/appointments/addBulkAppointments.cy.js b/integration-tests/integration/appointments/addBulkAppointments.cy.js index aa48d8c21..67fe2052e 100644 --- a/integration-tests/integration/appointments/addBulkAppointments.cy.js +++ b/integration-tests/integration/appointments/addBulkAppointments.cy.js @@ -16,10 +16,7 @@ context('A user can add a bulk appointment', () => { cy.signIn() }) const offenderNo = 'A12345' - cy.task('stubAppointmentTypes', [ - { code: 'ACTI', description: 'Activities' }, - { code: 'VLB', description: 'Video Link Booking' }, - ]) + cy.task('stubAppointmentTypes', [{ code: 'ACTI', description: 'Activities' }]) cy.task('stubPostAppointments') cy.task('stubSchedules', { agency: 'MDI', diff --git a/integration-tests/pages/appointments/confirmVideoLinkPrisonPage.js b/integration-tests/pages/appointments/confirmVideoLinkPrisonPage.js deleted file mode 100644 index 13520ac88..000000000 --- a/integration-tests/pages/appointments/confirmVideoLinkPrisonPage.js +++ /dev/null @@ -1,44 +0,0 @@ -const page = require('../page') - -const confirmVideoLinkPrisonPage = () => - page('The video link has been booked', { - printMovementSlip: () => cy.get('a.govuk-link'), - backLink: () => cy.get('a.govuk-button'), - offenderName: () => cy.get('.qa-name-value'), - prison: () => cy.get('.qa-prison-value'), - room: () => cy.get('.qa-prisonRoom-value'), - date: () => cy.get('.qa-date-value'), - startTime: () => cy.get('.qa-courtHearingStartTime-value'), - endTime: () => cy.get('.qa-courtHearingEndTime-value'), - legalBriefingBefore: () => cy.get('.qa-preCourtHearingBriefing-value'), - legalBriefingAfter: () => cy.get('.qa-postCourtHearingBriefing-value'), - courtLocation: () => cy.get('.qa-courtLocation-value'), - searchForAnotherPrisoner: () => ({ - exists: () => - cy - .get("[data-qa='search-for-another-prisoner']") - .should('contain', 'Search for another prisoner') - .should('have.attr', 'href') - .then((href) => { - expect(href).to.equal('/') - }), - }), - backToPrisonerProfile: () => ({ - exists: (offenderNo) => - cy - .get("[data-qa='back-to-profile']") - .should('contain', 'Back to prisoner profile') - .should('have.attr', 'href') - .then((href) => { - expect(href).to.equal(`/prisoner/${offenderNo}`) - }), - }), - }) - -export default { - verifyOnPage: confirmVideoLinkPrisonPage, - goTo: (offenderNo) => { - cy.visit(`/offenders/${offenderNo}/confirm-appointment`) - return confirmVideoLinkPrisonPage() - }, -} diff --git a/integration-tests/pages/appointments/otherCourtPage.js b/integration-tests/pages/appointments/otherCourtPage.js deleted file mode 100644 index c3662d11d..000000000 --- a/integration-tests/pages/appointments/otherCourtPage.js +++ /dev/null @@ -1,15 +0,0 @@ -const page = require('../page') - -const otherCourtPage = () => - page('What is the name of the court?', { - form: () => ({ - otherCourt: () => cy.get('#otherCourt'), - submitButton: () => cy.get('button[type="submit"]'), - cancelButton: () => cy.get('.govuk-button--secondary'), - }), - errorSummary: () => cy.get('.govuk-error-summary'), - }) - -export default { - verifyOnPage: otherCourtPage, -} diff --git a/integration-tests/pages/appointments/prePostAppointmentsPage.js b/integration-tests/pages/appointments/prePostAppointmentsPage.js deleted file mode 100644 index 275d3481a..000000000 --- a/integration-tests/pages/appointments/prePostAppointmentsPage.js +++ /dev/null @@ -1,24 +0,0 @@ -const page = require('../page') - -const prePostAppointmentsPage = () => - page('Video link booking details', { - form: () => ({ - preAppointmentYes: () => cy.get('#preAppointment'), - preAppointmentLocation: () => cy.get('#preAppointmentLocation'), - preAppointmentNo: () => cy.get('#preAppointment-2'), - postAppointmentYes: () => cy.get('#postAppointment'), - postAppointmentLocation: () => cy.get('#postAppointmentLocation'), - postAppointmentNo: () => cy.get('#postAppointment-2'), - court: () => cy.get('#court'), - submitButton: () => cy.get('button[type="submit"]'), - }), - errorSummary: () => cy.get('.govuk-error-summary'), - }) - -export default { - verifyOnPage: prePostAppointmentsPage, - goTo: offenderNo => { - cy.visit(`/offenders/${offenderNo}/prepost-appointments`) - return prePostAppointmentsPage() - }, -} diff --git a/views/addAppointment/addAppointment.njk b/views/addAppointment/addAppointment.njk index ab246fa06..67dcabb63 100644 --- a/views/addAppointment/addAppointment.njk +++ b/views/addAppointment/addAppointment.njk @@ -97,7 +97,7 @@ text: "Type of appointment" }, classes: 'js-appointment-type', - items: appointmentTypes | addDefaultSelectedVale('Select') | setSelected(formValues.appointmentType), + items: appointmentTypes | filterNot('value', ['VLB', 'VLPM']) | addDefaultSelectedVale('Select') | setSelected(formValues.appointmentType), errorMessage: errors | findError('appointment-type') }) }} diff --git a/views/prepostAppointments.njk b/views/prepostAppointments.njk deleted file mode 100644 index 73ed76889..000000000 --- a/views/prepostAppointments.njk +++ /dev/null @@ -1,170 +0,0 @@ -{% extends "./partials/layout.njk" %} -{% from "govuk/components/button/macro.njk" import govukButton %} -{% from "govuk/components/panel/macro.njk" import govukPanel %} -{% from "govuk/components/summary-list/macro.njk" import govukSummaryList %} -{% from "govuk/components/select/macro.njk" import govukSelect %} -{% from "govuk/components/radios/macro.njk" import govukRadios %} -{% from "components/scheduledEvents/scheduledEvents.njk" import scheduledEvents %} -{% from "govuk/components/error-summary/macro.njk" import govukErrorSummary %} - -{% set title = "Video link booking details" %} -{% set mainClasses = "govuk-main-wrapper--l" %} - -{% macro appointment(params) %} -