Skip to content

Commit

Permalink
add unit tests
Browse files Browse the repository at this point in the history
  • Loading branch information
SKarolFolio committed Dec 24, 2024
1 parent 958c80d commit f1d4d63
Showing 1 changed file with 285 additions and 0 deletions.
285 changes: 285 additions & 0 deletions src/test/__tests__/common/hooks/useRecordControls.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,285 @@
import { renderHook } from '@testing-library/react';
import { setInitialGlobalState } from '@src/test/__mocks__/store';
import * as recordsApi from '@common/api/records.api';
import * as recordHelper from '@common/helpers/record.helper';
import { RecordStatus } from '@common/constants/record.constants';
import { StatusType } from '@common/constants/status.constants';
import { BibframeEntities } from '@common/constants/bibframe.constants';
import { ExternalResourceIdType } from '@common/constants/api.constants';
import { ROUTES } from '@common/constants/routes.constants';
import { useRecordControls } from '@common/hooks/useRecordControls';
import { useRecordGeneration } from '@common/hooks/useRecordGeneration';
import { PreviewParams } from '@common/hooks/useConfig.hook';
import { UserNotificationFactory } from '@common/services/userNotification';
import { useInputsStore, useStatusStore } from '@src/store';

jest.mock('@common/constants/build.constants', () => ({ IS_EMBEDDED_MODE: false }));

const mockNavigate = jest.fn();
jest.mock('react-router-dom', () => ({
useNavigate: () => mockNavigate,
useLocation: () => ({ state: {} }),
useSearchParams: () => [new URLSearchParams(), jest.fn()],
}));

jest.mock('@common/api/records.api', () => ({
getRecord: jest.fn(),
postRecord: jest.fn(),
putRecord: jest.fn(),
}));

jest.mock('@common/helpers/recordFormatting.helper', () => ({
formatRecord: jest.fn(),
}));

const mockGenerateRecord = jest.fn();
jest.mock('@common/hooks/useRecordGeneration', () => ({
useRecordGeneration: () => ({
generateRecord: mockGenerateRecord,
}),
}));

const mockGetProfiles = jest.fn();
jest.mock('@common/hooks/useConfig.hook', () => ({
useConfig: () => ({
getProfiles: mockGetProfiles,
}),
}));

jest.mock('@common/services/userNotification', () => ({
UserNotificationFactory: {
createMessage: jest.fn(),
},
}));

