Skip to content

Commit

Permalink
Merge pull request #39 from ministryofjustice/feat/add-new-bail-condi…
Browse files Browse the repository at this point in the history
…tions-task

Add new bail information section
  • Loading branch information
patrickjfl authored Dec 16, 2024
2 parents 36eb05a + 73ba3af commit 0f922d7
Show file tree
Hide file tree
Showing 13 changed files with 441 additions and 2 deletions.
5 changes: 5 additions & 0 deletions e2e-tests/steps/apply.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import { completeAreaInformationTask, completeFundingInformationTask } from './a
import { completeCurrentOffencesTask, completeOffenceHistoryTask } from './offenceAndLicenceInformationSection'
import { completeCheckAnswersTask } from './checkAnswersSection'
import { TestOptions } from '../testOptions'
import { completeBailConditionsAndSupportSessionsTask } from './bailInformationSection'

export const startAnApplication = async (page: Page) => {
// Start page
Expand Down Expand Up @@ -85,6 +86,10 @@ export const completeOffenceInformationSection = async (page: Page, name: string
await completeOffenceHistoryTask(page, name)
}

export const completeBailInformationSection = async (page: Page, name: string) => {
await completeBailConditionsAndSupportSessionsTask(page, name)
}

export const completeCheckAnswersSection = async (page: Page, name: string) => {
await completeCheckAnswersTask(page, name)
}
Expand Down
28 changes: 28 additions & 0 deletions e2e-tests/steps/bailInformationSection.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { Page } from '@playwright/test'
import { ApplyPage, TaskListPage } from '../pages/apply'

export const completeBailConditionsAndSupportSessionsTask = async (page: Page, name: string) => {
const taskListPage = new TaskListPage(page)
await taskListPage.clickTask('Add bail conditions and support sessions')

await completeNonStandardBailConditionsPage(page, name)
await completeMandatorySupportSessionsPage(page, name)
}

async function completeNonStandardBailConditionsPage(page: Page, name: string) {
const nonStandardBailConditionsPage = await ApplyPage.initialize(
page,
`Are there any non-standard bail conditions being considered for ${name}?`,
)
await nonStandardBailConditionsPage.checkRadio('No')
await nonStandardBailConditionsPage.clickButton('Save and continue')
}

async function completeMandatorySupportSessionsPage(page: Page, name: string) {
const mandatorySupportSessionsPage = await ApplyPage.initialize(
page,
`Does the court require more than one mandatory support session per week for ${name}?`,
)
await mandatorySupportSessionsPage.checkRadio('No')
await mandatorySupportSessionsPage.clickButton('Save and continue')
}
4 changes: 3 additions & 1 deletion e2e-tests/tests/01_apply_as_pom.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
completeCheckAnswersSection,
completeOffenceInformationSection,
completeRisksAndNeedsSection,
completeBailInformationSection,
confirmApplicant,
enterPrisonerNumber,
startAnApplication,
Expand All @@ -30,8 +31,9 @@ test('create a CAS-2 application', async ({ page, person, pomUser }) => {
await completeAboutThePersonSection(page, person.name)
await completeRisksAndNeedsSection(page, person.name)
await completeOffenceInformationSection(page, person.name)
await completeBailInformationSection(page, person.name)
await completeCheckAnswersSection(page, person.name)
await expect(page.getByText('You have completed 15 of 15 tasks')).toBeVisible()
await expect(page.getByText('You have completed 16 of 16 tasks')).toBeVisible()
await submitApplication(page)
})

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/* istanbul ignore file */

import { Task } from '../../../utils/decorators'
import NonStandardBailConditions from './nonStandardBailConditions'
import MandatorySupportSessions from './mandatorySupportSessions'

@Task({
name: 'Add bail conditions and support sessions',
slug: 'bail-conditions-and-support-sessions',
pages: [NonStandardBailConditions, MandatorySupportSessions],
})
export default class BailConditionsAndSupportSessions {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import { itShouldHavePreviousValue, itShouldHaveNextValue } from '../../../shared-examples'
import { personFactory, applicationFactory } from '../../../../testutils/factories/index'
import MandatorySupportSessions, { MandatorySupportSessionsBody } from './mandatorySupportSessions'

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

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

expect(page.title).toEqual(
'Does the court require more than one mandatory support session per week for Roger Smith?',
)
})
})

