From 51c6957bfd38b62d1f9c0e9b305d8d1c3eb23b88 Mon Sep 17 00:00:00 2001 From: Zain Kassam Date: Tue, 21 May 2024 10:03:11 +0100 Subject: [PATCH 1/5] feat(EMS-895): removed abandoned application from dashboard and restricted accessing application --- .github/workflows/infrastructure.yml | 2 + e2e-tests/commands/api/index.js | 30 ++++ .../create-an-abandoned-application.js | 43 +++++ ...shboard-pagination-15-applications.spec.js | 9 +- ...no-access-to-application-abandoned.spec.js | 41 +++++ .../cypress/support/application/index.js | 1 + src/api/.keystone/config.js | 93 +++++++++-- .../allowed-graphql-resolvers/index.ts | 1 + src/api/custom-resolvers/index.ts | 2 + .../index.test.ts | 79 +++++++++ .../create-an-abandoned-application/index.ts | 57 +++++++ .../create-an-application/index.test.ts | 43 +---- .../mutations/create-an-application/index.ts | 145 +++-------------- src/api/custom-resolvers/mutations/index.ts | 2 + src/api/custom-schema/type-defs.ts | 15 ++ .../create-an-application/index.test.ts | 105 ++++++++++++ .../helpers/create-an-application/index.ts | 154 ++++++++++++++++++ src/api/schema.graphql | 10 ++ src/api/types/application-types/index.ts | 1 + src/ui/.eslintrc.js | 17 +- src/ui/server/graphql/queries/applications.ts | 9 +- .../application-status/index.test.ts | 34 +++- .../insurance/application-status/index.ts | 17 +- 23 files changed, 718 insertions(+), 192 deletions(-) create mode 100644 e2e-tests/commands/insurance/create-an-abandoned-application.js create mode 100644 e2e-tests/insurance/cypress/e2e/journeys/no-access-to-application/no-access-to-application-abandoned.spec.js create mode 100644 src/api/custom-resolvers/mutations/create-an-abandoned-application/index.test.ts create mode 100644 src/api/custom-resolvers/mutations/create-an-abandoned-application/index.ts create mode 100644 src/api/helpers/create-an-application/index.test.ts create mode 100644 src/api/helpers/create-an-application/index.ts diff --git a/.github/workflows/infrastructure.yml b/.github/workflows/infrastructure.yml index 943ddc54f1..3d7a7dc9cb 100644 --- a/.github/workflows/infrastructure.yml +++ b/.github/workflows/infrastructure.yml @@ -649,6 +649,8 @@ jobs: JWT_SIGNING_KEY='${{ secrets.JWT_SIGNING_KEY }}' \ UNDERWRITING_TEAM_EMAIL='${{ secrets.UNDERWRITING_TEAM_EMAIL }}' \ FEEDBACK_EMAIL_RECIPIENT='${{ secrets.FEEDBACK_EMAIL_RECIPIENT }}' + CRON_SCHEDULE_UNVERIFIED_ACCOUNT='${{ secrets.CRON_SCHEDULE_UNVERIFIED_ACCOUNT }}' + CRON_SCHEDULE_INACTIVE_APPLICATION='${{ secrets.CRON_SCHEDULE_INACTIVE_APPLICATION }}' - name: Extension ➕ uses: azure/cli@v1.0.9 diff --git a/e2e-tests/commands/api/index.js b/e2e-tests/commands/api/index.js index da134d3729..1e7385b2d0 100644 --- a/e2e-tests/commands/api/index.js +++ b/e2e-tests/commands/api/index.js @@ -34,6 +34,14 @@ const queryStrings = { } } `, + createAnAbandonedApplication: () => gql` + mutation createAnAbandonedApplication($accountId: String!, $eligibilityAnswers: ApplicationEligibility!, $company: CompanyInput!, $sectionReview: SectionReviewInput!) { + createAnAbandonedApplication(accountId: $accountId, eligibilityAnswers: $eligibilityAnswers, company: $company, sectionReview: $sectionReview) { + referenceNumber + status + } + } +`, createApplications: () => gql` mutation createApplications($data: [ApplicationCreateInput!]!) { createApplications(data: $data) { @@ -236,6 +244,27 @@ const createAnApplication = (accountId, eligibilityAnswers, company, sectionRevi context: APOLLO_CONTEXT, }).then((response) => response.data.createAnApplication); +/** + * createAnApplication + * Create an application + * @param {String} Account/application owner ID + * @param {Object} Eligibility answers + * @param {Object} Company object (obtained from eligibility companies house call) + * @param {Object} sectionReview object (with eligibility set to true) + * @returns {Object} Created application + */ +const createAnAbandonedApplication = (accountId, eligibilityAnswers, company, sectionReview) => + apollo.query({ + query: queryStrings.createAnAbandonedApplication(), + variables: { + accountId, + eligibilityAnswers, + company, + sectionReview, + }, + context: APOLLO_CONTEXT, + }).then((response) => response.data.createAnAbandonedApplication); + /** * createApplications * Create multiple applications @@ -559,6 +588,7 @@ const api = { createAnAccount, createBuyer, createAnApplication, + createAnAbandonedApplication, createApplications, getAccountByEmail, updateAccount, diff --git a/e2e-tests/commands/insurance/create-an-abandoned-application.js b/e2e-tests/commands/insurance/create-an-abandoned-application.js new file mode 100644 index 0000000000..27d7b4f0fb --- /dev/null +++ b/e2e-tests/commands/insurance/create-an-abandoned-application.js @@ -0,0 +1,43 @@ +import api from '../api'; +import mockApplication from '../../fixtures/application'; + +const { ELIGIBILITY: mockEligibilityAnswers, COMPANY: mockCompany } = mockApplication; + +/** + * createAnAbandonedApplication + * Create an abandoned application directly via the API + * 1) Create company from mock company + * 2) Create section review + * 3) Call API to create abandoned application + * @param {String} Account ID for the application owner + * @returns {Object} Created application + */ +const createAnAbandonedApplication = (accountId) => { + if (accountId) { + try { + /** + * Create initial company object from mockCompany/fixtures. + */ + const { financialYearEndDate, ...companyFields } = mockCompany; + const company = companyFields; + + /** + * pass sectionReview for eligibility to API + * has to be set to true for eligibility + */ + const sectionReview = { + eligibility: true, + }; + + return api.createAnAbandonedApplication(accountId, mockEligibilityAnswers, company, sectionReview).then((application) => application); + } catch (err) { + console.error('Creating an application', err); + + return err; + } + } + + return null; +}; + +export default createAnAbandonedApplication; diff --git a/e2e-tests/insurance/cypress/e2e/journeys/dashboard/dashboard-pagination/dashboard-pagination-15-applications.spec.js b/e2e-tests/insurance/cypress/e2e/journeys/dashboard/dashboard-pagination/dashboard-pagination-15-applications.spec.js index e38206b3ae..97061e5508 100644 --- a/e2e-tests/insurance/cypress/e2e/journeys/dashboard/dashboard-pagination/dashboard-pagination-15-applications.spec.js +++ b/e2e-tests/insurance/cypress/e2e/journeys/dashboard/dashboard-pagination/dashboard-pagination-15-applications.spec.js @@ -11,6 +11,7 @@ const dashboardUrl = `${baseUrl}${DASHBOARD}`; context(`Insurance - Dashboard - pagination - ${totalApplications} applications`, () => { let applications; + let abandonedApplication; before(() => { cy.completeSignInAndGoToDashboard().then(({ accountId }) => { @@ -18,6 +19,11 @@ context(`Insurance - Dashboard - pagination - ${totalApplications} applications` applications = createdApplications; }); + // creates an abandoned application + cy.createAnAbandonedApplication(accountId).then((createdApplication) => { + abandonedApplication = createdApplication; + }); + cy.navigateToUrl(dashboardUrl); }); }); @@ -28,9 +34,10 @@ context(`Insurance - Dashboard - pagination - ${totalApplications} applications` after(() => { cy.deleteApplications(applications); + cy.deleteApplication(abandonedApplication.referenceNumber); }); - it(`should NOT render pagination list items because there are no more than ${MAX_APPLICATIONS_PER_PAGE} applications`, () => { + it(`should NOT render pagination list items because there are no more than ${MAX_APPLICATIONS_PER_PAGE} applications (which are not Abandoned)`, () => { cy.assertPaginationDoesNotExist(); }); }); diff --git a/e2e-tests/insurance/cypress/e2e/journeys/no-access-to-application/no-access-to-application-abandoned.spec.js b/e2e-tests/insurance/cypress/e2e/journeys/no-access-to-application/no-access-to-application-abandoned.spec.js new file mode 100644 index 0000000000..c876be2361 --- /dev/null +++ b/e2e-tests/insurance/cypress/e2e/journeys/no-access-to-application/no-access-to-application-abandoned.spec.js @@ -0,0 +1,41 @@ +import { INSURANCE_ROUTES } from '../../../../../constants/routes/insurance'; + +const { + ROOT, + ALL_SECTIONS, + NO_ACCESS_TO_APPLICATION, +} = INSURANCE_ROUTES; + +const baseUrl = Cypress.config('baseUrl'); + +context('Insurance - no access to application page - Abandoned application', () => { + let refNumber; + let applicationUrl; + + before(() => { + cy.saveSession(); + + cy.completeSignInAndGoToDashboard().then(({ accountId }) => { + cy.createAnAbandonedApplication(accountId).then((createdApplication) => { + applicationUrl = `${baseUrl}${ROOT}/${createdApplication.referenceNumber}${ALL_SECTIONS}`; + refNumber = createdApplication.referenceNumber; + }); + }); + }); + + after(() => { + cy.deleteApplication(refNumber); + }); + + describe('when trying to access an Abandoned application', () => { + beforeEach(() => { + cy.navigateToUrl(applicationUrl); + }); + + it(`should redirect to ${NO_ACCESS_TO_APPLICATION}`, () => { + const expectedUrl = `${baseUrl}${NO_ACCESS_TO_APPLICATION}`; + + cy.assertUrl(expectedUrl); + }); + }); +}); diff --git a/e2e-tests/insurance/cypress/support/application/index.js b/e2e-tests/insurance/cypress/support/application/index.js index ade0af61f6..2e2af30542 100644 --- a/e2e-tests/insurance/cypress/support/application/index.js +++ b/e2e-tests/insurance/cypress/support/application/index.js @@ -6,6 +6,7 @@ import './policy'; Cypress.Commands.add('createAnApplication', require('../../../../commands/insurance/create-an-application')); Cypress.Commands.add('createApplications', require('../../../../commands/insurance/create-applications')); +Cypress.Commands.add('createAnAbandonedApplication', require('../../../../commands/insurance/create-an-abandoned-application')); Cypress.Commands.add('deleteApplication', require('../../../../commands/insurance/delete-application')); Cypress.Commands.add('deleteApplications', require('../../../../commands/insurance/delete-applications')); diff --git a/src/api/.keystone/config.js b/src/api/.keystone/config.js index 1f25679552..3be3f61ca2 100644 --- a/src/api/.keystone/config.js +++ b/src/api/.keystone/config.js @@ -586,6 +586,7 @@ if (isDevEnvironment) { "accounts", "addAndGetOTP", "createApplications", + "createAnAbandonedApplication", "createBuyer", "deleteAnAccount", "deleteApplications", @@ -2374,6 +2375,13 @@ var typeDefs = ` referenceNumber: Int } + type CreateAnAbandonedApplicationResponse { + success: Boolean! + id: String + referenceNumber: Int + status: String + } + type MappedCisCountry { isoCode: String! name: String @@ -2481,6 +2489,14 @@ var typeDefs = ` sectionReview: SectionReviewInput! ): CreateAnApplicationResponse + """ create an application """ + createAnAbandonedApplication( + accountId: String! + eligibilityAnswers: ApplicationEligibility! + company: CompanyInput! + sectionReview: SectionReviewInput! + ): CreateAnAbandonedApplicationResponse + """ delete an account """ deleteAnAccount( email: String! @@ -4529,24 +4545,26 @@ var createASectionReview = async (context, applicationId, sectionReviewData) => }; var create_a_section_review_default = createASectionReview; -// custom-resolvers/mutations/create-an-application/index.ts +// helpers/create-an-application/index.ts +var { SUBMISSION_TYPE: SUBMISSION_TYPE2 } = APPLICATION; var createAnApplication = async (root, variables, context) => { - console.info("Creating application for ", variables.accountId); + console.info("Creating an application (createAnApplication helper)"); try { - const { accountId, eligibilityAnswers, company: companyData, sectionReview: sectionReviewData } = variables; + const { accountId, eligibilityAnswers, company: companyData, sectionReview: sectionReviewData, status } = variables; const account2 = await get_account_by_id_default(context, accountId); if (!account2) { - return { - success: false - }; + return null; } const { buyerCountryIsoCode, needPreCreditPeriodCover, totalContractValueId, coverPeriodId, ...otherEligibilityAnswers } = eligibilityAnswers; const country = await get_country_by_field_default(context, "isoCode", buyerCountryIsoCode); + const submissionType = SUBMISSION_TYPE2.MIA; const application2 = await context.db.Application.createOne({ data: { owner: { connect: { id: accountId } - } + }, + status, + submissionType } }); const { id: applicationId } = application2; @@ -4587,16 +4605,68 @@ var createAnApplication = async (root, variables, context) => { } } }); + return updatedApplication; + } catch (err) { + console.error("Error creating an application (createAnApplication helper) %O", err); + throw new Error(`Creating an application (createAnApplication helper) ${err}`); + } +}; +var create_an_application_default = createAnApplication; + +// custom-resolvers/mutations/create-an-application/index.ts +var { STATUS: STATUS2 } = APPLICATION; +var createAnApplication2 = async (root, variables, context) => { + console.info("Creating application for ", variables.accountId); + const updatedVariables = variables; + updatedVariables.status = STATUS2.IN_PROGRESS; + try { + const updatedApplication = await create_an_application_default(root, updatedVariables, context); + if (updatedApplication) { + return { + ...updatedApplication, + success: true + }; + } return { - ...updatedApplication, - success: true + success: false }; } catch (err) { console.error("Error creating application %O", err); throw new Error(`Creating application ${err}`); } }; -var create_an_application_default = createAnApplication; +var create_an_application_default2 = createAnApplication2; + +// custom-resolvers/mutations/create-an-abandoned-application/index.ts +var { STATUS: STATUS3 } = APPLICATION; +var createAnAbandonedApplication = async (root, variables, context) => { + const abandonedApplicationVariables = variables; + abandonedApplicationVariables.status = STATUS3.ABANDONED; + try { + const createdApplication = await create_an_application_default(root, abandonedApplicationVariables, context); + if (createdApplication) { + const updatedApplication = await context.db.Application.updateOne({ + where: { + id: createdApplication.id + }, + data: { + status: STATUS3.ABANDONED + } + }); + return { + ...updatedApplication, + success: true + }; + } + return { + success: false + }; + } catch (err) { + console.error("Error creating an abandoned application %O", err); + throw new Error(`Creating an abandoned application ${err}`); + } +}; +var create_an_abandoned_application_default = createAnAbandonedApplication; // helpers/get-application-by-reference-number/index.ts var getApplicationByReferenceNumber = async (referenceNumber, context) => { @@ -7102,7 +7172,8 @@ var customResolvers = { accountPasswordReset: account_password_reset_default, sendEmailPasswordResetLink: send_email_password_reset_link_default, sendEmailReactivateAccountLink: send_email_reactivate_account_link_default, - createAnApplication: create_an_application_default, + createAnApplication: create_an_application_default2, + createAnAbandonedApplication: create_an_abandoned_application_default, deleteApplicationByReferenceNumber: delete_application_by_reference_number_default, submitApplication: submit_application_default, createFeedbackAndSendEmail: create_feedback_default, diff --git a/src/api/constants/allowed-graphql-resolvers/index.ts b/src/api/constants/allowed-graphql-resolvers/index.ts index e166527f3a..4c04d28324 100644 --- a/src/api/constants/allowed-graphql-resolvers/index.ts +++ b/src/api/constants/allowed-graphql-resolvers/index.ts @@ -87,6 +87,7 @@ if (isDevEnvironment) { 'accounts', 'addAndGetOTP', 'createApplications', + 'createAnAbandonedApplication', 'createBuyer', 'deleteAnAccount', 'deleteApplications', diff --git a/src/api/custom-resolvers/index.ts b/src/api/custom-resolvers/index.ts index 14c7ce3ec4..f199ea188c 100644 --- a/src/api/custom-resolvers/index.ts +++ b/src/api/custom-resolvers/index.ts @@ -11,6 +11,7 @@ import { sendEmailPasswordResetLink, sendEmailReactivateAccountLink, createAnApplication, + createAnAbandonedApplication, deleteApplicationByReferenceNumber, submitApplication, createFeedbackAndSendEmail, @@ -49,6 +50,7 @@ const customResolvers = { sendEmailPasswordResetLink, sendEmailReactivateAccountLink, createAnApplication, + createAnAbandonedApplication, deleteApplicationByReferenceNumber, submitApplication, createFeedbackAndSendEmail, diff --git a/src/api/custom-resolvers/mutations/create-an-abandoned-application/index.test.ts b/src/api/custom-resolvers/mutations/create-an-abandoned-application/index.test.ts new file mode 100644 index 0000000000..0dfce0aad5 --- /dev/null +++ b/src/api/custom-resolvers/mutations/create-an-abandoned-application/index.test.ts @@ -0,0 +1,79 @@ +import createAnAbandonedApplication from '.'; +import { mockAccount, mockCountries } from '../../../test-mocks'; +import mockCompany from '../../../test-mocks/mock-company'; +import { Account, Context, SuccessResponse } from '../../../types'; +import getKeystoneContext from '../../../test-helpers/get-keystone-context'; +import accounts from '../../../test-helpers/accounts'; +import { APPLICATION } from '../../../constants'; + +const { STATUS, SUBMISSION_TYPE } = APPLICATION; + + +describe('custom-resolvers/create-an-abandoned-application', () => { + let context: Context; + let account: Account; + let result: SuccessResponse; + + const { status, ...mockAccountUpdate } = mockAccount; + + const variables = { + accountId: '', + eligibilityAnswers: { + buyerCountryIsoCode: mockCountries[0].isoCode, + hasCompaniesHouseNumber: true, + }, + company: mockCompany, + sectionReview: { + eligibility: true, + }, + }; + + beforeAll(async () => { + context = getKeystoneContext(); + + account = await accounts.create({ context, data: mockAccountUpdate }); + + variables.accountId = account.id; + }); + + test('it should return success=true', async () => { + result = await createAnAbandonedApplication({}, variables, context); + + expect(result.success).toEqual(true); + }); + + test(`it should return status as ${STATUS.ABANDONED}`, async () => { + result = await createAnAbandonedApplication({}, variables, context); + + expect(result.status).toEqual(STATUS.ABANDONED); + }); + + test(`it should return submissionType as ${SUBMISSION_TYPE.MIA}`, async () => { + result = await createAnAbandonedApplication({}, variables, context); + + expect(result.submissionType).toEqual(SUBMISSION_TYPE.MIA); + }); + + describe('when there is no account for the provided accountId', () => { + test('it should return success=false', async () => { + variables.accountId = 'invalid-id'; + + result = await createAnAbandonedApplication({}, variables, context); + + expect(result.success).toEqual(false); + }); + }); + + describe('when creation is not successful', () => { + test('it should throw an error', async () => { + try { + // pass empty context object to force an error + await createAnAbandonedApplication({}, variables, {}); + } catch (err) { + const errorString = String(err); + + expect(errorString.includes('Creating an abandoned application')).toEqual(true); + } + }); + }); +}); diff --git a/src/api/custom-resolvers/mutations/create-an-abandoned-application/index.ts b/src/api/custom-resolvers/mutations/create-an-abandoned-application/index.ts new file mode 100644 index 0000000000..e619aadf2c --- /dev/null +++ b/src/api/custom-resolvers/mutations/create-an-abandoned-application/index.ts @@ -0,0 +1,57 @@ +import { APPLICATION } from '../../../constants'; +import createAnApplicationHelper from '../../../helpers/create-an-application'; +import { CreateAnApplicationVariables, Context } from '../../../types'; + +const { STATUS } = APPLICATION; + +/** + * createAnAbandonedApplication + * Create an abandoned application. + * 1) Set status to Abandoned + * 2) Create a new application with createAnApplicationHelper. + * 3) Updates application with abandoned status. + * 4) Returns application and success flag + * @param {Object} GraphQL root variables + * @param {CreateAnApplicationVariables} GraphQL variables for the CreateAnAbandonedApplication mutation + * @param {Context} KeystoneJS context API + * @returns {Promise} Object with success flag and application + */ +const createAnAbandonedApplication = async (root: any, variables: CreateAnApplicationVariables, context: Context) => { + const abandonedApplicationVariables = variables; + + // set status to abandoned + abandonedApplicationVariables.status = STATUS.ABANDONED; + + try{ + // creates and returns application + const createdApplication = await createAnApplicationHelper(root, abandonedApplicationVariables, context); + + if (createdApplication) { + // TODO: remove once schema hooks for status is removed + // updates application status to abandoned + const updatedApplication = await context.db.Application.updateOne({ + where: { + id: createdApplication.id, + }, + data: { + status: STATUS.ABANDONED, + }, + }); + + return { + ...updatedApplication, + success: true, + }; + } + + return { + success: false, + } + } catch (err) { + console.error('Error creating an abandoned application %O', err); + + throw new Error(`Creating an abandoned application ${err}`); + } +}; + +export default createAnAbandonedApplication; diff --git a/src/api/custom-resolvers/mutations/create-an-application/index.test.ts b/src/api/custom-resolvers/mutations/create-an-application/index.test.ts index d61698e364..bb43f7d8db 100644 --- a/src/api/custom-resolvers/mutations/create-an-application/index.test.ts +++ b/src/api/custom-resolvers/mutations/create-an-application/index.test.ts @@ -4,7 +4,10 @@ import mockCompany from '../../../test-mocks/mock-company'; import { Account, Context, SuccessResponse } from '../../../types'; import getKeystoneContext from '../../../test-helpers/get-keystone-context'; import accounts from '../../../test-helpers/accounts'; -import applications from '../../../test-helpers/applications'; +import { APPLICATION } from '../../../constants'; + +const { STATUS, SUBMISSION_TYPE } = APPLICATION; + describe('custom-resolvers/create-an-application', () => { let context: Context; @@ -39,46 +42,16 @@ describe('custom-resolvers/create-an-application', () => { expect(result.success).toEqual(true); }); - test('it should return an ID', async () => { + test(`it should return status as ${STATUS.IN_PROGRESS}`, async () => { result = await createAnApplication({}, variables, context); - const createdApplication = await applications.get({ context, applicationId: result.id }); - - expect(result.id).toEqual(createdApplication.id); - }); - - test('it should return a reference number', async () => { - result = await createAnApplication({}, variables, context); - - const createdApplication = await applications.get({ context, applicationId: result.id }); - - const expected = createdApplication.referenceNumber; - - expect(result.referenceNumber).toEqual(expected); + expect(result.status).toEqual(STATUS.IN_PROGRESS); }); - test('it should return application relationships', async () => { + test(`it should return submissionType as ${SUBMISSION_TYPE.MIA}`, async () => { result = await createAnApplication({}, variables, context); - const application = await applications.get({ context, applicationId: result.id }); - - const expected = { - buyerId: application.buyer.id, - companyId: application.company.id, - eligibilityId: application.eligibility.id, - exportContractId: application.exportContract.id, - nominatedLossPayeeId: application.nominatedLossPayee.id, - policyId: application.policy.id, - sectionReviewId: application.sectionReview.id, - }; - - expect(result.buyerId).toEqual(expected.buyerId); - expect(result.companyId).toEqual(expected.companyId); - expect(result.eligibilityId).toEqual(expected.eligibilityId); - expect(result.exportContractId).toEqual(expected.exportContractId); - expect(result.nominatedLossPayeeId).toEqual(expected.nominatedLossPayeeId); - expect(result.policyId).toEqual(expected.policyId); - expect(result.sectionReviewId).toEqual(expected.sectionReviewId); + expect(result.submissionType).toEqual(SUBMISSION_TYPE.MIA); }); describe('when there is no account for the provided accountId', () => { diff --git a/src/api/custom-resolvers/mutations/create-an-application/index.ts b/src/api/custom-resolvers/mutations/create-an-application/index.ts index 4de84b22f7..716afb3fe4 100644 --- a/src/api/custom-resolvers/mutations/create-an-application/index.ts +++ b/src/api/custom-resolvers/mutations/create-an-application/index.ts @@ -1,143 +1,42 @@ -import getAccountById from '../../../helpers/get-account-by-id'; -import getCountryByField from '../../../helpers/get-country-by-field'; -import getCreditPeriodValueByField from '../../../helpers/get-cover-period-value-by-field'; -import getTotalContractValueByField from '../../../helpers/get-total-contract-value-by-field'; -import createAnEligibility from '../../../helpers/create-an-eligibility'; -import createABuyer from '../../../helpers/create-a-buyer'; -import createAPolicy from '../../../helpers/create-a-policy'; -import createANominatedLossPayee from '../../../helpers/create-a-nominated-loss-payee'; -import createACompany from '../../../helpers/create-a-company'; -import createAnExportContract from '../../../helpers/create-an-export-contract'; -import createASectionReview from '../../../helpers/create-a-section-review'; +import createAnApplicationHelper from '../../../helpers/create-an-application'; +import { APPLICATION } from '../../../constants'; import { CreateAnApplicationVariables, Context } from '../../../types'; +const { STATUS } = APPLICATION; + + /** * createAnApplication * Create an application. - * 1) Get a country from a provided isoCode. - * 2) Create a new application with owner relationship. - * 3) Create eligibility relationship with the country and application. - * 4) Create buyer relationship with the country and application. - * 5) Update the application with buyer and eligibility IDs. + * 1) Set status to In progress + * 2) Create a new application with createAnApplicationHelper. + * 3) Returns success flag and application * @param {Object} GraphQL root variables - * @param {Object} GraphQL variables for the CreateAnApplication mutation - * @param {Object} KeystoneJS context API - * @returns {Promise} Object with success flag + * @param {CreateAnApplicationVariables} GraphQL variables for the createAnApplication mutation + * @param {Context} KeystoneJS context API + * @returns {Promise} Object with success flag and application */ const createAnApplication = async (root: any, variables: CreateAnApplicationVariables, context: Context) => { console.info('Creating application for ', variables.accountId); - try { - const { accountId, eligibilityAnswers, company: companyData, sectionReview: sectionReviewData } = variables; + const updatedVariables = variables; + + // set status to in progress + updatedVariables.status = STATUS.IN_PROGRESS; - /** - * Check the account exists. - * If not, reject. - */ - const account = await getAccountById(context, accountId); + try{ + const updatedApplication = await createAnApplicationHelper(root, updatedVariables, context); - if (!account) { + if (updatedApplication) { return { - success: false, + ...updatedApplication, + success: true, }; } - /** - * Get a country's ID from the provided ISO code. - * This is required to be used in: - * 1) Eligibility buyer country relationship - * 2) Buyer country relationship - */ - const { buyerCountryIsoCode, needPreCreditPeriodCover, totalContractValueId, coverPeriodId, ...otherEligibilityAnswers } = eligibilityAnswers; - - const country = await getCountryByField(context, 'isoCode', buyerCountryIsoCode); - - /** - * Create the initial application. - * We need to do this first so that we can use the application ID. - */ - const application = await context.db.Application.createOne({ - data: { - owner: { - connect: { id: accountId }, - }, - }, - }); - - const { id: applicationId } = application; - - /** - * 1) Create a new buyer with country and application relationship. - * 2) Get a totalContractValue DB entry, for linking a relationship to eligibility. - * 3) Create a cover period value from the DB. - * 4) Create a new eligibility with country and application relationship. - * 5) Create a new export contract with application relationship. - * 6) Create a new policy with application relationship. - * 7) Create a new nominated loss payee with application relationship. - * 8) Create a new company with application relationship. - * 9) Create a new sectionReview with application relationship - */ - const { buyer } = await createABuyer(context, country.id, applicationId); - - const totalContractValue = await getTotalContractValueByField(context, 'valueId', totalContractValueId); - - const coverPeriod = await getCreditPeriodValueByField(context, 'valueId', coverPeriodId); - - const eligibility = await createAnEligibility(context, country.id, applicationId, coverPeriod.id, totalContractValue.id, otherEligibilityAnswers); - - const { exportContract } = await createAnExportContract(context, applicationId); - - const { policy } = await createAPolicy(context, applicationId); - - const nominatedLossPayee = await createANominatedLossPayee(context, applicationId); - - const company = await createACompany(context, applicationId, companyData); - - const sectionReview = await createASectionReview(context, applicationId, sectionReviewData); - - /** - * Update the application with relationships for: - * 1) Buyer - * 2) Company - * 3) Eligibility - * 4) Export contract - * 5) Nominated loss payee - * 6) Policy - * 7) Section review - */ - const updatedApplication = await context.db.Application.updateOne({ - where: { - id: applicationId, - }, - data: { - buyer: { - connect: { id: buyer.id }, - }, - company: { - connect: { id: company.id }, - }, - eligibility: { - connect: { id: eligibility.id }, - }, - exportContract: { - connect: { id: exportContract.id }, - }, - nominatedLossPayee: { - connect: { id: nominatedLossPayee.id }, - }, - policy: { - connect: { id: policy.id }, - }, - sectionReview: { - connect: { id: sectionReview.id }, - }, - }, - }); - return { - ...updatedApplication, - success: true, - }; + success: false, + } } catch (err) { console.error('Error creating application %O', err); diff --git a/src/api/custom-resolvers/mutations/index.ts b/src/api/custom-resolvers/mutations/index.ts index 1b51a30fa0..3db407330f 100644 --- a/src/api/custom-resolvers/mutations/index.ts +++ b/src/api/custom-resolvers/mutations/index.ts @@ -10,6 +10,7 @@ import sendEmailPasswordResetLink from './send-email-password-reset-link'; import accountPasswordReset from './account-password-reset'; import sendEmailReactivateAccountLink from './send-email-reactivate-account-link'; import createAnApplication from './create-an-application'; +import createAnAbandonedApplication from './create-an-abandoned-application'; import deleteApplicationByReferenceNumber from './delete-application-by-reference-number'; import submitApplication from './submit-application'; import createFeedbackAndSendEmail from './create-feedback'; @@ -30,6 +31,7 @@ export { accountPasswordReset, sendEmailReactivateAccountLink, createAnApplication, + createAnAbandonedApplication, deleteApplicationByReferenceNumber, submitApplication, createFeedbackAndSendEmail, diff --git a/src/api/custom-schema/type-defs.ts b/src/api/custom-schema/type-defs.ts index 438ca47dab..864b9407c6 100644 --- a/src/api/custom-schema/type-defs.ts +++ b/src/api/custom-schema/type-defs.ts @@ -205,6 +205,13 @@ const typeDefs = ` referenceNumber: Int } + type CreateAnAbandonedApplicationResponse { + success: Boolean! + id: String + referenceNumber: Int + status: String + } + type MappedCisCountry { isoCode: String! name: String @@ -312,6 +319,14 @@ const typeDefs = ` sectionReview: SectionReviewInput! ): CreateAnApplicationResponse + """ create an application """ + createAnAbandonedApplication( + accountId: String! + eligibilityAnswers: ApplicationEligibility! + company: CompanyInput! + sectionReview: SectionReviewInput! + ): CreateAnAbandonedApplicationResponse + """ delete an account """ deleteAnAccount( email: String! diff --git a/src/api/helpers/create-an-application/index.test.ts b/src/api/helpers/create-an-application/index.test.ts new file mode 100644 index 0000000000..abba813fee --- /dev/null +++ b/src/api/helpers/create-an-application/index.test.ts @@ -0,0 +1,105 @@ +import createAnApplication from '.'; +import getKeystoneContext from '../../test-helpers/get-keystone-context'; +import applications from '../../test-helpers/applications'; +import accounts from '../../test-helpers/accounts'; +import { mockAccount, mockCountries } from '../../test-mocks'; +import mockCompany from '../../test-mocks/mock-company'; +import { APPLICATION } from '../../constants'; +import { Context, Account } from '../../types'; + +const { STATUS, SUBMISSION_TYPE } = APPLICATION; + + +describe('helpers/create-an-application', () => { + let context: Context; + let account: Account; + + const { status, ...mockAccountUpdate } = mockAccount; + + const variables = { + accountId: '', + eligibilityAnswers: { + buyerCountryIsoCode: mockCountries[0].isoCode, + hasCompaniesHouseNumber: true, + }, + company: mockCompany, + sectionReview: { + eligibility: true, + }, + status: STATUS.IN_PROGRESS, + submissionType: SUBMISSION_TYPE.MIA, + }; + + beforeAll(async () => { + context = getKeystoneContext(); + + account = await accounts.create({ context, data: mockAccountUpdate }); + + variables.accountId = account.id; + }); + + test('it should return an ID', async () => { + const result = await createAnApplication({}, variables, context); + + const createdApplication = await applications.get({ context, applicationId: result.id }); + + expect(result.id).toEqual(createdApplication.id); + }); + + test('it should return a reference number', async () => { + const result = await createAnApplication({}, variables, context); + + const createdApplication = await applications.get({ context, applicationId: result.id }); + + const expected = createdApplication.referenceNumber; + + expect(result.referenceNumber).toEqual(expected); + }); + + test('it should return application relationships', async () => { + const result = await createAnApplication({}, variables, context); + + const application = await applications.get({ context, applicationId: result.id }); + + const expected = { + buyerId: application.buyer.id, + companyId: application.company.id, + eligibilityId: application.eligibility.id, + exportContractId: application.exportContract.id, + nominatedLossPayeeId: application.nominatedLossPayee.id, + policyId: application.policy.id, + sectionReviewId: application.sectionReview.id, + }; + + expect(result.buyerId).toEqual(expected.buyerId); + expect(result.companyId).toEqual(expected.companyId); + expect(result.eligibilityId).toEqual(expected.eligibilityId); + expect(result.exportContractId).toEqual(expected.exportContractId); + expect(result.nominatedLossPayeeId).toEqual(expected.nominatedLossPayeeId); + expect(result.policyId).toEqual(expected.policyId); + expect(result.sectionReviewId).toEqual(expected.sectionReviewId); + }); + + describe('when there is no account for the provided accountId', () => { + test('it should return null', async () => { + variables.accountId = 'invalid-id'; + + const result = await createAnApplication({}, variables, context); + + expect(result).toBeNull(); + }); + }); + + describe('when creation is not successful', () => { + test('it should throw an error', async () => { + try { + // pass empty context object to force an error + await createAnApplication({}, variables, {}); + } catch (err) { + const errorString = String(err); + + expect(errorString.includes('Creating an application (createAnApplication helper)')).toEqual(true); + } + }); + }); +}); diff --git a/src/api/helpers/create-an-application/index.ts b/src/api/helpers/create-an-application/index.ts new file mode 100644 index 0000000000..1c39281694 --- /dev/null +++ b/src/api/helpers/create-an-application/index.ts @@ -0,0 +1,154 @@ +import getAccountById from '../get-account-by-id'; +import getCountryByField from '../get-country-by-field'; +import getCreditPeriodValueByField from '../get-cover-period-value-by-field'; +import getTotalContractValueByField from '../get-total-contract-value-by-field'; +import createAnEligibility from '../create-an-eligibility'; +import createABuyer from '../create-a-buyer'; +import createAPolicy from '../create-a-policy'; +import createANominatedLossPayee from '../create-a-nominated-loss-payee'; +import createACompany from '../create-a-company'; +import createAnExportContract from '../create-an-export-contract'; +import createASectionReview from '../create-a-section-review'; +import { APPLICATION } from '../../constants'; + +import { CreateAnApplicationVariables, Context } from '../../types'; + +const { SUBMISSION_TYPE } = APPLICATION; + +/** + * createAnApplication + * Create an application helper. + * 1) Get a country from a provided isoCode. + * 2) Create a new application with owner relationship. + * 3) Create eligibility relationship with the country and application. + * 4) Create buyer relationship with the country and application. + * 5) Update the application with buyer and eligibility IDs. + * 6) Updates status and sets submissionType to MIA + * 7) Returns an application or null + * @param {Object} GraphQL root variables + * @param {CreateAnApplicationVariables} GraphQL variables for the CreateAnApplication + * @param {Context} KeystoneJS context API + * @returns {Promise} Created application or null + */ +const createAnApplication = async (root: any, variables: CreateAnApplicationVariables, context: Context) => { + console.info('Creating an application (createAnApplication helper)'); + + try { + const { accountId, eligibilityAnswers, company: companyData, sectionReview: sectionReviewData, status } = variables; + + /** + * Check the account exists. + * If not, return null. + */ + const account = await getAccountById(context, accountId); + + if (!account) { + return null; + } + + /** + * Get a country's ID from the provided ISO code. + * This is required to be used in: + * 1) Eligibility buyer country relationship + * 2) Buyer country relationship + */ + const { buyerCountryIsoCode, needPreCreditPeriodCover, totalContractValueId, coverPeriodId, ...otherEligibilityAnswers } = eligibilityAnswers; + + const country = await getCountryByField(context, 'isoCode', buyerCountryIsoCode); + + // add default submission type + const submissionType = SUBMISSION_TYPE.MIA; + + /** + * Create the initial application. + * We need to do this first so that we can use the application ID. + */ + const application = await context.db.Application.createOne({ + data: { + owner: { + connect: { id: accountId }, + }, + status, + submissionType, + }, + }); + + const { id: applicationId } = application; + + /** + * 1) Create a new buyer with country and application relationship. + * 2) Get a totalContractValue DB entry, for linking a relationship to eligibility. + * 3) Create a cover period value from the DB. + * 4) Create a new eligibility with country and application relationship. + * 5) Create a new export contract with application relationship. + * 6) Create a new policy with application relationship. + * 7) Create a new nominated loss payee with application relationship. + * 8) Create a new company with application relationship. + * 9) Create a new sectionReview with application relationship + */ + const { buyer } = await createABuyer(context, country.id, applicationId); + + const totalContractValue = await getTotalContractValueByField(context, 'valueId', totalContractValueId); + + const coverPeriod = await getCreditPeriodValueByField(context, 'valueId', coverPeriodId); + + const eligibility = await createAnEligibility(context, country.id, applicationId, coverPeriod.id, totalContractValue.id, otherEligibilityAnswers); + + const { exportContract } = await createAnExportContract(context, applicationId); + + const { policy } = await createAPolicy(context, applicationId); + + const nominatedLossPayee = await createANominatedLossPayee(context, applicationId); + + const company = await createACompany(context, applicationId, companyData); + + const sectionReview = await createASectionReview(context, applicationId, sectionReviewData); + + /** + * Update the application with relationships for: + * 1) Buyer + * 2) Company + * 3) Eligibility + * 4) Export contract + * 5) Nominated loss payee + * 6) Policy + * 7) Section review + */ + const updatedApplication = await context.db.Application.updateOne({ + where: { + id: applicationId, + }, + data: { + buyer: { + connect: { id: buyer.id }, + }, + company: { + connect: { id: company.id }, + }, + eligibility: { + connect: { id: eligibility.id }, + }, + exportContract: { + connect: { id: exportContract.id }, + }, + nominatedLossPayee: { + connect: { id: nominatedLossPayee.id }, + }, + policy: { + connect: { id: policy.id }, + }, + sectionReview: { + connect: { id: sectionReview.id }, + }, + }, + }); + + return updatedApplication; + } catch (err) { + console.error('Error creating an application (createAnApplication helper) %O', err); + + throw new Error(`Creating an application (createAnApplication helper) ${err}`); + } +} + +export default createAnApplication; \ No newline at end of file diff --git a/src/api/schema.graphql b/src/api/schema.graphql index 7e7babdd12..ec13a2827f 100644 --- a/src/api/schema.graphql +++ b/src/api/schema.graphql @@ -3210,6 +3210,9 @@ type Mutation { """ create an application """ createAnApplication(accountId: String!, eligibilityAnswers: ApplicationEligibility!, company: CompanyInput!, sectionReview: SectionReviewInput!): CreateAnApplicationResponse + """ create an application """ + createAnAbandonedApplication(accountId: String!, eligibilityAnswers: ApplicationEligibility!, company: CompanyInput!, sectionReview: SectionReviewInput!): CreateAnAbandonedApplicationResponse + """ delete an account """ deleteAnAccount(email: String!): SuccessResponse @@ -3720,6 +3723,13 @@ type CreateAnApplicationResponse { referenceNumber: Int } +type CreateAnAbandonedApplicationResponse { + success: Boolean! + id: String + referenceNumber: Int + status: String +} + type MappedCisCountry { isoCode: String! name: String diff --git a/src/api/types/application-types/index.ts b/src/api/types/application-types/index.ts index 8da2eb31ca..c72fe0cb79 100644 --- a/src/api/types/application-types/index.ts +++ b/src/api/types/application-types/index.ts @@ -266,6 +266,7 @@ export interface CreateAnApplicationVariables { eligibilityAnswers: ApplicationEligibility; company: ApplicationCompanyCore; sectionReview: SectionReview; + status?: string; } export interface CreateExportContractAgentResponse { diff --git a/src/ui/.eslintrc.js b/src/ui/.eslintrc.js index c43567cd64..78cf59726b 100644 --- a/src/ui/.eslintrc.js +++ b/src/ui/.eslintrc.js @@ -1,13 +1,6 @@ module.exports = { parser: '@typescript-eslint/parser', - extends: [ - 'eslint:recommended', - 'plugin:import/recommended', - 'plugin:import/typescript', - 'plugin:@typescript-eslint/recommended', - 'airbnb', - 'prettier/prettier', - ], + extends: ['eslint:recommended', 'plugin:import/recommended', 'plugin:import/typescript', 'plugin:@typescript-eslint/recommended', 'airbnb', 'prettier'], plugins: ['@typescript-eslint', 'import', 'prettier'], parserOptions: { @@ -32,14 +25,6 @@ module.exports = { rules: { 'import/no-unresolved': [2, { caseSensitive: false }], '@typescript-eslint/indent': ['error', 2], - 'prettier/prettier': [ - 'error', - { - printWidth: 160, - endOfLine: 'auto', - parser: 'typescript', - }, - ], 'max-len': [ 'error', 160, diff --git a/src/ui/server/graphql/queries/applications.ts b/src/ui/server/graphql/queries/applications.ts index 62e0679930..f56399d1ff 100644 --- a/src/ui/server/graphql/queries/applications.ts +++ b/src/ui/server/graphql/queries/applications.ts @@ -2,7 +2,12 @@ import gql from 'graphql-tag'; const applicationsQuery = gql` query applications($accountId: ID!, $take: Int!, $skip: Int!) { - applications(where: { owner: { id: { equals: $accountId } } }, orderBy: { updatedAt: desc }, take: $take, skip: $skip) { + applications( + where: { AND: [{ owner: { id: { equals: $accountId } } }, { status: { not: { equals: "Abandoned" } } }] } + orderBy: { updatedAt: desc } + take: $take + skip: $skip + ) { status referenceNumber buyer { @@ -18,7 +23,7 @@ const applicationsQuery = gql` } submissionDate } - applicationsCount(where: { owner: { id: { equals: $accountId } } }) + applicationsCount(where: { AND: [{ owner: { id: { equals: $accountId } } }, { status: { not: { equals: "Abandoned" } } }] }) } `; diff --git a/src/ui/server/middleware/insurance/application-status/index.test.ts b/src/ui/server/middleware/insurance/application-status/index.test.ts index 570b59d9a2..78e6f929fc 100644 --- a/src/ui/server/middleware/insurance/application-status/index.test.ts +++ b/src/ui/server/middleware/insurance/application-status/index.test.ts @@ -1,13 +1,18 @@ import applicationStatusMiddleware from '.'; -import { ROUTES } from '../../../constants/routes'; +import { INSURANCE_ROUTES } from '../../../constants/routes/insurance'; import { APPLICATION } from '../../../constants'; import POLICY_FIELD_IDS from '../../../constants/field-ids/insurance/policy'; import { mockReq, mockRes, mockApplication, mockAccount, referenceNumber } from '../../../test-mocks'; import { Next, Request, Response } from '../../../../types'; const { - INSURANCE: { INSURANCE_ROOT, NO_ACCESS_APPLICATION_SUBMITTED, APPLICATION_SUBMITTED, CHECK_YOUR_ANSWERS, COMPLETE_OTHER_SECTIONS }, -} = ROUTES; + APPLICATION_SUBMITTED, + NO_ACCESS_TO_APPLICATION, + NO_ACCESS_APPLICATION_SUBMITTED, + INSURANCE_ROOT, + COMPLETE_OTHER_SECTIONS, + CHECK_YOUR_ANSWERS, +} = INSURANCE_ROUTES; const { TYPE_OF_POLICY: { POLICY_TYPE }, @@ -48,6 +53,29 @@ describe('middleware/insurance/application-status', () => { expect(res.redirect).toHaveBeenCalledWith(NO_ACCESS_APPLICATION_SUBMITTED); }); + describe(`when res.locals.application has a status of ${APPLICATION.STATUS.ABANDONED}`, () => { + beforeEach(() => { + req.session.user = { + ...mockAccount, + id: mockApplication.owner.id, + }; + + res.locals.application = { + ...mockApplication, + status: APPLICATION.STATUS.ABANDONED, + id: mockApplication.owner.id, + }; + + next = nextSpy; + }); + + it(`should redirect to ${NO_ACCESS_TO_APPLICATION}`, async () => { + await applicationStatusMiddleware(req, res, next); + + expect(res.redirect).toHaveBeenCalledWith(NO_ACCESS_TO_APPLICATION); + }); + }); + describe(`when the route is ${APPLICATION_SUBMITTED}`, () => { beforeEach(() => { req.session.user = { diff --git a/src/ui/server/middleware/insurance/application-status/index.ts b/src/ui/server/middleware/insurance/application-status/index.ts index 12c3d80756..7bcf36c679 100644 --- a/src/ui/server/middleware/insurance/application-status/index.ts +++ b/src/ui/server/middleware/insurance/application-status/index.ts @@ -4,7 +4,14 @@ import isSubmitYourApplicationRoute from './is-submit-your-application-route'; import canAccessSubmitYourApplicationRoutes from '../../../helpers/can-access-submit-your-application-routes'; import { Next, Request, Response } from '../../../../types'; -const { APPLICATION_SUBMITTED, NO_ACCESS_APPLICATION_SUBMITTED, INSURANCE_ROOT, COMPLETE_OTHER_SECTIONS, PROBLEM_WITH_SERVICE } = INSURANCE_ROUTES; +const { + APPLICATION_SUBMITTED, + NO_ACCESS_TO_APPLICATION, + NO_ACCESS_APPLICATION_SUBMITTED, + INSURANCE_ROOT, + COMPLETE_OTHER_SECTIONS, + PROBLEM_WITH_SERVICE +} = INSURANCE_ROUTES; /** * middleware to check if application is submitted @@ -37,6 +44,14 @@ export const applicationStatusMiddleware = async (req: Request, res: Response, n return res.redirect(NO_ACCESS_APPLICATION_SUBMITTED); } + /** + * If the status is abandoned, + * do not allow the user to view/access the application and redirect to NO_ACCESS_TO_APPLICATION. + */ + if (application.status === APPLICATION.STATUS.ABANDONED) { + return res.redirect(NO_ACCESS_TO_APPLICATION); + } + /** * If the URL is a route in the "submit your application" group ("check your answers" or "declarations") * and required data for these routes is missing, From 48553168d85b1e8c680dc11e3fd56ec2492c6ff9 Mon Sep 17 00:00:00 2001 From: Zain Kassam Date: Tue, 21 May 2024 10:20:51 +0100 Subject: [PATCH 2/5] feat(EMS-895): fix linting issues --- .../business/company-details/index.ts | 12 +++--- .../eligibility/check-if-eligible/index.ts | 4 +- .../controllers/insurance/start/index.ts | 4 +- .../controllers/root/contact-us/index.ts | 18 ++++----- .../helpers/sanitise-environment/index.ts | 6 +-- src/ui/server/helpers/task-list/index.test.ts | 4 +- src/ui/server/helpers/task-list/index.ts | 38 +++++++++---------- .../financial-address/index.ts | 4 +- .../shared-validation/full-address/index.ts | 5 +-- 9 files changed, 39 insertions(+), 56 deletions(-) diff --git a/src/ui/server/controllers/insurance/business/company-details/index.ts b/src/ui/server/controllers/insurance/business/company-details/index.ts index 756568b46e..e3b494cccd 100644 --- a/src/ui/server/controllers/insurance/business/company-details/index.ts +++ b/src/ui/server/controllers/insurance/business/company-details/index.ts @@ -42,13 +42,11 @@ export const FIELD_IDS = [HAS_DIFFERENT_TRADING_NAME, TRADING_ADDRESS, WEBSITE, const IS_APPLICATION_SUMMARY_LIST = true; -const pageVariables = (referenceNumber: number) => { - return { - SAVE_AND_BACK_URL: `${INSURANCE_ROOT}/${referenceNumber}${COMPANY_DETAILS_SAVE_AND_BACK}`, - DIFFERENT_COMPANIES_HOUSE_NUMBER_URL: `${INSURANCE_ROOT}/${referenceNumber}${COMPANY_DETAILS_ROOT}`, - FIELDS: BUSINESS_FIELD_IDS, - }; -}; +const pageVariables = (referenceNumber: number) => ({ + SAVE_AND_BACK_URL: `${INSURANCE_ROOT}/${referenceNumber}${COMPANY_DETAILS_SAVE_AND_BACK}`, + DIFFERENT_COMPANIES_HOUSE_NUMBER_URL: `${INSURANCE_ROOT}/${referenceNumber}${COMPANY_DETAILS_ROOT}`, + FIELDS: BUSINESS_FIELD_IDS, +}); /** * HTML_FLAGS diff --git a/src/ui/server/controllers/insurance/eligibility/check-if-eligible/index.ts b/src/ui/server/controllers/insurance/eligibility/check-if-eligible/index.ts index 64693c5547..32a5f8b53f 100644 --- a/src/ui/server/controllers/insurance/eligibility/check-if-eligible/index.ts +++ b/src/ui/server/controllers/insurance/eligibility/check-if-eligible/index.ts @@ -15,6 +15,4 @@ export const get = (req: Request, res: Response) => userName: getUserNameFromSession(req.session.user), }); -export const post = (req: Request, res: Response) => { - return res.redirect(ROUTES.INSURANCE.ELIGIBILITY.EXPORTER_LOCATION); -}; +export const post = (req: Request, res: Response) => res.redirect(ROUTES.INSURANCE.ELIGIBILITY.EXPORTER_LOCATION); diff --git a/src/ui/server/controllers/insurance/start/index.ts b/src/ui/server/controllers/insurance/start/index.ts index 60df9f5528..9476404088 100644 --- a/src/ui/server/controllers/insurance/start/index.ts +++ b/src/ui/server/controllers/insurance/start/index.ts @@ -22,6 +22,4 @@ export const get = (req: Request, res: Response) => { }); }; -export const post = (req: Request, res: Response) => { - return res.redirect(ROUTES.INSURANCE.ELIGIBILITY.CHECK_IF_ELIGIBLE); -}; +export const post = (req: Request, res: Response) => res.redirect(ROUTES.INSURANCE.ELIGIBILITY.CHECK_IF_ELIGIBLE); diff --git a/src/ui/server/controllers/root/contact-us/index.ts b/src/ui/server/controllers/root/contact-us/index.ts index 80654eea1e..722923ad15 100644 --- a/src/ui/server/controllers/root/contact-us/index.ts +++ b/src/ui/server/controllers/root/contact-us/index.ts @@ -12,13 +12,11 @@ export const TEMPLATE = TEMPLATES.CONTACT_US; * @param {Express.Response} Express response * @returns {Express.Response.render} renders contact us page */ -export const get = (req: Request, res: Response) => { - return res.render(TEMPLATE, { - ...corePageVariables({ - PAGE_CONTENT_STRINGS: PAGES.CONTACT_US_PAGE, - BACK_LINK: req.headers.referer, - ORIGINAL_URL: req.originalUrl, - }), - userName: getUserNameFromSession(req.session.user), - }); -}; +export const get = (req: Request, res: Response) => res.render(TEMPLATE, { + ...corePageVariables({ + PAGE_CONTENT_STRINGS: PAGES.CONTACT_US_PAGE, + BACK_LINK: req.headers.referer, + ORIGINAL_URL: req.originalUrl, + }), + userName: getUserNameFromSession(req.session.user), +}); diff --git a/src/ui/server/helpers/sanitise-environment/index.ts b/src/ui/server/helpers/sanitise-environment/index.ts index e39eec4bdf..b8df5637bb 100644 --- a/src/ui/server/helpers/sanitise-environment/index.ts +++ b/src/ui/server/helpers/sanitise-environment/index.ts @@ -1,4 +1,4 @@ -export const sanitise = (string?: string): string | undefined => { +export const sanitise = (string?: string): string | undefined => /** * Sanitises the input string by replacing occurrences of the sequence '\\n' with a newline character ('\n'). * @@ -13,5 +13,5 @@ export const sanitise = (string?: string): string | undefined => { * // ABC * // CBA */ - return string ? string.replace(/\\n|\\\n|\\\\n/g, '\n') : string; -}; + string ? string.replace(/\\n|\\\n|\\\\n/g, '\n') : string +; diff --git a/src/ui/server/helpers/task-list/index.test.ts b/src/ui/server/helpers/task-list/index.test.ts index 733f44263c..6fe4d59f32 100644 --- a/src/ui/server/helpers/task-list/index.test.ts +++ b/src/ui/server/helpers/task-list/index.test.ts @@ -53,9 +53,7 @@ describe('server/helpers/task-list', () => { const result = generateTaskStatusesAndLinks(mockTaskListData, mockApplicationFlat); const expectedTasks = { - initialChecks: () => { - return group1Tasks.map((task) => mapTask(task, mockApplicationFlat)); - }, + initialChecks: () => group1Tasks.map((task) => mapTask(task, mockApplicationFlat)), prepareApplication: () => { const { 1: tasksListData } = mockTaskListData; const { tasks: group2Tasks } = tasksListData; diff --git a/src/ui/server/helpers/task-list/index.ts b/src/ui/server/helpers/task-list/index.ts index 39ff49f456..d5a95bbe8b 100644 --- a/src/ui/server/helpers/task-list/index.ts +++ b/src/ui/server/helpers/task-list/index.ts @@ -27,12 +27,10 @@ export const mapTask = (task: TaskListDataTask, submittedData: ApplicationFlat) * @returns {Object} Task list groups and tasks with added statuses and links. */ export const generateTaskStatusesAndLinks = (taskListData: TaskListData, submittedData: ApplicationFlat): TaskListData => { - const tasksList = taskListData.map((group) => { - return { - ...group, - tasks: group.tasks.map((task) => mapTask(task, submittedData)), - }; - }) as TaskListData; + const tasksList = taskListData.map((group) => ({ + ...group, + tasks: group.tasks.map((task) => mapTask(task, submittedData)), + })) as TaskListData; return tasksList; }; @@ -42,21 +40,19 @@ export const generateTaskStatusesAndLinks = (taskListData: TaskListData, submitt * @param {Array} taskList Task list groups and tasks * @returns {Array} Array of groups and tasks with only the data required for UI consumption. */ -export const generateSimplifiedTaskList = (taskList: TaskListData): Array => { - return taskList.map( - (group) => - ({ - title: group.title, - hint: group.hint, - tasks: group.tasks.map((task: TaskListDataTask) => ({ - id: task.id, - href: task.href, - status: task.status, - title: task.title, - })), - }) as TaskListGroup, - ); -}; +export const generateSimplifiedTaskList = (taskList: TaskListData): Array => taskList.map( + (group) => + ({ + title: group.title, + hint: group.hint, + tasks: group.tasks.map((task: TaskListDataTask) => ({ + id: task.id, + href: task.href, + status: task.status, + title: task.title, + })), + }) as TaskListGroup, +); /** * generateTaskList diff --git a/src/ui/server/shared-validation/financial-address/index.ts b/src/ui/server/shared-validation/financial-address/index.ts index 65d85dd037..a374f8ead5 100644 --- a/src/ui/server/shared-validation/financial-address/index.ts +++ b/src/ui/server/shared-validation/financial-address/index.ts @@ -14,8 +14,6 @@ const { [FIELD_ID]: ERROR_MESSAGES_OBJECT } = ERROR_MESSAGES.INSURANCE.POLICY; * @param {Object} errors: Other validation errors for the same form * @returns {ValidationErrors} fullAddressValidation */ -const financialAddress = (formBody: RequestBody, errors: object) => { - return fullAddressValidation(formBody, FIELD_ID, ERROR_MESSAGES_OBJECT, errors); -}; +const financialAddress = (formBody: RequestBody, errors: object) => fullAddressValidation(formBody, FIELD_ID, ERROR_MESSAGES_OBJECT, errors); export default financialAddress; diff --git a/src/ui/server/shared-validation/full-address/index.ts b/src/ui/server/shared-validation/full-address/index.ts index 2c71ab38a0..f75d71449c 100644 --- a/src/ui/server/shared-validation/full-address/index.ts +++ b/src/ui/server/shared-validation/full-address/index.ts @@ -14,8 +14,7 @@ import { RequestBody, ErrorMessageObject } from '../../../types'; * @param {Object} errors: Other validation errors for the same form * @returns {ValidationErrors} providedAndMaxLength */ -const fullAddress = (formBody: RequestBody, fieldId: string, errorMessages: ErrorMessageObject, errors: object) => { - return providedAndMaxLength(formBody, fieldId, errorMessages, errors, MAXIMUM_CHARACTERS.FULL_ADDRESS); -}; +const fullAddress = (formBody: RequestBody, fieldId: string, errorMessages: ErrorMessageObject, errors: object) => + providedAndMaxLength(formBody, fieldId, errorMessages, errors, MAXIMUM_CHARACTERS.FULL_ADDRESS); export default fullAddress; From 5b05d50606782716a2ccd361a629f83e54c57039 Mon Sep 17 00:00:00 2001 From: Zain Kassam Date: Tue, 21 May 2024 16:28:01 +0100 Subject: [PATCH 3/5] feat(EMS-895): code and test fixes --- e2e-tests/commands/api/index.js | 2 +- .../create-an-abandoned-application.js | 2 +- .../dashboard-abandoned-applications.spec.js | 48 ++++++++ ...shboard-pagination-15-applications.spec.js | 9 +- ...on-16-applications-and-1-abandoned.spec.js | 104 ++++++++++++++++++ ...no-access-to-application-abandoned.spec.js | 13 ++- src/api/.keystone/config.js | 2 +- .../index.test.ts | 17 +-- .../create-an-abandoned-application/index.ts | 4 +- .../create-an-application/index.test.ts | 8 +- src/api/custom-schema/type-defs.ts | 1 - src/api/schema.graphql | 1 - .../insurance/application-status/index.ts | 1 + 13 files changed, 171 insertions(+), 41 deletions(-) create mode 100644 e2e-tests/insurance/cypress/e2e/journeys/dashboard/dashboard-abandoned-applications.spec.js create mode 100644 e2e-tests/insurance/cypress/e2e/journeys/dashboard/dashboard-pagination/dashboard-pagination-16-applications-and-1-abandoned.spec.js diff --git a/e2e-tests/commands/api/index.js b/e2e-tests/commands/api/index.js index 1e7385b2d0..777c494231 100644 --- a/e2e-tests/commands/api/index.js +++ b/e2e-tests/commands/api/index.js @@ -38,7 +38,6 @@ const queryStrings = { mutation createAnAbandonedApplication($accountId: String!, $eligibilityAnswers: ApplicationEligibility!, $company: CompanyInput!, $sectionReview: SectionReviewInput!) { createAnAbandonedApplication(accountId: $accountId, eligibilityAnswers: $eligibilityAnswers, company: $company, sectionReview: $sectionReview) { referenceNumber - status } } `, @@ -46,6 +45,7 @@ const queryStrings = { mutation createApplications($data: [ApplicationCreateInput!]!) { createApplications(data: $data) { id + referenceNumber } } `, diff --git a/e2e-tests/commands/insurance/create-an-abandoned-application.js b/e2e-tests/commands/insurance/create-an-abandoned-application.js index 27d7b4f0fb..0ee8b6745e 100644 --- a/e2e-tests/commands/insurance/create-an-abandoned-application.js +++ b/e2e-tests/commands/insurance/create-an-abandoned-application.js @@ -31,7 +31,7 @@ const createAnAbandonedApplication = (accountId) => { return api.createAnAbandonedApplication(accountId, mockEligibilityAnswers, company, sectionReview).then((application) => application); } catch (err) { - console.error('Creating an application', err); + console.error('Creating an abandoned application', err); return err; } diff --git a/e2e-tests/insurance/cypress/e2e/journeys/dashboard/dashboard-abandoned-applications.spec.js b/e2e-tests/insurance/cypress/e2e/journeys/dashboard/dashboard-abandoned-applications.spec.js new file mode 100644 index 0000000000..fb302c2b04 --- /dev/null +++ b/e2e-tests/insurance/cypress/e2e/journeys/dashboard/dashboard-abandoned-applications.spec.js @@ -0,0 +1,48 @@ +import { INSURANCE_ROUTES } from '../../../../../constants/routes/insurance'; +import { APPLICATION } from '../../../../../constants/application'; +import dashboardPage from '../../../../../pages/insurance/dashboard'; + +const { IN_PROGRESS, ABANDONED } = APPLICATION.STATUS; + +const { DASHBOARD } = INSURANCE_ROUTES; + +const baseUrl = Cypress.config('baseUrl'); + +const totalApplications = 1; + +const dashboardUrl = `${baseUrl}${DASHBOARD}`; + +const { table } = dashboardPage; + +context(`Insurance - Dashboard - pagination - 1 ${IN_PROGRESS} application and 1 ${ABANDONED} application`, () => { + let applications; + let abandonedApplication; + + before(() => { + cy.completeSignInAndGoToDashboard().then(({ accountId }) => { + cy.createApplications(accountId, totalApplications).then((createdApplications) => { + applications = createdApplications; + }); + + cy.createAnAbandonedApplication(accountId).then((createdApplication) => { + abandonedApplication = createdApplication; + }); + + cy.navigateToUrl(dashboardUrl); + }); + }); + + beforeEach(() => { + cy.saveSession(); + }); + + after(() => { + cy.deleteApplications(applications); + cy.deleteApplication(abandonedApplication.referenceNumber); + }); + + it(`should render 1 ${IN_PROGRESS} application on the dashboard`, () => { + cy.assertLength(table.body.rows(), totalApplications); + dashboardPage.table.body.row(applications[0].referenceNumber).status(); + }); +}); diff --git a/e2e-tests/insurance/cypress/e2e/journeys/dashboard/dashboard-pagination/dashboard-pagination-15-applications.spec.js b/e2e-tests/insurance/cypress/e2e/journeys/dashboard/dashboard-pagination/dashboard-pagination-15-applications.spec.js index 97061e5508..e38206b3ae 100644 --- a/e2e-tests/insurance/cypress/e2e/journeys/dashboard/dashboard-pagination/dashboard-pagination-15-applications.spec.js +++ b/e2e-tests/insurance/cypress/e2e/journeys/dashboard/dashboard-pagination/dashboard-pagination-15-applications.spec.js @@ -11,7 +11,6 @@ const dashboardUrl = `${baseUrl}${DASHBOARD}`; context(`Insurance - Dashboard - pagination - ${totalApplications} applications`, () => { let applications; - let abandonedApplication; before(() => { cy.completeSignInAndGoToDashboard().then(({ accountId }) => { @@ -19,11 +18,6 @@ context(`Insurance - Dashboard - pagination - ${totalApplications} applications` applications = createdApplications; }); - // creates an abandoned application - cy.createAnAbandonedApplication(accountId).then((createdApplication) => { - abandonedApplication = createdApplication; - }); - cy.navigateToUrl(dashboardUrl); }); }); @@ -34,10 +28,9 @@ context(`Insurance - Dashboard - pagination - ${totalApplications} applications` after(() => { cy.deleteApplications(applications); - cy.deleteApplication(abandonedApplication.referenceNumber); }); - it(`should NOT render pagination list items because there are no more than ${MAX_APPLICATIONS_PER_PAGE} applications (which are not Abandoned)`, () => { + it(`should NOT render pagination list items because there are no more than ${MAX_APPLICATIONS_PER_PAGE} applications`, () => { cy.assertPaginationDoesNotExist(); }); }); diff --git a/e2e-tests/insurance/cypress/e2e/journeys/dashboard/dashboard-pagination/dashboard-pagination-16-applications-and-1-abandoned.spec.js b/e2e-tests/insurance/cypress/e2e/journeys/dashboard/dashboard-pagination/dashboard-pagination-16-applications-and-1-abandoned.spec.js new file mode 100644 index 0000000000..c57e8fada4 --- /dev/null +++ b/e2e-tests/insurance/cypress/e2e/journeys/dashboard/dashboard-pagination/dashboard-pagination-16-applications-and-1-abandoned.spec.js @@ -0,0 +1,104 @@ +import pagination from '../../../../../../partials/pagination'; +import dashboardPage from '../../../../../../pages/insurance/dashboard'; +import { MAX_APPLICATIONS_PER_PAGE, APPLICATION } from '../../../../../../constants'; +import { INSURANCE_ROUTES } from '../../../../../../constants/routes/insurance'; + +const { DASHBOARD } = INSURANCE_ROUTES; + +const baseUrl = Cypress.config('baseUrl'); + +const totalApplications = MAX_APPLICATIONS_PER_PAGE + 1; +const totalPages = 2; + +const dashboardUrl = `${baseUrl}${DASHBOARD}`; + +const { table } = dashboardPage; + +const { ABANDONED } = APPLICATION.STATUS; + +context(`Insurance - Dashboard - pagination - ${totalApplications} applications and 1 abandoned application`, () => { + let applications; + let abandonedApplication; + + before(() => { + cy.completeSignInAndGoToDashboard().then(({ accountId }) => { + cy.createApplications(accountId, totalApplications).then((createdApplications) => { + applications = createdApplications; + }); + + cy.createAnAbandonedApplication(accountId).then((createdApplication) => { + abandonedApplication = createdApplication; + }); + + cy.navigateToUrl(dashboardUrl); + }); + }); + + beforeEach(() => { + cy.saveSession(); + }); + + after(() => { + cy.deleteApplications(applications); + cy.deleteApplication(abandonedApplication.referenceNumber); + }); + + describe('page tests', () => { + beforeEach(() => { + cy.navigateToUrl(dashboardUrl); + }); + + it('should render 2 pagination list items with links', () => { + cy.assertLength(pagination.listItems(), totalPages); + + cy.assertPaginationItemLink({ index: 0 }); + cy.assertPaginationItemLink({ index: 1 }); + }); + + it('should have the correct pagination state', () => { + cy.assertPaginationState({ + totalPages, + index: 0, + expectedPageNumber: 1, + previousLinkShouldExist: false, + expectedUrl: `${baseUrl}${DASHBOARD}`, + }); + }); + }); + + describe('when clicking on the `next` pagination link', () => { + beforeEach(() => { + cy.navigateToUrl(dashboardUrl); + + pagination.nextLink().click(); + }); + + it('should have the correct pagination state', () => { + cy.assertPaginationState({ + totalPages, + index: 2, + expectedPageNumber: 2, + nextLinkShouldExist: false, + }); + }); + + it(`should have 1 application on the dashboard (and not the ${ABANDONED} application)`, () => { + cy.assertLength(table.body.rows(), 1); + }); + }); + + describe('when clicking on page 2 pagination link', () => { + it('should have the correct pagination state', () => { + cy.navigateToUrl(dashboardUrl); + + pagination.listItemLink(1).click(); + + cy.assertPaginationState({ + totalPages, + index: 2, + expectedPageNumber: 2, + nextLinkShouldExist: false, + }); + }); + }); +}); diff --git a/e2e-tests/insurance/cypress/e2e/journeys/no-access-to-application/no-access-to-application-abandoned.spec.js b/e2e-tests/insurance/cypress/e2e/journeys/no-access-to-application/no-access-to-application-abandoned.spec.js index c876be2361..dc96eeb5bb 100644 --- a/e2e-tests/insurance/cypress/e2e/journeys/no-access-to-application/no-access-to-application-abandoned.spec.js +++ b/e2e-tests/insurance/cypress/e2e/journeys/no-access-to-application/no-access-to-application-abandoned.spec.js @@ -1,4 +1,7 @@ import { INSURANCE_ROUTES } from '../../../../../constants/routes/insurance'; +import { APPLICATION } from '../../../../../constants/application'; + +const { ABANDONED } = APPLICATION.STATUS; const { ROOT, @@ -8,16 +11,16 @@ const { const baseUrl = Cypress.config('baseUrl'); -context('Insurance - no access to application page - Abandoned application', () => { +context('Insurance - no access to application page - Abandoned application - to ensure that the system should automatically marks applications as abandoned where the application has not been submitted 30 days after it was started', () => { let refNumber; - let applicationUrl; + let allSectionsUrl; before(() => { cy.saveSession(); cy.completeSignInAndGoToDashboard().then(({ accountId }) => { cy.createAnAbandonedApplication(accountId).then((createdApplication) => { - applicationUrl = `${baseUrl}${ROOT}/${createdApplication.referenceNumber}${ALL_SECTIONS}`; + allSectionsUrl = `${baseUrl}${ROOT}/${createdApplication.referenceNumber}${ALL_SECTIONS}`; refNumber = createdApplication.referenceNumber; }); }); @@ -27,9 +30,9 @@ context('Insurance - no access to application page - Abandoned application', () cy.deleteApplication(refNumber); }); - describe('when trying to access an Abandoned application', () => { + describe(`when trying to access an ${ABANDONED} application`, () => { beforeEach(() => { - cy.navigateToUrl(applicationUrl); + cy.navigateToUrl(allSectionsUrl); }); it(`should redirect to ${NO_ACCESS_TO_APPLICATION}`, () => { diff --git a/src/api/.keystone/config.js b/src/api/.keystone/config.js index 3be3f61ca2..2fe30fd44c 100644 --- a/src/api/.keystone/config.js +++ b/src/api/.keystone/config.js @@ -2379,7 +2379,6 @@ var typeDefs = ` success: Boolean! id: String referenceNumber: Int - status: String } type MappedCisCountry { @@ -4640,6 +4639,7 @@ var create_an_application_default2 = createAnApplication2; // custom-resolvers/mutations/create-an-abandoned-application/index.ts var { STATUS: STATUS3 } = APPLICATION; var createAnAbandonedApplication = async (root, variables, context) => { + console.info("Creating an abandoned application for ", variables.accountId); const abandonedApplicationVariables = variables; abandonedApplicationVariables.status = STATUS3.ABANDONED; try { diff --git a/src/api/custom-resolvers/mutations/create-an-abandoned-application/index.test.ts b/src/api/custom-resolvers/mutations/create-an-abandoned-application/index.test.ts index 0dfce0aad5..5e79330958 100644 --- a/src/api/custom-resolvers/mutations/create-an-abandoned-application/index.test.ts +++ b/src/api/custom-resolvers/mutations/create-an-abandoned-application/index.test.ts @@ -6,7 +6,7 @@ import getKeystoneContext from '../../../test-helpers/get-keystone-context'; import accounts from '../../../test-helpers/accounts'; import { APPLICATION } from '../../../constants'; -const { STATUS, SUBMISSION_TYPE } = APPLICATION; +const { STATUS } = APPLICATION; describe('custom-resolvers/create-an-abandoned-application', () => { @@ -48,12 +48,6 @@ describe('custom-resolvers/create-an-abandoned-application', () => { expect(result.status).toEqual(STATUS.ABANDONED); }); - test(`it should return submissionType as ${SUBMISSION_TYPE.MIA}`, async () => { - result = await createAnAbandonedApplication({}, variables, context); - - expect(result.submissionType).toEqual(SUBMISSION_TYPE.MIA); - }); - describe('when there is no account for the provided accountId', () => { test('it should return success=false', async () => { variables.accountId = 'invalid-id'; @@ -66,14 +60,7 @@ describe('custom-resolvers/create-an-abandoned-application', () => { describe('when creation is not successful', () => { test('it should throw an error', async () => { - try { - // pass empty context object to force an error - await createAnAbandonedApplication({}, variables, {}); - } catch (err) { - const errorString = String(err); - - expect(errorString.includes('Creating an abandoned application')).toEqual(true); - } + await expect(createAnAbandonedApplication({}, variables, {})).rejects.toThrow('Creating an abandoned application'); }); }); }); diff --git a/src/api/custom-resolvers/mutations/create-an-abandoned-application/index.ts b/src/api/custom-resolvers/mutations/create-an-abandoned-application/index.ts index e619aadf2c..c3dc0a21a9 100644 --- a/src/api/custom-resolvers/mutations/create-an-abandoned-application/index.ts +++ b/src/api/custom-resolvers/mutations/create-an-abandoned-application/index.ts @@ -17,6 +17,8 @@ const { STATUS } = APPLICATION; * @returns {Promise} Object with success flag and application */ const createAnAbandonedApplication = async (root: any, variables: CreateAnApplicationVariables, context: Context) => { + console.info('Creating an abandoned application for ', variables.accountId); + const abandonedApplicationVariables = variables; // set status to abandoned @@ -27,7 +29,7 @@ const createAnAbandonedApplication = async (root: any, variables: CreateAnApplic const createdApplication = await createAnApplicationHelper(root, abandonedApplicationVariables, context); if (createdApplication) { - // TODO: remove once schema hooks for status is removed + // TODO: EMS-3387 remove once schema hooks for status is removed // updates application status to abandoned const updatedApplication = await context.db.Application.updateOne({ where: { diff --git a/src/api/custom-resolvers/mutations/create-an-application/index.test.ts b/src/api/custom-resolvers/mutations/create-an-application/index.test.ts index bb43f7d8db..56cc5ae862 100644 --- a/src/api/custom-resolvers/mutations/create-an-application/index.test.ts +++ b/src/api/custom-resolvers/mutations/create-an-application/index.test.ts @@ -6,7 +6,7 @@ import getKeystoneContext from '../../../test-helpers/get-keystone-context'; import accounts from '../../../test-helpers/accounts'; import { APPLICATION } from '../../../constants'; -const { STATUS, SUBMISSION_TYPE } = APPLICATION; +const { STATUS } = APPLICATION; describe('custom-resolvers/create-an-application', () => { @@ -48,12 +48,6 @@ describe('custom-resolvers/create-an-application', () => { expect(result.status).toEqual(STATUS.IN_PROGRESS); }); - test(`it should return submissionType as ${SUBMISSION_TYPE.MIA}`, async () => { - result = await createAnApplication({}, variables, context); - - expect(result.submissionType).toEqual(SUBMISSION_TYPE.MIA); - }); - describe('when there is no account for the provided accountId', () => { test('it should return success=false', async () => { variables.accountId = 'invalid-id'; diff --git a/src/api/custom-schema/type-defs.ts b/src/api/custom-schema/type-defs.ts index 864b9407c6..69c33b11dd 100644 --- a/src/api/custom-schema/type-defs.ts +++ b/src/api/custom-schema/type-defs.ts @@ -209,7 +209,6 @@ const typeDefs = ` success: Boolean! id: String referenceNumber: Int - status: String } type MappedCisCountry { diff --git a/src/api/schema.graphql b/src/api/schema.graphql index ec13a2827f..40385d675b 100644 --- a/src/api/schema.graphql +++ b/src/api/schema.graphql @@ -3727,7 +3727,6 @@ type CreateAnAbandonedApplicationResponse { success: Boolean! id: String referenceNumber: Int - status: String } type MappedCisCountry { diff --git a/src/ui/server/middleware/insurance/application-status/index.ts b/src/ui/server/middleware/insurance/application-status/index.ts index 7bcf36c679..ee8439a2f0 100644 --- a/src/ui/server/middleware/insurance/application-status/index.ts +++ b/src/ui/server/middleware/insurance/application-status/index.ts @@ -16,6 +16,7 @@ const { /** * middleware to check if application is submitted * if submitted, access is not allowed to application apart from application submitted page + * if abandoned, access is not allowed to application and should be redirected to NO_ACCESS_TO_APPLICATION * @param {Express.Request} Express request * @param {Express.Response} Express response * @param {Next} From 6b836b571b25d5035113b5f3d4d70ff96b6bd650 Mon Sep 17 00:00:00 2001 From: Zain Kassam Date: Wed, 22 May 2024 11:46:23 +0100 Subject: [PATCH 4/5] feat(EMS-895): e2e test fixes --- .../dashboard/dashboard-abandoned-applications.spec.js | 8 ++++---- ...-pagination-16-applications-and-1-abandoned.spec.js | 10 ++++++++-- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/e2e-tests/insurance/cypress/e2e/journeys/dashboard/dashboard-abandoned-applications.spec.js b/e2e-tests/insurance/cypress/e2e/journeys/dashboard/dashboard-abandoned-applications.spec.js index fb302c2b04..da2e5c8857 100644 --- a/e2e-tests/insurance/cypress/e2e/journeys/dashboard/dashboard-abandoned-applications.spec.js +++ b/e2e-tests/insurance/cypress/e2e/journeys/dashboard/dashboard-abandoned-applications.spec.js @@ -8,7 +8,7 @@ const { DASHBOARD } = INSURANCE_ROUTES; const baseUrl = Cypress.config('baseUrl'); -const totalApplications = 1; +const totalExpectedApplications = 1; const dashboardUrl = `${baseUrl}${DASHBOARD}`; @@ -20,7 +20,7 @@ context(`Insurance - Dashboard - pagination - 1 ${IN_PROGRESS} application and 1 before(() => { cy.completeSignInAndGoToDashboard().then(({ accountId }) => { - cy.createApplications(accountId, totalApplications).then((createdApplications) => { + cy.createApplications(accountId, totalExpectedApplications).then((createdApplications) => { applications = createdApplications; }); @@ -42,7 +42,7 @@ context(`Insurance - Dashboard - pagination - 1 ${IN_PROGRESS} application and 1 }); it(`should render 1 ${IN_PROGRESS} application on the dashboard`, () => { - cy.assertLength(table.body.rows(), totalApplications); - dashboardPage.table.body.row(applications[0].referenceNumber).status(); + cy.assertLength(table.body.rows(), totalExpectedApplications); + cy.checkText(dashboardPage.table.body.row(applications[0].referenceNumber).status(), IN_PROGRESS); }); }); diff --git a/e2e-tests/insurance/cypress/e2e/journeys/dashboard/dashboard-pagination/dashboard-pagination-16-applications-and-1-abandoned.spec.js b/e2e-tests/insurance/cypress/e2e/journeys/dashboard/dashboard-pagination/dashboard-pagination-16-applications-and-1-abandoned.spec.js index c57e8fada4..ac5b77bb30 100644 --- a/e2e-tests/insurance/cypress/e2e/journeys/dashboard/dashboard-pagination/dashboard-pagination-16-applications-and-1-abandoned.spec.js +++ b/e2e-tests/insurance/cypress/e2e/journeys/dashboard/dashboard-pagination/dashboard-pagination-16-applications-and-1-abandoned.spec.js @@ -14,9 +14,9 @@ const dashboardUrl = `${baseUrl}${DASHBOARD}`; const { table } = dashboardPage; -const { ABANDONED } = APPLICATION.STATUS; +const { ABANDONED, IN_PROGRESS } = APPLICATION.STATUS; -context(`Insurance - Dashboard - pagination - ${totalApplications} applications and 1 abandoned application`, () => { +context(`Insurance - Dashboard - pagination - ${totalApplications} applications and 1 ${ABANDONED} application`, () => { let applications; let abandonedApplication; @@ -64,6 +64,12 @@ context(`Insurance - Dashboard - pagination - ${totalApplications} applications expectedUrl: `${baseUrl}${DASHBOARD}`, }); }); + + it(`should have no ${ABANDONED} applications`, () => { + dashboardPage.body.rows().forEach((row) => { + cy.checkText(row.status(), IN_PROGRESS); + }); + }); }); describe('when clicking on the `next` pagination link', () => { From 75b71f98e09fed17407276762fe8b5b2d1d61789 Mon Sep 17 00:00:00 2001 From: Zain Kassam Date: Fri, 24 May 2024 15:27:47 +0100 Subject: [PATCH 5/5] feat(EMS-895): added check for statuses in progress --- ...board-application-statuses-are-in-progress.js | 16 ++++++++++++++++ .../shared-commands/assertions/pagination.js | 4 ++++ ...ation-16-applications-and-1-abandoned.spec.js | 7 +++---- e2e-tests/pages/insurance/dashboard/index.js | 1 + 4 files changed, 24 insertions(+), 4 deletions(-) create mode 100644 e2e-tests/commands/pagination/assert-all-dashboard-application-statuses-are-in-progress.js diff --git a/e2e-tests/commands/pagination/assert-all-dashboard-application-statuses-are-in-progress.js b/e2e-tests/commands/pagination/assert-all-dashboard-application-statuses-are-in-progress.js new file mode 100644 index 0000000000..441357877c --- /dev/null +++ b/e2e-tests/commands/pagination/assert-all-dashboard-application-statuses-are-in-progress.js @@ -0,0 +1,16 @@ +import { APPLICATION } from '../../constants'; +import dashboardPage from '../../pages/insurance/dashboard'; + +const { IN_PROGRESS } = APPLICATION.STATUS; + +/** + * assertAllDashboardApplicationStatusesAreInProgress + * asserts that all applications on dashboard page are IN_PROGRESS + */ +const assertAllDashboardApplicationStatusesAreInProgress = () => { + dashboardPage.table.body.firstColumn().each(($el) => { + expect($el.text().trim()).to.eq(IN_PROGRESS); + }); +}; + +export default assertAllDashboardApplicationStatusesAreInProgress; diff --git a/e2e-tests/commands/shared-commands/assertions/pagination.js b/e2e-tests/commands/shared-commands/assertions/pagination.js index 83382697cb..567d326952 100644 --- a/e2e-tests/commands/shared-commands/assertions/pagination.js +++ b/e2e-tests/commands/shared-commands/assertions/pagination.js @@ -5,3 +5,7 @@ Cypress.Commands.add('assertPaginationItemEllipsis', require('../../pagination/a Cypress.Commands.add('assertPaginationNextLink', require('../../pagination/assert-pagination-next-link')); Cypress.Commands.add('assertPaginationPreviousLink', require('../../pagination/assert-pagination-previous-link')); Cypress.Commands.add('assertPaginationState', require('../../pagination/assert-pagination-state')); +Cypress.Commands.add( + 'assertAllDashboardApplicationStatusesAreInProgress', + require('../../pagination/assert-all-dashboard-application-statuses-are-in-progress'), +); diff --git a/e2e-tests/insurance/cypress/e2e/journeys/dashboard/dashboard-pagination/dashboard-pagination-16-applications-and-1-abandoned.spec.js b/e2e-tests/insurance/cypress/e2e/journeys/dashboard/dashboard-pagination/dashboard-pagination-16-applications-and-1-abandoned.spec.js index ac5b77bb30..f83106b474 100644 --- a/e2e-tests/insurance/cypress/e2e/journeys/dashboard/dashboard-pagination/dashboard-pagination-16-applications-and-1-abandoned.spec.js +++ b/e2e-tests/insurance/cypress/e2e/journeys/dashboard/dashboard-pagination/dashboard-pagination-16-applications-and-1-abandoned.spec.js @@ -14,7 +14,7 @@ const dashboardUrl = `${baseUrl}${DASHBOARD}`; const { table } = dashboardPage; -const { ABANDONED, IN_PROGRESS } = APPLICATION.STATUS; +const { ABANDONED } = APPLICATION.STATUS; context(`Insurance - Dashboard - pagination - ${totalApplications} applications and 1 ${ABANDONED} application`, () => { let applications; @@ -66,9 +66,7 @@ context(`Insurance - Dashboard - pagination - ${totalApplications} applications }); it(`should have no ${ABANDONED} applications`, () => { - dashboardPage.body.rows().forEach((row) => { - cy.checkText(row.status(), IN_PROGRESS); - }); + cy.assertAllDashboardApplicationStatusesAreInProgress(); }); }); @@ -90,6 +88,7 @@ context(`Insurance - Dashboard - pagination - ${totalApplications} applications it(`should have 1 application on the dashboard (and not the ${ABANDONED} application)`, () => { cy.assertLength(table.body.rows(), 1); + cy.assertAllDashboardApplicationStatusesAreInProgress(); }); }); diff --git a/e2e-tests/pages/insurance/dashboard/index.js b/e2e-tests/pages/insurance/dashboard/index.js index 9611e6f207..f46c2d1af0 100644 --- a/e2e-tests/pages/insurance/dashboard/index.js +++ b/e2e-tests/pages/insurance/dashboard/index.js @@ -13,6 +13,7 @@ const dashboardPage = { }, body: { rows: () => cy.get('table tbody tr'), + firstColumn: () => cy.get('table tbody tr td:first'), row: (referenceNumber) => ({ status: () => cy.get(`[data-cy="ref-${referenceNumber}-status"]`), buyerLocation: () => cy.get(`[data-cy="ref-${referenceNumber}-buyerLocation"]`),