diff --git a/packages/manager/apps/vrack-services/package.json b/packages/manager/apps/vrack-services/package.json
index 685c8e08e29e..dc62cadc6bb6 100644
--- a/packages/manager/apps/vrack-services/package.json
+++ b/packages/manager/apps/vrack-services/package.json
@@ -12,20 +12,20 @@
"author": "OVH SAS",
"scripts": {
"beta-test:e2e": "tsc && node ../../../../scripts/run-playwright-bdd.js",
- "beta-test:e2e:ci": "tsc && node ../../../../scripts/run-playwright-bdd.js --ci",
+ "beta-test:e2e:cii": "tsc && node ../../../../scripts/run-playwright-bdd.js --ci",
"build": "tsc && vite build",
+ "coverage": "vitest run --coverage",
"dev": "tsc && vite",
"start": "lerna exec --stream --scope='@ovh-ux/manager-vrack-services-app' --include-dependencies -- npm run build --if-present",
"start:dev": "lerna exec --stream --scope='@ovh-ux/manager-vrack-services-app' --include-dependencies -- npm run dev --if-present",
"start:watch": "lerna exec --stream --parallel --scope='@ovh-ux/manager-vrack-services-app' --include-dependencies -- npm run dev:watch --if-present",
"test": "vitest run",
- "test:coverage": "vitest run --coverage",
- "test:e2e": "tsc && node ../../../../scripts/run-playwright-bdd.js",
- "test:e2e:cii": "tsc && node ../../../../scripts/run-playwright-bdd.js --ci"
+ "test:coverage": "vitest run --coverage"
},
"dependencies": {
"@ovh-ux/manager-config": "^8.0.0",
"@ovh-ux/manager-core-api": "^0.9.0",
+ "@ovh-ux/manager-core-utils": "*",
"@ovh-ux/manager-module-order": "^0.8.0",
"@ovh-ux/manager-react-components": "^1.41.1",
"@ovh-ux/manager-react-core-application": "^0.11.1",
@@ -51,17 +51,22 @@
"@ovh-ux/manager-vite-config": "^0.8.3",
"@playwright/test": "^1.41.2",
"@tanstack/react-query-devtools": "^5.51.21",
+ "@testing-library/jest-dom": "^6.5.0",
+ "@testing-library/react": "^16.0.1",
+ "@testing-library/user-event": "^14.5.2",
"@types/jest": "27.x",
- "@vitejs/plugin-react": "^4.2.1",
- "@vitest/coverage-v8": "^1.2.0",
+ "@vitejs/plugin-react": "^4.3.0",
+ "@vitest/coverage-v8": "^2.1.4",
"c8": "^9.1.0",
+ "element-internals-polyfill": "^1.3.11",
"jest": "27.x",
"msw": "2.1.7",
"ts-jest": "27.x",
"ts-loader": "^9.5.1",
"ts-node": "^10.9.1",
+ "typescript": "^4.3.2",
"vite": "^5.2.13",
- "vitest": "^1.2.0"
+ "vitest": "^2.1.4"
},
"msw": {
"workerDirectory": "./public"
diff --git a/packages/manager/apps/vrack-services/playwright.config.ts b/packages/manager/apps/vrack-services/playwright.config.ts
index 4a033cc8d87d..feb249bcbe3f 100644
--- a/packages/manager/apps/vrack-services/playwright.config.ts
+++ b/packages/manager/apps/vrack-services/playwright.config.ts
@@ -12,4 +12,9 @@ export default defineConfig({
// Collect trace when retrying the failed test.
trace: 'retain-on-failure',
},
+ testMatch: '**/*.e2e.ts',
+ webServer: {
+ command: 'yarn run dev',
+ url: 'http://localhost:9000/',
+ },
});
diff --git a/packages/manager/apps/vrack-services/setupTests.ts b/packages/manager/apps/vrack-services/setupTests.ts
new file mode 100644
index 000000000000..bbdaae5c93ac
--- /dev/null
+++ b/packages/manager/apps/vrack-services/setupTests.ts
@@ -0,0 +1,29 @@
+import { vi } from 'vitest';
+import '@testing-library/jest-dom';
+
+vi.mock('react-i18next', () => ({
+ useTranslation: () => ({
+ t: (translationKey: string) => translationKey,
+ i18n: { language: 'fr_FR' },
+ }),
+}));
+
+vi.mock('@ovh-ux/manager-react-shell-client', async (importOriginal) => {
+ const original: typeof import('@ovh-ux/manager-react-shell-client') = await importOriginal();
+ return {
+ ...original,
+ useOvhTracking: () => ({ trackClick: vi.fn(), trackPage: vi.fn() }),
+ };
+});
+
+vi.mock('react-router-dom', async (importOriginal) => {
+ const original: typeof import('react-router-dom') = await importOriginal();
+ return {
+ ...original,
+ useSearchParams: () => [{ get: (str: string) => str }],
+ useNavigate: vi.fn(),
+ useLocation: vi.fn().mockReturnValue({
+ pathname: 'pathname',
+ }),
+ };
+});
diff --git a/packages/manager/apps/vrack-services/src/components/Breadcrumb.spec.tsx b/packages/manager/apps/vrack-services/src/components/Breadcrumb.spec.tsx
new file mode 100644
index 000000000000..4f3afced0e88
--- /dev/null
+++ b/packages/manager/apps/vrack-services/src/components/Breadcrumb.spec.tsx
@@ -0,0 +1,23 @@
+import React from 'react';
+import { describe, expect, it, vi } from 'vitest';
+import { render, waitFor } from '@testing-library/react';
+import { Breadcrumb, BreadcrumbProps } from './Breadcrumb.component';
+
+/** Render */
+const renderComponent = ({ items, overviewUrl }: BreadcrumbProps) => {
+ return render();
+};
+
+/** END RENDER */
+
+describe('Breadcrumb Component', () => {
+ it('should display ODS breadcrumb', async () => {
+ const { getByRole } = renderComponent({
+ items: [],
+ });
+
+ await waitFor(() => {
+ expect(getByRole('navigation')).toBeDefined();
+ });
+ });
+});
diff --git a/packages/manager/apps/vrack-services/src/components/CreateVrack.spec.tsx b/packages/manager/apps/vrack-services/src/components/CreateVrack.spec.tsx
new file mode 100644
index 000000000000..b8f428ecafdd
--- /dev/null
+++ b/packages/manager/apps/vrack-services/src/components/CreateVrack.spec.tsx
@@ -0,0 +1,106 @@
+import React from 'react';
+import { describe, expect, it, vi } from 'vitest';
+import { render, fireEvent, waitFor, act } from '@testing-library/react';
+import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
+import { DetailedOrder } from '@ovh-ux/manager-module-order';
+import {
+ ShellContext,
+ ShellContextType,
+} from '@ovh-ux/manager-react-shell-client';
+import { CreateVrack, CreateVrackProps } from './CreateVrack.component';
+import { createVrackOnlyCart } from '@/utils/cart';
+
+const queryClient = new QueryClient();
+
+const shellContext = {
+ environment: {
+ user: { ovhSubsidiary: 'FR' },
+ },
+};
+
+const renderComponent = ({ closeModal }: CreateVrackProps) => {
+ return render(
+
+
+
+
+ ,
+ );
+};
+
+/** MOCKS */
+const closeModalMock = vi.fn();
+
+vi.mock('@/utils/cart', async (importOriginal) => {
+ const original: typeof import('@/utils/cart') = await importOriginal();
+ return {
+ ...original,
+ createVrackOnlyCart: vi.fn(),
+ };
+});
+
+vi.mock('@ovh-ux/manager-module-order', async (importOriginal) => {
+ const original: typeof import('@ovh-ux/manager-module-order') = await importOriginal();
+ return {
+ ...original,
+ useOrderPollingStatus: () => ({
+ data: [] as DetailedOrder[],
+ }),
+ };
+});
+
+/** END MOCKS */
+
+describe('CreateVrack Component', () => {
+ it('should display the contracts after click the button create a vrack', async () => {
+ vi.mocked(createVrackOnlyCart).mockResolvedValue({
+ contractList: [
+ {
+ name: 'test',
+ url: 'test',
+ content: 'test',
+ },
+ {
+ name: 'test2',
+ url: 'test2',
+ content: 'test2',
+ },
+ ],
+ cartId: '1',
+ });
+
+ const { getByText } = renderComponent({ closeModal: closeModalMock });
+ const button = await getByText('modalCreateNewVrackButtonLabel');
+ await act(() => {
+ fireEvent.click(button);
+ });
+
+ await waitFor(() => {
+ expect(createVrackOnlyCart).toHaveBeenCalledWith('FR');
+ expect(getByText('modalConfirmContractsCheckboxLabel')).not.toBeNull();
+ expect(
+ getByText('modalVrackCreationSubmitOrderButtonLabel'),
+ ).toBeDefined();
+ });
+ });
+
+ it('should display an error message if cart creation fail', async () => {
+ vi.mocked(createVrackOnlyCart).mockRejectedValue({
+ response: { data: { message: 'api-error' } },
+ });
+
+ const { getByText } = renderComponent({ closeModal: closeModalMock });
+ const button = await getByText('modalCreateNewVrackButtonLabel');
+
+ await act(() => {
+ fireEvent.click(button);
+ });
+
+ await waitFor(() => {
+ expect(createVrackOnlyCart).toHaveBeenCalledWith('FR');
+ expect(getByText('api-error')).toBeDefined();
+ });
+ });
+});
diff --git a/packages/manager/apps/vrack-services/src/components/DeliveringMessages.spec.tsx b/packages/manager/apps/vrack-services/src/components/DeliveringMessages.spec.tsx
new file mode 100644
index 000000000000..619cc6522736
--- /dev/null
+++ b/packages/manager/apps/vrack-services/src/components/DeliveringMessages.spec.tsx
@@ -0,0 +1,61 @@
+import React from 'react';
+import { describe, expect, it, vi } from 'vitest';
+import { render } from '@testing-library/react';
+import {
+ ShellContext,
+ ShellContextType,
+} from '@ovh-ux/manager-react-shell-client';
+import { DetailedOrder } from '@ovh-ux/manager-module-order';
+import { ErrorBannerProps, ErrorPage } from './ErrorPage.component';
+import {
+ DeliveringMessages,
+ DeliveringMessagesProps,
+} from './DeliveringMessages.component';
+
+/** MOCKS */
+vi.mock('react-i18next', () => ({
+ useTranslation: () => ({
+ t: (translationKey: string, params?: any) => {
+ const flattenParams = params
+ ? Object.keys(params).reduce((previous, current) => {
+ return `${previous} | ${current}:${params[current]}`;
+ }, '')
+ : undefined;
+ return flattenParams
+ ? `${translationKey}${flattenParams}`
+ : translationKey;
+ },
+ i18n: { language: 'fr_FR' },
+ }),
+}));
+/** END MOCKS */
+/** Render */
+const renderComponent = ({ orders }: { orders?: DetailedOrder[] }) => {
+ return render();
+};
+
+/** END RENDER */
+
+describe('DeliveringMessages Component', () => {
+ it('should display list of ongoing orders', async () => {
+ const { getByText } = renderComponent({
+ orders: [
+ {
+ date: '2024-12-06T12:10:00.000Z',
+ orderId: 1,
+ status: 'delivering',
+ } as DetailedOrder,
+ ],
+ });
+
+ const date = new Date('2024-12-06T12:10:00.000Z');
+
+ expect(
+ getByText(
+ `order-text | date:${date.toLocaleDateString(
+ 'fr-FR',
+ )} | time:${date.getHours()}:${date.getMinutes()} | status:orderStatus-delivering`,
+ ),
+ ).toBeDefined();
+ });
+});
diff --git a/packages/manager/apps/vrack-services/src/components/ErrorPage.spec.tsx b/packages/manager/apps/vrack-services/src/components/ErrorPage.spec.tsx
new file mode 100644
index 000000000000..30e951790792
--- /dev/null
+++ b/packages/manager/apps/vrack-services/src/components/ErrorPage.spec.tsx
@@ -0,0 +1,51 @@
+import React from 'react';
+import { describe, expect, it, vi } from 'vitest';
+import { render } from '@testing-library/react';
+import {
+ ShellContext,
+ ShellContextType,
+} from '@ovh-ux/manager-react-shell-client';
+import { ErrorBannerProps, ErrorPage } from './ErrorPage.component';
+
+/** Render */
+const shellContext = {
+ environment: {
+ user: { ovhSubsidiary: 'FR' },
+ },
+ shell: {
+ tracking: {
+ trackClick: vi.fn(),
+ trackPage: vi.fn(),
+ init: vi.fn(),
+ },
+ },
+};
+
+const renderComponent = ({ error }: ErrorBannerProps) => {
+ return render(
+
+
+ ,
+ );
+};
+
+/** END RENDER */
+
+describe('ErrorPage Component', () => {
+ it('should display an api error message with queryid', async () => {
+ const { getByText } = renderComponent({
+ error: {
+ response: {
+ data: { message: 'api-error-message' },
+ headers: {
+ 'x-ovh-queryid': 'api-error-queryid',
+ },
+ },
+ },
+ });
+ expect(getByText('api-error-message')).toBeDefined();
+ expect(getByText('api-error-queryid', { exact: false })).toBeDefined();
+ });
+});
diff --git a/packages/manager/apps/vrack-services/src/components/FormField.component.tsx b/packages/manager/apps/vrack-services/src/components/FormField.component.tsx
index 70fa05b1b2f5..e464b591ce62 100644
--- a/packages/manager/apps/vrack-services/src/components/FormField.component.tsx
+++ b/packages/manager/apps/vrack-services/src/components/FormField.component.tsx
@@ -51,7 +51,7 @@ export const FormField: React.FC = ({
{children}
{helperText && (
- {label}
+ {helperText}
)}
{visualHint && (
diff --git a/packages/manager/apps/vrack-services/src/components/FormField.spec.tsx b/packages/manager/apps/vrack-services/src/components/FormField.spec.tsx
new file mode 100644
index 000000000000..69cee02065e1
--- /dev/null
+++ b/packages/manager/apps/vrack-services/src/components/FormField.spec.tsx
@@ -0,0 +1,72 @@
+import React from 'react';
+import { describe, expect, it, vi } from 'vitest';
+import { render, waitFor } from '@testing-library/react';
+import { FormField } from './FormField.component';
+
+const renderComponent = ({
+ children,
+ label,
+ className = '',
+ fullWidth,
+ isLoading,
+ helperText,
+ visualHint,
+ error,
+}: React.PropsWithChildren<{
+ className?: string;
+ label: string;
+ fullWidth?: boolean;
+ isLoading?: boolean;
+ helperText?: string;
+ visualHint?: string;
+ error?: string;
+}>) => {
+ return render(
+
+ {children}
+ ,
+ );
+};
+
+describe('FormField Component', () => {
+ it('should display an inline form field', async () => {
+ const { getByText } = renderComponent({
+ label: 'form-field-label',
+ children: 'form-field-children',
+ helperText: 'form-field-helper-text',
+ visualHint: 'form-field-visual-hint',
+ });
+
+ await waitFor(() => {
+ const labelElement = getByText('form-field-label');
+ expect(labelElement).toBeInTheDocument();
+ const OsdsFormField = labelElement.closest('osds-form-field');
+ expect(OsdsFormField).toHaveAttribute('inline');
+ expect(getByText('form-field-children')).toBeDefined();
+ expect(getByText('form-field-helper-text')).toBeDefined();
+ expect(getByText('form-field-visual-hint')).toBeDefined();
+ });
+ });
+
+ it('should display form field', async () => {
+ const { getByText } = renderComponent({
+ label: 'form-field-label',
+ fullWidth: true,
+ });
+
+ await waitFor(() => {
+ const labelElement = getByText('form-field-label');
+ expect(labelElement).toBeInTheDocument();
+ const OsdsFormField = labelElement.closest('osds-form-field');
+ expect(OsdsFormField).not.toHaveAttribute('inline');
+ });
+ });
+});
diff --git a/packages/manager/apps/vrack-services/src/components/LoadingText.component.tsx b/packages/manager/apps/vrack-services/src/components/LoadingText.component.tsx
index 9eed7ea3aa8f..6b89f02a57d9 100644
--- a/packages/manager/apps/vrack-services/src/components/LoadingText.component.tsx
+++ b/packages/manager/apps/vrack-services/src/components/LoadingText.component.tsx
@@ -17,7 +17,11 @@ export const LoadingText: React.FC = ({
description,
}) => (
-
+
{
+ return render();
+};
+
+describe('LoadingText Component', () => {
+ it('should display a loader with a text aside and a description', async () => {
+ const { getByTestId, getByText } = renderComponent({
+ title: 'test',
+ description: 'description',
+ });
+ expect(getByTestId('loading-text-spinner')).toBeDefined();
+ expect(getByText('test')).toBeDefined();
+ expect(getByText('description')).toBeDefined();
+ });
+});
diff --git a/packages/manager/apps/vrack-services/src/components/OrderSubmitModalContent.spec.tsx b/packages/manager/apps/vrack-services/src/components/OrderSubmitModalContent.spec.tsx
new file mode 100644
index 000000000000..e8953db6475c
--- /dev/null
+++ b/packages/manager/apps/vrack-services/src/components/OrderSubmitModalContent.spec.tsx
@@ -0,0 +1,94 @@
+import React from 'react';
+import { describe, expect, it, vi } from 'vitest';
+import { act, fireEvent, render, waitFor } from '@testing-library/react';
+import {
+ ShellContext,
+ ShellContextType,
+} from '@ovh-ux/manager-react-shell-client';
+import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
+import { postOrderCartCartIdCheckout } from '@ovh-ux/manager-module-order';
+import userEvent from '@testing-library/user-event';
+import { OrderSubmitModalContent } from './OrderSubmitModalContent.component';
+
+/** MOCKS */
+
+const onErrorMock = vi.fn();
+
+vi.mock('@ovh-ux/manager-module-order', async (importOriginal) => {
+ const original: typeof import('@ovh-ux/manager-module-order') = await importOriginal();
+ return {
+ ...original,
+ postOrderCartCartIdCheckout: vi.fn(),
+ };
+});
+
+/** END MOCKS */
+
+/** RENDER */
+const queryClient = new QueryClient();
+
+const shellContext = {
+ environment: {
+ user: { ovhSubsidiary: 'FR' },
+ },
+};
+
+const renderComponent = ({ onError }: { onError: () => void }) => {
+ return render(
+
+
+
+ ,
+
+ ,
+ );
+};
+
+/** END RENDER */
+/** TESTS */
+describe('OrderSubmitModalContent Component', () => {
+ it('should display an error message if order fail', async () => {
+ const user = userEvent.setup();
+
+ vi.mocked(postOrderCartCartIdCheckout).mockRejectedValue({
+ response: { data: { message: 'api-error' } },
+ });
+
+ const { getByText } = renderComponent({ onError: onErrorMock });
+
+ const button = getByText('order-submit-modal-button-label');
+ const checkbox = getByText('modalConfirmContractsCheckboxLabel');
+
+ expect(button).toHaveAttribute('disabled');
+
+ await act(() => {
+ user.click(checkbox);
+ });
+
+ await waitFor(() => {
+ expect(button).not.toHaveAttribute('disabled');
+ });
+
+ await act(() => {
+ fireEvent.click(button);
+ });
+
+ await waitFor(() => {
+ expect(postOrderCartCartIdCheckout).toHaveBeenCalledWith({
+ cartId: '1',
+ autoPayWithPreferredPaymentMethod: true,
+ waiveRetractationPeriod: true,
+ });
+ expect(getByText('api-error')).toBeDefined();
+ expect(onErrorMock).toHaveBeenCalledOnce();
+ });
+ });
+});
diff --git a/packages/manager/apps/vrack-services/src/components/ProductStatusChip.component.tsx b/packages/manager/apps/vrack-services/src/components/ProductStatusChip.component.tsx
index a412a447cbf3..d5d1661bc2d0 100644
--- a/packages/manager/apps/vrack-services/src/components/ProductStatusChip.component.tsx
+++ b/packages/manager/apps/vrack-services/src/components/ProductStatusChip.component.tsx
@@ -24,6 +24,10 @@ export const ProductStatusChip: React.FC = ({
{t(productStatus)}
) : (
-
+
);
};
diff --git a/packages/manager/apps/vrack-services/src/components/ProductStatusChip.spec.tsx b/packages/manager/apps/vrack-services/src/components/ProductStatusChip.spec.tsx
new file mode 100644
index 000000000000..c4b745550475
--- /dev/null
+++ b/packages/manager/apps/vrack-services/src/components/ProductStatusChip.spec.tsx
@@ -0,0 +1,29 @@
+import React from 'react';
+import { describe, expect, it, vi } from 'vitest';
+import { act, fireEvent, render } from '@testing-library/react';
+import { ODS_THEME_COLOR_INTENT } from '@ovhcloud/ods-common-theming';
+import {
+ ProductStatusChip,
+ ProductStatusChipProps,
+} from './ProductStatusChip.component';
+import { ProductStatus } from '@/data';
+
+const renderComponent = ({ productStatus }: ProductStatusChipProps) => {
+ return render();
+};
+
+describe('ProductStatusChip Component', () => {
+ it.each([
+ [ProductStatus.ACTIVE, ODS_THEME_COLOR_INTENT.success],
+ [ProductStatus.SUSPENDED, ODS_THEME_COLOR_INTENT.default],
+ [ProductStatus.DRAFT, ODS_THEME_COLOR_INTENT.info],
+ ])('should display the %s status in the %s color', (status, color) => {
+ const { getByText } = renderComponent({ productStatus: status });
+ expect(getByText(status)).toHaveAttribute('color', color);
+ });
+
+ it('should display a loader if status not available', async () => {
+ const { getByTestId } = renderComponent({ productStatus: undefined });
+ expect(getByTestId('status-chip-spinner')).toBeDefined();
+ });
+});
diff --git a/packages/manager/apps/vrack-services/src/components/display-name/DisplayName.component.tsx b/packages/manager/apps/vrack-services/src/components/display-name/DisplayName.component.tsx
index d4c738e25fcf..c92d1cef41c0 100644
--- a/packages/manager/apps/vrack-services/src/components/display-name/DisplayName.component.tsx
+++ b/packages/manager/apps/vrack-services/src/components/display-name/DisplayName.component.tsx
@@ -38,6 +38,7 @@ export const DisplayName: React.FC = ({
});
navigate(urls.overview.replace(':id', vs.id));
}}
+ data-testid="display-name-link"
>
{name}
@@ -55,6 +56,7 @@ export const DisplayName: React.FC = ({
});
navigate(urls.overviewEdit.replace(':id', vs.id));
}}
+ data-testid="display-name-edit-button"
>
{name}
diff --git a/packages/manager/apps/vrack-services/src/components/display-name/DisplayName.spec.tsx b/packages/manager/apps/vrack-services/src/components/display-name/DisplayName.spec.tsx
new file mode 100644
index 000000000000..2945e63e3df2
--- /dev/null
+++ b/packages/manager/apps/vrack-services/src/components/display-name/DisplayName.spec.tsx
@@ -0,0 +1,59 @@
+import React from 'react';
+import { describe, expect, it, vi } from 'vitest';
+import { act, fireEvent, render } from '@testing-library/react';
+import { DisplayName } from '@/components/display-name/DisplayName.component';
+import vrackServicesList from '../../../mocks/vrack-services/get-vrack-services.json';
+import { VrackServicesWithIAM } from '@/data';
+
+const defaultVs = vrackServicesList[0] as VrackServicesWithIAM;
+
+const renderComponent = ({
+ isListing,
+ vs = defaultVs,
+}: {
+ isListing?: boolean;
+ vs?: VrackServicesWithIAM;
+}) => {
+ return render();
+};
+
+describe('DisplayName Component', () => {
+ it('In listing, should display the display name with link', async () => {
+ const { getByText, queryByTestId } = renderComponent({ isListing: true });
+
+ expect(queryByTestId('display-name-link')).toBeDefined();
+ expect(getByText(defaultVs.iam.displayName)).toBeDefined();
+ expect(queryByTestId('display-name-edit-button')).toBeNull();
+ });
+
+ it('In listing, should display the display name with info icon', async () => {
+ const { queryByTestId } = renderComponent({
+ isListing: true,
+ vs: vrackServicesList[2] as VrackServicesWithIAM,
+ });
+ expect(queryByTestId('warning-icon')).toBeDefined();
+ });
+
+ it('In listing, should display the display name with loader', async () => {
+ const { queryByTestId } = renderComponent({
+ isListing: true,
+ vs: vrackServicesList[3] as VrackServicesWithIAM,
+ });
+ expect(queryByTestId('vs-loader-operation-in-progress')).toBeDefined();
+ });
+
+ it('In Dashboard, should display the display name with edit action', async () => {
+ const { getByText, queryByTestId } = renderComponent({});
+
+ expect(getByText(defaultVs.iam.displayName)).toBeDefined();
+ expect(queryByTestId('edit-button')).toBeDefined();
+ expect(queryByTestId('display-name-link')).toBeNull();
+ });
+
+ it('In Dashboard, should display the display name with disabled edit action', async () => {
+ const { queryByTestId } = renderComponent({
+ vs: vrackServicesList[2] as VrackServicesWithIAM,
+ });
+ expect(queryByTestId('edit-button')).toHaveProperty('disabled');
+ });
+});
diff --git a/packages/manager/apps/vrack-services/src/components/display-name/EditButton.component.tsx b/packages/manager/apps/vrack-services/src/components/display-name/EditButton.component.tsx
index 153de57716d7..b6e118fcad94 100644
--- a/packages/manager/apps/vrack-services/src/components/display-name/EditButton.component.tsx
+++ b/packages/manager/apps/vrack-services/src/components/display-name/EditButton.component.tsx
@@ -43,6 +43,7 @@ export const EditButton: React.FC = ({
size={ODS_BUTTON_SIZE.sm}
{...handleClick(onClick)}
disabled={disabled || undefined}
+ data-testid="edit-button"
>
= ({ className, vs }) => {
color={ODS_THEME_COLOR_INTENT.warning}
size={size}
name={ODS_ICON_NAME.WARNING_CIRCLE}
+ data-testid="warning-icon"
/>
) : (
)}
diff --git a/packages/manager/apps/vrack-services/src/components/vrack-id/VrackId.spec.tsx b/packages/manager/apps/vrack-services/src/components/vrack-id/VrackId.spec.tsx
new file mode 100644
index 000000000000..fc841bed3ab3
--- /dev/null
+++ b/packages/manager/apps/vrack-services/src/components/vrack-id/VrackId.spec.tsx
@@ -0,0 +1,76 @@
+import React from 'react';
+import { describe, expect, it, vi } from 'vitest';
+import { render, waitFor } from '@testing-library/react';
+import {
+ ShellContext,
+ ShellContextType,
+} from '@ovh-ux/manager-react-shell-client';
+import { VrackId } from './VrackId.component';
+import { VrackServicesWithIAM } from '@/data';
+import vrackServicesList from '../../../mocks/vrack-services/get-vrack-services.json';
+
+const defaultVs = vrackServicesList[5] as VrackServicesWithIAM;
+const vsWithoutVrack = vrackServicesList[0] as VrackServicesWithIAM;
+
+/** Render */
+
+const shellContext = {
+ environment: {
+ user: { ovhSubsidiary: 'FR' },
+ },
+ shell: {
+ tracking: {
+ trackClick: vi.fn(),
+ trackPage: vi.fn(),
+ init: vi.fn(),
+ },
+ navigation: {
+ getURL: vi.fn().mockResolvedValue('link-to-vrack'),
+ },
+ },
+};
+
+const renderComponent = ({
+ isListing,
+ vs,
+}: {
+ isListing?: boolean;
+ vs: VrackServicesWithIAM;
+}) => {
+ return render(
+
+
+ ,
+ );
+};
+
+/** END RENDER */
+
+describe('VrackId Component', () => {
+ it('should display link to vrack if associated', async () => {
+ const { getByText, queryByText } = renderComponent({ vs: defaultVs });
+
+ await waitFor(() => {
+ expect(getByText(defaultVs.currentState.vrackId)).toBeDefined();
+ expect(getByText(defaultVs.currentState.vrackId)).toHaveAttribute(
+ 'href',
+ 'link-to-vrack',
+ );
+ expect(getByText('vrackActionAssociateToAnother')).toBeDefined();
+ expect(getByText('vrackActionDissociate')).toBeDefined();
+ expect(queryByText('associateVrackButtonLabel')).toBeNull();
+ });
+ });
+
+ it('should display action to link vrack if not associated', async () => {
+ const { getByText, queryByText } = renderComponent({ vs: vsWithoutVrack });
+
+ await waitFor(() => {
+ expect(queryByText('vrackActionAssociateToAnother')).toBeNull();
+ expect(queryByText('vrackActionDissociate')).toBeNull();
+ expect(getByText('associateVrackButtonLabel')).toBeDefined();
+ });
+ });
+});
diff --git a/packages/manager/apps/vrack-services/tailwind.config.js b/packages/manager/apps/vrack-services/tailwind.config.js
index d54ec991a400..657ab11bb87d 100644
--- a/packages/manager/apps/vrack-services/tailwind.config.js
+++ b/packages/manager/apps/vrack-services/tailwind.config.js
@@ -1,3 +1,4 @@
+import path from 'path';
import config from '@ovh-ux/manager-tailwind-config';
/** @type {import('tailwindcss').Config} */
@@ -5,6 +6,9 @@ module.exports = {
...config,
content: [
'./src/**/*.{js,jsx,ts,tsx}',
- '../../../manager-react-components/src/**/*.{js,jsx,ts,tsx}',
+ path.join(
+ path.dirname(require.resolve('@ovh-ux/manager-react-components')),
+ '**/*.{js,jsx,ts,tsx}',
+ ),
],
};
diff --git a/packages/manager/apps/vrack-services/tsconfig.json b/packages/manager/apps/vrack-services/tsconfig.json
index 148b6ca3f83b..a92d6883f770 100644
--- a/packages/manager/apps/vrack-services/tsconfig.json
+++ b/packages/manager/apps/vrack-services/tsconfig.json
@@ -1,9 +1,9 @@
{
"compilerOptions": {
- "lib": ["dom", "es2020"],
+ "lib": ["dom", "es2021"],
"noEmit": true,
"target": "es2020",
- "types": ["vite/client", "node", "jest"],
+ "types": ["vite/client", "node"],
"module": "ES2020",
"moduleResolution": "node",
"removeComments": true,
@@ -19,11 +19,9 @@
"jsx": "react",
"baseUrl": ".",
"paths": {
- "@/*": ["./src/*"],
- "@playwright-helpers": ["../../../../playwright-helpers/index"],
- "@playwright-helpers/*": ["../../../../playwright-helpers/*"]
+ "@/*": ["./src/*"]
}
},
- "include": ["**/*.ts", "**/*.tsx"],
- "exclude": ["node_modules", "dist", "types", "src/__tests__", "*.spec.ts"]
+ "include": ["src"],
+ "exclude": ["node_modules", "dist", "types"]
}
diff --git a/packages/manager/apps/vrack-services/vite.config.mjs b/packages/manager/apps/vrack-services/vite.config.mjs
index 2b63c8229ae8..f33ab6dc98cd 100644
--- a/packages/manager/apps/vrack-services/vite.config.mjs
+++ b/packages/manager/apps/vrack-services/vite.config.mjs
@@ -2,12 +2,7 @@ import { defineConfig } from 'vite';
import { getBaseConfig } from '@ovh-ux/manager-vite-config';
import { resolve } from 'path';
-const config = getBaseConfig({});
-
export default defineConfig({
- ...config,
+ ...getBaseConfig(),
root: resolve(process.cwd()),
- resolve: {
- ...config.resolve,
- },
});
diff --git a/packages/manager/apps/vrack-services/vitest.config.js b/packages/manager/apps/vrack-services/vitest.config.js
index 3fffd533bc02..a5e80ab7f4a3 100644
--- a/packages/manager/apps/vrack-services/vitest.config.js
+++ b/packages/manager/apps/vrack-services/vitest.config.js
@@ -8,8 +8,28 @@ export default defineConfig({
test: {
globals: true,
environment: 'jsdom',
+ setupFiles: './setupTests.ts',
coverage: {
- include: ['src/utils'],
+ include: ['src'],
+ exclude: [
+ 'src/types',
+ 'src/test-utils',
+ 'src/vite-*.ts',
+ 'src/App.tsx',
+ 'src/index.tsx',
+ 'src/tracking.constant.ts',
+ ],
+ },
+ testTimeout: 60000,
+ fileParallelism: false,
+ maxWorkers: 1,
+ pollOptions: {
+ forks: {
+ singleFork: true,
+ },
+ threads: {
+ singleThread: true,
+ },
},
},
resolve: {