Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test(addSourceModal): ds-767 lint, test updates #446

Merged
merged 1 commit into from
Sep 18, 2024
Merged
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
1 change: 0 additions & 1 deletion config/jest.setupTests.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import React, { act } from 'react';
import { render, renderHook } from '@testing-library/react';
import { dotenv } from 'weldable';
import { useNavigate } from 'react-router-dom';

/**
* Set dotenv params.
Expand Down
41 changes: 41 additions & 0 deletions src/hooks/__tests__/__snapshots__/useCredentialApi.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -344,3 +344,44 @@ exports[`useEditCredentialApi should process an API success response: callbackSu
],
]
`;

exports[`useEditCredentialApi useGetCredentialsApi should attempt an api call to retrieve credentials: apiCall 1`] = `
[
[
"/api/v1/credentials/",
{},
],
]
`;

exports[`useEditCredentialApi useGetCredentialsApi should handle errors while attempting to retrieve credentials: getCredentials, error 1`] = `
{
"isAxiosError": true,
"message": "Mock error",
}
`;

exports[`useEditCredentialApi useGetCredentialsApi should handle success while attempting to retrieve credentials: getCredentials, success 1`] = `{}`;

exports[`useEditCredentialApi useGetCredentialsApi should process an API error response: callbackError 1`] = `
{
"response": {
"data": {
"message": "Dolor sit",
},
},
}
`;

exports[`useEditCredentialApi useGetCredentialsApi should process an API success response: callbackSuccess 1`] = `
{
"data": {
"results": [
{
"id": "1",
"name": "Lorem",
},
],
},
}
`;
68 changes: 67 additions & 1 deletion src/hooks/__tests__/useCredentialApi.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import { renderHook } from '@testing-library/react';
import axios from 'axios';
import { useAddCredentialApi, useDeleteCredentialApi, useEditCredentialApi } from '../useCredentialApi';
import {
useAddCredentialApi,
useDeleteCredentialApi,
useEditCredentialApi,
useGetCredentialsApi
} from '../useCredentialApi';

describe('useDeleteCredentialApi', () => {
let mockOnAddAlert;
Expand Down Expand Up @@ -273,4 +278,65 @@ describe('useEditCredentialApi', () => {

expect(mockOnAddAlert.mock.calls).toMatchSnapshot('callbackError');
});

describe('useGetCredentialsApi', () => {
let hookResult;

beforeEach(() => {
const hook = renderHook(() => useGetCredentialsApi());
hookResult = hook?.result?.current;
});

afterEach(() => {
jest.clearAllMocks();
});

it('should attempt an api call to retrieve credentials', () => {
const { apiCall } = hookResult;
const spyAxios = jest.spyOn(axios, 'get');

apiCall().catch(Function.prototype);
expect(spyAxios.mock.calls).toMatchSnapshot('apiCall');
});

it('should handle success while attempting to retrieve credentials', async () => {
const { getCredentials } = hookResult;
jest.spyOn(axios, 'get').mockImplementation(() => Promise.resolve({}));

await expect(getCredentials()).resolves.toMatchSnapshot('getCredentials, success');
});

it('should handle errors while attempting to retrieve credentials', async () => {
const { getCredentials } = hookResult;
jest.spyOn(axios, 'get').mockImplementation(() => Promise.reject({ isAxiosError: true, message: 'Mock error' }));

await expect(getCredentials()).rejects.toMatchSnapshot('getCredentials, error');
});

it('should process an API success response', () => {
const { callbackSuccess } = hookResult;

expect(
callbackSuccess({
data: {
results: [{ name: 'Lorem', id: '1' }]
}
})
).toMatchSnapshot('callbackSuccess');
});

it('should process an API error response', async () => {
const { callbackError } = hookResult;

await expect(
callbackError({
response: {
data: {
message: 'Dolor sit'
}
}
})
).rejects.toMatchSnapshot('callbackError');
});
});
});
50 changes: 48 additions & 2 deletions src/hooks/useCredentialApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
import { useCallback } from 'react';
import { useTranslation } from 'react-i18next';
import { type AlertProps } from '@patternfly/react-core';
import axios, { type AxiosError, type AxiosResponse, isAxiosError } from 'axios';
import axios, { type AxiosError, type AxiosRequestConfig, type AxiosResponse, isAxiosError } from 'axios';
import { helpers } from '../helpers';
import apiHelpers from '../helpers/apiHelpers';
import { type CredentialType } from '../types/types';
Expand All @@ -24,6 +24,13 @@ type ApiDeleteCredentialSuccessType = {
skipped?: { credential: number; sources: number[] }[];
};

type ApiGetCredentialsSuccessType = {
count?: number;
next?: string | null;
previous?: string | null;
results?: CredentialType[];
};

type ApiCredentialErrorType = {
detail?: string;
message: string;
Expand Down Expand Up @@ -279,4 +286,43 @@ const useEditCredentialApi = (onAddAlert: (alert: AlertProps) => void) => {
};
};

export { useDeleteCredentialApi, useAddCredentialApi, useEditCredentialApi };
const useGetCredentialsApi = () => {
const apiCall = useCallback(
(config: AxiosRequestConfig = {}): Promise<AxiosResponse<ApiGetCredentialsSuccessType>> =>
axios.get(`${process.env.REACT_APP_CREDENTIALS_SERVICE}`, config),
[]
);

const callbackSuccess = useCallback((response: AxiosResponse<ApiGetCredentialsSuccessType>) => {
return response;
}, []);

const callbackError = useCallback((error: AxiosError<ApiCredentialErrorType>) => Promise.reject(error), []);

const getCredentials = useCallback(
async (config: AxiosRequestConfig = {}) => {
let response;
try {
response = await apiCall(config);
} catch (error) {
if (isAxiosError(error)) {
return callbackError(error);
}
if (!helpers.TEST_MODE) {
console.error(error);
}
}
return callbackSuccess(response);
},
[apiCall, callbackSuccess, callbackError]
);

return {
apiCall,
callbackError,
callbackSuccess,
getCredentials
};
};

export { useDeleteCredentialApi, useAddCredentialApi, useEditCredentialApi, useGetCredentialsApi };
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`AddSourceModal should call onSubmit with the correct filtered data when "Save" is clicked: onSubmit, filtered data 1`] = `
[
[
{
"credentials": [],
"hosts": [
"",
],
"name": "Test Source",
"options": {
"use_paramiko": false,
},
"port": "22",
"source_type": "network",
},
],
]
`;

exports[`AddSourceModal should have the correct title: title 1`] = `
<span
class="pf-v5-c-modal-box__title-text"
>
Add source: network
</span>
`;

exports[`AddSourceModal should render a basic component: basic 1`] = `
<Modal
actions={[]}
appendTo={[Function]}
aria-describedby=""
aria-label=""
aria-labelledby=""
className=""
hasNoBodyWrapper={false}
isOpen={true}
onClose={[Function]}
ouiaSafe={true}
position="default"
showClose={true}
title="Add source: undefined"
titleIconVariant={null}
titleLabel=""
variant="small"
>
<FormContextProvider
initialValues={
{
"hosts": "",
"name": "",
"port": "",
}
}
>
[Function]
</FormContextProvider>
</Modal>
`;
58 changes: 58 additions & 0 deletions src/views/sources/__tests__/addSourceModal.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import React, { act } from 'react';
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import axios from 'axios';
import { shallowComponent } from '../../../../config/jest.setupTests';
import { AddSourceModal } from '../addSourceModal';

describe('AddSourceModal', () => {
let mockOnClose;
let mockOnSubmit;

beforeEach(async () => {
jest.spyOn(axios, 'get').mockImplementation(() => Promise.resolve({}));

await act(async () => {
mockOnClose = jest.fn();
mockOnSubmit = jest.fn();
await render(
<AddSourceModal
isOpen={true}
sourceType="network"
source={undefined}
onClose={mockOnClose}
onSubmit={mockOnSubmit}
/>
);
});
});

afterEach(() => {
jest.clearAllMocks();
});

it('should render a basic component', async () => {
const component = await shallowComponent(<AddSourceModal isOpen={true} />);
expect(component).toMatchSnapshot('basic');
});

it('should have the correct title', () => {
const title = screen.getByText(/Add\sSource:\snetwork/i);
expect(title).toMatchSnapshot('title');
});

it('should call onSubmit with the correct filtered data when "Save" is clicked', async () => {
const user = userEvent.setup();
await user.type(screen.getByPlaceholderText('Enter a name for the source'), 'Test Source');
await user.click(screen.getByText('Save'));

expect(mockOnSubmit.mock.calls).toMatchSnapshot('onSubmit, filtered data');
});

it('should call onClose', async () => {
const user = userEvent.setup();
await user.click(screen.getByText('Cancel'));

expect(mockOnClose).toHaveBeenCalledTimes(1);
});
});
Loading
Loading