itShouldHavePreviousValue(new MandatorySupportSessions({}, application), 'non-standard-bail-conditions')
itShouldHaveNextValue(new MandatorySupportSessions({}, application), '')

describe('items', () => {
it('returns the radio with the expected label text', () => {
const page = new MandatorySupportSessions(
{
mandatorySupportSessions: 'no',
},
application,
)

expect(page.items('some html')).toEqual([
{
value: 'yes',
text: 'Yes',
checked: false,
conditional: { html: 'some html' },
},
{
value: 'no',
text: 'No',
checked: true,
},
])
})
})

describe('errors', () => {
describe('when they have not provided any answer', () => {
it('does not return an error', () => {
const page = new MandatorySupportSessions({}, application)
expect(page.errors()).toEqual({})
})
})
describe('when they have not provided detail', () => {
it('does not return an error', () => {
const page = new MandatorySupportSessions({ mandatorySupportSessions: 'yes' }, application)
expect(page.errors()).toEqual({})
})
})
})

describe('onSave', () => {
it('removes non-standard bail conditions data when the question is not set to "yes"', () => {
const body: MandatorySupportSessionsBody = {
mandatorySupportSessions: 'no',
mandatorySupportSessionsDetail: 'Non-standard bail condition detail',
}

const page = new MandatorySupportSessions(body, application)

page.onSave()

expect(page.body).toEqual({
mandatorySupportSessions: 'no',
})
})
})
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { Radio, TaskListErrors, YesOrNo } from '@approved-premises/ui'
import { Cas2Application as Application } from '@approved-premises/api'
import { Page } from '../../../utils/decorators'
import TaskListPage from '../../../taskListPage'
import { nameOrPlaceholderCopy } from '../../../../utils/utils'
import { getQuestions } from '../../../utils/questions'
import { convertKeyValuePairToRadioItems } from '../../../../utils/formUtils'

export type MandatorySupportSessionsBody = {
mandatorySupportSessions: YesOrNo
mandatorySupportSessionsDetail: string
}

@Page({
name: 'mandatory-support-sessions',
bodyProperties: ['mandatorySupportSessions', 'mandatorySupportSessionsDetail'],
})
export default class MandatorySupportSessions implements TaskListPage {
documentTitle = 'Does the court require more than one mandatory support session per week for the applicant?'

personName = nameOrPlaceholderCopy(this.application.person)

title

questions = getQuestions(this.personName)['bail-conditions-and-support-sessions']['mandatory-support-sessions']

body: MandatorySupportSessionsBody

constructor(
body: Partial<MandatorySupportSessionsBody>,
private readonly application: Application,
) {
this.body = body as MandatorySupportSessionsBody
this.title = this.questions.mandatorySupportSessions.question
}

previous() {
return 'non-standard-bail-conditions'
}

next() {
return ''
}

items(detailConditionalHtml: string) {
const items = convertKeyValuePairToRadioItems(
this.questions.mandatorySupportSessions.answers,
this.body.mandatorySupportSessions,
) as Array<Radio>

items.forEach(item => {
if (item.value === 'yes') {
item.conditional = { html: detailConditionalHtml }
}
})

return items
}

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

onSave(): void {
if (this.body.mandatorySupportSessions !== 'yes') {
delete this.body.mandatorySupportSessionsDetail
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import { itShouldHavePreviousValue, itShouldHaveNextValue } from '../../../shared-examples'
import { personFactory, applicationFactory } from '../../../../testutils/factories/index'
import NonStandardBailConditions, { NonStandardBailConditionsBody } from './nonStandardBailConditions'

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

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

expect(page.title).toEqual('Are there any non-standard bail conditions being considered for Roger Smith?')
})
})

