Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 12 additions & 4 deletions src/server/plugins/engine/components/CheckboxesField.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,25 +25,33 @@ export class CheckboxesField extends SelectionControlField {
const { listType: type } = this
const { options } = def

let formSchema =
const baseSchema =
type === 'string' ? joi.array<string>() : joi.array<number>()

const itemsSchema = joi[type]()
.valid(...this.values)
.label(this.label)

formSchema = formSchema
let formSchema = baseSchema
.items(itemsSchema)
.single()
.label(this.label)
.required()
.default([])

const stateSchema = baseSchema
.items(itemsSchema)
.label(this.label)
.required()
.default(null)
.allow(null)

if (options.required === false) {
formSchema = formSchema.optional()
}

this.formSchema = formSchema.default([])
this.stateSchema = formSchema.default(null).allow(null)
this.formSchema = formSchema
this.stateSchema = stateSchema
this.options = options
}

Expand Down
77 changes: 75 additions & 2 deletions test/condition/checkboxes.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@ import { StatusCodes } from 'http-status-codes'
import { FORM_PREFIX } from '~/src/server/constants.js'
import { createServer } from '~/src/server/index.js'
import { getFormMetadata } from '~/src/server/plugins/engine/services/formsService.js'
import { CacheService } from '~/src/server/services/cacheService.js'
import * as fixtures from '~/test/fixtures/index.js'
import { renderResponse } from '~/test/helpers/component-helpers.js'
import { getCookie, getCookieHeader } from '~/test/utils/get-cookie.js'

const basePath = `${FORM_PREFIX}/checkboxes`
const key = 'wqJmSf'
Expand All @@ -16,6 +18,10 @@ jest.mock('~/src/server/plugins/engine/services/formsService.js')
describe('Checkboxes based conditions', () => {
/** @type {Server} */
let server
/** @type {string} */
let csrfToken
/** @type {ReturnType<typeof getCookieHeader>} */
let headers

// Create server before each test
beforeAll(async () => {
Expand All @@ -25,6 +31,13 @@ describe('Checkboxes based conditions', () => {
})

await server.initialize()
// Navigate to first page to establish a session and get CSRF + session cookies
const response = await server.inject({
url: `${basePath}/first-page`
})

csrfToken = getCookie(response, 'crumb')
headers = getCookieHeader(response, ['session', 'crumb'])
})

beforeEach(() => {
Expand Down Expand Up @@ -79,12 +92,13 @@ describe('Checkboxes based conditions', () => {
}
})

test('Testing POST /first-page with nothing checked redirects correctly', async () => {
const form = {}
test('Testing POST /first-page with nothing checked redirects correctly with single value', async () => {
const form = { crumb: csrfToken }

const res = await server.inject({
url: `${basePath}/first-page`,
method: 'POST',
headers,
payload: form
})

Expand All @@ -94,17 +108,76 @@ describe('Checkboxes based conditions', () => {

test('Testing POST /first-page with "other" checked redirects correctly', async () => {
const form = {
crumb: csrfToken,
[key]: 'other'
}

const setStateSpy = jest.spyOn(CacheService.prototype, 'setState')

const res = await server.inject({
url: `${basePath}/first-page`,
method: 'POST',
headers,
payload: form
})

expect(res.statusCode).toBe(StatusCodes.SEE_OTHER)
expect(res.headers.location).toBe(`${basePath}/third-page`)

// Ensure the stored state contains an array for the checkbox key
expect(setStateSpy).toHaveBeenCalled()
const savedState = setStateSpy.mock.calls[0][1]
expect(Array.isArray(savedState[key])).toBe(true)

setStateSpy.mockRestore()
})

test('Testing POST /first-page with "other" checked redirects correctly with multiple options', async () => {
const form = {
crumb: csrfToken,
[key]: ['other', 'shire']
}

const setStateSpy = jest.spyOn(CacheService.prototype, 'setState')

const res = await server.inject({
url: `${basePath}/first-page`,
method: 'POST',
headers,
payload: form
})

// Ensure the stored state contains an array for the checkbox key
expect(setStateSpy).toHaveBeenCalled()
const savedState = setStateSpy.mock.calls[0][1]
expect(Array.isArray(savedState[key])).toBe(true)

expect(res.statusCode).toBe(StatusCodes.SEE_OTHER)
expect(res.headers.location).toBe(`${basePath}/third-page`)
})

test('DF-686 Test that when a radio question is changed to a checkbox question, the state validation forces the user to re-answer the question', async () => {
// Create a fresh session and extract cookies
const response = await server.inject({ url: `${basePath}/first-page` })
const hdrs = getCookieHeader(response, ['session', 'crumb'])

// Mock CacheService.getState to return a malformed value (string instead of array)
const malformedState = { [key]: 'other' }
const getStateSpy = jest
.spyOn(CacheService.prototype, 'getState')
.mockResolvedValueOnce(malformedState)

// GET the summary - server should redirect back to first-page because value isn't an array
const res = await server.inject({
url: `${basePath}/third-page`,
method: 'GET',
headers: hdrs
})

expect(res.statusCode).toBe(StatusCodes.MOVED_TEMPORARILY)
expect(res.headers.location).toBe(`${basePath}/first-page`)

getStateSpy.mockRestore()
})
})

Expand Down
Loading