From f85917a6a8a3842599675731476ee36bec7326c4 Mon Sep 17 00:00:00 2001 From: Romain Jamet Date: Tue, 22 Jul 2025 14:43:29 +0200 Subject: [PATCH 1/4] fix(okms): comment create secret wip sections ref: #MANAGER-18309 Signed-off-by: Romain Jamet --- .../secret-manager/pages/create/Create.page.spec.tsx | 3 --- .../pages/create/SecretForm.component.tsx | 10 ++++++---- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/packages/manager/apps/key-management-service/src/modules/secret-manager/pages/create/Create.page.spec.tsx b/packages/manager/apps/key-management-service/src/modules/secret-manager/pages/create/Create.page.spec.tsx index fff35ee2d408..3f2f943fe1bb 100644 --- a/packages/manager/apps/key-management-service/src/modules/secret-manager/pages/create/Create.page.spec.tsx +++ b/packages/manager/apps/key-management-service/src/modules/secret-manager/pages/create/Create.page.spec.tsx @@ -53,9 +53,6 @@ describe('Create secret page test suite', () => { await assertTextVisibility( labels.secretManager.create.secret_section_title, ); - await assertTextVisibility( - labels.secretManager.create.paiement_section_title, - ); }); it('should navigate to the created secret page on submit', async () => { diff --git a/packages/manager/apps/key-management-service/src/modules/secret-manager/pages/create/SecretForm.component.tsx b/packages/manager/apps/key-management-service/src/modules/secret-manager/pages/create/SecretForm.component.tsx index 489581199c10..eb62bf0522c4 100644 --- a/packages/manager/apps/key-management-service/src/modules/secret-manager/pages/create/SecretForm.component.tsx +++ b/packages/manager/apps/key-management-service/src/modules/secret-manager/pages/create/SecretForm.component.tsx @@ -138,16 +138,18 @@ export const SecretForm = ({ domainId }: SecretFormProps) => { )} /> -
+ {/* TEMP: waiting for metadata management */} + {/*
{t('custom_metadata_title')}
{t('metadata_title')} -
+
*/} -
+ {/* TEMP: waiting for payment informations */} + {/*
{t('paiement_section_title')} -
+
*/}
Date: Fri, 25 Jul 2025 11:04:38 +0200 Subject: [PATCH 2/4] feat(okms): add order okms modal on create secret page ref: #MANAGER-18309 Signed-off-by: Romain Jamet --- .../secret-manager/create/Messages_fr_FR.json | 4 + .../OrderOkmsModal.component.tsx | 167 ++++++++++++++++++ .../src/data/hooks/useCheckoutOrder.ts | 22 +++ .../src/data/hooks/useCreateCart.ts | 27 +++ .../src/data/hooks/useOkms.ts | 7 +- .../pages/create/Create.page.tsx | 2 + .../DomainManagement.component.spec.tsx | 14 +- .../create/DomainManagement.component.tsx | 44 +++-- .../pages/create/DomainSelector.component.tsx | 67 +++++-- .../pages/create/OrderOkms/OrderOkms.page.tsx | 6 + .../secret-manager/routes/routes.constants.ts | 3 + .../modules/secret-manager/routes/routes.tsx | 10 +- 12 files changed, 332 insertions(+), 41 deletions(-) create mode 100644 packages/manager/apps/key-management-service/src/common/components/OrderOkmsModal/OrderOkmsModal.component.tsx create mode 100644 packages/manager/apps/key-management-service/src/data/hooks/useCheckoutOrder.ts create mode 100644 packages/manager/apps/key-management-service/src/data/hooks/useCreateCart.ts create mode 100644 packages/manager/apps/key-management-service/src/modules/secret-manager/pages/create/OrderOkms/OrderOkms.page.tsx diff --git a/packages/manager/apps/key-management-service/public/translations/secret-manager/create/Messages_fr_FR.json b/packages/manager/apps/key-management-service/public/translations/secret-manager/create/Messages_fr_FR.json index 3b7a00e2a3e4..dd34bfad0b43 100644 --- a/packages/manager/apps/key-management-service/public/translations/secret-manager/create/Messages_fr_FR.json +++ b/packages/manager/apps/key-management-service/public/translations/secret-manager/create/Messages_fr_FR.json @@ -3,6 +3,10 @@ "domain_section_title": "1. Sélection du domaine", "region_selector_title": "Sélectionnez une région", "domain_selector_title": "Sélectionnez un domaine", + "domain_activation_in_progress": "Veuillez patienter, creation en cours.", + "create_domain_tc_title": "Activation de la région", + "create_domain_tc_description": "Pour activer votre région il est nécessaire de créer un domaine OKMS. Souhaitez-vous continuer l'activation de votre région ?", + "create_domain_tc_confirm_label": "J'ai pris connaissance et j'accepte les conditions générales, l'annexe relative au traitement des données personnelles et les conditions particulières de services d'OVH.", "secret_section_title": "2. Configuration du secret", "path_title": "Path", "path_helper": "Pour créer une hiérarchie, utilisez des '/'. Exemple : prod/database/MySQL", diff --git a/packages/manager/apps/key-management-service/src/common/components/OrderOkmsModal/OrderOkmsModal.component.tsx b/packages/manager/apps/key-management-service/src/common/components/OrderOkmsModal/OrderOkmsModal.component.tsx new file mode 100644 index 000000000000..d0b07f31ec37 --- /dev/null +++ b/packages/manager/apps/key-management-service/src/common/components/OrderOkmsModal/OrderOkmsModal.component.tsx @@ -0,0 +1,167 @@ +import React, { useContext, useEffect, useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import { useNavigate, useParams } from 'react-router-dom'; +import { + OdsButton, + OdsCheckbox, + OdsMessage, + OdsModal, + OdsSpinner, + OdsText, +} from '@ovhcloud/ods-components/react'; +import { Links, LinkType } from '@ovh-ux/manager-react-components'; +import { NAMESPACES } from '@ovh-ux/manager-common-translations'; +import { ShellContext } from '@ovh-ux/manager-react-shell-client'; +import { CreateCartResult } from '@ovh-ux/manager-module-order'; +import { useCreateCart } from '@/data/hooks/useCreateCart'; +import { useCheckoutOrder } from '@/data/hooks/useCheckoutOrder'; + +// custom type for the state +export type OkmsRegionOrderSuccessful = { + orderRegion: string; +}; + +const CancelButton = ({ onClick }: { onClick: () => void }) => { + const { t } = useTranslation(NAMESPACES.ACTIONS); + + return ( + + ); +}; + +const TermsAndConditions = ({ + cart, + onCancel, +}: { + cart: CreateCartResult; + onCancel: () => void; +}) => { + const { t } = useTranslation(['secret-manager/create', NAMESPACES.ERROR]); + const navigate = useNavigate(); + const { region } = useParams(); + const [isContractAccepted, setIsContractAccepted] = useState(false); + + const { mutate, isPending, error } = useCheckoutOrder({ + onSuccess: () => { + const state: OkmsRegionOrderSuccessful = { + orderRegion: region, + }; + navigate('..', { state }); + }, + }); + + return ( + <> +
+ {t('create_domain_tc_title')} + + {t('create_domain_tc_description')} + +
+ {cart.contractList.map(({ name, url }) => ( + + ))} +
+
+ setIsContractAccepted(event.detail.checked)} + /> + +
+ {error && ( + + {t(`${NAMESPACES.ERROR}:error_message`, { + message: error.message, + })} + + )} +
+ + + mutate({ cartId: cart.cartId })} + isLoading={isPending} + /> + + ); +}; + +export const OrderOkmsModal = () => { + const { t } = useTranslation([ + 'secret-manager/create', + NAMESPACES.ERROR, + NAMESPACES.ACTIONS, + ]); + const { environment } = useContext(ShellContext); + const { region } = useParams(); + const { ovhSubsidiary } = environment.getUser(); + const navigate = useNavigate(); + + const cancel = () => { + navigate('..'); + }; + + const { mutate: createCart, data: cart, isPending, error } = useCreateCart( + ovhSubsidiary, + region, + ); + + /* STEP 1 - create cart on mount */ + useEffect(() => { + createCart(); + }, []); + + return ( + + {isPending && ( +
+ +
+ )} + {error && ( + <> + + {t(`${NAMESPACES.ERROR}:error_message`, { + message: error.message, + })} + + + createCart()} + isLoading={isPending} + /> + + )} + {/* STEP 2 - Terms and Conditions */} + {cart && } +
+ ); +}; diff --git a/packages/manager/apps/key-management-service/src/data/hooks/useCheckoutOrder.ts b/packages/manager/apps/key-management-service/src/data/hooks/useCheckoutOrder.ts new file mode 100644 index 000000000000..b2e4cb2f2f25 --- /dev/null +++ b/packages/manager/apps/key-management-service/src/data/hooks/useCheckoutOrder.ts @@ -0,0 +1,22 @@ +import { ApiError, ApiResponse } from '@ovh-ux/manager-core-api'; +import { + Order, + postOrderCartCartIdCheckout, +} from '@ovh-ux/manager-module-order'; +import { useMutation, UseMutationOptions } from '@tanstack/react-query'; + +export const useCheckoutOrder = ( + options: Partial< + UseMutationOptions, ApiError, { cartId: string }> + > = {}, +) => { + return useMutation, ApiError, { cartId: string }>({ + mutationFn: ({ cartId }) => + postOrderCartCartIdCheckout({ + cartId, + autoPayWithPreferredPaymentMethod: true, + waiveRetractationPeriod: true, + }), + ...(options ?? {}), + }); +}; diff --git a/packages/manager/apps/key-management-service/src/data/hooks/useCreateCart.ts b/packages/manager/apps/key-management-service/src/data/hooks/useCreateCart.ts new file mode 100644 index 000000000000..d00b9203039d --- /dev/null +++ b/packages/manager/apps/key-management-service/src/data/hooks/useCreateCart.ts @@ -0,0 +1,27 @@ +import { ApiError } from '@ovh-ux/manager-core-api'; +import { createCart, CreateCartResult } from '@ovh-ux/manager-module-order'; +import { useMutation } from '@tanstack/react-query'; + +/* CREATE DEFAULT OKMS CART */ +const createOkmsCart = async (ovhSubsidiary: string, regionId: string) => + createCart({ + ovhSubsidiary, + items: [ + { + itemEndpoint: 'okms', + options: { + duration: 'P1M', + planCode: 'okms', + pricingMode: 'default', + quantity: 1, + }, + configurations: [{ label: 'region', value: regionId }], + }, + ], + }); + +export const useCreateCart = (ovhSubsidiary: string, regionId: string) => { + return useMutation({ + mutationFn: () => createOkmsCart(ovhSubsidiary, regionId), + }); +}; diff --git a/packages/manager/apps/key-management-service/src/data/hooks/useOkms.ts b/packages/manager/apps/key-management-service/src/data/hooks/useOkms.ts index dc6d44d011cf..eb37ca5cedaf 100644 --- a/packages/manager/apps/key-management-service/src/data/hooks/useOkms.ts +++ b/packages/manager/apps/key-management-service/src/data/hooks/useOkms.ts @@ -1,4 +1,4 @@ -import { useQuery } from '@tanstack/react-query'; +import { useQuery, UseQueryOptions } from '@tanstack/react-query'; import { useResourcesIcebergV2 } from '@ovh-ux/manager-react-components'; import { OKMS } from '@/types/okms.type'; import { ErrorResponse } from '@/types/api.type'; @@ -15,10 +15,13 @@ export const useOkmsById = (okmsId: string) => { }); }; -export const useOkmsList = () => { +export const useOkmsList = ( + options: Partial> = {}, +) => { return useQuery({ queryKey: okmsQueryKeys.list, queryFn: getOkmsList, + ...options, }); }; diff --git a/packages/manager/apps/key-management-service/src/modules/secret-manager/pages/create/Create.page.tsx b/packages/manager/apps/key-management-service/src/modules/secret-manager/pages/create/Create.page.tsx index c2e858f7af1d..07f7c1a84927 100644 --- a/packages/manager/apps/key-management-service/src/modules/secret-manager/pages/create/Create.page.tsx +++ b/packages/manager/apps/key-management-service/src/modules/secret-manager/pages/create/Create.page.tsx @@ -1,4 +1,5 @@ import React, { useState } from 'react'; +import { Outlet } from 'react-router-dom'; import { BaseLayout, Notifications } from '@ovh-ux/manager-react-components'; import { useTranslation } from 'react-i18next'; import { DomainManagement } from './DomainManagement.component'; @@ -20,6 +21,7 @@ export default function SecretCreatePage() { />
+ ); } diff --git a/packages/manager/apps/key-management-service/src/modules/secret-manager/pages/create/DomainManagement.component.spec.tsx b/packages/manager/apps/key-management-service/src/modules/secret-manager/pages/create/DomainManagement.component.spec.tsx index 97695b7c3448..5465a9412796 100644 --- a/packages/manager/apps/key-management-service/src/modules/secret-manager/pages/create/DomainManagement.component.spec.tsx +++ b/packages/manager/apps/key-management-service/src/modules/secret-manager/pages/create/DomainManagement.component.spec.tsx @@ -43,6 +43,10 @@ vi.mock('@/data/api/orderCatalogOKMS', () => ({ getOrderCatalogOKMS: vi.fn(), })); +const location = { + state: '', +}; + vi.mock('react-router-dom', async (importOriginal) => { const module: typeof import('react-router-dom') = await importOriginal(); return { @@ -50,6 +54,7 @@ vi.mock('react-router-dom', async (importOriginal) => { useNavigate: () => vi.fn(), useHref: vi.fn((link) => link), useSearchParams: vi.fn(), + useLocation: () => location, }; }); @@ -246,9 +251,6 @@ describe('Domain management test suite', () => { await assertDomainsAreNotInTheDocument(okmsMock); await assertActivateButtonIsInTheDocument(); // assert we reset the selectedDomainId - await waitFor(() => - expect(setSelectedDomainIdMocked).toHaveBeenCalledTimes(1), - ); expect(setSelectedDomainIdMocked).toHaveBeenCalledWith(undefined); }); @@ -270,9 +272,6 @@ describe('Domain management test suite', () => { await assertDomainsAreNotInTheDocument(okmsMock); await assertActivateButtonIsNotInTheDocument(); // assert we select the region's domain - await waitFor(() => - expect(setSelectedDomainIdMocked).toHaveBeenCalledTimes(1), - ); expect(setSelectedDomainIdMocked).toHaveBeenCalledWith( regionWithOneOkms.okmsMock[0].id, ); @@ -305,9 +304,6 @@ describe('Domain management test suite', () => { expect(domainRadioCard).toBeChecked(); }); // assert we set the selected domain id - await waitFor(() => - expect(setSelectedDomainIdMocked).toHaveBeenCalledTimes(1), - ); expect(setSelectedDomainIdMocked).toHaveBeenCalledWith( regionWithMultipleOkms.okmsMock[0].id, ); diff --git a/packages/manager/apps/key-management-service/src/modules/secret-manager/pages/create/DomainManagement.component.tsx b/packages/manager/apps/key-management-service/src/modules/secret-manager/pages/create/DomainManagement.component.tsx index f1a695da5775..53e279d77464 100644 --- a/packages/manager/apps/key-management-service/src/modules/secret-manager/pages/create/DomainManagement.component.tsx +++ b/packages/manager/apps/key-management-service/src/modules/secret-manager/pages/create/DomainManagement.component.tsx @@ -1,7 +1,7 @@ import React, { useContext, useEffect, useState } from 'react'; import { NAMESPACES } from '@ovh-ux/manager-common-translations'; import { useTranslation } from 'react-i18next'; -import { useSearchParams } from 'react-router-dom'; +import { useLocation, useSearchParams } from 'react-router-dom'; import { SECRET_MANAGER_SEARCH_PARAMS } from '@secret-manager/routes/routes.constants'; import { filterDomainsByRegion } from '@secret-manager/utils/domains'; import { ShellContext } from '@ovh-ux/manager-react-shell-client'; @@ -12,9 +12,9 @@ import { } from '@ovhcloud/ods-components/react'; import { useOkmsList } from '@/data/hooks/useOkms'; import { useOrderCatalogOkms } from '@/data/hooks/useOrderCatalogOkms'; -import { OKMS } from '@/types/okms.type'; import { DomainSelector } from './DomainSelector.component'; import { RegionSelector } from './RegionSelector.component'; +import { OkmsRegionOrderSuccessful } from '@/common/components/OrderOkmsModal/OrderOkmsModal.component'; type DomainManagementProps = { selectedDomainId: string; @@ -36,13 +36,36 @@ export const DomainManagement = ({ const regions = orderCatalogOKMS?.plans[0]?.configurations[0]?.values; const [selectedRegion, setSelectedRegion] = useState(); - const [regionDomains, setRegionDomains] = useState([]); + const [updatingOkmsList, setUpdatingOkmsList] = useState(false); + + const location = useLocation(); + const state = location.state as OkmsRegionOrderSuccessful; + + // if there is an okms order, begin refreshing the okms list + useEffect(() => { + if (state?.orderRegion) setUpdatingOkmsList(true); + }, [state]); const { data: domains, error: okmsError, isLoading: isOkmsListLoading, - } = useOkmsList(); + } = useOkmsList({ + refetchInterval: (query) => { + if (!updatingOkmsList) return false; + + // refresh the list until there's one okms on the order region. + const domainList = filterDomainsByRegion( + query.state.data, + state?.orderRegion, + ); + if (domainList.length === 0) return 2000; + + // finish updating the list + setUpdatingOkmsList(false); + return false; + }, + }); const [searchParams, setSearchParams] = useSearchParams(); @@ -57,7 +80,7 @@ export const DomainManagement = ({ SECRET_MANAGER_SEARCH_PARAMS.domainId, ); - if (!domains || !domainIdSearchParam) return; + if (!domains || selectedRegion) return; const domainFromSearchParam = domains.find( (domain) => domain.id === domainIdSearchParam, @@ -72,9 +95,6 @@ export const DomainManagement = ({ setSelectedRegion(domainFromSearchParam.region); setSelectedDomainId(domainIdSearchParam); - setRegionDomains( - filterDomainsByRegion(domains, domainFromSearchParam.region), - ); }, [domains, searchParams]); const handleRegionSelection = (region: string) => { @@ -84,8 +104,6 @@ export const DomainManagement = ({ const filteredDomainList = filterDomainsByRegion(domains, region); - setRegionDomains(filteredDomainList); - if (filteredDomainList.length === 0) { setSelectedDomainId(undefined); return; @@ -120,9 +138,11 @@ export const DomainManagement = ({ setSelectedRegion={handleRegionSelection} /> ); diff --git a/packages/manager/apps/key-management-service/src/modules/secret-manager/pages/create/DomainSelector.component.tsx b/packages/manager/apps/key-management-service/src/modules/secret-manager/pages/create/DomainSelector.component.tsx index 23ecd3007e93..9022f6ecc73b 100644 --- a/packages/manager/apps/key-management-service/src/modules/secret-manager/pages/create/DomainSelector.component.tsx +++ b/packages/manager/apps/key-management-service/src/modules/secret-manager/pages/create/DomainSelector.component.tsx @@ -1,28 +1,60 @@ import React from 'react'; import { useTranslation } from 'react-i18next'; +import { useNavigate } from 'react-router-dom'; import { TagsList, useServiceDetails } from '@ovh-ux/manager-react-components'; import { OdsButton, OdsSkeleton, + OdsSpinner, OdsText, } from '@ovhcloud/ods-components/react'; import { ODS_BADGE_SIZE } from '@ovhcloud/ods-components'; import { ACTIVATE_DOMAIN_BTN_TEST_ID } from '@secret-manager/utils/tests/secret.constants'; +import { SECRET_MANAGER_ROUTES_URLS } from '@secret-manager/routes/routes.constants'; import { NAMESPACES } from '@ovh-ux/manager-common-translations'; import { OKMS } from '@/types/okms.type'; import { OkmsServiceState } from '@/components/layout-helpers/Dashboard/okmsServiceState/OkmsServiceState.component'; import { RadioCard } from '@/common/components/RadioCard/RadioCard.component'; +const ActivationInProgress = () => { + const { t } = useTranslation('secret-manager/create'); + + return ( +
+ + {t('domain_activation_in_progress')} +
+ ); +}; + +const ActivateRegion = ({ selectedRegion }: { selectedRegion: string }) => { + const { t } = useTranslation(NAMESPACES.ACTIONS); + const navigate = useNavigate(); + + return ( +
+ + navigate( + SECRET_MANAGER_ROUTES_URLS.secretCreateOrderOkms(selectedRegion), + ) + } + /> +
+ ); +}; + const DomainStatus = ({ id }: { id: string }) => { const { data: OkmsServiceInfos, isLoading, isError } = useServiceDetails({ resourceName: id, }); - if (isLoading) { - return ; - } - if (isError) { - return <>; - } + + if (isLoading) return ; + + if (isError) return null; + return ( { type DomainSelectorProps = { domains: OKMS[]; selectedDomain: string; + selectedRegion: string; + updatingOkmsList: boolean; onDomainSelection: (domainId: string) => void; }; export const DomainSelector = ({ domains, - onDomainSelection, selectedDomain, + selectedRegion, + updatingOkmsList, + onDomainSelection, }: DomainSelectorProps) => { - const { t } = useTranslation(['secret-manager/create', NAMESPACES.ACTIONS]); + const { t } = useTranslation('secret-manager/create'); + + if (!selectedRegion || domains.length === 1) return null; if (domains.length === 0) { - return ( - + return updatingOkmsList ? ( + + ) : ( + ); } - if (domains.length === 1) { - return null; - } - return (
{t('domain_selector_title')} diff --git a/packages/manager/apps/key-management-service/src/modules/secret-manager/pages/create/OrderOkms/OrderOkms.page.tsx b/packages/manager/apps/key-management-service/src/modules/secret-manager/pages/create/OrderOkms/OrderOkms.page.tsx new file mode 100644 index 000000000000..5f42b992f330 --- /dev/null +++ b/packages/manager/apps/key-management-service/src/modules/secret-manager/pages/create/OrderOkms/OrderOkms.page.tsx @@ -0,0 +1,6 @@ +import React from 'react'; +import { OrderOkmsModal } from '@/common/components/OrderOkmsModal/OrderOkmsModal.component'; + +export default function OrderOkms() { + return ; +} diff --git a/packages/manager/apps/key-management-service/src/modules/secret-manager/routes/routes.constants.ts b/packages/manager/apps/key-management-service/src/modules/secret-manager/routes/routes.constants.ts index aba3c2dad2f6..e8765c64698f 100644 --- a/packages/manager/apps/key-management-service/src/modules/secret-manager/routes/routes.constants.ts +++ b/packages/manager/apps/key-management-service/src/modules/secret-manager/routes/routes.constants.ts @@ -6,12 +6,15 @@ const URIS = { region: 'region', versions: 'versions', create: 'create', + order: 'order', }; const URLS = { secretManagerRoot: `/${URIS.root}`, secretManagerOnboarding: `/${URIS.root}/${URIS.onboarding}`, secretCreate: `/${URIS.root}/${URIS.create}`, + secretCreateOrderOkms: (regionId: string) => + `/${URIS.root}/${URIS.create}/${URIS.order}/${regionId}`, secretDomains: (regionId: string) => `/${URIS.root}/${URIS.region}/${regionId}`, secretListing: (domainId: string) => `/${URIS.root}/${domainId}`, diff --git a/packages/manager/apps/key-management-service/src/modules/secret-manager/routes/routes.tsx b/packages/manager/apps/key-management-service/src/modules/secret-manager/routes/routes.tsx index d9b4c79a0a0f..98a5f66bbdfd 100644 --- a/packages/manager/apps/key-management-service/src/modules/secret-manager/routes/routes.tsx +++ b/packages/manager/apps/key-management-service/src/modules/secret-manager/routes/routes.tsx @@ -34,6 +34,9 @@ const SecretVersions = React.lazy(() => const SecretCreate = React.lazy(() => import('@/modules/secret-manager/pages/create/Create.page'), ); +const OrderOkms = React.lazy(() => + import('@/modules/secret-manager/pages/create/OrderOkms/OrderOkms.page'), +); export default ( - + + + Date: Tue, 29 Jul 2025 13:53:10 +0200 Subject: [PATCH 3/4] test(okms): add order okms tests on create secret page ref: #MANAGER-18309 Signed-off-by: Romain Jamet --- .../OrderOkmsModal.component.constants.ts | 7 + .../OrderOkmsModal.component.spec.tsx | 311 ++++++++++++++++++ .../OrderOkmsModal.component.tsx | 21 +- .../mocks/secrets/secrets.handler.ts | 8 +- .../create/ActivateRegion.component.spec.tsx | 102 ++++++ .../pages/create/ActivateRegion.component.tsx | 42 +++ .../pages/create/Create.page.spec.tsx | 8 +- .../DomainManagement.component.spec.tsx | 102 ++++-- .../create/DomainManagement.component.tsx | 2 +- .../pages/create/DomainSelector.component.tsx | 55 +--- .../utils/tests/secret.constants.ts | 3 +- .../src/utils/tests/init.i18n.ts | 3 + 12 files changed, 584 insertions(+), 80 deletions(-) create mode 100644 packages/manager/apps/key-management-service/src/common/components/OrderOkmsModal/OrderOkmsModal.component.constants.ts create mode 100644 packages/manager/apps/key-management-service/src/common/components/OrderOkmsModal/OrderOkmsModal.component.spec.tsx create mode 100644 packages/manager/apps/key-management-service/src/modules/secret-manager/pages/create/ActivateRegion.component.spec.tsx create mode 100644 packages/manager/apps/key-management-service/src/modules/secret-manager/pages/create/ActivateRegion.component.tsx diff --git a/packages/manager/apps/key-management-service/src/common/components/OrderOkmsModal/OrderOkmsModal.component.constants.ts b/packages/manager/apps/key-management-service/src/common/components/OrderOkmsModal/OrderOkmsModal.component.constants.ts new file mode 100644 index 000000000000..c6ddeda98cc1 --- /dev/null +++ b/packages/manager/apps/key-management-service/src/common/components/OrderOkmsModal/OrderOkmsModal.component.constants.ts @@ -0,0 +1,7 @@ +export const ORDER_OKMS_CREATE_CART_SPINNER_TEST_ID = 'create-cart-spinner'; +export const ORDER_OKMS_CREATE_CANCEL_BUTTON_TEST_ID = + 'create-cart-cancel-button'; +export const ORDER_OKMS_CREATE_RETRY_BUTTON_TEST_ID = + 'create-cart-retry-button'; +export const ORDER_OKMS_TC_CONFIRM_CHECKBOX_TEST_ID = 'tc-confirm-checkbox'; +export const ORDER_OKMS_TC_CONFIRM_BUTTON_TEST_ID = 'tc-confirm-button'; diff --git a/packages/manager/apps/key-management-service/src/common/components/OrderOkmsModal/OrderOkmsModal.component.spec.tsx b/packages/manager/apps/key-management-service/src/common/components/OrderOkmsModal/OrderOkmsModal.component.spec.tsx new file mode 100644 index 000000000000..4f85eb3dd8d1 --- /dev/null +++ b/packages/manager/apps/key-management-service/src/common/components/OrderOkmsModal/OrderOkmsModal.component.spec.tsx @@ -0,0 +1,311 @@ +import React from 'react'; +import { i18n } from 'i18next'; +import { AxiosResponse } from 'axios'; +import { I18nextProvider } from 'react-i18next'; +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; +import { vi } from 'vitest'; +import { act, render, screen, waitFor } from '@testing-library/react'; +import userEvent, { UserEvent } from '@testing-library/user-event'; +import { + ShellContext, + ShellContextType, +} from '@ovh-ux/manager-react-shell-client'; +import { + Contract, + createCart, + Order, + postOrderCartCartIdCheckout, +} from '@ovh-ux/manager-module-order'; +import { assertTextVisibility } from '@ovh-ux/manager-core-test-utils'; +import { initTestI18n, labels } from '@/utils/tests/init.i18n'; +import { + OkmsRegionOrderSuccessful, + OrderOkmsModal, +} from './OrderOkmsModal.component'; +import { + ORDER_OKMS_CREATE_CANCEL_BUTTON_TEST_ID, + ORDER_OKMS_CREATE_CART_SPINNER_TEST_ID, + ORDER_OKMS_CREATE_RETRY_BUTTON_TEST_ID, + ORDER_OKMS_TC_CONFIRM_BUTTON_TEST_ID, + ORDER_OKMS_TC_CONFIRM_CHECKBOX_TEST_ID, +} from './OrderOkmsModal.component.constants'; + +let i18nValue: i18n; + +const shellContext = { + environment: { + getUser: () => ({ ovhSubsidiary: 'mocked_ovhSubsidiary' }), + }, +}; + +const mockedRegion = 'mocked-region'; + +const mockedContracts: Contract[] = [ + { + content: 'contract-1-content', + name: 'contract-1', + url: 'contrat-1-url', + }, + { + content: 'contract-2-content', + name: 'contract-2', + url: 'contrat-2-url', + }, +]; + +const navigate = vi.fn(); + +vi.mock('react-router-dom', async (importOriginal) => { + const module: typeof import('react-router-dom') = await importOriginal(); + return { + ...module, + useNavigate: () => navigate, + useParams: () => ({ region: mockedRegion }), + }; +}); + +vi.mock('@ovh-ux/manager-module-order', async (importOriginal) => { + const module: typeof import('@ovh-ux/manager-module-order') = await importOriginal(); + return { + ...module, + createCart: vi.fn(), + postOrderCartCartIdCheckout: vi.fn(), + }; +}); + +const renderOrderOkmsModal = async () => { + const queryClient = new QueryClient(); + if (!i18nValue) { + i18nValue = await initTestI18n(); + } + + return render( + + + + + + + , + ); +}; + +const clickOnConfirmCheckbox = async () => { + const confirmCheckbox = screen.getByTestId( + ORDER_OKMS_TC_CONFIRM_CHECKBOX_TEST_ID, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + ) as any; + await act(() => { + confirmCheckbox.odsChange.emit({ + checked: 'true', + }); + }); +}; + +const clickOnConfirmButton = async (user: UserEvent) => { + const confirmButton = screen.getByTestId( + ORDER_OKMS_TC_CONFIRM_BUTTON_TEST_ID, + ); + await waitFor(() => { + expect(confirmButton).toHaveAttribute('is-disabled', 'false'); + }); + await act(() => user.click(confirmButton)); + return confirmButton; +}; + +describe('Order Okms Modal test suite', () => { + beforeEach(() => { + vi.resetAllMocks(); + }); + describe('on init', () => { + it('should display a loading state when creating a cart', async () => { + // GIVEN + vi.mocked(createCart).mockImplementation(() => new Promise(() => {})); + + // WHEN + await renderOrderOkmsModal(); + + // THEN + await waitFor(() => { + expect( + screen.getByTestId(ORDER_OKMS_CREATE_CART_SPINNER_TEST_ID), + ).toBeVisible(); + }); + }); + + it('should display terms and conditions on cart succesful creation', async () => { + // GIVEN + vi.mocked(createCart).mockResolvedValueOnce({ + cartId: 'cart-id', + contractList: [], + }); + + // WHEN + await renderOrderOkmsModal(); + + // THEN + await assertTextVisibility( + labels.secretManager.create.create_domain_tc_title, + ); + }); + + it('should display a notification and a retry button on error', async () => { + // GIVEN + const mockError = new Error('Failed to create cart'); + vi.mocked(createCart).mockRejectedValue(mockError); + + // WHEN + await renderOrderOkmsModal(); + + // THEN + await assertTextVisibility( + labels.common.error.error_message.replace( + '{{message}}', + mockError.message, + ), + ); + expect( + screen.getByTestId(ORDER_OKMS_CREATE_CANCEL_BUTTON_TEST_ID), + ).toBeVisible(); + expect( + screen.getByTestId(ORDER_OKMS_CREATE_RETRY_BUTTON_TEST_ID), + ).toBeVisible(); + }); + }); + + describe('on terms and conditions', () => { + beforeEach(() => { + vi.resetAllMocks(); + vi.mocked(createCart).mockResolvedValue({ + cartId: 'cart-id', + contractList: mockedContracts, + }); + }); + + it('should display terms and conditions', async () => { + // GIVEN + // WHEN + await renderOrderOkmsModal(); + + // THEN + await assertTextVisibility( + labels.secretManager.create.create_domain_tc_title, + ); + await assertTextVisibility( + labels.secretManager.create.create_domain_tc_description, + ); + + mockedContracts.forEach(async (contract) => { + await assertTextVisibility(contract.name); + }); + + const confirmCheckbox = screen.getByTestId( + ORDER_OKMS_TC_CONFIRM_CHECKBOX_TEST_ID, + ); + expect(confirmCheckbox).toBeVisible(); + expect(confirmCheckbox).toHaveAttribute('is-checked', 'false'); + + expect( + screen.getByTestId(ORDER_OKMS_CREATE_CANCEL_BUTTON_TEST_ID), + ).toBeVisible(); + + const confirmButton = screen.getByTestId( + ORDER_OKMS_TC_CONFIRM_BUTTON_TEST_ID, + ); + expect(confirmButton).toBeVisible(); + expect(confirmButton).toHaveAttribute('is-disabled', 'true'); + }); + + it('should enable confirm button on condition approval', async () => { + // GIVEN + await renderOrderOkmsModal(); + await assertTextVisibility( + labels.secretManager.create.create_domain_tc_title, + ); + + // WHEN + await clickOnConfirmCheckbox(); + + // THEN + const confirmButton = screen.getByTestId( + ORDER_OKMS_TC_CONFIRM_BUTTON_TEST_ID, + ); + expect(confirmButton).toHaveAttribute('is-disabled', 'false'); + }); + + it('should show a loading button on confirmation', async () => { + const user = userEvent.setup(); + + // GIVEN + vi.mocked(postOrderCartCartIdCheckout).mockImplementation( + () => new Promise(() => {}), + ); + await renderOrderOkmsModal(); + await assertTextVisibility( + labels.secretManager.create.create_domain_tc_title, + ); + + // WHEN + await clickOnConfirmCheckbox(); + const confirmButton = await clickOnConfirmButton(user); + + // THEN + await waitFor(() => { + expect(confirmButton).toHaveAttribute('is-loading', 'true'); + }); + }); + + it('should close the modal on success', async () => { + const user = userEvent.setup(); + // GIVEN + vi.mocked(postOrderCartCartIdCheckout).mockResolvedValueOnce( + {} as AxiosResponse, + ); + + await renderOrderOkmsModal(); + await assertTextVisibility( + labels.secretManager.create.create_domain_tc_title, + ); + + // WHEN + await clickOnConfirmCheckbox(); + await clickOnConfirmButton(user); + + // THEN + const state: OkmsRegionOrderSuccessful = { + orderRegion: mockedRegion, + }; + + await waitFor(() => { + expect(navigate).toHaveBeenCalledTimes(1); + }); + expect(navigate).toHaveBeenCalledWith('..', { state }); + }); + + it('should display a notification on error', async () => { + const user = userEvent.setup(); + // GIVEN + const mockError = new Error('Failed to submit order'); + vi.mocked(postOrderCartCartIdCheckout).mockRejectedValueOnce(mockError); + + await renderOrderOkmsModal(); + await assertTextVisibility( + labels.secretManager.create.create_domain_tc_title, + ); + + // WHEN + await clickOnConfirmCheckbox(); + await clickOnConfirmButton(user); + + // THEN + await assertTextVisibility( + labels.common.error.error_message.replace( + '{{message}}', + mockError.message, + ), + ); + }); + }); +}); diff --git a/packages/manager/apps/key-management-service/src/common/components/OrderOkmsModal/OrderOkmsModal.component.tsx b/packages/manager/apps/key-management-service/src/common/components/OrderOkmsModal/OrderOkmsModal.component.tsx index d0b07f31ec37..1e5b019a4cc5 100644 --- a/packages/manager/apps/key-management-service/src/common/components/OrderOkmsModal/OrderOkmsModal.component.tsx +++ b/packages/manager/apps/key-management-service/src/common/components/OrderOkmsModal/OrderOkmsModal.component.tsx @@ -15,6 +15,13 @@ import { ShellContext } from '@ovh-ux/manager-react-shell-client'; import { CreateCartResult } from '@ovh-ux/manager-module-order'; import { useCreateCart } from '@/data/hooks/useCreateCart'; import { useCheckoutOrder } from '@/data/hooks/useCheckoutOrder'; +import { + ORDER_OKMS_CREATE_CANCEL_BUTTON_TEST_ID, + ORDER_OKMS_CREATE_CART_SPINNER_TEST_ID, + ORDER_OKMS_CREATE_RETRY_BUTTON_TEST_ID, + ORDER_OKMS_TC_CONFIRM_BUTTON_TEST_ID, + ORDER_OKMS_TC_CONFIRM_CHECKBOX_TEST_ID, +} from './OrderOkmsModal.component.constants'; // custom type for the state export type OkmsRegionOrderSuccessful = { @@ -26,6 +33,7 @@ const CancelButton = ({ onClick }: { onClick: () => void }) => { return ( void; }) => { - const { t } = useTranslation(['secret-manager/create', NAMESPACES.ERROR]); + const { t } = useTranslation([ + 'secret-manager/create', + NAMESPACES.ERROR, + NAMESPACES.ACTIONS, + ]); const navigate = useNavigate(); const { region } = useParams(); const [isContractAccepted, setIsContractAccepted] = useState(false); @@ -76,6 +88,7 @@ const TermsAndConditions = ({
mutate({ cartId: cart.cartId })} isLoading={isPending} @@ -137,7 +151,7 @@ export const OrderOkmsModal = () => { {isPending && (
- +
)} {error && ( @@ -153,6 +167,7 @@ export const OrderOkmsModal = () => { createCart()} diff --git a/packages/manager/apps/key-management-service/src/modules/secret-manager/mocks/secrets/secrets.handler.ts b/packages/manager/apps/key-management-service/src/modules/secret-manager/mocks/secrets/secrets.handler.ts index 7de262a4517d..3ea4e24a5413 100644 --- a/packages/manager/apps/key-management-service/src/modules/secret-manager/mocks/secrets/secrets.handler.ts +++ b/packages/manager/apps/key-management-service/src/modules/secret-manager/mocks/secrets/secrets.handler.ts @@ -68,6 +68,9 @@ export const getSecretMock = ({ ]; // POST + +export const createSecretErrorMessage = 'create-secret-error-message'; + export type CreateSecretsMockParams = { isCreateSecretKO?: boolean; }; @@ -79,10 +82,7 @@ export const createSecretsMock = ({ url: '/okms/resource/:okmsId/secret', response: isCreateSecretKO ? { - status: 500, - data: { - message: 'createsecrets error', - }, + message: createSecretErrorMessage, } : createSecretResponseMock, status: isCreateSecretKO ? 500 : 200, diff --git a/packages/manager/apps/key-management-service/src/modules/secret-manager/pages/create/ActivateRegion.component.spec.tsx b/packages/manager/apps/key-management-service/src/modules/secret-manager/pages/create/ActivateRegion.component.spec.tsx new file mode 100644 index 000000000000..6f739605ee76 --- /dev/null +++ b/packages/manager/apps/key-management-service/src/modules/secret-manager/pages/create/ActivateRegion.component.spec.tsx @@ -0,0 +1,102 @@ +import React from 'react'; +import { i18n } from 'i18next'; +import { I18nextProvider } from 'react-i18next'; +import { SECRET_MANAGER_ROUTES_URLS } from '@secret-manager/routes/routes.constants'; +import userEvent from '@testing-library/user-event'; +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; +import { vi } from 'vitest'; +import { assertTextVisibility } from '@ovh-ux/manager-core-test-utils'; +import { render, screen } from '@testing-library/react'; +import { + ACTIVATE_DOMAIN_BTN_TEST_ID, + ACTIVATE_DOMAIN_SPINNER_TEST_ID, +} from '@secret-manager/utils/tests/secret.constants'; +import { labels, initTestI18n } from '@/utils/tests/init.i18n'; +import { REGION_EU_WEST_RBX } from '@/mocks/catalog/catalog.mock'; +import { + ActivateRegion, + ActivateRegionParams, +} from './ActivateRegion.component'; + +let i18nValue: i18n; + +const navigate = vi.fn(); + +vi.mock('react-router-dom', async (importOriginal) => { + const module: typeof import('react-router-dom') = await importOriginal(); + return { + ...module, + useNavigate: () => navigate, + }; +}); + +const renderActivateRegion = async ({ + isUpdatingOkmsList, + selectedRegion, +}: ActivateRegionParams) => { + const queryClient = new QueryClient(); + if (!i18nValue) { + i18nValue = await initTestI18n(); + } + + return render( + + + + + , + ); +}; + +describe('ActivateRegion test suite', () => { + describe('when okms list is not updating', () => { + it('should render a button to navigate to okms silent creation modal', async () => { + const user = userEvent.setup(); + // GIVEN + const selectedRegionMock = REGION_EU_WEST_RBX; + const updating = false; + + // WHEN + await renderActivateRegion({ + isUpdatingOkmsList: updating, + selectedRegion: selectedRegionMock, + }); + + // THEN + const activateButton = screen.queryByTestId(ACTIVATE_DOMAIN_BTN_TEST_ID); + expect(activateButton).toBeVisible(); + + await user.click(activateButton); + + expect(navigate).toHaveBeenCalledTimes(1); + expect(navigate).toHaveBeenCalledWith( + SECRET_MANAGER_ROUTES_URLS.secretCreateOrderOkms(selectedRegionMock), + ); + }); + }); + + describe('when okms list is updating', () => { + it('should render a loading state', async () => { + // GIVEN + const selectedRegionMock = REGION_EU_WEST_RBX; + const updating = true; + + // WHEN + await renderActivateRegion({ + isUpdatingOkmsList: updating, + selectedRegion: selectedRegionMock, + }); + + // THEN + const Spinner = screen.queryByTestId(ACTIVATE_DOMAIN_SPINNER_TEST_ID); + expect(Spinner).toBeVisible(); + + await assertTextVisibility( + labels.secretManager.create.domain_activation_in_progress, + ); + }); + }); +}); diff --git a/packages/manager/apps/key-management-service/src/modules/secret-manager/pages/create/ActivateRegion.component.tsx b/packages/manager/apps/key-management-service/src/modules/secret-manager/pages/create/ActivateRegion.component.tsx new file mode 100644 index 000000000000..ac2008eecee2 --- /dev/null +++ b/packages/manager/apps/key-management-service/src/modules/secret-manager/pages/create/ActivateRegion.component.tsx @@ -0,0 +1,42 @@ +import React from 'react'; +import { useTranslation } from 'react-i18next'; +import { useNavigate } from 'react-router-dom'; +import { OdsButton, OdsSpinner, OdsText } from '@ovhcloud/ods-components/react'; +import { + ACTIVATE_DOMAIN_BTN_TEST_ID, + ACTIVATE_DOMAIN_SPINNER_TEST_ID, +} from '@secret-manager/utils/tests/secret.constants'; +import { SECRET_MANAGER_ROUTES_URLS } from '@secret-manager/routes/routes.constants'; +import { NAMESPACES } from '@ovh-ux/manager-common-translations'; + +export type ActivateRegionParams = { + selectedRegion: string; + isUpdatingOkmsList: boolean; +}; + +export const ActivateRegion = ({ + selectedRegion, + isUpdatingOkmsList, +}: ActivateRegionParams) => { + const { t } = useTranslation(['secret-manager/create', NAMESPACES.ACTIONS]); + const navigate = useNavigate(); + + return isUpdatingOkmsList ? ( +
+ + {t('domain_activation_in_progress')} +
+ ) : ( +
+ + navigate( + SECRET_MANAGER_ROUTES_URLS.secretCreateOrderOkms(selectedRegion), + ) + } + /> +
+ ); +}; diff --git a/packages/manager/apps/key-management-service/src/modules/secret-manager/pages/create/Create.page.spec.tsx b/packages/manager/apps/key-management-service/src/modules/secret-manager/pages/create/Create.page.spec.tsx index 3f2f943fe1bb..36d6a85a9908 100644 --- a/packages/manager/apps/key-management-service/src/modules/secret-manager/pages/create/Create.page.spec.tsx +++ b/packages/manager/apps/key-management-service/src/modules/secret-manager/pages/create/Create.page.spec.tsx @@ -12,6 +12,7 @@ import { } from '@secret-manager/utils/tests/secret.constants'; import { fireEvent, act, screen } from '@testing-library/react'; import userEvent, { UserEvent } from '@testing-library/user-event'; +import { createSecretErrorMessage } from '@secret-manager/mocks/secrets/secrets.handler'; import { SECRET_MANAGER_ROUTES_URLS } from '@secret-manager/routes/routes.constants'; import { renderTestApp } from '@/utils/tests/renderTestApp'; import { labels } from '@/utils/tests/init.i18n'; @@ -105,6 +106,11 @@ describe('Create secret page test suite', () => { await act(() => user.click(submitButton)); // THEN - await assertTextVisibility('error_message'); + await assertTextVisibility( + labels.common.error.error_message.replace( + '{{message}}', + createSecretErrorMessage, + ), + ); }); }); diff --git a/packages/manager/apps/key-management-service/src/modules/secret-manager/pages/create/DomainManagement.component.spec.tsx b/packages/manager/apps/key-management-service/src/modules/secret-manager/pages/create/DomainManagement.component.spec.tsx index 5465a9412796..205e13ad9dfc 100644 --- a/packages/manager/apps/key-management-service/src/modules/secret-manager/pages/create/DomainManagement.component.spec.tsx +++ b/packages/manager/apps/key-management-service/src/modules/secret-manager/pages/create/DomainManagement.component.spec.tsx @@ -22,7 +22,10 @@ import { vi } from 'vitest'; import { assertTextVisibility } from '@ovh-ux/manager-core-test-utils'; import { waitFor } from '@testing-library/dom'; import { act, render, screen } from '@testing-library/react'; -import { ACTIVATE_DOMAIN_BTN_TEST_ID } from '@secret-manager/utils/tests/secret.constants'; +import { + ACTIVATE_DOMAIN_BTN_TEST_ID, + ACTIVATE_DOMAIN_SPINNER_TEST_ID, +} from '@secret-manager/utils/tests/secret.constants'; import { labels, initTestI18n } from '@/utils/tests/init.i18n'; import { DomainManagement } from './DomainManagement.component'; import { catalogMock } from '@/mocks/catalog/catalog.mock'; @@ -36,6 +39,7 @@ import { useOkmsList } from '@/data/hooks/useOkms'; import { OKMS } from '@/types/okms.type'; import { getOrderCatalogOKMS } from '@/data/api/orderCatalogOKMS'; import { ErrorResponse } from '@/types/api.type'; +import { OkmsRegionOrderSuccessful } from '@/common/components/OrderOkmsModal/OrderOkmsModal.component'; let i18nValue: i18n; @@ -43,8 +47,8 @@ vi.mock('@/data/api/orderCatalogOKMS', () => ({ getOrderCatalogOKMS: vi.fn(), })); -const location = { - state: '', +let useLocationMock: { state: OkmsRegionOrderSuccessful } = { + state: { orderRegion: '' }, }; vi.mock('react-router-dom', async (importOriginal) => { @@ -54,7 +58,7 @@ vi.mock('react-router-dom', async (importOriginal) => { useNavigate: () => vi.fn(), useHref: vi.fn((link) => link), useSearchParams: vi.fn(), - useLocation: () => location, + useLocation: () => useLocationMock, }; }); @@ -181,6 +185,20 @@ const assertActivateButtonIsNotInTheDocument = async () => { }); }; +const assertLoadingListIsInTheDocument = async () => { + const activateSpinner = screen.queryByTestId(ACTIVATE_DOMAIN_SPINNER_TEST_ID); + await waitFor(() => { + expect(activateSpinner).toBeVisible(); + }); +}; + +const assertLoadingListIsNotInTheDocument = async () => { + const activateSpinner = screen.queryByTestId(ACTIVATE_DOMAIN_SPINNER_TEST_ID); + await waitFor(() => { + expect(activateSpinner).toBeNull(); + }); +}; + const selectRegion = async (user: UserEvent, region: string) => { const regionCard = screen.getByTestId(region); @@ -206,14 +224,21 @@ describe('Domain management test suite', () => { it('should display a notification message when error on catalog api', async () => { // GIVEN - vi.mocked(getOrderCatalogOKMS).mockRejectedValueOnce( - new Error('Mocked error'), - ); + const mockError: ErrorResponse = { + response: { data: { message: 'errorCatalog' }, status: 500 }, + }; + vi.mocked(getOrderCatalogOKMS).mockRejectedValue(mockError); + // WHEN await renderDomainManagement(); // THEN - await assertTextVisibility('error_message'); + await assertTextVisibility( + labels.common.error.error_message.replace( + '{{message}}', + mockError.response.data.message, + ), + ); }); it('should display the available region list', async () => { @@ -233,25 +258,54 @@ describe('Domain management test suite', () => { afterEach(() => { vi.clearAllMocks(); }); - it('should display a CTA when there is no domain', async () => { - const user = userEvent.setup(); - // GIVEN - vi.mocked(getOrderCatalogOKMS).mockResolvedValueOnce(catalogMock); + describe('when there is no domain', () => { + it('should display a CTA', async () => { + const user = userEvent.setup(); + // GIVEN + vi.mocked(getOrderCatalogOKMS).mockResolvedValueOnce(catalogMock); + + await renderDomainManagement(); + await assertTextVisibility( + labels.secretManager.create.domain_section_title, + ); + await assertTextVisibility(regionWithoutOkms.region); - await renderDomainManagement(); - await assertTextVisibility( - labels.secretManager.create.domain_section_title, - ); - await assertTextVisibility(regionWithoutOkms.region); + // WHEN + await selectRegion(user, regionWithoutOkms.region); - // WHEN - await selectRegion(user, regionWithoutOkms.region); + // THEN + await assertDomainsAreNotInTheDocument(okmsMock); + await assertActivateButtonIsInTheDocument(); + await assertLoadingListIsNotInTheDocument(); + // assert we reset the selectedDomainId + expect(setSelectedDomainIdMocked).toHaveBeenCalledWith(undefined); + }); - // THEN - await assertDomainsAreNotInTheDocument(okmsMock); - await assertActivateButtonIsInTheDocument(); - // assert we reset the selectedDomainId - expect(setSelectedDomainIdMocked).toHaveBeenCalledWith(undefined); + it('should display a loading state when an order is done', async () => { + const user = userEvent.setup(); + // GIVEN + useLocationMock = { + state: { + orderRegion: 'new-example-region', // New mocked orderRegion value + }, + }; + + vi.mocked(getOrderCatalogOKMS).mockResolvedValueOnce(catalogMock); + + await renderDomainManagement(); + await assertTextVisibility( + labels.secretManager.create.domain_section_title, + ); + await assertTextVisibility(regionWithoutOkms.region); + + // WHEN + await selectRegion(user, regionWithoutOkms.region); + + // THEN + await assertDomainsAreNotInTheDocument(okmsMock); + await assertActivateButtonIsNotInTheDocument(); + await assertLoadingListIsInTheDocument(); + }); }); it('should not display anything when there is exactly one domain', async () => { diff --git a/packages/manager/apps/key-management-service/src/modules/secret-manager/pages/create/DomainManagement.component.tsx b/packages/manager/apps/key-management-service/src/modules/secret-manager/pages/create/DomainManagement.component.tsx index 53e279d77464..c72806b4bb93 100644 --- a/packages/manager/apps/key-management-service/src/modules/secret-manager/pages/create/DomainManagement.component.tsx +++ b/packages/manager/apps/key-management-service/src/modules/secret-manager/pages/create/DomainManagement.component.tsx @@ -141,7 +141,7 @@ export const DomainManagement = ({ domains={filterDomainsByRegion(domains, selectedRegion)} selectedRegion={selectedRegion} selectedDomain={selectedDomainId} - updatingOkmsList={updatingOkmsList} + isUpdatingOkmsList={updatingOkmsList} onDomainSelection={setSelectedDomainId} />
diff --git a/packages/manager/apps/key-management-service/src/modules/secret-manager/pages/create/DomainSelector.component.tsx b/packages/manager/apps/key-management-service/src/modules/secret-manager/pages/create/DomainSelector.component.tsx index 9022f6ecc73b..d21e78a0b724 100644 --- a/packages/manager/apps/key-management-service/src/modules/secret-manager/pages/create/DomainSelector.component.tsx +++ b/packages/manager/apps/key-management-service/src/modules/secret-manager/pages/create/DomainSelector.component.tsx @@ -1,50 +1,12 @@ import React from 'react'; import { useTranslation } from 'react-i18next'; -import { useNavigate } from 'react-router-dom'; import { TagsList, useServiceDetails } from '@ovh-ux/manager-react-components'; -import { - OdsButton, - OdsSkeleton, - OdsSpinner, - OdsText, -} from '@ovhcloud/ods-components/react'; +import { OdsSkeleton, OdsText } from '@ovhcloud/ods-components/react'; import { ODS_BADGE_SIZE } from '@ovhcloud/ods-components'; -import { ACTIVATE_DOMAIN_BTN_TEST_ID } from '@secret-manager/utils/tests/secret.constants'; -import { SECRET_MANAGER_ROUTES_URLS } from '@secret-manager/routes/routes.constants'; -import { NAMESPACES } from '@ovh-ux/manager-common-translations'; import { OKMS } from '@/types/okms.type'; import { OkmsServiceState } from '@/components/layout-helpers/Dashboard/okmsServiceState/OkmsServiceState.component'; import { RadioCard } from '@/common/components/RadioCard/RadioCard.component'; - -const ActivationInProgress = () => { - const { t } = useTranslation('secret-manager/create'); - - return ( -
- - {t('domain_activation_in_progress')} -
- ); -}; - -const ActivateRegion = ({ selectedRegion }: { selectedRegion: string }) => { - const { t } = useTranslation(NAMESPACES.ACTIONS); - const navigate = useNavigate(); - - return ( -
- - navigate( - SECRET_MANAGER_ROUTES_URLS.secretCreateOrderOkms(selectedRegion), - ) - } - /> -
- ); -}; +import { ActivateRegion } from './ActivateRegion.component'; const DomainStatus = ({ id }: { id: string }) => { const { data: OkmsServiceInfos, isLoading, isError } = useServiceDetails({ @@ -67,7 +29,7 @@ type DomainSelectorProps = { domains: OKMS[]; selectedDomain: string; selectedRegion: string; - updatingOkmsList: boolean; + isUpdatingOkmsList: boolean; onDomainSelection: (domainId: string) => void; }; @@ -75,7 +37,7 @@ export const DomainSelector = ({ domains, selectedDomain, selectedRegion, - updatingOkmsList, + isUpdatingOkmsList, onDomainSelection, }: DomainSelectorProps) => { const { t } = useTranslation('secret-manager/create'); @@ -83,10 +45,11 @@ export const DomainSelector = ({ if (!selectedRegion || domains.length === 1) return null; if (domains.length === 0) { - return updatingOkmsList ? ( - - ) : ( - + return ( + ); } diff --git a/packages/manager/apps/key-management-service/src/modules/secret-manager/utils/tests/secret.constants.ts b/packages/manager/apps/key-management-service/src/modules/secret-manager/utils/tests/secret.constants.ts index bcd21287f541..aa82fac813aa 100644 --- a/packages/manager/apps/key-management-service/src/modules/secret-manager/utils/tests/secret.constants.ts +++ b/packages/manager/apps/key-management-service/src/modules/secret-manager/utils/tests/secret.constants.ts @@ -1,5 +1,6 @@ -/* ACTIVATE button */ +/* ACTIVATE DOMAIN */ export const ACTIVATE_DOMAIN_BTN_TEST_ID = 'secret-activate-domain-button'; +export const ACTIVATE_DOMAIN_SPINNER_TEST_ID = 'secret-activate-domain-spinner'; /* PATH input */ export const PATH_INPUT_TEST_ID = 'secret-input-path'; diff --git a/packages/manager/apps/key-management-service/src/utils/tests/init.i18n.ts b/packages/manager/apps/key-management-service/src/utils/tests/init.i18n.ts index 9f38e4958f11..7f2b6bf0d63a 100644 --- a/packages/manager/apps/key-management-service/src/utils/tests/init.i18n.ts +++ b/packages/manager/apps/key-management-service/src/utils/tests/init.i18n.ts @@ -3,6 +3,7 @@ import { NAMESPACES } from '@ovh-ux/manager-common-translations'; import commonDashboard from '../../../../../modules/common-translations/public/translations/dashboard/Messages_fr_FR.json'; import commonForm from '../../../../../modules/common-translations/public/translations/form/Messages_fr_FR.json'; import commonStatus from '../../../../../modules/common-translations/public/translations/status/Messages_fr_FR.json'; +import commonError from '../../../../../modules/common-translations/public/translations/error/Messages_fr_FR.json'; import kmsCommon from '../../../public/translations/key-management-service/common/Messages_fr_FR.json'; import create from '../../../public/translations/key-management-service/create/Messages_fr_FR.json'; import dashboard from '../../../public/translations/key-management-service/dashboard/Messages_fr_FR.json'; @@ -50,6 +51,7 @@ function addTranslations() { .addResources(defaultLocale, NAMESPACES.DASHBOARD, commonDashboard) .addResources(defaultLocale, NAMESPACES.FORM, commonForm) .addResources(defaultLocale, NAMESPACES.STATUS, commonStatus) + .addResources(defaultLocale, NAMESPACES.ERROR, commonError) .addResources(defaultLocale, 'secret-manager/common', secretCommon) .addResources(defaultLocale, 'secret-manager/onboarding', secretOnboarding) .addResources(defaultLocale, 'secret-manager/dashboard', secretDashboard) @@ -91,6 +93,7 @@ const commonLabels = { dashboard: commonDashboard, form: commonForm, status: commonStatus, + error: commonError, }; const secretManagerLabels = { From c55717016181be712e143a6bda3368725e7fd228 Mon Sep 17 00:00:00 2001 From: Romain Jamet Date: Wed, 30 Jul 2025 11:55:20 +0200 Subject: [PATCH 4/4] fix(okms): fix order okms tests and modal page ref: #MANAGER-18309 Signed-off-by: Romain Jamet --- .../secret-manager/create/Messages_fr_FR.json | 2 +- .../OrderOkmsModal.page.constants.ts} | 0 .../OrderOkmsModal/OrderOkmsModal.page.spec.tsx} | 13 ++++++------- .../OrderOkmsModal/OrderOkmsModal.page.tsx} | 6 ++++-- .../pages/create/ActivateRegion.component.spec.tsx | 4 ++++ .../create/DomainManagement.component.spec.tsx | 4 ++-- .../pages/create/DomainManagement.component.tsx | 2 +- .../pages/create/OrderOkms/OrderOkms.page.tsx | 6 ------ .../src/modules/secret-manager/routes/routes.tsx | 2 +- 9 files changed, 19 insertions(+), 20 deletions(-) rename packages/manager/apps/key-management-service/src/common/{components/OrderOkmsModal/OrderOkmsModal.component.constants.ts => pages/OrderOkmsModal/OrderOkmsModal.page.constants.ts} (100%) rename packages/manager/apps/key-management-service/src/common/{components/OrderOkmsModal/OrderOkmsModal.component.spec.tsx => pages/OrderOkmsModal/OrderOkmsModal.page.spec.tsx} (97%) rename packages/manager/apps/key-management-service/src/common/{components/OrderOkmsModal/OrderOkmsModal.component.tsx => pages/OrderOkmsModal/OrderOkmsModal.page.tsx} (98%) delete mode 100644 packages/manager/apps/key-management-service/src/modules/secret-manager/pages/create/OrderOkms/OrderOkms.page.tsx diff --git a/packages/manager/apps/key-management-service/public/translations/secret-manager/create/Messages_fr_FR.json b/packages/manager/apps/key-management-service/public/translations/secret-manager/create/Messages_fr_FR.json index dd34bfad0b43..c22f25e3f253 100644 --- a/packages/manager/apps/key-management-service/public/translations/secret-manager/create/Messages_fr_FR.json +++ b/packages/manager/apps/key-management-service/public/translations/secret-manager/create/Messages_fr_FR.json @@ -3,7 +3,7 @@ "domain_section_title": "1. Sélection du domaine", "region_selector_title": "Sélectionnez une région", "domain_selector_title": "Sélectionnez un domaine", - "domain_activation_in_progress": "Veuillez patienter, creation en cours.", + "domain_activation_in_progress": "Veuillez patienter, création en cours.", "create_domain_tc_title": "Activation de la région", "create_domain_tc_description": "Pour activer votre région il est nécessaire de créer un domaine OKMS. Souhaitez-vous continuer l'activation de votre région ?", "create_domain_tc_confirm_label": "J'ai pris connaissance et j'accepte les conditions générales, l'annexe relative au traitement des données personnelles et les conditions particulières de services d'OVH.", diff --git a/packages/manager/apps/key-management-service/src/common/components/OrderOkmsModal/OrderOkmsModal.component.constants.ts b/packages/manager/apps/key-management-service/src/common/pages/OrderOkmsModal/OrderOkmsModal.page.constants.ts similarity index 100% rename from packages/manager/apps/key-management-service/src/common/components/OrderOkmsModal/OrderOkmsModal.component.constants.ts rename to packages/manager/apps/key-management-service/src/common/pages/OrderOkmsModal/OrderOkmsModal.page.constants.ts diff --git a/packages/manager/apps/key-management-service/src/common/components/OrderOkmsModal/OrderOkmsModal.component.spec.tsx b/packages/manager/apps/key-management-service/src/common/pages/OrderOkmsModal/OrderOkmsModal.page.spec.tsx similarity index 97% rename from packages/manager/apps/key-management-service/src/common/components/OrderOkmsModal/OrderOkmsModal.component.spec.tsx rename to packages/manager/apps/key-management-service/src/common/pages/OrderOkmsModal/OrderOkmsModal.page.spec.tsx index 4f85eb3dd8d1..361429147c40 100644 --- a/packages/manager/apps/key-management-service/src/common/components/OrderOkmsModal/OrderOkmsModal.component.spec.tsx +++ b/packages/manager/apps/key-management-service/src/common/pages/OrderOkmsModal/OrderOkmsModal.page.spec.tsx @@ -18,17 +18,16 @@ import { } from '@ovh-ux/manager-module-order'; import { assertTextVisibility } from '@ovh-ux/manager-core-test-utils'; import { initTestI18n, labels } from '@/utils/tests/init.i18n'; -import { +import OrderOkmsModal, { OkmsRegionOrderSuccessful, - OrderOkmsModal, -} from './OrderOkmsModal.component'; +} from './OrderOkmsModal.page'; import { ORDER_OKMS_CREATE_CANCEL_BUTTON_TEST_ID, ORDER_OKMS_CREATE_CART_SPINNER_TEST_ID, ORDER_OKMS_CREATE_RETRY_BUTTON_TEST_ID, ORDER_OKMS_TC_CONFIRM_BUTTON_TEST_ID, ORDER_OKMS_TC_CONFIRM_CHECKBOX_TEST_ID, -} from './OrderOkmsModal.component.constants'; +} from './OrderOkmsModal.page.constants'; let i18nValue: i18n; @@ -116,9 +115,10 @@ const clickOnConfirmButton = async (user: UserEvent) => { }; describe('Order Okms Modal test suite', () => { - beforeEach(() => { - vi.resetAllMocks(); + afterEach(() => { + vi.restoreAllMocks(); }); + describe('on init', () => { it('should display a loading state when creating a cart', async () => { // GIVEN @@ -177,7 +177,6 @@ describe('Order Okms Modal test suite', () => { describe('on terms and conditions', () => { beforeEach(() => { - vi.resetAllMocks(); vi.mocked(createCart).mockResolvedValue({ cartId: 'cart-id', contractList: mockedContracts, diff --git a/packages/manager/apps/key-management-service/src/common/components/OrderOkmsModal/OrderOkmsModal.component.tsx b/packages/manager/apps/key-management-service/src/common/pages/OrderOkmsModal/OrderOkmsModal.page.tsx similarity index 98% rename from packages/manager/apps/key-management-service/src/common/components/OrderOkmsModal/OrderOkmsModal.component.tsx rename to packages/manager/apps/key-management-service/src/common/pages/OrderOkmsModal/OrderOkmsModal.page.tsx index 1e5b019a4cc5..d733cc755c3f 100644 --- a/packages/manager/apps/key-management-service/src/common/components/OrderOkmsModal/OrderOkmsModal.component.tsx +++ b/packages/manager/apps/key-management-service/src/common/pages/OrderOkmsModal/OrderOkmsModal.page.tsx @@ -21,7 +21,7 @@ import { ORDER_OKMS_CREATE_RETRY_BUTTON_TEST_ID, ORDER_OKMS_TC_CONFIRM_BUTTON_TEST_ID, ORDER_OKMS_TC_CONFIRM_CHECKBOX_TEST_ID, -} from './OrderOkmsModal.component.constants'; +} from './OrderOkmsModal.page.constants'; // custom type for the state export type OkmsRegionOrderSuccessful = { @@ -122,7 +122,7 @@ const TermsAndConditions = ({ ); }; -export const OrderOkmsModal = () => { +const OrderOkmsModal = () => { const { t } = useTranslation([ 'secret-manager/create', NAMESPACES.ERROR, @@ -180,3 +180,5 @@ export const OrderOkmsModal = () => { ); }; + +export default OrderOkmsModal; diff --git a/packages/manager/apps/key-management-service/src/modules/secret-manager/pages/create/ActivateRegion.component.spec.tsx b/packages/manager/apps/key-management-service/src/modules/secret-manager/pages/create/ActivateRegion.component.spec.tsx index 6f739605ee76..9b39f9d319cd 100644 --- a/packages/manager/apps/key-management-service/src/modules/secret-manager/pages/create/ActivateRegion.component.spec.tsx +++ b/packages/manager/apps/key-management-service/src/modules/secret-manager/pages/create/ActivateRegion.component.spec.tsx @@ -52,6 +52,10 @@ const renderActivateRegion = async ({ }; describe('ActivateRegion test suite', () => { + afterEach(() => { + vi.resetAllMocks(); + }); + describe('when okms list is not updating', () => { it('should render a button to navigate to okms silent creation modal', async () => { const user = userEvent.setup(); diff --git a/packages/manager/apps/key-management-service/src/modules/secret-manager/pages/create/DomainManagement.component.spec.tsx b/packages/manager/apps/key-management-service/src/modules/secret-manager/pages/create/DomainManagement.component.spec.tsx index 205e13ad9dfc..332a35320aaa 100644 --- a/packages/manager/apps/key-management-service/src/modules/secret-manager/pages/create/DomainManagement.component.spec.tsx +++ b/packages/manager/apps/key-management-service/src/modules/secret-manager/pages/create/DomainManagement.component.spec.tsx @@ -39,7 +39,7 @@ import { useOkmsList } from '@/data/hooks/useOkms'; import { OKMS } from '@/types/okms.type'; import { getOrderCatalogOKMS } from '@/data/api/orderCatalogOKMS'; import { ErrorResponse } from '@/types/api.type'; -import { OkmsRegionOrderSuccessful } from '@/common/components/OrderOkmsModal/OrderOkmsModal.component'; +import { OkmsRegionOrderSuccessful } from '@/common/pages/OrderOkmsModal/OrderOkmsModal.page'; let i18nValue: i18n; @@ -286,7 +286,7 @@ describe('Domain management test suite', () => { // GIVEN useLocationMock = { state: { - orderRegion: 'new-example-region', // New mocked orderRegion value + orderRegion: 'regionId', }, }; diff --git a/packages/manager/apps/key-management-service/src/modules/secret-manager/pages/create/DomainManagement.component.tsx b/packages/manager/apps/key-management-service/src/modules/secret-manager/pages/create/DomainManagement.component.tsx index c72806b4bb93..ba576264f2bd 100644 --- a/packages/manager/apps/key-management-service/src/modules/secret-manager/pages/create/DomainManagement.component.tsx +++ b/packages/manager/apps/key-management-service/src/modules/secret-manager/pages/create/DomainManagement.component.tsx @@ -14,7 +14,7 @@ import { useOkmsList } from '@/data/hooks/useOkms'; import { useOrderCatalogOkms } from '@/data/hooks/useOrderCatalogOkms'; import { DomainSelector } from './DomainSelector.component'; import { RegionSelector } from './RegionSelector.component'; -import { OkmsRegionOrderSuccessful } from '@/common/components/OrderOkmsModal/OrderOkmsModal.component'; +import { OkmsRegionOrderSuccessful } from '@/common/pages/OrderOkmsModal/OrderOkmsModal.page'; type DomainManagementProps = { selectedDomainId: string; diff --git a/packages/manager/apps/key-management-service/src/modules/secret-manager/pages/create/OrderOkms/OrderOkms.page.tsx b/packages/manager/apps/key-management-service/src/modules/secret-manager/pages/create/OrderOkms/OrderOkms.page.tsx deleted file mode 100644 index 5f42b992f330..000000000000 --- a/packages/manager/apps/key-management-service/src/modules/secret-manager/pages/create/OrderOkms/OrderOkms.page.tsx +++ /dev/null @@ -1,6 +0,0 @@ -import React from 'react'; -import { OrderOkmsModal } from '@/common/components/OrderOkmsModal/OrderOkmsModal.component'; - -export default function OrderOkms() { - return ; -} diff --git a/packages/manager/apps/key-management-service/src/modules/secret-manager/routes/routes.tsx b/packages/manager/apps/key-management-service/src/modules/secret-manager/routes/routes.tsx index 98a5f66bbdfd..a5d0e6548280 100644 --- a/packages/manager/apps/key-management-service/src/modules/secret-manager/routes/routes.tsx +++ b/packages/manager/apps/key-management-service/src/modules/secret-manager/routes/routes.tsx @@ -35,7 +35,7 @@ const SecretCreate = React.lazy(() => import('@/modules/secret-manager/pages/create/Create.page'), ); const OrderOkms = React.lazy(() => - import('@/modules/secret-manager/pages/create/OrderOkms/OrderOkms.page'), + import('@/common/pages/OrderOkmsModal/OrderOkmsModal.page'), ); export default (