diff --git a/e2e-tests/commands/insurance/complete-and-submit-how-was-the-contract-awarded-form.js b/e2e-tests/commands/insurance/complete-and-submit-how-was-the-contract-awarded-form.js index cea1505d84..a348f4b756 100644 --- a/e2e-tests/commands/insurance/complete-and-submit-how-was-the-contract-awarded-form.js +++ b/e2e-tests/commands/insurance/complete-and-submit-how-was-the-contract-awarded-form.js @@ -1,57 +1,22 @@ -import { field, radios } from '../../pages/shared'; -import { EXPORT_CONTRACT as EXPORT_CONTRACT_FIELD_IDS } from '../../constants/field-ids/insurance/export-contract'; -import { EXPORT_CONTRACT_FIELDS as FIELDS } from '../../content-strings/fields/insurance'; -import application from '../../fixtures/application'; - -const { - HOW_WAS_THE_CONTRACT_AWARDED: { AWARD_METHOD, OTHER_AWARD_METHOD }, -} = EXPORT_CONTRACT_FIELD_IDS; - -const { OPEN_TENDER, NEGOTIATED_CONTRACT, DIRECT_AWARD, COMPETITIVE_BIDDING, OTHER } = FIELDS.HOW_WAS_THE_CONTRACT_AWARDED[AWARD_METHOD].OPTIONS; - /** * completeAndSubmitHowWasTheContractAwardedForm * Complete and submit the "How was the contract awarded" form - * @param {Boolean} openTender: Award method as OPEN_TENDER. Defaults to true - * @param {Boolean} negotiatedContract: Award method as NEGOTIATED_CONTRACT. Defaults to false - * @param {String} directAward: Award method as DIRECT_AWARD. Defaults to false - * @param {String} competitiveBidding: Award method as COMPETITIVE_BIDDING. Defaults to false - * @param {String} otherMethod: Award method as OTHER. Defaults to false + * @param {Boolean} openTender: Award method as OPEN_TENDER + * @param {Boolean} negotiatedContract: Award method as NEGOTIATED_CONTRACT + * @param {Boolean} directAward: Award method as DIRECT_AWARD + * @param {Boolean} competitiveBidding: Award method as COMPETITIVE_BIDDING + * @param {Boolean} otherMethod: Award method as OTHER + * @param {String} otherMethodText: OTHER award method text */ -const completeAndSubmitHowWasTheContractAwardedForm = ({ - openTender = true, - negotiatedContract = false, - directAward = false, - competitiveBidding = false, - otherMethod = false, -}) => { - let selector; - - if (openTender) { - selector = radios(OPEN_TENDER.ID).option; - } - - if (negotiatedContract) { - selector = radios(NEGOTIATED_CONTRACT.ID).option; - } - - if (directAward) { - selector = radios(DIRECT_AWARD.ID).option; - } - - if (competitiveBidding) { - selector = radios(COMPETITIVE_BIDDING.ID).option; - } - - if (otherMethod) { - selector = radios(OTHER.ID).option; - - selector.label().click(); - - cy.keyboardInput(field(OTHER_AWARD_METHOD).input(), application.EXPORT_CONTRACT[OTHER_AWARD_METHOD]); - } - - selector.label().click(); +const completeAndSubmitHowWasTheContractAwardedForm = ({ openTender, negotiatedContract, directAward, competitiveBidding, otherMethod, otherMethodText }) => { + cy.completeHowWasTheContractAwardedForm({ + openTender, + negotiatedContract, + directAward, + competitiveBidding, + otherMethod, + otherMethodText, + }); cy.clickSubmitButton(); }; diff --git a/e2e-tests/commands/insurance/complete-how-was-the-contract-awarded-form.js b/e2e-tests/commands/insurance/complete-how-was-the-contract-awarded-form.js new file mode 100644 index 0000000000..d4496201fc --- /dev/null +++ b/e2e-tests/commands/insurance/complete-how-was-the-contract-awarded-form.js @@ -0,0 +1,61 @@ +import { field, radios } from '../../pages/shared'; +import { EXPORT_CONTRACT as EXPORT_CONTRACT_FIELD_IDS } from '../../constants/field-ids/insurance/export-contract'; +import { EXPORT_CONTRACT_FIELDS as FIELDS } from '../../content-strings/fields/insurance'; +import application from '../../fixtures/application'; + +const { + HOW_WAS_THE_CONTRACT_AWARDED: { AWARD_METHOD, OTHER_AWARD_METHOD }, +} = EXPORT_CONTRACT_FIELD_IDS; + +const { OPEN_TENDER, NEGOTIATED_CONTRACT, DIRECT_AWARD, COMPETITIVE_BIDDING, OTHER } = FIELDS.HOW_WAS_THE_CONTRACT_AWARDED[AWARD_METHOD].OPTIONS; + +/** + * completeHowWasTheContractAwardedForm + * Complete the "How was the contract awarded" form + * @param {Boolean} openTender: Award method as OPEN_TENDER. Defaults to true + * @param {Boolean} negotiatedContract: Award method as NEGOTIATED_CONTRACT. Defaults to false + * @param {Boolean} directAward: Award method as DIRECT_AWARD. Defaults to false + * @param {Boolean} competitiveBidding: Award method as COMPETITIVE_BIDDING. Defaults to false + * @param {Boolean} otherMethod: Award method as OTHER. Defaults to false + * @param {String} otherMethodText: OTHER award method text + */ +const completeHowWasTheContractAwardedForm = ({ + openTender = true, + negotiatedContract = false, + directAward = false, + competitiveBidding = false, + otherMethod = false, + otherMethodText = application.EXPORT_CONTRACT[OTHER_AWARD_METHOD], +}) => { + let selector; + + if (openTender) { + selector = radios(OPEN_TENDER.ID).option; + } + + if (negotiatedContract) { + selector = radios(NEGOTIATED_CONTRACT.ID).option; + } + + if (directAward) { + selector = radios(DIRECT_AWARD.ID).option; + } + + if (competitiveBidding) { + selector = radios(COMPETITIVE_BIDDING.ID).option; + } + + if (otherMethod) { + selector = radios(OTHER.ID).option; + + selector.label().click(); + + if (otherMethodText) { + cy.keyboardInput(field(OTHER_AWARD_METHOD).input(), otherMethodText); + } + } + + selector.label().click(); +}; + +export default completeHowWasTheContractAwardedForm; diff --git a/e2e-tests/commands/shared-commands/assertions/assert-how-was-the-contract-awarded-field-values.js b/e2e-tests/commands/shared-commands/assertions/assert-how-was-the-contract-awarded-field-values.js index 2e30225c07..bbbd3fb5e1 100644 --- a/e2e-tests/commands/shared-commands/assertions/assert-how-was-the-contract-awarded-field-values.js +++ b/e2e-tests/commands/shared-commands/assertions/assert-how-was-the-contract-awarded-field-values.js @@ -17,6 +17,7 @@ const { OPEN_TENDER, NEGOTIATED_CONTRACT, DIRECT_AWARD, COMPETITIVE_BIDDING, OTH * @param {String} directAward: Award method as DIRECT_AWARD * @param {String} competitiveBidding: Award method as COMPETITIVE_BIDDING * @param {String} otherMethod: Award method as OTHER + * @param {String} otherMethodText: OTHER award method text */ const assertHowWasTheContractAwardedFieldValues = ({ openTender = false, @@ -24,6 +25,7 @@ const assertHowWasTheContractAwardedFieldValues = ({ directAward = false, competitiveBidding = false, otherMethod = false, + otherMethodText = application.EXPORT_CONTRACT[OTHER_AWARD_METHOD], }) => { const selectors = { openTender: radios(OPEN_TENDER.ID).option.input(), @@ -73,7 +75,9 @@ const assertHowWasTheContractAwardedFieldValues = ({ if (otherMethod) { cy.assertRadioOptionIsChecked(selectors.otherMethod); - cy.checkValue(selectors.otherMethodTextInput, application.EXPORT_CONTRACT[OTHER_AWARD_METHOD]); + if (otherMethodText) { + cy.checkValue(selectors.otherMethodTextInput, otherMethodText); + } cy.assertRadioOptionIsNotChecked(selectors.openTender); cy.assertRadioOptionIsNotChecked(selectors.negotiatedContract); diff --git a/e2e-tests/insurance/cypress/e2e/journeys/export-contract/agent-charges/save-and-back-method-fixed-sum.spec.js b/e2e-tests/insurance/cypress/e2e/journeys/export-contract/agent-charges/save-and-back-method-fixed-sum.spec.js index c294d95952..b2ca5a628c 100644 --- a/e2e-tests/insurance/cypress/e2e/journeys/export-contract/agent-charges/save-and-back-method-fixed-sum.spec.js +++ b/e2e-tests/insurance/cypress/e2e/journeys/export-contract/agent-charges/save-and-back-method-fixed-sum.spec.js @@ -92,11 +92,8 @@ context(`Insurance - Export contract - Agent charges - Save and go back - ${METH cy.startInsuranceExportContractSection({}); - // TODO: EMS-3665 - increment the value in command below instead. - cy.completeAndSubmitHowWasTheContractAwardedForm({}); - - // go through 5 export contract forms. - cy.clickSubmitButtonMultipleTimes({ count: 5 }); + // go through 6 export contract forms. + cy.clickSubmitButtonMultipleTimes({ count: 6 }); cy.assertAgentChargesFieldValues({ fixedSumMethod: true }); }); diff --git a/e2e-tests/insurance/cypress/e2e/journeys/export-contract/agent-charges/save-and-back-method-percentage.spec.js b/e2e-tests/insurance/cypress/e2e/journeys/export-contract/agent-charges/save-and-back-method-percentage.spec.js index 1df03da261..36f6d015cb 100644 --- a/e2e-tests/insurance/cypress/e2e/journeys/export-contract/agent-charges/save-and-back-method-percentage.spec.js +++ b/e2e-tests/insurance/cypress/e2e/journeys/export-contract/agent-charges/save-and-back-method-percentage.spec.js @@ -92,11 +92,8 @@ context(`Insurance - Export contract - Agent charges - Save and go back - ${METH cy.startInsuranceExportContractSection({}); - // TODO: EMS-3665 - increment the value in command below instead. - cy.completeAndSubmitHowWasTheContractAwardedForm({}); - - // go through 5 export contract forms. - cy.clickSubmitButtonMultipleTimes({ count: 5 }); + // go through 6 export contract forms. + cy.clickSubmitButtonMultipleTimes({ count: 6 }); cy.assertAgentChargesFieldValues({ percentageMethod: true }); }); diff --git a/e2e-tests/insurance/cypress/e2e/journeys/export-contract/agent-charges/save-and-back.spec.js b/e2e-tests/insurance/cypress/e2e/journeys/export-contract/agent-charges/save-and-back.spec.js index 15a0fa13e9..5a92953a9e 100644 --- a/e2e-tests/insurance/cypress/e2e/journeys/export-contract/agent-charges/save-and-back.spec.js +++ b/e2e-tests/insurance/cypress/e2e/journeys/export-contract/agent-charges/save-and-back.spec.js @@ -44,7 +44,7 @@ context(`Insurance - Export contract - Agent charges - Save and go back - empty }); // TODO: partially submitted - country, no method. - // . should be populated when going back to the page. + // should be populated when going back to the page. describe('when submitting an empty form via `save and go back` button', () => { beforeEach(() => { diff --git a/e2e-tests/insurance/cypress/e2e/journeys/export-contract/agent-details/save-and-back.spec.js b/e2e-tests/insurance/cypress/e2e/journeys/export-contract/agent-details/save-and-back.spec.js index 5d25491d60..154de1fea6 100644 --- a/e2e-tests/insurance/cypress/e2e/journeys/export-contract/agent-details/save-and-back.spec.js +++ b/e2e-tests/insurance/cypress/e2e/journeys/export-contract/agent-details/save-and-back.spec.js @@ -95,11 +95,8 @@ context('Insurance - Export contract - Agent details - Save and go back', () => cy.startInsuranceExportContractSection({}); - // TODO: EMS-3665 - increment the value in command below instead. - cy.completeAndSubmitHowWasTheContractAwardedForm({}); - - // go through 3 export contract forms. - cy.clickSubmitButtonMultipleTimes({ count: 3 }); + // go through 4 export contract forms. + cy.clickSubmitButtonMultipleTimes({ count: 4 }); cy.assertAgentDetailsFieldValues({}); }); diff --git a/e2e-tests/insurance/cypress/e2e/journeys/export-contract/agent-service/save-and-back.spec.js b/e2e-tests/insurance/cypress/e2e/journeys/export-contract/agent-service/save-and-back.spec.js index 2b0ad19d9e..daa28a757c 100644 --- a/e2e-tests/insurance/cypress/e2e/journeys/export-contract/agent-service/save-and-back.spec.js +++ b/e2e-tests/insurance/cypress/e2e/journeys/export-contract/agent-service/save-and-back.spec.js @@ -94,11 +94,8 @@ context('Insurance - Export contract - Agent service - Save and go back', () => cy.startInsuranceExportContractSection({}); - // TODO: EMS-3665 - increment the value in command below instead. - cy.completeAndSubmitHowWasTheContractAwardedForm({}); - - // go through 4 export contract forms. - cy.clickSubmitButtonMultipleTimes({ count: 4 }); + // go through 5 export contract forms. + cy.clickSubmitButtonMultipleTimes({ count: 5 }); cy.assertAgentServiceFieldValues({}); }); @@ -124,11 +121,8 @@ context('Insurance - Export contract - Agent service - Save and go back', () => cy.startInsuranceExportContractSection({}); - // TODO: EMS-3665 - increment the value in command below instead. - cy.completeAndSubmitHowWasTheContractAwardedForm({}); - - // go through 4 export contract forms. - cy.clickSubmitButtonMultipleTimes({ count: 4 }); + // go through 5 export contract forms. + cy.clickSubmitButtonMultipleTimes({ count: 5 }); cy.assertAgentServiceFieldValues({ agentIsCharging: true }); }); diff --git a/e2e-tests/insurance/cypress/e2e/journeys/export-contract/agent/save-and-back.spec.js b/e2e-tests/insurance/cypress/e2e/journeys/export-contract/agent/save-and-back.spec.js index f8a9b929be..38ce68ffae 100644 --- a/e2e-tests/insurance/cypress/e2e/journeys/export-contract/agent/save-and-back.spec.js +++ b/e2e-tests/insurance/cypress/e2e/journeys/export-contract/agent/save-and-back.spec.js @@ -75,11 +75,8 @@ context('Insurance - Export contract - Agent - Save and go back', () => { cy.startInsuranceExportContractSection({}); - // TODO: EMS-3665 - increment the value in command below instead. - cy.completeAndSubmitHowWasTheContractAwardedForm({}); - - // go through 2 export contract forms. - cy.clickSubmitButtonMultipleTimes({ count: 2 }); + // go through 3 export contract forms. + cy.clickSubmitButtonMultipleTimes({ count: 3 }); cy.assertNoRadioOptionIsChecked(); }); @@ -108,11 +105,8 @@ context('Insurance - Export contract - Agent - Save and go back', () => { cy.startInsuranceExportContractSection({}); - // TODO: EMS-3665 - increment the value in command below instead. - cy.completeAndSubmitHowWasTheContractAwardedForm({}); - - // go through 2 export contract forms. - cy.clickSubmitButtonMultipleTimes({ count: 2 }); + // go through 3 export contract forms. + cy.clickSubmitButtonMultipleTimes({ count: 3 }); cy.assertYesRadioOptionIsChecked(); }); diff --git a/e2e-tests/insurance/cypress/e2e/journeys/export-contract/declined-by-private-market/save-and-back.spec.js b/e2e-tests/insurance/cypress/e2e/journeys/export-contract/declined-by-private-market/save-and-back.spec.js index a9c02901fc..85ff6dff32 100644 --- a/e2e-tests/insurance/cypress/e2e/journeys/export-contract/declined-by-private-market/save-and-back.spec.js +++ b/e2e-tests/insurance/cypress/e2e/journeys/export-contract/declined-by-private-market/save-and-back.spec.js @@ -76,11 +76,8 @@ context('Insurance - Export contract - Declined by private market - Save and go cy.startInsuranceExportContractSection({}); - // TODO: EMS-3665 - increment the value in command below instead. - cy.completeAndSubmitHowWasTheContractAwardedForm({}); - - // go through 3 export contract forms. - cy.clickSubmitButtonMultipleTimes({ count: 3 }); + // go through 4 export contract forms. + cy.clickSubmitButtonMultipleTimes({ count: 4 }); cy.checkTextareaValue({ fieldId: FIELD_ID, diff --git a/e2e-tests/insurance/cypress/e2e/journeys/export-contract/how-was-the-contract-awarded/save-and-back.spec.js b/e2e-tests/insurance/cypress/e2e/journeys/export-contract/how-was-the-contract-awarded/save-and-back.spec.js new file mode 100644 index 0000000000..297ef896c8 --- /dev/null +++ b/e2e-tests/insurance/cypress/e2e/journeys/export-contract/how-was-the-contract-awarded/save-and-back.spec.js @@ -0,0 +1,101 @@ +import { INSURANCE_ROUTES } from '../../../../../../constants/routes/insurance'; + +const { + ROOT, + EXPORT_CONTRACT: { HOW_WAS_THE_CONTRACT_AWARDED }, +} = INSURANCE_ROUTES; + +const baseUrl = Cypress.config('baseUrl'); + +context('Insurance - Export contract - How was the contract awarded page - Save and go back', () => { + let referenceNumber; + let url; + + before(() => { + cy.completeSignInAndGoToApplication({}).then(({ referenceNumber: refNumber }) => { + referenceNumber = refNumber; + + // go to the page we want to test. + cy.startInsuranceExportContractSection({}); + + url = `${baseUrl}${ROOT}/${referenceNumber}${HOW_WAS_THE_CONTRACT_AWARDED}`; + + cy.assertUrl(url); + }); + }); + + beforeEach(() => { + cy.saveSession(); + }); + + after(() => { + cy.deleteApplication(referenceNumber); + }); + + describe('when submitting an empty form via `save and go back` button', () => { + beforeEach(() => { + cy.navigateToUrl(url); + + cy.clickSaveAndBackButton(); + }); + + it('should redirect to `all sections`', () => { + cy.assertAllSectionsUrl(referenceNumber); + }); + + it('should retain the `export contract` task status as `not started yet`', () => { + cy.checkTaskExportContractStatusIsNotStartedYet(); + }); + }); + + describe('when fields are partially completed', () => { + beforeEach(() => { + cy.navigateToUrl(url); + }); + + it('should update the status of task `export contract` to `in progress`', () => { + cy.completeHowWasTheContractAwardedForm({ + otherMethod: true, + otherMethodText: null, + }); + + cy.clickSaveAndBackButton(); + + cy.assertAllSectionsUrl(referenceNumber); + + cy.checkTaskExportContractStatusIsInProgress(); + }); + + describe('when going back to the page', () => { + it('should have the submitted value', () => { + cy.assertHowWasTheContractAwardedFieldValues({ + otherMethodText: '', + }); + }); + }); + }); + + describe('when all fields are provided', () => { + it('should retain the status of task `export contract` as `in progress`', () => { + cy.navigateToUrl(url); + + cy.completeHowWasTheContractAwardedForm({ otherMethod: true }); + + cy.clickSaveAndBackButton(); + + cy.navigateToAllSectionsUrl(referenceNumber); + + cy.checkTaskExportContractStatusIsInProgress(); + }); + + describe('when going back to the page', () => { + it('should have the submitted values', () => { + cy.navigateToAllSectionsUrl(referenceNumber); + + cy.startInsuranceExportContractSection({}); + + cy.assertHowWasTheContractAwardedFieldValues({ otherMethod: true }); + }); + }); + }); +}); diff --git a/e2e-tests/insurance/cypress/e2e/journeys/export-contract/private-market/save-and-back.spec.js b/e2e-tests/insurance/cypress/e2e/journeys/export-contract/private-market/save-and-back.spec.js index ada234549e..6f132ddc62 100644 --- a/e2e-tests/insurance/cypress/e2e/journeys/export-contract/private-market/save-and-back.spec.js +++ b/e2e-tests/insurance/cypress/e2e/journeys/export-contract/private-market/save-and-back.spec.js @@ -74,11 +74,8 @@ context('Insurance - Export contract - Private market - Save and go back', () => cy.startInsuranceExportContractSection({}); - // TODO: EMS-3665 - increment the value in command below instead. - cy.completeAndSubmitHowWasTheContractAwardedForm({}); - - // go through 2 export contract forms. - cy.clickSubmitButtonMultipleTimes({ count: 2 }); + // go through 3 export contract forms. + cy.clickSubmitButtonMultipleTimes({ count: 3 }); cy.assertNoRadioOptionIsChecked(); }); @@ -107,11 +104,8 @@ context('Insurance - Export contract - Private market - Save and go back', () => cy.startInsuranceExportContractSection({}); - // TODO: EMS-3665 - increment the value in command below instead. - cy.completeAndSubmitHowWasTheContractAwardedForm({}); - - // go through 2 export contract forms. - cy.clickSubmitButtonMultipleTimes({ count: 2 }); + // go through 3 export contract forms. + cy.clickSubmitButtonMultipleTimes({ count: 3 }); cy.assertYesRadioOptionIsChecked(); }); 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 088110d370..36a89f1402 100644 --- a/e2e-tests/insurance/cypress/support/application/export-contract/index.js +++ b/e2e-tests/insurance/cypress/support/application/export-contract/index.js @@ -1,4 +1,5 @@ Cypress.Commands.add('completeAboutGoodsOrServicesForm', require('../../../../../commands/insurance/complete-about-goods-or-services-form')); +Cypress.Commands.add('completeHowWasTheContractAwardedForm', require('../../../../../commands/insurance/complete-how-was-the-contract-awarded-form')); Cypress.Commands.add( 'completeAndSubmitHowWasTheContractAwardedForm', require('../../../../../commands/insurance/complete-and-submit-how-was-the-contract-awarded-form'), diff --git a/src/ui/server/controllers/insurance/export-contract/how-was-the-contract-awarded/index.test.ts b/src/ui/server/controllers/insurance/export-contract/how-was-the-contract-awarded/index.test.ts index 6e985aa224..3f14b73342 100644 --- a/src/ui/server/controllers/insurance/export-contract/how-was-the-contract-awarded/index.test.ts +++ b/src/ui/server/controllers/insurance/export-contract/how-was-the-contract-awarded/index.test.ts @@ -16,7 +16,7 @@ import { mockReq, mockRes, mockExportContract, referenceNumber } from '../../../ const { INSURANCE_ROOT, - EXPORT_CONTRACT: { ABOUT_GOODS_OR_SERVICES }, + EXPORT_CONTRACT: { ABOUT_GOODS_OR_SERVICES, HOW_WAS_THE_CONTRACT_AWARDED_SAVE_AND_BACK: SAVE_AND_BACK }, PROBLEM_WITH_SERVICE, } = INSURANCE_ROUTES; @@ -71,7 +71,7 @@ describe('controllers/insurance/export-contract/how-was-the-contract-awarded', ( describe('pageVariables', () => { it('should have correct properties', () => { - const result = pageVariables(); + const result = pageVariables(referenceNumber); const expected = { FIELDS: { @@ -84,7 +84,7 @@ describe('controllers/insurance/export-contract/how-was-the-contract-awarded', ( ...FIELDS.HOW_WAS_THE_CONTRACT_AWARDED[OTHER_AWARD_METHOD], }, }, - SAVE_AND_BACK_URL: '#', + SAVE_AND_BACK_URL: `${INSURANCE_ROOT}/${referenceNumber}${SAVE_AND_BACK}`, }; expect(result).toEqual(expected); @@ -100,7 +100,7 @@ describe('controllers/insurance/export-contract/how-was-the-contract-awarded', ( PAGE_CONTENT_STRINGS, BACK_LINK: req.headers.referer, }), - ...pageVariables(), + ...pageVariables(referenceNumber), CONDITIONAL_OTHER_METHOD_HTML, userName: getUserNameFromSession(req.session.user), submittedValues: mockExportContract, @@ -140,7 +140,7 @@ describe('controllers/insurance/export-contract/how-was-the-contract-awarded', ( PAGE_CONTENT_STRINGS, BACK_LINK: req.headers.referer, }), - ...pageVariables(), + ...pageVariables(referenceNumber), CONDITIONAL_OTHER_METHOD_HTML, userName: getUserNameFromSession(req.session.user), submittedValues: { diff --git a/src/ui/server/controllers/insurance/export-contract/how-was-the-contract-awarded/index.ts b/src/ui/server/controllers/insurance/export-contract/how-was-the-contract-awarded/index.ts index cf3fa3152a..aa6828d9c5 100644 --- a/src/ui/server/controllers/insurance/export-contract/how-was-the-contract-awarded/index.ts +++ b/src/ui/server/controllers/insurance/export-contract/how-was-the-contract-awarded/index.ts @@ -14,7 +14,7 @@ import { ObjectType, Request, Response } from '../../../../../types'; const { INSURANCE_ROOT, - EXPORT_CONTRACT: { ABOUT_GOODS_OR_SERVICES }, + EXPORT_CONTRACT: { ABOUT_GOODS_OR_SERVICES, HOW_WAS_THE_CONTRACT_AWARDED_SAVE_AND_BACK: SAVE_AND_BACK }, PROBLEM_WITH_SERVICE, } = INSURANCE_ROUTES; @@ -39,9 +39,10 @@ export const FIELD_IDS = [AWARD_METHOD, OTHER_AWARD_METHOD]; /** * pageVariables * Page fields and "save and go back" URL + * @param {Number} Application reference number * @returns {Object} Page variables */ -export const pageVariables = () => ({ +export const pageVariables = (referenceNumber: number) => ({ FIELDS: { AWARD_METHOD: { ID: AWARD_METHOD, @@ -52,7 +53,7 @@ export const pageVariables = () => ({ ...FIELDS.HOW_WAS_THE_CONTRACT_AWARDED[OTHER_AWARD_METHOD], }, }, - SAVE_AND_BACK_URL: '#', + SAVE_AND_BACK_URL: `${INSURANCE_ROOT}/${referenceNumber}${SAVE_AND_BACK}`, }); /** @@ -69,12 +70,14 @@ export const get = (req: Request, res: Response) => { return res.redirect(PROBLEM_WITH_SERVICE); } + const { referenceNumber } = application; + return res.render(TEMPLATE, { ...insuranceCorePageVariables({ PAGE_CONTENT_STRINGS, BACK_LINK: req.headers.referer, }), - ...pageVariables(), + ...pageVariables(referenceNumber), CONDITIONAL_OTHER_METHOD_HTML, userName: getUserNameFromSession(req.session.user), submittedValues: application.exportContract, @@ -120,7 +123,7 @@ export const post = async (req: Request, res: Response) => { PAGE_CONTENT_STRINGS, BACK_LINK: req.headers.referer, }), - ...pageVariables(), + ...pageVariables(referenceNumber), CONDITIONAL_OTHER_METHOD_HTML, userName: getUserNameFromSession(req.session.user), submittedValues, diff --git a/src/ui/server/controllers/insurance/export-contract/how-was-the-contract-awarded/save-and-back/index.test.ts b/src/ui/server/controllers/insurance/export-contract/how-was-the-contract-awarded/save-and-back/index.test.ts new file mode 100644 index 0000000000..f1ec197d3b --- /dev/null +++ b/src/ui/server/controllers/insurance/export-contract/how-was-the-contract-awarded/save-and-back/index.test.ts @@ -0,0 +1,121 @@ +import { post } from '.'; +import { INSURANCE_ROUTES } from '../../../../../constants/routes/insurance'; +import EXPORT_CONTRACT_FIELD_IDS from '../../../../../constants/field-ids/insurance/export-contract'; +import { FIELD_IDS } from '..'; +import constructPayload from '../../../../../helpers/construct-payload'; +import mapAndSave from '../../map-and-save/export-contract'; +import generateValidationErrors from '../validation'; +import { Request, Response } from '../../../../../../types'; +import { mockReq, mockRes, referenceNumber } from '../../../../../test-mocks'; + +const { INSURANCE_ROOT, ALL_SECTIONS, PROBLEM_WITH_SERVICE } = INSURANCE_ROUTES; + +const { + HOW_WAS_THE_CONTRACT_AWARDED: { OTHER_AWARD_METHOD }, +} = EXPORT_CONTRACT_FIELD_IDS; + +describe('controllers/insurance/export-contract/how-was-the-contract-awarded/save-and-back', () => { + let req: Request; + let res: Response; + + jest.mock('../../map-and-save/export-contract'); + + let mapAndSaveSpy = jest.fn(() => Promise.resolve(true)); + + const mockFormBody = { + _csrf: '1234', + [OTHER_AWARD_METHOD]: 'Mock other method', + }; + + beforeEach(() => { + req = mockReq(); + res = mockRes(); + + req.body = mockFormBody; + + mapAndSave.exportContract = mapAndSaveSpy; + }); + + describe('when the form has data', () => { + beforeEach(() => { + jest.resetAllMocks(); + + mapAndSaveSpy = jest.fn(() => Promise.resolve(true)); + mapAndSave.exportContract = mapAndSaveSpy; + }); + + it('should call mapAndSave.exportContract with data from constructPayload function, application and validationErrors', async () => { + await post(req, res); + + const payload = constructPayload(req.body, FIELD_IDS); + + const validationErrors = generateValidationErrors(payload); + + expect(mapAndSave.exportContract).toHaveBeenCalledTimes(1); + expect(mapAndSave.exportContract).toHaveBeenCalledWith(payload, res.locals.application, validationErrors); + }); + + it(`should redirect to ${ALL_SECTIONS}`, async () => { + mapAndSave.exportContract = mapAndSaveSpy; + + await post(req, res); + + const expected = `${INSURANCE_ROOT}/${referenceNumber}${ALL_SECTIONS}`; + + expect(res.redirect).toHaveBeenCalledWith(expected); + }); + }); + + describe('when the form does not have any data', () => { + it(`should redirect to ${ALL_SECTIONS}`, async () => { + req.body = { _csrf: '1234' }; + + await post(req, res); + + const expected = `${INSURANCE_ROOT}/${referenceNumber}${ALL_SECTIONS}`; + + expect(res.redirect).toHaveBeenCalledWith(expected); + }); + }); + + 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('when the mapAndSave call does not return anything', () => { + beforeEach(() => { + mapAndSaveSpy = jest.fn(() => Promise.resolve(false)); + mapAndSave.exportContract = mapAndSaveSpy; + }); + + it(`should redirect to ${PROBLEM_WITH_SERVICE}`, async () => { + await post(req, res); + + expect(res.redirect).toHaveBeenCalledWith(PROBLEM_WITH_SERVICE); + }); + }); + + describe('when the mapAndSave call fails', () => { + beforeEach(() => { + mapAndSaveSpy = jest.fn(() => Promise.reject(new Error('mock'))); + + mapAndSave.exportContract = mapAndSaveSpy; + }); + + 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-was-the-contract-awarded/save-and-back/index.ts b/src/ui/server/controllers/insurance/export-contract/how-was-the-contract-awarded/save-and-back/index.ts new file mode 100644 index 0000000000..b78e860385 --- /dev/null +++ b/src/ui/server/controllers/insurance/export-contract/how-was-the-contract-awarded/save-and-back/index.ts @@ -0,0 +1,53 @@ +import { INSURANCE_ROUTES } from '../../../../../constants/routes/insurance'; +import hasFormData from '../../../../../helpers/has-form-data'; +import { FIELD_IDS } from '..'; +import constructPayload from '../../../../../helpers/construct-payload'; +import generateValidationErrors from '../validation'; +import mapAndSave from '../../map-and-save/export-contract'; +import { Request, Response } from '../../../../../../types'; + +const { INSURANCE_ROOT, ALL_SECTIONS, PROBLEM_WITH_SERVICE } = INSURANCE_ROUTES; + +/** + * post + * Save any valid "How the contract was awarded" form fields and if successful, redirect to the all sections page + * @param {Express.Request} Express request + * @param {Express.Response} Express response + * @returns {Express.Response.redirect} All sections page or error page + */ +export const post = async (req: Request, res: Response) => { + try { + const { application } = res.locals; + + if (!application) { + return res.redirect(PROBLEM_WITH_SERVICE); + } + + const { referenceNumber } = req.params; + + /** + * If form data is populated: + * 1) generate a payload. + * 2) generate validation errors. + * 3) call mapAndSave + * 4) redirect + */ + if (hasFormData(req.body)) { + const payload = constructPayload(req.body, FIELD_IDS); + + const validationErrors = generateValidationErrors(payload); + + const saveResponse = await mapAndSave.exportContract(payload, application, validationErrors); + + if (!saveResponse) { + return res.redirect(PROBLEM_WITH_SERVICE); + } + } + + return res.redirect(`${INSURANCE_ROOT}/${referenceNumber}${ALL_SECTIONS}`); + } catch (err) { + console.error('Error updating application - export contract - how was the contract awarded (save and back) %O', err); + + return res.redirect(PROBLEM_WITH_SERVICE); + } +}; diff --git a/src/ui/server/controllers/insurance/export-contract/map-submitted-data/agent-service-charge/index.ts b/src/ui/server/controllers/insurance/export-contract/map-submitted-data/agent-service-charge/index.ts index ba41b65533..b45f10a892 100644 --- a/src/ui/server/controllers/insurance/export-contract/map-submitted-data/agent-service-charge/index.ts +++ b/src/ui/server/controllers/insurance/export-contract/map-submitted-data/agent-service-charge/index.ts @@ -71,6 +71,10 @@ const mapSubmittedData = (formBody: RequestBody): object => { delete populatedData[ALTERNATIVE_CURRENCY_CODE]; } + /** + * If METHOD is an empty string, + * nullify the field. + */ if (isEmptyString(populatedData[METHOD])) { populatedData[METHOD] = null; } diff --git a/src/ui/server/controllers/insurance/export-contract/map-submitted-data/agent-service/index.ts b/src/ui/server/controllers/insurance/export-contract/map-submitted-data/agent-service/index.ts index dbd2df32cb..83ac9507db 100644 --- a/src/ui/server/controllers/insurance/export-contract/map-submitted-data/agent-service/index.ts +++ b/src/ui/server/controllers/insurance/export-contract/map-submitted-data/agent-service/index.ts @@ -17,6 +17,10 @@ const { const mapSubmittedData = (formBody: RequestBody): object => { const populatedData = formBody; + /** + * If IS_CHARGING is an empty string, + * nullify the field. + */ if (isEmptyString(formBody[IS_CHARGING])) { populatedData[IS_CHARGING] = null; } diff --git a/src/ui/server/controllers/insurance/export-contract/map-submitted-data/export-contract/index.test.ts b/src/ui/server/controllers/insurance/export-contract/map-submitted-data/export-contract/index.test.ts index 0f396a1d2e..f7ff3d04be 100644 --- a/src/ui/server/controllers/insurance/export-contract/map-submitted-data/export-contract/index.test.ts +++ b/src/ui/server/controllers/insurance/export-contract/map-submitted-data/export-contract/index.test.ts @@ -22,8 +22,21 @@ describe('controllers/insurance/export-contract/map-submitted-data/export-contra }; }); + describe(`when ${AWARD_METHOD} field is an empty string`, () => { + it(`should return the form body without ${AWARD_METHOD}`, () => { + const mockBodyWithAwardMethod = { + ...mockFormBody, + [AWARD_METHOD]: '', + }; + + const result = mapSubmittedData(mockBodyWithAwardMethod); + + expect(result[AWARD_METHOD]).toBeUndefined(); + }); + }); + describe(`when ${AWARD_METHOD} field is provided`, () => { - it(`should return the form body with ${FINAL_DESTINATION} as a country ISO code`, () => { + it(`should return the form body with ${AWARD_METHOD} as a connect object`, () => { const mockBodyWithAwardMethod = { ...mockFormBody, [AWARD_METHOD]: mockAwardMethodId, diff --git a/src/ui/server/controllers/insurance/export-contract/map-submitted-data/export-contract/index.ts b/src/ui/server/controllers/insurance/export-contract/map-submitted-data/export-contract/index.ts index 7b30bfa968..a941567d9c 100644 --- a/src/ui/server/controllers/insurance/export-contract/map-submitted-data/export-contract/index.ts +++ b/src/ui/server/controllers/insurance/export-contract/map-submitted-data/export-contract/index.ts @@ -1,6 +1,7 @@ import FIELD_IDS from '../../../../../constants/field-ids/insurance/export-contract'; import getCountryByIsoCode from '../../../../../helpers/get-country-by-iso-code'; import { objectHasProperty } from '../../../../../helpers/object'; +import { isEmptyString } from '../../../../../helpers/string'; import { Country, RequestBody } from '../../../../../../types'; const { @@ -13,9 +14,10 @@ const { * Map "export contract" related data for the following forms: * - "How was the contract awarded" * - "About goods or services" - * 1) if AWARD_METHOD is provided, map as a KeystoneJS connect object with ID relationship. - * 2) if FINAL_DESTINATION is provided, map as the country ISO code. - * 3) if FINAL_DESTINATION_KNOWN is false, delete FINAL_DESTINATION. + * 1) if AWARD_METHOD is an empty string, delete the field. + * 2) if AWARD_METHOD is provided, map as a KeystoneJS connect object with ID relationship. + * 3) if FINAL_DESTINATION is provided, map as the country ISO code. + * 4) if FINAL_DESTINATION_KNOWN is false, delete FINAL_DESTINATION. * @param {RequestBody} formBody * @param {Array} countries * @returns {Object} populatedData @@ -23,7 +25,15 @@ const { const mapSubmittedData = (formBody: RequestBody, countries?: Array): object => { const populatedData = formBody; - if (objectHasProperty(formBody, AWARD_METHOD)) { + /** + * If AWARD_METHOD is an empty string, + * delete the field. + */ + if (isEmptyString(formBody[AWARD_METHOD])) { + delete populatedData[AWARD_METHOD]; + } + + if (objectHasProperty(populatedData, AWARD_METHOD)) { populatedData[AWARD_METHOD] = { connect: { id: formBody[AWARD_METHOD], diff --git a/src/ui/server/controllers/insurance/policy/map-submitted-data/broker/index.ts b/src/ui/server/controllers/insurance/policy/map-submitted-data/broker/index.ts index 18dd55b430..2f7388dc12 100644 --- a/src/ui/server/controllers/insurance/policy/map-submitted-data/broker/index.ts +++ b/src/ui/server/controllers/insurance/policy/map-submitted-data/broker/index.ts @@ -25,7 +25,7 @@ const mapSubmittedData = (formBody: RequestBody): object => { /** * If USING_BROKER is an empty string, - * Delete the field. + * delete the field. */ if (isEmptyString(formBody[USING_BROKER])) { delete populatedData[USING_BROKER]; diff --git a/src/ui/server/controllers/insurance/policy/map-submitted-data/policy-contact/index.ts b/src/ui/server/controllers/insurance/policy/map-submitted-data/policy-contact/index.ts index 80448b6cf0..d416e8e940 100644 --- a/src/ui/server/controllers/insurance/policy/map-submitted-data/policy-contact/index.ts +++ b/src/ui/server/controllers/insurance/policy/map-submitted-data/policy-contact/index.ts @@ -65,7 +65,7 @@ const mapSubmittedData = (formBody: RequestBody, application: Application): obje /** * If NAME is an empty string, - * Delete the field. + * delete the field. */ if (isEmptyString(populatedData[NAME])) { delete populatedData[NAME]; 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 fe3561a025..536015fba9 100644 --- a/src/ui/server/routes/insurance/export-contract/index.test.ts +++ b/src/ui/server/routes/insurance/export-contract/index.test.ts @@ -82,7 +82,7 @@ describe('routes/insurance/export-contract', () => { it('should setup all routes', () => { expect(get).toHaveBeenCalledTimes(30); - expect(post).toHaveBeenCalledTimes(37); + expect(post).toHaveBeenCalledTimes(38); expect(get).toHaveBeenCalledWith(`/:referenceNumber${ROOT}`, exportContractRootGet); diff --git a/src/ui/server/routes/insurance/export-contract/index.ts b/src/ui/server/routes/insurance/export-contract/index.ts index 6e4b5da7cf..328c344aa5 100644 --- a/src/ui/server/routes/insurance/export-contract/index.ts +++ b/src/ui/server/routes/insurance/export-contract/index.ts @@ -5,6 +5,7 @@ import { get as howWasTheContractAwardedGet, post as howWasTheContractAwardedPost, } from '../../../controllers/insurance/export-contract/how-was-the-contract-awarded'; +import { post as howWasTheContractAwardedSaveAndBackPost } from '../../../controllers/insurance/export-contract/how-was-the-contract-awarded/save-and-back'; import { get as aboutGoodsOrServicesGet, post as aboutGoodsOrServicesPost } from '../../../controllers/insurance/export-contract/about-goods-or-services'; import { post as aboutGoodsOrServicesSaveAndBackPost } from '../../../controllers/insurance/export-contract/about-goods-or-services/save-and-back'; import { get as howWillYouGetPaidGet, post as howWillYouGetPaidPost } from '../../../controllers/insurance/export-contract/how-will-you-get-paid'; @@ -33,6 +34,7 @@ import { get as checkYourAnswersGet, post as checkYourAnswersPost } from '../../ const { ROOT, HOW_WAS_THE_CONTRACT_AWARDED, + HOW_WAS_THE_CONTRACT_AWARDED_SAVE_AND_BACK, ABOUT_GOODS_OR_SERVICES, ABOUT_GOODS_OR_SERVICES_SAVE_AND_BACK, ABOUT_GOODS_OR_SERVICES_CHANGE, @@ -79,6 +81,8 @@ exportContractRoute.get(`/:referenceNumber${ROOT}`, exportContractRootGet); exportContractRoute.get(`/:referenceNumber${HOW_WAS_THE_CONTRACT_AWARDED}`, howWasTheContractAwardedGet); exportContractRoute.post(`/:referenceNumber${HOW_WAS_THE_CONTRACT_AWARDED}`, howWasTheContractAwardedPost); +exportContractRoute.post(`/:referenceNumber${HOW_WAS_THE_CONTRACT_AWARDED_SAVE_AND_BACK}`, howWasTheContractAwardedSaveAndBackPost); + exportContractRoute.get(`/:referenceNumber${ABOUT_GOODS_OR_SERVICES}`, aboutGoodsOrServicesGet); exportContractRoute.post(`/:referenceNumber${ABOUT_GOODS_OR_SERVICES}`, aboutGoodsOrServicesPost); exportContractRoute.post(`/:referenceNumber${ABOUT_GOODS_OR_SERVICES_SAVE_AND_BACK}`, aboutGoodsOrServicesSaveAndBackPost); diff --git a/src/ui/server/routes/insurance/index.test.ts b/src/ui/server/routes/insurance/index.test.ts index 9c4de85cce..679701c48b 100644 --- a/src/ui/server/routes/insurance/index.test.ts +++ b/src/ui/server/routes/insurance/index.test.ts @@ -22,7 +22,7 @@ describe('routes/insurance', () => { it('should setup all routes', () => { expect(get).toHaveBeenCalledTimes(207); - expect(post).toHaveBeenCalledTimes(211); + expect(post).toHaveBeenCalledTimes(212); expect(get).toHaveBeenCalledWith(INSURANCE_ROUTES.START, startGet);