itShouldHavePreviousValue(new NonStandardBailConditions({}, application), 'taskList')
itShouldHaveNextValue(new NonStandardBailConditions({}, application), 'mandatory-support-sessions')

describe('items', () => {
it('returns the radio with the expected label text', () => {
const page = new NonStandardBailConditions(
{
nonStandardBailConditions: 'no',
},
application,
)

expect(page.items('some html')).toEqual([
{
value: 'yes',
text: 'Yes',
checked: false,
conditional: { html: 'some html' },
},
{
value: 'no',
text: 'No',
checked: true,
},
])
})
})

describe('errors', () => {
describe('when they have not provided any answer', () => {
it('does not return an error', () => {
const page = new NonStandardBailConditions({}, application)
expect(page.errors()).toEqual({})
})
})
describe('when they have not provided detail', () => {
it('does not return an error', () => {
const page = new NonStandardBailConditions({ nonStandardBailConditions: 'yes' }, application)
expect(page.errors()).toEqual({})
})
})
})

describe('onSave', () => {
it('removes non-standard bail conditions data when the question is not set to "yes"', () => {
const body: NonStandardBailConditionsBody = {
nonStandardBailConditions: 'no',
nonStandardBailConditionsDetail: 'Non-standard bail condition detail',
}

const page = new NonStandardBailConditions(body, application)

page.onSave()

expect(page.body).toEqual({
nonStandardBailConditions: 'no',
})
})
})
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { Radio, TaskListErrors, YesOrNo } from '@approved-premises/ui'
import { Cas2Application as Application } from '@approved-premises/api'
import { Page } from '../../../utils/decorators'
import TaskListPage from '../../../taskListPage'
import { nameOrPlaceholderCopy } from '../../../../utils/utils'
import { getQuestions } from '../../../utils/questions'
import { convertKeyValuePairToRadioItems } from '../../../../utils/formUtils'

export type NonStandardBailConditionsBody = {
nonStandardBailConditions: YesOrNo
nonStandardBailConditionsDetail: string
}

@Page({
name: 'non-standard-bail-conditions',
bodyProperties: ['nonStandardBailConditions', 'nonStandardBailConditionsDetail'],
})
export default class NonStandardBailConditions implements TaskListPage {
documentTitle = 'Are there any non-standard bail conditions being considered for this applicant?'

personName = nameOrPlaceholderCopy(this.application.person)

title

questions = getQuestions(this.personName)['bail-conditions-and-support-sessions']['non-standard-bail-conditions']

body: NonStandardBailConditionsBody

constructor(
body: Partial<NonStandardBailConditionsBody>,
private readonly application: Application,
) {
this.body = body as NonStandardBailConditionsBody
this.title = this.questions.nonStandardBailConditions.question
}

previous() {
return 'taskList'
}

next() {
return 'mandatory-support-sessions'
}

items(detailConditionalHtml: string) {
const items = convertKeyValuePairToRadioItems(
this.questions.nonStandardBailConditions.answers,
this.body.nonStandardBailConditions,
) as Array<Radio>

items.forEach(item => {
if (item.value === 'yes') {
item.conditional = { html: detailConditionalHtml }
}
})

return items
}

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

return errors
}

onSave(): void {
if (this.body.nonStandardBailConditions !== 'yes') {
delete this.body.nonStandardBailConditionsDetail
}
}
}
10 changes: 10 additions & 0 deletions server/form-pages/apply/bail-information/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/* istanbul ignore file */

import { Section } from '../../utils/decorators'
import BailConditionsAndSupportSessions from './bail-conditions-and-support-sessions'

@Section({
title: 'Bail information',
tasks: [BailConditionsAndSupportSessions],
})
export default class BailInformation {}
Loading

0 comments on commit 0f922d7

Please sign in to comment.