diff --git a/packages/core/src/react/hooks/__tests__/useFetchAppSettings.tsx b/packages/core/src/react/hooks/__tests__/useFetchAppSettings.tsx index ec2332791..c5c8e9ff3 100644 --- a/packages/core/src/react/hooks/__tests__/useFetchAppSettings.tsx +++ b/packages/core/src/react/hooks/__tests__/useFetchAppSettings.tsx @@ -1,7 +1,9 @@ -import { renderHook } from '@testing-library/react'; +import { renderHook, waitFor } from '@testing-library/react'; import { expectTypeOf } from 'expect-type'; import { AppEntity, EndpointParams } from '../../../data'; import { useFetchAppSettings } from '../useFetchAppSettings'; +import * as useFetchModule from '../useFetch'; +import { mockUseFetchErrorResponse } from '../mocks'; describe('useFetchAppSettings types', () => { it('allows overriding types', () => { @@ -23,4 +25,27 @@ describe('useFetchAppSettings types', () => { | undefined >(); }); + + it('handles response if has error or there is no data', async () => { + const spyUseFetch = jest + .spyOn(useFetchModule, 'useFetch') + .mockReturnValueOnce(mockUseFetchErrorResponse); + const { result } = renderHook(() => useFetchAppSettings({ includeCustomSettings: true })); + + const expectedKeys = ['error', 'loading', 'data', 'isMainQuery']; + const returnedKeys = Object.keys(result.current); + const missingKeys = returnedKeys.filter((key) => !expectedKeys.includes(key)); + + await waitFor(() => { + expect(missingKeys).toHaveLength(0); + expect(spyUseFetch).toHaveBeenCalledTimes(1); + expect(result.current.error).toBe('Not found'); + expect(result.current.loading).toBe(true); + expect(() => result.current.data).not.toThrow(); + expect(() => result.current.data?.posts).toThrow(); + expect(result.current.isMainQuery).toBe(true); + }); + + spyUseFetch.mockRestore(); + }); }); diff --git a/packages/core/src/react/hooks/__tests__/useFetchPost.tsx b/packages/core/src/react/hooks/__tests__/useFetchPost.tsx index 2ddcb0f4e..cb089a064 100644 --- a/packages/core/src/react/hooks/__tests__/useFetchPost.tsx +++ b/packages/core/src/react/hooks/__tests__/useFetchPost.tsx @@ -5,6 +5,8 @@ import { DRAFT_POST_ID, VALID_AUTH_TOKEN } from '../../../../test/server'; import { PostEntity, PostParams } from '../../../data'; import { SettingsProvider } from '../../provider'; import { useFetchPost } from '../useFetchPost'; +import * as useFetchModule from '../useFetch'; +import { mockUseFetchErrorResponse } from '../mocks'; describe('useFetchPost', () => { const wrapper = ({ children }) => { @@ -43,6 +45,31 @@ describe('useFetchPost', () => { ); }); + it('handles response if has error or there is no data', async () => { + const spyUseFetch = jest + .spyOn(useFetchModule, 'useFetch') + .mockReturnValueOnce(mockUseFetchErrorResponse); + const { result } = renderHook(() => useFetchPost({}), { + wrapper, + }); + + const expectedKeys = ['error', 'loading', 'data', 'isMainQuery']; + const returnedKeys = Object.keys(result.current); + const missingKeys = returnedKeys.filter((key) => !expectedKeys.includes(key)); + + await waitFor(() => { + expect(missingKeys).toHaveLength(0); + expect(spyUseFetch).toHaveBeenCalledTimes(1); + expect(result.current.error).toBe('Not found'); + expect(result.current.loading).toBe(false); + expect(() => result.current.data).not.toThrow(); + expect(() => result.current.data?.post.title).toThrow(); + expect(result.current.isMainQuery).toBe(true); + }); + + spyUseFetch.mockRestore(); + }); + it('fetch by id', async () => { const { result } = renderHook(() => useFetchPost({ id: 64 }), { wrapper, diff --git a/packages/core/src/react/hooks/__tests__/useFetchPostOrPosts.tsx b/packages/core/src/react/hooks/__tests__/useFetchPostOrPosts.tsx index 00814d1db..c712bfae0 100644 --- a/packages/core/src/react/hooks/__tests__/useFetchPostOrPosts.tsx +++ b/packages/core/src/react/hooks/__tests__/useFetchPostOrPosts.tsx @@ -6,6 +6,8 @@ import { useFetchPostOrPosts } from '../useFetchPostOrPosts'; import { PostEntity, PostOrPostsParams } from '../../../data'; import { useFetchPost } from '../useFetchPost'; import { useFetchPosts } from '../useFetchPosts'; +import * as useFetchModule from '../useFetch'; +import { mockUseFetchErrorResponse } from '../mocks'; describe('useFetchPostOrPosts', () => { const wrapper = ({ children }) => { @@ -75,6 +77,33 @@ describe('useFetchPostOrPosts', () => { }); }); + it('handles response if has error or there is no data', async () => { + const spyUseFetch = jest + .spyOn(useFetchModule, 'useFetch') + .mockReturnValueOnce(mockUseFetchErrorResponse); + const { result } = renderHook(() => useFetchPostOrPosts({}), { + wrapper, + }); + const expectedKeys = ['error', 'loading', 'isArchive', 'isSingle', 'data', 'isMainQuery']; + const returnedKeys = Object.keys(result.current); + const missingKeys = returnedKeys.filter((key) => !expectedKeys.includes(key)); + + await waitFor(() => { + expect(missingKeys).toHaveLength(0); + expect(spyUseFetch).toHaveBeenCalledTimes(3); // #1 useFetch, #2 useFetchPost, #3 useFetchPosts + expect(result.current.error).toBe('Not found'); + expect(result.current.loading).toBe(false); + expect(result.current.isArchive).toBe(false); + expect(result.current.isSingle).toBe(false); + expect(() => result.current.data).not.toThrow(); + expect(() => result.current.data?.post!.title).toThrow(); + expect(() => result.current.data?.posts![0].title).toThrow(); + expect(result.current.isMainQuery).toBe(true); + }); + + spyUseFetch.mockRestore(); + }); + it('populates internal swr cache (single)', async () => { const p: PostOrPostsParams = { archive: { taxonomy: 'category' }, diff --git a/packages/core/src/react/hooks/__tests__/useFetchPosts.tsx b/packages/core/src/react/hooks/__tests__/useFetchPosts.tsx index 6a92b4768..3d6a8c492 100644 --- a/packages/core/src/react/hooks/__tests__/useFetchPosts.tsx +++ b/packages/core/src/react/hooks/__tests__/useFetchPosts.tsx @@ -6,6 +6,8 @@ import { PostEntity, PostsArchiveParams } from '../../../data'; import { SettingsProvider } from '../../provider'; import { useFetchPosts } from '../useFetchPosts'; import { setHeadlessConfig } from '../../../utils'; +import * as useFetchModule from '../useFetch'; +import { mockUseFetchErrorResponse } from '../mocks'; describe('useFetchPosts', () => { const wrapper = ({ children }) => { @@ -40,6 +42,33 @@ describe('useFetchPosts', () => { }); }); + it('handles response if has error or there is no data', async () => { + const spyUseFetch = jest + .spyOn(useFetchModule, 'useFetch') + .mockReturnValueOnce(mockUseFetchErrorResponse); + const { result } = renderHook(() => useFetchPosts({}), { + wrapper, + }); + + const expectedKeys = ['error', 'loading', 'pageType', 'data', 'isMainQuery']; + const returnedKeys = Object.keys(result.current); + const missingKeys = returnedKeys.filter((key) => !expectedKeys.includes(key)); + + await waitFor(() => { + expect(missingKeys).toHaveLength(0); + expect(spyUseFetch).toHaveBeenCalledTimes(1); + expect(result.current.error).toBe('Not found'); + expect(result.current.loading).toBe(true); + expect(() => result.current.data).not.toThrow(); + expect(() => result.current.data?.posts[0].title).toThrow(); + expect(() => result.current.data?.pageInfo[0].title).toThrow(); + expect(() => result.current.data?.queriedObject[0].title).toThrow(); + expect(result.current.isMainQuery).toBe(true); + }); + + spyUseFetch.mockRestore(); + }); + it('returns queried object for category archives', async () => { const { result } = renderHook( () => diff --git a/packages/core/src/react/hooks/__tests__/useFetchSearch.tsx b/packages/core/src/react/hooks/__tests__/useFetchSearch.tsx index d90de9a7c..bcb021cbb 100644 --- a/packages/core/src/react/hooks/__tests__/useFetchSearch.tsx +++ b/packages/core/src/react/hooks/__tests__/useFetchSearch.tsx @@ -6,8 +6,10 @@ import { PostEntity, PostsArchiveParams } from '../../../data'; import { SettingsProvider } from '../../provider'; import { useFetchSearch } from '../useFetchSearch'; import { setHeadlessConfig } from '../../../utils'; +import * as useFetchModule from '../useFetch'; +import { mockUseFetchErrorResponse } from '../mocks'; -describe('useFetchPosts', () => { +describe('useFetchSearch', () => { const wrapper = ({ children }) => { return {children}; }; @@ -29,6 +31,33 @@ describe('useFetchPosts', () => { }); }); + it('handles response if has error or there is no data', async () => { + const spyUseFetch = jest + .spyOn(useFetchModule, 'useFetch') + .mockReturnValueOnce(mockUseFetchErrorResponse); + const { result } = renderHook(() => useFetchSearch({}), { + wrapper, + }); + + const expectedKeys = ['error', 'loading', 'data', 'isMainQuery']; + const returnedKeys = Object.keys(result.current); + const missingKeys = returnedKeys.filter((key) => !expectedKeys.includes(key)); + + await waitFor(() => { + expect(missingKeys).toHaveLength(0); + expect(spyUseFetch).toHaveBeenCalledTimes(1); + expect(result.current.error).toBe('Not found'); + expect(result.current.loading).toBe(true); + expect(() => result.current.data).not.toThrow(); + expect(() => result.current.data?.posts[0].title).toThrow(); + expect(() => result.current.data?.pageInfo[0].title).toThrow(); + expect(() => result.current.data?.queriedObject[0].title).toThrow(); + expect(result.current.isMainQuery).toBe(true); + }); + + spyUseFetch.mockRestore(); + }); + it('fetches data properly', async () => { const { result } = renderHook(() => useFetchSearch({ per_page: 2 }, {}, '/ipsum'), { wrapper, diff --git a/packages/core/src/react/hooks/__tests__/useFetchTerms.tsx b/packages/core/src/react/hooks/__tests__/useFetchTerms.tsx index 5264ca67e..a87c84183 100644 --- a/packages/core/src/react/hooks/__tests__/useFetchTerms.tsx +++ b/packages/core/src/react/hooks/__tests__/useFetchTerms.tsx @@ -1,7 +1,9 @@ -import { renderHook } from '@testing-library/react'; +import { renderHook, waitFor } from '@testing-library/react'; import { expectTypeOf } from 'expect-type'; import { TaxonomyArchiveParams, TermEntity } from '../../../data'; import { useFetchTerms } from '../useFetchTerms'; +import * as useFetchModule from '../useFetch'; +import { mockUseFetchErrorResponse } from '../mocks'; describe('useFetchTerms types', () => { it('allows overriding types', () => { @@ -22,4 +24,28 @@ describe('useFetchTerms types', () => { | undefined >(); }); + + it('handles response if has error or there is no data', async () => { + const spyUseFetch = jest + .spyOn(useFetchModule, 'useFetch') + .mockReturnValueOnce(mockUseFetchErrorResponse); + const { result } = renderHook(() => useFetchTerms({ includeCustomSettings: true })); + + const expectedKeys = ['error', 'loading', 'data', 'isMainQuery']; + const returnedKeys = Object.keys(result.current); + const missingKeys = returnedKeys.filter((key) => !expectedKeys.includes(key)); + + await waitFor(() => { + expect(missingKeys).toHaveLength(0); + expect(spyUseFetch).toHaveBeenCalledTimes(1); + expect(result.current.error).toBe('Not found'); + expect(result.current.loading).toBe(true); + expect(() => result.current.data).not.toThrow(); + expect(() => result.current.data?.terms[0].title).toThrow(); + expect(() => result.current.data?.pageInfo[0].title).toThrow(); + expect(result.current.isMainQuery).toBe(true); + }); + + spyUseFetch.mockRestore(); + }); }); diff --git a/packages/core/src/react/hooks/mocks.ts b/packages/core/src/react/hooks/mocks.ts new file mode 100644 index 000000000..46fecc3c0 --- /dev/null +++ b/packages/core/src/react/hooks/mocks.ts @@ -0,0 +1,9 @@ +export const mockUseFetchErrorResponse = { + error: 'Not found', + params: {}, + data: undefined, + isMainQuery: true, + mutate: jest.fn(), + isLoading: false, + isValidating: false, +};