import fetchMock from 'fetch-mock-jest'; import { fireEvent, render, screen, initializeMocks, } from '../testUtils'; import { getContentSearchConfigUrl } from '../search-manager/data/api'; import { mockContentLibrary } from './data/api.mocks'; import mockEmptyResult from '../search-modal/__mocks__/empty-search-result.json'; import { LibraryProvider } from './common/context/LibraryContext'; import LibraryContent from './LibraryContent'; import { libraryComponentsMock } from './__mocks__'; const searchEndpoint = 'http://mock.meilisearch.local/multi-search'; mockContentLibrary.applyMock(); const mockFetchNextPage = jest.fn(); const mockUseSearchContext = jest.fn(); const data = { totalContentAndCollectionHits: 0, contentAndCollectionHits: [], isFetchingNextPage: false, hasNextPage: false, fetchNextPage: mockFetchNextPage, searchKeywords: '', isFiltered: false, isLoading: false, }; const returnEmptyResult = (_url: string, req) => { const requestData = JSON.parse(req.body?.toString() ?? ''); const query = requestData?.queries[0]?.q ?? ''; // We have to replace the query (search keywords) in the mock results with the actual query, // because otherwise we may have an inconsistent state that causes more queries and unexpected results. mockEmptyResult.results[0].query = query; // And fake the required '_formatted' fields; it contains the highlighting ... around matched words // eslint-disable-next-line no-underscore-dangle, no-param-reassign mockEmptyResult.results[0]?.hits.forEach((hit: any) => { hit._formatted = { ...hit }; }); return mockEmptyResult; }; jest.mock('../search-manager', () => ({ ...jest.requireActual('../search-manager'), useSearchContext: () => mockUseSearchContext(), })); const withLibraryId = (libraryId: string) => ({ extraWrapper: ({ children }: { children: React.ReactNode }) => ( {children} ), }); describe('', () => { beforeEach(() => { const { axiosMock } = initializeMocks(); fetchMock.post(searchEndpoint, returnEmptyResult, { overwriteRoutes: true }); // The API method to get the Meilisearch connection details uses Axios: axiosMock.onGet(getContentSearchConfigUrl()).reply(200, { url: 'http://mock.meilisearch.local', index_name: 'studio', api_key: 'test-key', }); }); afterEach(() => { fetchMock.reset(); mockFetchNextPage.mockReset(); }); it('should render a spinner while loading', async () => { mockUseSearchContext.mockReturnValue({ ...data, isLoading: true, }); render(, withLibraryId(mockContentLibrary.libraryId)); expect(screen.getByText('Loading...')).toBeInTheDocument(); }); it('should render an empty state when there are no results', async () => { mockUseSearchContext.mockReturnValue({ ...data, totalHits: 0, }); render(, withLibraryId(mockContentLibrary.libraryId)); expect(screen.getByText('You have not added any content to this library yet.')).toBeInTheDocument(); }); it('should load more results when the user scrolls to the bottom', async () => { mockUseSearchContext.mockReturnValue({ ...data, hits: libraryComponentsMock, hasNextPage: true, }); render(, withLibraryId(mockContentLibrary.libraryId)); Object.defineProperty(window, 'innerHeight', { value: 800 }); Object.defineProperty(document.body, 'scrollHeight', { value: 1600 }); fireEvent.scroll(window, { target: { scrollY: 1000 } }); expect(mockFetchNextPage).toHaveBeenCalled(); }); });