Skip to content
Snippets Groups Projects
Commit 2d522990 authored by Adrian Orłów's avatar Adrian Orłów
Browse files

feat: add tests for publications list modal

parent e325e4a6
No related branches found
No related tags found
2 merge requests!223reset the pin numbers before search results are fetch (so the results will be...,!179feat: Add publications list modal download csv (MIN-242)
Pipeline #88680 passed
Showing with 355 additions and 5 deletions
/* eslint-disable react/no-children-prop */
import { FIRST_ARRAY_ELEMENT, SECOND_ARRAY_ELEMENT, THIRD_ARRAY_ELEMENT } from '@/constants/common';
import { DEFAULT_ERROR } from '@/constants/errors';
import { bioEntityContentFixture } from '@/models/fixtures/bioEntityContentsFixture';
import { MODELS_MOCK } from '@/models/mocks/modelsMock';
import { PUBLICATIONS_DEFAULT_SEARCH_FIRST_10_MOCK } from '@/models/mocks/publicationsResponseMock';
import { apiPath } from '@/redux/apiPath';
import { downloadFileFromBlob } from '@/redux/export/export.utils';
import { StoreType } from '@/redux/store';
import { BioEntityContent, Publication } from '@/types/models';
import { mockNetworkResponse } from '@/utils/mockNetworkResponse';
import {
InitialStoreState,
getReduxWrapperWithStore,
} from '@/utils/testing/getReduxWrapperWithStore';
import { render, screen, waitFor } from '@testing-library/react';
import { HttpStatusCode } from 'axios';
import { fetchElementData } from '../utils/fetchElementData';
import { PublicationsModalLayout } from './PublicationsModalLayout.component';
const FIRST_MODEL_ID = MODELS_MOCK[FIRST_ARRAY_ELEMENT].idObject;
const SECOND_MODEL_ID = MODELS_MOCK[SECOND_ARRAY_ELEMENT].idObject;
const THIRD_MODEL_ID = MODELS_MOCK[THIRD_ARRAY_ELEMENT].idObject;
const FIRST_ELEMENT_ID = 100;
const SECOND_ELEMENT_ID = 200;
const THIRD_ELEMENT_ID = 300;
const FOURTH_ELEMENT_ID = 400;
const BASE_PUBLICATION: Publication =
PUBLICATIONS_DEFAULT_SEARCH_FIRST_10_MOCK.data[FIRST_ARRAY_ELEMENT];
const BASE_ELEMENT = BASE_PUBLICATION.elements[FIRST_ARRAY_ELEMENT];
const PUBLICATION: Publication = {
...BASE_PUBLICATION,
elements: [
{
...BASE_ELEMENT,
id: FIRST_ELEMENT_ID,
modelId: FIRST_MODEL_ID,
},
{
...BASE_ELEMENT,
id: SECOND_ELEMENT_ID,
modelId: SECOND_MODEL_ID,
},
{
...BASE_ELEMENT,
id: THIRD_ELEMENT_ID,
modelId: THIRD_MODEL_ID, // model id duplicate
},
{
...BASE_ELEMENT,
id: FOURTH_ELEMENT_ID,
modelId: THIRD_MODEL_ID, // model id duplicate
},
],
};
const BIO_ENTITY_CONTENT = (id: number, elementId: string): BioEntityContent => ({
...bioEntityContentFixture,
bioEntity: {
...bioEntityContentFixture.bioEntity,
id,
elementId,
},
});
jest.mock('../../../../../redux/export/export.utils');
jest.mock('../utils/fetchElementData');
(fetchElementData as jest.Mock).mockImplementation(
(id: string): BioEntityContent =>
({
[`${FIRST_ELEMENT_ID}`]: BIO_ENTITY_CONTENT(FIRST_ELEMENT_ID, 'mi100'),
[`${SECOND_ELEMENT_ID}`]: BIO_ENTITY_CONTENT(SECOND_ELEMENT_ID, 'ne200'),
[`${THIRD_ELEMENT_ID}`]: BIO_ENTITY_CONTENT(THIRD_ELEMENT_ID, 'r300'),
[`${FOURTH_ELEMENT_ID}`]: BIO_ENTITY_CONTENT(FOURTH_ELEMENT_ID, 'va400'),
})[id] as BioEntityContent,
);
const mockedAxiosClient = mockNetworkResponse();
const renderComponent = (initialStore?: InitialStoreState): { store: StoreType } => {
const { Wrapper, store } = getReduxWrapperWithStore(initialStore);
return (
render(
<Wrapper>
<PublicationsModalLayout children={null} />
</Wrapper>,
),
{
store,
}
);
};
describe('PublicationsModalLayout - component', () => {
const length = 1;
mockedAxiosClient
.onGet(apiPath.getPublications({ params: { length } }))
.reply(HttpStatusCode.Ok, {
...PUBLICATIONS_DEFAULT_SEARCH_FIRST_10_MOCK,
data: [PUBLICATION],
});
it('should render download csv button', () => {
renderComponent();
expect(screen.getByTestId('download-csv-button')).toBeInTheDocument();
});
it('should run download file with valid content when download csv clicked', async () => {
renderComponent({
publications: {
data: {
...PUBLICATIONS_DEFAULT_SEARCH_FIRST_10_MOCK,
data: [PUBLICATION],
filteredSize: length,
},
loading: 'succeeded',
error: DEFAULT_ERROR,
sortColumn: '',
sortOrder: 'asc',
searchValue: '',
selectedModelId: undefined,
},
models: { data: MODELS_MOCK, loading: 'idle', error: DEFAULT_ERROR },
});
const downloadButton = screen.getByTestId('download-csv-button');
downloadButton.click();
await waitFor(() => {
expect(downloadFileFromBlob).toHaveBeenCalledWith(
'"10049997","The glutamate receptor ion channels.","Dingledine R, Borges K, Bowie D, Traynelis SF.","Pharmacological reviews","1999","mi100,ne200,r300,va400","Core PD map,Histamine signaling,PRKN substrates"',
'publications.csv',
);
});
});
});
......@@ -12,7 +12,7 @@ import { MODAL_ROLE } from './PublicationsModalLayout.constants';
import { useDownloadPublicationsAsCSVFile } from './utils/useDownloadPublicationsAsCSVFile';
type ModalLayoutProps = {
children: React.ReactNode;
children: React.ReactNode | null;
};
export const PublicationsModalLayout = ({ children }: ModalLayoutProps): JSX.Element => {
......@@ -39,6 +39,7 @@ export const PublicationsModalLayout = ({ children }: ModalLayoutProps): JSX.Ele
<Button
onClick={downloadPublicationsAsCSVFile}
disabled={isLoading || !numberOfPublications}
data-testid="download-csv-button"
>
{isLoading && (
<Image
......
import { apiPath } from '@/redux/apiPath';
import { mockNetworkResponse } from '@/utils/mockNetworkResponse';
import { HttpStatusCode } from 'axios';
import { PUBLICATIONS_DEFAULT_SEARCH_FIRST_10_MOCK } from '../../../../../../models/mocks/publicationsResponseMock';
import { showToast } from '../../../../../../utils/showToast';
import { getBasePublications } from './getBasePublications';
const mockedAxiosClient = mockNetworkResponse();
jest.mock('./../../../../../../utils/showToast');
describe('getBasePublications - util', () => {
const length = 10;
it('should return valid data if provided', async () => {
mockedAxiosClient
.onGet(apiPath.getPublications({ params: { length } }))
.reply(HttpStatusCode.Ok, PUBLICATIONS_DEFAULT_SEARCH_FIRST_10_MOCK);
const result = await getBasePublications({ length });
expect(result).toStrictEqual(PUBLICATIONS_DEFAULT_SEARCH_FIRST_10_MOCK.data);
});
it('should return empty array if data structure is invalid', async () => {
mockedAxiosClient
.onGet(apiPath.getPublications({ params: { length } }))
.reply(HttpStatusCode.Ok, { randomObject: true });
const result = await getBasePublications({ length });
expect(result).toStrictEqual([]);
});
it('should return empty array and show toast error if http error', async () => {
mockedAxiosClient
.onGet(apiPath.getPublications({ params: { length } }))
.reply(HttpStatusCode.BadRequest);
const result = await getBasePublications({ length });
expect(result).toStrictEqual([]);
expect(showToast).toHaveBeenCalledWith({
message:
"Problem with fetching publications: The server couldn't understand your request. Please check your input and try again.",
type: 'error',
});
});
});
/* eslint-disable no-magic-numbers */
import { FIRST_ARRAY_ELEMENT } from '@/constants/common';
import { bioEntityContentFixture } from '@/models/fixtures/bioEntityContentsFixture';
import { PUBLICATIONS_DEFAULT_SEARCH_FIRST_10_MOCK } from '@/models/mocks/publicationsResponseMock';
import { BioEntityContent, Publication } from '@/types/models';
import { StandarizedPublication } from '@/types/publications';
import { fetchElementData } from '../../utils/fetchElementData';
import { mapBasePublicationToStandarized } from './mapBasePublicationToStandarized';
const FIRST_MODEL_ID = 53;
const SECOND_MODEL_ID = 63;
const THIRD_MODEL_ID = 99;
const FIRST_ELEMENT_ID = 100;
const SECOND_ELEMENT_ID = 200;
const THIRD_ELEMENT_ID = 300;
const FOURTH_ELEMENT_ID = 400;
const MODEL_NAME_ID_MAP: Record<number, string> = {
[FIRST_MODEL_ID]: 'first model',
[SECOND_MODEL_ID]: 'second model',
[THIRD_MODEL_ID]: 'third model',
};
const BASE_PUBLICATION: Publication =
PUBLICATIONS_DEFAULT_SEARCH_FIRST_10_MOCK.data[FIRST_ARRAY_ELEMENT];
const BASE_ELEMENT = BASE_PUBLICATION.elements[FIRST_ARRAY_ELEMENT];
const PUBLICATION: Publication = {
...BASE_PUBLICATION,
elements: [
{
...BASE_ELEMENT,
id: FIRST_ELEMENT_ID,
modelId: FIRST_MODEL_ID,
},
{
...BASE_ELEMENT,
id: SECOND_ELEMENT_ID,
modelId: SECOND_MODEL_ID,
},
{
...BASE_ELEMENT,
id: THIRD_ELEMENT_ID,
modelId: THIRD_MODEL_ID, // model id duplicate
},
{
...BASE_ELEMENT,
id: FOURTH_ELEMENT_ID,
modelId: THIRD_MODEL_ID, // model id duplicate
},
],
};
const BIO_ENTITY_CONTENT = (id: number, elementId: string): BioEntityContent => ({
...bioEntityContentFixture,
bioEntity: {
...bioEntityContentFixture.bioEntity,
id,
elementId,
},
});
jest.mock('../../utils/fetchElementData');
(fetchElementData as jest.Mock).mockImplementation(
(id: string): BioEntityContent =>
({
[`${FIRST_ELEMENT_ID}`]: BIO_ENTITY_CONTENT(FIRST_ELEMENT_ID, 'mi100'),
[`${SECOND_ELEMENT_ID}`]: BIO_ENTITY_CONTENT(SECOND_ELEMENT_ID, 'ne200'),
[`${THIRD_ELEMENT_ID}`]: BIO_ENTITY_CONTENT(THIRD_ELEMENT_ID, 'r300'),
[`${FOURTH_ELEMENT_ID}`]: BIO_ENTITY_CONTENT(FOURTH_ELEMENT_ID, 'va400'),
})[id] as BioEntityContent,
);
const getFuncResult = async (
publication: Publication,
modelNameIdMap: Record<number, string> = MODEL_NAME_ID_MAP,
): Promise<StandarizedPublication> =>
mapBasePublicationToStandarized(publication, { modelNameIdMap });
describe('mapBasePublicationToStandarized - util', () => {
it('should return valid pubmedId, journal, year and title', async () => {
const results = await getFuncResult(PUBLICATION);
const { pubmedId, journal, year, title } = PUBLICATION.publication.article;
expect(results.pubmedId).toBe(pubmedId);
expect(results.journal).toBe(journal);
expect(results.year).toBe(year.toString());
expect(results.title).toBe(title);
});
it('should return joined authors', async () => {
const results = await getFuncResult(PUBLICATION);
const { authors } = PUBLICATION.publication.article;
expect(results.authors).toBe(authors.join(','));
});
it('should return joined and unique model names if present', async () => {
const results = await getFuncResult(PUBLICATION);
expect(results.modelNames).toBe(['first model', 'second model', 'third model'].join(','));
});
it('should return empty model names string if model not existing', async () => {
const EMPTY_MODEL_ID_MAP = {};
const results = await getFuncResult(PUBLICATION, EMPTY_MODEL_ID_MAP);
expect(results.modelNames).toBe('');
});
it('should return joined elementsIds', async () => {
const results = await getFuncResult(PUBLICATION);
expect(results.elementsIds).toBe(['mi100', 'ne200', 'r300', 'va400'].join(','));
});
});
......@@ -7,6 +7,8 @@ interface Options {
modelNameIdMap: Record<number, string>;
}
const JOIN_SEPARATOR = ',';
export const mapBasePublicationToStandarized = async (
publication: Publication,
options: Options,
......@@ -28,8 +30,8 @@ export const mapBasePublicationToStandarized = async (
journal,
title,
year: year ? `${year}` : '',
authors: authors.join(','),
modelNames: getUniqueArray(modelNames).join(','),
elementsIds: elementsIds.join(','),
authors: authors.join(JOIN_SEPARATOR),
modelNames: getUniqueArray(modelNames).join(JOIN_SEPARATOR),
elementsIds: elementsIds.join(JOIN_SEPARATOR),
};
};
import { StandarizedPublication } from '@/types/publications';
import { mapStandarizedPublicationsToCSVString } from './mapStandarizedPublicationsToCSVString';
const CASES: [StandarizedPublication[], string][] = [
[[], ''],
[
[
{
pubmedId: '',
year: '',
journal: '',
authors: '',
title: '',
modelNames: '',
elementsIds: '',
},
],
'"","","","","","",""',
],
[
[
{
authors: 'authors',
title: 'title',
journal: 'journal',
pubmedId: 'pubmedId',
modelNames: 'modelNames',
elementsIds: 'elementsIds',
year: 'year',
},
],
'"pubmedId","title","authors","journal","year","elementsIds","modelNames"',
],
];
describe('mapStandarizedPublicationsToCSVString - util', () => {
it.each(CASES)('should return valid string', (input, result) => {
expect(mapStandarizedPublicationsToCSVString(input)).toBe(result);
});
});
export const PUBLICATIONS_DEFAULT_SEARCH_FIRST_10_MOCK = {
import { PublicationsResponse } from '@/types/models';
export const PUBLICATIONS_DEFAULT_SEARCH_FIRST_10_MOCK: PublicationsResponse = {
data: [
{
elements: [
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment