From 0fad435edf96c31d627480dc2ce908503d018e91 Mon Sep 17 00:00:00 2001 From: oguhpereira Date: Mon, 12 Sep 2022 21:07:42 -0300 Subject: [PATCH 1/3] feat: add prevent validateResponseFormat() from validating custom routes --- packages/ra-core/src/dataProvider/dataFetchActions.ts | 5 +++++ packages/ra-core/src/dataProvider/useDataProvider.ts | 6 +++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/packages/ra-core/src/dataProvider/dataFetchActions.ts b/packages/ra-core/src/dataProvider/dataFetchActions.ts index de988518083..6ef9bc5ab4b 100644 --- a/packages/ra-core/src/dataProvider/dataFetchActions.ts +++ b/packages/ra-core/src/dataProvider/dataFetchActions.ts @@ -21,6 +21,11 @@ export const fetchActionsWithArrayOfRecordsResponse = [ ]; export const fetchActionsWithTotalResponse = ['getList', 'getManyReference']; +export const fetchActions = [ + ...fetchActionsWithRecordResponse, + ...fetchActionsWithArrayOfRecordsResponse, +]; + export const sanitizeFetchType = (fetchType: string) => { switch (fetchType) { case GET_LIST: diff --git a/packages/ra-core/src/dataProvider/useDataProvider.ts b/packages/ra-core/src/dataProvider/useDataProvider.ts index bd713cfd997..9bc92e9f759 100644 --- a/packages/ra-core/src/dataProvider/useDataProvider.ts +++ b/packages/ra-core/src/dataProvider/useDataProvider.ts @@ -5,6 +5,7 @@ import { defaultDataProvider } from './defaultDataProvider'; import validateResponseFormat from './validateResponseFormat'; import { DataProvider } from '../types'; import useLogoutIfAccessDenied from '../auth/useLogoutIfAccessDenied'; +import { fetchActions } from './dataFetchActions'; /** * Hook for getting a dataProvider @@ -98,7 +99,10 @@ export const useDataProvider = < return dataProvider[type] .apply(dataProvider, args) .then(response => { - if (process.env.NODE_ENV !== 'production') { + if ( + process.env.NODE_ENV !== 'production' && + fetchActions.includes(type) + ) { validateResponseFormat(response, type); } return response; From 448a8eca6631632713b4f14513c951f5af0d4b97 Mon Sep 17 00:00:00 2001 From: oguhpereira Date: Fri, 16 Sep 2022 17:54:44 -0300 Subject: [PATCH 2/3] feat: add unit test --- .../src/dataProvider/dataFetchActions.ts | 2 +- .../src/dataProvider/useDataProvider.spec.tsx | 99 +++++++++++++++++++ .../src/dataProvider/useDataProvider.ts | 4 +- 3 files changed, 102 insertions(+), 3 deletions(-) diff --git a/packages/ra-core/src/dataProvider/dataFetchActions.ts b/packages/ra-core/src/dataProvider/dataFetchActions.ts index 6ef9bc5ab4b..249d90a7647 100644 --- a/packages/ra-core/src/dataProvider/dataFetchActions.ts +++ b/packages/ra-core/src/dataProvider/dataFetchActions.ts @@ -21,7 +21,7 @@ export const fetchActionsWithArrayOfRecordsResponse = [ ]; export const fetchActionsWithTotalResponse = ['getList', 'getManyReference']; -export const fetchActions = [ +export const reactAdminFetchActions = [ ...fetchActionsWithRecordResponse, ...fetchActionsWithArrayOfRecordsResponse, ]; diff --git a/packages/ra-core/src/dataProvider/useDataProvider.spec.tsx b/packages/ra-core/src/dataProvider/useDataProvider.spec.tsx index b5b631df99f..0fc2c6ffeec 100644 --- a/packages/ra-core/src/dataProvider/useDataProvider.spec.tsx +++ b/packages/ra-core/src/dataProvider/useDataProvider.spec.tsx @@ -5,6 +5,7 @@ import expect from 'expect'; import { useDataProvider } from './useDataProvider'; import { CoreAdminContext } from '../core'; +import { GetListResult } from '..'; const UseGetOne = () => { const [data, setData] = useState(); @@ -21,6 +22,40 @@ const UseGetOne = () => { return
loading
; }; +const UseGetList = () => { + const [data, setData] = useState(); + const [error, setError] = useState(); + const dataProvider = useDataProvider(); + useEffect(() => { + dataProvider + .getList('posts', { + pagination: { page: 1, perPage: 10 }, + sort: { field: 'id', order: 'ASC' }, + filter: {}, + }) + .then(({ data, total }) => setData({ data, total })) + .catch(e => setError(e)); + }, [dataProvider]); + if (error) return
{error.message}
; + if (data) return
{JSON.stringify(data)}
; + return
loading
; +}; + +const UseGetCustom = () => { + const [data, setData] = useState(); + const [error, setError] = useState(); + const dataProvider = useDataProvider(); + useEffect(() => { + dataProvider + .getCustom('posts', { id: 1 }) + .then(res => setData(res.result)) + .catch(e => setError(e)); + }, [dataProvider]); + if (error) return
{error.message}
; + if (data) return
{JSON.stringify(data)}
; + return
loading
; +}; + describe('useDataProvider', () => { it('should return a way to call the dataProvider', async () => { const getOne = jest.fn(() => @@ -173,4 +208,68 @@ describe('useDataProvider', () => { expect(customVerb).toHaveBeenCalledWith({ id: 1 }, ['something']); }); + + it('should call getList and not show error', async () => { + const getList = jest.fn(() => + Promise.resolve({ data: [{ id: 1, title: 'foo' }], total: 1 }) + ); + const dataProvider = { getList }; + const { queryByTestId } = render( + + + + ); + expect(queryByTestId('loading')).not.toBeNull(); + await act(async () => { + await new Promise(resolve => setTimeout(resolve)); + }); + expect(getList).toBeCalledTimes(1); + expect(queryByTestId('loading')).toBeNull(); + expect(queryByTestId('data')?.textContent).toBe( + '{"data":[{"id":1,"title":"foo"}],"total":1}' + ); + }); + + it('should call getList and show error', async () => { + const getList = jest.fn(() => + Promise.resolve({ data: [{ id: 1, title: 'foo' }] }) + ); + const dataProvider = { getList }; + const { queryByTestId } = render( + + + + ); + expect(queryByTestId('loading')).not.toBeNull(); + await act(async () => { + await new Promise(resolve => setTimeout(resolve)); + }); + expect(getList).toBeCalledTimes(1); + expect(queryByTestId('loading')).toBeNull(); + expect(queryByTestId('error')?.textContent).toBe( + 'ra.notification.data_provider_error' + ); + }); + + it('should call custom and not show error', async () => { + const getCustom = jest.fn(() => + Promise.resolve({ result: [{ id: 1, title: 'foo' }] }) + ); + const dataProvider = { getCustom }; + const { queryByTestId } = render( + + + + ); + expect(queryByTestId('loading')).not.toBeNull(); + await act(async () => { + await new Promise(resolve => setTimeout(resolve)); + }); + expect(getCustom).toBeCalledTimes(1); + expect(queryByTestId('loading')).toBeNull(); + expect(queryByTestId('data')?.textContent).toBe( + '[{"id":1,"title":"foo"}]' + ); + expect(queryByTestId('error')?.textContent).toBeUndefined(); + }); }); diff --git a/packages/ra-core/src/dataProvider/useDataProvider.ts b/packages/ra-core/src/dataProvider/useDataProvider.ts index 9bc92e9f759..b5f5410762f 100644 --- a/packages/ra-core/src/dataProvider/useDataProvider.ts +++ b/packages/ra-core/src/dataProvider/useDataProvider.ts @@ -5,7 +5,7 @@ import { defaultDataProvider } from './defaultDataProvider'; import validateResponseFormat from './validateResponseFormat'; import { DataProvider } from '../types'; import useLogoutIfAccessDenied from '../auth/useLogoutIfAccessDenied'; -import { fetchActions } from './dataFetchActions'; +import { reactAdminFetchActions } from './dataFetchActions'; /** * Hook for getting a dataProvider @@ -101,7 +101,7 @@ export const useDataProvider = < .then(response => { if ( process.env.NODE_ENV !== 'production' && - fetchActions.includes(type) + reactAdminFetchActions.includes(type) ) { validateResponseFormat(response, type); } From c0bc6bb10130f9dc93a5efa2cdfad072d54d198a Mon Sep 17 00:00:00 2001 From: oguhpereira Date: Mon, 19 Sep 2022 19:23:01 -0300 Subject: [PATCH 3/3] feat: disable console error in jest --- packages/ra-core/src/dataProvider/useDataProvider.spec.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/ra-core/src/dataProvider/useDataProvider.spec.tsx b/packages/ra-core/src/dataProvider/useDataProvider.spec.tsx index 0fc2c6ffeec..b1083fd54ad 100644 --- a/packages/ra-core/src/dataProvider/useDataProvider.spec.tsx +++ b/packages/ra-core/src/dataProvider/useDataProvider.spec.tsx @@ -231,6 +231,8 @@ describe('useDataProvider', () => { }); it('should call getList and show error', async () => { + jest.spyOn(console, 'error').mockImplementation(() => {}); + const getList = jest.fn(() => Promise.resolve({ data: [{ id: 1, title: 'foo' }] }) ); @@ -240,6 +242,7 @@ describe('useDataProvider', () => { ); + expect(queryByTestId('loading')).not.toBeNull(); await act(async () => { await new Promise(resolve => setTimeout(resolve));