Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
import { screen, waitFor } from '@testing-library/react';
import { renderWithProviders } from '@console/shared/src/test-utils/unit-test-utils';
import { fetchHelmReleaseHistory } from '../../../../utils/helm-utils';
import HelmReleaseHistory from '../HelmReleaseHistory';

jest.mock('react-router', () => ({
...jest.requireActual('react-router'),
useParams: jest.fn().mockReturnValue({ ns: 'test-ns', name: 'my-release' }),
}));

jest.mock('../../../../utils/helm-utils', () => ({
...jest.requireActual('../../../../utils/helm-utils'),
fetchHelmReleaseHistory: jest.fn(),
}));

jest.mock('../HelmReleaseHistoryTable', () => {
const MockTable = (props: { releaseHistory: unknown[]; isLoading: boolean }) => (
<div
data-test="mock-history-table"
data-loading={String(props.isLoading)}
data-count={String(props.releaseHistory?.length ?? 0)}
/>
);
return { __esModule: true, default: MockTable };
});

jest.mock('../HelmReleaseHistoryTableHelpers', () => ({
useHelmReleaseHistoryColumns: jest.fn(),
getHelmReleaseHistoryRows: jest.fn().mockReturnValue([]),
getHistoryColumnIndexById: jest.fn().mockReturnValue(0),
}));

jest.mock('@console/internal/components/utils', () => ({
StatusBox: (props: { loadError: string; label: string }) => (
<div data-test="status-box">{props.loadError}</div>
),
}));

jest.mock('@console/shared/src/components/layout/PaneBody', () => {
const MockPaneBody = (props: { children: unknown }) => (
<div data-test="pane-body">{props.children as string}</div>
);
return { __esModule: true, default: MockPaneBody };
});

jest.mock('@console/shared/src/hooks/useDeepCompareMemoize', () => ({
useDeepCompareMemoize: (value: unknown) => value,
}));

const mockFetchHistory = fetchHelmReleaseHistory as jest.Mock;

const mockObj = {
metadata: { name: 'my-release', namespace: 'test-ns' },
};

const mockHelmRelease = {
name: 'my-release',
namespace: 'test-ns',
version: 3,
chart: {
metadata: { name: 'my-chart', version: '1.0.0', apiVersion: 'v2', urls: [] },
files: [],
templates: [],
values: {},
},
info: {
description: 'Install complete',
deleted: '',
first_deployed: '2024-01-01T00:00:00Z',
last_deployed: '2024-01-03T00:00:00Z',
status: 'deployed',
notes: '',
},
};

const mockRevisions = [
{
...mockHelmRelease,
version: 1,
info: { ...mockHelmRelease.info, last_deployed: '2024-01-01T00:00:00Z' },
},
{
...mockHelmRelease,
version: 2,
info: { ...mockHelmRelease.info, last_deployed: '2024-01-02T00:00:00Z' },
},
{
...mockHelmRelease,
version: 3,
info: { ...mockHelmRelease.info, last_deployed: '2024-01-03T00:00:00Z' },
},
];

const defaultProps = {
obj: mockObj,
customData: mockHelmRelease,
};

