diff --git a/app/presenters/return-requirements/check/returns-requirements.presenter.js b/app/presenters/return-requirements/check/returns-requirements.presenter.js index a3058e6acb..d1e730067b 100644 --- a/app/presenters/return-requirements/check/returns-requirements.presenter.js +++ b/app/presenters/return-requirements/check/returns-requirements.presenter.js @@ -20,17 +20,16 @@ const agreementsExceptionsText = { * Formats return requirements data for the `/return-requirements/{sessionId}/check` page * * @param {Object[]} requirements - The existing return requirements in the current session - * @param {module:PurposeModel[]} purposes - All purposes that match those selected across the return requirements * @param {Object[]} points - The points related to the licence * @param {string} journey - Whether the setup journey is 'no-returns-required' or 'returns-required' * * @returns {Object} returns requirement data needed by the view template */ -function go (requirements, purposes, points, journey) { +function go (requirements, points, journey) { return { returnsRequired: journey === 'returns-required', - requirements: _requirements(requirements, purposes, points) + requirements: _requirements(requirements, points) } } @@ -48,10 +47,6 @@ function _abstractionPeriod (abstractionPeriod) { } function _agreementsExceptions (agreementsExceptions) { - if (agreementsExceptions[0] === agreementsExceptionsText.none) { - return 'None' - } - const formattedExceptions = agreementsExceptions.map((exception) => { return agreementsExceptionsText[exception] }) @@ -68,32 +63,33 @@ function _agreementsExceptions (agreementsExceptions) { .join(', ') + ', and ' + formattedExceptions[formattedExceptions.length - 1] } -function _requirements (requirements, purposes, points) { +function _requirements (requirements, points) { const completedRequirements = [] for (const [index, requirement] of requirements.entries()) { const { agreementsExceptions } = requirement + // NOTE: We determine a requirement is complete because agreement exceptions is populated and it is the last step in // the journey if (agreementsExceptions) { - completedRequirements.push(_mapRequirement(requirement, index, purposes, points)) + completedRequirements.push(_mapRequirement(requirement, index, points)) } } return completedRequirements } -function _mapPurposes (requirementPurposes, purposes) { - return requirementPurposes.map((requirementPurpose) => { - const matchedPurpose = purposes.find((purpose) => { - return purpose.id === requirementPurpose - }) +function _mapPurposes (purposes) { + return purposes.map((purpose) => { + if (purpose.alias) { + return `${purpose.description} (${purpose.alias})` + } - return matchedPurpose.description + return purpose.description }) } -function _mapRequirement (requirement, index, purposes, points) { +function _mapRequirement (requirement, index, points) { return { abstractionPeriod: _abstractionPeriod(requirement.abstractionPeriod), agreementsExceptions: _agreementsExceptions(requirement.agreementsExceptions), @@ -101,7 +97,7 @@ function _mapRequirement (requirement, index, purposes, points) { frequencyReported: requirement.frequencyReported, index, points: _mapPoints(requirement.points, points), - purposes: _mapPurposes(requirement.purposes, purposes), + purposes: _mapPurposes(requirement.purposes), returnsCycle: requirement.returnsCycle === 'summer' ? 'Summer' : 'Winter and all year', siteDescription: requirement.siteDescription } diff --git a/app/presenters/return-requirements/purpose.presenter.js b/app/presenters/return-requirements/purpose.presenter.js index 443e2b8f06..2ea1577c59 100644 --- a/app/presenters/return-requirements/purpose.presenter.js +++ b/app/presenters/return-requirements/purpose.presenter.js @@ -10,20 +10,19 @@ * * @param {module:SessionModel} session - The returns requirements session instance * @param {string} requirementIndex - The index of the requirement being added or changed - * @param {module:PurposeModel[]} purposesData - The purposes for the licence + * @param {module:PurposeModel[]} licencePurposes - All the purposes for the licence * * @returns {Object} - The data formatted for the view template */ -function go (session, requirementIndex, purposesData) { +function go (session, requirementIndex, licencePurposes) { const { id: sessionId, licence, requirements } = session const requirement = requirements[requirementIndex] return { backLink: _backLink(session), licenceId: licence.id, - licencePurposes: _licencePurposes(purposesData), licenceRef: licence.licenceRef, - purposes: requirement?.purposes ? requirement.purposes.join(',') : '', + purposes: _purposes(licencePurposes, requirement.purposes), sessionId } } @@ -47,11 +46,17 @@ function _backLink (session) { return `/system/return-requirements/${id}/setup` } -function _licencePurposes (purposesData) { - return purposesData.map((purpose) => { +function _purposes (licencePurposes, requirementPurposes) { + return licencePurposes.map((licencePurpose) => { + const matchedRequirementPurpose = requirementPurposes?.find((requirementPurpose) => { + return requirementPurpose.id === licencePurpose.id + }) + return { - id: purpose.id, - description: purpose.description + alias: matchedRequirementPurpose?.alias ? matchedRequirementPurpose.alias : '', + checked: !!matchedRequirementPurpose, + description: licencePurpose.description, + id: licencePurpose.id } }) } diff --git a/app/services/return-requirements/check.service.js b/app/services/return-requirements/check.service.js index d8cdfeccc7..91cb944c8f 100644 --- a/app/services/return-requirements/check.service.js +++ b/app/services/return-requirements/check.service.js @@ -6,7 +6,8 @@ */ const CheckPresenter = require('../../presenters/return-requirements/check.presenter.js') -const ReturnRequirementsService = require('./check/returns-requirements.service.js') +const FetchPointsService = require('./fetch-points.service.js') +const ReturnRequirementsPresenter = require('../../presenters/return-requirements/check/returns-requirements.presenter.js') const SessionModel = require('../../models/session.model.js') /** @@ -22,7 +23,7 @@ async function go (sessionId, yar) { await _markCheckPageVisited(session) - const returnRequirements = await ReturnRequirementsService.go(session) + const returnRequirements = await _returnRequirements(session) const formattedData = CheckPresenter.go(session) @@ -42,6 +43,14 @@ async function _markCheckPageVisited (session) { return session.$update() } +async function _returnRequirements (session) { + const { licence, requirements, journey } = session + + const points = await FetchPointsService.go(licence.id) + + return ReturnRequirementsPresenter.go(requirements, points, journey) +} + module.exports = { go } diff --git a/app/services/return-requirements/check/returns-requirements.service.js b/app/services/return-requirements/check/returns-requirements.service.js deleted file mode 100644 index fe1e1db781..0000000000 --- a/app/services/return-requirements/check/returns-requirements.service.js +++ /dev/null @@ -1,52 +0,0 @@ -'use strict' - -/** - * Orchestrates fetching and presenting the return requirements for the check page - * @module ReturnRequirementsService - */ - -const FetchPointsService = require('../fetch-points.service.js') -const PurposeModel = require('../../../models/purpose.model.js') -const ReturnRequirementsPresenter = require('../../../presenters/return-requirements/check/returns-requirements.presenter.js') - -/** - * Orchestrates fetching and presenting the return requirements for `/return-requirements/{sessionId}/check` page - * - * @param {module:SessionModel} session - The session for the return requirement journey - * - * @returns {Promise} page data needed by the view template - */ -async function go (session) { - const { licence, requirements, journey } = session - - const points = await FetchPointsService.go(licence.id) - const purposeIds = _purposeIds(requirements) - const purposes = await _fetchPurposes(purposeIds) - - return ReturnRequirementsPresenter.go(requirements, purposes, points, journey) -} - -async function _fetchPurposes (purposeIds) { - return PurposeModel.query() - .select([ - 'purposes.id', - 'purposes.description' - ]) - .findByIds(purposeIds) -} - -function _purposeIds (requirements) { - const requirementPurposes = requirements.flatMap((requirement) => { - if (requirement.purposes) { - return requirement.purposes - } - - return [] - }) - - return [...new Set(requirementPurposes)] -} - -module.exports = { - go -} diff --git a/app/services/return-requirements/submit-purpose.service.js b/app/services/return-requirements/submit-purpose.service.js index 22023df4fe..cd9d72172b 100644 --- a/app/services/return-requirements/submit-purpose.service.js +++ b/app/services/return-requirements/submit-purpose.service.js @@ -30,14 +30,16 @@ const SessionModel = require('../../models/session.model.js') */ async function go (sessionId, requirementIndex, payload, yar) { const session = await SessionModel.query().findById(sessionId) - const purposesData = await FetchLicencePurposesService.go(session.licence.id) + const licencePurposes = await FetchLicencePurposesService.go(session.licence.id) _handleOneOptionSelected(payload) - const validationResult = await _validate(payload, purposesData) + const purposes = _combinePurposeDetails(payload, licencePurposes) + + const validationResult = await _validate(purposes, licencePurposes) if (!validationResult) { - await _save(session, requirementIndex, payload) + await _save(session, requirementIndex, purposes) if (session.checkPageVisited) { GeneralLib.flashNotification(yar) @@ -48,33 +50,65 @@ async function go (sessionId, requirementIndex, payload, yar) { } } - const formattedData = PurposePresenter.go(session, requirementIndex, purposesData) + const submittedSessionData = _submittedSessionData(session, requirementIndex, purposes, licencePurposes) return { activeNavBar: 'search', error: validationResult, pageTitle: 'Select the purpose for the requirements for returns', - ...formattedData + ...submittedSessionData } } +function _combinePurposeDetails (payload, licencePurposes) { + const combinedValues = [] + + for (const purpose of payload.purposes) { + const alias = payload[`alias-${purpose}`] + const matchedLicencePurpose = licencePurposes.find((licencePurpose) => { + return licencePurpose.id === purpose + }) + + combinedValues.push({ + id: purpose, + alias: alias || '', + description: matchedLicencePurpose.description + }) + } + + return combinedValues +} + /** * When a single purpose is checked by the user, it returns as a string. When multiple purposes are checked, the * 'purposes' is returned as an array. This function works to make those single selected string 'purposes' into an array * for uniformity. */ function _handleOneOptionSelected (payload) { + if (!payload.purposes) { + payload.purposes = [] + } + if (!Array.isArray(payload.purposes)) { payload.purposes = [payload.purposes] } } -async function _save (session, requirementIndex, payload) { - session.requirements[requirementIndex].purposes = payload.purposes +async function _save (session, requirementIndex, purposes) { + session.requirements[requirementIndex].purposes = purposes return session.$update() } +/** + * Combines the existing session data with the submitted payload formatted by the presenter + */ +function _submittedSessionData (session, requirementIndex, purposes, licencePurposes) { + session.requirements[requirementIndex].purposes = purposes + + return PurposePresenter.go(session, requirementIndex, licencePurposes) +} + async function _validate (payload, purposesData) { const purposeIds = purposesData.map((purpose) => { return purpose.id diff --git a/app/validators/return-requirements/purpose.validator.js b/app/validators/return-requirements/purpose.validator.js index 12960d150b..f094872fd2 100644 --- a/app/validators/return-requirements/purpose.validator.js +++ b/app/validators/return-requirements/purpose.validator.js @@ -14,30 +14,50 @@ const Joi = require('joi') * Users must select one or more purposes linked to the licence. If these requirements are not met * the validation will return an error. * - * @param {Object} payload - The payload from the request to be validated + * Users also have the option to add an alias (purpose description) to the purpose. This is not required but if added + * we check to ensure it is no more than 100 characters. + * + * @param {Object[]} purposes - The selected purposes and aliases (if entered) from the payload * * @returns {Object} The result from calling Joi's schema.validate(). If any errors are found the * `error:` property will also exist detailing what the issue is. */ -function go (payload, purposeIds) { - const purposes = payload.purposes - +function go (purposes, purposeIds) { const errorMessage = 'Select any purpose for the requirements for returns' const schema = Joi.object({ purposes: Joi.array() - .items(Joi.string().valid(...purposeIds)) + .items({ + id: Joi + .string() + .valid(...purposeIds) + .required() + .messages({ + 'any.required': errorMessage, + 'any.only': errorMessage + }), + alias: Joi + .string() + .max(100) + .optional() + .allow('') + .messages({ + 'string.max': 'Purpose description must be 100 characters or less' + }), + // Description will not be persisted. It is simply to avoid having to fetch the purpose description again in + // the /check page. But if we didn't match a selected ID to a description in the SubmitPurposeService then + // something has gone wrong! + description: Joi + .string() + }) + .min(1) .required() .messages({ - 'any.required': errorMessage, - 'any.only': errorMessage, - 'array.includesOne': errorMessage, - 'array.includes': errorMessage, - 'array.sparse': errorMessage + 'array.min': errorMessage }) }) - return schema.validate({ purposes }, { abortEarly: false }) + return schema.validate({ purposes }, { abortEarly: true }) } module.exports = { diff --git a/app/views/return-requirements/purpose.njk b/app/views/return-requirements/purpose.njk index e832bf19b8..e71bd3a74b 100644 --- a/app/views/return-requirements/purpose.njk +++ b/app/views/return-requirements/purpose.njk @@ -4,6 +4,7 @@ {% from "govuk/components/checkboxes/macro.njk" import govukCheckboxes %} {% from "govuk/components/error-message/macro.njk" import govukErrorMessage %} {% from "govuk/components/error-summary/macro.njk" import govukErrorSummary %} +{% from "govuk/components/input/macro.njk" import govukInput %} {% block breadcrumbs %} {# Back link #} @@ -41,12 +42,32 @@
{% set checkBoxItems = [] %} - {% for purpose in licencePurposes %} + {% for purpose in purposes %} + {# Set an easier to use index. Also means we can refer to it inside our loop #} + {% set checkIndex = loop.index0 %} + + {% set aliasInput %} + {{ govukInput({ + id: 'alias-' + checkIndex, + name: 'alias-' + purpose.id, + type: 'text', + value: purpose.alias, + classes: "govuk-!-width-one-third", + label: { + text: "Add a purpose description (optional)" + }, + attributes: { 'data-test': 'purpose-alias-' + checkIndex } + }) }} + {% endset %} {% set checkBoxItem = { + id: 'purposes-' + checkIndex, + name: 'purposes', value: purpose.id, text: purpose.description, - checked: purpose.id in purposes + checked: purpose.checked, + attributes: { 'data-test': 'purpose-' + checkIndex }, + conditional: { html: aliasInput } } %} {% set checkBoxItems = (checkBoxItems.push(checkBoxItem), checkBoxItems) %} @@ -56,7 +77,8 @@ {{ govukCheckboxes({ name: "purposes", errorMessage: error, - items: checkBoxItems + items: checkBoxItems, + attributes: { 'data-test': 'purposes' } }) }} {{ govukButton({ text: "Continue", preventDoubleClick: true }) }} diff --git a/test/presenters/return-requirements/check/returns-requirements.presenter.test.js b/test/presenters/return-requirements/check/returns-requirements.presenter.test.js index 1d50590f15..57ce9139a2 100644 --- a/test/presenters/return-requirements/check/returns-requirements.presenter.test.js +++ b/test/presenters/return-requirements/check/returns-requirements.presenter.test.js @@ -12,195 +12,224 @@ const ReturnRequirementsPresenter = require('../../../../app/presenters/return-r describe('Return Requirements presenter', () => { let journey - let points = [] - let purposes = [] + let point let requirement - let requirements = [] - beforeEach(() => { - journey = {} - - purposes = [{ - id: '772136d1-9184-417b-90cd-91053287d1df', - description: 'A singular purpose' - }] - - requirement = { - abstractionPeriod: { - 'end-abstraction-period-day': '01', - 'end-abstraction-period-month': '03', - 'start-abstraction-period-day': '01', - 'start-abstraction-period-month': '06' - }, - agreementsExceptions: [ - 'gravity-fill' - ], - frequencyCollected: 'daily', - frequencyReported: 'daily', - points: [ - '9000031' - ], - purposes: [ - purposes[0].id - ], - returnsCycle: 'summer', - siteDescription: 'A place in the sun' - } - - points = [ - { - ID: '9000031', - AADD_ID: '9000020', - NGR1_EAST: '1234', - LOCAL_NAME: 'Test local name', - NGR1_NORTH: '1234', - NGR1_SHEET: 'TQ', - FGAC_REGION_CODE: '9' - } - ] - - requirements = [{ ...requirement }] - }) + describe('when provided requirements, points and a journey', () => { + beforeEach(() => { + journey = 'returns-required' + requirement = _requirement() + point = _point() + }) - describe('when provided requirements and purposes', () => { - it('correctly presents the data', () => { - const result = ReturnRequirementsPresenter.go(requirements, purposes, points, journey) - - expect(result).to.equal({ - returnsRequired: false, - requirements: [{ - abstractionPeriod: 'From 1 June to 1 March', - agreementsExceptions: 'Gravity fill', - frequencyCollected: 'daily', - frequencyReported: 'daily', - index: 0, - points: [ - 'At National Grid Reference TQ 1234 1234 (Test local name)' - ], - purposes: [ - 'A singular purpose' - ], - returnsCycle: 'Summer', - siteDescription: 'A place in the sun' - }] + describe('and the requirements are "complete"', () => { + beforeEach(() => { + requirement = _requirement() }) - }) - }) - describe("the 'requirements' property", () => { - describe('when the requirement is "complete" (agreements exceptions is populated)', () => { - it('correctly returns the requirement', () => { - const result = ReturnRequirementsPresenter.go(requirements, purposes, points, journey) - - expect(result.requirements).to.equal([{ - abstractionPeriod: 'From 1 June to 1 March', - agreementsExceptions: 'Gravity fill', - frequencyCollected: 'daily', - frequencyReported: 'daily', - index: 0, - points: [ - 'At National Grid Reference TQ 1234 1234 (Test local name)' - ], - purposes: [ - 'A singular purpose' - ], - returnsCycle: 'Summer', - siteDescription: 'A place in the sun' - } - ]) + it('correctly presents the data', () => { + const result = ReturnRequirementsPresenter.go([requirement], [point], journey) + + expect(result).to.equal({ + returnsRequired: true, + requirements: [{ + abstractionPeriod: 'From 1 June to 1 March', + agreementsExceptions: 'Gravity fill', + frequencyCollected: 'daily', + frequencyReported: 'daily', + index: 0, + points: [ + 'At National Grid Reference TQ 1234 1234 (Test local name)' + ], + purposes: [ + 'Spray irrigation' + ], + returnsCycle: 'Summer', + siteDescription: 'A place in the sun' + }] + }) }) - it('maps the selected purpose ID\'s to their description', () => { - const result = ReturnRequirementsPresenter.go(requirements, purposes, points, journey) + describe('the requirements "abstractionPeriod" property', () => { + it('formats the abstraction period for display', () => { + const result = ReturnRequirementsPresenter.go([requirement], [point], journey) + + const { abstractionPeriod } = result.requirements[0] - expect(result.requirements[0].purposes).to.equal(['A singular purpose']) + expect(abstractionPeriod).to.equal('From 1 June to 1 March') + }) }) - it('maps the selected points to the abstraction point details format', () => { - const result = ReturnRequirementsPresenter.go(requirements, purposes, points, journey) + describe('the requirements "agreementsExceptions" property', () => { + describe('when "none" was the selected option', () => { + beforeEach(() => { + requirement.agreementsExceptions = ['none'] + }) - expect(result.requirements[0].points).to.equal(['At National Grid Reference TQ 1234 1234 (Test local name)']) - }) + it('returns "None"', () => { + const result = ReturnRequirementsPresenter.go([requirement], [point], journey) + + const { agreementsExceptions } = result.requirements[0] + + expect(agreementsExceptions).to.equal('None') + }) + }) + + describe('when one option was selected', () => { + it("returns the option's display text (Gravity fill)", () => { + const result = ReturnRequirementsPresenter.go([requirement], [point], journey) - describe('and the return cycle is', () => { - describe('Summer', () => { - it('should return the text for a summer return cycle', () => { - const result = ReturnRequirementsPresenter.go(requirements, purposes, points, journey) + const { agreementsExceptions } = result.requirements[0] - expect(result.requirements[0].returnsCycle).to.equal('Summer') + expect(agreementsExceptions).to.equal('Gravity fill') }) }) - describe('Winter and all year', () => { + describe('when two options were selected', () => { beforeEach(() => { - requirements = [{ ...requirement, returnsCycle: 'winter-and-all-year' }] + requirement.agreementsExceptions = ['gravity-fill', 'transfer-re-abstraction-scheme'] }) - it('should return the text for a Winter and all year return cycle', () => { - const result = ReturnRequirementsPresenter.go(requirements, purposes, points, journey) + it('returns the options display text joined with an "and" (Gravity fill and Transfer re-abstraction scheme)', () => { + const result = ReturnRequirementsPresenter.go([requirement], [point], journey) + + const { agreementsExceptions } = result.requirements[0] - expect(result.requirements[0].returnsCycle).to.equal('Winter and all year') + expect(agreementsExceptions).to.equal('Gravity fill and Transfer re-abstraction scheme') }) }) - }) - describe('and the agreement exceptions', () => { - describe('is "none"', () => { + describe('when more than two options were selected', () => { beforeEach(() => { - requirements = [{ ...requirement, agreementsExceptions: ['none'] }] + requirement.agreementsExceptions = ['gravity-fill', 'transfer-re-abstraction-scheme', 'two-part-tariff', '56-returns-exception'] }) - it('should return "None"', () => { - const result = ReturnRequirementsPresenter.go(requirements, purposes, points, journey) + it('returns the options display text joined with an ", and" "Gravity fill, Transfer re-abstraction scheme, Two-part tariff, and 56 returns exception"', () => { + const result = ReturnRequirementsPresenter.go([requirement], [point], journey) + + const { agreementsExceptions } = result.requirements[0] - expect(result.requirements[0].agreementsExceptions).to.equal('None') + expect(agreementsExceptions).to.equal('Gravity fill, Transfer re-abstraction scheme, Two-part tariff, and 56 returns exception') }) }) + }) - describe('has one exception', () => { - it('should return the exception as "Gravity fill"', () => { - const result = ReturnRequirementsPresenter.go(requirements, purposes, points, journey) + describe('the requirements "points" property', () => { + // Formatting of the points uses GeneralLib.generateAbstractionPointDetail() so testing is 'light' here + it('formats the points for display', () => { + const result = ReturnRequirementsPresenter.go([requirement], [point], journey) - expect(result.requirements[0].agreementsExceptions).to.equal('Gravity fill') - }) + const { points } = result.requirements[0] + + expect(points).to.equal(['At National Grid Reference TQ 1234 1234 (Test local name)']) }) + }) - describe('has two exceptions', () => { + describe('the requirements "purposes" property', () => { + describe('when a purpose description (alias) was added to the selected purpose', () => { beforeEach(() => { - requirements = [{ ...requirement, agreementsExceptions: ['gravity-fill', 'transfer-re-abstraction-scheme'] }] + requirement.purposes[0].alias = 'spray indiscreetly' + }) + + it('formats the purposes for display with the purpose description in brackets', () => { + const result = ReturnRequirementsPresenter.go([requirement], [point], journey) + + const { purposes } = result.requirements[0] + + expect(purposes).to.equal(['Spray irrigation (spray indiscreetly)']) + }) + }) + + describe('when a purpose description (alias) was not added to the selected purpose', () => { + it('formats the purposes for display with just the default description', () => { + const result = ReturnRequirementsPresenter.go([requirement], [point], journey) + + const { purposes } = result.requirements[0] + + expect(purposes).to.equal(['Spray irrigation']) }) + }) + }) - it('should return the exceptions as "Gravity fill and Transfer re-abstraction scheme"', () => { - const result = ReturnRequirementsPresenter.go(requirements, purposes, points, journey) + describe('the requirements "returnsCycle" property', () => { + describe('when the requirement is for the "summer" returns cycle', () => { + it('formats the cycle for display (Summer)', () => { + const result = ReturnRequirementsPresenter.go([requirement], [point], journey) - expect(result.requirements[0].agreementsExceptions).to.equal('Gravity fill and Transfer re-abstraction scheme') + const { returnsCycle } = result.requirements[0] + + expect(returnsCycle).to.equal('Summer') }) }) - describe('has more than two exceptions', () => { + describe('when the requirement is for the "winter-and-all-year" returns cycle', () => { beforeEach(() => { - requirements = [{ ...requirement, agreementsExceptions: ['gravity-fill', 'transfer-re-abstraction-scheme', 'two-part-tariff', '56-returns-exception'] }] + requirement.returnsCycle = 'winter-and-all-year' }) - it('should return the exceptions as "Gravity fill, Transfer re-abstraction scheme, Two-part tariff, and 56 returns exception"', () => { - const result = ReturnRequirementsPresenter.go(requirements, purposes, points, journey) + it('formats the cycle for display (Winter and all year)', () => { + const result = ReturnRequirementsPresenter.go([requirement], [point], journey) + + const { returnsCycle } = result.requirements[0] - expect(result.requirements[0].agreementsExceptions).to.equal('Gravity fill, Transfer re-abstraction scheme, Two-part tariff, and 56 returns exception') + expect(returnsCycle).to.equal('Winter and all year') }) }) }) }) - describe('when the requirement is "incomplete" (agreements exceptions is not populated)', () => { + describe('and none of the requirements are "complete"', () => { beforeEach(() => { - delete requirements[0].agreementsExceptions + requirement = _requirement() + delete requirement.agreementsExceptions }) - it('does not return the requirement', () => { - const result = ReturnRequirementsPresenter.go(requirements, purposes, journey) + it('correctly presents the data (empty requirements)', () => { + const result = ReturnRequirementsPresenter.go([requirement], [point], journey) - expect(result.requirements).to.equal([]) + expect(result).to.equal({ + returnsRequired: true, + requirements: [] + }) }) }) }) }) + +function _point () { + return { + ID: '9000031', + AADD_ID: '9000020', + NGR1_EAST: '1234', + LOCAL_NAME: 'Test local name', + NGR1_NORTH: '1234', + NGR1_SHEET: 'TQ', + FGAC_REGION_CODE: '9' + } +} + +function _requirement () { + return { + abstractionPeriod: { + 'end-abstraction-period-day': '01', + 'end-abstraction-period-month': '03', + 'start-abstraction-period-day': '01', + 'start-abstraction-period-month': '06' + }, + agreementsExceptions: [ + 'gravity-fill' + ], + frequencyCollected: 'daily', + frequencyReported: 'daily', + points: [ + '9000031' + ], + purposes: [{ + id: '772136d1-9184-417b-90cd-91053287d1df', + alias: '', + description: 'Spray irrigation' + }], + returnsCycle: 'summer', + siteDescription: 'A place in the sun' + } +} diff --git a/test/presenters/return-requirements/purpose.presenter.test.js b/test/presenters/return-requirements/purpose.presenter.test.js index cd334dc0e1..c507e9231d 100644 --- a/test/presenters/return-requirements/purpose.presenter.test.js +++ b/test/presenters/return-requirements/purpose.presenter.test.js @@ -13,11 +13,11 @@ const PurposePresenter = require('../../../app/presenters/return-requirements/pu describe('Return Requirements - Purpose presenter', () => { const requirementIndex = 0 - let purposesData + let licencePurposes let session beforeEach(() => { - purposesData = [ + licencePurposes = [ { id: '14794d57-1acf-4c91-8b48-4b1ec68bfd6f', description: 'Heat Pump' }, { id: '49088608-ee9f-491a-8070-6831240945ac', description: 'Horticultural Watering' }, { id: '1d03c79b-da97-4838-a68c-ccb613d54367', description: 'Hydraulic Rams' }, @@ -44,19 +44,18 @@ describe('Return Requirements - Purpose presenter', () => { describe('when provided with a session', () => { it('correctly presents the data', () => { - const result = PurposePresenter.go(session, requirementIndex, purposesData) + const result = PurposePresenter.go(session, requirementIndex, licencePurposes) expect(result).to.equal({ backLink: '/system/return-requirements/61e07498-f309-4829-96a9-72084a54996d/setup', licenceId: '8b7f78ba-f3ad-4cb6-a058-78abc4d1383d', - licencePurposes: [ - { id: '14794d57-1acf-4c91-8b48-4b1ec68bfd6f', description: 'Heat Pump' }, - { id: '49088608-ee9f-491a-8070-6831240945ac', description: 'Horticultural Watering' }, - { id: '1d03c79b-da97-4838-a68c-ccb613d54367', description: 'Hydraulic Rams' }, - { id: '02036782-81d2-43be-b6af-bf20898653e1', description: 'Hydraulic Testing' } - ], licenceRef: '01/ABC', - purposes: '', + purposes: [ + { alias: '', checked: false, description: 'Heat Pump', id: '14794d57-1acf-4c91-8b48-4b1ec68bfd6f' }, + { alias: '', checked: false, description: 'Horticultural Watering', id: '49088608-ee9f-491a-8070-6831240945ac' }, + { alias: '', checked: false, description: 'Hydraulic Rams', id: '1d03c79b-da97-4838-a68c-ccb613d54367' }, + { alias: '', checked: false, description: 'Hydraulic Testing', id: '02036782-81d2-43be-b6af-bf20898653e1' } + ], sessionId: '61e07498-f309-4829-96a9-72084a54996d' }) }) @@ -64,57 +63,114 @@ describe('Return Requirements - Purpose presenter', () => { describe('the "backLink" property', () => { describe('when the user has come from the "check" page', () => { - beforeEach(() => { - session.checkPageVisited = true + describe('because they wish to change the purpose', () => { + beforeEach(() => { + session.checkPageVisited = true + }) + + it('returns a link back to the "check" page', () => { + const result = PurposePresenter.go(session, requirementIndex, licencePurposes) + + expect(result.backLink).to.equal('/system/return-requirements/61e07498-f309-4829-96a9-72084a54996d/check') + }) }) - it('returns a link back to the "check" page', () => { - const result = PurposePresenter.go(session, requirementIndex, purposesData) + describe('because they are adding a new return requirement', () => { + beforeEach(() => { + // The logic to determine we came from the /check to add a new requirement looks at the number of + // requirements. Hence we just need to add a second to exercise that branch of the logic + session.requirements.push({}) + session.checkPageVisited = false + }) + + it('returns a link back to the "check" page', () => { + const result = PurposePresenter.go(session, requirementIndex, licencePurposes) - expect(result.backLink).to.equal('/system/return-requirements/61e07498-f309-4829-96a9-72084a54996d/check') + expect(result.backLink).to.equal('/system/return-requirements/61e07498-f309-4829-96a9-72084a54996d/check') + }) }) }) describe('when the user has come from somewhere else', () => { it('returns a link back to the "setup" page', () => { - const result = PurposePresenter.go(session, requirementIndex, purposesData) + const result = PurposePresenter.go(session, requirementIndex, licencePurposes) expect(result.backLink).to.equal('/system/return-requirements/61e07498-f309-4829-96a9-72084a54996d/setup') }) }) }) - describe('the "licencePurposes" property', () => { - it('returns the id and description from each "purpose" passed in', () => { - const result = PurposePresenter.go(session, requirementIndex, purposesData) - - expect(result.licencePurposes).to.equal([ - { id: '14794d57-1acf-4c91-8b48-4b1ec68bfd6f', description: 'Heat Pump' }, - { id: '49088608-ee9f-491a-8070-6831240945ac', description: 'Horticultural Watering' }, - { id: '1d03c79b-da97-4838-a68c-ccb613d54367', description: 'Hydraulic Rams' }, - { id: '02036782-81d2-43be-b6af-bf20898653e1', description: 'Hydraulic Testing' } - ]) - }) - }) - describe('the "purposes" property', () => { describe('when the user has previously submitted purposes', () => { beforeEach(() => { - session.requirements[0].purposes = ['Heat Pump', 'Hydraulic Rams'] + session.requirements[0].purposes = [ + { id: '14794d57-1acf-4c91-8b48-4b1ec68bfd6f', description: 'Heat Pump', alias: '' }, + { id: '1d03c79b-da97-4838-a68c-ccb613d54367', description: 'Hydraulic Rams', alias: 'Steampunk sheep' } + ] }) - it('returns a populated purposes', () => { - const result = PurposePresenter.go(session, requirementIndex, purposesData) - - expect(result.purposes).to.equal('Heat Pump,Hydraulic Rams') + it('returns all the licence purposes with those previously submitted checked and optional aliases populate', () => { + const result = PurposePresenter.go(session, requirementIndex, licencePurposes) + + expect(result.purposes).to.equal([ + { + alias: '', + checked: true, + description: 'Heat Pump', + id: '14794d57-1acf-4c91-8b48-4b1ec68bfd6f' + }, + { + alias: '', + checked: false, + description: 'Horticultural Watering', + id: '49088608-ee9f-491a-8070-6831240945ac' + }, + { + alias: 'Steampunk sheep', + checked: true, + description: 'Hydraulic Rams', + id: '1d03c79b-da97-4838-a68c-ccb613d54367' + }, + { + alias: '', + checked: false, + description: 'Hydraulic Testing', + id: '02036782-81d2-43be-b6af-bf20898653e1' + } + ]) }) }) describe('when the user has not previously submitted a purpose', () => { - it('returns an empty purposes', () => { - const result = PurposePresenter.go(session, requirementIndex, purposesData) - - expect(result.purposes).to.equal('') + it('returns all the licence purposes with nothing checked and no aliases populated', () => { + const result = PurposePresenter.go(session, requirementIndex, licencePurposes) + + expect(result.purposes).to.equal([ + { + alias: '', + checked: false, + description: 'Heat Pump', + id: '14794d57-1acf-4c91-8b48-4b1ec68bfd6f' + }, + { + alias: '', + checked: false, + description: 'Horticultural Watering', + id: '49088608-ee9f-491a-8070-6831240945ac' + }, + { + alias: '', + checked: false, + description: 'Hydraulic Rams', + id: '1d03c79b-da97-4838-a68c-ccb613d54367' + }, + { + alias: '', + checked: false, + description: 'Hydraulic Testing', + id: '02036782-81d2-43be-b6af-bf20898653e1' + } + ]) }) }) }) diff --git a/test/services/return-requirements/check.service.test.js b/test/services/return-requirements/check.service.test.js index f7f9231d18..b79fc1cd00 100644 --- a/test/services/return-requirements/check.service.test.js +++ b/test/services/return-requirements/check.service.test.js @@ -9,8 +9,7 @@ const { describe, it, beforeEach, afterEach } = exports.lab = Lab.script() const { expect } = Code // Test helpers -const DatabaseSupport = require('../../support/database.js') -const ReturnRequirementsService = require('../../../app/services/return-requirements/check/returns-requirements.service.js') +const FetchPointsService = require('../../../app/services/return-requirements/fetch-points.service.js') const SessionHelper = require('../../support/helpers/session.helper.js') // Thing under test @@ -21,12 +20,7 @@ describe('Return Requirements - Check service', () => { let yarStub beforeEach(async () => { - Sinon.stub(ReturnRequirementsService, 'go').resolves({ - requirements: [], - returnsRequired: true - }) - - await DatabaseSupport.clean() + Sinon.stub(FetchPointsService, 'go').resolves([]) session = await SessionHelper.add({ data: { diff --git a/test/services/return-requirements/check/returns-requirements.service.test.js b/test/services/return-requirements/check/returns-requirements.service.test.js deleted file mode 100644 index 687e7fe899..0000000000 --- a/test/services/return-requirements/check/returns-requirements.service.test.js +++ /dev/null @@ -1,155 +0,0 @@ -'use strict' - -// Test framework dependencies -const Lab = require('@hapi/lab') -const Code = require('@hapi/code') - -const { describe, it, beforeEach } = exports.lab = Lab.script() -const { expect } = Code - -// Test helpers -const DatabaseSupport = require('../../../support/database.js') -const PurposeHelper = require('../../../support/helpers/purpose.helper.js') -const LicenceHelper = require('../../../support/helpers/licence.helper.js') -const PermitLicenceHelper = require('../../../support/helpers/permit-licence.helper.js') - -// Thing under test -const ReturnRequirementsService = require('../../../../app/services/return-requirements/check/returns-requirements.service.js') - -describe('Return Requirements service', () => { - let licence - let points - let purpose - let requirement - let session - - beforeEach(async () => { - await DatabaseSupport.clean() - - purpose = await PurposeHelper.add() - - points = await PermitLicenceHelper.add() - - licence = await LicenceHelper.add({ - licenceRef: points.licenceRef - }) - - requirement = { - abstractionPeriod: { - 'end-abstraction-period-day': '01', - 'end-abstraction-period-month': '03', - 'start-abstraction-period-day': '01', - 'start-abstraction-period-month': '06' - }, - agreementsExceptions: [ - 'gravity-fill' - ], - frequencyCollected: 'daily', - frequencyReported: 'daily', - points: [ - '9000031' // Hard coded from the permit licence data - ], - purposes: [ - purpose.id - ], - returnsCycle: 'summer', - siteDescription: 'A place in the sun' - } - }) - - describe('when the session contains a single requirement', () => { - beforeEach(() => { - session = { - licence: { - id: licence.id - }, - journey: 'returns-required', - requirements: [{ ...requirement }] - } - }) - - it('returns the return requirements data', async () => { - const result = await ReturnRequirementsService.go(session) - - expect(result).to.equal({ - requirements: [{ - abstractionPeriod: 'From 1 June to 1 March', - agreementsExceptions: 'Gravity fill', - frequencyCollected: 'daily', - frequencyReported: 'daily', - index: 0, - points: [ - 'At National Grid Reference TQ 1234 1234 (Test local name 1)' - ], - purposes: [ - 'Spray Irrigation - Storage' - ], - returnsCycle: 'Summer', - siteDescription: 'A place in the sun' - }], - returnsRequired: true - }) - }) - - describe('and the requirement has points', () => { - it('returns point data formatted to the abstraction point details format', async () => { - const result = await ReturnRequirementsService.go(session) - - expect(result.requirements[0].points).to.equal([ - 'At National Grid Reference TQ 1234 1234 (Test local name 1)' - ]) - }) - }) - }) - - describe('when two or more requirements have purpose ids', () => { - describe('and they are different', () => { - let purposeAdditional - - beforeEach(async () => { - purposeAdditional = await PurposeHelper.add({ - description: 'Sunny field' - }) - - session = { - licence: { - id: licence.id - }, - journey: 'returns-required', - requirements: [{ ...requirement }, { ...requirement, purposes: [purposeAdditional.id] }] - } - - it('returns the first requirement with the correct purpose description ', async () => { - const result = await ReturnRequirementsService.go(session) - - expect(result.requirements[0].purposes).to.equal(['Spray Irrigation - Storage']) - }) - - it('returns the second requirement with the correct purpose description ', async () => { - const result = await ReturnRequirementsService.go(session) - - expect(result.requirements[1].purposes).to.equal(['Sunny field']) - }) - }) - }) - - describe('and the description is the same', () => { - beforeEach(async () => { - session = { - licence: { - id: licence.id - }, - journey: 'returns-required', - requirements: [{ ...requirement }, { ...requirement }] - } - }) - - it('returns the requirements with the same description', async () => { - const result = await ReturnRequirementsService.go(session) - - expect(result.requirements[0].purposes).to.equal(['Spray Irrigation - Storage']) - expect(result.requirements[1].purposes).to.equal(['Spray Irrigation - Storage']) - }) - }) - }) -}) diff --git a/test/services/return-requirements/purpose.service.test.js b/test/services/return-requirements/purpose.service.test.js index 4fb37bf749..15be26a4a6 100644 --- a/test/services/return-requirements/purpose.service.test.js +++ b/test/services/return-requirements/purpose.service.test.js @@ -69,14 +69,13 @@ describe('Return Requirements - Purpose service', () => { pageTitle: 'Select the purpose for the requirements for returns', backLink: `/system/return-requirements/${session.id}/setup`, licenceId: '8b7f78ba-f3ad-4cb6-a058-78abc4d1383d', - licencePurposes: [ - { id: '14794d57-1acf-4c91-8b48-4b1ec68bfd6f', description: 'Heat Pump' }, - { id: '49088608-ee9f-491a-8070-6831240945ac', description: 'Horticultural Watering' } - ], licenceRef: '01/ABC', - purposes: '', + purposes: [ + { alias: '', checked: false, description: 'Heat Pump', id: '14794d57-1acf-4c91-8b48-4b1ec68bfd6f' }, + { alias: '', checked: false, description: 'Horticultural Watering', id: '49088608-ee9f-491a-8070-6831240945ac' } + ], sessionId: session.id - }, { skip: ['sessionId'] }) + }) }) }) }) diff --git a/test/services/return-requirements/submit-purpose.service.test.js b/test/services/return-requirements/submit-purpose.service.test.js index e62f6e1891..a0f8751d90 100644 --- a/test/services/return-requirements/submit-purpose.service.test.js +++ b/test/services/return-requirements/submit-purpose.service.test.js @@ -9,7 +9,6 @@ const { describe, it, beforeEach, afterEach } = exports.lab = Lab.script() const { expect } = Code // Test helpers -const DatabaseSupport = require('../../support/database.js') const SessionHelper = require('../../support/helpers/session.helper.js') // Things we need to stub @@ -27,8 +26,6 @@ describe('Return Requirements - Submit Purpose service', () => { let yarStub beforeEach(async () => { - await DatabaseSupport.clean() - sessionData = { data: { checkPageVisited: false, @@ -50,6 +47,11 @@ describe('Return Requirements - Submit Purpose service', () => { session = await SessionHelper.add(sessionData) yarStub = { flash: Sinon.stub() } + + Sinon.stub(FetchLicencePurposesService, 'go').resolves([ + { id: '14794d57-1acf-4c91-8b48-4b1ec68bfd6f', description: 'Heat Pump' }, + { id: '49088608-ee9f-491a-8070-6831240945ac', description: 'Horticultural Watering' } + ]) }) afterEach(() => { @@ -60,14 +62,9 @@ describe('Return Requirements - Submit Purpose service', () => { describe('with a valid payload', () => { beforeEach(async () => { payload = { - purposes: ['14794d57-1acf-4c91-8b48-4b1ec68bfd6f'] + purposes: ['14794d57-1acf-4c91-8b48-4b1ec68bfd6f'], + 'alias-14794d57-1acf-4c91-8b48-4b1ec68bfd6f': 'great warm machine' } - - Sinon.stub(FetchLicencePurposesService, 'go').resolves([ - { id: '14794d57-1acf-4c91-8b48-4b1ec68bfd6f', description: 'Heat Pump' }, - { id: '49088608-ee9f-491a-8070-6831240945ac', description: 'Horticultural Watering' }, - { id: '8290bb6a-4265-4cc8-b9bb-37cde1357d5d', description: 'Large Garden Watering' } - ]) }) it('saves the submitted value', async () => { @@ -75,7 +72,9 @@ describe('Return Requirements - Submit Purpose service', () => { const refreshedSession = await session.$query() - expect(refreshedSession.requirements[0].purposes).to.equal(['14794d57-1acf-4c91-8b48-4b1ec68bfd6f']) + expect(refreshedSession.requirements[0].purposes).to.equal([ + { alias: 'great warm machine', description: 'Heat Pump', id: '14794d57-1acf-4c91-8b48-4b1ec68bfd6f' } + ]) }) describe('and the page has been not been visited', () => { @@ -113,43 +112,69 @@ describe('Return Requirements - Submit Purpose service', () => { }) describe('with an invalid payload', () => { - beforeEach(async () => { - payload = {} + describe('because it is empty', () => { + beforeEach(async () => { + payload = {} + }) - Sinon.stub(FetchLicencePurposesService, 'go').resolves([ - { id: '14794d57-1acf-4c91-8b48-4b1ec68bfd6f', description: 'Heat Pump' }, - { id: '49088608-ee9f-491a-8070-6831240945ac', description: 'Horticultural Watering' }, - { id: '8290bb6a-4265-4cc8-b9bb-37cde1357d5d', description: 'Large Garden Watering' } - ]) + it('returns page data for the view', async () => { + const result = await SubmitPurposeService.go(session.id, requirementIndex, payload, yarStub) + + expect(result).to.equal({ + activeNavBar: 'search', + error: { + text: 'Select any purpose for the requirements for returns' + }, + pageTitle: 'Select the purpose for the requirements for returns', + backLink: `/system/return-requirements/${session.id}/setup`, + licenceId: '8b7f78ba-f3ad-4cb6-a058-78abc4d1383d', + licenceRef: '01/ABC', + purposes: [ + { alias: '', checked: false, description: 'Heat Pump', id: '14794d57-1acf-4c91-8b48-4b1ec68bfd6f' }, + { alias: '', checked: false, description: 'Horticultural Watering', id: '49088608-ee9f-491a-8070-6831240945ac' } + ], + sessionId: session.id + }) + }) }) - it('returns page data for the view', async () => { - const result = await SubmitPurposeService.go(session.id, requirementIndex, payload, yarStub) - - expect(result).to.equal({ - activeNavBar: 'search', - pageTitle: 'Select the purpose for the requirements for returns', - backLink: `/system/return-requirements/${session.id}/setup`, - licenceId: '8b7f78ba-f3ad-4cb6-a058-78abc4d1383d', - error: { - text: 'Select any purpose for the requirements for returns' - }, - licencePurposes: [{ - description: 'Heat Pump', - id: '14794d57-1acf-4c91-8b48-4b1ec68bfd6f' - }, - { - description: 'Horticultural Watering', - id: '49088608-ee9f-491a-8070-6831240945ac' - }, - { - description: 'Large Garden Watering', - id: '8290bb6a-4265-4cc8-b9bb-37cde1357d5d' - }], - licenceRef: '01/ABC', - purposes: '', - sessionId: session.id - }, { skip: ['sessionId', 'error'] }) + describe('because they entered an alias that is too long', () => { + beforeEach(async () => { + payload = { + purposes: '14794d57-1acf-4c91-8b48-4b1ec68bfd6f', + 'alias-14794d57-1acf-4c91-8b48-4b1ec68bfd6f': 'THGBk2GM85EyXB54SsfenU2yWiKjDuPTcJCrPfTsSzojNvj6ciVmI3PXJ2fisQgXWfSI4ZPIqV5GLPtR15qbcw3Hamoeit764Cojz' + } + }) + + it('returns page data for the view', async () => { + const result = await SubmitPurposeService.go(session.id, requirementIndex, payload, yarStub) + + expect(result).to.equal({ + activeNavBar: 'search', + error: { + text: 'Purpose description must be 100 characters or less' + }, + pageTitle: 'Select the purpose for the requirements for returns', + backLink: `/system/return-requirements/${session.id}/setup`, + licenceId: '8b7f78ba-f3ad-4cb6-a058-78abc4d1383d', + licenceRef: '01/ABC', + purposes: [ + { + alias: 'THGBk2GM85EyXB54SsfenU2yWiKjDuPTcJCrPfTsSzojNvj6ciVmI3PXJ2fisQgXWfSI4ZPIqV5GLPtR15qbcw3Hamoeit764Cojz', + checked: true, + description: 'Heat Pump', + id: '14794d57-1acf-4c91-8b48-4b1ec68bfd6f' + }, + { + alias: '', + checked: false, + description: 'Horticultural Watering', + id: '49088608-ee9f-491a-8070-6831240945ac' + } + ], + sessionId: session.id + }) + }) }) }) }) diff --git a/test/validators/return-requirements/purpose.validator.test.js b/test/validators/return-requirements/purpose.validator.test.js index dad7ea32b2..b9a0eea292 100644 --- a/test/validators/return-requirements/purpose.validator.test.js +++ b/test/validators/return-requirements/purpose.validator.test.js @@ -4,65 +4,170 @@ const Lab = require('@hapi/lab') const Code = require('@hapi/code') -const { describe, it } = exports.lab = Lab.script() +const { describe, it, beforeEach } = exports.lab = Lab.script() const { expect } = Code // Thing under test const PurposeValidator = require('../../../app/validators/return-requirements/purpose.validator.js') describe('Purpose validator', () => { - const purposeIds = [ + const validPurposeIds = [ '14794d57-1acf-4c91-8b48-4b1ec68bfd6f', '49088608-ee9f-491a-8070-6831240945ac', '1d03c79b-da97-4838-a68c-ccb613d54367', '02036782-81d2-43be-b6af-bf20898653e1' ] + let purposes + describe('when valid data is provided', () => { - const payload = { - purposes: [ - '14794d57-1acf-4c91-8b48-4b1ec68bfd6f', - '49088608-ee9f-491a-8070-6831240945ac' - ] - } - - it('confirms the data is valid', () => { - const result = PurposeValidator.go(payload, purposeIds) - - expect(result.value).to.exist() - expect(result.error).not.to.exist() + describe('that has purpose IDs, aliases and descriptions', () => { + beforeEach(() => { + purposes = [ + { + id: '14794d57-1acf-4c91-8b48-4b1ec68bfd6f', + alias: 'Spray irrigation indiscreet', + description: 'Spray irrigation - direct' + }, + { + id: '1d03c79b-da97-4838-a68c-ccb613d54367', + alias: 'Trickle irrigation all over', + description: 'Trickle irrigation' + } + ] + }) + + it('confirms the data is valid', () => { + const result = PurposeValidator.go(purposes, validPurposeIds) + + expect(result.value).to.exist() + expect(result.error).not.to.exist() + }) + }) + + describe('that has purpose IDs, descriptions, but only some aliases', () => { + beforeEach(() => { + purposes = [ + { + id: '14794d57-1acf-4c91-8b48-4b1ec68bfd6f', + alias: 'Spray irrigation indiscreet', + description: 'Spray irrigation - direct' + }, + { + id: '1d03c79b-da97-4838-a68c-ccb613d54367', + alias: '', + description: 'Trickle irrigation' + } + ] + }) + + it('confirms the data is valid', () => { + const result = PurposeValidator.go(purposes, validPurposeIds) + + expect(result.value).to.exist() + expect(result.error).not.to.exist() + }) + }) + + describe('that has purpose IDs, descriptions, and no aliases', () => { + beforeEach(() => { + purposes = [ + { id: '14794d57-1acf-4c91-8b48-4b1ec68bfd6f', alias: '', description: 'Spray irrigation - direct' }, + { id: '1d03c79b-da97-4838-a68c-ccb613d54367', alias: '', description: 'Trickle irrigation' } + ] + }) + + it('confirms the data is valid', () => { + const result = PurposeValidator.go(purposes, validPurposeIds) + + expect(result.value).to.exist() + expect(result.error).not.to.exist() + }) + }) + + describe('that has purpose IDs but no descriptions or aliases', () => { + beforeEach(() => { + purposes = [ + { id: '14794d57-1acf-4c91-8b48-4b1ec68bfd6f', alias: '' }, + { id: '1d03c79b-da97-4838-a68c-ccb613d54367', alias: '' } + ] + }) + + it('confirms the data is valid', () => { + const result = PurposeValidator.go(purposes, validPurposeIds) + + expect(result.value).to.exist() + expect(result.error).not.to.exist() + }) }) }) describe('when invalid data is provided', () => { - const payload = { - purposes: [ - 'Invalid purpose', - 'Invalid purpose', - 'Invalid purpose', - 'Invalid purpose', - 'Invalid purpose' - ] - } - - it('fails validation', () => { - const result = PurposeValidator.go(payload, purposeIds) - - expect(result.value).to.exist() - expect(result.error).to.exist() - expect(result.error.details[0].message).to.equal('Select any purpose for the requirements for returns') + describe('because it contains an invalid purpose id', () => { + beforeEach(() => { + purposes = [ + { id: 'c73399a6-2194-4d50-b4c8-c4fcf225c711', alias: '', description: 'Spray irrigation - direct' } + ] + }) + + it('fails validation', () => { + const result = PurposeValidator.go(purposes, validPurposeIds) + + expect(result.value).to.exist() + expect(result.error).to.exist() + expect(result.error.details[0].message).to.equal('Select any purpose for the requirements for returns') + }) + }) + + describe('because it does not contain a purpose id', () => { + beforeEach(() => { + purposes = [ + { alias: 'Spray irrigation indiscreet', description: 'Spray irrigation - direct' } + ] + }) + + it('fails validation', () => { + const result = PurposeValidator.go(purposes, validPurposeIds) + + expect(result.value).to.exist() + expect(result.error).to.exist() + expect(result.error.details[0].message).to.equal('Select any purpose for the requirements for returns') + }) + }) + + describe('because it contains an an alias that is too long', () => { + beforeEach(() => { + // Max is 100 characters. This is 101 characters + purposes = [ + { + id: '14794d57-1acf-4c91-8b48-4b1ec68bfd6f', + alias: 'THGBk2GM85EyXB54SsfenU2yWiKjDuPTcJCrPfTsSzojNvj6ciVmI3PXJ2fisQgXWfSI4ZPIqV5GLPtR15qbcw3Hamoeit764Cojz', + description: 'Spray irrigation - direct' + } + ] + }) + + it('fails validation', () => { + const result = PurposeValidator.go(purposes, validPurposeIds) + + expect(result.value).to.exist() + expect(result.error).to.exist() + expect(result.error.details[0].message).to.equal('Purpose description must be 100 characters or less') + }) }) - }) - describe('when no data is provided', () => { - const payload = {} + describe('because it is empty', () => { + beforeEach(() => { + purposes = [] + }) - it('fails validation', () => { - const result = PurposeValidator.go(payload, purposeIds) + it('fails validation', () => { + const result = PurposeValidator.go(purposes, validPurposeIds) - expect(result.value).to.exist() - expect(result.error).to.exist() - expect(result.error.details[0].message).to.equal('Select any purpose for the requirements for returns') + expect(result.value).to.exist() + expect(result.error).to.exist() + expect(result.error.details[0].message).to.equal('Select any purpose for the requirements for returns') + }) }) }) })