Skip to content

Commit

Permalink
Merge pull request #19 from DEFRA/453
Browse files Browse the repository at this point in the history
[DSFAA-453] origin address page
  • Loading branch information
DrogoNevets authored Nov 20, 2024
2 parents 66e7cc8 + ebe2b39 commit f4e5000
Show file tree
Hide file tree
Showing 8 changed files with 536 additions and 20 deletions.
8 changes: 1 addition & 7 deletions src/server/cph-number/controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,13 +50,7 @@ export const postController = {

req.yar.set('cphNumber', input)

return res.view(indexView, {
pageTitle,
heading: pageTitle,
cphNumber: {
value: input
}
})
return res.redirect('/origin-address')
}
}

Expand Down
16 changes: 4 additions & 12 deletions src/server/cph-number/controller.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { pageTitle } from './controller.js'
import { withCsrfProtection } from '~/src/server/common/test-helpers/csrf.js'
import { parseDocument } from '~/src/server/common/test-helpers/dom.js'

describe('#onOffFarmController', () => {
describe('#cphNumber', () => {
/** @type {Server} */
let server

Expand All @@ -28,7 +28,7 @@ describe('#onOffFarmController', () => {
})

test('Should process the result and provide expected response', async () => {
const { payload, statusCode } = await server.inject(
const { headers, statusCode } = await server.inject(
withCsrfProtection({
method: 'POST',
url: '/cph-number',
Expand All @@ -38,17 +38,9 @@ describe('#onOffFarmController', () => {
})
)

expect(parseDocument(payload).title).toEqual(pageTitle)

expect(payload).toEqual(expect.not.stringContaining('There is a problem'))
expect(statusCode).toBe(statusCodes.redirect)

expect(payload).toEqual(
expect.stringContaining(
'<input class="govuk-input govuk-input--width-10" id="cph-number" name="cphNumber" type="text" value="12/456/7899" aria-describedby="cph-number-hint" autocomplete="cph-number">'
)
)

expect(statusCode).toBe(statusCodes.ok)
expect(headers.location).toBe('/origin-address')
})

test('Should display an error to the user if no value entered', async () => {
Expand Down
71 changes: 71 additions & 0 deletions src/server/origin-address/controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import validator from './validator.js'

const indexView = 'origin-address/index.njk'
export const pageTitle =
'What is the address of your farm or premises where the animals are moving off?'
export const pageHeading =
'What is the address of your farm or premises where the animals are moving off?'

/**
* @satisfies {Partial<ServerRoute>}
*/
export const originAddressGetController = {
handler(req, h) {
const originAddress = req.yar.get('originAddress')

return h.view(indexView, {
pageTitle,
heading: pageHeading,
values: originAddress
})
}
}

/**
* @satisfies {Partial<ServerRoute>}
* @param req
*/
export const originAddressPostController = {
handler(req, res) {
const {
addressLine1,
addressLine2,
addressTown,
addressCounty,
addressPostcode
} = /** @type {OriginAddress} */ (req.payload)

const originAddress = {
addressLine1,
addressLine2,
addressTown,
addressCounty,
addressPostcode
}

const { isValid, errors } = validator(originAddress)
const errorMessages = Object.entries(errors ?? {}).map(([key, value]) => ({
text: value.text,
href: `#${key}`
}))

if (!isValid) {
return res.view(indexView, {
pageTitle: `Error: ${pageTitle}`,
heading: pageHeading,
values: req.payload,
errorMessages,
errors
})
}

req.yar.set('originAddress', originAddress)

return res.redirect('/origin-address')
}
}

/**
* @import { ServerRoute } from '@hapi/hapi'
* @import { OriginAddress } from './validator.js'
*/
262 changes: 262 additions & 0 deletions src/server/origin-address/controller.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,262 @@
import { createServer } from '~/src/server/index.js'
import { statusCodes } from '~/src/server/common/constants/status-codes.js'
import {
testCsrfProtectedGet,
testCsrfProtectedPost,
withCsrfProtection
} from '~/src/server/common/test-helpers/csrf.js'
import { parseDocument } from '~/src/server/common/test-helpers/dom.js'

import { pageTitle } from './controller.js'

describe('#originAddressController', () => {
/** @type {Server} */
let server

beforeAll(async () => {
server = await createServer()
await server.initialize()
})

afterAll(async () => {
await server.stop({ timeout: 0 })
})

test('Should provide expected response', async () => {
const { payload, statusCode } = await server.inject({
method: 'GET',
url: '/origin-address'
})

const document = parseDocument(payload)
expect(document.title).toBe(pageTitle)
expect(statusCode).toBe(statusCodes.ok)
})

test('should redirect to self', async () => {
const { headers, statusCode } = await server.inject(
withCsrfProtection({
method: 'POST',
url: '/origin-address',
payload: {
addressLine1: 'Starfleet Headquarters',
addressLine2: '24-593 Federation Drive',
addressTown: 'San Francisco',
addressCounty: 'San Francisco',
addressPostcode: 'RG24 8RR'
}
})
)

expect(headers.location).toBe('/origin-address')

expect(statusCode).toBe(statusCodes.redirect)
})

test('Should display an error to the user if no value selected', async () => {
const { payload, statusCode } = await server.inject(
withCsrfProtection({
method: 'POST',
url: '/origin-address'
})
)

expect(parseDocument(payload).title).toBe(`Error: ${pageTitle}`)
expect(payload).toEqual(expect.stringContaining('There is a problem'))

expect(statusCode).toBe(statusCodes.ok)
})

describe('When the user has not entered address details', () => {
test('Should display an error to the user if no value entered', async () => {
const { payload, statusCode } = await server.inject(
withCsrfProtection({
method: 'POST',
url: '/origin-address',
payload: {}
})
)

expect(parseDocument(payload).title).toBe(`Error: ${pageTitle}`)

expect(payload).toEqual(expect.stringContaining('There is a problem'))
expect(payload).toEqual(expect.stringContaining('Enter address line 1'))
expect(payload).toEqual(expect.stringContaining('Enter town or city'))
expect(payload).toEqual(expect.stringContaining('Enter postcode'))

expect(statusCode).toBe(statusCodes.ok)
})

test('Should display an error to the user if no value entered for address line 1', async () => {
const { payload, statusCode } = await server.inject(
withCsrfProtection({
method: 'POST',
url: '/origin-address',
payload: {
addressLine2: '24-593 Federation Drive',
addressTown: 'San Francisco',
addressCounty: 'San Francisco',
addressPostcode: 'RG24 8RR'
}
})
)

expect(parseDocument(payload).title).toBe(`Error: ${pageTitle}`)

expect(payload).toEqual(expect.stringContaining('There is a problem'))
expect(payload).toEqual(expect.stringContaining('Enter address line 1'))
expect(payload).toEqual(expect.not.stringContaining('Enter town or city'))
expect(payload).toEqual(expect.not.stringContaining('Enter postcode'))

expect(statusCode).toBe(statusCodes.ok)
})

test('Should display an error to the user if no value entered for town/city', async () => {
const { payload, statusCode } = await server.inject(
withCsrfProtection({
method: 'POST',
url: '/origin-address',
payload: {
addressLine1: 'Starfleet Headquarters',
addressLine2: '24-593 Federation Drive',
addressCounty: 'San Francisco',
addressPostcode: 'RG24 8RR'
}
})
)

expect(parseDocument(payload).title).toBe(`Error: ${pageTitle}`)

expect(payload).toEqual(expect.stringContaining('There is a problem'))
expect(payload).toEqual(
expect.not.stringContaining('Enter address line 1')
)
expect(payload).toEqual(expect.stringContaining('Enter town or city'))
expect(payload).toEqual(expect.not.stringContaining('Enter postcode'))

expect(statusCode).toBe(statusCodes.ok)
})

test('Should display an error to the user if no value entered for postcode', async () => {
const { payload, statusCode } = await server.inject(
withCsrfProtection({
method: 'POST',
url: '/origin-address',
payload: {
addressLine1: 'Starfleet Headquarters',
addressLine2: '24-593 Federation Drive',
addressTown: 'San Francisco',
addressCounty: 'San Francisco'
}
})
)

expect(parseDocument(payload).title).toBe(`Error: ${pageTitle}`)

expect(payload).toEqual(expect.stringContaining('There is a problem'))
expect(payload).toEqual(
expect.not.stringContaining('Enter address line 1')
)
expect(payload).toEqual(expect.not.stringContaining('Enter town or city'))
expect(payload).toEqual(expect.stringContaining('Enter postcode'))

expect(statusCode).toBe(statusCodes.ok)
})
})

test('Should report malformed error on incorrect postcode', async () => {
const { payload, statusCode } = await server.inject(
withCsrfProtection({
method: 'POST',
url: '/origin-address',
payload: {
addressLine1: 'Starfleet Headquarters',
addressLine2: '24-593 Federation Drive',
addressTown: 'San Francisco',
addressCounty: 'San Francisco',
addressPostcode: 'invalid format'
}
})
)

expect(parseDocument(payload).title).toBe(`Error: ${pageTitle}`)

expect(payload).toEqual(expect.stringContaining('There is a problem'))
expect(payload).toEqual(expect.stringContaining('Enter a full UK postcode'))

expect(statusCode).toBe(statusCodes.ok)
})

test('Should process input correctly with missing optional fields', async () => {
const { headers, statusCode } = await server.inject(
withCsrfProtection({
method: 'POST',
url: '/origin-address',
payload: {
addressLine1: 'Starfleet Headquarters',
addressTown: 'San Francisco',
addressPostcode: 'SW1A 1AA' // Valid UK postcode
}
})
)

expect(statusCode).toBe(statusCodes.redirect) // Assuming a redirect on success
expect(headers.location).toBe('/origin-address')
})

test('Should report error for addressLine1 exceeding max length', async () => {
const { payload, statusCode } = await server.inject(
withCsrfProtection({
method: 'POST',
url: '/origin-address',
payload: {
addressLine1: 'A'.repeat(256), // Exceeding max length
addressTown: 'San Francisco',
addressPostcode: 'SW1A 1AA' // Valid UK postcode
}
})
)

expect(parseDocument(payload).title).toBe(`Error: ${pageTitle}`)
expect(payload).toEqual(
expect.stringContaining(
'Address line 1 must be no longer than 255 characters'
)
)
expect(statusCode).toBe(statusCodes.ok)
})

test('Should process addressLine1 at max length correctly', async () => {
const { headers, statusCode } = await server.inject(
withCsrfProtection({
method: 'POST',
url: '/origin-address',
payload: {
addressLine1: 'A'.repeat(255), // Max length
addressTown: 'San Francisco',
addressPostcode: 'SW1A 1AA' // Valid UK postcode
}
})
)

expect(statusCode).toBe(statusCodes.redirect) // Assuming a redirect on success
expect(headers.location).toBe('/origin-address') // Check for redirect response
})

testCsrfProtectedGet(() => server, {
method: 'GET',
url: '/origin-address'
})

testCsrfProtectedPost(() => server, {
method: 'POST',
url: '/origin-address',
payload: {
originAddress: 'on'
}
})
})

/**
* @import { Server } from '@hapi/hapi'
*/
Loading

0 comments on commit f4e5000

Please sign in to comment.