describe('useRecordControls', () => {
const mockSetRecordStatus = jest.fn();
const mockApiResponse = { id: 'test-id' };

beforeEach(() => {
jest.spyOn(console, 'error').mockReturnValue();
});

describe('saveRecord', () => {
beforeEach(() => {
setInitialGlobalState([
{
store: useStatusStore,
state: {
setRecordStatus: mockSetRecordStatus,
},
},
{
store: useInputsStore,
state: {
selectedRecordBlocks: null,
record: {},
},
},
]);

mockGenerateRecord.mockReturnValue({});
});

it('saves record with default props', async () => {
const mockResponse = { json: () => Promise.resolve(mockApiResponse) };
(recordsApi.postRecord as jest.Mock).mockResolvedValue(mockResponse);

const { result } = renderHook(() => useRecordControls());
await result.current.saveRecord();

expect(recordsApi.postRecord).toHaveBeenCalled();
});

it('handles save with asRefToNewRecord=true', async () => {
const mockResponse = { json: () => Promise.resolve(mockApiResponse) };
(recordsApi.postRecord as jest.Mock).mockResolvedValue(mockResponse);

const { result } = renderHook(() => useRecordControls());
await result.current.saveRecord({ asRefToNewRecord: true });

expect(recordsApi.postRecord).toHaveBeenCalled();
});

it('handles save with isNavigatingBack=false', async () => {
const mockResponse = { json: () => Promise.resolve(mockApiResponse) };
(recordsApi.putRecord as jest.Mock).mockResolvedValue(mockResponse);

jest.spyOn(recordHelper, 'getRecordId').mockReturnValue('existing-id');

const { result } = renderHook(() => useRecordControls());
await result.current.saveRecord({ isNavigatingBack: false });

expect(recordsApi.putRecord).toHaveBeenCalled();
});

it('handles save errors', async () => {
(recordsApi.postRecord as jest.Mock).mockRejectedValue(new Error('Save failed'));

const { result } = renderHook(() => useRecordControls());
await result.current.saveRecord();

expect(UserNotificationFactory.createMessage).toHaveBeenCalledWith(StatusType.error, 'ld.cantSaveRd');
});

it('updates record status on successful save', async () => {
const mockResponse = { json: () => Promise.resolve(mockApiResponse) };
(recordsApi.postRecord as jest.Mock).mockResolvedValue(mockResponse);

const { result } = renderHook(() => useRecordControls());
await result.current.saveRecord();

expect(mockSetRecordStatus).toHaveBeenCalledWith({
type: RecordStatus.saveAndClose,
});
});

it('does not save if generateRecord returns null', async () => {
jest.spyOn(useRecordGeneration(), 'generateRecord').mockReturnValue(undefined);

const { result } = renderHook(() => useRecordControls());
await result.current.saveRecord();

expect(recordsApi.postRecord).not.toHaveBeenCalled();
expect(recordsApi.putRecord).not.toHaveBeenCalled();
});
});

describe('fetchRecordAndSelectEntityValues', () => {
it('navigates to create route when contents are not found', async () => {
(recordsApi.getRecord as jest.Mock).mockResolvedValue({ resource: {} });

const { result } = renderHook(() => useRecordControls());
await result.current.fetchRecordAndSelectEntityValues('test-id', BibframeEntities.WORK);

expect(mockNavigate).toHaveBeenCalledWith(ROUTES.RESOURCE_CREATE.uri);
expect(UserNotificationFactory.createMessage).toHaveBeenCalledWith(
StatusType.error,
'ld.cantSelectReferenceContents',
);
});

it('handles errors during fetch', async () => {
(recordsApi.getRecord as jest.Mock).mockRejectedValue(new Error('Fetch failed'));

const { result } = renderHook(() => useRecordControls());
await result.current.fetchRecordAndSelectEntityValues('test-id', BibframeEntities.WORK);

expect(UserNotificationFactory.createMessage).toHaveBeenCalledWith(StatusType.error, 'ld.errorFetching');
});

it('returns undefined when the record fetch fails', async () => {
(recordsApi.getRecord as jest.Mock).mockRejectedValue(new Error('Fetch failed'));

const { result } = renderHook(() => useRecordControls());
const response = await result.current.fetchRecordAndSelectEntityValues('test-id', BibframeEntities.WORK);

expect(response).toBeUndefined();
});
});

describe('getRecordAndInitializeParsing', () => {
const mockRecord = { id: 'test-id', data: 'test data' };

it('returns undefined when no recordId and no cachedRecord provided', async () => {
const { result } = renderHook(() => useRecordControls());
const response = await result.current.getRecordAndInitializeParsing({});

expect(response).toBeUndefined();
expect(recordsApi.getRecord).not.toHaveBeenCalled();
expect(mockGetProfiles).not.toHaveBeenCalled();
});

it('uses cachedRecord when provided', async () => {
const cachedRecord = { ...mockRecord } as unknown as RecordEntry;
mockGetProfiles.mockResolvedValue(undefined);

const { result } = renderHook(() => useRecordControls());
const response = await result.current.getRecordAndInitializeParsing({
cachedRecord,
});

expect(response).toEqual(cachedRecord);
expect(recordsApi.getRecord).not.toHaveBeenCalled();
expect(mockGetProfiles).toHaveBeenCalledWith({
record: cachedRecord,
recordId: undefined,
previewParams: undefined,
});
});

it('fetches record when recordId is provided', async () => {
(recordsApi.getRecord as jest.Mock).mockResolvedValue(mockRecord);
mockGetProfiles.mockResolvedValue(undefined);

const { result } = renderHook(() => useRecordControls());
const response = await result.current.getRecordAndInitializeParsing({
recordId: 'test-id',
});

expect(response).toEqual(mockRecord);
expect(recordsApi.getRecord).toHaveBeenCalledWith({
recordId: 'test-id',
idType: undefined,
});
});

it('passes idType to getRecord when provided', async () => {
(recordsApi.getRecord as jest.Mock).mockResolvedValue(mockRecord);
mockGetProfiles.mockResolvedValue(undefined);

const { result } = renderHook(() => useRecordControls());
await result.current.getRecordAndInitializeParsing({
recordId: 'test-id',
idType: ExternalResourceIdType.Inventory,
});

expect(recordsApi.getRecord).toHaveBeenCalledWith({
recordId: 'test-id',
idType: ExternalResourceIdType.Inventory,
});
});

it('handles error with default error message', async () => {
(recordsApi.getRecord as jest.Mock).mockRejectedValue(new Error('API Error'));

const { result } = renderHook(() => useRecordControls());
const response = await result.current.getRecordAndInitializeParsing({
recordId: 'test-id',
});

expect(response).toBeUndefined();
expect(UserNotificationFactory.createMessage).toHaveBeenCalledWith(StatusType.error, 'ld.errorFetching');
});

it('handles error with custom error message', async () => {
(recordsApi.getRecord as jest.Mock).mockRejectedValue(new Error('API Error'));

const { result } = renderHook(() => useRecordControls());
await result.current.getRecordAndInitializeParsing({
recordId: 'test-id',
errorMessage: 'custom.error',
});

expect(UserNotificationFactory.createMessage).toHaveBeenCalledWith(StatusType.error, 'custom.error');
});

it('passes preview params to getProfiles', async () => {
const previewParams = { param: 'value' } as unknown as PreviewParams;
(recordsApi.getRecord as jest.Mock).mockResolvedValue(mockRecord);
mockGetProfiles.mockResolvedValue(undefined);

const { result } = renderHook(() => useRecordControls());
await result.current.getRecordAndInitializeParsing({
recordId: 'test-id',
previewParams,
});

expect(mockGetProfiles).toHaveBeenCalledWith({
record: mockRecord,
recordId: 'test-id',
previewParams,
});
});
});
});

0 comments on commit f1d4d63

Please sign in to comment.