diff --git a/src/library-authoring/components/LibraryComponents.test.tsx b/src/library-authoring/components/LibraryComponents.test.tsx
index 83919d94e7..5dc689cb14 100644
--- a/src/library-authoring/components/LibraryComponents.test.tsx
+++ b/src/library-authoring/components/LibraryComponents.test.tsx
@@ -1,38 +1,60 @@
import React from 'react';
import { AppProvider } from '@edx/frontend-platform/react';
import { initializeMockApp } from '@edx/frontend-platform';
+import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
import { IntlProvider } from '@edx/frontend-platform/i18n';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { render, screen, fireEvent } from '@testing-library/react';
-import LibraryComponents from './LibraryComponents';
+import MockAdapter from 'axios-mock-adapter';
+import fetchMock from 'fetch-mock-jest';
+import type { Store } from 'redux';
+import { getContentSearchConfigUrl } from '../../search-modal/data/api';
+import mockEmptyResult from '../../search-modal/__mocks__/empty-search-result.json';
+import { SearchContextProvider } from '../../search-modal/manager/SearchManager';
import initializeStore from '../../store';
import { libraryComponentsMock } from '../__mocks__';
+import LibraryComponents from './LibraryComponents';
+
+const searchEndpoint = 'http://mock.meilisearch.local/multi-search';
-const mockUseLibraryComponents = jest.fn();
-const mockUseLibraryComponentCount = jest.fn();
const mockUseLibraryBlockTypes = jest.fn();
const mockFetchNextPage = jest.fn();
-let store;
-const queryClient = new QueryClient({
- defaultOptions: {
- queries: {
- retry: false,
- },
- },
-});
+const mockUseSearchContext = jest.fn();
const data = {
+ totalHits: 1,
hits: [],
isFetching: true,
isFetchingNextPage: false,
hasNextPage: false,
fetchNextPage: mockFetchNextPage,
+ searchKeywords: '',
};
-const countData = {
- componentCount: 1,
- collectionCount: 0,
+
+let store: Store;
+let axiosMock: MockAdapter;
+
+const queryClient = new QueryClient({
+ defaultOptions: {
+ queries: {
+ retry: 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;
};
+
const blockTypeData = {
data: [
{
@@ -51,16 +73,21 @@ const blockTypeData = {
};
jest.mock('../data/apiHook', () => ({
- useLibraryComponents: () => mockUseLibraryComponents(),
- useLibraryComponentCount: () => mockUseLibraryComponentCount(),
useLibraryBlockTypes: () => mockUseLibraryBlockTypes(),
}));
+jest.mock('../../search-modal/manager/SearchManager', () => ({
+ ...jest.requireActual('../../search-modal/manager/SearchManager'),
+ useSearchContext: () => mockUseSearchContext(),
+}));
+
const RootWrapper = (props) => (
-
+
+
+
@@ -77,9 +104,18 @@ describe('', () => {
},
});
store = initializeStore();
- mockUseLibraryComponents.mockReturnValue(data);
- mockUseLibraryComponentCount.mockReturnValue(countData);
mockUseLibraryBlockTypes.mockReturnValue(blockTypeData);
+ mockUseSearchContext.mockReturnValue(data);
+
+ fetchMock.post(searchEndpoint, returnEmptyResult, { overwriteRoutes: true });
+
+ // The API method to get the Meilisearch connection details uses Axios:
+ axiosMock = new MockAdapter(getAuthenticatedHttpClient());
+ axiosMock.onGet(getContentSearchConfigUrl()).reply(200, {
+ url: 'http://mock.meilisearch.local',
+ index_name: 'studio',
+ api_key: 'test-key',
+ });
});
afterEach(() => {
@@ -87,10 +123,11 @@ describe('', () => {
});
it('should render empty state', async () => {
- mockUseLibraryComponentCount.mockReturnValueOnce({
- ...countData,
- componentCount: 0,
+ mockUseSearchContext.mockReturnValue({
+ ...data,
+ totalHits: 0,
});
+
render();
expect(await screen.findByText(/you have not added any content to this library yet\./i));
});
@@ -101,7 +138,7 @@ describe('', () => {
});
it('should render components in full variant', async () => {
- mockUseLibraryComponents.mockReturnValue({
+ mockUseSearchContext.mockReturnValue({
...data,
hits: libraryComponentsMock,
isFetching: false,
@@ -117,7 +154,7 @@ describe('', () => {
});
it('should render components in preview variant', async () => {
- mockUseLibraryComponents.mockReturnValue({
+ mockUseSearchContext.mockReturnValue({
...data,
hits: libraryComponentsMock,
isFetching: false,
@@ -133,7 +170,7 @@ describe('', () => {
});
it('should call `fetchNextPage` on scroll to bottom in full variant', async () => {
- mockUseLibraryComponents.mockReturnValue({
+ mockUseSearchContext.mockReturnValue({
...data,
hits: libraryComponentsMock,
isFetching: false,
@@ -151,7 +188,7 @@ describe('', () => {
});
it('should not call `fetchNextPage` on croll to bottom in preview variant', async () => {
- mockUseLibraryComponents.mockReturnValue({
+ mockUseSearchContext.mockReturnValue({
...data,
hits: libraryComponentsMock,
isFetching: false,
@@ -169,7 +206,7 @@ describe('', () => {
});
it('should render content and loading when fetching next page', async () => {
- mockUseLibraryComponents.mockReturnValue({
+ mockUseSearchContext.mockReturnValue({
...data,
hits: libraryComponentsMock,
isFetching: true,
diff --git a/src/library-authoring/components/LibraryComponents.tsx b/src/library-authoring/components/LibraryComponents.tsx
index 1d416a2cc3..372dbb7226 100644
--- a/src/library-authoring/components/LibraryComponents.tsx
+++ b/src/library-authoring/components/LibraryComponents.tsx
@@ -8,10 +8,7 @@ import { ComponentCard, ComponentCardLoading } from './ComponentCard';
type LibraryComponentsProps = {
libraryId: string,
- filter: {
- searchKeywords: string,
- },
- variant: string,
+ variant: 'full' | 'preview',
};
/**
diff --git a/src/library-authoring/data/apiHook.ts b/src/library-authoring/data/apiHook.ts
index a6d9b13f5c..b7e6b92cf8 100644
--- a/src/library-authoring/data/apiHook.ts
+++ b/src/library-authoring/data/apiHook.ts
@@ -1,4 +1,3 @@
-import React from 'react';
import { useQuery } from '@tanstack/react-query';
import { getContentLibrary, getLibraryBlockTypes } from './api';
diff --git a/src/search-modal/manager/SearchManager.ts b/src/search-modal/manager/SearchManager.ts
index 7b1204fa7d..eed267b46c 100644
--- a/src/search-modal/manager/SearchManager.ts
+++ b/src/search-modal/manager/SearchManager.ts
@@ -42,8 +42,8 @@ export const SearchContextProvider: React.FC<{
closeSearchModal?: () => void,
}> = ({ extraFilter, ...props }) => {
const [searchKeywords, setSearchKeywords] = React.useState('');
- const [blockTypesFilter, setBlockTypesFilter] = React.useState(/** type {string[]} */([]));
- const [tagsFilter, setTagsFilter] = React.useState(/** type {string[]} */([]));
+ const [blockTypesFilter, setBlockTypesFilter] = React.useState([]);
+ const [tagsFilter, setTagsFilter] = React.useState([]);
const canClearFilters = blockTypesFilter.length > 0 || tagsFilter.length > 0;
const clearFilters = React.useCallback(() => {