diff --git a/src/client/components/Resource/ExportWin.js b/src/client/components/Resource/ExportWin.js new file mode 100644 index 00000000000..8d8721628b5 --- /dev/null +++ b/src/client/components/Resource/ExportWin.js @@ -0,0 +1,3 @@ +import { createEntityResource } from './Resource' + +export default createEntityResource('Export Win', (id) => `v4/export-win/${id}`) diff --git a/src/client/components/Resource/tasks.js b/src/client/components/Resource/tasks.js index afc06257043..cc9fbe8c808 100644 --- a/src/client/components/Resource/tasks.js +++ b/src/client/components/Resource/tasks.js @@ -21,6 +21,7 @@ import Interaction from './Interaction' import Event from './Event' import CompanyOneListTeam from './CompanyOneListTeam' import ExportYears from './ExportYears' +import ExportWin from './ExportWin' import ExportWins from './ExportWins' import Sector from './Sector' import Export from './Export' @@ -120,6 +121,7 @@ export default { ...ExportExperienceCategories.tasks, ...Event.tasks, ...ExportYears.tasks, + ...ExportWin.tasks, ...ExportWins.tasks, ...Sector.tasks, ...Export.tasks, diff --git a/src/client/components/SummaryTable/index.jsx b/src/client/components/SummaryTable/index.jsx index 17d6f1ff77c..8504685722e 100644 --- a/src/client/components/SummaryTable/index.jsx +++ b/src/client/components/SummaryTable/index.jsx @@ -64,7 +64,7 @@ const StyledTableRow = styled(Table.Row)` `} ` -SummaryTable.Row = ({ heading, children, hideWhenEmpty, flag }) => { +SummaryTable.Row = ({ heading, children, hideWhenEmpty, flag, ...props }) => { if (hideWhenEmpty && isEmpty(children)) { return null } @@ -72,7 +72,7 @@ SummaryTable.Row = ({ heading, children, hideWhenEmpty, flag }) => { const renderChildren = () => { if (Array.isArray(children)) { return ( - + {children .filter((c) => c) .map((c, index) => ( @@ -86,7 +86,7 @@ SummaryTable.Row = ({ heading, children, hideWhenEmpty, flag }) => { } return ( - + {heading && {heading}} {renderChildren()} diff --git a/src/client/modules/ExportWins/Details.jsx b/src/client/modules/ExportWins/Details.jsx new file mode 100644 index 00000000000..db77fa8f6a1 --- /dev/null +++ b/src/client/modules/ExportWins/Details.jsx @@ -0,0 +1,160 @@ +import React from 'react' +import { Link } from 'govuk-react' +import { Link as ReactRouterLink } from 'react-router-dom/cjs/react-router-dom' +import styled from 'styled-components' +import { SPACING } from '@govuk-react/constants' +import pluralize from 'pluralize' + +import { DefaultLayout, SummaryTable } from '../../components' +import urls from '../../../lib/urls' +import { formatMediumDate } from '../../utils/date' +import { currencyGBP } from '../../utils/number-utils' +import ExportWin from '../../components/Resource/ExportWin' + +const VerticalSpacer = styled.div` + display: flex; + flex-direction: column; + gap: ${SPACING.SCALE_1}; + margin-bottom: ${SPACING.SCALE_5}; +` + +const NormalFontWeightRow = styled(SummaryTable.Row)` + & th { + font-weight: normal; + } +` + +const ExportWinTitle = (props) => ( + + {(exportWin) => ( + <> + {exportWin.nameOfExport} to {exportWin.country?.name} + + )} + +) + +const groupBreakdowns = (breakdowns) => { + const result = + breakdowns.reduce( + ({ groups: groups, totalAmount, yearRange }, breakdown) => { + const group = groups[breakdown.type.name] + + return { + totalAmount: totalAmount + breakdown.value, + yearRange: { + min: Math.min(yearRange.min, breakdown.year), + max: Math.max(yearRange.max, breakdown.year), + }, + groups: { + ...groups, + [breakdown.type.name]: { + yearRange: { + min: Math.min(group?.yearRange.min || Infinity, breakdown.year), + max: Math.max(group?.yearRange.min || 0, breakdown.year), + }, + total: (group?.total || 0) + breakdown.value, + }, + }, + } + }, + { + groups: {}, + totalAmount: 0, + yearRange: { min: Infinity, max: 0 }, + } + ) || {} + + return { + ...result, + totalYears: result.yearRange.max - result.yearRange.min + 1, + } +} + +const Detail = ({ + match: { + params: { winId }, + }, +}) => ( + } + pageTitle={} + breadcrumbs={[ + { + link: urls.dashboard.index(), + text: 'Home', + }, + { + link: urls.companies.exportWins.index(), + text: 'Export wins', + }, + { text: }, + ]} + > + + {(exportWin) => { + const { groups, totalAmount, totalYears } = exportWin + ? groupBreakdowns(exportWin.breakdowns) + : {} + + return ( + <> + + + {exportWin?.goodsVsServices.name} + + + {exportWin?.country.name} + + {exportWin && + Object.keys(groups).length > 1 && + Object.entries(groups).map(([k, { total, yearRange }]) => { + const years = yearRange.max - yearRange.min + 1 + return ( + + {`${currencyGBP(total)} over ${years} ${pluralize('year', years)}`} + + ) + })} + + {exportWin && + `${currencyGBP(totalAmount)} over ${totalYears} ${pluralize('year', totalYears)}`} + + + {exportWin && formatMediumDate(exportWin.date)} + + + {exportWin?.leadOfficer.name} + + + {exportWin?.comments} + + + {exportWin && (exportWin.isPersonallyConfirmed ? 'Yes' : 'No')} + + + + {exportWin?.isPersonallyConfirmed && ( + + View customer feedback + + )} + + Export wins + + + + ) + }} + + +) + +export default Detail diff --git a/src/client/modules/ExportWins/Details/DetailsTable.js b/src/client/modules/ExportWins/Details/DetailsTable.js deleted file mode 100644 index b5b47835956..00000000000 --- a/src/client/modules/ExportWins/Details/DetailsTable.js +++ /dev/null @@ -1,52 +0,0 @@ -import React from 'react' -import { connect } from 'react-redux' -import { useLocation } from 'react-router-dom' - -import { ID, TASK_GET_EXPORT_WIN_DETAILS, state2props } from './state' -import { EXPORT_WINS__DETAILS_LOADED } from '../../../actions' -import { DefaultLayout } from '../../../components' -import Task from '../../../components/Task' -import urls from '../../../../lib/urls' - -const DetailsTable = ({ exportWin }) => { - const location = useLocation() - const companyId = location.pathname.split('/')[2] - const title = `${exportWin.name_of_export} to ${exportWin?.country?.name}` - return ( - - {() => ( - -
-            {JSON.stringify(exportWin, null, 2)}
-          
-
- )} -
- ) -} - -export default connect(state2props)(DetailsTable) diff --git a/src/client/modules/ExportWins/Details/confirmed.json b/src/client/modules/ExportWins/Details/confirmed.json deleted file mode 100644 index b69a04e0040..00000000000 --- a/src/client/modules/ExportWins/Details/confirmed.json +++ /dev/null @@ -1,153 +0,0 @@ -{ -"id": "df35c164-7b60-41c5-babe-52570766907f", -"adviser": { - "id": "7a2a32e7-e9c2-4eda-953d-66c751a2849c", - "name": "Barry Confirmed" -}, -"company": { - "id": "5b6a6a83-888c-4136-9609-334ce698a5be", - "name": "Corporation" -}, -"customer_name": "John Doe", -"customer_job_title": "Director", -"customer_email_address": "john.doe@abcdef.ghi", -"customer_location": { - "id": "7b7a780f-ca7b-43b3-994e-9a288fb1f3f4" -}, -"business_type": "What kind of business deal was this win?", -"description": "Summarise the support provided to help achieve this win", -"name_of_customer": "Overseas customer", -"name_of_export": "Rolls Reece cars", -"type": { - "id": "34159c9f-aac5-442b-8f1f-0e9d46634280" -}, -"date": "2023-10-10 00:00:00", -"country": { - "id": "7b143d64-65d9-4788-9ffd-32206bb0ca39", - "name": "United Arab Emirates" -}, -"total_expected_export_value": 1000000, -"goods_vs_services": { - "id": "c04786de-38d7-4960-97d0-833c34f66036", - "name": "Services" -}, -"total_expected_non_export_value": 10000, -"total_expected_odi_value": 0, -"sector": { - "id": "91a13b39-e9ee-428b-86ae-79453380494b", - "name": "Online gaming" -}, -"is_prosperity_fund_related": false, -"hvc": { - "id": "4975a9dc-f6e9-4c9b-8258-a8666df62170", - "campaign_id": "566e6945-a63e-4254-820d-5a20c4a9c6a8", - "financial_year": 2023, - "name": "HVC" -}, -"hvo_programme": { - "id": "5d121709-7c27-4bcf-9e9a-3889f0598b50", - "name": "Rail" -}, -"has_hvo_specialist_involvement": true, -"is_e_exported": false, -"type_of_support": [ - {"id": "08ac1fe0-3dd2-4a8a-abc5-d4c8dfc2f878", "name": "Briefing"}, - {"id": "744c7c8a-8228-40d0-a6f6-7ed4e9f138af", "name": "Other"} -], -"associated_programme": [ - {"id": "526fd91b-1d82-4cd4-82fc-d59c637d329f", "name": "Programme 1"}, - {"id": "0fe70c28-a910-4668-93a7-60ed6af16c84", "name": "Programme 2"} -], -"is_personally_confirmed": false, -"is_line_manager_confirmed": false, -"lead_officer": { - "id": "90895780-ada9-41fe-813b-e923a7ef5c15", - "name": "John Doe" -}, -"lead_officer_name": "John Doe", -"help_text": "This is the name that will be included in the email to the customer", -"lead_officer_email_address": "John.Doe@email", -"other_official_email_address": "", -"line_manager": { - "id": "90895780-ada9-41fe-813b-e923a7ef5c15", - "name": "John Doe" -}, -"line_manager_name": "John Doe", -"team_type": { - "id": "68c82eb8-ba96-40f3-9b25-7ef431ecaabc", - "name": "DSO" -}, -"hq_team": { - "id": "b557e5b7-82e2-4252-b7d8-58694297a37d", - "name": "DSO - RD 1C" -}, -"business_potential": { - "id": "40d9c017-6959-4357-baeb-846fe442a547", - "name": "The company is an exporter with High Export Potential" -}, -"export_experience": { - "id": "c7b95cd7-245f-4379-afa6-b07190b2f7a6", - "name": "Has never exported before" -}, -"location": "Brighton", -"complete": false, -"audit": "", -"match_id": 12345, -"company_name": "Corporation", -"cdms_reference": "UK12345", -"customer_response": { - "our_support": { - "id": "3ca4c1eb-0d86-401f-8c69-d9ceeabe7224", - "name": "N/A" - }, - "access_to_contacts": { - "id": "3ca4c1eb-0d86-401f-8c69-d9ceeabe7224", - "name": "N/A" - }, - "access_to_information": { - "id": "3ca4c1eb-0d86-401f-8c69-d9ceeabe7224", - "name": "N/A" - }, - "improved_profile": { - "id": "3ca4c1eb-0d86-401f-8c69-d9ceeabe7224", - "name": "N/A" - }, - "gained_confidence": { - "id": "3ca4c1eb-0d86-401f-8c69-d9ceeabe7224", - "name": "N/A" - }, - "developed_relationships": { - "id": "3ca4c1eb-0d86-401f-8c69-d9ceeabe7224", - "name": "N/A" - }, - "overcame_problem": { - "id": "3ca4c1eb-0d86-401f-8c69-d9ceeabe7224", - "name": "N/A" - }, - "involved_state_enterprise": true, - "interventions_were_prerequisite": true, - "support_improved_speed": true, - "expected_portion_without_help": { - "id": "fd9bcb81-61e3-4d13-bc27-96968a7d9489", - "name": "Up to 40%" - }, - "last_export": { - "id": "abcbcb82-31a3-4d13-bc27-46968a7d9153", - "name": "We had not started exporting before this win" - }, - "has_enabled_expansion_into_new_market": true, - "has_enabled_expansion_into_existing_market": true, - "has_increased_exports_as_percent_of_turnover": true, - "company_was_at_risk_of_not_exporting": true, - "has_explicit_export_plans": false, - "agree_with_win": true, - "case_study_willing": false, - "comments": "Lorem ipsum", - "name": "John Doe", - "created_on": "2020-03-23", - "marketing_source": { - "id": "b4fe471c-378d-4426-a2be-f185a85df21e", - "name": "Business / professional contacts in the private sector" - } - } -} diff --git a/src/client/modules/ExportWins/Details/index.jsx b/src/client/modules/ExportWins/Details/index.jsx deleted file mode 100644 index d0dcb7099e5..00000000000 --- a/src/client/modules/ExportWins/Details/index.jsx +++ /dev/null @@ -1,3 +0,0 @@ -import DetailsTable from './DetailsTable' - -export default DetailsTable diff --git a/src/client/modules/ExportWins/Details/reducer.js b/src/client/modules/ExportWins/Details/reducer.js deleted file mode 100644 index 6c782d17b1b..00000000000 --- a/src/client/modules/ExportWins/Details/reducer.js +++ /dev/null @@ -1,17 +0,0 @@ -import { EXPORT_WINS__DETAILS_LOADED } from '../../../actions' - -const initialState = { - exportWin: {}, -} - -export default (state = initialState, { type, result }) => { - switch (type) { - case EXPORT_WINS__DETAILS_LOADED: - return { - ...state, - ...result, - } - default: - return state - } -} diff --git a/src/client/modules/ExportWins/Details/state.js b/src/client/modules/ExportWins/Details/state.js deleted file mode 100644 index baf6fb777a3..00000000000 --- a/src/client/modules/ExportWins/Details/state.js +++ /dev/null @@ -1,3 +0,0 @@ -export const TASK_GET_EXPORT_WIN_DETAILS = 'TASK_GET_EXPORT_WIN_DETAILS' -export const ID = 'exportWinDetails' -export const state2props = (state) => state[ID] diff --git a/src/client/modules/ExportWins/Details/tasks.js b/src/client/modules/ExportWins/Details/tasks.js deleted file mode 100644 index cbd8fe9fbac..00000000000 --- a/src/client/modules/ExportWins/Details/tasks.js +++ /dev/null @@ -1,11 +0,0 @@ -import exportWin from './confirmed.json' - -// eslint-disable-next-line no-unused-vars -export const getExportWinDetails = ({ companyId }) => { - // Stub the endpoint until we have one. Once we have - // an endpoint then pass the companyId to fetch the - // export win - return Promise.resolve({ - exportWin, - }) -} diff --git a/src/client/modules/ExportWins/Status/WinsWonTable.jsx b/src/client/modules/ExportWins/Status/WinsWonTable.jsx index d01a96994c4..7fba38b0d31 100644 --- a/src/client/modules/ExportWins/Status/WinsWonTable.jsx +++ b/src/client/modules/ExportWins/Status/WinsWonTable.jsx @@ -32,8 +32,8 @@ export const WinsWonTable = ({ exportWins }) => ( company, country, date, - customer_response, total_expected_export_value, + customer_response, }) => ( diff --git a/src/client/reducers.js b/src/client/reducers.js index afa086026b6..0ef707117ac 100644 --- a/src/client/reducers.js +++ b/src/client/reducers.js @@ -169,9 +169,6 @@ import oneListDetailsReducer from './modules/Companies/CoreTeam/reducer' import { ID as TASK_DETAILS_ID } from './modules/Tasks/TaskDetails/state' import taskDetailsReducer from './modules/Tasks/TaskDetails/reducer' -import { ID as EXPORT_WINS_DETAILS_ID } from './modules/ExportWins/Details/state' -import exportWinDetailsTaskReducer from './modules/ExportWins/Details/reducer' - import { ID as OMIS_COMPANY_SELECT_ID } from './modules/Omis/CreateOrder/CompanySelect/state' import omisCompanyReducer from './modules/Omis/CreateOrder/CompanySelect/reducer.js' @@ -285,7 +282,6 @@ export const reducers = { [RECIPIENT_COMPANY_LIST_ID]: recipientCompanyReducer, [ONE_LIST_DETAILS_ID]: oneListDetailsReducer, [TASK_DETAILS_ID]: taskDetailsReducer, - [EXPORT_WINS_DETAILS_ID]: exportWinDetailsTaskReducer, [OMIS_COMPANY_SELECT_ID]: omisCompanyReducer, [GET_MY_TASKS_ID]: getMyTasksReducer, [INTERACTION_ID]: getInteractionReducer, diff --git a/src/client/tasks.js b/src/client/tasks.js index 0e8d2db07b6..040830d6371 100644 --- a/src/client/tasks.js +++ b/src/client/tasks.js @@ -433,9 +433,6 @@ import { import { saveTaskDetail } from './modules/Tasks/TaskForm/tasks' import { TASK_SAVE_TASK_DETAILS } from './modules/Tasks/TaskForm/state' -import { TASK_GET_EXPORT_WIN_DETAILS } from './modules/ExportWins/Details/state' -import { getExportWinDetails } from './modules/ExportWins/Details/tasks' - import { TASK_GET_EXPORT_WINS_SAVE_FORM } from './modules/ExportWins/Form/state' import { saveExportWin } from './modules/ExportWins/Form/tasks' @@ -668,7 +665,6 @@ export const tasks = { [TASK_SAVE_MY_TASKS_DUE_DATE_APPROACHING_REMINDER_SUBSCRIPTIONS]: reminderSettings.saveUpcomingDueDateExportSubscriptions, [TASK_COMPLETE_ORDER]: completeOrder, - [TASK_GET_EXPORT_WIN_DETAILS]: getExportWinDetails, [TASK_SAVE_TASK_ASSIGNED_TO_ME_FROM_OTHERS_REMINDER_SUBSCRIPTIONS]: reminderSettings.saveTaskAssignedToMeFromOthersExportSubscriptions, [TASK_EDIT_ORDER_CONTACT]: updateOrder, diff --git a/src/lib/urls.js b/src/lib/urls.js index 96a2104fe5a..116037e61e3 100644 --- a/src/lib/urls.js +++ b/src/lib/urls.js @@ -225,6 +225,8 @@ module.exports = { rejected: url('/exportwins/rejected'), sent: url('/exportwins/sent'), won: url('/exportwins/won'), + edit: url('/exportwins', '/:winId/edit'), + customerFeedback: url('/exportwins', '/:winId/customer-feedback'), }, overview: { index: url('/companies', '/:companyId/overview'), diff --git a/src/middleware/api-proxy.js b/src/middleware/api-proxy.js index 0c2ac3d694e..650bde06222 100644 --- a/src/middleware/api-proxy.js +++ b/src/middleware/api-proxy.js @@ -57,6 +57,7 @@ const ALLOWLIST = [ '/v3/event/', '/v4/event/', '/v4/export-win/', + '/v4/export-win/:exportWinId', '/v4/event/:id', '/v3/omis/order/:id/assignee', '/v3/omis/order/:id/subscriber-list', diff --git a/test/a11y/cypress/config/urlTestExclusions.js b/test/a11y/cypress/config/urlTestExclusions.js index 6e7f370df02..29284399a66 100644 --- a/test/a11y/cypress/config/urlTestExclusions.js +++ b/test/a11y/cypress/config/urlTestExclusions.js @@ -145,9 +145,10 @@ export const urlTestExclusions = [ { url: '/oauth/' }, { url: '/oauth/callback/' }, { url: '/oauth/sign-out/' }, - { url: '/exportwins/1/details' }, { url: '/exportwins/sent/' }, { url: '/exportwins/rejected/' }, + { url: '/exportwins/:winId/edit' }, + { url: '/exportwins/:winId/customer-feedback' }, { url: '/companies/e59a2b0f-7d84-4de7-bc1e-f70339f4255f/overview' }, // Exclude all metadata { url: '/api-proxy/v4/metadata/likelihood-to-land' }, diff --git a/test/component/cypress/specs/ExportWins/Details.cy.jsx b/test/component/cypress/specs/ExportWins/Details.cy.jsx index fe3b2046d38..6d8d9c0a4d9 100644 --- a/test/component/cypress/specs/ExportWins/Details.cy.jsx +++ b/test/component/cypress/specs/ExportWins/Details.cy.jsx @@ -1,40 +1,340 @@ import React from 'react' -import { assertBreadcrumbs } from '../../../../functional/cypress/support/assertions' -import ExportWinDetails from '../../../../../src/client/modules/ExportWins/Details' +import { + assertBreadcrumbs, + assertLocalHeader, + assertLink, +} from '../../../../functional/cypress/support/assertions' +import Details from '../../../../../src/client/modules/ExportWins/Details' import urls from '../../../../../src/lib/urls' import DataHubProvider from '../provider' -describe('Export wins tab navigation', () => { - const Component = (props) => ( - - +const insertAfter = (o, n, oo) => { + const entries = Object.entries(o) + return Object.fromEntries( + entries.toSpliced( + entries.findIndex(([k]) => k === n) + 1, + null, + ...Object.entries(oo) + ) + ) +} + +const EXPORT_WIN = { + id: 'export-win-id', + name_of_export: 'Rubber chicken', + lead_officer: { + name: 'Leo Jacobs', + }, + country: { + name: 'Australia', + }, + comments: 'Lorem ipsum dolor sit amet', + total_expected_export_value: 2528571, + date: '2024-03-26T14:29:01.521Z', + goods_vs_services: { + name: 'Services', + }, + is_personally_confirmed: false, + breakdowns: [ + { + type: { + name: 'Export', + }, + value: 123456, + year: 3, + }, + ], +} + +const EXPECTED_ROWS = { + 'Goods or services': EXPORT_WIN.goods_vs_services.name, + 'Destination country': EXPORT_WIN.country.name, + 'Total value': '£123,456 over 1 year', + 'Date won': '26 Mar 2024', + 'Lead officer name': 'Leo Jacobs', + Comments: EXPORT_WIN.comments, + 'Export win confirmed': 'No', +} + +describe('ExportWins/Details', () => { + const title = `${EXPORT_WIN.name_of_export} to ${EXPORT_WIN.country.name}` + + const Component = ({ exportWinAPIResponse }) => ( + exportWinAPIResponse }} + > +
) - context('when rendering the breadcrumbs', () => { - it('should render...', () => { - cy.mount() - assertBreadcrumbs({ - Home: urls.dashboard.index(), - 'Export wins': urls.companies.exportWins.index(), - 'Rolls Reece cars to United Arab Emirates': null, + ;[ + { + testTitle: 'Confirmed', + exportWinAPIResponse: { + ...EXPORT_WIN, + is_personally_confirmed: true, + }, + tableRows: { + ...EXPECTED_ROWS, + 'Export win confirmed': 'Yes', + }, + shouldHaveCustomerFeedbackLink: true, + }, + { + testTitle: 'Unconfirmed', + exportWinAPIResponse: { + ...EXPORT_WIN, + is_personally_confirmed: false, + }, + tableRows: { + ...EXPECTED_ROWS, + 'Export win confirmed': 'No', + }, + }, + { + testTitle: 'Single Foo breakdown', + exportWinAPIResponse: { + ...EXPORT_WIN, + breakdowns: [ + { + type: { name: 'Foo' }, + value: 111111, + year: 1, + }, + ], + }, + tableRows: { + ...EXPECTED_ROWS, + 'Total value': '£111,111 over 1 year', + }, + }, + { + testTitle: 'Single Bar breakdown', + exportWinAPIResponse: { + ...EXPORT_WIN, + breakdowns: [ + { + type: { name: 'Bar' }, + value: 222222, + year: 1, + }, + ], + }, + tableRows: { + ...EXPECTED_ROWS, + 'Total value': '£222,222 over 1 year', + }, + }, + { + testTitle: 'Two Bar breakdowns', + exportWinAPIResponse: { + ...EXPORT_WIN, + breakdowns: [ + { + type: { name: 'Bar' }, + value: 222222, + year: 2, + }, + { + type: { name: 'Bar' }, + value: 111111, + year: 2, + }, + ], + }, + tableRows: { + ...EXPECTED_ROWS, + 'Total value': '£333,333 over 1 year', + }, + }, + { + testTitle: 'Three Foo breakdowns', + exportWinAPIResponse: { + ...EXPORT_WIN, + breakdowns: [ + { + type: { name: 'Foo' }, + value: 222222, + year: 3, + }, + { + type: { name: 'Foo' }, + value: 111111, + year: 3, + }, + { + type: { name: 'Foo' }, + value: 333333, + year: 3, + }, + ], + }, + tableRows: { + ...EXPECTED_ROWS, + 'Total value': '£666,666 over 1 year', + }, + }, + { + testTitle: 'Three different 1 year breakdowns', + exportWinAPIResponse: { + ...EXPORT_WIN, + breakdowns: [ + { + type: { name: 'Foo' }, + value: 222222, + year: 4, + }, + { + type: { name: 'Bar' }, + value: 111111, + year: 4, + }, + { + type: { name: 'Baz' }, + value: 333333, + year: 4, + }, + ], + }, + tableRows: insertAfter( + { + ...EXPECTED_ROWS, + 'Total value': '£666,666 over 1 year', + }, + 'Destination country', + { + Foo: '£222,222 over 1 year', + Bar: '£111,111 over 1 year', + Baz: '£333,333 over 1 year', + } + ), + }, + { + testTitle: 'Two different breakdowns over 5 years', + exportWinAPIResponse: { + ...EXPORT_WIN, + breakdowns: [ + { + type: { name: 'Foo' }, + value: 3, + year: 1, + }, + { + type: { name: 'Bar' }, + value: 20, + year: 5, + }, + ], + }, + tableRows: insertAfter( + { + ...EXPECTED_ROWS, + 'Total value': '£23 over 5 years', + }, + 'Destination country', + { + Foo: '£3 over 1 year', + Bar: '£20 over 1 year', + } + ), + }, + { + testTitle: 'Three different breakdowns over 5 years', + exportWinAPIResponse: { + ...EXPORT_WIN, + breakdowns: [ + { + type: { name: 'Foo' }, + value: 1, + year: 1, + }, + { + type: { name: 'Baz' }, + value: 100, + year: 2, + }, + { + type: { name: 'Bar' }, + value: 10, + year: 3, + }, + { + type: { name: 'Baz' }, + value: 100, + year: 4, + }, + { + type: { name: 'Foo' }, + value: 1, + year: 3, + }, + { + type: { name: 'Baz' }, + value: 100, + year: 5, + }, + ], + }, + tableRows: insertAfter( + { + ...EXPECTED_ROWS, + 'Total value': '£312 over 5 years', + }, + 'Destination country', + { + Foo: '£2 over 3 years', + Baz: '£300 over 4 years', + Bar: '£10 over 1 year', + } + ), + }, + ].forEach( + ({ + testTitle, + exportWinAPIResponse, + tableRows, + shouldHaveCustomerFeedbackLink, + }) => { + it(testTitle, () => { + const rowEntries = Object.entries(tableRows) + cy.mount() + + assertBreadcrumbs({ + Home: urls.dashboard.index(), + 'Export wins': urls.companies.exportWins.index(), + [title]: null, + }) + + assertLocalHeader(title) + + // TODO: Abstract it away into assertSummaryTableStrict + cy.get('main table tr') + .as('rows') + .should('have.length', rowEntries.length) + + rowEntries.forEach(([key, val], i) => { + cy.get('@rows') + .eq(i) + .within(() => { + cy.get('th').should('have.length', 1).should('have.text', key) + cy.get('td').should('have.length', 1).should('have.text', val) + }) + }) + + assertLink('export-wins-link', '/exportwins') + + if (shouldHaveCustomerFeedbackLink) { + cy.contains('a', 'View customer feedback').should( + 'have.attr', + 'href', + `/exportwins/${EXPORT_WIN.id}/customer-feedback` + ) + } else { + cy.contains('View customer feedback').should('not.exist') + } }) - }) - }) - - context('when rendering the title', () => { - it('should render Export wins', () => { - cy.mount() - cy.get('[data-test="heading"]').should( - 'have.text', - 'Rolls Reece cars to United Arab Emirates' - ) - }) - }) + } + ) }) diff --git a/test/functional/cypress/support/assertions.js b/test/functional/cypress/support/assertions.js index 07bf6df0e18..8de79d1b35b 100644 --- a/test/functional/cypress/support/assertions.js +++ b/test/functional/cypress/support/assertions.js @@ -50,6 +50,8 @@ const assertValueTable = (dataTest, expected) => { }) } +// FIXME: The assertion only asserts subset of rows, +// but it should assert exact list including order const assertSummaryTable = ({ dataTest, heading, @@ -868,6 +870,7 @@ const assertTypeaheadValues = (selector, values) => { * Assert that a link exists and that the href url is correct */ const assertLink = (dataTest, expected) => { + // FIXME: Should also assert the link text cy.get(`[data-test=${dataTest}]`) .should('exist') .should('have.attr', 'href', expected) diff --git a/test/sandbox/routes/v4/export-win/export-win.js b/test/sandbox/routes/v4/export-win/export-win.js index 20e8a5dd391..62e0b82a257 100644 --- a/test/sandbox/routes/v4/export-win/export-win.js +++ b/test/sandbox/routes/v4/export-win/export-win.js @@ -1,11 +1,35 @@ import { faker } from '@faker-js/faker' -const fakeExportWin = (_, i) => ({ - id: `export-win-id-#${i}`, +const fakeBreakdown = () => ({ + id: faker.string.uuid(), + type: { + id: faker.string.uuid(), + name: faker.helpers.arrayElement([ + 'Export', + 'Business success', + 'Outward Direct Investment', + ]), + }, + value: faker.number.int({ + min: 1_000, + max: 10_000_000, + }), + year: faker.number.int({ + min: 1, + max: 5, + }), +}) + +const fakeExportWin = () => ({ + id: faker.string.uuid(), adviser: { id: faker.string.uuid(), name: faker.person.fullName(), }, + lead_officer: { + id: faker.string.uuid(), + name: faker.person.fullName(), + }, company: { id: faker.string.uuid(), name: faker.company.name(), @@ -21,6 +45,7 @@ const fakeExportWin = (_, i) => ({ email: faker.internet.email(), }, ], + name_of_export: faker.commerce.productName(), customer_name: faker.person.fullName(), customer_job_title: faker.person.jobTitle(), customer_email_address: faker.internet.email(), @@ -32,6 +57,11 @@ const fakeExportWin = (_, i) => ({ customer_response: { created_on: faker.date.anytime(), }, + goods_vs_services: { + id: faker.string.uuid(), + name: faker.helpers.arrayElement(['Goods', 'Services']), + }, + breakdowns: faker.helpers.multiple(fakeBreakdown), }) const WON_EXPORT_WINS = Array(123).fill().map(fakeExportWin) @@ -49,3 +79,7 @@ export const getExportWinCollection = (req, res) => { results: exportWins.slice(offset, offset + limit), }) } + +export const getExportWin = (req, res) => { + res.json(fakeExportWin()) +} diff --git a/test/sandbox/server.js b/test/sandbox/server.js index bcddf56dc2c..9a274de1680 100644 --- a/test/sandbox/server.js +++ b/test/sandbox/server.js @@ -285,7 +285,10 @@ import { person, bulkPerson } from './routes/api/consentService.js' import { createCompaniesSearch } from './routes/api/dnbService.js' import { tasks } from './routes/v4/search/tasks.js' -import { getExportWinCollection } from './routes/v4/export-win/export-win.js' +import { + getExportWin, + getExportWinCollection, +} from './routes/v4/export-win/export-win.js' // Data store service (github.com/uktrade/data-store-service) app.get('/api/v1/get-postcode-data/', toRegion) @@ -526,7 +529,8 @@ app.patch('/v4/event/:eventId', patchEvent) app.post('/v4/event', createEvent) // V4 Export Win -app.get('/v4/export-win', getExportWinCollection) +app.get('/v4/export_win', getExportWinCollection) +app.get('/v4/export-win/:exportWinId', getExportWin) // V3 Feature Flag app.get('/v3/feature-flag', featureFlag)