describe('HelmReleaseHistory', () => {
beforeEach(() => {
jest.clearAllMocks();
});

it('should render the history table when revisions load successfully', async () => {
mockFetchHistory.mockResolvedValue(mockRevisions);
renderWithProviders(<HelmReleaseHistory {...defaultProps} />);
await waitFor(() => {
expect(screen.getByTestId('mock-history-table')).toBeTruthy();
});
});

it('should show loading state while fetching revisions', () => {
mockFetchHistory.mockReturnValue(new Promise(() => {}));
renderWithProviders(<HelmReleaseHistory {...defaultProps} />);
const table = screen.getByTestId('mock-history-table');
expect(table.getAttribute('data-loading')).toBe('true');
});

it('should show StatusBox with error when fetchHelmReleaseHistory fails', async () => {
mockFetchHistory.mockRejectedValue(new Error('Network error'));
renderWithProviders(<HelmReleaseHistory {...defaultProps} />);
await waitFor(() => {
expect(screen.getByTestId('status-box')).toBeTruthy();
});
expect(screen.getByText('Network error')).toBeTruthy();
});

it('should pass revisions to HelmReleaseHistoryTable', async () => {
mockFetchHistory.mockResolvedValue(mockRevisions);
renderWithProviders(<HelmReleaseHistory {...defaultProps} />);
await waitFor(() => {
const table = screen.getByTestId('mock-history-table');
expect(table.getAttribute('data-count')).toBe('3');
});
});

it('should fetch history with the correct namespace and release name', () => {
mockFetchHistory.mockReturnValue(new Promise(() => {}));
renderWithProviders(<HelmReleaseHistory {...defaultProps} />);
expect(mockFetchHistory).toHaveBeenCalledWith('my-release', 'test-ns');
});

it('should show default error message when error has no message', async () => {
mockFetchHistory.mockRejectedValue(new Error());
renderWithProviders(<HelmReleaseHistory {...defaultProps} />);
await waitFor(() => {
expect(screen.getByTestId('status-box')).toBeTruthy();
});
expect(screen.getByText('Unable to load Helm Release history')).toBeTruthy();
});

it('should render within PaneBody when loaded successfully', async () => {
mockFetchHistory.mockResolvedValue(mockRevisions);
renderWithProviders(<HelmReleaseHistory {...defaultProps} />);
await waitFor(() => {
expect(screen.getByTestId('pane-body')).toBeTruthy();
});
expect(screen.getByTestId('mock-history-table')).toBeTruthy();
});

it('should not render PaneBody when there is a load error', async () => {
mockFetchHistory.mockRejectedValue(new Error('Server error'));
renderWithProviders(<HelmReleaseHistory {...defaultProps} />);
await waitFor(() => {
expect(screen.getByTestId('status-box')).toBeTruthy();
});
expect(screen.queryByTestId('pane-body')).toBeNull();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,204 @@
import { screen } from '@testing-library/react';
import { EditorType } from '@console/shared/src/components/synced-editor/editor-toggle';
import { renderWithProviders } from '@console/shared/src/test-utils/unit-test-utils';
import CreateHelmChartRepositoryForm from '../CreateHelmChartRepositoryForm';

jest.mock('@console/shared/src/components/form-utils/FlexForm', () => ({
FlexForm: ({ children, onSubmit }: any) => (
<form data-test="flex-form" onSubmit={onSubmit}>
{children}
</form>
),
}));

jest.mock('@console/shared/src/components/form-utils/FormBody', () => ({
FormBody: ({ children }: any) => <div>{children}</div>,
}));

jest.mock('@console/shared/src/components/form-utils/FormHeader', () => ({
FormHeader: ({ title, helpText }: any) => (
<div data-test="form-header">
<h1>{title}</h1>
{helpText && <span>{helpText}</span>}
</div>
),
}));

jest.mock('@console/shared/src/components/form-utils/FormFooter', () => ({
FormFooter: ({ submitLabel, disableSubmit, errorMessage, successMessage, handleCancel }: any) => (
<div data-test="form-footer">
<button type="submit" disabled={disableSubmit}>
{submitLabel}
</button>
<button type="button" onClick={handleCancel}>
Cancel
</button>
{errorMessage && <div data-test="error-message">{errorMessage}</div>}
{successMessage && <div data-test="success-message">{successMessage}</div>}
</div>
),
}));

jest.mock('@console/shared/src/components/formik-fields/SyncedEditorField', () => ({
SyncedEditorField: ({ formContext, yamlContext }: any) => (
<div data-test="synced-editor">
{formContext?.editor}
{yamlContext?.editor}
</div>
),
}));

jest.mock('@console/shared/src/components/formik-fields/CodeEditorField', () => ({
CodeEditorField: () => <div data-test="code-editor">YAML Editor</div>,
}));

jest.mock('../CreateHelmChartRepositoryFormEditor', () => ({
__esModule: true,
default: () => <div data-test="form-editor">Form Editor</div>,
}));

jest.mock('../helmchartrepository-create-utils', () => ({
convertToForm: jest.fn((v: any) => v),
convertToHelmChartRepository: jest.fn(() => ({})),
}));

jest.mock('@console/shared/src/components/editor/yaml-download-utils', () => ({
downloadYaml: jest.fn(),
}));

jest.mock('@console/shared/src/utils/yaml', () => ({
safeJSToYAML: jest.fn(() => 'yaml: data'),
}));

const defaultFormData = {
repoName: 'my-repo',
repoUrl: 'https://example.com/charts',
scope: 'HelmChartRepository',
repoDisplayName: '',
repoDescription: '',
};

const defaultFormikProps = {
values: {
editorType: EditorType.Form,
formData: defaultFormData,
yamlData: 'apiVersion: v1',
},
errors: {},
touched: {},
isSubmitting: false,
isValidating: false,
status: undefined,
submitCount: 0,
dirty: false,
isValid: true,
initialValues: {
editorType: EditorType.Form,
formData: defaultFormData,
yamlData: 'apiVersion: v1',
},
initialErrors: {},
initialTouched: {},
initialStatus: undefined,
handleSubmit: jest.fn(),
handleReset: jest.fn(),
handleBlur: jest.fn(),
handleChange: jest.fn(),
resetForm: jest.fn(),
setErrors: jest.fn(),
setFieldError: jest.fn(),
setFieldTouched: jest.fn(),
setFieldValue: jest.fn(),
setFormikState: jest.fn(),
setStatus: jest.fn(),
setSubmitting: jest.fn(),
setTouched: jest.fn(),
setValues: jest.fn(),
submitForm: jest.fn(),
validateForm: jest.fn(),
validateField: jest.fn(),
getFieldProps: jest.fn(),
getFieldMeta: jest.fn(),
getFieldHelpers: jest.fn(),
registerField: jest.fn(),
unregisterField: jest.fn(),
};

const defaultProps = {
...defaultFormikProps,
namespace: 'test-ns',
handleCancel: jest.fn(),
showScopeType: true,
existingRepo: null,
};

describe('CreateHelmChartRepositoryForm', () => {
afterEach(() => {
jest.restoreAllMocks();
});

it('should render the form title for creating a new repository', () => {
renderWithProviders(<CreateHelmChartRepositoryForm {...defaultProps} />);

expect(screen.getByText('Create Helm Chart Repository')).toBeVisible();
});

it('should render the form and YAML editors via SyncedEditorField', () => {
renderWithProviders(<CreateHelmChartRepositoryForm {...defaultProps} />);

expect(screen.getByTestId('synced-editor')).toBeVisible();
expect(screen.getByTestId('form-editor')).toBeVisible();
expect(screen.getByTestId('code-editor')).toBeVisible();
});

it('should render Create as the submit button label for new repos', () => {
renderWithProviders(<CreateHelmChartRepositoryForm {...defaultProps} />);

expect(screen.getByRole('button', { name: 'Create' })).toBeVisible();
});

it('should render Save as the submit button label when editing existing repo', () => {
const existingRepo = {
apiVersion: 'helm.openshift.io/v1beta1',
kind: 'HelmChartRepository',
metadata: { name: 'existing-repo' },
spec: { connectionConfig: { url: 'https://example.com' } },
};
renderWithProviders(
<CreateHelmChartRepositoryForm {...defaultProps} existingRepo={existingRepo} />,
);

expect(screen.getByRole('button', { name: 'Save' })).toBeVisible();
});

it('should disable submit button when form is not dirty', () => {
renderWithProviders(<CreateHelmChartRepositoryForm {...defaultProps} dirty={false} />);

expect(screen.getByRole('button', { name: 'Create' })).toBeDisabled();
});

it('should enable submit button when form is dirty and has no errors', () => {
renderWithProviders(
<CreateHelmChartRepositoryForm {...defaultProps} dirty isSubmitting={false} />,
);

expect(screen.getByRole('button', { name: 'Create' })).not.toBeDisabled();
});

it('should display submit error message', () => {
renderWithProviders(
<CreateHelmChartRepositoryForm
{...defaultProps}
status={{ submitError: 'Failed to create' }}
/>,
);

expect(screen.getByText('Failed to create')).toBeVisible();
});

it('should show the form description text', () => {
renderWithProviders(<CreateHelmChartRepositoryForm {...defaultProps} />);

expect(screen.getByText('Add helm chart repository.')).toBeVisible();
});
});
Loading