Skip to content

Commit

Permalink
WIP - Introduce alleged offences task
Browse files Browse the repository at this point in the history
  • Loading branch information
Daniel Liburd committed Jan 9, 2025
1 parent 51cddfb commit 3a7a0a1
Show file tree
Hide file tree
Showing 11 changed files with 770 additions and 2 deletions.
21 changes: 21 additions & 0 deletions integration_tests/fixtures/applicationData.json
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,27 @@
],
"current-offences": {}
},
"alleged-offences": {
"alleged-offence-data": [
{
"titleAndNumber": "Arson",
"offenceCategory": "Arson",
"offenceDate-day": "5",
"offenceDate-month": "6",
"offenceDate-year": "1940",
"summary": "summary detail"
},
{
"titleAndNumber": "Stalking",
"offenceCategory": "Stalking",
"offenceDate-day": "6",
"offenceDate-month": "7",
"offenceDate-year": "2023",
"summary": "more summary detail"
}
],
"alleged-offences": {}
},
"offending-history": {
"any-previous-convictions": { "hasAnyPreviousConvictions": "yesRelevantRisk" },
"offence-history-data": [
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
import { itShouldHaveNextValue, itShouldHavePreviousValue } from '../../../shared-examples'
import { personFactory, applicationFactory } from '../../../../testutils/factories/index'
import AllegedOffences from './allegedOffences'
import AllegedOffenceData from './custom-forms/allegedOffenceData'

jest.mock('./custom-forms/allegedOffenceData')

describe('AllegedOffences', () => {
const application = applicationFactory.build({ person: personFactory.build({ name: 'Roger Smith' }) })

const applicationWithData = applicationFactory.build({
person: personFactory.build({ name: 'Roger Smith' }),
data: {
'alleged-offences': {
'alleged-offence-data': [
{
titleAndNumber: 'Stalking',
offenceCategory: 'stalkingOrHarassment',
'offenceDate-day': '1',
'offenceDate-month': '2',
'offenceDate-year': '2023',
summary: 'summary detail',
},
{
titleAndNumber: 'Arson',
offenceCategory: 'arson',
'offenceDate-day': '5',
'offenceDate-month': '6',
'offenceDate-year': '1940',
summary: 'second summary detail',
},
],
},
},
})

describe('title', () => {
it('personalises the page title', () => {
const page = new AllegedOffences({}, application)

expect(page.title).toEqual('Alleged offences for Roger Smith')
})
})

describe('alleged offence data', () => {
describe('when there is alleged offence data on the application', () => {
it('assigns them to the offences field on the page', () => {
const page = new AllegedOffences({}, applicationWithData)

expect(page.offences).toEqual([
{
titleAndNumber: 'Stalking',
offenceCategoryTag: '<strong class="govuk-tag govuk-tag--blue">Stalking or Harassment</strong>',
offenceCategoryText: 'Stalking or Harassment',
offenceDate: '1 February 2023',
summary: 'summary detail',
removeLink: `/applications/${applicationWithData.id}/tasks/alleged-offences/pages/alleged-offence-data/0/removeFromList?redirectPage=alleged-offences`,
},
{
titleAndNumber: 'Arson',
offenceCategoryTag: '<strong class="govuk-tag govuk-tag--yellow">Arson</strong>',
offenceCategoryText: 'Arson',
offenceDate: '5 June 1940',
summary: 'second summary detail',
removeLink: `/applications/${applicationWithData.id}/tasks/alleged-offences/pages/alleged-offence-data/1/removeFromList?redirectPage=alleged-offences`,
},
])
})
})
})

itShouldHaveNextValue(new AllegedOffences({}, application), '')
itShouldHavePreviousValue(new AllegedOffences({}, application), 'taskList')

describe('errors', () => {
it('returns an empty object where there is alleged offence data', () => {
const page = new AllegedOffences({}, applicationWithData)
expect(page.errors()).toEqual({})
})

it('returns an error where there is no alleged offence data', () => {
const page = new AllegedOffences({}, application)
expect(page.errors()).toEqual({ offenceList: 'Alleged offences must be added to the application' })
})
})

describe('response', () => {
it('returns the offence information', () => {
const page = new AllegedOffences({}, applicationWithData)
expect(page.response()).toEqual({
'Alleged offence 1':

Check failure on line 91 in server/form-pages/apply/offence-information/alleged-offences/allegedOffences.test.ts

View workflow job for this annotation

GitHub Actions / node build / Run the node build

Delete `⏎·········`
'Stalking\r\nStalking or Harassment\r\n1 February 2023\r\n\nSummary: summary detail',
'Alleged offence 2':

Check failure on line 93 in server/form-pages/apply/offence-information/alleged-offences/allegedOffences.test.ts

View workflow job for this annotation

GitHub Actions / node build / Run the node build

Delete `⏎·········`
'Arson\r\nArson\r\n5 June 1940\r\n\nSummary: second summary detail',
})
})

it('returns empty object when there are no offences', () => {
const page = new AllegedOffences({}, application)
expect(page.response()).toEqual({})
})
})

describe('getOffenceTagColour', () => {
const categories = [
['stalkingOrHarassment', 'blue'],
['weaponsOrFirearms', 'red'],
['arson', 'yellow'],
['violence', 'pink'],
['domesticAbuse', 'purple'],
['hateCrime', 'green'],
['drugs', 'custom-brown'],
['other', 'grey'],
['undefinedCategory', 'grey'],
]
it.each(categories)('returns correct colour for category %s', (category, colour) => {
const page = new AllegedOffences({}, applicationWithData)
expect(page.getOffenceTagColour(category)).toEqual(colour)
})
})

describe('initialize', () => {
it('returns AllegedOffenceData page if there is no alleged offences data', () => {
const allegedOffenceDataPageConstructor = jest.fn()

;(AllegedOffenceData as jest.Mock).mockImplementation(() => {
return allegedOffenceDataPageConstructor
})

AllegedOffences.initialize({}, application)

expect(AllegedOffenceData).toHaveBeenCalledWith({}, application)
})

it('returns AllegedOffence page if there is alleged offences data', async () => {
const page = (await AllegedOffences.initialize({}, applicationWithData)) as AllegedOffences

expect(page.title).toBe('Alleged offences for Roger Smith')
})
})
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
import type { TaskListErrors } from '@approved-premises/ui'
import { Cas2Application as Application } from '@approved-premises/api'
import { Page } from '../../../utils/decorators'
import TaskListPage from '../../../taskListPage'
import AllegedOffenceData, { AllegedOffenceDataBody } from './custom-forms/allegedOffenceData'
import { DateFormats } from '../../../../utils/dateUtils'
import { createQueryString, nameOrPlaceholderCopy } from '../../../../utils/utils'
import paths from '../../../../paths/apply'
import { getQuestions } from '../../../utils/questions'

type AllegedOffencesBody = { offenceList: string }

type AllegedOffencesUI = {
titleAndNumber: string
offenceCategoryTag: string
offenceCategoryText: string
offenceDate: string
summary: string
removeLink: string
}

@Page({
name: 'alleged-offences',
bodyProperties: ['offenceList'],
})
export default class AllegedOffences implements TaskListPage {
personName = nameOrPlaceholderCopy(this.application.person)

documentTitle = 'Alleged offences'

title = `Alleged offences for ${this.personName}`

body: AllegedOffencesBody

offences: AllegedOffencesUI[]

pageName = 'alleged-offences'

dataPageName = 'alleged-offence-data'

taskName = 'alleged-offences'

allegedOffenceQuestions = getQuestions('')['alleged-offences']['alleged-offence-data']

constructor(
body: Partial<AllegedOffencesBody>,
private readonly application: Application,
) {
if (application.data[this.taskName]?.[this.dataPageName]) {
const AllegedOffencesData = application.data[this.taskName][this.dataPageName] as [AllegedOffenceDataBody]

const query = {
redirectPage: this.pageName,
}

this.offences = AllegedOffencesData.map((offence, index) => {
const offenceDate = DateFormats.dateAndTimeInputsToUiDate(offence, 'offenceDate')

const offenceCategoryText =
this.allegedOffenceQuestions.offenceCategory.answers[
offence.offenceCategory as keyof typeof this.allegedOffenceQuestions.offenceCategory.answers
]

return {
titleAndNumber: offence.titleAndNumber,
offenceCategoryTag: this.getOffenceCategoryTag(offence.offenceCategory, offenceCategoryText),
offenceCategoryText,
offenceDate,
summary: offence.summary,
removeLink: `${paths.applications.removeFromList({
id: application.id,
task: this.taskName,
page: this.dataPageName,
index: index.toString(),
})}?${createQueryString(query)}`,
}
})
}
this.body = body as AllegedOffencesBody
}

static async initialize(body: Partial<AllegedOffenceDataBody>, application: Application) {
if (!application.data['alleged-offences']?.['alleged-offence-data']) {
return new AllegedOffenceData(body, application)
}
return new AllegedOffences({}, application)
}

previous() {
return 'taskList'
}

next() {
return ''
}

errors() {
const errors: TaskListErrors<this> = {}

if (!this.application.data['alleged-offences']?.['alleged-offence-data'].length) {
errors.offenceList = 'Alleged offences must be added to the application'
}

return errors
}

response() {
const response: Record<string, string> = {}

this.offences?.forEach((offence, index) => {
const { titleAndNumber, offenceCategoryText, offenceDate, summary } = offence
response[`Alleged offence ${index + 1}`] =
`${titleAndNumber}\r\n${offenceCategoryText}\r\n${offenceDate}\r\n\nSummary: ${summary}`
})

return response
}

getOffenceCategoryTag(offenceCategory: string, offenceCategoryText: string) {
return `<strong class="govuk-tag govuk-tag--${this.getOffenceTagColour(
offenceCategory,
)}">${offenceCategoryText}</strong>`
}

getOffenceTagColour(offenceCategory: string) {
switch (offenceCategory) {
case 'stalkingOrHarassment':
return 'blue'
case 'weaponsOrFirearms':
return 'red'
case 'arson':
return 'yellow'
case 'violence':
return 'pink'
case 'domesticAbuse':
return 'purple'
case 'hateCrime':
return 'green'
case 'drugs':
return 'custom-brown'
default:
return 'grey'
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { itShouldHaveNextValue, itShouldHavePreviousValue } from '../../../../shared-examples'
import { personFactory, applicationFactory } from '../../../../../testutils/factories/index'
import AllegedOffenceData from './allegedOffenceData'

describe('AllegedOffenceData', () => {
const application = applicationFactory.build({ person: personFactory.build({ name: 'Roger Smith' }) })

const allegedOffenceData = [
{
titleAndNumber: 'Stalking',
offenceCategory: 'Arson',
'offenceDate-day': '1',
'offenceDate-month': '2',
'offenceDate-year': '2023',
summary: 'summary detail',
},
]

describe('title', () => {
it('has a page title', () => {
const page = new AllegedOffenceData({}, application)

expect(page.title).toEqual(`Add Roger Smith's alleged offence details`)
})
})

itShouldHaveNextValue(new AllegedOffenceData({}, application), 'alleged-offences')
itShouldHavePreviousValue(new AllegedOffenceData({}, application), 'alleged-offences')

describe('errors', () => {
describe('when there are no errors', () => {
it('returns empty object', () => {
const page = new AllegedOffenceData(allegedOffenceData[0], application)
expect(page.errors()).toEqual({})
})
})

describe('when there are errors', () => {
const requiredFields = [
['titleAndNumber', 'Enter the offence title'],
['offenceCategory', 'Select the offence type'],
['offenceDate', 'Enter the date the offence was committed'],
['summary', 'Enter a summary of the offence'],
]

it.each(requiredFields)('it includes a validation error for %s', (field, message) => {
const page = new AllegedOffenceData({ offenceCategory: 'choose' }, application)
const errors = page.errors()

expect(errors[field as keyof typeof errors]).toEqual(message)
})
})

describe('response', () => {
it('returns empty object', () => {
const page = new AllegedOffenceData({}, application)
expect(page.response()).toEqual({})
})
})
})
})
Loading

0 comments on commit 3a7a0a1

Please sign in to comment.