diff --git a/src/components/FunctionalArea/Modal/PublicationsModal/PublicationsModalLayout/utils/getBasePublications.test.ts b/src/components/FunctionalArea/Modal/PublicationsModal/PublicationsModalLayout/utils/getBasePublications.test.ts index eb9a4b10c26bcb879b2e9066173d1e728f355e1c..e29e23ec27a5d0661f79468b66909900ec51644b 100644 --- a/src/components/FunctionalArea/Modal/PublicationsModal/PublicationsModalLayout/utils/getBasePublications.test.ts +++ b/src/components/FunctionalArea/Modal/PublicationsModal/PublicationsModalLayout/utils/getBasePublications.test.ts @@ -1,13 +1,13 @@ import { apiPath } from '@/redux/apiPath'; import { mockNetworkResponse } from '@/utils/mockNetworkResponse'; import { HttpStatusCode } from 'axios'; +import { handleError } from '@/utils/error-report/errorReporting'; 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'); +jest.mock('./../../../../../../utils/error-report/errorReporting'); describe('getBasePublications - util', () => { const length = 10; @@ -32,7 +32,7 @@ describe('getBasePublications - util', () => { expect(result).toStrictEqual([]); }); - it('should return empty array and show toast error if http error', async () => { + it('should return empty array and handle error if http error', async () => { mockedAxiosClient .onGet(apiPath.getPublications({ params: { length } })) .reply(HttpStatusCode.BadRequest); @@ -40,10 +40,6 @@ describe('getBasePublications - util', () => { 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', - }); + expect(handleError).toHaveBeenCalled(); }); }); diff --git a/src/components/FunctionalArea/Modal/PublicationsModal/PublicationsModalLayout/utils/getBasePublications.ts b/src/components/FunctionalArea/Modal/PublicationsModal/PublicationsModalLayout/utils/getBasePublications.ts index 7725df112b3f7d8f4d5464a52ba0caee6e29557c..8b3039aa361a96598b4eef6c78cae9c678d9f553 100644 --- a/src/components/FunctionalArea/Modal/PublicationsModal/PublicationsModalLayout/utils/getBasePublications.ts +++ b/src/components/FunctionalArea/Modal/PublicationsModal/PublicationsModalLayout/utils/getBasePublications.ts @@ -3,9 +3,9 @@ import { apiPath } from '@/redux/apiPath'; import { PUBLICATIONS_FETCHING_ERROR_PREFIX } from '@/redux/publications/publications.constatns'; import { axiosInstance } from '@/services/api/utils/axiosInstance'; import { Publication, PublicationsResponse } from '@/types/models'; -import { getErrorMessage } from '@/utils/getErrorMessage'; -import { showToast } from '@/utils/showToast'; import { validateDataUsingZodSchema } from '@/utils/validateDataUsingZodSchema'; +import { getError } from '@/utils/error-report/getError'; +import { handleError } from '@/utils/error-report/errorReporting'; interface Args { length: number; @@ -21,12 +21,7 @@ export const getBasePublications = async ({ length }: Args): Promise<Publication return isDataValid ? response.data.data : []; } catch (error) { - const errorMessage = getErrorMessage({ error, prefix: PUBLICATIONS_FETCHING_ERROR_PREFIX }); - showToast({ - type: 'error', - message: errorMessage, - }); - + handleError(getError({ error, prefix: PUBLICATIONS_FETCHING_ERROR_PREFIX })); return []; } }; diff --git a/src/components/FunctionalArea/Modal/PublicationsModal/utils/fetchElementData.ts b/src/components/FunctionalArea/Modal/PublicationsModal/utils/fetchElementData.ts index ceaf1b8a355e43d7c29c4c54eb8635c1f1f3233c..3fea9bf72dc028d68cf7901c05eb8218201328b5 100644 --- a/src/components/FunctionalArea/Modal/PublicationsModal/utils/fetchElementData.ts +++ b/src/components/FunctionalArea/Modal/PublicationsModal/utils/fetchElementData.ts @@ -4,9 +4,9 @@ import { apiPath } from '@/redux/apiPath'; import { BIO_ENTITY_FETCHING_ERROR_PREFIX } from '@/redux/bioEntity/bioEntity.constants'; import { axiosInstanceNewAPI } from '@/services/api/utils/axiosInstance'; import { BioEntityContent, BioEntityResponse } from '@/types/models'; -import { getErrorMessage } from '@/utils/getErrorMessage'; -import { showToast } from '@/utils/showToast'; import { validateDataUsingZodSchema } from '@/utils/validateDataUsingZodSchema'; +import { getError } from '@/utils/error-report/getError'; +import { handleError } from '@/utils/error-report/errorReporting'; export const fetchElementData = async ( searchQuery: string, @@ -25,15 +25,7 @@ export const fetchElementData = async ( return response.data.content[FIRST_ARRAY_ELEMENT]; } } catch (error) { - const errorMessage = getErrorMessage({ - error, - prefix: BIO_ENTITY_FETCHING_ERROR_PREFIX, - }); - - showToast({ - type: 'error', - message: errorMessage, - }); + handleError(getError({ error, prefix: BIO_ENTITY_FETCHING_ERROR_PREFIX })); } return undefined; diff --git a/src/components/Map/Drawer/AvailablePluginsDrawer/LoadPluginFromUrl/LoadPluginFromUrl.component.test.tsx b/src/components/Map/Drawer/AvailablePluginsDrawer/LoadPluginFromUrl/LoadPluginFromUrl.component.test.tsx index 208136f0a71e569aad40d317399ece1a53c2b4f8..c5c47d94eeadc53f6d587d238ac66ce5cce77aee 100644 --- a/src/components/Map/Drawer/AvailablePluginsDrawer/LoadPluginFromUrl/LoadPluginFromUrl.component.test.tsx +++ b/src/components/Map/Drawer/AvailablePluginsDrawer/LoadPluginFromUrl/LoadPluginFromUrl.component.test.tsx @@ -9,13 +9,13 @@ import { fireEvent, render, screen, waitFor } from '@testing-library/react'; import axios, { HttpStatusCode } from 'axios'; import MockAdapter from 'axios-mock-adapter'; import { act } from 'react-dom/test-utils'; -import { showToast } from '@/utils/showToast'; +import { handleError } from '@/utils/error-report/errorReporting'; import { LoadPluginFromUrl } from './LoadPluginFromUrl.component'; const mockedAxiosApiClient = mockNetworkResponse(); const mockedAxiosClient = new MockAdapter(axios); -jest.mock('../../../../../utils/showToast'); +jest.mock('../../../../../utils/error-report/errorReporting'); const renderComponent = (initialStore?: InitialStoreState): { store: StoreType } => { const { Wrapper, store } = getReduxWrapperWithStore(initialStore); @@ -116,7 +116,7 @@ describe('LoadPluginFromUrl - component', () => { const button = screen.getByTestId('load-plugin-button'); expect(button).toBeDisabled(); }); - it('should show toast if plugin failed to load', async () => { + it('should handle error if plugin failed to load', async () => { const pluginUrl = 'http://example.com/plugin.js'; mockedAxiosClient.onGet(pluginUrl).reply(HttpStatusCode.Unauthorized, null); @@ -137,12 +137,7 @@ describe('LoadPluginFromUrl - component', () => { }); await waitFor(() => { - expect(showToast).toHaveBeenCalled(); - expect(showToast).toHaveBeenCalledWith({ - message: - "Failed to load plugin: You're not authorized to access this resource. Please log in or check your credentials.", - type: 'error', - }); + expect(handleError).toHaveBeenCalled(); }); }); diff --git a/src/components/Map/Drawer/AvailablePluginsDrawer/LoadPluginFromUrl/hooks/useLoadPluginFromUrl.ts b/src/components/Map/Drawer/AvailablePluginsDrawer/LoadPluginFromUrl/hooks/useLoadPluginFromUrl.ts index 54276c16149fd733bf0dddae65a8db45f21624a6..5753d72398c916d8aa90c042af4d4e2a512f5bea 100644 --- a/src/components/Map/Drawer/AvailablePluginsDrawer/LoadPluginFromUrl/hooks/useLoadPluginFromUrl.ts +++ b/src/components/Map/Drawer/AvailablePluginsDrawer/LoadPluginFromUrl/hooks/useLoadPluginFromUrl.ts @@ -1,9 +1,9 @@ import { PluginsManager } from '@/services/pluginsManager'; -import { showToast } from '@/utils/showToast'; import axios from 'axios'; import { ChangeEvent, useMemo, useState, KeyboardEvent } from 'react'; -import { getErrorMessage } from '@/utils/getErrorMessage'; import { ENTER_KEY_CODE } from '@/constants/common'; +import { getError } from '@/utils/error-report/getError'; +import { handleError } from '@/utils/error-report/errorReporting'; import { PLUGIN_LOADING_ERROR_PREFIX } from '../../AvailablePluginsDrawer.constants'; type UseLoadPluginReturnType = { @@ -43,11 +43,7 @@ export const useLoadPluginFromUrl = (): UseLoadPluginReturnType => { setPluginUrl(''); } catch (error) { - const errorMessage = getErrorMessage({ error, prefix: PLUGIN_LOADING_ERROR_PREFIX }); - showToast({ - type: 'error', - message: errorMessage, - }); + handleError(getError({ error, prefix: PLUGIN_LOADING_ERROR_PREFIX })); } finally { setIsLoading(false); } diff --git a/src/redux/backgrounds/backgrounds.reducers.test.ts b/src/redux/backgrounds/backgrounds.reducers.test.ts index 4f70938fa505c0da1813ec8df6cf16e2f9fe4ad8..dd13a7711f9d39428ffa0ea0d3b22f90a448a962 100644 --- a/src/redux/backgrounds/backgrounds.reducers.test.ts +++ b/src/redux/backgrounds/backgrounds.reducers.test.ts @@ -6,6 +6,7 @@ import { } from '@/utils/createStoreInstanceUsingSliceReducer'; import { mockNetworkResponse } from '@/utils/mockNetworkResponse'; import { HttpStatusCode } from 'axios'; +import { unwrapResult } from '@reduxjs/toolkit'; import { apiPath } from '../apiPath'; import backgroundsReducer from './backgrounds.slice'; import { getAllBackgroundsByProjectId } from './backgrounds.thunks'; @@ -51,14 +52,14 @@ describe('backgrounds reducer', () => { .onGet(apiPath.getAllBackgroundsByProjectIdQuery(PROJECT_ID)) .reply(HttpStatusCode.NotFound, []); - const { type, payload } = await store.dispatch(getAllBackgroundsByProjectId(PROJECT_ID)); + const action = await store.dispatch(getAllBackgroundsByProjectId(PROJECT_ID)); const { data, loading, error } = store.getState().backgrounds; - expect(type).toBe('backgrounds/getAllBackgroundsByProjectId/rejected'); + expect(action.type).toBe('backgrounds/getAllBackgroundsByProjectId/rejected'); expect(loading).toEqual('failed'); expect(error).toEqual({ message: '', name: '' }); expect(data).toEqual([]); - expect(payload).toBe( + expect(() => unwrapResult(action)).toThrow( "Failed to fetch backgrounds: The page you're looking for doesn't exist. Please verify the URL and try again.", ); }); diff --git a/src/redux/backgrounds/backgrounds.thunks.ts b/src/redux/backgrounds/backgrounds.thunks.ts index 18a0c56bcfed6861ffabd3fb944378930dad13c4..d2ce1949ef25fd4f9768f23e63e556925be48b2b 100644 --- a/src/redux/backgrounds/backgrounds.thunks.ts +++ b/src/redux/backgrounds/backgrounds.thunks.ts @@ -4,14 +4,14 @@ import { MapBackground } from '@/types/models'; import { validateDataUsingZodSchema } from '@/utils/validateDataUsingZodSchema'; import { createAsyncThunk } from '@reduxjs/toolkit'; import { z } from 'zod'; -import { getErrorMessage } from '@/utils/getErrorMessage'; import { ThunkConfig } from '@/types/store'; +import { getError } from '@/utils/error-report/getError'; import { apiPath } from '../apiPath'; import { BACKGROUNDS_FETCHING_ERROR_PREFIX } from './backgrounds.constants'; export const getAllBackgroundsByProjectId = createAsyncThunk<MapBackground[], string, ThunkConfig>( 'backgrounds/getAllBackgroundsByProjectId', - async (projectId: string, { rejectWithValue }) => { + async (projectId: string) => { try { const response = await axiosInstance.get<MapBackground[]>( apiPath.getAllBackgroundsByProjectIdQuery(projectId), @@ -21,9 +21,7 @@ export const getAllBackgroundsByProjectId = createAsyncThunk<MapBackground[], st return isDataValid ? response.data : []; } catch (error) { - const errorMessage = getErrorMessage({ error, prefix: BACKGROUNDS_FETCHING_ERROR_PREFIX }); - - return rejectWithValue(errorMessage); + return Promise.reject(getError({ error, prefix: BACKGROUNDS_FETCHING_ERROR_PREFIX })); } }, ); diff --git a/src/redux/bioEntity/bioEntity.reducers.test.ts b/src/redux/bioEntity/bioEntity.reducers.test.ts index d48998d83951e3289f1da610ff3f62915a5c3281..07af4ebe2cc8d18f49b353fe759039125617c1cf 100644 --- a/src/redux/bioEntity/bioEntity.reducers.test.ts +++ b/src/redux/bioEntity/bioEntity.reducers.test.ts @@ -7,6 +7,7 @@ import { } from '@/utils/createStoreInstanceUsingSliceReducer'; import { mockNetworkNewAPIResponse } from '@/utils/mockNetworkResponse'; import { HttpStatusCode } from 'axios'; +import { unwrapResult } from '@reduxjs/toolkit'; import bioEntityContentsReducer from './bioEntity.slice'; import { getBioEntity } from './bioEntity.thunks'; import { BioEntityContentsState } from './bioEntity.types'; @@ -14,6 +15,8 @@ import { BioEntityContentsState } from './bioEntity.types'; const mockedAxiosClient = mockNetworkNewAPIResponse(); const SEARCH_QUERY = 'park7'; +jest.mock('../../utils/error-report/errorReporting'); + const INITIAL_STATE: BioEntityContentsState = { data: [], loading: 'idle', @@ -77,7 +80,7 @@ describe('bioEntity reducer', () => { ) .reply(HttpStatusCode.NotFound, bioEntityResponseFixture); - const { type, payload } = await store.dispatch( + const action = await store.dispatch( getBioEntity({ searchQuery: SEARCH_QUERY, isPerfectMatch: false, @@ -85,14 +88,14 @@ describe('bioEntity reducer', () => { ); const { data } = store.getState().bioEntity; - const bioEnityWithSearchElement = data.find( + const bioEntityWithSearchElement = data.find( bioEntity => bioEntity.searchQueryElement === SEARCH_QUERY, ); - expect(type).toBe('project/getBioEntityContents/rejected'); - expect(payload).toBe( + expect(action.type).toBe('project/getBioEntityContents/rejected'); + expect(() => unwrapResult(action)).toThrow( "Failed to fetch bio entity: The page you're looking for doesn't exist. Please verify the URL and try again.", ); - expect(bioEnityWithSearchElement).toEqual({ + expect(bioEntityWithSearchElement).toEqual({ searchQueryElement: SEARCH_QUERY, data: undefined, loading: 'failed', @@ -118,11 +121,11 @@ describe('bioEntity reducer', () => { ); const { data } = store.getState().bioEntity; - const bioEnityWithSearchElement = data.find( + const bioEntityWithSearchElement = data.find( bioEntity => bioEntity.searchQueryElement === SEARCH_QUERY, ); - expect(bioEnityWithSearchElement).toEqual({ + expect(bioEntityWithSearchElement).toEqual({ searchQueryElement: SEARCH_QUERY, data: undefined, loading: 'pending', @@ -132,11 +135,11 @@ describe('bioEntity reducer', () => { bioEntityContentsPromise.then(() => { const { data: dataPromiseFulfilled } = store.getState().bioEntity; - const bioEnityWithSearchElementFulfilled = dataPromiseFulfilled.find( + const bioEntityWithSearchElementFulfilled = dataPromiseFulfilled.find( bioEntity => bioEntity.searchQueryElement === SEARCH_QUERY, ); - expect(bioEnityWithSearchElementFulfilled).toEqual({ + expect(bioEntityWithSearchElementFulfilled).toEqual({ searchQueryElement: SEARCH_QUERY, data: bioEntityResponseFixture.content, loading: 'succeeded', diff --git a/src/redux/bioEntity/bioEntity.thunks.test.ts b/src/redux/bioEntity/bioEntity.thunks.test.ts index f2b54f3abdd5a5e258c51bfc8d224a3877d02fdf..fb433fb0fb0b18033b883c6fa8213432f5f3260b 100644 --- a/src/redux/bioEntity/bioEntity.thunks.test.ts +++ b/src/redux/bioEntity/bioEntity.thunks.test.ts @@ -6,10 +6,13 @@ import { } from '@/utils/createStoreInstanceUsingSliceReducer'; import { mockNetworkNewAPIResponse } from '@/utils/mockNetworkResponse'; import { HttpStatusCode } from 'axios'; +import { unwrapResult } from '@reduxjs/toolkit'; import contentsReducer from './bioEntity.slice'; import { getBioEntity, getMultiBioEntity } from './bioEntity.thunks'; import { BioEntityContentsState } from './bioEntity.types'; +jest.mock('../../utils/error-report/errorReporting'); + const mockedAxiosClient = mockNetworkNewAPIResponse(); const SEARCH_QUERY = 'park7'; @@ -65,13 +68,13 @@ describe('bioEntityContents thunks', () => { ) .reply(HttpStatusCode.NotFound, null); - const { payload } = await store.dispatch( + const action = await store.dispatch( getBioEntity({ searchQuery: SEARCH_QUERY, isPerfectMatch: false, }), ); - expect(payload).toEqual( + expect(() => unwrapResult(action)).toThrow( "Failed to fetch bio entity: The page you're looking for doesn't exist. Please verify the URL and try again.", ); }); diff --git a/src/redux/bioEntity/thunks/getBioEntity.ts b/src/redux/bioEntity/thunks/getBioEntity.ts index f8a302ee49a3d97ea240da6bbad3f282b0bc0355..bf54870a8db007d3785bb07606516fa293044a6f 100644 --- a/src/redux/bioEntity/thunks/getBioEntity.ts +++ b/src/redux/bioEntity/thunks/getBioEntity.ts @@ -4,9 +4,9 @@ import { axiosInstanceNewAPI } from '@/services/api/utils/axiosInstance'; import { BioEntityContent, BioEntityResponse } from '@/types/models'; import { PerfectSearchParams } from '@/types/search'; import { ThunkConfig } from '@/types/store'; -import { getErrorMessage } from '@/utils/getErrorMessage'; import { validateDataUsingZodSchema } from '@/utils/validateDataUsingZodSchema'; import { createAsyncThunk } from '@reduxjs/toolkit'; +import { getError } from '@/utils/error-report/getError'; import { addNumbersToEntityNumberData } from '../../entityNumber/entityNumber.slice'; import { BIO_ENTITY_FETCHING_ERROR_PREFIX } from '../bioEntity.constants'; @@ -18,10 +18,7 @@ export const getBioEntity = createAsyncThunk< ThunkConfig >( 'project/getBioEntityContents', - async ( - { searchQuery, isPerfectMatch, addNumbersToEntityNumber = true }, - { rejectWithValue, dispatch }, - ) => { + async ({ searchQuery, isPerfectMatch, addNumbersToEntityNumber = true }, { dispatch }) => { try { const response = await axiosInstanceNewAPI.get<BioEntityResponse>( apiPath.getBioEntityContentsStringWithQuery({ searchQuery, isPerfectMatch }), @@ -39,11 +36,7 @@ export const getBioEntity = createAsyncThunk< return isDataValidBioEnity ? response.data.content : undefined; } catch (error) { - const errorMessage = getErrorMessage({ - error, - prefix: BIO_ENTITY_FETCHING_ERROR_PREFIX, - }); - return rejectWithValue(errorMessage); + return Promise.reject(getError({ error, prefix: BIO_ENTITY_FETCHING_ERROR_PREFIX })); } }, ); diff --git a/src/redux/bioEntity/thunks/getSubmapConnectionsBioEntity.ts b/src/redux/bioEntity/thunks/getSubmapConnectionsBioEntity.ts index bfda62b8ed94d82ddb73c8991053aac611b5f725..e50189938bbe9b45e5fa38601d799c327785df2f 100644 --- a/src/redux/bioEntity/thunks/getSubmapConnectionsBioEntity.ts +++ b/src/redux/bioEntity/thunks/getSubmapConnectionsBioEntity.ts @@ -3,16 +3,15 @@ import { submapConnection } from '@/models/submapConnection'; import { apiPath } from '@/redux/apiPath'; import { axiosInstance, axiosInstanceNewAPI } from '@/services/api/utils/axiosInstance'; import { BioEntityContent, BioEntityResponse, SubmapConnection } from '@/types/models'; -import { getErrorMessage } from '@/utils/getErrorMessage'; import { validateDataUsingZodSchema } from '@/utils/validateDataUsingZodSchema'; import { createAsyncThunk } from '@reduxjs/toolkit'; import { z } from 'zod'; +import { getError } from '@/utils/error-report/getError'; import { MULTI_BIO_ENTITY_FETCHING_ERROR_PREFIX } from '../bioEntity.constants'; export const getSubmapConnectionsBioEntity = createAsyncThunk<BioEntityContent[]>( 'project/getSubmapConnectionsBioEntity', - // eslint-disable-next-line consistent-return - async (_, { rejectWithValue }) => { + async () => { try { const response = await axiosInstance.get<SubmapConnection[]>(apiPath.getSubmapConnections()); @@ -39,12 +38,7 @@ export const getSubmapConnectionsBioEntity = createAsyncThunk<BioEntityContent[] return bioEntityContents; } catch (error) { - const errorMessage = getErrorMessage({ - error, - prefix: MULTI_BIO_ENTITY_FETCHING_ERROR_PREFIX, - }); - - return rejectWithValue(errorMessage); + return Promise.reject(getError({ error, prefix: MULTI_BIO_ENTITY_FETCHING_ERROR_PREFIX })); } }, ); diff --git a/src/redux/chemicals/chemicals.reducers.test.ts b/src/redux/chemicals/chemicals.reducers.test.ts index 9b66afe34421293ef0e89ec2b02b45767c5c151c..a877f38ea214c68ba8609f4645d53c417c99c5c6 100644 --- a/src/redux/chemicals/chemicals.reducers.test.ts +++ b/src/redux/chemicals/chemicals.reducers.test.ts @@ -7,6 +7,7 @@ import { } from '@/utils/createStoreInstanceUsingSliceReducer'; import { mockNetworkNewAPIResponse } from '@/utils/mockNetworkResponse'; import { HttpStatusCode } from 'axios'; +import { unwrapResult } from '@reduxjs/toolkit'; import chemicalsReducer from './chemicals.slice'; import { getChemicals } from './chemicals.thunks'; import { ChemicalsState } from './chemicals.types'; @@ -57,15 +58,15 @@ describe('chemicals reducer', () => { .onGet(apiPath.getChemicalsStringWithQuery(SEARCH_QUERY)) .reply(HttpStatusCode.NotFound, chemicalsFixture); - const { type, payload } = await store.dispatch(getChemicals(SEARCH_QUERY)); + const action = await store.dispatch(getChemicals(SEARCH_QUERY)); const { data } = store.getState().chemicals; const chemicalsWithSearchElement = data.find( bioEntity => bioEntity.searchQueryElement === SEARCH_QUERY, ); - expect(type).toBe('project/getChemicals/rejected'); - expect(payload).toBe( + expect(action.type).toBe('project/getChemicals/rejected'); + expect(() => unwrapResult(action)).toThrow( "Failed to fetch chemicals: The page you're looking for doesn't exist. Please verify the URL and try again.", ); expect(chemicalsWithSearchElement).toEqual({ diff --git a/src/redux/chemicals/chemicals.thunks.test.ts b/src/redux/chemicals/chemicals.thunks.test.ts index 73cb2d0ef46659c702a4cda23ea6eda640bb871e..7a93ec084524cd7a3dd6ea61675088377a08b789 100644 --- a/src/redux/chemicals/chemicals.thunks.test.ts +++ b/src/redux/chemicals/chemicals.thunks.test.ts @@ -6,6 +6,7 @@ import { } from '@/utils/createStoreInstanceUsingSliceReducer'; import { mockNetworkNewAPIResponse } from '@/utils/mockNetworkResponse'; import { HttpStatusCode } from 'axios'; +import { unwrapResult } from '@reduxjs/toolkit'; import chemicalsReducer from './chemicals.slice'; import { getChemicals } from './chemicals.thunks'; import { ChemicalsState } from './chemicals.types'; @@ -13,12 +14,14 @@ import { ChemicalsState } from './chemicals.types'; const mockedAxiosClient = mockNetworkNewAPIResponse(); const SEARCH_QUERY = 'Corticosterone'; +jest.mock('../../utils/error-report/errorReporting'); + describe('chemicals thunks', () => { let store = {} as ToolkitStoreWithSingleSlice<ChemicalsState>; beforeEach(() => { store = createStoreInstanceUsingSliceReducer('chemicals', chemicalsReducer); }); - describe('getChemiclas', () => { + describe('getChemicals', () => { it('should return data when data response from API is valid', async () => { mockedAxiosClient .onGet(apiPath.getChemicalsStringWithQuery(SEARCH_QUERY)) @@ -35,13 +38,13 @@ describe('chemicals thunks', () => { const { payload } = await store.dispatch(getChemicals(SEARCH_QUERY)); expect(payload).toEqual(undefined); }); - it('should handle error message when getChemiclas failed ', async () => { + it('should handle error message when getChemicals failed ', async () => { mockedAxiosClient .onGet(apiPath.getChemicalsStringWithQuery(SEARCH_QUERY)) .reply(HttpStatusCode.Forbidden, { randomProperty: 'randomValue' }); - const { payload } = await store.dispatch(getChemicals(SEARCH_QUERY)); - expect(payload).toEqual( + const action = await store.dispatch(getChemicals(SEARCH_QUERY)); + expect(() => unwrapResult(action)).toThrow( "Failed to fetch chemicals: Access Forbidden! You don't have permission to access this resource.", ); }); diff --git a/src/redux/chemicals/chemicals.thunks.ts b/src/redux/chemicals/chemicals.thunks.ts index 8a2f9878eb66b14c781d3ad69e484cf83efe6c6d..8553d6a4f3d06bbc8fbf50073c4c18ee53e70b63 100644 --- a/src/redux/chemicals/chemicals.thunks.ts +++ b/src/redux/chemicals/chemicals.thunks.ts @@ -3,10 +3,10 @@ import { apiPath } from '@/redux/apiPath'; import { axiosInstanceNewAPI } from '@/services/api/utils/axiosInstance'; import { Chemical } from '@/types/models'; import { ThunkConfig } from '@/types/store'; -import { getErrorMessage } from '@/utils/getErrorMessage'; import { validateDataUsingZodSchema } from '@/utils/validateDataUsingZodSchema'; import { createAsyncThunk } from '@reduxjs/toolkit'; import { z } from 'zod'; +import { getError } from '@/utils/error-report/getError'; import { addNumbersToEntityNumberData } from '../entityNumber/entityNumber.slice'; import { CHEMICALS_FETCHING_ERROR_PREFIX, @@ -15,7 +15,7 @@ import { export const getChemicals = createAsyncThunk<Chemical[] | undefined, string, ThunkConfig>( 'project/getChemicals', - async (searchQuery, { rejectWithValue }) => { + async searchQuery => { try { const response = await axiosInstanceNewAPI.get<Chemical[]>( apiPath.getChemicalsStringWithQuery(searchQuery), @@ -25,8 +25,7 @@ export const getChemicals = createAsyncThunk<Chemical[] | undefined, string, Thu return isDataValid ? response.data : undefined; } catch (error) { - const errorMessage = getErrorMessage({ error, prefix: CHEMICALS_FETCHING_ERROR_PREFIX }); - return rejectWithValue(errorMessage); + return Promise.reject(getError({ error, prefix: CHEMICALS_FETCHING_ERROR_PREFIX })); } }, ); @@ -34,7 +33,7 @@ export const getChemicals = createAsyncThunk<Chemical[] | undefined, string, Thu export const getMultiChemicals = createAsyncThunk<void, string[], ThunkConfig>( 'project/getMultChemicals', // eslint-disable-next-line consistent-return - async (searchQueries, { dispatch, rejectWithValue }) => { + async (searchQueries, { dispatch }) => { try { const asyncGetChemicalsFunctions = searchQueries.map(searchQuery => dispatch(getChemicals(searchQuery)), @@ -55,12 +54,7 @@ export const getMultiChemicals = createAsyncThunk<void, string[], ThunkConfig>( const chemicalsIds = chemicalsTargetsData.map(d => d.elementId); dispatch(addNumbersToEntityNumberData(chemicalsIds)); } catch (error) { - const errorMessage = getErrorMessage({ - error, - prefix: MULTI_CHEMICALS_FETCHING_ERROR_PREFIX, - }); - - return rejectWithValue(errorMessage); + return Promise.reject(getError({ error, prefix: MULTI_CHEMICALS_FETCHING_ERROR_PREFIX })); } }, ); diff --git a/src/redux/compartmentPathways/compartmentPathways.reducers.test.ts b/src/redux/compartmentPathways/compartmentPathways.reducers.test.ts index 94d445b845aa63f1c45fef92ea66a5a75a05fd00..1df36392927a3d24ea6fd7b64d7e11043c11a205 100644 --- a/src/redux/compartmentPathways/compartmentPathways.reducers.test.ts +++ b/src/redux/compartmentPathways/compartmentPathways.reducers.test.ts @@ -11,6 +11,7 @@ import { compartmentPathwaysOverLimitFixture, } from '@/models/fixtures/compartmentPathways'; import { getModelsIds } from '@/components/Map/Drawer/ExportDrawer/ExportDrawer.component.utils'; +import { unwrapResult } from '@reduxjs/toolkit'; import { apiPath } from '../apiPath'; import compartmentPathwaysReducer from './compartmentPathways.slice'; import { CompartmentPathwaysState } from './compartmentPathways.types'; @@ -114,7 +115,7 @@ describe('compartmentPathways reducer', () => { const dispatchData = await compartmentPathwaysPromise; - expect(dispatchData.payload).toBe( + expect(() => unwrapResult(dispatchData)).toThrow( "Failed to fetch compartment pathways: The page you're looking for doesn't exist. Please verify the URL and try again.", ); const { loading: promiseFulfilled, data: dataFulfilled } = store.getState().compartmentPathways; diff --git a/src/redux/compartmentPathways/compartmentPathways.thunks.ts b/src/redux/compartmentPathways/compartmentPathways.thunks.ts index b8143dbe2627fe7239a9eadf8546497e70b5bbfe..8f1bd216b8ecbff5f0cd4e608a7ee8737b9e9ad8 100644 --- a/src/redux/compartmentPathways/compartmentPathways.thunks.ts +++ b/src/redux/compartmentPathways/compartmentPathways.thunks.ts @@ -8,7 +8,7 @@ import { compartmentPathwaySchema, } from '@/models/compartmentPathwaySchema'; import { z } from 'zod'; -import { getErrorMessage } from '@/utils/getErrorMessage'; +import { getError } from '@/utils/error-report/getError'; import { COMPARMENT_PATHWAYS_FETCHING_ERROR_PREFIX, MAX_NUMBER_OF_IDS_IN_GET_QUERY, @@ -116,18 +116,14 @@ export const fetchCompartmentPathways = async ( export const getCompartmentPathways = createAsyncThunk( 'compartmentPathways/getCompartmentPathways', - async (modelsIds: number[] | undefined, { rejectWithValue }) => { + async (modelsIds: number[] | undefined) => { try { const compartmentIds = await fetchCompartmentPathwaysIds(modelsIds); const comparmentPathways = await fetchCompartmentPathways(compartmentIds); return comparmentPathways; } catch (error) { - const errorMessage = getErrorMessage({ - error, - prefix: COMPARMENT_PATHWAYS_FETCHING_ERROR_PREFIX, - }); - return rejectWithValue(errorMessage); + return Promise.reject(getError({ error, prefix: COMPARMENT_PATHWAYS_FETCHING_ERROR_PREFIX })); } }, ); diff --git a/src/redux/configuration/configuration.thunks.ts b/src/redux/configuration/configuration.thunks.ts index 8b4db5ea19083cc0f044966eb567cc12610aa66f..6b03cfa68a9fa4484528c02bc276a7f72d866f34 100644 --- a/src/redux/configuration/configuration.thunks.ts +++ b/src/redux/configuration/configuration.thunks.ts @@ -5,8 +5,8 @@ import { axiosInstance } from '@/services/api/utils/axiosInstance'; import { validateDataUsingZodSchema } from '@/utils/validateDataUsingZodSchema'; import { configurationSchema } from '@/models/configurationSchema'; import { configurationOptionSchema } from '@/models/configurationOptionSchema'; -import { getErrorMessage } from '@/utils/getErrorMessage'; import { ThunkConfig } from '@/types/store'; +import { getError } from '@/utils/error-report/getError'; import { apiPath } from '../apiPath'; import { CONFIGURATION_FETCHING_ERROR_PREFIX, @@ -17,7 +17,7 @@ export const getConfigurationOptions = createAsyncThunk< ConfigurationOption[] | undefined, void, ThunkConfig ->('configuration/getConfigurationOptions', async (_, { rejectWithValue }) => { +>('configuration/getConfigurationOptions', async () => { try { const response = await axiosInstance.get<ConfigurationOption[]>( apiPath.getConfigurationOptions(), @@ -30,17 +30,13 @@ export const getConfigurationOptions = createAsyncThunk< return isDataValid ? response.data : undefined; } catch (error) { - const errorMessage = getErrorMessage({ - error, - prefix: CONFIGURATION_OPTIONS_FETCHING_ERROR_PREFIX, - }); - return rejectWithValue(errorMessage); + return Promise.reject(getError({ error, prefix: CONFIGURATION_OPTIONS_FETCHING_ERROR_PREFIX })); } }); export const getConfiguration = createAsyncThunk<Configuration | undefined, void, ThunkConfig>( 'configuration/getConfiguration', - async (_, { rejectWithValue }) => { + async () => { try { const response = await axiosInstance.get<Configuration>(apiPath.getConfiguration()); @@ -48,8 +44,7 @@ export const getConfiguration = createAsyncThunk<Configuration | undefined, void return isDataValid ? response.data : undefined; } catch (error) { - const errorMessage = getErrorMessage({ error, prefix: CONFIGURATION_FETCHING_ERROR_PREFIX }); - return rejectWithValue(errorMessage); + return Promise.reject(getError({ error, prefix: CONFIGURATION_FETCHING_ERROR_PREFIX })); } }, ); diff --git a/src/redux/constant/constant.thunks.ts b/src/redux/constant/constant.thunks.ts index c939e0812f6bc36ecc195e51dd4226515b435408..9fb31a768864e749ee437b078c462e74a6390411 100644 --- a/src/redux/constant/constant.thunks.ts +++ b/src/redux/constant/constant.thunks.ts @@ -1,12 +1,12 @@ import { createAsyncThunk } from '@reduxjs/toolkit'; -import { getErrorMessage } from '@/utils/getErrorMessage'; import { ThunkConfig } from '@/types/store'; import { ConstantType } from '@/redux/constant/constant.types'; import { getConfigValue } from '@/constants/index.utils'; +import { getError } from '@/utils/error-report/getError'; export const getConstant = createAsyncThunk<ConstantType | undefined, void, ThunkConfig>( 'constant/getConstant', - async (_, { rejectWithValue }) => { + async () => { try { const apiBaseUrl = getConfigValue('BASE_API_URL'); const result: ConstantType = { @@ -15,8 +15,7 @@ export const getConstant = createAsyncThunk<ConstantType | undefined, void, Thun }; return result; } catch (error) { - const errorMessage = getErrorMessage({ error, prefix: 'Failed to build constants' }); - return rejectWithValue(errorMessage); + return Promise.reject(getError({ error, prefix: 'Failed to build constants' })); } }, ); diff --git a/src/redux/drawer/drawer.thunks.ts b/src/redux/drawer/drawer.thunks.ts index f08955f9c5aafd0d79a7b960fe584abf7b688ac1..a590ad90d775be513a15646b8f8f523cfed59aa2 100644 --- a/src/redux/drawer/drawer.thunks.ts +++ b/src/redux/drawer/drawer.thunks.ts @@ -6,7 +6,7 @@ import { Chemical, Drug, TargetSearchNameResult } from '@/types/models'; import { validateDataUsingZodSchema } from '@/utils/validateDataUsingZodSchema'; import { createAsyncThunk } from '@reduxjs/toolkit'; import { ThunkConfig } from '@/types/store'; -import { getErrorMessage } from '@/utils/getErrorMessage'; +import { getError } from '@/utils/error-report/getError'; import { apiPath } from '../apiPath'; import { CHEMICALS_FOR_BIO_ENTITY_FETCHING_ERROR_PREFIX, @@ -36,7 +36,7 @@ const getDrugsByName = async (drugName: string): Promise<Drug[]> => { export const getDrugsForBioEntityDrawerTarget = createAsyncThunk<Drug[], string, ThunkConfig>( 'drawer/getDrugsForBioEntityDrawerTarget', - async (target, { rejectWithValue }) => { + async target => { try { const drugsNames = await getDrugsNamesForTarget(target); const drugsArrays = await Promise.all( @@ -46,12 +46,9 @@ export const getDrugsForBioEntityDrawerTarget = createAsyncThunk<Drug[], string, return drugs; } catch (error) { - const errorMessage = getErrorMessage({ - error, - prefix: DRUGS_FOR_BIO_ENTITY_FETCHING_ERROR_PREFIX, - }); - - return rejectWithValue(errorMessage); + return Promise.reject( + getError({ error, prefix: DRUGS_FOR_BIO_ENTITY_FETCHING_ERROR_PREFIX }), + ); } }, ); @@ -82,7 +79,7 @@ export const getChemicalsForBioEntityDrawerTarget = createAsyncThunk< Chemical[], string, ThunkConfig ->('drawer/getChemicalsForBioEntityDrawerTarget', async (target, { rejectWithValue }) => { +>('drawer/getChemicalsForBioEntityDrawerTarget', async target => { try { const chemicalsNames = await getChemicalsNamesForTarget(target); const chemicalsArrays = await Promise.all( @@ -92,11 +89,8 @@ export const getChemicalsForBioEntityDrawerTarget = createAsyncThunk< return chemicals; } catch (error) { - const errorMessage = getErrorMessage({ - error, - prefix: CHEMICALS_FOR_BIO_ENTITY_FETCHING_ERROR_PREFIX, - }); - - return rejectWithValue(errorMessage); + return Promise.reject( + getError({ error, prefix: CHEMICALS_FOR_BIO_ENTITY_FETCHING_ERROR_PREFIX }), + ); } }); diff --git a/src/redux/map/map.thunks.ts b/src/redux/map/map.thunks.ts index 05b675bba64d223d63cab0865e8692f5f9620da4..0ea2c2ea5766dcee135c80d56ab3122a3cc6217e 100644 --- a/src/redux/map/map.thunks.ts +++ b/src/redux/map/map.thunks.ts @@ -5,8 +5,8 @@ import { QueryData } from '@/types/query'; import { DEFAULT_ZOOM } from '@/constants/map'; import { getPointMerged } from '@/utils/object/getPointMerged'; import { PluginsEventBus } from '@/services/pluginsManager/pluginsEventBus'; -import { getErrorMessage } from '@/utils/getErrorMessage'; import { ThunkConfig } from '@/types/store'; +import { getError } from '@/utils/error-report/getError'; import type { AppDispatch, RootState } from '../store'; import { InitMapBackgroundActionPayload, @@ -145,15 +145,13 @@ export const initMapSizeAndModelId = createAsyncThunk< InitMapSizeAndModelIdActionPayload, InitMapSizeAndModelIdParams, { dispatch: AppDispatch; state: RootState } & ThunkConfig ->('map/initMapSizeAndModelId', async ({ queryData }, { getState, rejectWithValue }) => { +>('map/initMapSizeAndModelId', async ({ queryData }, { getState }) => { try { const state = getState(); return getInitMapSizeAndModelId(state, queryData); } catch (error) { - const errorMessage = getErrorMessage({ error, prefix: INIT_MAP_SIZE_MODEL_ID_ERROR_PREFIX }); - - return rejectWithValue(errorMessage); + return Promise.reject(getError({ error, prefix: INIT_MAP_SIZE_MODEL_ID_ERROR_PREFIX })); } }); @@ -161,15 +159,13 @@ export const initMapPosition = createAsyncThunk< InitMapPositionActionPayload, InitMapPositionParams, { dispatch: AppDispatch; state: RootState } & ThunkConfig ->('map/initMapPosition', async ({ queryData }, { getState, rejectWithValue }) => { +>('map/initMapPosition', async ({ queryData }, { getState }) => { try { const state = getState(); return getInitMapPosition(state, queryData); } catch (error) { - const errorMessage = getErrorMessage({ error, prefix: INIT_MAP_POSITION_ERROR_PREFIX }); - - return rejectWithValue(errorMessage); + return Promise.reject(getError({ error, prefix: INIT_MAP_POSITION_ERROR_PREFIX })); } }); @@ -177,14 +173,12 @@ export const initMapBackground = createAsyncThunk< InitMapBackgroundActionPayload, InitMapBackgroundParams, { dispatch: AppDispatch; state: RootState } & ThunkConfig ->('map/initMapBackground', async ({ queryData }, { getState, rejectWithValue }) => { +>('map/initMapBackground', async ({ queryData }, { getState }) => { try { const state = getState(); return getBackgroundId(state, queryData); } catch (error) { - const errorMessage = getErrorMessage({ error, prefix: INIT_MAP_BACKGROUND_ERROR_PREFIX }); - - return rejectWithValue(errorMessage); + return Promise.reject(getError({ error, prefix: INIT_MAP_BACKGROUND_ERROR_PREFIX })); } }); @@ -192,14 +186,12 @@ export const initOpenedMaps = createAsyncThunk< InitOpenedMapsActionPayload, InitOpenedMapsProps, { dispatch: AppDispatch; state: RootState } & ThunkConfig ->('appInit/initOpenedMaps', async ({ queryData }, { getState, rejectWithValue }) => { +>('appInit/initOpenedMaps', async ({ queryData }, { getState }) => { try { const state = getState(); return getOpenedMaps(state, queryData); } catch (error) { - const errorMessage = getErrorMessage({ error, prefix: INIT_OPENED_MAPS_ERROR_PREFIX }); - - return rejectWithValue(errorMessage); + return Promise.reject(getError({ error, prefix: INIT_OPENED_MAPS_ERROR_PREFIX })); } }); diff --git a/src/redux/middlewares/error.middleware.test.ts b/src/redux/middlewares/error.middleware.test.ts index c9f3d022e742d839a7abc1f91e2215f96da0d68b..a49e29f300118e9aeab0438d850b8b43dde56c94 100644 --- a/src/redux/middlewares/error.middleware.test.ts +++ b/src/redux/middlewares/error.middleware.test.ts @@ -12,7 +12,7 @@ describe('errorMiddlewareListener', () => { jest.clearAllMocks(); }); - it('should show toast with error message when action is rejected with value', async () => { + it('should handle error with message when action is rejected', async () => { const action = { type: 'action/rejected', payload: 'Error message', @@ -29,7 +29,7 @@ describe('errorMiddlewareListener', () => { expect(handleError).toHaveBeenCalledWith({ code: 'Error 2' }); }); - it('should show toast with unknown error when action is rejected without value', async () => { + it('should handle error without message when action is rejected without value', async () => { const action = { type: 'action/rejected', payload: null, @@ -46,7 +46,7 @@ describe('errorMiddlewareListener', () => { expect(handleError).toHaveBeenCalledWith({ code: 'Error 3' }); }); - it('should not show toast when action is not rejected', async () => { + it('should not handle error when action is not rejected', async () => { const action = { type: 'action/loading', payload: null, @@ -59,7 +59,7 @@ describe('errorMiddlewareListener', () => { expect(handleError).not.toHaveBeenCalled(); }); - it('should show toast with unknown error when action payload is not a string', async () => { + it('should handle error with unknown error message when action payload is not a string', async () => { const action = { type: 'action/rejected', payload: {}, @@ -77,7 +77,7 @@ describe('errorMiddlewareListener', () => { expect(handleError).toHaveBeenCalledWith({ code: 'ERROR', message: 'Error message' }); }); - it('should show toast with custom message when action payload is a string', async () => { + it('should handle error with custom message when action payload is a string', async () => { const action = { type: 'action/rejected', payload: 'Failed to fetch', @@ -88,9 +88,10 @@ describe('errorMiddlewareListener', () => { }, error: { code: 'ERROR', + message: 'xyz', }, }; await errorMiddlewareListener(action); - expect(handleError).toHaveBeenCalledWith({ code: 'ERROR' }); + expect(handleError).toHaveBeenCalledWith({ code: 'ERROR', message: 'xyz' }); }); }); diff --git a/src/redux/overlayBioEntity/overlayBioEntity.thunk.ts b/src/redux/overlayBioEntity/overlayBioEntity.thunk.ts index 956b6751b59b785cf082e89b5630f65d8acaf08f..f726bd8fff76200318f7e4f0b40c64bfb985eb3e 100644 --- a/src/redux/overlayBioEntity/overlayBioEntity.thunk.ts +++ b/src/redux/overlayBioEntity/overlayBioEntity.thunk.ts @@ -3,8 +3,8 @@ import { axiosInstanceNewAPI } from '@/services/api/utils/axiosInstance'; import { OverlayBioEntity } from '@/types/models'; import { OverlayBioEntityRender } from '@/types/OLrendering'; import { PluginsEventBus } from '@/services/pluginsManager/pluginsEventBus'; -import { getErrorMessage } from '@/utils/getErrorMessage'; import { ThunkConfig } from '@/types/store'; +import { getError } from '@/utils/error-report/getError'; import { getValidOverlayBioEntities, parseOverlayBioEntityToOlRenderingFormat, @@ -30,7 +30,7 @@ export const getOverlayBioEntity = createAsyncThunk< OverlayBioEntityRender[] | undefined, GetOverlayBioEntityThunkProps, ThunkConfig ->('overlayBioEntity/getOverlayBioEntity', async ({ overlayId, modelId }, { rejectWithValue }) => { +>('overlayBioEntity/getOverlayBioEntity', async ({ overlayId, modelId }) => { try { const response = await axiosInstanceNewAPI.get<OverlayBioEntity[]>( apiPath.getOverlayBioEntity({ overlayId, modelId }), @@ -47,12 +47,7 @@ export const getOverlayBioEntity = createAsyncThunk< return undefined; } catch (error) { - const errorMessage = getErrorMessage({ - error, - prefix: OVERLAY_BIO_ENTITY_FETCHING_ERROR_PREFIX, - }); - - return rejectWithValue(errorMessage); + return Promise.reject(getError({ error, prefix: OVERLAY_BIO_ENTITY_FETCHING_ERROR_PREFIX })); } }); @@ -65,7 +60,7 @@ export const getOverlayBioEntityForAllModels = createAsyncThunk< >( 'overlayBioEntity/getOverlayBioEntityForAllModels', // eslint-disable-next-line consistent-return - async ({ overlayId }, { dispatch, getState, rejectWithValue }) => { + async ({ overlayId }, { dispatch, getState }) => { try { const state = getState(); const modelsIds = modelsIdsSelector(state); @@ -76,12 +71,9 @@ export const getOverlayBioEntityForAllModels = createAsyncThunk< await Promise.all(asyncGetOverlayBioEntityFunctions); } catch (error) { - const errorMessage = getErrorMessage({ - error, - prefix: OVERLAY_BIO_ENTITY_ALL_MODELS_FETCHING_ERROR_PREFIX, - }); - - return rejectWithValue(errorMessage); + return Promise.reject( + getError({ error, prefix: OVERLAY_BIO_ENTITY_ALL_MODELS_FETCHING_ERROR_PREFIX }), + ); } }, ); @@ -93,7 +85,7 @@ export const getInitOverlays = createAsyncThunk< GetInitOverlaysProps, { state: RootState } & ThunkConfig // eslint-disable-next-line consistent-return ->('appInit/getInitOverlays', async ({ overlaysId }, { dispatch, getState, rejectWithValue }) => { +>('appInit/getInitOverlays', async ({ overlaysId }, { dispatch, getState }) => { try { const state = getState(); @@ -114,8 +106,6 @@ export const getInitOverlays = createAsyncThunk< dispatch(getOverlayBioEntityForAllModels({ overlayId: id })); }); } catch (error) { - const errorMessage = getErrorMessage({ error, prefix: INIT_OVERLAYS_ERROR_PREFIX }); - - return rejectWithValue(errorMessage); + return Promise.reject(getError({ error, prefix: INIT_OVERLAYS_ERROR_PREFIX })); } }); diff --git a/src/redux/plugins/plugins.reducers.test.ts b/src/redux/plugins/plugins.reducers.test.ts index ecbedd35721534bef5f6d274192873088206a28f..d5a7f0103f696eb9654a7edb482114fd2dbc4c42 100644 --- a/src/redux/plugins/plugins.reducers.test.ts +++ b/src/redux/plugins/plugins.reducers.test.ts @@ -6,6 +6,7 @@ import { import { mockNetworkResponse } from '@/utils/mockNetworkResponse'; import { HttpStatusCode } from 'axios'; import { pluginFixture } from '@/models/fixtures/pluginFixture'; +import { unwrapResult } from '@reduxjs/toolkit'; import { apiPath } from '../apiPath'; import { PluginsState } from './plugins.types'; import pluginsReducer, { removePlugin } from './plugins.slice'; @@ -59,7 +60,7 @@ describe('plugins reducer', () => { it('should update store after failed registerPlugin query', async () => { mockedAxiosClient.onPost(apiPath.registerPluign()).reply(HttpStatusCode.NotFound, undefined); - const { type, payload } = await store.dispatch( + const action = await store.dispatch( registerPlugin({ hash: pluginFixture.hash, isPublic: pluginFixture.isPublic, @@ -70,8 +71,8 @@ describe('plugins reducer', () => { }), ); - expect(type).toBe('plugins/registerPlugin/rejected'); - expect(payload).toEqual( + expect(action.type).toBe('plugins/registerPlugin/rejected'); + expect(() => unwrapResult(action)).toThrow( "Failed to register plugin: The page you're looking for doesn't exist. Please verify the URL and try again.", ); const { data, pluginsId } = store.getState().plugins.activePlugins; diff --git a/src/redux/plugins/plugins.thunks.ts b/src/redux/plugins/plugins.thunks.ts index 9f2108c4973f882ae17268ea73d2ca0e11da30c9..19dd2592d828443d2dd5ff52e41147d3ccc231a8 100644 --- a/src/redux/plugins/plugins.thunks.ts +++ b/src/redux/plugins/plugins.thunks.ts @@ -6,9 +6,9 @@ import type { MinervaPlugin } from '@/types/models'; import { validateDataUsingZodSchema } from '@/utils/validateDataUsingZodSchema'; import { createAsyncThunk } from '@reduxjs/toolkit'; import axios from 'axios'; -import { getErrorMessage } from '@/utils/getErrorMessage'; import { ThunkConfig } from '@/types/store'; import { getPluginHashWithoutPrefix } from '@/utils/plugins/getPluginHashWithoutPrefix'; +import { getError } from '@/utils/error-report/getError'; import { apiPath } from '../apiPath'; import { PLUGIN_FETCHING_ALL_ERROR_PREFIX, @@ -31,10 +31,7 @@ export const registerPlugin = createAsyncThunk< ThunkConfig >( 'plugins/registerPlugin', - async ( - { hash, isPublic, pluginName, pluginUrl, pluginVersion, extendedPluginName }, - { rejectWithValue }, - ) => { + async ({ hash, isPublic, pluginName, pluginUrl, pluginVersion, extendedPluginName }) => { try { const hashWihtoutPrefix = getPluginHashWithoutPrefix(hash); @@ -66,8 +63,7 @@ export const registerPlugin = createAsyncThunk< return undefined; } catch (error) { - const errorMessage = getErrorMessage({ error, prefix: PLUGIN_REGISTER_ERROR_PREFIX }); - return rejectWithValue(errorMessage); + return Promise.reject(getError({ error, prefix: PLUGIN_REGISTER_ERROR_PREFIX })); } }, ); @@ -86,7 +82,7 @@ type GetInitPluginsProps = { export const getInitPlugins = createAsyncThunk<void, GetInitPluginsProps, ThunkConfig>( 'plugins/getInitPlugins', // eslint-disable-next-line consistent-return - async ({ pluginsId, setHashedPlugin }, { rejectWithValue }) => { + async ({ pluginsId, setHashedPlugin }) => { try { /* eslint-disable no-restricted-syntax, no-await-in-loop */ @@ -112,15 +108,14 @@ export const getInitPlugins = createAsyncThunk<void, GetInitPluginsProps, ThunkC } } } catch (error) { - const errorMessage = getErrorMessage({ error, prefix: PLUGIN_INIT_FETCHING_ERROR_PREFIX }); - return rejectWithValue(errorMessage); + return Promise.reject(getError({ error, prefix: PLUGIN_INIT_FETCHING_ERROR_PREFIX })); } }, ); export const getAllPlugins = createAsyncThunk<MinervaPlugin[], void, ThunkConfig>( 'plugins/getAllPlugins', - async (_, { rejectWithValue }) => { + async () => { try { const response = await axiosInstance.get<MinervaPlugin[]>(apiPath.getAllPlugins()); @@ -130,8 +125,7 @@ export const getAllPlugins = createAsyncThunk<MinervaPlugin[], void, ThunkConfig return validPlugins; } catch (error) { - const errorMessage = getErrorMessage({ error, prefix: PLUGIN_FETCHING_ALL_ERROR_PREFIX }); - return rejectWithValue(errorMessage); + return Promise.reject(getError({ error, prefix: PLUGIN_FETCHING_ALL_ERROR_PREFIX })); } }, ); diff --git a/src/redux/project/project.reducers.test.ts b/src/redux/project/project.reducers.test.ts index 0521d9c262092e38956e8d239c5a8b82fb337610..6579fe788be4eb12bbebb7284648bf35b065925d 100644 --- a/src/redux/project/project.reducers.test.ts +++ b/src/redux/project/project.reducers.test.ts @@ -6,6 +6,7 @@ import { } from '@/utils/createStoreInstanceUsingSliceReducer'; import { mockNetworkNewAPIResponse } from '@/utils/mockNetworkResponse'; import { HttpStatusCode } from 'axios'; +import { unwrapResult } from '@reduxjs/toolkit'; import { apiPath } from '../apiPath'; import projectReducer from './project.slice'; import { getProjectById } from './project.thunks'; @@ -48,11 +49,11 @@ describe('project reducer', () => { it('should update store after failed getProjectById query', async () => { mockedAxiosClient.onGet(apiPath.getProjectById(PROJECT_ID)).reply(HttpStatusCode.NotFound, []); - const { type, payload } = await store.dispatch(getProjectById(PROJECT_ID)); + const action = await store.dispatch(getProjectById(PROJECT_ID)); const { data, loading, error } = store.getState().project; - expect(type).toBe('project/getProjectById/rejected'); - expect(payload).toBe( + expect(action.type).toBe('project/getProjectById/rejected'); + expect(() => unwrapResult(action)).toThrow( "Failed to fetch project by id: The page you're looking for doesn't exist. Please verify the URL and try again.", ); expect(loading).toEqual('failed'); diff --git a/src/redux/project/project.thunks.ts b/src/redux/project/project.thunks.ts index 23a73a0934aae9f5351d7810beb40666d93f9076..21d990741207aa2beea8d5cfab79a561fb47b1a8 100644 --- a/src/redux/project/project.thunks.ts +++ b/src/redux/project/project.thunks.ts @@ -3,16 +3,16 @@ import { axiosInstanceNewAPI } from '@/services/api/utils/axiosInstance'; import { Project } from '@/types/models'; import { validateDataUsingZodSchema } from '@/utils/validateDataUsingZodSchema'; import { createAsyncThunk } from '@reduxjs/toolkit'; -import { getErrorMessage } from '@/utils/getErrorMessage'; import { ThunkConfig } from '@/types/store'; import { DEFAULT_PROJECT_ID } from '@/constants'; +import { getError } from '@/utils/error-report/getError'; import { apiPath } from '../apiPath'; import { PROJECT_FETCHING_ERROR_PREFIX } from './project.constants'; import { SetProjectIdParams } from './project.types'; export const getProjectById = createAsyncThunk<Project | undefined, string, ThunkConfig>( 'project/getProjectById', - async (id, { rejectWithValue }) => { + async id => { try { const response = await axiosInstanceNewAPI.get<Project>(apiPath.getProjectById(id)); @@ -20,8 +20,7 @@ export const getProjectById = createAsyncThunk<Project | undefined, string, Thun return isDataValid ? response.data : undefined; } catch (error) { - const errorMessage = getErrorMessage({ error, prefix: PROJECT_FETCHING_ERROR_PREFIX }); - return rejectWithValue(errorMessage); + return Promise.reject(getError({ error, prefix: PROJECT_FETCHING_ERROR_PREFIX })); } }, ); diff --git a/src/redux/publications/publications.thunks.ts b/src/redux/publications/publications.thunks.ts index f50b611eec013b7e09a0b9773775f6fafc9e4cba..4c03def9f0b594ea9450d5733df8034d7bc8cce7 100644 --- a/src/redux/publications/publications.thunks.ts +++ b/src/redux/publications/publications.thunks.ts @@ -3,8 +3,8 @@ import { validateDataUsingZodSchema } from '@/utils/validateDataUsingZodSchema'; import { axiosInstance } from '@/services/api/utils/axiosInstance'; import { PublicationsResponse } from '@/types/models'; import { publicationsResponseSchema } from '@/models/publicationsResponseSchema'; -import { getErrorMessage } from '@/utils/getErrorMessage'; import { ThunkConfig } from '@/types/store'; +import { getError } from '@/utils/error-report/getError'; import { GetPublicationsParams } from './publications.types'; import { apiPath } from '../apiPath'; import { PUBLICATIONS_FETCHING_ERROR_PREFIX } from './publications.constatns'; @@ -13,7 +13,7 @@ export const getPublications = createAsyncThunk< PublicationsResponse | undefined, GetPublicationsParams, ThunkConfig ->('publications/getPublications', async (params, { rejectWithValue }) => { +>('publications/getPublications', async params => { try { const response = await axiosInstance.get<PublicationsResponse>(apiPath.getPublications(params)); @@ -21,7 +21,6 @@ export const getPublications = createAsyncThunk< return isDataValid ? response.data : undefined; } catch (error) { - const errorMessage = getErrorMessage({ error, prefix: PUBLICATIONS_FETCHING_ERROR_PREFIX }); - return rejectWithValue(errorMessage); + return Promise.reject(getError({ error, prefix: PUBLICATIONS_FETCHING_ERROR_PREFIX })); } }); diff --git a/src/redux/reactions/reactions.thunks.ts b/src/redux/reactions/reactions.thunks.ts index 610a83adfced0672fe526d609df7072842e3c264..5a650ebce706d23eecad1e8c1fdd72a95b2411d2 100644 --- a/src/redux/reactions/reactions.thunks.ts +++ b/src/redux/reactions/reactions.thunks.ts @@ -3,10 +3,10 @@ import { apiPath } from '@/redux/apiPath'; import { axiosInstance } from '@/services/api/utils/axiosInstance'; import { Reaction } from '@/types/models'; import { ThunkConfig } from '@/types/store'; -import { getErrorMessage } from '@/utils/getErrorMessage'; import { validateDataUsingZodSchema } from '@/utils/validateDataUsingZodSchema'; import { createAsyncThunk } from '@reduxjs/toolkit'; import { z } from 'zod'; +import { getError } from '@/utils/error-report/getError'; import { REACTIONS_FETCHING_ERROR_PREFIX } from './reactions.constants'; type GetReactionsByIdsArgs = @@ -25,7 +25,7 @@ export const getReactionsByIds = createAsyncThunk< GetReactionsByIdsResult | undefined, GetReactionsByIdsArgs, ThunkConfig ->('reactions/getByIds', async (args, { rejectWithValue }) => { +>('reactions/getByIds', async args => { const ids = args instanceof Array ? args : args.ids; const shouldConcat = args instanceof Array ? false : args.shouldConcat || false; @@ -42,7 +42,6 @@ export const getReactionsByIds = createAsyncThunk< shouldConcat, }; } catch (error) { - const errorMessage = getErrorMessage({ error, prefix: REACTIONS_FETCHING_ERROR_PREFIX }); - return rejectWithValue(errorMessage); + return Promise.reject(getError({ error, prefix: REACTIONS_FETCHING_ERROR_PREFIX })); } }); diff --git a/src/redux/user/user.thunks.ts b/src/redux/user/user.thunks.ts index 5741e090ceaf830c7abc704a847becf95cee9668..2efa9104a067da0ac23d399d5fcf8a96efb1f059 100644 --- a/src/redux/user/user.thunks.ts +++ b/src/redux/user/user.thunks.ts @@ -4,8 +4,8 @@ import { validateDataUsingZodSchema } from '@/utils/validateDataUsingZodSchema'; import { loginSchema } from '@/models/loginSchema'; import { sessionSchemaValid } from '@/models/sessionValidSchema'; import { Login, SessionValid, UserPrivileges } from '@/types/models'; -import { getErrorMessage } from '@/utils/getErrorMessage'; import { USER_ROLE } from '@/constants/user'; +import { getError } from '@/utils/error-report/getError'; import { apiPath } from '../apiPath'; import { closeModal, openLoggedInMenuModal } from '../modal/modal.slice'; import { hasPrivilege } from './user.utils'; @@ -31,7 +31,7 @@ const getUserRole = async (login: string): Promise<string> => { export const login = createAsyncThunk( 'user/login', - async (credentials: { login: string; password: string }, { dispatch, rejectWithValue }) => { + async (credentials: { login: string; password: string }, { dispatch }) => { try { const searchParams = new URLSearchParams(credentials); const response = await axiosInstance.post<Login>(apiPath.postLogin(), searchParams, { @@ -58,12 +58,7 @@ export const login = createAsyncThunk( return undefined; } catch (error) { - const errorMessage = getErrorMessage({ - error, - prefix: 'Login', - }); - - return rejectWithValue(errorMessage); + return Promise.reject(getError({ error, prefix: 'Login' })); } }, ); @@ -91,7 +86,7 @@ export const getSessionValid = createAsyncThunk('user/getSessionValid', async () return undefined; }); -export const logout = createAsyncThunk('user/logout', async (_, { rejectWithValue }) => { +export const logout = createAsyncThunk('user/logout', async () => { try { await axiosInstance.post(apiPath.logout(), null, { withCredentials: true, @@ -99,11 +94,6 @@ export const logout = createAsyncThunk('user/logout', async (_, { rejectWithValu return undefined; } catch (error) { - const errorMessage = getErrorMessage({ - error, - prefix: 'Log out', - }); - - return rejectWithValue(errorMessage); + return Promise.reject(getError({ error, prefix: 'Log out' })); } });