From 6ea513ab99466c2f8364f141468ab7314754d034 Mon Sep 17 00:00:00 2001 From: danbenton-mojdt <113102670+danbenton-mojdt@users.noreply.github.com> Date: Fri, 19 Jan 2024 08:54:45 +0000 Subject: [PATCH] MAP-573 Add escorting hold question (#671) * MAP-573 Add escorting hold question with follow up * Bump node-sass to remove CVE * MAP-573 Remove followup question for escorting hold * MAP-573 Check escorting hold in integration tests * Remove Non-breaking space characters * MAP-573 Add typo fixes and missing question mark --- .../reportPages/check-your-answers.cy.js | 2 +- .../enter-use-of-force-details.cy.js | 11 +++++-- integration-tests/integration/seedData.js | 1 + .../createReport/useOfForceDetailsPage.js | 8 +++-- .../pages/sections/reportDetails.js | 1 + package-lock.json | 10 +++---- package.json | 2 +- server/config/forms/useOfForceDetailsForm.js | 2 ++ .../forms/useOfForceDetailsValidation.test.js | 29 +++++++++++++++++-- server/data/UseOfForceReport.ts | 1 + .../creatingReports/checkYourAnswers.test.ts | 1 + .../creatingReports/createReport.test.ts | 3 ++ .../drafts/reportStatusChecker.test.ts | 2 ++ server/services/reportDetailBuilder.test.ts | 1 + server/services/reportSummary.ts | 1 + .../addingStaff/delete-staff-member.html | 2 +- .../addingStaff/select-staff-member.html | 2 +- .../formPages/addingStaff/staff-involved.html | 2 +- .../addingStaff/staff-member-name.html | 2 +- .../addingStaff/staff-member-not-found.html | 2 +- server/views/formPages/formTemplate.html | 6 ++-- .../formPages/incident/changePrison.html | 2 +- server/views/formPages/incident/evidence.html | 4 +-- .../formPages/incident/incidentDetails.html | 2 +- .../incident/relocationAndInjuries.html | 4 +-- .../formPages/incident/report-cancelled.html | 2 +- .../incident/report-has-been-deleted.html | 2 +- .../incident/select-primary-uof-reason.html | 2 +- .../incident/select-uof-reasons.html | 2 +- .../formPages/incident/useOfForceDetails.html | 13 +++++++-- server/views/pages/check-your-answers.html | 2 +- .../add-involved-staff/already-exists.html | 2 +- .../add-involved-staff/missing.html | 2 +- .../add-involved-staff/unverified.html | 2 +- server/views/pages/error.html | 2 +- server/views/pages/report-sent.html | 2 +- server/views/pages/report-use-of-force.html | 2 +- server/views/pages/reportDetailMacro.njk | 10 +++++++ server/views/pages/search-for-prisoner.html | 2 +- .../statement/add-comment-to-statement.html | 2 +- .../pages/statement/already-removed.html | 2 +- .../pages/statement/check-your-statement.html | 2 +- .../statement/removal-already-requested.html | 2 +- .../pages/statement/removal-requested.html | 2 +- .../pages/statement/request-removal.html | 2 +- .../pages/statement/statement-submitted.html | 2 +- .../pages/statement/write-your-statement.html | 6 ++-- .../views/pages/statement/your-statement.html | 2 +- server/views/pages/your-report.html | 2 +- server/views/partials/incidentPage.html | 2 +- server/views/partials/layout.html | 2 +- 51 files changed, 121 insertions(+), 57 deletions(-) diff --git a/integration-tests/integration/reportPages/check-your-answers.cy.js b/integration-tests/integration/reportPages/check-your-answers.cy.js index 0edbb1de..8c96dace 100644 --- a/integration-tests/integration/reportPages/check-your-answers.cy.js +++ b/integration-tests/integration/reportPages/check-your-answers.cy.js @@ -134,7 +134,7 @@ context('Check your answers page', () => { selectUofReasonsPage.checkReason('FIGHT_BETWEEN_PRISONERS') selectUofReasonsPage.clickSave() const useOfForceDetailsPage = UseOfForceDetailsPage.verifyOnPage() - useOfForceDetailsPage.postiveCommunication().check('false') + useOfForceDetailsPage.positiveCommunication().check('false') operation(useOfForceDetailsPage) const revisitedAnswersPage = CheckAnswersPage.verifyOnPage() revisitedAnswersPage.positiveCommunicationUsed().contains(finalValue) diff --git a/integration-tests/integration/reportPages/enter-use-of-force-details.cy.js b/integration-tests/integration/reportPages/enter-use-of-force-details.cy.js index 1a9019db..05dc650b 100644 --- a/integration-tests/integration/reportPages/enter-use-of-force-details.cy.js +++ b/integration-tests/integration/reportPages/enter-use-of-force-details.cy.js @@ -20,7 +20,7 @@ context('Enter use of force details page', () => { selectUofReasonsPage.clickSaveAndContinue() const useOfForceDetailsPage = UseOfForceDetailsPage.verifyOnPage() - useOfForceDetailsPage.postiveCommunication().check('true') + useOfForceDetailsPage.positiveCommunication().check('true') useOfForceDetailsPage.personalProtectionTechniques().check('true') useOfForceDetailsPage.batonDrawn().check('true') useOfForceDetailsPage.batonUsed().check('true') @@ -28,6 +28,7 @@ context('Enter use of force details page', () => { useOfForceDetailsPage.pavaUsed().check('true') useOfForceDetailsPage.guidingHold().check('true') useOfForceDetailsPage.guidingHoldOfficersInvolved.check('2') + useOfForceDetailsPage.escortingHold().check('true') useOfForceDetailsPage.restraint().check('true') useOfForceDetailsPage.restraintPositions.check(restraintPositions) useOfForceDetailsPage.handcuffsApplied().check('true') @@ -48,6 +49,7 @@ context('Enter use of force details page', () => { batonUsed: true, guidingHold: true, guidingHoldOfficersInvolved: 2, + escortingHold: true, handcuffsApplied: true, pavaDrawn: true, pavaUsed: true, @@ -72,6 +74,7 @@ context('Enter use of force details page', () => { batonUsed: true, guidingHold: true, guidingHoldOfficersInvolved: 2, + escortingHold: true, handcuffsApplied: true, pavaDrawn: true, pavaUsed: true, @@ -92,7 +95,7 @@ context('Enter use of force details page', () => { cy.go('back') const useOfForceDetailsPage = UseOfForceDetailsPage.verifyOnPage() - useOfForceDetailsPage.postiveCommunication().should('have.value', 'true') + useOfForceDetailsPage.positiveCommunication().should('have.value', 'true') useOfForceDetailsPage.personalProtectionTechniques().should('have.value', 'true') useOfForceDetailsPage.batonDrawn().should('have.value', 'true') useOfForceDetailsPage.batonUsed().should('have.value', 'true') @@ -100,6 +103,7 @@ context('Enter use of force details page', () => { useOfForceDetailsPage.pavaUsed().should('have.value', 'true') useOfForceDetailsPage.guidingHold().should('have.value', 'true') useOfForceDetailsPage.guidingHoldOfficersInvolved.two().should('be.checked') + useOfForceDetailsPage.escortingHold().should('have.value', 'true') useOfForceDetailsPage.restraint().should('have.value', 'true') useOfForceDetailsPage.restraintPositions.standing().should('be.checked') useOfForceDetailsPage.restraintPositions.faceDown().should('not.be.checked') @@ -118,12 +122,13 @@ context('Enter use of force details page', () => { selectUofReasonsPage.clickSaveAndContinue() const useOfForceDetailsPage = UseOfForceDetailsPage.verifyOnPage() - useOfForceDetailsPage.postiveCommunication().check('true') + useOfForceDetailsPage.positiveCommunication().check('true') useOfForceDetailsPage.personalProtectionTechniques().check('true') useOfForceDetailsPage.pavaDrawn().check('true') useOfForceDetailsPage.pavaUsed().check('true') useOfForceDetailsPage.guidingHold().check('true') useOfForceDetailsPage.guidingHoldOfficersInvolved.check('2') + useOfForceDetailsPage.escortingHold().check('true') useOfForceDetailsPage.restraint().check('false') useOfForceDetailsPage.handcuffsApplied().check('true') useOfForceDetailsPage.painInducingTechniques().check('true') diff --git a/integration-tests/integration/seedData.js b/integration-tests/integration/seedData.js index f7e96011..998908a0 100644 --- a/integration-tests/integration/seedData.js +++ b/integration-tests/integration/seedData.js @@ -30,6 +30,7 @@ const expectedPayload = { pavaDrawn: true, batonDrawn: true, guidingHold: true, + escortingHold: true, restraint: true, handcuffsApplied: true, restraintPositions: ['STANDING', 'ON_BACK', 'FACE_DOWN', 'KNEELING'], diff --git a/integration-tests/pages/createReport/useOfForceDetailsPage.js b/integration-tests/pages/createReport/useOfForceDetailsPage.js index 26134ca8..8006b975 100644 --- a/integration-tests/pages/createReport/useOfForceDetailsPage.js +++ b/integration-tests/pages/createReport/useOfForceDetailsPage.js @@ -3,20 +3,21 @@ import RelocationAndInjuriesPage from './relocationAndInjuriesPage' const useOfForceDetailsPage = () => page('Use of force details', { - postiveCommunication: () => cy.get('[name="positiveCommunication"]'), + positiveCommunication: () => cy.get('[name="positiveCommunication"]'), personalProtectionTechniques: () => cy.get('[name="personalProtectionTechniques"]'), batonDrawn: () => cy.get('[name="batonDrawn"]'), batonUsed: () => cy.get('[name="batonUsed"]'), pavaDrawn: () => cy.get('[name="pavaDrawn"]'), pavaUsed: () => cy.get('[name="pavaUsed"]'), guidingHold: () => cy.get('[name="guidingHold"]'), - guidingHoldOfficersInvolved: { check: value => cy.get('[name="guidingHoldOfficersInvolved"]').check(value), one: () => cy.get('[name="guidingHoldOfficersInvolved"][value="1"]'), two: () => cy.get('[name="guidingHoldOfficersInvolved"][value="2"]'), }, + escortingHold: () => cy.get('[name="escortingHold"]'), + restraintPositions: { check: value => cy.get('#control-and-restraint [type="checkbox"]').check(value), standing: () => cy.get('#control-and-restraint [type="checkbox"][value="STANDING"]'), @@ -43,7 +44,7 @@ const useOfForceDetailsPage = () => }, fillForm() { - this.postiveCommunication().check('true') + this.positiveCommunication().check('true') this.personalProtectionTechniques().check('true') this.batonDrawn().check('true') this.batonUsed().check('true') @@ -51,6 +52,7 @@ const useOfForceDetailsPage = () => this.pavaUsed().check('true') this.guidingHold().check('true') this.guidingHoldOfficersInvolved.check('2') + this.escortingHold().check('true') this.restraint().check('true') this.restraintPositions.check(['STANDING', 'ON_BACK', 'FACE_DOWN', 'KNEELING']) this.handcuffsApplied().check('true') diff --git a/integration-tests/pages/sections/reportDetails.js b/integration-tests/pages/sections/reportDetails.js index d53b7f94..d8e92c05 100644 --- a/integration-tests/pages/sections/reportDetails.js +++ b/integration-tests/pages/sections/reportDetails.js @@ -62,6 +62,7 @@ module.exports = { cy.get('[data-qa="batonDrawn"]').contains('Yes and used') cy.get('[data-qa="pavaDrawn"]').contains('Yes and used') cy.get('[data-qa="guidingHold"]').contains('Yes - 2 officers involved') + cy.get('[data-qa="escortingHold"]').contains('Yes') cy.get('[data-qa="restraintUsed"]').contains('Yes - standing, on back (supine), on front (prone), kneeling') handcuffsApplied().contains('Yes') painInducingTechniques().contains('Yes') diff --git a/package-lock.json b/package-lock.json index aeabd236..143c6fe8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -84,7 +84,7 @@ "mocha": "^10.2.0", "mocha-junit-reporter": "^2.2.0", "nock": "^13.3.1", - "node-sass": "^8.0.0", + "node-sass": "^9.0.0", "nodemon": "^3.0.1", "prettier": "^2.8.8", "supertest": "^6.3.3", @@ -9633,9 +9633,9 @@ "dev": true }, "node_modules/node-sass": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/node-sass/-/node-sass-8.0.0.tgz", - "integrity": "sha512-jPzqCF2/e6JXw6r3VxfIqYc8tKQdkj5Z/BDATYyG6FL6b/LuYBNFGFVhus0mthcWifHm/JzBpKAd+3eXsWeK/A==", + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/node-sass/-/node-sass-9.0.0.tgz", + "integrity": "sha512-yltEuuLrfH6M7Pq2gAj5B6Zm7m+gdZoG66wTqG6mIZV/zijq3M2OO2HswtT6oBspPyFhHDcaxWpsBm0fRNDHPg==", "dev": true, "hasInstallScript": true, "dependencies": { @@ -9658,7 +9658,7 @@ "node-sass": "bin/node-sass" }, "engines": { - "node": ">=14" + "node": ">=16" } }, "node_modules/nodemon": { diff --git a/package.json b/package.json index 580a2e27..498cea73 100644 --- a/package.json +++ b/package.json @@ -171,7 +171,7 @@ "mocha": "^10.2.0", "mocha-junit-reporter": "^2.2.0", "nock": "^13.3.1", - "node-sass": "^8.0.0", + "node-sass": "^9.0.0", "nodemon": "^3.0.1", "prettier": "^2.8.8", "supertest": "^6.3.3", diff --git a/server/config/forms/useOfForceDetailsForm.js b/server/config/forms/useOfForceDetailsForm.js index 5cc5859f..39ac0e6d 100644 --- a/server/config/forms/useOfForceDetailsForm.js +++ b/server/config/forms/useOfForceDetailsForm.js @@ -40,6 +40,8 @@ const completeSchema = joi.object({ otherwise: joi.any().strip(), }), + escortingHold: requiredBooleanMsg('Select yes if an escorting hold was used').alter(optionalForPartialValidation), + restraint: requiredBooleanMsg('Select yes if control and restraint was used').alter(optionalForPartialValidation), restraintPositions: joi.when('restraint', { diff --git a/server/config/forms/useOfForceDetailsValidation.test.js b/server/config/forms/useOfForceDetailsValidation.test.js index ed476740..44ff3a65 100644 --- a/server/config/forms/useOfForceDetailsValidation.test.js +++ b/server/config/forms/useOfForceDetailsValidation.test.js @@ -18,6 +18,7 @@ beforeEach(() => { pavaUsed: 'true', guidingHold: 'true', guidingHoldOfficersInvolved: '2', + escortingHold: 'true', restraint: 'true', restraintPositions: ['STANDING', 'FACE_DOWN'], handcuffsApplied: 'true', @@ -44,6 +45,7 @@ describe('complete schema', () => { pavaUsed: true, guidingHold: true, guidingHoldOfficersInvolved: 2, + escortingHold: true, restraint: true, restraintPositions: ['STANDING', 'FACE_DOWN'], handcuffsApplied: true, @@ -52,7 +54,7 @@ describe('complete schema', () => { }) }) - it('Should return 8 error massages if no input field is completed', () => { + it('Should return 9 error massages if no input field is completed', () => { const input = {} const { errors, formResponse } = check(input) @@ -77,6 +79,10 @@ describe('complete schema', () => { href: '#guidingHold', text: 'Select yes if a guiding hold was used', }, + { + href: '#escortingHold', + text: 'Select yes if an escorting hold was used', + }, { href: '#restraint', text: 'Select yes if control and restraint was used', @@ -91,7 +97,7 @@ describe('complete schema', () => { }, ]) - expect(errors.length).toEqual(8) + expect(errors.length).toEqual(9) expect(formResponse).toEqual({}) }) @@ -240,6 +246,22 @@ describe('complete schema', () => { expect(formResponse.guidingHoldOfficersInvolved).toEqual(2) }) + it("Not selecting an option for 'escorting hold' returns validation error message plus 'how many officers involved' is undefined", () => { + const input = { + ...validInput, + escortingHold: undefined, + } + const { errors, formResponse } = check(input) + + expect(errors).toEqual([ + { + href: '#escortingHold', + text: 'Select yes if an escorting hold was used', + }, + ]) + expect(formResponse.escortingHold).toEqual(undefined) + }) + it("Not selecting an option for 'restraint'returns a validation error message plus 'restraint positions' is undefined", () => { const input = { ...validInput, @@ -393,6 +415,7 @@ describe('partial schema', () => { pavaUsed: true, guidingHold: true, guidingHoldOfficersInvolved: 2, + escortingHold: true, restraint: true, restraintPositions: ['STANDING', 'FACE_DOWN'], handcuffsApplied: true, @@ -415,6 +438,7 @@ describe('partial schema', () => { batonDrawn: 'true', pavaDrawn: 'true', guidingHold: 'true', + escortingHold: 'true', restraint: 'true', restraintPositions: [], }) @@ -423,6 +447,7 @@ describe('partial schema', () => { expect(formResponse).toEqual({ batonDrawn: true, guidingHold: true, + escortingHold: true, pavaDrawn: true, restraint: true, }) diff --git a/server/data/UseOfForceReport.ts b/server/data/UseOfForceReport.ts index 5dd771af..12cbc92c 100644 --- a/server/data/UseOfForceReport.ts +++ b/server/data/UseOfForceReport.ts @@ -14,6 +14,7 @@ export type UseOfForceDetails = { pavaUsed: boolean guidingHold: boolean guidingHoldOfficersInvolved: number + escortingHold?: boolean restraint: boolean restraintPositions: string[] handcuffsApplied: boolean diff --git a/server/routes/creatingReports/checkYourAnswers.test.ts b/server/routes/creatingReports/checkYourAnswers.test.ts index 9072376d..4d10704d 100644 --- a/server/routes/creatingReports/checkYourAnswers.test.ts +++ b/server/routes/creatingReports/checkYourAnswers.test.ts @@ -57,6 +57,7 @@ describe('GET /check-your-answers', () => { restraint: false, batonDrawn: false, guidingHold: false, + escortingHold: false, handcuffsApplied: false, positiveCommunication: false, personalProtectionTechniques: false, diff --git a/server/routes/creatingReports/createReport.test.ts b/server/routes/creatingReports/createReport.test.ts index 8e4386fd..16a5b0ea 100644 --- a/server/routes/creatingReports/createReport.test.ts +++ b/server/routes/creatingReports/createReport.test.ts @@ -43,6 +43,7 @@ const validUseOfForceDetailsRequest = { batonDrawn: 'false', pavaDrawn: 'false', guidingHold: 'false', + escortingHold: 'false', restraint: 'false', painInducingTechniques: 'false', handcuffsApplied: 'false', @@ -56,6 +57,7 @@ const validUseofForceDetailUpdate = [ { batonDrawn: false, guidingHold: false, + escortingHold: false, handcuffsApplied: false, painInducingTechniques: false, pavaDrawn: false, @@ -112,6 +114,7 @@ describe('POST save and return to tasklist', () => { expect(draftReportService.process).toBeCalledTimes(1) expect(draftReportService.process).toBeCalledWith(user, 1, 'useOfForceDetails', { guidingHold: false, + escortingHold: false, handcuffsApplied: false, painInducingTechniques: false, pavaDrawn: false, diff --git a/server/services/drafts/reportStatusChecker.test.ts b/server/services/drafts/reportStatusChecker.test.ts index 78eaf516..97b8366b 100644 --- a/server/services/drafts/reportStatusChecker.test.ts +++ b/server/services/drafts/reportStatusChecker.test.ts @@ -17,6 +17,7 @@ describe('statusCheck', () => { restraint: false, batonDrawn: false, guidingHold: false, + escortingHold: false, handcuffsApplied: false, positiveCommunication: false, personalProtectionTechniques: true, @@ -105,6 +106,7 @@ describe('statusCheck', () => { restraint: false, batonDrawn: null, guidingHold: false, + escortingHold: false, handcuffsApplied: null, positiveCommunication: false, personalProtectionTechniques: undefined, diff --git a/server/services/reportDetailBuilder.test.ts b/server/services/reportDetailBuilder.test.ts index b7e733ed..148903fd 100644 --- a/server/services/reportDetailBuilder.test.ts +++ b/server/services/reportDetailBuilder.test.ts @@ -110,6 +110,7 @@ describe('Build details', () => { batonDrawn: undefined, controlAndRestraintUsed: undefined, guidingHoldUsed: undefined, + escortingHoldUsed: undefined, handcuffsApplied: undefined, painInducingTechniques: undefined, pavaDrawn: undefined, diff --git a/server/services/reportSummary.ts b/server/services/reportSummary.ts index a23302cc..0ddb3658 100644 --- a/server/services/reportSummary.ts +++ b/server/services/reportSummary.ts @@ -61,6 +61,7 @@ const createUseOfForceDetails = ( guidingHoldUsed: whenPresent(details.guidingHold, value => value ? howManyOfficersInvolved(details.guidingHoldOfficersInvolved) : NO ), + escortingHoldUsed: details.escortingHold, controlAndRestraintUsed: whenPresent(details.restraint, value => value === true && details.restraintPositions ? getRestraintPositions(details.restraintPositions) : NO ), diff --git a/server/views/formPages/addingStaff/delete-staff-member.html b/server/views/formPages/addingStaff/delete-staff-member.html index f2df6715..afb6e133 100644 --- a/server/views/formPages/addingStaff/delete-staff-member.html +++ b/server/views/formPages/addingStaff/delete-staff-member.html @@ -3,7 +3,7 @@ {% from "govuk/components/radios/macro.njk" import govukRadios %} {% from "govuk/components/error-summary/macro.njk" import govukErrorSummary %} -{% set pageTitle = 'Are you sure you want to delete staff member?' %} +{% set pageTitle = 'Are you sure you want to delete staff member?' %} {% block content %}