From 42e4c11f8b52a01db279172bb05277887686d2a7 Mon Sep 17 00:00:00 2001 From: Tony Barnes Date: Fri, 20 Sep 2024 11:52:32 +0100 Subject: [PATCH 1/2] feat(EMS-3829): export contract - how much is the agent charging - page setup, validation (#3072) * feat(EMS-3826-3827): export contract - agenct charges currency - page setup * feat(EMS-3826-3827): export contract - agenct charges currency - page setup * feat(EMS-3829): export contract - how much is agent charging - page setup * feat(EMS-3829): export contract - how much is agent charging - e2e tests * feat(EMS-3829): fix/update e2e tests * feat(EMS-3829): update alternative-currency nunjucks template * chore(EMS-3829): fix/update route constants * feat(EMS-3829): add missing unit test --- ...mit-how-much-is-the-agent-charging-form.js | 20 ++ .../routes/insurance/export-contract.js | 12 +- .../fields/insurance/export-contract/index.js | 1 + .../pages/insurance/export-contract/index.js | 6 + .../how-much-is-the-agent-charging.spec.js | 86 ++++++ ...h-is-the-agent-charging-validation.spec.js | 80 +++++ .../application/export-contract/index.js | 5 + .../routes/insurance/export-contract.ts | 12 +- .../insurance/export-contract/index.ts | 1 + .../fields/insurance/export-contract/index.ts | 1 + .../pages/insurance/export-contract/index.ts | 6 + .../index.test.ts | 282 ++++++++++++++++++ .../how-much-is-the-agent-charging/index.ts | 174 +++++++++++ .../validation/index.test.ts | 15 + .../validation/index.ts | 7 + .../rules/fixed-sum-amount/index.test.ts | 35 +++ .../rules/fixed-sum-amount/index.ts | 31 ++ .../validation/rules/index.ts | 8 + .../insurance/export-contract/index.test.ts | 12 +- .../routes/insurance/export-contract/index.ts | 8 + src/ui/server/routes/insurance/index.test.ts | 4 +- .../how-much-is-the-agent-charging.njk | 62 ++++ .../shared-pages/alternative-currency.njk | 15 +- 23 files changed, 863 insertions(+), 20 deletions(-) create mode 100644 e2e-tests/commands/insurance/complete-and-submit-how-much-is-the-agent-charging-form.js create mode 100644 e2e-tests/insurance/cypress/e2e/journeys/export-contract/how-much-is-the-agent-charging/how-much-is-the-agent-charging.spec.js create mode 100644 e2e-tests/insurance/cypress/e2e/journeys/export-contract/how-much-is-the-agent-charging/validation/how-much-is-the-agent-charging-validation.spec.js create mode 100644 src/ui/server/controllers/insurance/export-contract/how-much-is-the-agent-charging/index.test.ts create mode 100644 src/ui/server/controllers/insurance/export-contract/how-much-is-the-agent-charging/index.ts create mode 100644 src/ui/server/controllers/insurance/export-contract/how-much-is-the-agent-charging/validation/index.test.ts create mode 100644 src/ui/server/controllers/insurance/export-contract/how-much-is-the-agent-charging/validation/index.ts create mode 100644 src/ui/server/controllers/insurance/export-contract/how-much-is-the-agent-charging/validation/rules/fixed-sum-amount/index.test.ts create mode 100644 src/ui/server/controllers/insurance/export-contract/how-much-is-the-agent-charging/validation/rules/fixed-sum-amount/index.ts create mode 100644 src/ui/server/controllers/insurance/export-contract/how-much-is-the-agent-charging/validation/rules/index.ts create mode 100644 src/ui/templates/insurance/export-contract/how-much-is-the-agent-charging.njk diff --git a/e2e-tests/commands/insurance/complete-and-submit-how-much-is-the-agent-charging-form.js b/e2e-tests/commands/insurance/complete-and-submit-how-much-is-the-agent-charging-form.js new file mode 100644 index 0000000000..8d989f32b9 --- /dev/null +++ b/e2e-tests/commands/insurance/complete-and-submit-how-much-is-the-agent-charging-form.js @@ -0,0 +1,20 @@ +import { field } from '../../pages/shared'; +import FIELD_IDS from '../../constants/field-ids/insurance/export-contract'; +import application from '../../fixtures/application'; + +const { + AGENT_CHARGES: { FIXED_SUM_AMOUNT }, +} = FIELD_IDS; + +/** + * completeAndSubmitHowMuchIsTheAgentChargingForm + * Complete and submit the "How much is the agent charging" form + * @param {String} fixedSumAmount: Fixed sum amount + */ +const completeAndSubmitHowMuchIsTheAgentChargingForm = ({ fixedSumAmount = application.EXPORT_CONTRACT.AGENT_CHARGES[FIXED_SUM_AMOUNT] }) => { + cy.keyboardInput(field(FIXED_SUM_AMOUNT).input(), fixedSumAmount); + + cy.clickSubmitButton(); +}; + +export default completeAndSubmitHowMuchIsTheAgentChargingForm; diff --git a/e2e-tests/constants/routes/insurance/export-contract.js b/e2e-tests/constants/routes/insurance/export-contract.js index 5a6d7ce4b2..de8d74f680 100644 --- a/e2e-tests/constants/routes/insurance/export-contract.js +++ b/e2e-tests/constants/routes/insurance/export-contract.js @@ -8,6 +8,8 @@ const AGENT_ROOT = `${ROOT}/agent`; const AGENT_DETAILS_ROOT = `${ROOT}/agent-details`; const AGENT_SERVICE_ROOT = `${ROOT}/agent-service`; const AGENT_CHARGES_ROOT = `${ROOT}/agent-charges`; +const AGENT_CHARGES_CURRENCY_ROOT = `${ROOT}/currency-of-agents-charge`; +const HOW_MUCH_IS_THE_AGENT_CHARGING_ROOT = `${ROOT}/how-much-is-the-agent-charging`; export const EXPORT_CONTRACT = { ROOT, @@ -48,8 +50,12 @@ export const EXPORT_CONTRACT = { AGENT_CHARGES_CHANGE: `${AGENT_CHARGES_ROOT}/change`, AGENT_CHARGES_CHECK_AND_CHANGE: `${AGENT_CHARGES_ROOT}/check-and-change`, AGENT_CHARGES_CURRENCY_SAVE_AND_BACK: `${AGENT_CHARGES_ROOT}/save-and-go-back`, - AGENT_CHARGES_CURRENCY: `${AGENT_CHARGES_ROOT}/alternative-currency`, - AGENT_CHARGES_CURRENCY_CHANGE: `${AGENT_CHARGES_ROOT}/alternative-currency/change`, - AGENT_CHARGES_CURRENCY_CHECK_AND_CHANGE: `${AGENT_CHARGES_ROOT}/alternative-currency/check-and-change`, + AGENT_CHARGES_CURRENCY: AGENT_CHARGES_CURRENCY_ROOT, + AGENT_CHARGES_CURRENCY_CHANGE: `${AGENT_CHARGES_CURRENCY_ROOT}/change`, + AGENT_CHARGES_CURRENCY_CHECK_AND_CHANGE: `${AGENT_CHARGES_CURRENCY_ROOT}/check-and-change`, + HOW_MUCH_IS_THE_AGENT_CHARGING_SAVE_AND_BACK: `${HOW_MUCH_IS_THE_AGENT_CHARGING_ROOT}/save-and-go-back`, + HOW_MUCH_IS_THE_AGENT_CHARGING: HOW_MUCH_IS_THE_AGENT_CHARGING_ROOT, + HOW_MUCH_IS_THE_AGENT_CHARGING_CHANGE: `${HOW_MUCH_IS_THE_AGENT_CHARGING_ROOT}/change`, + HOW_MUCH_IS_THE_AGENT_CHARGING_CHECK_AND_CHANGE: `${HOW_MUCH_IS_THE_AGENT_CHARGING_ROOT}/check-and-change`, CHECK_YOUR_ANSWERS: `${ROOT}/check-your-answers`, }; diff --git a/e2e-tests/content-strings/fields/insurance/export-contract/index.js b/e2e-tests/content-strings/fields/insurance/export-contract/index.js index 9f51f05125..a77de8620c 100644 --- a/e2e-tests/content-strings/fields/insurance/export-contract/index.js +++ b/e2e-tests/content-strings/fields/insurance/export-contract/index.js @@ -184,6 +184,7 @@ export const EXPORT_CONTRACT_FIELDS = { }, [FIXED_SUM_AMOUNT]: { LABEL: 'How much are they charging in', + HINT: 'Enter a whole number. Do not enter decimals.', SUMMARY: { TITLE: 'How much they are charging', FORM_TITLE: EXPORT_CONTRACT_FORM_TITLES.AGENT, diff --git a/e2e-tests/content-strings/pages/insurance/export-contract/index.js b/e2e-tests/content-strings/pages/insurance/export-contract/index.js index 7ca0e32478..5b26b0143d 100644 --- a/e2e-tests/content-strings/pages/insurance/export-contract/index.js +++ b/e2e-tests/content-strings/pages/insurance/export-contract/index.js @@ -66,6 +66,11 @@ const AGENT_CHARGES_CURRENCY = { PAGE_TITLE: 'What currency is the agent charging you in?', }; +const HOW_MUCH_IS_THE_AGENT_CHARGING = { + ...SHARED, + PAGE_TITLE: 'How much is the agent charging in', +}; + const CHECK_YOUR_ANSWERS = { ...SHARED, PAGE_TITLE: 'Check your answers for this section', @@ -83,5 +88,6 @@ module.exports = { AGENT_SERVICE, AGENT_CHARGES, AGENT_CHARGES_CURRENCY, + HOW_MUCH_IS_THE_AGENT_CHARGING, CHECK_YOUR_ANSWERS, }; diff --git a/e2e-tests/insurance/cypress/e2e/journeys/export-contract/how-much-is-the-agent-charging/how-much-is-the-agent-charging.spec.js b/e2e-tests/insurance/cypress/e2e/journeys/export-contract/how-much-is-the-agent-charging/how-much-is-the-agent-charging.spec.js new file mode 100644 index 0000000000..f32f4a078c --- /dev/null +++ b/e2e-tests/insurance/cypress/e2e/journeys/export-contract/how-much-is-the-agent-charging/how-much-is-the-agent-charging.spec.js @@ -0,0 +1,86 @@ +import { field as fieldSelector, headingCaption } from '../../../../../../pages/shared'; +import { PAGES } from '../../../../../../content-strings'; +import { EXPORT_CONTRACT_FIELDS as FIELD_STRINGS } from '../../../../../../content-strings/fields/insurance/export-contract'; +import FIELD_IDS from '../../../../../../constants/field-ids/insurance/export-contract'; +import { INSURANCE_ROUTES } from '../../../../../../constants/routes/insurance'; + +const CONTENT_STRINGS = PAGES.INSURANCE.EXPORT_CONTRACT.HOW_MUCH_IS_THE_AGENT_CHARGING; + +const { + ROOT, + EXPORT_CONTRACT: { HOW_MUCH_IS_THE_AGENT_CHARGING, CHECK_YOUR_ANSWERS }, +} = INSURANCE_ROUTES; + +const { + AGENT_CHARGES: { FIXED_SUM_AMOUNT: FIELD_ID }, +} = FIELD_IDS; + +const baseUrl = Cypress.config('baseUrl'); + +context( + "Insurance - Export contract - How much is the agent charging page - As an exporter, I want to state what my agent's charges are, So that UKEF, the legal team and the British Embassy are aware of expenses incurred in my export contract bid", + () => { + let referenceNumber; + let url; + let checkYourAnswersUrl; + + before(() => { + cy.completeSignInAndGoToApplication({ totalContractValueOverThreshold: true }).then(({ referenceNumber: refNumber }) => { + referenceNumber = refNumber; + + url = `${baseUrl}${ROOT}/${referenceNumber}${HOW_MUCH_IS_THE_AGENT_CHARGING}`; + checkYourAnswersUrl = `${baseUrl}${ROOT}/${referenceNumber}${CHECK_YOUR_ANSWERS}`; + + cy.navigateToUrl(); + }); + }); + + beforeEach(() => { + cy.saveSession(); + }); + + after(() => { + cy.deleteApplication(referenceNumber); + }); + + it('renders core page elements', () => { + cy.corePageChecks({ + pageTitle: CONTENT_STRINGS.PAGE_TITLE, + currentHref: `${ROOT}/${referenceNumber}${HOW_MUCH_IS_THE_AGENT_CHARGING}`, + backLink: '#', + }); + }); + + describe('page tests', () => { + beforeEach(() => { + cy.navigateToUrl(url); + }); + + it('renders a heading caption', () => { + cy.checkText(headingCaption(), CONTENT_STRINGS.HEADING_CAPTION); + }); + + it(`renders ${FIELD_ID} hint and input`, () => { + const field = fieldSelector(FIELD_ID); + + cy.checkText(field.hint(), FIELD_STRINGS.AGENT_CHARGES[FIELD_ID]); + + field.input().should('exist'); + }); + + it('renders a `save and back` button', () => { + cy.assertSaveAndBackButton(); + }); + }); + + describe('form submission', () => { + it(`should redirect to ${CHECK_YOUR_ANSWERS}`, () => { + cy.navigateToUrl(url); + + cy.completeAndSubmitHowMuchIsTheAgentChargingForm({}); + + cy.assertUrl(checkYourAnswersUrl); + }); + }); + }, +); diff --git a/e2e-tests/insurance/cypress/e2e/journeys/export-contract/how-much-is-the-agent-charging/validation/how-much-is-the-agent-charging-validation.spec.js b/e2e-tests/insurance/cypress/e2e/journeys/export-contract/how-much-is-the-agent-charging/validation/how-much-is-the-agent-charging-validation.spec.js new file mode 100644 index 0000000000..b09e633483 --- /dev/null +++ b/e2e-tests/insurance/cypress/e2e/journeys/export-contract/how-much-is-the-agent-charging/validation/how-much-is-the-agent-charging-validation.spec.js @@ -0,0 +1,80 @@ +import { field as fieldSelector } from '../../../../../../../pages/shared'; +import { MINIMUM_CHARACTERS } from '../../../../../../../constants'; +import { ERROR_MESSAGES } from '../../../../../../../content-strings'; +import FIELD_IDS from '../../../../../../../constants/field-ids/insurance/export-contract'; +import { INSURANCE_ROUTES } from '../../../../../../../constants/routes/insurance'; + +const { + ROOT, + EXPORT_CONTRACT: { HOW_MUCH_IS_THE_AGENT_CHARGING }, +} = INSURANCE_ROUTES; + +const { + AGENT_CHARGES: { FIXED_SUM_AMOUNT: FIELD_ID }, +} = FIELD_IDS; + +const { + INSURANCE: { + EXPORT_CONTRACT: { + AGENT_CHARGES: { [FIELD_ID]: ERROR_MESSAGES_OBJECT }, + }, + }, +} = ERROR_MESSAGES; + +const assertions = { + field: fieldSelector(FIELD_ID), + errorIndex: 0, + expectedErrorsCount: 1, +}; + +const baseUrl = Cypress.config('baseUrl'); + +context('Insurance - Export contract - How much is the agent charging page - form validation', () => { + let referenceNumber; + let url; + + before(() => { + cy.completeSignInAndGoToApplication({}).then(({ referenceNumber: refNumber }) => { + referenceNumber = refNumber; + + url = `${baseUrl}${ROOT}/${referenceNumber}${HOW_MUCH_IS_THE_AGENT_CHARGING}`; + }); + }); + + beforeEach(() => { + cy.saveSession(); + + cy.navigateToUrl(url); + }); + + after(() => { + cy.deleteApplication(referenceNumber); + }); + + it(`should display validation errors when ${FIELD_ID} is left empty`, () => { + cy.submitAndAssertFieldErrors({ ...assertions, expectedErrorMessage: ERROR_MESSAGES_OBJECT.IS_EMPTY }); + }); + + it(`should display validation errors when ${FIELD_ID} has special characters`, () => { + cy.submitAndAssertFieldErrors({ + ...assertions, + value: '10!', + expectedErrorMessage: ERROR_MESSAGES_OBJECT.INCORRECT_FORMAT, + }); + }); + + it(`should display validation errors when ${FIELD_ID} is below ${MINIMUM_CHARACTERS.ONE}`, () => { + cy.submitAndAssertFieldErrors({ + ...assertions, + value: String(MINIMUM_CHARACTERS.ONE - 1), + expectedErrorMessage: ERROR_MESSAGES_OBJECT.BELOW_MINIMUM, + }); + }); + + it(`should NOT display validation errors when ${FIELD_ID} is a decimal place number`, () => { + cy.keyboardInput(fieldSelector(FIELD_ID).input(), '1.50'); + + cy.clickSubmitButton(); + cy.assertErrorSummaryListDoesNotExist(); + }); +}); diff --git a/e2e-tests/insurance/cypress/support/application/export-contract/index.js b/e2e-tests/insurance/cypress/support/application/export-contract/index.js index 9c1aa1cba4..61667ab82b 100644 --- a/e2e-tests/insurance/cypress/support/application/export-contract/index.js +++ b/e2e-tests/insurance/cypress/support/application/export-contract/index.js @@ -26,6 +26,11 @@ Cypress.Commands.add('completeAndSubmitAgentServiceForm', require('../../../../. Cypress.Commands.add('completeAgentChargesForm', require('../../../../../commands/insurance/complete-agent-charges-form')); Cypress.Commands.add('completeAndSubmitAgentChargesForm', require('../../../../../commands/insurance/complete-and-submit-agent-charges-form')); +Cypress.Commands.add( + 'completeAndSubmitHowMuchIsTheAgentChargingForm', + require('../../../../../commands/insurance/complete-and-submit-how-much-is-the-agent-charging-form'), +); + Cypress.Commands.add( 'completeAndSubmitExportContractForms', require('../../../../../commands/insurance/export-contract/complete-and-submit-export-contract-forms'), diff --git a/src/ui/server/constants/routes/insurance/export-contract.ts b/src/ui/server/constants/routes/insurance/export-contract.ts index 5a6d7ce4b2..de8d74f680 100644 --- a/src/ui/server/constants/routes/insurance/export-contract.ts +++ b/src/ui/server/constants/routes/insurance/export-contract.ts @@ -8,6 +8,8 @@ const AGENT_ROOT = `${ROOT}/agent`; const AGENT_DETAILS_ROOT = `${ROOT}/agent-details`; const AGENT_SERVICE_ROOT = `${ROOT}/agent-service`; const AGENT_CHARGES_ROOT = `${ROOT}/agent-charges`; +const AGENT_CHARGES_CURRENCY_ROOT = `${ROOT}/currency-of-agents-charge`; +const HOW_MUCH_IS_THE_AGENT_CHARGING_ROOT = `${ROOT}/how-much-is-the-agent-charging`; export const EXPORT_CONTRACT = { ROOT, @@ -48,8 +50,12 @@ export const EXPORT_CONTRACT = { AGENT_CHARGES_CHANGE: `${AGENT_CHARGES_ROOT}/change`, AGENT_CHARGES_CHECK_AND_CHANGE: `${AGENT_CHARGES_ROOT}/check-and-change`, AGENT_CHARGES_CURRENCY_SAVE_AND_BACK: `${AGENT_CHARGES_ROOT}/save-and-go-back`, - AGENT_CHARGES_CURRENCY: `${AGENT_CHARGES_ROOT}/alternative-currency`, - AGENT_CHARGES_CURRENCY_CHANGE: `${AGENT_CHARGES_ROOT}/alternative-currency/change`, - AGENT_CHARGES_CURRENCY_CHECK_AND_CHANGE: `${AGENT_CHARGES_ROOT}/alternative-currency/check-and-change`, + AGENT_CHARGES_CURRENCY: AGENT_CHARGES_CURRENCY_ROOT, + AGENT_CHARGES_CURRENCY_CHANGE: `${AGENT_CHARGES_CURRENCY_ROOT}/change`, + AGENT_CHARGES_CURRENCY_CHECK_AND_CHANGE: `${AGENT_CHARGES_CURRENCY_ROOT}/check-and-change`, + HOW_MUCH_IS_THE_AGENT_CHARGING_SAVE_AND_BACK: `${HOW_MUCH_IS_THE_AGENT_CHARGING_ROOT}/save-and-go-back`, + HOW_MUCH_IS_THE_AGENT_CHARGING: HOW_MUCH_IS_THE_AGENT_CHARGING_ROOT, + HOW_MUCH_IS_THE_AGENT_CHARGING_CHANGE: `${HOW_MUCH_IS_THE_AGENT_CHARGING_ROOT}/change`, + HOW_MUCH_IS_THE_AGENT_CHARGING_CHECK_AND_CHANGE: `${HOW_MUCH_IS_THE_AGENT_CHARGING_ROOT}/check-and-change`, CHECK_YOUR_ANSWERS: `${ROOT}/check-your-answers`, }; diff --git a/src/ui/server/constants/templates/insurance/export-contract/index.ts b/src/ui/server/constants/templates/insurance/export-contract/index.ts index 67e355e758..1b160289c1 100644 --- a/src/ui/server/constants/templates/insurance/export-contract/index.ts +++ b/src/ui/server/constants/templates/insurance/export-contract/index.ts @@ -6,5 +6,6 @@ export const EXPORT_CONTRACT_TEMPLATES = { AGENT_DETAILS: 'insurance/export-contract/agent-details.njk', AGENT_SERVICE: 'insurance/export-contract/agent-service.njk', AGENT_CHARGES: 'insurance/export-contract/agent-charges.njk', + HOW_MUCH_IS_THE_AGENT_CHARGING: 'insurance/export-contract/how-much-is-the-agent-charging.njk', CHECK_YOUR_ANSWERS: 'insurance/export-contract/check-your-answers.njk', }; diff --git a/src/ui/server/content-strings/fields/insurance/export-contract/index.ts b/src/ui/server/content-strings/fields/insurance/export-contract/index.ts index 2103794807..8b9a77df40 100644 --- a/src/ui/server/content-strings/fields/insurance/export-contract/index.ts +++ b/src/ui/server/content-strings/fields/insurance/export-contract/index.ts @@ -181,6 +181,7 @@ export const EXPORT_CONTRACT_FIELDS = { }, [FIXED_SUM_AMOUNT]: { LABEL: 'How much are they charging in', + HINT: 'Enter a whole number. Do not enter decimals.', SUMMARY: { TITLE: 'How much they are charging', FORM_TITLE: EXPORT_CONTRACT_FORM_TITLES.AGENT, diff --git a/src/ui/server/content-strings/pages/insurance/export-contract/index.ts b/src/ui/server/content-strings/pages/insurance/export-contract/index.ts index 57f9b4d22f..3fbff5641e 100644 --- a/src/ui/server/content-strings/pages/insurance/export-contract/index.ts +++ b/src/ui/server/content-strings/pages/insurance/export-contract/index.ts @@ -66,6 +66,11 @@ const AGENT_CHARGES_CURRENCY = { PAGE_TITLE: 'What currency is the agent charging you in?', }; +const HOW_MUCH_IS_THE_AGENT_CHARGING = { + ...SHARED, + PAGE_TITLE: 'How much is the agent charging in', +}; + const CHECK_YOUR_ANSWERS = { ...SHARED, PAGE_TITLE: 'Check your answers for this section', @@ -83,5 +88,6 @@ export default { AGENT_SERVICE, AGENT_CHARGES, AGENT_CHARGES_CURRENCY, + HOW_MUCH_IS_THE_AGENT_CHARGING, CHECK_YOUR_ANSWERS, }; diff --git a/src/ui/server/controllers/insurance/export-contract/how-much-is-the-agent-charging/index.test.ts b/src/ui/server/controllers/insurance/export-contract/how-much-is-the-agent-charging/index.test.ts new file mode 100644 index 0000000000..d30a51acde --- /dev/null +++ b/src/ui/server/controllers/insurance/export-contract/how-much-is-the-agent-charging/index.test.ts @@ -0,0 +1,282 @@ +import { PAGE_CONTENT_STRINGS, pageVariables, TEMPLATE, FIELD_ID, get, post } from '.'; +import { TEMPLATES } from '../../../../constants'; +import { INSURANCE_ROUTES } from '../../../../constants/routes/insurance'; +import { EXPORT_CONTRACT as EXPORT_CONTRACT_FIELD_IDS } from '../../../../constants/field-ids/insurance/export-contract'; +import { PAGES } from '../../../../content-strings'; +import { EXPORT_CONTRACT_FIELDS as FIELDS } from '../../../../content-strings/fields/insurance/export-contract'; +import getCurrencyByCode from '../../../../helpers/get-currency-by-code'; +import singleInputPageVariables from '../../../../helpers/page-variables/single-input/insurance'; +import getUserNameFromSession from '../../../../helpers/get-user-name-from-session'; +import constructPayload from '../../../../helpers/construct-payload'; +import api from '../../../../api'; +import mapApplicationToFormFields from '../../../../helpers/mappings/map-application-to-form-fields'; +import generateValidationErrors from './validation'; +import { Request, Response } from '../../../../../types'; +import { mockReq, mockRes, mockCurrencies, mockCurrenciesResponse, mockCurrenciesEmptyResponse, mockSpyPromiseRejection } from '../../../../test-mocks'; +import { mockApplicationMultiplePolicy as mockApplication } from '../../../../test-mocks/mock-application'; + +const { + INSURANCE_ROOT, + EXPORT_CONTRACT: { CHECK_YOUR_ANSWERS }, + PROBLEM_WITH_SERVICE, +} = INSURANCE_ROUTES; + +const { + AGENT_CHARGES: { FIXED_SUM_AMOUNT }, +} = EXPORT_CONTRACT_FIELD_IDS; + +const { + exportContract: { + agent: { + service: { + charge: { fixedSumCurrencyCode }, + }, + }, + }, + referenceNumber, +} = mockApplication; + +const { allCurrencies } = mockCurrenciesResponse; + +describe('controllers/insurance/export-contract/how-much-is-the-agent-charging', () => { + let req: Request; + let res: Response; + + let getCurrenciesSpy = jest.fn(() => Promise.resolve(mockCurrenciesResponse)); + + beforeEach(() => { + req = mockReq(); + res = mockRes(); + + api.keystone.APIM.getCurrencies = getCurrenciesSpy; + }); + + afterAll(() => { + jest.resetAllMocks(); + }); + + describe('PAGE_CONTENT_STRINGS', () => { + it('should have the correct strings', () => { + expect(PAGE_CONTENT_STRINGS).toEqual(PAGES.INSURANCE.EXPORT_CONTRACT.HOW_MUCH_IS_THE_AGENT_CHARGING); + }); + }); + + describe('pageVariables', () => { + it('should have correct properties', () => { + const result = pageVariables(referenceNumber, allCurrencies, String(fixedSumCurrencyCode)); + + const currency = getCurrencyByCode(allCurrencies, String(fixedSumCurrencyCode)); + + const expected = { + FIELD: { + ID: FIELD_ID, + ...FIELDS.AGENT_CHARGES[FIELD_ID], + }, + DYNAMIC_PAGE_TITLE: `${PAGE_CONTENT_STRINGS.PAGE_TITLE} ${currency.name}?`, + CURRENCY_PREFIX_SYMBOL: currency.symbol, + SAVE_AND_BACK_URL: `#${referenceNumber}`, + }; + + expect(result).toEqual(expected); + }); + }); + + describe('TEMPLATE', () => { + it('should have the correct template defined', () => { + expect(TEMPLATE).toEqual(TEMPLATES.INSURANCE.EXPORT_CONTRACT.HOW_MUCH_IS_THE_AGENT_CHARGING); + }); + }); + + describe('FIELD_ID', () => { + it('should have the correct ID', () => { + const expected = FIXED_SUM_AMOUNT; + + expect(FIELD_ID).toEqual(expected); + }); + }); + + describe('get', () => { + it('should call api.keystone.APIM.getCurrencies', async () => { + await get(req, res); + + expect(getCurrenciesSpy).toHaveBeenCalledTimes(1); + }); + + it('should render template', async () => { + res.locals.application = mockApplication; + + await get(req, res); + + const generatedPageVariables = pageVariables(referenceNumber, allCurrencies, String(fixedSumCurrencyCode)); + + const { DYNAMIC_PAGE_TITLE } = generatedPageVariables; + + const expectedVariables = { + ...singleInputPageVariables({ + FIELD_ID, + PAGE_CONTENT_STRINGS: { + ...PAGE_CONTENT_STRINGS, + PAGE_TITLE: DYNAMIC_PAGE_TITLE, + }, + BACK_LINK: req.headers.referer, + }), + ...generatedPageVariables, + userName: getUserNameFromSession(req.session.user), + application: mapApplicationToFormFields(mockApplication), + }; + + expect(res.render).toHaveBeenCalledWith(TEMPLATE, expectedVariables); + }); + + describe('when there is no application', () => { + beforeEach(() => { + delete res.locals.application; + }); + + it(`should redirect to ${PROBLEM_WITH_SERVICE}`, async () => { + await get(req, res); + + expect(res.redirect).toHaveBeenCalledWith(PROBLEM_WITH_SERVICE); + }); + }); + + describe('api error handling', () => { + describe('when the get currencies API call fails', () => { + beforeEach(() => { + getCurrenciesSpy = mockSpyPromiseRejection; + api.keystone.APIM.getCurrencies = getCurrenciesSpy; + }); + + it(`should redirect to ${PROBLEM_WITH_SERVICE}`, async () => { + await get(req, res); + + expect(res.redirect).toHaveBeenCalledWith(PROBLEM_WITH_SERVICE); + }); + }); + + describe('when the get currencies response does not return a populated array', () => { + beforeEach(() => { + getCurrenciesSpy = jest.fn(() => Promise.resolve(mockCurrenciesEmptyResponse)); + api.keystone.APIM.getCurrencies = getCurrenciesSpy; + }); + + it(`should redirect to ${PROBLEM_WITH_SERVICE}`, async () => { + await get(req, res); + + expect(res.redirect).toHaveBeenCalledWith(PROBLEM_WITH_SERVICE); + }); + }); + }); + }); + + describe('post', () => { + beforeEach(() => { + getCurrenciesSpy = jest.fn(() => Promise.resolve(mockCurrenciesResponse)); + api.keystone.APIM.getCurrencies = getCurrenciesSpy; + }); + + const validBody = { + [FIELD_ID]: mockApplication.exportContract.agent.service.charge[FIELD_ID], + }; + + describe('when there are no validation errors', () => { + beforeEach(() => { + req.body = validBody; + }); + + it('should NOT call api.keystone.APIM.getCurrencies', async () => { + await post(req, res); + + expect(getCurrenciesSpy).toHaveBeenCalledTimes(0); + }); + + it(`should redirect to ${CHECK_YOUR_ANSWERS}`, async () => { + await post(req, res); + + const expected = `${INSURANCE_ROOT}/${referenceNumber}${CHECK_YOUR_ANSWERS}`; + + expect(res.redirect).toHaveBeenCalledWith(expected); + }); + }); + + describe('when there are validation errors', () => { + it('should call api.keystone.APIM.getCurrencies', async () => { + await get(req, res); + + expect(getCurrenciesSpy).toHaveBeenCalledTimes(1); + }); + + it('should render template with validation errors and submitted values from constructPayload function', async () => { + res.locals.application = mockApplication; + + await post(req, res); + + const payload = constructPayload(req.body, [FIELD_ID]); + + const generatedPageVariables = pageVariables(referenceNumber, mockCurrencies, String(fixedSumCurrencyCode)); + + const { DYNAMIC_PAGE_TITLE } = generatedPageVariables; + + const expectedVariables = { + ...singleInputPageVariables({ + FIELD_ID, + PAGE_CONTENT_STRINGS: { + ...PAGE_CONTENT_STRINGS, + PAGE_TITLE: DYNAMIC_PAGE_TITLE, + }, + BACK_LINK: req.headers.referer, + }), + ...generatedPageVariables, + userName: getUserNameFromSession(req.session.user), + application: mapApplicationToFormFields(mockApplication), + submittedValues: payload, + validationErrors: generateValidationErrors(payload), + }; + + expect(res.render).toHaveBeenCalledWith(TEMPLATE, expectedVariables); + }); + }); + + describe('when there is no application', () => { + beforeEach(() => { + delete res.locals.application; + }); + + it(`should redirect to ${PROBLEM_WITH_SERVICE}`, async () => { + await post(req, res); + + expect(res.redirect).toHaveBeenCalledWith(PROBLEM_WITH_SERVICE); + }); + }); + + describe('api error handling', () => { + describe('get currencies call', () => { + describe('when the get currencies API call fails', () => { + beforeEach(() => { + getCurrenciesSpy = mockSpyPromiseRejection; + api.keystone.APIM.getCurrencies = getCurrenciesSpy; + }); + + it(`should redirect to ${PROBLEM_WITH_SERVICE}`, async () => { + await post(req, res); + + expect(res.redirect).toHaveBeenCalledWith(PROBLEM_WITH_SERVICE); + }); + }); + + describe('when the get currencies response does not return a populated array', () => { + beforeEach(() => { + getCurrenciesSpy = jest.fn(() => Promise.resolve(mockCurrenciesEmptyResponse)); + api.keystone.APIM.getCurrencies = getCurrenciesSpy; + }); + + it(`should redirect to ${PROBLEM_WITH_SERVICE}`, async () => { + await post(req, res); + + expect(res.redirect).toHaveBeenCalledWith(PROBLEM_WITH_SERVICE); + }); + }); + }); + }); + }); +}); diff --git a/src/ui/server/controllers/insurance/export-contract/how-much-is-the-agent-charging/index.ts b/src/ui/server/controllers/insurance/export-contract/how-much-is-the-agent-charging/index.ts new file mode 100644 index 0000000000..25b5a4e560 --- /dev/null +++ b/src/ui/server/controllers/insurance/export-contract/how-much-is-the-agent-charging/index.ts @@ -0,0 +1,174 @@ +import { TEMPLATES } from '../../../../constants'; +import { INSURANCE_ROUTES } from '../../../../constants/routes/insurance'; +import { EXPORT_CONTRACT as EXPORT_CONTRACT_FIELD_IDS } from '../../../../constants/field-ids/insurance/export-contract'; +import { PAGES } from '../../../../content-strings'; +import { EXPORT_CONTRACT_FIELDS as FIELDS } from '../../../../content-strings/fields/insurance/export-contract'; +import getCurrencyByCode from '../../../../helpers/get-currency-by-code'; +import singleInputPageVariables from '../../../../helpers/page-variables/single-input/insurance'; +import getUserNameFromSession from '../../../../helpers/get-user-name-from-session'; +import constructPayload from '../../../../helpers/construct-payload'; +import api from '../../../../api'; +import { isPopulatedArray } from '../../../../helpers/array'; +import mapApplicationToFormFields from '../../../../helpers/mappings/map-application-to-form-fields'; +import generateValidationErrors from './validation'; +import { Currency, Request, Response } from '../../../../../types'; + +const { + INSURANCE_ROOT, + EXPORT_CONTRACT: { CHECK_YOUR_ANSWERS }, + PROBLEM_WITH_SERVICE, +} = INSURANCE_ROUTES; + +const { + AGENT_CHARGES: { FIXED_SUM_AMOUNT }, +} = EXPORT_CONTRACT_FIELD_IDS; + +export const PAGE_CONTENT_STRINGS = PAGES.INSURANCE.EXPORT_CONTRACT.HOW_MUCH_IS_THE_AGENT_CHARGING; + +export const FIELD_ID = FIXED_SUM_AMOUNT; + +/** + * pageVariables + * Page fields and "save and go back" URL + * @param {Number} referenceNumber: Application reference number + * @param {Array} currencies: Currencies + * @param {String} currencyCode: Fixed sum currency code + * @returns {Object} Page variables + */ +export const pageVariables = (referenceNumber: number, currencies: Array, currencyCode: string) => { + const currency = getCurrencyByCode(currencies, currencyCode); + + return { + FIELD: { + ID: FIELD_ID, + ...FIELDS.AGENT_CHARGES[FIELD_ID], + }, + DYNAMIC_PAGE_TITLE: `${PAGE_CONTENT_STRINGS.PAGE_TITLE} ${currency.name}?`, + CURRENCY_PREFIX_SYMBOL: currency.symbol, + SAVE_AND_BACK_URL: `#${referenceNumber}`, + }; +}; + +export const TEMPLATE = TEMPLATES.INSURANCE.EXPORT_CONTRACT.HOW_MUCH_IS_THE_AGENT_CHARGING; + +/** + * get + * Get the application and render the "Export contract - How much is the agent charging" + * @param {Express.Request} Express request + * @param {Express.Response} Express response + * @returns {Express.Response.render} "Export contract - How much is the agent charging" page + */ +export const get = async (req: Request, res: Response) => { + const { application } = res.locals; + + if (!application) { + return res.redirect(PROBLEM_WITH_SERVICE); + } + + const { + referenceNumber, + exportContract: { + agent: { + service: { + charge: { fixedSumCurrencyCode }, + }, + }, + }, + } = application; + + try { + const { allCurrencies } = await api.keystone.APIM.getCurrencies(); + + if (!isPopulatedArray(allCurrencies)) { + return res.redirect(PROBLEM_WITH_SERVICE); + } + + const generatedPageVariables = pageVariables(referenceNumber, allCurrencies, String(fixedSumCurrencyCode)); + + const { DYNAMIC_PAGE_TITLE } = generatedPageVariables; + + return res.render(TEMPLATE, { + ...singleInputPageVariables({ + FIELD_ID, + PAGE_CONTENT_STRINGS: { + ...PAGE_CONTENT_STRINGS, + PAGE_TITLE: DYNAMIC_PAGE_TITLE, + }, + BACK_LINK: req.headers.referer, + }), + ...generatedPageVariables, + userName: getUserNameFromSession(req.session.user), + application: mapApplicationToFormFields(application), + }); + } catch (error) { + console.error('Error getting currencies %O', error); + + return res.redirect(PROBLEM_WITH_SERVICE); + } +}; + +/** + * post + * Check "Export contract - How much is the agent charging" validation errors and if successful, redirect to the next part of the flow. + * @param {Express.Request} Express request + * @param {Express.Response} Express response + * @returns {Express.Response.redirect} Next part of the flow or error page + */ +export const post = async (req: Request, res: Response) => { + const { application } = res.locals; + + if (!application) { + return res.redirect(PROBLEM_WITH_SERVICE); + } + + const { + referenceNumber, + exportContract: { + agent: { + service: { + charge: { fixedSumCurrencyCode }, + }, + }, + }, + } = application; + + const payload = constructPayload(req.body, [FIELD_ID]); + + const validationErrors = generateValidationErrors(payload); + + if (validationErrors) { + try { + const { supportedCurrencies } = await api.keystone.APIM.getCurrencies(); + + if (!isPopulatedArray(supportedCurrencies)) { + return res.redirect(PROBLEM_WITH_SERVICE); + } + + const generatedPageVariables = pageVariables(referenceNumber, supportedCurrencies, String(fixedSumCurrencyCode)); + + const { DYNAMIC_PAGE_TITLE } = generatedPageVariables; + + return res.render(TEMPLATE, { + ...singleInputPageVariables({ + FIELD_ID, + PAGE_CONTENT_STRINGS: { + ...PAGE_CONTENT_STRINGS, + PAGE_TITLE: DYNAMIC_PAGE_TITLE, + }, + BACK_LINK: req.headers.referer, + }), + ...generatedPageVariables, + userName: getUserNameFromSession(req.session.user), + application: mapApplicationToFormFields(application), + submittedValues: payload, + validationErrors, + }); + } catch (error) { + console.error('Error getting currencies %O', error); + + return res.redirect(PROBLEM_WITH_SERVICE); + } + } + + return res.redirect(`${INSURANCE_ROOT}/${referenceNumber}${CHECK_YOUR_ANSWERS}`); +}; diff --git a/src/ui/server/controllers/insurance/export-contract/how-much-is-the-agent-charging/validation/index.test.ts b/src/ui/server/controllers/insurance/export-contract/how-much-is-the-agent-charging/validation/index.test.ts new file mode 100644 index 0000000000..5c7c45ab2e --- /dev/null +++ b/src/ui/server/controllers/insurance/export-contract/how-much-is-the-agent-charging/validation/index.test.ts @@ -0,0 +1,15 @@ +import validation from '.'; +import validationRules from './rules'; +import combineValidationRules from '../../../../../helpers/combine-validation-rules'; + +describe('controllers/insurance/export-contract/how-much-the-agent-is-charging/validation', () => { + it('should return an array of results from rule functions', () => { + const mockFormBody = {}; + + const result = validation(mockFormBody); + + const expected = combineValidationRules(validationRules, mockFormBody); + + expect(result).toEqual(expected); + }); +}); diff --git a/src/ui/server/controllers/insurance/export-contract/how-much-is-the-agent-charging/validation/index.ts b/src/ui/server/controllers/insurance/export-contract/how-much-is-the-agent-charging/validation/index.ts new file mode 100644 index 0000000000..d42a0d2786 --- /dev/null +++ b/src/ui/server/controllers/insurance/export-contract/how-much-is-the-agent-charging/validation/index.ts @@ -0,0 +1,7 @@ +import validationRules from './rules'; +import combineValidationRules from '../../../../../helpers/combine-validation-rules'; +import { RequestBody, ValidationErrors } from '../../../../../../types'; + +const validation = (formBody: RequestBody): ValidationErrors => combineValidationRules(validationRules, formBody) as ValidationErrors; + +export default validation; diff --git a/src/ui/server/controllers/insurance/export-contract/how-much-is-the-agent-charging/validation/rules/fixed-sum-amount/index.test.ts b/src/ui/server/controllers/insurance/export-contract/how-much-is-the-agent-charging/validation/rules/fixed-sum-amount/index.test.ts new file mode 100644 index 0000000000..a5dcec393b --- /dev/null +++ b/src/ui/server/controllers/insurance/export-contract/how-much-is-the-agent-charging/validation/rules/fixed-sum-amount/index.test.ts @@ -0,0 +1,35 @@ +import fixedSumAmountRule from '.'; +import { MINIMUM_CHARACTERS } from '../../../../../../../constants'; +import FIELD_IDS from '../../../../../../../constants/field-ids/insurance/export-contract'; +import { ERROR_MESSAGES } from '../../../../../../../content-strings'; +import numberAboveMinimumValidation from '../../../../../../../shared-validation/number-above-minimum'; +import { mockErrors } from '../../../../../../../test-mocks'; + +const { + AGENT_CHARGES: { FIXED_SUM_AMOUNT: FIELD_ID }, +} = FIELD_IDS; + +const { + AGENT_CHARGES: { [FIELD_ID]: FIXED_SUM_AMOUNT_ERROR_MESSAGES }, +} = ERROR_MESSAGES.INSURANCE.EXPORT_CONTRACT; + +describe('controllers/insurance/export-contract/how-much-the-agent-is-charging/validation/rules/fixed-sum-amount', () => { + it('should return the result of numberAboveMinimumValidation', () => { + const mockBody = { + [FIELD_ID]: '', + }; + + const result = fixedSumAmountRule(mockBody, mockErrors); + + const expected = numberAboveMinimumValidation({ + formBody: mockBody, + fieldId: FIELD_ID, + errorMessage: FIXED_SUM_AMOUNT_ERROR_MESSAGES, + errors: mockErrors, + minimum: MINIMUM_CHARACTERS.ONE, + allowDecimalPlaces: true, + }); + + expect(result).toEqual(expected); + }); +}); diff --git a/src/ui/server/controllers/insurance/export-contract/how-much-is-the-agent-charging/validation/rules/fixed-sum-amount/index.ts b/src/ui/server/controllers/insurance/export-contract/how-much-is-the-agent-charging/validation/rules/fixed-sum-amount/index.ts new file mode 100644 index 0000000000..5e4ee26025 --- /dev/null +++ b/src/ui/server/controllers/insurance/export-contract/how-much-is-the-agent-charging/validation/rules/fixed-sum-amount/index.ts @@ -0,0 +1,31 @@ +import { MINIMUM_CHARACTERS } from '../../../../../../../constants'; +import FIELD_IDS from '../../../../../../../constants/field-ids/insurance/export-contract'; +import { ERROR_MESSAGES } from '../../../../../../../content-strings'; +import numberAboveMinimumValidation from '../../../../../../../shared-validation/number-above-minimum'; +import { RequestBody } from '../../../../../../../../types'; + +const { + AGENT_CHARGES: { FIXED_SUM_AMOUNT: FIELD_ID }, +} = FIELD_IDS; + +const { + AGENT_CHARGES: { [FIELD_ID]: FIXED_SUM_AMOUNT_ERROR_MESSAGES }, +} = ERROR_MESSAGES.INSURANCE.EXPORT_CONTRACT; + +/** + * validate the FIXED_SUM_AMOUNT field via numberAboveMinimumValidation. + * @param {RequestBody} formBody: Form body + * @param {Object} errors: Other validation errors for the same form + * @returns {ValidationErrors} + */ +const fixedSumAmountRule = (formBody: RequestBody, errors: object) => + numberAboveMinimumValidation({ + formBody, + fieldId: FIELD_ID, + errorMessage: FIXED_SUM_AMOUNT_ERROR_MESSAGES, + errors, + minimum: MINIMUM_CHARACTERS.ONE, + allowDecimalPlaces: true, + }); + +export default fixedSumAmountRule; diff --git a/src/ui/server/controllers/insurance/export-contract/how-much-is-the-agent-charging/validation/rules/index.ts b/src/ui/server/controllers/insurance/export-contract/how-much-is-the-agent-charging/validation/rules/index.ts new file mode 100644 index 0000000000..bfcac2c0c4 --- /dev/null +++ b/src/ui/server/controllers/insurance/export-contract/how-much-is-the-agent-charging/validation/rules/index.ts @@ -0,0 +1,8 @@ +import fixedSumAmountRule from './fixed-sum-amount'; +import { ValidationErrors } from '../../../../../../../types'; + +const rules = [fixedSumAmountRule]; + +const validationRules = rules as Array<() => ValidationErrors>; + +export default validationRules; diff --git a/src/ui/server/routes/insurance/export-contract/index.test.ts b/src/ui/server/routes/insurance/export-contract/index.test.ts index acfa06a8e8..425f566f9a 100644 --- a/src/ui/server/routes/insurance/export-contract/index.test.ts +++ b/src/ui/server/routes/insurance/export-contract/index.test.ts @@ -25,6 +25,10 @@ import { post as agentServiceSaveAndBackPost } from '../../../controllers/insura import { get as agentChargesGet, post as agentChargesPost } from '../../../controllers/insurance/export-contract/agent-charges'; import { post as agentChargesSaveAndBackPost } from '../../../controllers/insurance/export-contract/agent-charges/save-and-back'; import { get as agentChargesCurrencyGet, post as agentChargesCurrencyPost } from '../../../controllers/insurance/export-contract/currency-of-agents-charge'; +import { + get as howMuchIsTheAgentChargingGet, + post as howMuchIsTheAgentChargingPost, +} from '../../../controllers/insurance/export-contract/how-much-is-the-agent-charging'; import { get as checkYourAnswersGet, post as checkYourAnswersPost } from '../../../controllers/insurance/export-contract/check-your-answers'; const { @@ -67,6 +71,7 @@ const { AGENT_CHARGES_CURRENCY, AGENT_CHARGES_CURRENCY_CHANGE, AGENT_CHARGES_CURRENCY_CHECK_AND_CHANGE, + HOW_MUCH_IS_THE_AGENT_CHARGING, CHECK_YOUR_ANSWERS, } = EXPORT_CONTRACT; @@ -80,8 +85,8 @@ describe('routes/insurance/export-contract', () => { }); it('should setup all routes', () => { - expect(get).toHaveBeenCalledTimes(32); - expect(post).toHaveBeenCalledTimes(40); + expect(get).toHaveBeenCalledTimes(33); + expect(post).toHaveBeenCalledTimes(41); expect(get).toHaveBeenCalledWith(`/:referenceNumber${ROOT}`, exportContractRootGet); @@ -163,6 +168,9 @@ describe('routes/insurance/export-contract', () => { expect(get).toHaveBeenCalledWith(`/:referenceNumber${AGENT_CHARGES_CURRENCY_CHECK_AND_CHANGE}`, agentChargesCurrencyGet); expect(post).toHaveBeenCalledWith(`/:referenceNumber${AGENT_CHARGES_CURRENCY_CHECK_AND_CHANGE}`, agentChargesCurrencyPost); + expect(get).toHaveBeenCalledWith(`/:referenceNumber${HOW_MUCH_IS_THE_AGENT_CHARGING}`, howMuchIsTheAgentChargingGet); + expect(post).toHaveBeenCalledWith(`/:referenceNumber${HOW_MUCH_IS_THE_AGENT_CHARGING}`, howMuchIsTheAgentChargingPost); + expect(get).toHaveBeenCalledWith(`/:referenceNumber${CHECK_YOUR_ANSWERS}`, checkYourAnswersGet); expect(post).toHaveBeenCalledWith(`/:referenceNumber${CHECK_YOUR_ANSWERS}`, checkYourAnswersPost); }); diff --git a/src/ui/server/routes/insurance/export-contract/index.ts b/src/ui/server/routes/insurance/export-contract/index.ts index 3b26af42ba..f6fa60ce7a 100644 --- a/src/ui/server/routes/insurance/export-contract/index.ts +++ b/src/ui/server/routes/insurance/export-contract/index.ts @@ -26,6 +26,10 @@ import { post as agentServiceSaveAndBackPost } from '../../../controllers/insura import { get as agentChargesGet, post as agentChargesPost } from '../../../controllers/insurance/export-contract/agent-charges'; import { post as agentChargesSaveAndBackPost } from '../../../controllers/insurance/export-contract/agent-charges/save-and-back'; import { get as agentChargesCurrencyGet, post as agentChargesCurrencyPost } from '../../../controllers/insurance/export-contract/currency-of-agents-charge'; +import { + get as howMuchIsTheAgentChargingGet, + post as howMuchIsTheAgentChargingPost, +} from '../../../controllers/insurance/export-contract/how-much-is-the-agent-charging'; import { get as checkYourAnswersGet, post as checkYourAnswersPost } from '../../../controllers/insurance/export-contract/check-your-answers'; const { @@ -69,6 +73,7 @@ const { AGENT_CHARGES_CURRENCY, AGENT_CHARGES_CURRENCY_CHANGE, AGENT_CHARGES_CURRENCY_CHECK_AND_CHANGE, + HOW_MUCH_IS_THE_AGENT_CHARGING, CHECK_YOUR_ANSWERS, } = EXPORT_CONTRACT; @@ -159,6 +164,9 @@ exportContractRoute.post(`/:referenceNumber${AGENT_CHARGES_CURRENCY_CHANGE}`, ag exportContractRoute.get(`/:referenceNumber${AGENT_CHARGES_CURRENCY_CHECK_AND_CHANGE}`, agentChargesCurrencyGet); exportContractRoute.post(`/:referenceNumber${AGENT_CHARGES_CURRENCY_CHECK_AND_CHANGE}`, agentChargesCurrencyPost); +exportContractRoute.get(`/:referenceNumber${HOW_MUCH_IS_THE_AGENT_CHARGING}`, howMuchIsTheAgentChargingGet); +exportContractRoute.post(`/:referenceNumber${HOW_MUCH_IS_THE_AGENT_CHARGING}`, howMuchIsTheAgentChargingPost); + exportContractRoute.get(`/:referenceNumber${CHECK_YOUR_ANSWERS}`, checkYourAnswersGet); exportContractRoute.post(`/:referenceNumber${CHECK_YOUR_ANSWERS}`, checkYourAnswersPost); diff --git a/src/ui/server/routes/insurance/index.test.ts b/src/ui/server/routes/insurance/index.test.ts index 54d0eb9bd7..5e46288379 100644 --- a/src/ui/server/routes/insurance/index.test.ts +++ b/src/ui/server/routes/insurance/index.test.ts @@ -21,8 +21,8 @@ describe('routes/insurance', () => { }); it('should setup all routes', () => { - expect(get).toHaveBeenCalledTimes(215); - expect(post).toHaveBeenCalledTimes(222); + expect(get).toHaveBeenCalledTimes(216); + expect(post).toHaveBeenCalledTimes(223); expect(get).toHaveBeenCalledWith(INSURANCE_ROUTES.START, startGet); diff --git a/src/ui/templates/insurance/export-contract/how-much-is-the-agent-charging.njk b/src/ui/templates/insurance/export-contract/how-much-is-the-agent-charging.njk new file mode 100644 index 0000000000..a3dfcc9d45 --- /dev/null +++ b/src/ui/templates/insurance/export-contract/how-much-is-the-agent-charging.njk @@ -0,0 +1,62 @@ +{% extends 'index.njk' %} +{% from "govuk/components/back-link/macro.njk" import govukBackLink %} +{% from 'govuk/components/error-summary/macro.njk' import govukErrorSummary %} +{% import '../../components/monetary-value-input.njk' as monetaryValueInput %} +{% import '../../components/form-buttons.njk' as formButtons %} + +{% block pageTitle %} + {{ CONTENT_STRINGS.PAGE_TITLE }} +{% endblock %} + +{% block content %} + + {{ govukBackLink({ + text: CONTENT_STRINGS.LINKS.BACK, + href: BACK_LINK, + attributes: { + "data-cy": DATA_CY.BACK_LINK + } + }) }} + + {% if validationErrors.summary %} + {{ govukErrorSummary({ + titleText: CONTENT_STRINGS.ERROR_MESSAGES.THERE_IS_A_PROBLEM, + errorList: validationErrors.summary + }) }} + {% endif %} + +
+
+
+ {{ CONTENT_STRINGS.HEADING_CAPTION }} +

{{ CONTENT_STRINGS.PAGE_TITLE }}

+
+
+
+ +
+ + + +
+
+ + {{ monetaryValueInput.render({ + fieldId: FIELD_ID, + label: FIELDS[FIELD_ID].LABEL, + currencyPrefixSymbol: CURRENCY_PREFIX_SYMBOL, + submittedValue: submittedValues[FIELD_ID] or application.exportContract.agent.service.charge[FIELD_ID], + validationError: validationErrors.errorList[FIELD_ID] + }) }} + +
+
+ + {{ formButtons.render({ + contentStrings: CONTENT_STRINGS.BUTTONS, + saveAndBackUrl: SAVE_AND_BACK_URL + }) }} + +
+ +{% endblock %} diff --git a/src/ui/templates/shared-pages/alternative-currency.njk b/src/ui/templates/shared-pages/alternative-currency.njk index d28266cf51..0ec0c02652 100644 --- a/src/ui/templates/shared-pages/alternative-currency.njk +++ b/src/ui/templates/shared-pages/alternative-currency.njk @@ -5,7 +5,7 @@ {% from "govuk/components/summary-list/macro.njk" import govukSummaryList %} {% from 'govuk/components/error-summary/macro.njk' import govukErrorSummary %} {% import '../components/currency-radio-inputs.njk' as currencyRadios %} -{% import '../components/submit-button.njk' as submitButton %} +{% import '../components/form-buttons.njk' as formButtons %} {% block pageTitle %} {{ CONTENT_STRINGS.PAGE_TITLE }} @@ -55,15 +55,10 @@ -
-
-
- {{ submitButton.render({ - text: CONTENT_STRINGS.BUTTONS.CONTINUE - }) }} -
-
-
+ {{ formButtons.render({ + contentStrings: CONTENT_STRINGS.BUTTONS, + saveAndBackUrl: SAVE_AND_BACK_URL + }) }} From 89437cfcf23c5ff8b6364f304f9cc82df3128e39 Mon Sep 17 00:00:00 2001 From: Tony Barnes Date: Fri, 20 Sep 2024 11:53:28 +0100 Subject: [PATCH 2/2] style(EMS-3823): application submission - xlsx - credit limit row (#3097) * style(EMS-3823): application submission - xlsx - credit limit row * style(EMS-3823): fix/update unit test --- src/api/.keystone/config.js | 14 +++++++------- .../map-single-contract-policy/index.test.ts | 3 ++- .../map-policy/map-single-contract-policy/index.ts | 3 ++- src/api/package.json | 2 +- src/api/test-helpers/create-full-application.ts | 1 + 5 files changed, 13 insertions(+), 10 deletions(-) diff --git a/src/api/.keystone/config.js b/src/api/.keystone/config.js index bf5a250adb..6707768eb4 100644 --- a/src/api/.keystone/config.js +++ b/src/api/.keystone/config.js @@ -6962,14 +6962,14 @@ var YOUR_BUYER_FIELDS = { } }, [FAILED_PAYMENTS]: { - LABEL: "Has the buyer ever failed to pay you on time?", + HINT: "This is when an invoice has still not been paid 30 days or more after the agreed payment date.", SUMMARY: { TITLE: "Buyer failed to pay on time?", FORM_TITLE: TRADING_HISTORY } }, [CURRENCY_CODE2]: { - LEGEND: "What currency are the outstanding or overdue payments in?" + LEGEND: "What is the currency the outstanding or overdue payments are in?" }, [HAS_PREVIOUS_CREDIT_INSURANCE_COVER_WITH_BUYER]: { LABEL: "Have you in the past held credit insurance cover on the buyer?", @@ -6988,15 +6988,14 @@ var YOUR_BUYER_FIELDS = { MAXIMUM: MAXIMUM_CHARACTERS.BUYER.PREVIOUS_CREDIT_INSURANCE_COVER }, [TOTAL_OUTSTANDING_PAYMENTS]: { - HEADING: "Tell us about the outstanding or overdue payments", - LABEL: "Total outstanding, including overdue", + LABEL: "Total outstanding, including overdue in", SUMMARY: { TITLE: "Total outstanding including overdue", FORM_TITLE: TRADING_HISTORY } }, [TOTAL_AMOUNT_OVERDUE]: { - LABEL: "Amount overdue", + LABEL: "Amount overdue in", SUMMARY: { TITLE: "Amount overdue", FORM_TITLE: TRADING_HISTORY @@ -7494,7 +7493,7 @@ var { CONTRACT_POLICY: { SINGLE: { CONTRACT_COMPLETION_DATE: CONTRACT_COMPLETION_DATE3 }, POLICY_CURRENCY_CODE, - SINGLE: { TOTAL_CONTRACT_VALUE: TOTAL_CONTRACT_VALUE2 } + SINGLE: { REQUESTED_CREDIT_LIMIT, TOTAL_CONTRACT_VALUE: TOTAL_CONTRACT_VALUE2 } } } } = insurance_default; @@ -7502,7 +7501,8 @@ var mapSingleContractPolicy = (policy) => { const mapped = [ xlsx_row_default(String(FIELDS8[CONTRACT_COMPLETION_DATE3]), format_date_default(policy[CONTRACT_COMPLETION_DATE3], DATE_FORMAT.XLSX)), xlsx_row_default(String(CONTENT_STRINGS2[CURRENCY_CODE3].SUMMARY?.TITLE), policy[POLICY_CURRENCY_CODE]), - xlsx_row_default(String(FIELDS8[TOTAL_CONTRACT_VALUE2]), format_currency_default2(policy[TOTAL_CONTRACT_VALUE2], policy[POLICY_CURRENCY_CODE])) + xlsx_row_default(String(FIELDS8[TOTAL_CONTRACT_VALUE2]), format_currency_default2(policy[TOTAL_CONTRACT_VALUE2], policy[POLICY_CURRENCY_CODE])), + xlsx_row_default(String(CONTENT_STRINGS2[REQUESTED_CREDIT_LIMIT].SUMMARY?.TITLE), format_currency_default2(policy[REQUESTED_CREDIT_LIMIT], policy[POLICY_CURRENCY_CODE])) ]; return mapped; }; diff --git a/src/api/generate-xlsx/map-application-to-XLSX/map-policy/map-single-contract-policy/index.test.ts b/src/api/generate-xlsx/map-application-to-XLSX/map-policy/map-single-contract-policy/index.test.ts index 905ac843eb..4b7ccf14c8 100644 --- a/src/api/generate-xlsx/map-application-to-XLSX/map-policy/map-single-contract-policy/index.test.ts +++ b/src/api/generate-xlsx/map-application-to-XLSX/map-policy/map-single-contract-policy/index.test.ts @@ -24,7 +24,7 @@ const { CONTRACT_POLICY: { SINGLE: { CONTRACT_COMPLETION_DATE }, POLICY_CURRENCY_CODE, - SINGLE: { TOTAL_CONTRACT_VALUE }, + SINGLE: { REQUESTED_CREDIT_LIMIT, TOTAL_CONTRACT_VALUE }, }, }, } = FIELD_IDS; @@ -50,6 +50,7 @@ describe('api/generate-xlsx/map-application-to-xlsx/map-policy/map-single-contra xlsxRow(String(FIELDS[CONTRACT_COMPLETION_DATE]), formatDate(policy[CONTRACT_COMPLETION_DATE], DATE_FORMAT.XLSX)), xlsxRow(String(CONTENT_STRINGS[CURRENCY_CODE].SUMMARY?.TITLE), policy[POLICY_CURRENCY_CODE]), xlsxRow(String(FIELDS[TOTAL_CONTRACT_VALUE]), formatCurrency(policy[TOTAL_CONTRACT_VALUE], policy[POLICY_CURRENCY_CODE])), + xlsxRow(String(CONTENT_STRINGS[REQUESTED_CREDIT_LIMIT].SUMMARY?.TITLE), formatCurrency(policy[REQUESTED_CREDIT_LIMIT], policy[POLICY_CURRENCY_CODE])), ]; expect(result).toEqual(expected); diff --git a/src/api/generate-xlsx/map-application-to-XLSX/map-policy/map-single-contract-policy/index.ts b/src/api/generate-xlsx/map-application-to-XLSX/map-policy/map-single-contract-policy/index.ts index dee0a1eff3..b2edeed748 100644 --- a/src/api/generate-xlsx/map-application-to-XLSX/map-policy/map-single-contract-policy/index.ts +++ b/src/api/generate-xlsx/map-application-to-XLSX/map-policy/map-single-contract-policy/index.ts @@ -21,7 +21,7 @@ const { CONTRACT_POLICY: { SINGLE: { CONTRACT_COMPLETION_DATE }, POLICY_CURRENCY_CODE, - SINGLE: { TOTAL_CONTRACT_VALUE }, + SINGLE: { REQUESTED_CREDIT_LIMIT, TOTAL_CONTRACT_VALUE }, }, }, } = FIELD_IDS; @@ -37,6 +37,7 @@ const mapSingleContractPolicy = (policy: ApplicationPolicy) => { xlsxRow(String(FIELDS[CONTRACT_COMPLETION_DATE]), formatDate(policy[CONTRACT_COMPLETION_DATE], DATE_FORMAT.XLSX)), xlsxRow(String(CONTENT_STRINGS[CURRENCY_CODE].SUMMARY?.TITLE), policy[POLICY_CURRENCY_CODE]), xlsxRow(String(FIELDS[TOTAL_CONTRACT_VALUE]), formatCurrency(policy[TOTAL_CONTRACT_VALUE], policy[POLICY_CURRENCY_CODE])), + xlsxRow(String(CONTENT_STRINGS[REQUESTED_CREDIT_LIMIT].SUMMARY?.TITLE), formatCurrency(policy[REQUESTED_CREDIT_LIMIT], policy[POLICY_CURRENCY_CODE])), ]; return mapped; diff --git a/src/api/package.json b/src/api/package.json index 929c6dbf21..cc110efc93 100644 --- a/src/api/package.json +++ b/src/api/package.json @@ -20,7 +20,7 @@ "lint": "eslint ./", "lint:fix": "eslint ./ --fix", "start": "keystone start --no-ui", - "test": "jest --runInBand --verbose --silent --coverage --config=jest.config.js" + "test": "jest --runInBand --verbose --silent --coverage --config=jest.config.js" }, "dependencies": { "@babel/core": "^7.25.2", diff --git a/src/api/test-helpers/create-full-application.ts b/src/api/test-helpers/create-full-application.ts index 7dc3494568..59ed01a6f7 100644 --- a/src/api/test-helpers/create-full-application.ts +++ b/src/api/test-helpers/create-full-application.ts @@ -61,6 +61,7 @@ export const createFullApplication = async (context: Context, policyType?: strin */ const policyData = { policyType: POLICY_TYPE.SINGLE, + requestedCreditLimit: 100, totalSalesToBuyer: 123, totalValueOfContract: 456, maximumBuyerWillOwe: 789,