diff --git a/src/client/modules/ExportWins/Form/WinDetailsStep.jsx b/src/client/modules/ExportWins/Form/WinDetailsStep.jsx index 9677202ac1f..4873f6eb260 100644 --- a/src/client/modules/ExportWins/Form/WinDetailsStep.jsx +++ b/src/client/modules/ExportWins/Form/WinDetailsStep.jsx @@ -28,8 +28,8 @@ import { } from './constants' import { formatValue, - getTwelveMonthsAgo, sumAllWinTypeYearlyValues, + getDateTwelveMonthsAgoWithFirstDay, } from './utils' const MAX_WORDS = 100 @@ -46,7 +46,7 @@ const StyledExportTotal = styled('p')({ const WinDetailsStep = () => { const { values } = useFormContext() - const twelveMonthsAgo = getTwelveMonthsAgo() + const twelveMonthsAgo = getDateTwelveMonthsAgoWithFirstDay() const month = twelveMonthsAgo.getMonth() + 1 const year = twelveMonthsAgo.getFullYear() diff --git a/src/client/modules/ExportWins/Form/transformers.js b/src/client/modules/ExportWins/Form/transformers.js index 641b21781a0..fd4a6b58eb9 100644 --- a/src/client/modules/ExportWins/Form/transformers.js +++ b/src/client/modules/ExportWins/Form/transformers.js @@ -2,7 +2,7 @@ import { isEmpty } from 'lodash' import { convertDateToFieldDateObject } from '../../../../client/utils/date' import { OPTION_YES, OPTION_NO } from '../../../../common/constants' -import { sumWinTypeYearlyValues, isDateWithinLastTwelveMonths } from './utils' +import { sumWinTypeYearlyValues, isWithinLastTwelveMonths } from './utils' import { idNameToValueLabel } from '../../../../client/utils' import { winTypeId, @@ -116,7 +116,7 @@ export const transformExportProjectForForm = (exportProject) => { ? transformCompanyContact(exportProject.contacts[0]) : null, // Get the user to choose the contact // Win Details - date: isDateWithinLastTwelveMonths(date) && { + date: isWithinLastTwelveMonths(date) && { year: String(date.getFullYear()), month: String(date.getMonth() + 1), }, diff --git a/src/client/modules/ExportWins/Form/utils.js b/src/client/modules/ExportWins/Form/utils.js index ddbd968fc3d..3149dc3faf8 100644 --- a/src/client/modules/ExportWins/Form/utils.js +++ b/src/client/modules/ExportWins/Form/utils.js @@ -1,5 +1,9 @@ import { currencyGBP } from '../../../../client/utils/number-utils' - +import { + subtractMonths, + isDateAfter, + getStartOfMonth, +} from '../../../utils/date' /** * @param {String} winType the name of the win type * @param {Object} values the form values object containing the keys @@ -113,19 +117,22 @@ export const getYearFromWinType = (winType, values) => export const getMaxYearFromWinTypes = (winTypes, values) => Math.max(...winTypes.map((winType) => getYearFromWinType(winType, values))) -export const formatValue = (sum) => currencyGBP(sum) - -export const getTwelveMonthsAgo = () => { - const today = new Date() - return new Date(today.getFullYear() - 1, today.getMonth(), 1) -} - -export const getRandomDate = ({ start, end }) => - new Date(start.getTime() + Math.random() * (end.getTime() - start.getTime())) +/** + * Returns a date that is 12 months ago from the current date that includes the 1st of the month + * @returns {Date} A date that is 12 months ago from the current date that includes the 1st of the month + */ +export const getDateTwelveMonthsAgoWithFirstDay = () => + subtractMonths(getStartOfMonth(new Date()), 12) -export const isDateWithinLastTwelveMonths = (date) => { +/** + * Tests whether a given date is within the last twelve months and not in the future. + * @param {Date} date - The date to test. + * @returns {boolean} True if the date is within the last twelve months and not in the future, false otherwise. + */ +export const isWithinLastTwelveMonths = (date) => // Business date logic - const today = new Date() - const from = getTwelveMonthsAgo() - return date >= from && date <= today -} + isDateAfter(date, new Date()) + ? false // Date is in the future + : isDateAfter(date, getDateTwelveMonthsAgoWithFirstDay()) + +export const formatValue = (sum) => currencyGBP(sum) diff --git a/src/client/modules/ExportWins/Form/validators.js b/src/client/modules/ExportWins/Form/validators.js index 37fbd047b3b..ddc57148d31 100644 --- a/src/client/modules/ExportWins/Form/validators.js +++ b/src/client/modules/ExportWins/Form/validators.js @@ -1,4 +1,4 @@ -import { isDateWithinLastTwelveMonths } from './utils' +import { isWithinLastTwelveMonths } from './utils' export const POSITIVE_INT_REGEX = /^[0-9]{1,19}$/ export const isPositiveInteger = (value) => POSITIVE_INT_REGEX.test(value) @@ -9,6 +9,6 @@ export const validateTeamMembers = (team_members) => : null export const validateWinDate = ({ month, year }) => - isDateWithinLastTwelveMonths(new Date(year, month - 1)) + isWithinLastTwelveMonths(new Date(year, month - 1)) ? null : 'Date must be in the last 12 months' diff --git a/src/client/utils/date.js b/src/client/utils/date.js index 3e6dc1b7b66..3b2f490ec5e 100644 --- a/src/client/utils/date.js +++ b/src/client/utils/date.js @@ -10,8 +10,11 @@ const { addDays: addDaysFns, addMonths: addMonthsFns, addYears: addYearsFns, + differenceInDays, differenceInCalendarDays, + isSameDay, endOfToday, + startOfMonth: getStartOfMonth, endOfYesterday, format: formatFns, formatDistanceToNowStrict, @@ -285,6 +288,25 @@ function convertDateToFieldDateObject(date) { return { day: '', month: '', year: '' } } +/** + * Generates a random date within the range specified by startDate and endDate (inclusive). + * @param {Date} startDate - The start date of the range. + * @param {Date} endDate - The end date of the range. + * @returns {Date} A random date within the specified range. + * @throws {Error} If startDate is greater than endDate or if startDate and endDate are the same date. + */ +function getRandomDateInRange(startDate, endDate) { + if (isSameDay(startDate, endDate)) { + throw new Error('Start date and end date cannot be the same.') + } + if (startDate > endDate) { + throw new Error('Start date must be less than or equal to end date.') + } + const daysDifference = differenceInDays(endDate, startDate) + const randomNumberOfDays = Math.floor(Math.random() * (daysDifference + 1)) + return addDays(startDate, randomNumberOfDays) +} + module.exports = { addDays, addMonths, @@ -322,4 +344,6 @@ module.exports = { isDateInFuture, parseDateISO, convertDateToFieldDateObject, + getRandomDateInRange, + getStartOfMonth, } diff --git a/test/functional/cypress/fakers/export-wins.js b/test/functional/cypress/fakers/export-wins.js index 58d8b898f96..19f32ee09fb 100644 --- a/test/functional/cypress/fakers/export-wins.js +++ b/test/functional/cypress/fakers/export-wins.js @@ -1,5 +1,7 @@ import { faker } from '@faker-js/faker' +import { contactFaker } from './contacts' + /** * Generate fake data for a single export win. */ @@ -20,6 +22,7 @@ export const exportWinsFaker = () => ({ customer_name: faker.person.fullName(), customer_job_title: faker.person.jobTitle(), customer_email_address: faker.internet.email(), + company_contacts: [contactFaker()], total_expected_export_value: faker.number.int({ min: 10_000, max: 10_000_000, diff --git a/test/functional/cypress/specs/export-win/add-export-win-from-export-project.js b/test/functional/cypress/specs/export-win/add-export-win-from-export-project.js new file mode 100644 index 00000000000..83aec753d85 --- /dev/null +++ b/test/functional/cypress/specs/export-win/add-export-win-from-export-project.js @@ -0,0 +1,356 @@ +import { pick } from 'lodash' + +import { formFields, company, successPageRegex } from './constants' +import { exportWinsFaker } from '../../fakers/export-wins' +import { contactFaker } from '../../fakers/contacts' +import { exportFaker } from '../../fakers/export' +import urls from '../../../../../src/lib/urls' + +import { + assertTypeaheadValues, + assertSingleTypeaheadOptionSelected, +} from '../../support/assertions' + +import { + fillWinDetails, + fillOfficerDetails, + fillCustomerDetails, + fillSupportProvided, + fillCreditForThisWin, + getDateNextMonth, + getDateThirteenMonthsAgo, + clickContinueAndAssertUrl, + getDateWithinLastTwelveMonths, +} from './utils' + +const { month, year } = getDateWithinLastTwelveMonths() + +const exportWin = exportWinsFaker() + +// These are the fields that will pre-populate the export win form +const exportProject = exportFaker({ + owner: { + id: '1', + name: 'Sally Truston', // lead officer + }, + team_members: [ + { + id: '2', + name: 'Adrian Jewel', + }, + { + id: '3', + name: 'Kelly Williams', + }, + ], + // To pre-populate the form with a customer contact + // there can only be one of them otherwise we don't + // know who to select from of a list of them. + contacts: [ + { + id: '4', + name: 'Jessica Groom', + }, + ], + exporter_experience: { + id: '5', + name: 'Never exported', + }, + destination_country: { id: '6', name: 'Dubai' }, + // To pre-populate the form the estimated win + // date has to be within the last 12 months + estimated_win_date: `${year}-${month}-01`, + sector: { + id: '7', + name: 'Railways', + }, +}) + +const createFromExportUrl = urls.companies.exportWins.createFromExport( + company.id, + exportProject.id +) + +// All form step URLs (creating a win from an export project) +const officerDetailsStep = `${createFromExportUrl}?step=officer_details` +const creditForThisWinStep = `${createFromExportUrl}?step=credit_for_this_win` +const customerDetailsStep = `${createFromExportUrl}?step=customer_details` +const winDetailsStep = `${createFromExportUrl}?step=win_details` +const supportProvidedStep = `${createFromExportUrl}?step=support_provided` +const checkBeforeSendingStep = `${createFromExportUrl}?step=check_before_sending` + +describe('Adding an export win from an export project', () => { + const { officerDetails, customerDetails, winDetails } = formFields + + context( + 'When the export win form fields are pre-populated from the export project', + () => { + const interceptExportApiCall = (overrides = {}) => { + cy.intercept('GET', `/api-proxy/v4/export/${exportProject.id}`, { + ...exportProject, + ...overrides, + }) + } + + beforeEach(() => { + cy.setUserFeatureGroups(['export-wins']) + interceptExportApiCall() + }) + + it('should pre-populate the lead officer field', () => { + cy.visit(officerDetailsStep) + cy.get(officerDetails.leadOfficer) + .find('input') + .should('have.value', exportProject.owner.name) + }) + + it('should pre-populate the team members field', () => { + cy.visit(officerDetailsStep) + assertTypeaheadValues( + '[data-test="field-team_members"]', + exportProject.team_members.map(({ name }) => name) + ) + }) + + it('should pre-populate the customer contact field', () => { + cy.visit(customerDetailsStep) + assertSingleTypeaheadOptionSelected({ + element: customerDetails.contacts, + expectedOption: exportProject.contacts[0].name, + }) + }) + + it('should pre-populate the exporter experience field', () => { + cy.visit(customerDetailsStep) + assertSingleTypeaheadOptionSelected({ + element: customerDetails.experience, + expectedOption: exportProject.exporter_experience.name, + }) + }) + + it('should pre-populate the export destination country', () => { + cy.visit(winDetailsStep) + assertSingleTypeaheadOptionSelected({ + element: winDetails.country, + expectedOption: exportProject.destination_country.name, + }) + }) + + it( + 'should not pre-populate the win date when the estimated ' + + 'win date is greater than twelve months ago', + () => { + const { year, month } = getDateThirteenMonthsAgo() + + interceptExportApiCall({ + estimated_win_date: `${year}-${month}-01`, + }) + + cy.visit(winDetailsStep) + cy.get(winDetails.dateMonth).should('have.value', '') + cy.get(winDetails.dateYear).should('have.value', '') + } + ) + + it( + 'should pre-populate the win date when the estimated win ' + + 'date is less than or equal to 12 months ago', + () => { + interceptExportApiCall({ + estimated_win_date: `${year}-${month}-01`, + }) + + cy.visit(winDetailsStep) + cy.get(winDetails.dateYear).should('have.value', year) + cy.get(winDetails.dateMonth).should('have.value', month) + } + ) + + it( + 'should not pre-populate the win date when the ' + + 'estimated date is in the future', + () => { + const { year, month } = getDateNextMonth() + + interceptExportApiCall({ + estimated_win_date: `${year}-${month}-01`, + }) + + cy.visit(winDetailsStep) + cy.get(winDetails.dateYear).should('have.value', '') + cy.get(winDetails.dateMonth).should('have.value', '') + } + ) + + it('should pre-populate the sector', () => { + cy.visit(winDetailsStep) + assertSingleTypeaheadOptionSelected({ + element: winDetails.sector, + expectedOption: exportProject.sector.name, + }) + }) + } + ) + + context( + 'When the export win is created from an export project', + { testIsolation: false }, + () => { + beforeEach(() => { + cy.setUserFeatureGroups(['export-wins']) + cy.intercept( + 'GET', + `/api-proxy/v4/export/${exportProject.id}`, + exportProject + ) + cy.intercept('POST', '/api-proxy/v4/export-win', { + statusCode: 201, + }).as('apiPostExportWin') + cy.intercept('GET', '/api-proxy/v4/export-win/*', exportWin).as( + 'apiGetExportWin' + ) + cy.intercept('GET', '/api-proxy/v4/metadata/hq-team-region-or-post?*', [ + { name: 'DIT Education' }, + { name: 'Healthcare UK' }, + ]) + cy.intercept('GET', `/api-proxy/v4/contact?company_id=${company.id}`, { + results: [ + contactFaker({ + name: 'Joseph Barker', + email: 'joseph.barker@test.com', + }), + ], + }) + cy.intercept('GET', '/api-proxy/v4/metadata/hvc', [ + { name: 'Australia Consumer Goods & Retail: E004' }, + ]) + cy.intercept('GET', '/api-proxy/v4/metadata/support-type', [ + { + name: 'Market entry advice and support – DIT/FCO in UK', + }, + ]) + cy.intercept('GET', '/api-proxy/v4/metadata/associated-programme', [ + { name: 'Afterburner' }, + ]) + }) + + it( + 'should fill out the entire export win user journey ommiting ' + + 'the pre-populated fields from the export project', + () => { + cy.visit(officerDetailsStep) + + fillOfficerDetails({ + leadOfficer: null, // pre-populated from the export project + teamType: 'Investment (ITFG or IG)', + hqTeam: 'DIT Education', + teamMembers: null, // pre-populated from the export project + }) + + clickContinueAndAssertUrl(creditForThisWinStep) + + fillCreditForThisWin({ + contributingOfficer: 'John', + teamType: 'Trade (TD or ST)', + hqTeam: 'Healthcare UK', + }) + + clickContinueAndAssertUrl(customerDetailsStep) + + fillCustomerDetails({ + contact: null, // pre-populated providing there's a single customer contact + location: 'Scotland', + potential: 'The company is a Medium Sized Business', + experience: null, // pre-populated from the export project + }) + + clickContinueAndAssertUrl(winDetailsStep) + + fillWinDetails({ + country: null, // pre-populated from the export project + dateMonth: null, // pre-populated but must be within the last 12 months + dateYear: null, // pre-populated but must be within the last 12 months + description: 'Foo bar baz', + nameOfCustomer: 'David French', + isConfidential: true, + businessType: 'Contract', + exportValues: ['1000000'], + goodsVsServices: 'goods', + nameOfExport: 'Biscuits', + sector: null, // pre-populated from the export project + }) + + clickContinueAndAssertUrl(supportProvidedStep) + + fillSupportProvided({ + hvc: 'Aus', + typeOfSupport: 'Mar', + associatedProgramme: 'Aft', + personallyConfirmed: true, + lineManagerConfirmed: true, + }) + + clickContinueAndAssertUrl(checkBeforeSendingStep) + } + ) + it('should have the pre-populated values on the "Check before sending" step', () => { + cy.get('[data-test="officer-details"]').should( + 'contain', + exportProject.owner.name // lead officer + ) + cy.get('[data-test="officer-details"]').should( + 'contain', + exportProject.team_members.map(({ name }) => name).join('') + ) + cy.get('[data-test="customer-details"]').should( + 'contain', + exportProject.exporter_experience.name + ) + cy.get('[data-test="win-details"]').should( + 'contain', + exportProject.destination_country.name + ) + cy.get('[data-test="win-details"]').should( + 'contain', + `${month}/${year}` // win date + ) + cy.get('[data-test="win-details"]').should( + 'contain', + exportProject.sector.name + ) + }) + + it('should POST to the API and redirect to the success page', () => { + cy.get('[data-test="confirm-and-send-to-customer"]').click() + cy.wait('@apiPostExportWin').then(({ request }) => { + expect( + pick(request.body, [ + 'lead_officer', + 'team_members', + 'company_contacts', + 'export_experience', + 'country', + 'date', + 'sector', + ]) + ).to.deep.equal({ + // We only need to assert the fields that have + // been pre-populated from an export project + lead_officer: exportProject.owner.id, + team_members: exportProject.team_members.map(({ id }) => id), + company_contacts: exportProject.contacts.map(({ id }) => id), + export_experience: exportProject.exporter_experience.id, + country: exportProject.destination_country.id, + date: `${year}-${month}-01`, + sector: exportProject.sector.id, + }) + }) + + // Ensure we end up on the success page + cy.location().should(({ pathname }) => { + expect(pathname).to.match(successPageRegex) + }) + }) + } + ) +}) diff --git a/test/functional/cypress/specs/export-win/add-export-win-spec.js b/test/functional/cypress/specs/export-win/add-export-win-spec.js index 2156cb34f83..5ce7f5e8e99 100644 --- a/test/functional/cypress/specs/export-win/add-export-win-spec.js +++ b/test/functional/cypress/specs/export-win/add-export-win-spec.js @@ -1,36 +1,39 @@ import { omit } from 'lodash' -import { - getRandomDate, - getTwelveMonthsAgo, -} from '../../../../../src/client/modules/ExportWins/Form/utils' import { winTypeId } from '../../../../../src/client/modules/ExportWins/Form/constants' -import { clickContinueButton } from '../../support/actions' -import { companyFaker } from '../../fakers/companies' -import { contactFaker } from '../../fakers/contacts' -import { exportFaker } from '../../fakers/export' import { exportWinsFaker } from '../../fakers/export-wins' +import { contactFaker } from '../../fakers/contacts' import urls from '../../../../../src/lib/urls' + import { - assertUrl, - assertFieldInput, - assertFieldError, assertBreadcrumbs, assertLocalHeader, - assertErrorSummary, assertSummaryTable, - assertFieldTextarea, - assertFieldTypeahead, - assertFieldDateShort, - assertFieldCheckboxes, - assertTypeaheadValues, - assertFieldRadiosWithLegend, - assertSingleTypeaheadOptionSelected, } from '../../support/assertions' -const company = companyFaker({ - name: 'Advanced Mini Devices', -}) +import { + fillWinDetails, + fillOfficerDetails, + fillCustomerDetails, + fillSupportProvided, + fillCreditForThisWin, + clickContinueAndAssertUrl, + getDateWithinLastTwelveMonths, +} from './utils' + +const { month, year } = getDateWithinLastTwelveMonths() + +import { + company, + formFields, + winDetailsStep, + successPageRegex, + officerDetailsStep, + customerDetailsStep, + supportProvidedStep, + creditForThisWinStep, + checkBeforeSendingStep, +} from './constants' const exportWin = { ...exportWinsFaker(), @@ -39,99 +42,6 @@ const exportWin = { company_contacts: [{ email: 'jeff.marks@test.com' }], } -const twelveMonthsAgo = getTwelveMonthsAgo() -const month = twelveMonthsAgo.getMonth() + 1 -const year = twelveMonthsAgo.getFullYear() - -const create = urls.companies.exportWins.create(company.id) - -const officerDetailsStep = create + '?step=officer_details' -const creditForThisWinStep = create + '?step=credit_for_this_win' -const customerDetailsStep = create + '?step=customer_details' -const winDetailsStep = create + '?step=win_details' -const supportProvidedStep = create + '?step=support_provided' -const checkBeforeSendingStep = create + '?step=check_before_sending' - -const formFields = { - officerDetails: { - heading: '[data-test="step-heading"]', - leadOfficer: '[data-test="field-lead_officer"]', - teamType: '[data-test="field-team_type"]', - hqTeam: '[data-test="field-hq_team"]', - teamMembers: '[data-test="field-team_members"]', - teamMembersHintText: '[data-test="hint-text"]', - }, - creditForThisWin: { - heading: '[data-test="step-heading"]', - hint: '[data-test="hint"]', - hintText: '[data-test="hint-text"]', - radiosBtns: '[data-test="field-credit_for_win"]', - radiosBtnYes: '[data-test="credit-for-win-yes"]', - radiosBtnNo: '[data-test="credit-for-win-no"]', - addAnother: '[data-test="field-addAnother"]', - contributingOfficer: '[data-test="field-contributing_officer_0"]', - teamType: '[data-test="field-team_type_0"]', - hqTeam: '[data-test="field-hq_team_0"]', - }, - customerDetails: { - heading: '[data-test="step-heading"]', - contacts: '[data-test="field-company_contacts"]', - contactHint: '[data-test="contact-hint"]', - location: '[data-test="field-customer_location"]', - potential: '[data-test="field-business_potential"]', - experience: '[data-test="field-export_experience"]', - }, - winDetails: { - heading: '[data-test="step-heading"]', - hint: '[data-test="hint"]', - country: '[data-test="field-country"]', - date: '[data-test="field-date"]', - dateMonth: '[data-test="date-month"]', - dateYear: '[data-test="date-year"]', - description: '[data-test="field-description"]', - nameOfCustomer: '[data-test="field-name_of_customer"]', - confidential: '[data-test="field-name_of_customer_confidential"]', - businessType: '[data-test="field-business_type"]', - winType: '[data-test="field-win_type"]', - goodsVsServices: '[data-test="field-goods_vs_services"]', - nameOfExport: '[data-test="field-name_of_export"]', - sector: '[data-test="field-sector"]', - exportWinCheckbox: '[data-test="checkbox-export_win"]', - businessSuccessCheckbox: '[data-test="checkbox-business_success_win"]', - odiCheckbox: '[data-test="checkbox-odi_win"]', - winTypeValuesExport: '[data-test="win-type-values-export_win"]', - winTypeValuesBusSupp: '[data-test="win-type-values-business_success_win"]', - winTypeValuesODI: '[data-test="win-type-values-odi_win"]', - totalExportValue: '[data-test="total-export-value"]', - }, - supportProvided: { - heading: '[data-test="step-heading"]', - hint: '[data-test="hint"]', - hvc: '[data-test="field-hvc"]', - typeOfSupport: '[data-test="field-type_of_support"]', - associatedProgramme: '[data-test="field-associated_programme"]', - personallyConfirmed: '[data-test="field-is_personally_confirmed"]', - lineManagerConfirmed: '[data-test="field-is_line_manager_confirmed"]', - }, - successPage: { - flash: '[data-test="flash"]', - heading: '[data-test="heading"]', - review: '[data-test="review"]', - email: '[data-test="email"]', - exportWinsLink: '[data-test="export-wins-link"]', - }, -} - -const clickContinueAndAssertUrl = (url) => { - clickContinueButton() - assertUrl(url) -} - -const populateWinWithValues = ({ alias, winType, values }) => - values.forEach((value, index) => - cy.get(alias).find(`[data-test="${winType}-${index}-input"]`).type(value) - ) - const createBreakdown = ({ type, values }) => values.map((value, index) => ({ type, @@ -209,1070 +119,254 @@ describe('Adding an export win', () => { }) }) - context('Officer details', () => { - const { officerDetails } = formFields - - beforeEach(() => cy.visit(officerDetailsStep)) - - it('should render an officer details heading', () => { - cy.get(officerDetails.heading).should('have.text', 'Officer details') - }) - - it('should render Lead Officer name label and a Typeahead', () => { - cy.get(officerDetails.leadOfficer).then((element) => { - assertFieldTypeahead({ - element, - label: 'Lead officer name', + // Disable testIsolation due to multi step form with lots of data. + context( + 'When the export win is created from scratch', + { testIsolation: false }, + () => { + before(() => cy.visit(officerDetailsStep)) + + it('should complete the entire export win user journey', () => { + fillOfficerDetails({ + leadOfficer: 'David', + teamType: 'Investment (ITFG or IG)', + hqTeam: 'DIT Education', }) - }) - }) - it('should render both Team Type and HQ Team', () => { - // The HQ Team field is not visible until a team has been selected - cy.get(officerDetails.hqTeam).should('not.exist') - cy.get(officerDetails.teamType).then((element) => { - assertFieldTypeahead({ - element, - label: 'Team type', - }) - }) - cy.get(officerDetails.teamType).find('input').as('teamTypeInput') - cy.get('@teamTypeInput').type('Inv') - cy.get('@teamTypeInput').type('{downarrow}{enter}{esc}') - // Now the user has selected a team the HQ Team field is visible - cy.get(officerDetails.hqTeam).should('exist') - cy.get(officerDetails.hqTeam).then((element) => { - assertFieldTypeahead({ - element, - label: 'HQ team, region or post', - }) - }) - }) + clickContinueAndAssertUrl(creditForThisWinStep) - it('should render a Team Members Typeahead and hint text', () => { - cy.get(officerDetails.teamMembers).then((element) => { - assertFieldTypeahead({ - element, - label: 'Team members (optional)', + fillCreditForThisWin({ + contributingOfficer: 'John', + teamType: 'Trade (TD or ST)', + hqTeam: 'Healthcare UK', }) - }) - cy.get(officerDetails.teamMembersHintText).should( - 'have.text', - 'You can add up to 5 team members. They will not be credited for the win but will be notified when this win is updated.' - ) - }) - it('should display validation error messages on mandatory fields', () => { - clickContinueButton() - assertErrorSummary(['Enter a lead officer', 'Select a team type']) - assertFieldError( - cy.get(officerDetails.leadOfficer), - 'Enter a lead officer', - false - ) - assertFieldError( - cy.get(officerDetails.teamType), - 'Select a team type', - false - ) - // Select a team to reveal the HQ Team field - cy.get(officerDetails.teamType).find('input').as('teamTypeInput') - cy.get('@teamTypeInput').type('Inv') - cy.get('@teamTypeInput').type('{downarrow}{enter}{esc}') - clickContinueButton() - assertErrorSummary([ - 'Enter a lead officer', - 'Select HQ team, region or post', - ]) - assertFieldError( - cy.get(officerDetails.hqTeam), - 'Select HQ team, region or post', - false - ) - }) - }) + clickContinueAndAssertUrl(customerDetailsStep) - context('Credit for this win', () => { - const { creditForThisWin } = formFields - - beforeEach(() => cy.visit(creditForThisWinStep)) - - it('should render a step heading', () => { - cy.get(creditForThisWin.heading).should( - 'have.text', - 'Credit for this win' - ) - }) - - it('should render a hint', () => { - cy.get(creditForThisWin.hint).should( - 'have.text', - 'Other teams that helped with this win should be added so they can be credited, this will not reduce your credit for this win.' - ) - }) - - it('should render two unselected radio buttons', () => { - cy.get(creditForThisWin.radiosBtns).then((element) => { - assertFieldRadiosWithLegend({ - element, - legend: 'Did any other teams help with this win?', - optionsCount: 2, - }) - }) - cy.get(creditForThisWin.radiosBtnYes) - .should('not.be.checked') - .parent() - .should('have.text', 'Yes') - cy.get(creditForThisWin.radiosBtnNo) - .should('not.be.checked') - .parent() - .should('have.text', 'No') - }) - - it('should go to the next step when selecting "No" and then "Continue"', () => { - cy.get(creditForThisWin.radiosBtnNo).check() - clickContinueAndAssertUrl(customerDetailsStep) - }) - - it('should render a legend and hint text', () => { - cy.get(creditForThisWin.radiosBtnYes).check() - cy.get(creditForThisWin.addAnother) - .find('legend') - .eq(0) - .should('have.text', 'Contributing advisers') - cy.get(creditForThisWin.hintText).should( - 'have.text', - 'Up to 5 advisers can be added.' - ) - }) - - it('should render a Typeahead for the contributing officer', () => { - cy.get(creditForThisWin.radiosBtnYes).check() - cy.get(creditForThisWin.contributingOfficer).then((element) => { - assertFieldTypeahead({ - element, - label: 'Contributing officer', - }) - }) - }) - - it('should render a Typeahead for the team type', () => { - cy.get(creditForThisWin.radiosBtnYes).check() - cy.get(creditForThisWin.teamType).then((element) => { - assertFieldTypeahead({ - element, - label: 'Team type', + fillCustomerDetails({ + contact: 'Joseph Barker', + location: 'Scotland', + potential: 'The company is a Medium Sized Business', + experience: 'Never exported', }) - }) - }) - it('should render an "Add another" button', () => { - cy.get(creditForThisWin.radiosBtnYes).check() - cy.get(creditForThisWin.addAnother).should('exist') - }) - - it('should display validation error messages on mandatory fields', () => { - clickContinueButton() - // Assert Yes and No radio buttons - assertErrorSummary(['Select Yes or No']) - assertFieldError( - cy.get(creditForThisWin.radiosBtns), - 'Select Yes or No', - true - ) - cy.get(creditForThisWin.radiosBtnYes).check() - clickContinueButton() - // Assert Contributing officer and Team type - assertErrorSummary(['Enter a contributing officer', 'Enter a team type']) - assertFieldError( - cy.get(creditForThisWin.contributingOfficer), - 'Enter a contributing officer', - false - ) - assertFieldError( - cy.get(creditForThisWin.teamType), - 'Enter a team type', - false - ) - // Select a team type to render the HQ team, region or post field - cy.get(creditForThisWin.teamType).find('input').as('teamTypeInput') - cy.get('@teamTypeInput').type('Inv') - cy.get('@teamTypeInput').type('{downarrow}{enter}{esc}') - clickContinueButton() - // Assert HQ team, region or post - assertErrorSummary([ - 'Enter a contributing officer', - 'Enter a HQ team, region or post', - ]) - assertFieldError( - cy.get(creditForThisWin.hqTeam), - 'Enter a HQ team, region or post', - false - ) - }) - }) - - context('Customer details', () => { - const { customerDetails } = formFields - - beforeEach(() => cy.visit(customerDetailsStep)) - - it('should render a step heading', () => { - cy.get(customerDetails.heading).should('have.text', 'Customer details') - }) + clickContinueAndAssertUrl(winDetailsStep) - it('should render a contact hint', () => { - cy.get(customerDetails.contactHint).should( - 'have.text', - 'To select a customer contact name, it must have already been added to Data Hub. If not listed, go to the company page to add them.' - ) - }) - - it('should render Company contacts label and a Typeahead', () => { - cy.get(customerDetails.contacts).then((element) => { - assertFieldTypeahead({ - element, - label: 'Company contacts', - hint: 'This contact will be emailed to approve the win.', - }) - }) - }) - - it('should render HQ location label and a Typeahead', () => { - cy.get(customerDetails.location).then((element) => { - assertFieldTypeahead({ - element, - label: 'HQ location', - }) - }) - }) - - it('should render Export potential label and a Typeahead', () => { - cy.get(customerDetails.potential).then((element) => { - assertFieldTypeahead({ - element, - label: 'Export potential', - }) - }) - }) - - it('should render Export potential label and a Typeahead', () => { - cy.get(customerDetails.experience).then((element) => { - assertFieldTypeahead({ - element, - label: 'Export experience', - hint: 'Your customer will be asked to confirm this information.', + fillWinDetails({ + country: 'United states', + dateMonth: month, + dateYear: year, + description: 'Foo bar baz', + nameOfCustomer: 'David French', + isConfidential: true, + businessType: 'Contract', + exportValues: ['1000000', '1000000', '1000000', '1000000', '1000000'], + businessSuccessValues: [ + '2000000', + '2000000', + '2000000', + '2000000', + '2000000', + ], + odiValues: ['3000000', '3000000', '3000000', '3000000', '3000000'], + goodsVsServices: 'goods', + nameOfExport: 'Biscuits', + sector: 'Advanced Engineering', }) - }) - }) - - it('should display validation error messages on mandatory fields', () => { - clickContinueButton() - assertErrorSummary([ - 'Select a company contact', - 'Select HQ location', - 'Select export potential', - 'Select export experience', - ]) - assertFieldError( - cy.get(customerDetails.contacts), - 'Select a company contact', - true - ) - assertFieldError( - cy.get(customerDetails.location), - 'Select HQ location', - false - ) - assertFieldError( - cy.get(customerDetails.potential), - 'Select export potential', - false - ) - assertFieldError( - cy.get(customerDetails.experience), - 'Select export experience', - true - ) - }) - }) - - context('Win details', () => { - const { winDetails } = formFields - - beforeEach(() => cy.visit(winDetailsStep)) - it('should render a step heading', () => { - cy.get(winDetails.heading).should('have.text', 'Win details') - }) - - it('should render a hint', () => { - cy.get(winDetails.hint).should( - 'have.text', - 'The customer will be asked to confirm this information.' - ) - }) - - it('should render Destination country label and a Typeahead', () => { - cy.get(winDetails.country).then((element) => { - assertFieldTypeahead({ - element, - label: 'Destination country', - }) - }) - }) + clickContinueAndAssertUrl(supportProvidedStep) - it('should render the Win date', () => { - cy.get(winDetails.date).then((element) => { - // Both Month and Year labels are tested within the assertion - assertFieldDateShort({ - element, - label: 'Date won', - hint: `For example ${month} ${year}, date of win must be in the last 12 months.`, + fillSupportProvided({ + hvc: 'Aus', + typeOfSupport: 'Mar', + associatedProgramme: 'Aft', + personallyConfirmed: true, + lineManagerConfirmed: true, }) - }) - }) - it('should render Summary of the support given', () => { - cy.get(winDetails.description).then((element) => { - assertFieldTextarea({ - element, - label: 'Summary of the support given', - hint: 'Outline what had the most impact or would be memorable to the customer in less than 100 words.', - wordCount: 'You have 100 words remaining.', - }) + clickContinueAndAssertUrl(checkBeforeSendingStep) }) - }) - it('should renderer Overseas customer', () => { - cy.get(winDetails.nameOfCustomer).then((element) => { - assertFieldInput({ - element, - label: 'Overseas customer', - placeholder: 'Add name', - }) + it('should render a step heading', () => { + cy.get('[data-test="step-heading"]').should( + 'have.text', + 'Check before sending' + ) }) - }) - it('should render a Confidential checkbox', () => { - assertFieldCheckboxes({ - element: winDetails.confidential, - hint: 'Check this box if your customer has asked for this not to be public (optional).', - options: [ - { - label: 'Confidential', - checked: false, + it('should render an officer details table', () => { + assertSummaryTable({ + dataTest: 'officer-details', + heading: 'Officer details', + showEditLink: false, + content: { + 'Lead officer name': 'David Meyer', + 'Team type': 'Investment (ITFG or IG)', + 'HQ Team, region or post': 'DIT Education', + 'Team members (optional)': 'Not set', }, - ], - }) - }) - - it('should renderer a type of business deal', () => { - cy.get(winDetails.businessType).then((element) => { - assertFieldInput({ - element, - label: 'Type of business deal', - hint: 'For example: export sales, contract, order, distributor, tender / competition win, joint venture, outward investment.', - placeholder: 'Enter a type of business deal', }) }) - }) - - it('should render Type of win ', () => { - assertFieldCheckboxes({ - element: winDetails.winType, - legend: 'Type of win', - options: [ - { - label: 'Export', - checked: false, - }, - { - label: 'Business success', - checked: false, - }, - { - label: 'Outward Direct Investment (ODI)', - checked: false, - }, - ], - }) - }) - - it('should render the WinTypeValues component for each win type', () => { - cy.get(winDetails.winType).as('winType') - - // Export win - cy.get('@winType') - .find(winDetails.winTypeValuesExport) - .should('not.exist') - .get('@winType') - .find(winDetails.exportWinCheckbox) - .check() - .next() - .get(winDetails.winTypeValuesExport) - .should('exist') - - // Business type - cy.get('@winType') - .find(winDetails.winTypeValuesBusSupp) - .should('not.exist') - .get('@winType') - .find(winDetails.businessSuccessCheckbox) - .check() - .next() - .get(winDetails.winTypeValuesBusSupp) - .should('exist') - - // ODI - cy.get('@winType') - .find(winDetails.winTypeValuesODI) - .should('not.exist') - .get('@winType') - .find(winDetails.odiCheckbox) - .check() - .next() - .get(winDetails.winTypeValuesODI) - .should('exist') - }) - - it('should render the total export value across all 3 win types', () => { - cy.get(winDetails.winType).as('winType') - - // Check all 3 win types to render 15 (3 x 5) inputs - cy.get('@winType').find(winDetails.exportWinCheckbox).check() - cy.get('@winType').find(winDetails.businessSuccessCheckbox).check() - cy.get('@winType').find(winDetails.odiCheckbox).check() - - populateWinWithValues({ - alias: '@winType', - winType: 'export-win', - values: ['1000000', '1000000', '1000000', '1000000', '1000000'], // 5M - }) - - populateWinWithValues({ - alias: '@winType', - winType: 'business-success-win', - values: ['2000000', '2000000', '2000000', '2000000', '2000000'], // 10M - }) - - populateWinWithValues({ - alias: '@winType', - winType: 'odi-win', - values: ['3000000', '3000000', '3000000', '3000000', '3000000'], // 15M - }) - // Assert the total export value - cy.get(winDetails.totalExportValue).should( - 'have.text', - 'Total export value: £30,000,000' // 5M + 10M + 15M - ) - }) - - it('should render Goods and Services', () => { - assertFieldCheckboxes({ - element: winDetails.goodsVsServices, - legend: 'What does the value relate to?', - hint: 'Select all that apply.', - options: [ - { - label: 'Goods', - checked: false, - }, - { - label: 'Services', - checked: false, + it('should render a credit for this win table', () => { + assertSummaryTable({ + dataTest: 'credit-for-this-win', + heading: 'Credit for this win', + showEditLink: false, + content: { + 'Did any other teams help with this win?': + 'YesContributing teams and advisersContributing officer: John SmithTeam ' + + 'type: Trade (TD or ST)HQ team, region or post: Healthcare UK', }, - ], - }) - }) - - it('should renderer name of goods or services', () => { - cy.get(winDetails.nameOfExport).then((element) => { - assertFieldInput({ - element, - label: 'Name of goods or services', - hint: "For instance 'shortbread biscuits'.", - placeholder: 'Enter a name for goods or services', }) }) - }) - it('should render a sector label and typeahead', () => { - cy.get(winDetails.sector).then((element) => { - assertFieldTypeahead({ - element, - label: 'Sector', + it('should render a customer details table', () => { + assertSummaryTable({ + dataTest: 'customer-details', + heading: 'Customer details', + showEditLink: false, + content: { + 'Contact name': 'Joseph Barker', + 'HQ location': 'Scotland', + 'Export potential': 'The company is a Medium Sized Business', + 'Export experience': 'Never exported', + }, }) }) - }) - - it('should display validation error messages on mandatory fields', () => { - clickContinueButton() - assertErrorSummary([ - 'Choose a destination country', - 'Enter the win date', - 'Enter a summary', - 'Enter the name of the overseas customer', - 'Enter the type of business deal', - 'Choose at least one type of win', - 'Select at least one option', - 'Enter the name of goods or services', - 'Enter a sector', - ]) - assertFieldError( - cy.get(winDetails.country), - 'Choose a destination country', - false - ) - assertFieldError(cy.get(winDetails.date), 'Enter the win date', true) - assertFieldError(cy.get(winDetails.description), 'Enter a summary', true) - assertFieldError( - cy.get(winDetails.nameOfCustomer), - 'Enter the name of the overseas customer', - false - ) - assertFieldError( - cy.get(winDetails.businessType), - 'Enter the type of business deal', - true - ) - assertFieldError( - cy.get(winDetails.winType), - 'Choose at least one type of win', - true - ) - // We can't use assertFieldError here as it picks up the wrong span - cy.get(winDetails.goodsVsServices).should( - 'contain', - 'Select at least one option' - ) - assertFieldError( - cy.get(winDetails.nameOfExport), - 'Enter the name of goods or services', - true - ) - assertFieldError(cy.get(winDetails.sector), 'Enter a sector', false) - }) - it('should display a validation error message when the win date is in the future', () => { - const today = new Date() - // Indexing starts at zero (0 - 11) so we have to increment by 2 for next month - const month = today.getMonth() + 2 - const year = today.getFullYear() - cy.get(winDetails.dateMonth).type(month) - cy.get(winDetails.dateYear).type(year) - clickContinueButton() - assertFieldError( - cy.get(winDetails.date), - 'Date must be in the last 12 months', - true - ) - }) - - it('should display a validation error message when the win date is greater than twelve months ago', () => { - const today = new Date() - // Indexing starts at zero (0 - 11) so no need to decrement by 1 - const month = today.getMonth() - const year = today.getFullYear() - 1 - cy.get(winDetails.dateMonth).type(month) - cy.get(winDetails.dateYear).type(year) - clickContinueButton() - assertFieldError( - cy.get(winDetails.date), - 'Date must be in the last 12 months', - true - ) - }) - }) - - context('Support provided', () => { - const { supportProvided } = formFields - - beforeEach(() => cy.visit(supportProvidedStep)) - - it('should render a step heading', () => { - cy.get(supportProvided.heading).should('have.text', 'Support given') - }) - - it('should render a hint', () => { - cy.get(supportProvided.hint).should( - 'have.text', - 'Did any of these help the customer achieve this win?' - ) - }) - - it('should render a typeahead for high value campaign', () => { - cy.get(supportProvided.hvc).then((element) => { - assertFieldTypeahead({ - element, - label: 'High Value Campaign (HVC) code (optional)', - hint: 'If the win was linked to a HVC, select the appropriate campaign.', + it('should render a win details table', () => { + assertSummaryTable({ + dataTest: 'win-details', + heading: 'Win details', + showEditLink: false, + content: { + Destination: 'United States', + 'Date won': `${month}/${year}`, + 'Summary of support given': 'Foo bar baz', + 'Overseas customer': 'David French', + Confidential: 'Yes', + 'Type of win': 'Contract', + 'Export value': '£5,000,000 over 5 years', + 'Business success value': '£10,000,000 over 5 years', + 'Outward Direct Investment (ODI) value': '£15,000,000 over 5 years', + 'Total value': '£30,000,000 over 5 years', + 'What does the value relate to?': 'Goods', + 'Type of goods or services': 'Biscuits', + Sector: 'Advanced Engineering', + }, }) }) - }) - it('should render a support given typeahead', () => { - cy.get(supportProvided.typeOfSupport).then((element) => { - assertFieldTypeahead({ - element, - label: 'What type of support was given?', - hint: 'You can add up to 5 types of support.', + it('should render a support given table', () => { + assertSummaryTable({ + dataTest: 'support-given', + heading: 'Support given', + showEditLink: false, + content: { + 'HVC code': 'Australia Consumer Goods & Retail: E004', + 'What type of support was given?': + 'Market entry advice and support – DIT/FCO in UK', + 'Was there a DBT campaign or event that contributed to this win?': + 'Afterburner', + }, }) }) - }) - it('should render an associated programme typeahead', () => { - cy.get(supportProvided.associatedProgramme).then((element) => { - assertFieldTypeahead({ - element, - label: - 'Was there a DBT campaign or event that contributed to this win?', - hint: 'You can add up to 5 campaigns or events.', + it('should render warning text', () => { + cy.get('[data-test="warning-text"]').should( + 'contain', + 'This information will be sent to joseph.barker@test.com so they can confirm the export win.' + ) + }) + + it('should POST to the API and redirect to the success page', () => { + cy.get('[data-test="confirm-and-send-to-customer"]').should( + 'have.text', + 'Confirm and send to customer' + ) + cy.get('[data-test="confirm-and-send-to-customer"]').click() + cy.wait('@apiPostExportWin').then(({ request }) => { + expect(omit(request.body, '_csrf')).to.deep.equal({ + lead_officer: '100', + team_type: '42bdaf2e-ae19-4589-9840-5dbb67b50add', + hq_team: '300', + team_members: [], + advisers: [ + { + adviser: '101', + hq_team: '301', + team_type: '201', + }, + ], + company_contacts: ['000'], + customer_location: '8c4cd12a-6095-e211-a939-e4115bead28a', + business_potential: 'e4d74957-60a4-4eab-a17b-d4c7b792ad25', + export_experience: '051a0362-d1a9-41c0-8a58-3171e5f59a8e', + country: '81756b9a-5d95-e211-a939-e4115bead28a', + date: `${year}-${month}-01`, + description: 'Foo bar baz', + name_of_customer: 'David French', + name_of_customer_confidential: true, + business_type: 'Contract', + breakdowns: [ + ...createBreakdown({ + type: winTypeId.EXPORT, + values: ['1000000', '1000000', '1000000', '1000000', '1000000'], + }), + ...createBreakdown({ + type: winTypeId.BUSINESS_SUCCESS, + values: ['2000000', '2000000', '2000000', '2000000', '2000000'], + }), + ...createBreakdown({ + type: winTypeId.ODI, + values: ['3000000', '3000000', '3000000', '3000000', '3000000'], + }), + ], + goods_vs_services: '456e951d-a633-4f21-afde-d41381407efe', + name_of_export: 'Biscuits', + sector: 'af959812-6095-e211-a939-e4115bead28a', + hvc: '400', + type_of_support: ['500'], + associated_programme: ['600'], + is_personally_confirmed: true, + is_line_manager_confirmed: true, + total_expected_export_value: 5000000, + total_expected_non_export_value: 10000000, + total_expected_odi_value: 15000000, + company: company.id, + adviser: '7d19d407-9aec-4d06-b190-d3f404627f21', + }) }) - }) - }) - - it('should render personally confirmed checkbox', () => { - assertFieldCheckboxes({ - element: supportProvided.personallyConfirmed, - options: [ - { - label: 'I confirm that this information is complete and accurate.', - checked: false, - }, - ], - }) - }) - - it('should render a manager confirmed checkbox', () => { - assertFieldCheckboxes({ - element: supportProvided.lineManagerConfirmed, - options: [ - { - label: - 'My line manager has agreed that this win should be recorded.', - checked: false, - }, - ], - }) - }) - - it('should display validation error messages on mandatory fields', () => { - clickContinueButton() - assertErrorSummary([ - 'Select at least one type of support', - 'Select at least one type of DBT campaign or event', - 'Confirm that this information is complete and accurate', - 'Confirm your line manager has agreed that this win should be recorded', - ]) - assertFieldError( - cy.get(supportProvided.typeOfSupport), - 'Select at least one type of support', - true - ) - assertFieldError( - cy.get(supportProvided.associatedProgramme), - 'Select at least one type of DBT campaign or event', - true - ) - cy.get(supportProvided.personallyConfirmed).should( - 'contain', - 'Confirm that this information is complete and accurate' - ) - cy.get(supportProvided.lineManagerConfirmed).should( - 'contain', - 'Confirm your line manager has agreed that this win should be recorded' - ) - }) - }) - - // Disable testIsolation due to multi step form with lots of data. - context('Check before sending', { testIsolation: false }, () => { - const { - officerDetails, - creditForThisWin, - customerDetails, - winDetails, - supportProvided, - } = formFields - - before(() => cy.visit(officerDetailsStep)) - - it('should complete the entire export win user journey', () => { - // Officer details - cy.get(officerDetails.leadOfficer).selectTypeaheadOption('David') - cy.get(officerDetails.teamType).selectTypeaheadOption( - 'Investment (ITFG or IG)' - ) - cy.get(officerDetails.hqTeam).selectTypeaheadOption('DIT Education') - - clickContinueAndAssertUrl(creditForThisWinStep) - - // Credit for this win - cy.get(creditForThisWin.radiosBtnYes).check() - cy.get(creditForThisWin.contributingOfficer).selectTypeaheadOption('John') - cy.get(creditForThisWin.teamType).selectTypeaheadOption( - 'Trade (TD or ST)' - ) - cy.get(creditForThisWin.hqTeam).selectTypeaheadOption('Healthcare UK') - - clickContinueAndAssertUrl(customerDetailsStep) - - // Customer details - cy.get(customerDetails.contacts).selectTypeaheadOption('Joseph Barker') - cy.get(customerDetails.location).selectTypeaheadOption('Scotland') - cy.get(customerDetails.potential).selectTypeaheadOption( - 'The company is a Medium Sized Business' - ) - cy.get(customerDetails.experience).selectTypeaheadOption('Never exported') - - clickContinueAndAssertUrl(winDetailsStep) - - // Win details - cy.get(winDetails.country).selectTypeaheadOption('United states') - cy.get(winDetails.dateMonth).type(month) - cy.get(winDetails.dateYear).type(year) - cy.get(winDetails.description).find('textarea').type('Foo bar baz') - cy.get(winDetails.nameOfCustomer).find('input').type('David French') - cy.get(winDetails.confidential).find('input').check() - cy.get(winDetails.businessType).find('input').type('Contract') - - cy.get(winDetails.winType).as('winType') - - // Check all 3 win types to render 15 (3 x 5) inputs - cy.get('@winType').find(winDetails.exportWinCheckbox).check() - cy.get('@winType').find(winDetails.businessSuccessCheckbox).check() - cy.get('@winType').find(winDetails.odiCheckbox).check() - - populateWinWithValues({ - alias: '@winType', - winType: 'export-win', - values: ['1000000', '1000000', '1000000', '1000000', '1000000'], // 5M - }) - - populateWinWithValues({ - alias: '@winType', - winType: 'business-success-win', - values: ['2000000', '2000000', '2000000', '2000000', '2000000'], // 10M - }) - - populateWinWithValues({ - alias: '@winType', - winType: 'odi-win', - values: ['3000000', '3000000', '3000000', '3000000', '3000000'], // 15M - }) - - cy.get(winDetails.goodsVsServices).find('input').eq(0).check() // Goods - cy.get(winDetails.nameOfExport).find('input').type('Biscuits') - cy.get(winDetails.sector).selectTypeaheadOption('Advanced Engineering') - - clickContinueAndAssertUrl(supportProvidedStep) - - // Suppport Provided - cy.get(supportProvided.hvc).selectTypeaheadOption('Aus') - cy.get(supportProvided.typeOfSupport).selectTypeaheadOption('Mar') - cy.get(supportProvided.associatedProgramme).selectTypeaheadOption('Aft') - cy.get(supportProvided.personallyConfirmed) - .find('[data-test="checkbox-yes"]') - .check() - cy.get(supportProvided.lineManagerConfirmed) - .find('[data-test="checkbox-yes"]') - .check() - - clickContinueAndAssertUrl(checkBeforeSendingStep) - }) - - it('should render a step heading', () => { - cy.get('[data-test="step-heading"]').should( - 'have.text', - 'Check before sending' - ) - }) - - it('should render an officer details table', () => { - assertSummaryTable({ - dataTest: 'officer-details', - heading: 'Officer details', - showEditLink: false, - content: { - 'Lead officer name': 'David Meyer', - 'Team type': 'Investment (ITFG or IG)', - 'HQ Team, region or post': 'DIT Education', - 'Team members (optional)': 'Not set', - }, - }) - }) - - it('should render a credit for this win table', () => { - assertSummaryTable({ - dataTest: 'credit-for-this-win', - heading: 'Credit for this win', - showEditLink: false, - content: { - 'Did any other teams help with this win?': - 'YesContributing teams and advisersContributing officer: John SmithTeam ' + - 'type: Trade (TD or ST)HQ team, region or post: Healthcare UK', - }, - }) - }) - - it('should render a customer details table', () => { - assertSummaryTable({ - dataTest: 'customer-details', - heading: 'Customer details', - showEditLink: false, - content: { - 'Contact name': 'Joseph Barker', - 'HQ location': 'Scotland', - 'Export potential': 'The company is a Medium Sized Business', - 'Export experience': 'Never exported', - }, - }) - }) - - it('should render a win details table', () => { - assertSummaryTable({ - dataTest: 'win-details', - heading: 'Win details', - showEditLink: false, - content: { - Destination: 'United States', - 'Date won': `${month}/${year}`, - 'Summary of support given': 'Foo bar baz', - 'Overseas customer': 'David French', - Confidential: 'Yes', - 'Type of win': 'Contract', - 'Export value': '£5,000,000 over 5 years', - 'Business success value': '£10,000,000 over 5 years', - 'Outward Direct Investment (ODI) value': '£15,000,000 over 5 years', - 'Total value': '£30,000,000 over 5 years', - 'What does the value relate to?': 'Goods', - 'Type of goods or services': 'Biscuits', - Sector: 'Advanced Engineering', - }, - }) - }) - - it('should render a support given table', () => { - assertSummaryTable({ - dataTest: 'support-given', - heading: 'Support given', - showEditLink: false, - content: { - 'HVC code': 'Australia Consumer Goods & Retail: E004', - 'What type of support was given?': - 'Market entry advice and support – DIT/FCO in UK', - 'Was there a DBT campaign or event that contributed to this win?': - 'Afterburner', - }, - }) - }) - - it('should render warning text', () => { - cy.get('[data-test="warning-text"]').should( - 'contain', - 'This information will be sent to joseph.barker@test.com so they can confirm the export win.' - ) - }) - - it('should POST to the API and redirect to the success page', () => { - const successPageRegex = /\/exportwins\/[^/]+\/success/ - - cy.get('[data-test="confirm-and-send-to-customer"]').should( - 'have.text', - 'Confirm and send to customer' - ) - cy.get('[data-test="confirm-and-send-to-customer"]').click() - cy.wait('@apiPostExportWin').then(({ request }) => { - expect(omit(request.body, '_csrf')).to.deep.equal({ - lead_officer: '100', - team_type: '42bdaf2e-ae19-4589-9840-5dbb67b50add', - hq_team: '300', - team_members: [], - advisers: [ - { - adviser: '101', - hq_team: '301', - team_type: '201', - }, - ], - company_contacts: ['000'], - customer_location: '8c4cd12a-6095-e211-a939-e4115bead28a', - business_potential: 'e4d74957-60a4-4eab-a17b-d4c7b792ad25', - export_experience: '051a0362-d1a9-41c0-8a58-3171e5f59a8e', - country: '81756b9a-5d95-e211-a939-e4115bead28a', - date: `${year}-${month}-01`, - description: 'Foo bar baz', - name_of_customer: 'David French', - name_of_customer_confidential: true, - business_type: 'Contract', - breakdowns: [ - ...createBreakdown({ - type: winTypeId.EXPORT, - values: ['1000000', '1000000', '1000000', '1000000', '1000000'], - }), - ...createBreakdown({ - type: winTypeId.BUSINESS_SUCCESS, - values: ['2000000', '2000000', '2000000', '2000000', '2000000'], - }), - ...createBreakdown({ - type: winTypeId.ODI, - values: ['3000000', '3000000', '3000000', '3000000', '3000000'], - }), - ], - goods_vs_services: '456e951d-a633-4f21-afde-d41381407efe', - name_of_export: 'Biscuits', - sector: 'af959812-6095-e211-a939-e4115bead28a', - hvc: '400', - type_of_support: ['500'], - associated_programme: ['600'], - is_personally_confirmed: true, - is_line_manager_confirmed: true, - total_expected_export_value: 5000000, - total_expected_non_export_value: 10000000, - total_expected_odi_value: 15000000, - company: company.id, - adviser: '7d19d407-9aec-4d06-b190-d3f404627f21', + cy.location().should(({ pathname }) => { + expect(pathname).to.match(successPageRegex) }) }) - cy.location().should(({ pathname }) => { - expect(pathname).to.match(successPageRegex) - }) - }) - - it('should render a success page', () => { - const { successPage } = formFields - cy.get(successPage.flash).should( - 'have.text', - 'The export win Rolls Reece Cars to Dubai has been sent to jeff.marks@test.com for review and confirmation.' - ) + it('should render a success page', () => { + const { successPage } = formFields - cy.get(successPage.heading).should('have.text', 'What happens next?') - - cy.get(successPage.review).should( - 'have.text', - 'The customer will review the export win and have the option to provide feedback.' - ) - - cy.get(successPage.email).should( - 'have.text', - 'You will be sent an email once the customer has responded.' - ) - - cy.get(successPage.exportWinsLink) - .should('have.text', 'Export wins') - .should('have.attr', 'href', '/exportwins') - }) - }) + cy.get(successPage.flash).should( + 'have.text', + 'The export win Rolls Reece Cars to Dubai has been sent to jeff.marks@test.com for review and confirmation.' + ) - context('Pre-propulating the form with an export project', () => { - const { officerDetails, customerDetails, winDetails } = formFields + cy.get(successPage.heading).should('have.text', 'What happens next?') - const today = new Date() - const exportProject = exportFaker() - const create = urls.companies.exportWins.createFromExport( - company.id, - exportProject.id - ) + cy.get(successPage.review).should( + 'have.text', + 'The customer will review the export win and have the option to provide feedback.' + ) - const officerDetailsStep = create + '?step=officer_details' - const customerDetailsStep = create + '?step=customer_details' - const winDetailsStep = create + '?step=win_details' + cy.get(successPage.email).should( + 'have.text', + 'You will be sent an email once the customer has responded.' + ) - const interceptExportApiCall = (overrides = {}) => { - cy.intercept('GET', `/api-proxy/v4/export/${exportProject.id}`, { - ...exportProject, - ...overrides, + cy.get(successPage.exportWinsLink) + .should('have.text', 'Export wins') + .should('have.attr', 'href', '/exportwins') }) } - - beforeEach(() => { - cy.setUserFeatureGroups(['export-wins']) - interceptExportApiCall() - }) - - it('should pre-populate the lead officer field', () => { - cy.visit(officerDetailsStep) - cy.get(officerDetails.leadOfficer) - .find('input') - .should('have.value', exportProject.owner.name) - }) - - it('should pre-populate the team members field', () => { - cy.visit(officerDetailsStep) - assertTypeaheadValues( - '[data-test="field-team_members"]', - exportProject.team_members.map(({ name }) => name) - ) - }) - - it('should pre-populate the exporter experience field', () => { - cy.visit(customerDetailsStep) - assertSingleTypeaheadOptionSelected({ - element: customerDetails.experience, - expectedOption: exportProject.exporter_experience.name, - }) - }) - - it('should pre-populate the export destination country', () => { - cy.visit(winDetailsStep) - assertSingleTypeaheadOptionSelected({ - element: winDetails.country, - expectedOption: exportProject.destination_country.name, - }) - }) - - it('should not pre-populate the win date when the estimated win date is greater than twelve months ago', () => { - const thirteenMonthsAgo = new Date( - today.getFullYear() - 1, - today.getMonth() - 1, - 1 - ) - - interceptExportApiCall({ - estimated_win_date: thirteenMonthsAgo.toISOString(), - }) - - cy.visit(winDetailsStep) - cy.get(winDetails.dateMonth).should('have.value', '') - cy.get(winDetails.dateYear).should('have.value', '') - }) - - it('should pre-populate the win date when the estimated date is less than or equal to 12 months ago', () => { - const date = getRandomDate({ - start: twelveMonthsAgo, - end: today, - }) - - interceptExportApiCall({ - estimated_win_date: date.toISOString(), - }) - - cy.visit(winDetailsStep) - cy.get(winDetails.dateYear).should('have.value', date.getFullYear()) - cy.get(winDetails.dateMonth).should('have.value', date.getMonth() + 1) - }) - - it('should not pre-populate the win date when the estimated date is in the future', () => { - const nextMonth = new Date(today.getFullYear(), today.getMonth() + 1) - - interceptExportApiCall({ - estimated_win_date: nextMonth.toISOString(), - }) - - cy.visit(winDetailsStep) - cy.get(winDetails.dateYear).should('have.value', '') - cy.get(winDetails.dateMonth).should('have.value', '') - }) - - it('should pre-populate the sector', () => { - cy.visit(winDetailsStep) - assertSingleTypeaheadOptionSelected({ - element: winDetails.sector, - expectedOption: exportProject.sector.name, - }) - }) - }) + ) }) diff --git a/test/functional/cypress/specs/export-win/constants.js b/test/functional/cypress/specs/export-win/constants.js new file mode 100644 index 00000000000..36c6e10d745 --- /dev/null +++ b/test/functional/cypress/specs/export-win/constants.js @@ -0,0 +1,88 @@ +import urls from '../../../../../src/lib/urls' + +import { companyFaker } from '../../fakers/companies' + +export const company = companyFaker({ + name: 'Advanced Mini Devices', +}) + +export const successPageRegex = /\/exportwins\/[^/]+\/success/ + +// All form step URLs (creating an export win from scratch) +const create = urls.companies.exportWins.create(company.id) +export const officerDetailsStep = `${create}?step=officer_details` +export const creditForThisWinStep = `${create}?step=credit_for_this_win` +export const customerDetailsStep = `${create}?step=customer_details` +export const winDetailsStep = `${create}?step=win_details` +export const supportProvidedStep = `${create}?step=support_provided` +export const checkBeforeSendingStep = `${create}?step=check_before_sending` + +export const formFields = { + officerDetails: { + heading: '[data-test="step-heading"]', + leadOfficer: '[data-test="field-lead_officer"]', + teamType: '[data-test="field-team_type"]', + hqTeam: '[data-test="field-hq_team"]', + teamMembers: '[data-test="field-team_members"]', + teamMembersHintText: '[data-test="hint-text"]', + }, + creditForThisWin: { + heading: '[data-test="step-heading"]', + hint: '[data-test="hint"]', + hintText: '[data-test="hint-text"]', + radiosBtns: '[data-test="field-credit_for_win"]', + radiosBtnYes: '[data-test="credit-for-win-yes"]', + radiosBtnNo: '[data-test="credit-for-win-no"]', + addAnother: '[data-test="field-addAnother"]', + contributingOfficer: '[data-test="field-contributing_officer_0"]', + teamType: '[data-test="field-team_type_0"]', + hqTeam: '[data-test="field-hq_team_0"]', + }, + customerDetails: { + heading: '[data-test="step-heading"]', + contacts: '[data-test="field-company_contacts"]', + contactHint: '[data-test="contact-hint"]', + location: '[data-test="field-customer_location"]', + potential: '[data-test="field-business_potential"]', + experience: '[data-test="field-export_experience"]', + }, + winDetails: { + heading: '[data-test="step-heading"]', + hint: '[data-test="hint"]', + country: '[data-test="field-country"]', + date: '[data-test="field-date"]', + dateMonth: '[data-test="date-month"]', + dateYear: '[data-test="date-year"]', + description: '[data-test="field-description"]', + nameOfCustomer: '[data-test="field-name_of_customer"]', + confidential: '[data-test="field-name_of_customer_confidential"]', + businessType: '[data-test="field-business_type"]', + winType: '[data-test="field-win_type"]', + goodsVsServices: '[data-test="field-goods_vs_services"]', + nameOfExport: '[data-test="field-name_of_export"]', + sector: '[data-test="field-sector"]', + exportWinCheckbox: '[data-test="checkbox-export_win"]', + businessSuccessCheckbox: '[data-test="checkbox-business_success_win"]', + odiCheckbox: '[data-test="checkbox-odi_win"]', + winTypeValuesExport: '[data-test="win-type-values-export_win"]', + winTypeValuesBusSupp: '[data-test="win-type-values-business_success_win"]', + winTypeValuesODI: '[data-test="win-type-values-odi_win"]', + totalExportValue: '[data-test="total-export-value"]', + }, + supportProvided: { + heading: '[data-test="step-heading"]', + hint: '[data-test="hint"]', + hvc: '[data-test="field-hvc"]', + typeOfSupport: '[data-test="field-type_of_support"]', + associatedProgramme: '[data-test="field-associated_programme"]', + personallyConfirmed: '[data-test="field-is_personally_confirmed"]', + lineManagerConfirmed: '[data-test="field-is_line_manager_confirmed"]', + }, + successPage: { + flash: '[data-test="flash"]', + heading: '[data-test="heading"]', + review: '[data-test="review"]', + email: '[data-test="email"]', + exportWinsLink: '[data-test="export-wins-link"]', + }, +} diff --git a/test/functional/cypress/specs/export-win/credit-for-this-win-spec.js b/test/functional/cypress/specs/export-win/credit-for-this-win-spec.js new file mode 100644 index 00000000000..d4e503252bc --- /dev/null +++ b/test/functional/cypress/specs/export-win/credit-for-this-win-spec.js @@ -0,0 +1,137 @@ +import { + formFields, + customerDetailsStep, + creditForThisWinStep, +} from './constants' + +import { + assertUrl, + assertFieldError, + assertErrorSummary, + assertFieldTypeahead, + assertFieldRadiosStrict, +} from '../../support/assertions' + +import { clickContinueButton } from '../../support/actions' + +describe('Credit for this win', () => { + const { creditForThisWin } = formFields + + beforeEach(() => { + cy.intercept('GET', '/api-proxy/v4/metadata/hq-team-region-or-post?*', [ + { name: 'DIT Education' }, + { name: 'Healthcare UK' }, + ]) + cy.visit(creditForThisWinStep) + }) + + it('should render a step heading', () => { + cy.get(creditForThisWin.heading).should('have.text', 'Credit for this win') + }) + + it('should render a hint', () => { + cy.get(creditForThisWin.hint).should( + 'have.text', + 'Other teams that helped with this win should be added so they can be credited, this will not reduce your credit for this win.' + ) + }) + + it('should render two unselected radio buttons', () => { + assertFieldRadiosStrict({ + inputName: 'credit_for_win', + legend: 'Did any other teams help with this win?', + options: ['Yes', 'No'], + }) + cy.get(creditForThisWin.radiosBtnYes) + .should('not.be.checked') + .parent() + .should('have.text', 'Yes') + cy.get(creditForThisWin.radiosBtnNo) + .should('not.be.checked') + .parent() + .should('have.text', 'No') + }) + + it('should go to the next step when selecting "No" and then "Continue"', () => { + cy.get(creditForThisWin.radiosBtnNo).check() + clickContinueButton() + assertUrl(customerDetailsStep) + }) + + it('should render a legend and hint text', () => { + cy.get(creditForThisWin.radiosBtnYes).check() + cy.get(creditForThisWin.addAnother) + .find('legend') + .eq(0) + .should('have.text', 'Contributing advisers') + cy.get(creditForThisWin.hintText).should( + 'have.text', + 'Up to 5 advisers can be added.' + ) + }) + + it('should render a Typeahead for the contributing officer', () => { + cy.get(creditForThisWin.radiosBtnYes).check() + cy.get(creditForThisWin.contributingOfficer).then((element) => { + assertFieldTypeahead({ + element, + label: 'Contributing officer', + }) + }) + }) + + it('should render a Typeahead for the team type', () => { + cy.get(creditForThisWin.radiosBtnYes).check() + cy.get(creditForThisWin.teamType).then((element) => { + assertFieldTypeahead({ + element, + label: 'Team type', + }) + }) + }) + + it('should render an "Add another" button', () => { + cy.get(creditForThisWin.radiosBtnYes).check() + cy.get(creditForThisWin.addAnother).should('exist') + }) + + it('should display validation error messages on mandatory fields', () => { + clickContinueButton() + // Assert Yes and No radio buttons + assertErrorSummary(['Select Yes or No']) + assertFieldError( + cy.get(creditForThisWin.radiosBtns), + 'Select Yes or No', + true + ) + cy.get(creditForThisWin.radiosBtnYes).check() + clickContinueButton() + // Assert Contributing officer and Team type + assertErrorSummary(['Enter a contributing officer', 'Enter a team type']) + assertFieldError( + cy.get(creditForThisWin.contributingOfficer), + 'Enter a contributing officer', + false + ) + assertFieldError( + cy.get(creditForThisWin.teamType), + 'Enter a team type', + false + ) + // Select a team type to render the HQ team, region or post field + cy.get(creditForThisWin.teamType).find('input').as('teamTypeInput') + cy.get('@teamTypeInput').type('Inv') + cy.get('@teamTypeInput').type('{downarrow}{enter}{esc}') + clickContinueButton() + // Assert HQ team, region or post + assertErrorSummary([ + 'Enter a contributing officer', + 'Enter a HQ team, region or post', + ]) + assertFieldError( + cy.get(creditForThisWin.hqTeam), + 'Enter a HQ team, region or post', + false + ) + }) +}) diff --git a/test/functional/cypress/specs/export-win/customer-details-spec.js b/test/functional/cypress/specs/export-win/customer-details-spec.js new file mode 100644 index 00000000000..784a6b29254 --- /dev/null +++ b/test/functional/cypress/specs/export-win/customer-details-spec.js @@ -0,0 +1,92 @@ +import { formFields, customerDetailsStep } from './constants' +import { clickContinueButton } from '../../support/actions' +import { + assertFieldError, + assertErrorSummary, + assertFieldTypeahead, +} from '../../support/assertions' + +describe('Customer details', () => { + const { customerDetails } = formFields + + beforeEach(() => cy.visit(customerDetailsStep)) + + it('should render a step heading', () => { + cy.get(customerDetails.heading).should('have.text', 'Customer details') + }) + + it('should render a contact hint', () => { + cy.get(customerDetails.contactHint).should( + 'have.text', + 'To select a customer contact name, it must have already been added to Data Hub. If not listed, go to the company page to add them.' + ) + }) + + it('should render Company contacts label and a Typeahead', () => { + cy.get(customerDetails.contacts).then((element) => { + assertFieldTypeahead({ + element, + label: 'Company contacts', + hint: 'This contact will be emailed to approve the win.', + }) + }) + }) + + it('should render HQ location label and a Typeahead', () => { + cy.get(customerDetails.location).then((element) => { + assertFieldTypeahead({ + element, + label: 'HQ location', + }) + }) + }) + + it('should render Export potential label and a Typeahead', () => { + cy.get(customerDetails.potential).then((element) => { + assertFieldTypeahead({ + element, + label: 'Export potential', + }) + }) + }) + + it('should render Export potential label and a Typeahead', () => { + cy.get(customerDetails.experience).then((element) => { + assertFieldTypeahead({ + element, + label: 'Export experience', + hint: 'Your customer will be asked to confirm this information.', + }) + }) + }) + + it('should display validation error messages on mandatory fields', () => { + clickContinueButton() + assertErrorSummary([ + 'Select a company contact', + 'Select HQ location', + 'Select export potential', + 'Select export experience', + ]) + assertFieldError( + cy.get(customerDetails.contacts), + 'Select a company contact', + true + ) + assertFieldError( + cy.get(customerDetails.location), + 'Select HQ location', + false + ) + assertFieldError( + cy.get(customerDetails.potential), + 'Select export potential', + false + ) + assertFieldError( + cy.get(customerDetails.experience), + 'Select export experience', + true + ) + }) +}) diff --git a/test/functional/cypress/specs/export-win/officer-details-spec.js b/test/functional/cypress/specs/export-win/officer-details-spec.js new file mode 100644 index 00000000000..f6ba77275e6 --- /dev/null +++ b/test/functional/cypress/specs/export-win/officer-details-spec.js @@ -0,0 +1,97 @@ +import { formFields, officerDetailsStep } from './constants' +import { clickContinueButton } from '../../support/actions' + +import { + assertFieldError, + assertErrorSummary, + assertFieldTypeahead, +} from '../../support/assertions' + +describe('Officer details', () => { + const { officerDetails } = formFields + + beforeEach(() => { + cy.intercept('GET', '/api-proxy/v4/metadata/hq-team-region-or-post?*', [ + { name: 'DIT Education' }, + { name: 'Healthcare UK' }, + ]) + cy.visit(officerDetailsStep) + }) + + it('should render an officer details heading', () => { + cy.get(officerDetails.heading).should('have.text', 'Officer details') + }) + + it('should render Lead Officer name label and a Typeahead', () => { + cy.get(officerDetails.leadOfficer).then((element) => { + assertFieldTypeahead({ + element, + label: 'Lead officer name', + }) + }) + }) + + it('should render both Team Type and HQ Team', () => { + // The HQ Team field is not visible until a team has been selected + cy.get(officerDetails.hqTeam).should('not.exist') + cy.get(officerDetails.teamType).then((element) => { + assertFieldTypeahead({ + element, + label: 'Team type', + }) + }) + cy.get(officerDetails.teamType).find('input').as('teamTypeInput') + cy.get('@teamTypeInput').type('Inv') + cy.get('@teamTypeInput').type('{downarrow}{enter}{esc}') + // Now the user has selected a team the HQ Team field is visible + cy.get(officerDetails.hqTeam).should('exist') + cy.get(officerDetails.hqTeam).then((element) => { + assertFieldTypeahead({ + element, + label: 'HQ team, region or post', + }) + }) + }) + + it('should render a Team Members Typeahead and hint text', () => { + cy.get(officerDetails.teamMembers).then((element) => { + assertFieldTypeahead({ + element, + label: 'Team members (optional)', + }) + }) + cy.get(officerDetails.teamMembersHintText).should( + 'have.text', + 'You can add up to 5 team members. They will not be credited for the win but will be notified when this win is updated.' + ) + }) + + it('should display validation error messages on mandatory fields', () => { + clickContinueButton() + assertErrorSummary(['Enter a lead officer', 'Select a team type']) + assertFieldError( + cy.get(officerDetails.leadOfficer), + 'Enter a lead officer', + false + ) + assertFieldError( + cy.get(officerDetails.teamType), + 'Select a team type', + false + ) + // Select a team to reveal the HQ Team field + cy.get(officerDetails.teamType).find('input').as('teamTypeInput') + cy.get('@teamTypeInput').type('Inv') + cy.get('@teamTypeInput').type('{downarrow}{enter}{esc}') + clickContinueButton() + assertErrorSummary([ + 'Enter a lead officer', + 'Select HQ team, region or post', + ]) + assertFieldError( + cy.get(officerDetails.hqTeam), + 'Select HQ team, region or post', + false + ) + }) +}) diff --git a/test/functional/cypress/specs/export-win/support-provided-spec.js b/test/functional/cypress/specs/export-win/support-provided-spec.js new file mode 100644 index 00000000000..a1ff81fa559 --- /dev/null +++ b/test/functional/cypress/specs/export-win/support-provided-spec.js @@ -0,0 +1,120 @@ +import { formFields, supportProvidedStep } from './constants' +import { clickContinueButton } from '../../support/actions' + +import { + assertFieldError, + assertErrorSummary, + assertFieldTypeahead, + assertFieldCheckboxes, +} from '../../support/assertions' + +describe('Support provided', () => { + const { supportProvided } = formFields + + beforeEach(() => { + cy.intercept('GET', '/api-proxy/v4/metadata/associated-programme', [ + { name: 'Afterburner' }, + ]) + cy.intercept('GET', '/api-proxy/v4/metadata/support-type', [ + { name: 'Market entry advice and support – DIT/FCO in UK' }, + ]) + cy.intercept('GET', '/api-proxy/v4/metadata/hvc', [ + { name: 'Australia Consumer Goods & Retail: E004' }, + ]) + cy.visit(supportProvidedStep) + }) + + it('should render a step heading', () => { + cy.get(supportProvided.heading).should('have.text', 'Support given') + }) + + it('should render a hint', () => { + cy.get(supportProvided.hint).should( + 'have.text', + 'Did any of these help the customer achieve this win?' + ) + }) + + it('should render a typeahead for high value campaign', () => { + cy.get(supportProvided.hvc).then((element) => { + assertFieldTypeahead({ + element, + label: 'High Value Campaign (HVC) code (optional)', + hint: 'If the win was linked to a HVC, select the appropriate campaign.', + }) + }) + }) + + it('should render a support given typeahead', () => { + cy.get(supportProvided.typeOfSupport).then((element) => { + assertFieldTypeahead({ + element, + label: 'What type of support was given?', + hint: 'You can add up to 5 types of support.', + }) + }) + }) + + it('should render an associated programme typeahead', () => { + cy.get(supportProvided.associatedProgramme).then((element) => { + assertFieldTypeahead({ + element, + label: + 'Was there a DBT campaign or event that contributed to this win?', + hint: 'You can add up to 5 campaigns or events.', + }) + }) + }) + + it('should render personally confirmed checkbox', () => { + assertFieldCheckboxes({ + element: supportProvided.personallyConfirmed, + options: [ + { + label: 'I confirm that this information is complete and accurate.', + checked: false, + }, + ], + }) + }) + + it('should render a manager confirmed checkbox', () => { + assertFieldCheckboxes({ + element: supportProvided.lineManagerConfirmed, + options: [ + { + label: 'My line manager has agreed that this win should be recorded.', + checked: false, + }, + ], + }) + }) + + it('should display validation error messages on mandatory fields', () => { + clickContinueButton() + assertErrorSummary([ + 'Select at least one type of support', + 'Select at least one type of DBT campaign or event', + 'Confirm that this information is complete and accurate', + 'Confirm your line manager has agreed that this win should be recorded', + ]) + assertFieldError( + cy.get(supportProvided.typeOfSupport), + 'Select at least one type of support', + true + ) + assertFieldError( + cy.get(supportProvided.associatedProgramme), + 'Select at least one type of DBT campaign or event', + true + ) + cy.get(supportProvided.personallyConfirmed).should( + 'contain', + 'Confirm that this information is complete and accurate' + ) + cy.get(supportProvided.lineManagerConfirmed).should( + 'contain', + 'Confirm your line manager has agreed that this win should be recorded' + ) + }) +}) diff --git a/test/functional/cypress/specs/export-win/utils.js b/test/functional/cypress/specs/export-win/utils.js new file mode 100644 index 00000000000..a1c24a57ae0 --- /dev/null +++ b/test/functional/cypress/specs/export-win/utils.js @@ -0,0 +1,176 @@ +import { getDateTwelveMonthsAgoWithFirstDay } from '../../../../../src/client/modules/ExportWins/Form/utils' +import { + subtractMonths, + addMonths, + getRandomDateInRange, +} from '../../../../../src/client/utils/date' +import { clickContinueButton } from '../../support/actions' +import { assertUrl } from '../../support/assertions' +import { formFields } from './constants' + +export const populateWinWithValues = ({ alias, winType, values }) => + values.forEach((value, index) => + cy.get(alias).find(`[data-test="${winType}-${index}-input"]`).type(value) + ) + +export const fillOfficerDetails = ({ leadOfficer, teamType, hqTeam }) => { + const { officerDetails } = formFields + leadOfficer && + cy.get(officerDetails.leadOfficer).selectTypeaheadOption(leadOfficer) + teamType && cy.get(officerDetails.teamType).selectTypeaheadOption(teamType) + hqTeam && cy.get(officerDetails.hqTeam).selectTypeaheadOption(hqTeam) +} + +export const fillCreditForThisWin = ({ + contributingOfficer, + teamType, + hqTeam, +}) => { + const { creditForThisWin } = formFields + cy.get(creditForThisWin.radiosBtnYes).check() + contributingOfficer && + cy + .get(creditForThisWin.contributingOfficer) + .selectTypeaheadOption(contributingOfficer) + teamType && cy.get(creditForThisWin.teamType).selectTypeaheadOption(teamType) + hqTeam && cy.get(creditForThisWin.hqTeam).selectTypeaheadOption(hqTeam) +} + +export const fillCustomerDetails = ({ + contact, + location, + potential, + experience, +}) => { + const { customerDetails } = formFields + contact && cy.get(customerDetails.contacts).selectTypeaheadOption(contact) + location && cy.get(customerDetails.location).selectTypeaheadOption(location) + potential && + cy.get(customerDetails.potential).selectTypeaheadOption(potential) + experience && + cy.get(customerDetails.experience).selectTypeaheadOption(experience) +} + +export const fillWinDetails = ({ + country, + dateMonth, + dateYear, + description, + nameOfCustomer, + isConfidential, + businessType, + exportValues, + businessSuccessValues, + odiValues, + goodsVsServices, + nameOfExport, + sector, +}) => { + const { winDetails } = formFields + + country && cy.get(winDetails.country).selectTypeaheadOption(country) + + dateMonth && cy.get(winDetails.dateMonth).type(dateMonth) + dateYear && cy.get(winDetails.dateYear).type(dateYear) + + description && + cy.get(winDetails.description).find('textarea').type(description) + + nameOfCustomer && + cy.get(winDetails.nameOfCustomer).find('input').type(nameOfCustomer) + + isConfidential && cy.get(winDetails.confidential).find('input').check() + + businessType && + cy.get(winDetails.businessType).find('input').type(businessType) + + cy.get(winDetails.winType).as('winType') + + exportValues && cy.get('@winType').find(winDetails.exportWinCheckbox).check() + + businessSuccessValues && + cy.get('@winType').find(winDetails.businessSuccessCheckbox).check() + + odiValues && cy.get('@winType').find(winDetails.odiCheckbox).check() + + exportValues && + populateWinWithValues({ + alias: '@winType', + winType: 'export-win', + values: exportValues, + }) + + businessSuccessValues && + populateWinWithValues({ + alias: '@winType', + winType: 'business-success-win', + values: businessSuccessValues, + }) + + odiValues && + populateWinWithValues({ + alias: '@winType', + winType: 'odi-win', + values: odiValues, + }) + + goodsVsServices === 'goods' && + cy.get(winDetails.goodsVsServices).find('input').eq(0).check() + + goodsVsServices === 'services' && + cy.get(winDetails.goodsVsServices).find('input').eq(1).check() + + nameOfExport && + cy.get(winDetails.nameOfExport).find('input').type(nameOfExport) + + sector && cy.get(winDetails.sector).selectTypeaheadOption(sector) +} + +export const fillSupportProvided = ({ + hvc, + typeOfSupport, + associatedProgramme, + personallyConfirmed, + lineManagerConfirmed, +}) => { + const { supportProvided } = formFields + + hvc && cy.get(supportProvided.hvc).selectTypeaheadOption(hvc) + typeOfSupport && + cy.get(supportProvided.typeOfSupport).selectTypeaheadOption(typeOfSupport) + associatedProgramme && + cy + .get(supportProvided.associatedProgramme) + .selectTypeaheadOption(associatedProgramme) + personallyConfirmed && + cy + .get(supportProvided.personallyConfirmed) + .find('[data-test="checkbox-yes"]') + .check() + lineManagerConfirmed && + cy + .get(supportProvided.lineManagerConfirmed) + .find('[data-test="checkbox-yes"]') + .check() +} + +export const clickContinueAndAssertUrl = (url) => { + clickContinueButton() + assertUrl(url) +} + +const getMonthAndYearFromDate = (date) => ({ + month: date.getMonth() + 1, // indexing starts at 0 + year: date.getFullYear(), +}) + +export const getDateWithinLastTwelveMonths = () => + getMonthAndYearFromDate( + getRandomDateInRange(getDateTwelveMonthsAgoWithFirstDay(), new Date()) + ) + +export const getDateThirteenMonthsAgo = () => + getMonthAndYearFromDate(subtractMonths(new Date(), 13)) + +export const getDateNextMonth = () => + getMonthAndYearFromDate(addMonths(new Date(), 1)) diff --git a/test/functional/cypress/specs/export-win/win-details-spec.js b/test/functional/cypress/specs/export-win/win-details-spec.js new file mode 100644 index 00000000000..b84f9ac9b0a --- /dev/null +++ b/test/functional/cypress/specs/export-win/win-details-spec.js @@ -0,0 +1,306 @@ +import { clickContinueButton } from '../../support/actions' +import { formFields, winDetailsStep } from './constants' + +import { + populateWinWithValues, + getDateNextMonth, + getDateThirteenMonthsAgo, + getDateWithinLastTwelveMonths, +} from './utils' + +import { + assertFieldInput, + assertFieldError, + assertErrorSummary, + assertFieldTextarea, + assertFieldTypeahead, + assertFieldDateShort, + assertFieldCheckboxes, +} from '../../support/assertions' + +const { month, year } = getDateWithinLastTwelveMonths() + +describe('Win details', () => { + const { winDetails } = formFields + + beforeEach(() => { + cy.visit(winDetailsStep) + }) + + it('should render a step heading', () => { + cy.get(winDetails.heading).should('have.text', 'Win details') + }) + + it('should render a hint', () => { + cy.get(winDetails.hint).should( + 'have.text', + 'The customer will be asked to confirm this information.' + ) + }) + + it('should render Destination country label and a Typeahead', () => { + cy.get(winDetails.country).then((element) => { + assertFieldTypeahead({ + element, + label: 'Destination country', + }) + }) + }) + + it('should render the Win date', () => { + cy.get(winDetails.date).then((element) => { + // Both Month and Year labels are tested within the assertion + assertFieldDateShort({ + element, + label: 'Date won', + hint: `For example ${month} ${year}, date of win must be in the last 12 months.`, + }) + }) + }) + + it('should render Summary of the support given', () => { + cy.get(winDetails.description).then((element) => { + assertFieldTextarea({ + element, + label: 'Summary of the support given', + hint: 'Outline what had the most impact or would be memorable to the customer in less than 100 words.', + wordCount: 'You have 100 words remaining.', + }) + }) + }) + + it('should renderer Overseas customer', () => { + cy.get(winDetails.nameOfCustomer).then((element) => { + assertFieldInput({ + element, + label: 'Overseas customer', + placeholder: 'Add name', + }) + }) + }) + + it('should render a Confidential checkbox', () => { + assertFieldCheckboxes({ + element: winDetails.confidential, + hint: 'Check this box if your customer has asked for this not to be public (optional).', + options: [ + { + label: 'Confidential', + checked: false, + }, + ], + }) + }) + + it('should renderer a type of business deal', () => { + cy.get(winDetails.businessType).then((element) => { + assertFieldInput({ + element, + label: 'Type of business deal', + hint: 'For example: export sales, contract, order, distributor, tender / competition win, joint venture, outward investment.', + placeholder: 'Enter a type of business deal', + }) + }) + }) + + it('should render Type of win ', () => { + assertFieldCheckboxes({ + element: winDetails.winType, + legend: 'Type of win', + options: [ + { + label: 'Export', + checked: false, + }, + { + label: 'Business success', + checked: false, + }, + { + label: 'Outward Direct Investment (ODI)', + checked: false, + }, + ], + }) + }) + + it('should render the WinTypeValues component for each win type', () => { + cy.get(winDetails.winType).as('winType') + + // Export win + cy.get('@winType') + .find(winDetails.winTypeValuesExport) + .should('not.exist') + .get('@winType') + .find(winDetails.exportWinCheckbox) + .check() + .next() + .get(winDetails.winTypeValuesExport) + .should('exist') + + // Business type + cy.get('@winType') + .find(winDetails.winTypeValuesBusSupp) + .should('not.exist') + .get('@winType') + .find(winDetails.businessSuccessCheckbox) + .check() + .next() + .get(winDetails.winTypeValuesBusSupp) + .should('exist') + + // ODI + cy.get('@winType') + .find(winDetails.winTypeValuesODI) + .should('not.exist') + .get('@winType') + .find(winDetails.odiCheckbox) + .check() + .next() + .get(winDetails.winTypeValuesODI) + .should('exist') + }) + + it('should render the total export value across all 3 win types', () => { + cy.get(winDetails.winType).as('winType') + + // Check all 3 win types to render 15 (3 x 5) inputs + cy.get('@winType').find(winDetails.exportWinCheckbox).check() + cy.get('@winType').find(winDetails.businessSuccessCheckbox).check() + cy.get('@winType').find(winDetails.odiCheckbox).check() + + populateWinWithValues({ + alias: '@winType', + winType: 'export-win', + values: ['1000000', '1000000', '1000000', '1000000', '1000000'], // 5M + }) + + populateWinWithValues({ + alias: '@winType', + winType: 'business-success-win', + values: ['2000000', '2000000', '2000000', '2000000', '2000000'], // 10M + }) + + populateWinWithValues({ + alias: '@winType', + winType: 'odi-win', + values: ['3000000', '3000000', '3000000', '3000000', '3000000'], // 15M + }) + + // Assert the total export value + cy.get(winDetails.totalExportValue).should( + 'have.text', + 'Total export value: £30,000,000' // 5M + 10M + 15M + ) + }) + + it('should render Goods and Services', () => { + assertFieldCheckboxes({ + element: winDetails.goodsVsServices, + legend: 'What does the value relate to?', + hint: 'Select all that apply.', + options: [ + { + label: 'Goods', + checked: false, + }, + { + label: 'Services', + checked: false, + }, + ], + }) + }) + + it('should renderer name of goods or services', () => { + cy.get(winDetails.nameOfExport).then((element) => { + assertFieldInput({ + element, + label: 'Name of goods or services', + hint: "For instance 'shortbread biscuits'.", + placeholder: 'Enter a name for goods or services', + }) + }) + }) + + it('should render a sector label and typeahead', () => { + cy.get(winDetails.sector).then((element) => { + assertFieldTypeahead({ + element, + label: 'Sector', + }) + }) + }) + + it('should display validation error messages on mandatory fields', () => { + clickContinueButton() + assertErrorSummary([ + 'Choose a destination country', + 'Enter the win date', + 'Enter a summary', + 'Enter the name of the overseas customer', + 'Enter the type of business deal', + 'Choose at least one type of win', + 'Select at least one option', + 'Enter the name of goods or services', + 'Enter a sector', + ]) + assertFieldError( + cy.get(winDetails.country), + 'Choose a destination country', + false + ) + assertFieldError(cy.get(winDetails.date), 'Enter the win date', true) + assertFieldError(cy.get(winDetails.description), 'Enter a summary', true) + assertFieldError( + cy.get(winDetails.nameOfCustomer), + 'Enter the name of the overseas customer', + false + ) + assertFieldError( + cy.get(winDetails.businessType), + 'Enter the type of business deal', + true + ) + assertFieldError( + cy.get(winDetails.winType), + 'Choose at least one type of win', + true + ) + // We can't use assertFieldError here as it picks up the wrong span + cy.get(winDetails.goodsVsServices).should( + 'contain', + 'Select at least one option' + ) + assertFieldError( + cy.get(winDetails.nameOfExport), + 'Enter the name of goods or services', + true + ) + assertFieldError(cy.get(winDetails.sector), 'Enter a sector', false) + }) + + it('should display a validation error message when the win date is in the future', () => { + const { month, year } = getDateNextMonth() + cy.get(winDetails.dateMonth).type(month) + cy.get(winDetails.dateYear).type(year) + clickContinueButton() + assertFieldError( + cy.get(winDetails.date), + 'Date must be in the last 12 months', + true + ) + }) + + it('should display a validation error message when the win date is greater than twelve months ago', () => { + const { month, year } = getDateThirteenMonthsAgo() + cy.get(winDetails.dateMonth).type(month) + cy.get(winDetails.dateYear).type(year) + clickContinueButton() + assertFieldError( + cy.get(winDetails.date), + 'Date must be in the last 12 months', + true + ) + }) +})