From 1edc5d35977e766b1a484560f17c00d738da7c34 Mon Sep 17 00:00:00 2001 From: Arthur Bullet Date: Fri, 10 Jan 2025 09:38:01 +0100 Subject: [PATCH] feat(pci.ai.notebook): add unit tests on order funnel REF:DATATR-1573 Signed-off-by: Arthur Bullet --- .../src/__tests__/helpers/mocks/catalog.ts | 6 +- .../helpers/mocks/notebook/editor.ts | 2 +- .../helpers/mocks/notebook/framework.ts | 2 +- .../src/__tests__/helpers/mocks/suggestion.ts | 2 +- .../src/lib/orderFunnelHelper.spec.tsx | 2 +- .../pages/notebooks/create/Create.page.tsx | 12 +- .../pages/notebooks/create/Create.spec.tsx | 269 ++++++++++++++++++ .../_components/OrderFunnel.component.tsx | 16 +- .../_components/OrderSummary.component.tsx | 5 + .../create/_components/OrderSummary.spec.tsx | 155 ++++++++++ 10 files changed, 451 insertions(+), 20 deletions(-) create mode 100644 packages/manager/apps/pci-ai-notebooks/src/pages/notebooks/create/Create.spec.tsx create mode 100644 packages/manager/apps/pci-ai-notebooks/src/pages/notebooks/create/_components/OrderSummary.spec.tsx diff --git a/packages/manager/apps/pci-ai-notebooks/src/__tests__/helpers/mocks/catalog.ts b/packages/manager/apps/pci-ai-notebooks/src/__tests__/helpers/mocks/catalog.ts index 76b32f502be9..675826ccca63 100644 --- a/packages/manager/apps/pci-ai-notebooks/src/__tests__/helpers/mocks/catalog.ts +++ b/packages/manager/apps/pci-ai-notebooks/src/__tests__/helpers/mocks/catalog.ts @@ -55,7 +55,7 @@ export const mockedCatalogPlanMonth: order.publicOrder.Plan = { }, ], invoiceName: 'invoiceName1', - planCode: 'ai-notebook.flavorCPUId.minute.consumption', + planCode: 'ai-notebook.flavorBisCPUId.minute.consumption', pricingType: order.cart.GenericProductPricingTypeEnum.consumption, pricings: [mockedPricing], product: 'product', @@ -75,7 +75,7 @@ export const mockedCatalogStorageMonth: order.publicOrder.Plan = { }, ], invoiceName: 'invoiceName2', - planCode: 'ai-notebook.flavorCPUId.minute.consumption', + planCode: 'ai-notebook.flavorGPUId.minute.consumption', pricingType: order.cart.GenericProductPricingTypeEnum.consumption, pricings: [mockedPricing], product: 'product', @@ -95,7 +95,7 @@ export const mockedCatalogStorageHour: order.publicOrder.Plan = { }, ], invoiceName: 'invoiceName3', - planCode: 'ai-notebook.flavorCPUId.minute.consumption', + planCode: 'ai-notebook.flavorOtherCpuId.minute.consumption', pricingType: order.cart.GenericProductPricingTypeEnum.consumption, pricings: [mockedPricing], product: 'product', diff --git a/packages/manager/apps/pci-ai-notebooks/src/__tests__/helpers/mocks/notebook/editor.ts b/packages/manager/apps/pci-ai-notebooks/src/__tests__/helpers/mocks/notebook/editor.ts index 4fc28f0f6e21..c7c191ad54df 100644 --- a/packages/manager/apps/pci-ai-notebooks/src/__tests__/helpers/mocks/notebook/editor.ts +++ b/packages/manager/apps/pci-ai-notebooks/src/__tests__/helpers/mocks/notebook/editor.ts @@ -3,7 +3,7 @@ import * as ai from '@/types/cloud/project/ai'; export const mockedEditor: ai.capabilities.notebook.Editor = { description: 'description', docUrl: 'docURl', - id: 'editorId', + id: 'jupyterlab', logoUrl: 'logo', name: 'EditorName', versions: ['version'], diff --git a/packages/manager/apps/pci-ai-notebooks/src/__tests__/helpers/mocks/notebook/framework.ts b/packages/manager/apps/pci-ai-notebooks/src/__tests__/helpers/mocks/notebook/framework.ts index bb0ed6111084..089dba6c6a81 100644 --- a/packages/manager/apps/pci-ai-notebooks/src/__tests__/helpers/mocks/notebook/framework.ts +++ b/packages/manager/apps/pci-ai-notebooks/src/__tests__/helpers/mocks/notebook/framework.ts @@ -3,7 +3,7 @@ import * as ai from '@/types/cloud/project/ai'; export const mockedFramework: ai.notebook.Framework = { description: 'description', docUrl: 'docURl', - id: 'frameworkId', + id: 'one-for-all', logoUrl: 'logo', name: 'FrameworkName', versions: ['version'], diff --git a/packages/manager/apps/pci-ai-notebooks/src/__tests__/helpers/mocks/suggestion.ts b/packages/manager/apps/pci-ai-notebooks/src/__tests__/helpers/mocks/suggestion.ts index f71f897bd437..107085cd1f78 100644 --- a/packages/manager/apps/pci-ai-notebooks/src/__tests__/helpers/mocks/suggestion.ts +++ b/packages/manager/apps/pci-ai-notebooks/src/__tests__/helpers/mocks/suggestion.ts @@ -5,7 +5,7 @@ export const mockedSuggestion: Suggestions[] = [ region: 'GRA', ressources: { nb: 1, - flavor: 'l4-1-gpu', + flavor: 'flavorCPUId', }, framework: { id: 'one-for-all', diff --git a/packages/manager/apps/pci-ai-notebooks/src/lib/orderFunnelHelper.spec.tsx b/packages/manager/apps/pci-ai-notebooks/src/lib/orderFunnelHelper.spec.tsx index eafc15c78745..6642e878d7b2 100644 --- a/packages/manager/apps/pci-ai-notebooks/src/lib/orderFunnelHelper.spec.tsx +++ b/packages/manager/apps/pci-ai-notebooks/src/lib/orderFunnelHelper.spec.tsx @@ -62,7 +62,7 @@ describe('orderFunnelHelper', () => { const notebookSpecInputCPU: ai.notebook.NotebookSpecInput = { env: { - editorId: 'editorId', + editorId: 'jupyterlab', frameworkId: 'noId', frameworkVersion: '', }, diff --git a/packages/manager/apps/pci-ai-notebooks/src/pages/notebooks/create/Create.page.tsx b/packages/manager/apps/pci-ai-notebooks/src/pages/notebooks/create/Create.page.tsx index 8eeaadc9bff7..3950d300e733 100644 --- a/packages/manager/apps/pci-ai-notebooks/src/pages/notebooks/create/Create.page.tsx +++ b/packages/manager/apps/pci-ai-notebooks/src/pages/notebooks/create/Create.page.tsx @@ -45,12 +45,12 @@ const Notebook = () => { }); const loading = - regionsQuery.isLoading || - catalogQuery.isLoading || - frameworkQuery.isLoading || - editorQuery.isLoading || - sshKeyQuery.isLoading || - suggestionsQuery.isLoading; + regionsQuery.isPending || + catalogQuery.isPending || + frameworkQuery.isPending || + editorQuery.isPending || + sshKeyQuery.isPending || + suggestionsQuery.isPending; return ( <> diff --git a/packages/manager/apps/pci-ai-notebooks/src/pages/notebooks/create/Create.spec.tsx b/packages/manager/apps/pci-ai-notebooks/src/pages/notebooks/create/Create.spec.tsx new file mode 100644 index 000000000000..39f0e3e31efd --- /dev/null +++ b/packages/manager/apps/pci-ai-notebooks/src/pages/notebooks/create/Create.spec.tsx @@ -0,0 +1,269 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; +import { + act, + fireEvent, + render, + screen, + waitFor, +} from '@testing-library/react'; + +import Notebook, { + breadcrumb as Breadcrumb, +} from '@/pages/notebooks/create/Create.page'; + +import { Locale } from '@/hooks/useLocale'; +import { RouterWithQueryClientWrapper } from '@/__tests__/helpers/wrappers/RouterWithQueryClientWrapper'; + +import { mockedUser } from '@/__tests__/helpers/mocks/user'; +import { mockedCatalog } from '@/__tests__/helpers/mocks/catalog'; +import { mockedPciProject } from '@/__tests__/helpers/mocks/project'; +import { mockedSuggestion } from '@/__tests__/helpers/mocks/suggestion'; +import { + mockedCapabilitiesRegionBHS, + mockedCapabilitiesRegionGRA, +} from '@/__tests__/helpers/mocks/region'; +import { + mockedEditor, + mockedEditorBis, +} from '@/__tests__/helpers/mocks/notebook/editor'; +import { + mockedFramework, + mockedFrameworkBis, +} from '@/__tests__/helpers/mocks/notebook/framework'; +import { + mockedSshKey, + mockedSshKeyBis, +} from '@/__tests__/helpers/mocks/sshkey'; +import { mockedCommand } from '@/__tests__/helpers/mocks/command'; +import { mockedCapabilitiesFlavorCPU } from '@/__tests__/helpers/mocks/flavor'; +import { + mockedDatastoreWithContainerGit, + mockedDatastoreWithContainerS3, +} from '@/__tests__/helpers/mocks/datastore'; +import * as notebookApi from '@/data/api/ai/notebook/notebook.api'; +import { apiErrorMock } from '@/__tests__/helpers/mocks/aiError'; +import { useToast } from '@/components/ui/use-toast'; + +const mockedUsedNavigate = vi.fn(); +describe('Order funnel page', () => { + beforeEach(() => { + vi.restoreAllMocks(); + + // Mock necessary hooks and dependencies + vi.mock('react-i18next', () => ({ + useTranslation: () => ({ + t: (key: string) => key, + }), + Trans: ({ children }: { children: React.ReactNode }) => children, + })); + vi.mock('@ovh-ux/manager-react-shell-client', async (importOriginal) => { + const mod = await importOriginal< + typeof import('@ovh-ux/manager-react-shell-client') + >(); + return { + ...mod, + useShell: vi.fn(() => ({ + i18n: { + getLocale: vi.fn(() => Locale.fr_FR), + onLocaleChange: vi.fn(), + setLocale: vi.fn(), + }, + environment: { + getEnvironment: vi.fn(() => ({ + getUser: vi.fn(() => mockedUser), + })), + }, + })), + }; + }); + + const ResizeObserverMock = vi.fn(() => ({ + observe: vi.fn(), + unobserve: vi.fn(), + disconnect: vi.fn(), + })); + vi.stubGlobal('ResizeObserver', ResizeObserverMock); + + vi.mock('react-router-dom', async () => { + const mod = await vi.importActual('react-router-dom'); + return { + ...mod, + useParams: () => ({ + projectId: 'projectId', + }), + useNavigate: () => mockedUsedNavigate, + }; + }); + + vi.mock('@/data/api/project/project.api', () => { + return { + getProject: vi.fn(() => mockedPciProject), + }; + }); + + vi.mock('@/data/api/ai/notebook/notebook.api', () => ({ + getCommand: vi.fn(() => mockedCommand), + addNotebook: vi.fn((notebook) => notebook), + })); + + vi.mock('@/data/api/ai/notebook/suggestions.api', () => ({ + getSuggestions: vi.fn(() => mockedSuggestion), + })); + + vi.mock('@/data/api/catalog/catalog.api', () => ({ + catalogApi: { + getCatalog: vi.fn(() => mockedCatalog), + }, + })); + + vi.mock('@/data/api/ai/capabilities.api', () => ({ + getRegions: vi.fn(() => [ + mockedCapabilitiesRegionGRA, + mockedCapabilitiesRegionBHS, + ]), + })); + + vi.mock('@/data/api/ai/notebook/capabilities/framework.api', () => ({ + getFramework: vi.fn(() => [mockedFramework, mockedFrameworkBis]), + })); + + vi.mock('@/data/api/ai/notebook/capabilities/editor.api', () => ({ + getEditor: vi.fn(() => [mockedEditor, mockedEditorBis]), + })); + + vi.mock('@/data/api/ai/capabilities.api', () => ({ + getRegions: vi.fn(() => [ + mockedCapabilitiesRegionGRA, + mockedCapabilitiesRegionBHS, + ]), + getFlavor: vi.fn(() => [mockedCapabilitiesFlavorCPU]), + })); + + vi.mock('@/data/api/ai/datastore.api', () => ({ + getDatastores: vi.fn(() => [ + mockedDatastoreWithContainerGit, + mockedDatastoreWithContainerS3, + ]), + })); + + vi.mock('@/data/api/sshkey/sshkey.api', () => ({ + getSshkey: vi.fn(() => [mockedSshKey, mockedSshKeyBis]), + })); + + vi.mock('@/components/ui/use-toast', () => { + const toastMock = vi.fn(); + return { + useToast: vi.fn(() => ({ + toast: toastMock, + })), + }; + }); + + const mockScrollIntoView = vi.fn(); + window.HTMLElement.prototype.scrollIntoView = mockScrollIntoView; + }); + afterEach(() => { + vi.clearAllMocks(); + }); + + it('renders the breadcrumb component', async () => { + const translationKey = 'breadcrumb'; + render(, { wrapper: RouterWithQueryClientWrapper }); + await waitFor(() => { + expect(screen.getByText(translationKey)).toBeInTheDocument(); + }); + }); + + it('renders the skeleton component while loading', async () => { + render(, { wrapper: RouterWithQueryClientWrapper }); + await waitFor(() => { + expect(screen.getByTestId('order-funnel-skeleton')).toBeInTheDocument(); + }); + }); + + it('renders the order funnel', async () => { + render(, { wrapper: RouterWithQueryClientWrapper }); + await waitFor(() => { + expect(screen.getByTestId('order-funnel-container')).toBeInTheDocument(); + expect(screen.getByTestId('name-section')).toBeInTheDocument(); + expect(screen.getByTestId('flavor-section')).toBeInTheDocument(); + expect(screen.getByTestId('region-section')).toBeInTheDocument(); + expect(screen.getByTestId('framework-section')).toBeInTheDocument(); + expect(screen.getByTestId('editor-section')).toBeInTheDocument(); + expect(screen.getByTestId('advance-config-section')).toBeInTheDocument(); + expect(screen.getByTestId('order-submit-button')).toBeInTheDocument(); + }); + }); + + it('trigger toast error on getCommand API Error', async () => { + vi.mocked(notebookApi.getCommand).mockImplementation(() => { + throw apiErrorMock; + }); + render(, { wrapper: RouterWithQueryClientWrapper }); + await waitFor(() => { + expect(screen.getByTestId('order-funnel-container')).toBeInTheDocument(); + }); + act(() => { + fireEvent.click(screen.getByTestId('advanced-config-button')); + }); + act(() => { + fireEvent.click(screen.getByTestId('cli-command-button')); + }); + await waitFor(() => { + expect(notebookApi.getCommand).toHaveBeenCalled(); + expect(useToast().toast).toHaveBeenCalledWith({ + title: 'errorGetCommandCli', + description: apiErrorMock.response.data.message, + variant: 'destructive', + }); + }); + }); + + it('trigger getCommand on Cli Command button click', async () => { + render(, { wrapper: RouterWithQueryClientWrapper }); + await waitFor(() => { + expect(screen.getByTestId('order-funnel-container')).toBeInTheDocument(); + }); + act(() => { + fireEvent.click(screen.getByTestId('cli-command-button')); + }); + await waitFor(() => { + expect(notebookApi.getCommand).toHaveBeenCalled(); + }); + }); + + it('trigger toast error on addNotebook API Error', async () => { + vi.mocked(notebookApi.addNotebook).mockImplementation(() => { + throw apiErrorMock; + }); + render(, { wrapper: RouterWithQueryClientWrapper }); + await waitFor(() => { + expect(screen.getByTestId('order-funnel-container')).toBeInTheDocument(); + }); + act(() => { + fireEvent.click(screen.getByTestId('order-submit-button')); + }); + await waitFor(() => { + expect(notebookApi.addNotebook).toHaveBeenCalled(); + expect(useToast().toast).toHaveBeenCalledWith({ + title: 'errorCreatingNotebook', + description: apiErrorMock.response.data.message, + variant: 'destructive', + }); + }); + }); + + it('trigger add notebook on click', async () => { + render(, { wrapper: RouterWithQueryClientWrapper }); + await waitFor(() => { + expect(screen.getByTestId('order-funnel-container')).toBeInTheDocument(); + }); + act(() => { + fireEvent.click(screen.getByTestId('order-submit-button')); + }); + await waitFor(() => { + expect(notebookApi.addNotebook).toHaveBeenCalled(); + }); + expect(mockedUsedNavigate).toHaveBeenCalledWith('../undefined'); + }); +}); diff --git a/packages/manager/apps/pci-ai-notebooks/src/pages/notebooks/create/_components/OrderFunnel.component.tsx b/packages/manager/apps/pci-ai-notebooks/src/pages/notebooks/create/_components/OrderFunnel.component.tsx index 7d90374366f2..b03eaff23a5e 100644 --- a/packages/manager/apps/pci-ai-notebooks/src/pages/notebooks/create/_components/OrderFunnel.component.tsx +++ b/packages/manager/apps/pci-ai-notebooks/src/pages/notebooks/create/_components/OrderFunnel.component.tsx @@ -176,7 +176,7 @@ const OrderFunnel = ({ data-testid="order-funnel-container" className="col-span-1 md:col-span-3 divide-y-[24px] divide-transparent" > -
+

{t('fieldDimensionLabel')}

-
+
-
+
-
+

{t('fieldCaracteristicLabel')}

-
+
-
+
{/* Advanced configuration */} -
+