Skip to content

Commit

Permalink
test: add test for collections card
Browse files Browse the repository at this point in the history
  • Loading branch information
navinkarkera committed Sep 6, 2024
1 parent 7810833 commit 4ef64a8
Show file tree
Hide file tree
Showing 6 changed files with 333 additions and 21 deletions.
59 changes: 42 additions & 17 deletions src/library-authoring/LibraryAuthoringPage.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,12 @@ const returnEmptyResult = (_url, req) => {
// 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;
mockEmptyResult.results[2].query = query;
// And fake the required '_formatted' fields; it contains the highlighting <mark>...</mark> around matched words
// eslint-disable-next-line no-underscore-dangle, no-param-reassign
mockEmptyResult.results[0]?.hits.forEach((hit) => { hit._formatted = { ...hit }; });
// eslint-disable-next-line no-underscore-dangle, no-param-reassign
mockEmptyResult.results[2]?.hits.forEach((hit) => { hit._formatted = { ...hit }; });
return mockEmptyResult;
};

Expand All @@ -68,10 +71,14 @@ const returnLowNumberResults = (_url, req) => {
newMockResult.results[0].query = query;
// Limit number of results to just 2
newMockResult.results[0].hits = mockResult.results[0]?.hits.slice(0, 2);
newMockResult.results[2].hits = mockResult.results[2]?.hits.slice(0, 2);
newMockResult.results[0].estimatedTotalHits = 2;
newMockResult.results[2].estimatedTotalHits = 2;
// And fake the required '_formatted' fields; it contains the highlighting <mark>...</mark> around matched words
// eslint-disable-next-line no-underscore-dangle, no-param-reassign
newMockResult.results[0]?.hits.forEach((hit) => { hit._formatted = { ...hit }; });
// eslint-disable-next-line no-underscore-dangle, no-param-reassign
newMockResult.results[2]?.hits.forEach((hit) => { hit._formatted = { ...hit }; });
return newMockResult;
};

Expand Down Expand Up @@ -220,47 +227,52 @@ describe('<LibraryAuthoringPage />', () => {

// "Recently Modified" header + sort shown
expect(getAllByText('Recently Modified').length).toEqual(2);
expect(getByText('Collections (0)')).toBeInTheDocument();
expect(getByText('Collections (6)')).toBeInTheDocument();
expect(getByText('Components (6)')).toBeInTheDocument();
expect((await findAllByText('Test HTML Block'))[0]).toBeInTheDocument();

// Navigate to the components tab
fireEvent.click(getByRole('tab', { name: 'Components' }));
// "Recently Modified" default sort shown
expect(getAllByText('Recently Modified').length).toEqual(1);
expect(queryByText('Collections (0)')).not.toBeInTheDocument();
expect(queryByText('Collections (6)')).not.toBeInTheDocument();
expect(queryByText('Components (6)')).not.toBeInTheDocument();

// Navigate to the collections tab
fireEvent.click(getByRole('tab', { name: 'Collections' }));
// "Recently Modified" default sort shown
expect(getAllByText('Recently Modified').length).toEqual(1);
expect(queryByText('Collections (0)')).not.toBeInTheDocument();
expect(queryByText('Collections (6)')).not.toBeInTheDocument();
expect(queryByText('Components (6)')).not.toBeInTheDocument();
expect(queryByText('There are 6 components in this library')).not.toBeInTheDocument();
expect(getByText('Coming soon!')).toBeInTheDocument();
expect((await findAllByText('Collection 1'))[0]).toBeInTheDocument();

// Go back to Home tab
// This step is necessary to avoid the url change leak to other tests
fireEvent.click(getByRole('tab', { name: 'Home' }));
// "Recently Modified" header + sort shown
expect(getAllByText('Recently Modified').length).toEqual(2);
expect(getByText('Collections (0)')).toBeInTheDocument();
expect(getByText('Collections (6)')).toBeInTheDocument();
expect(getByText('Components (6)')).toBeInTheDocument();
});

it('show library without components', async () => {
it('show library without components and collections', async () => {
mockUseParams.mockReturnValue({ libraryId: libraryData.id });
axiosMock.onGet(getContentLibraryApiUrl(libraryData.id)).reply(200, libraryData);
fetchMock.post(searchEndpoint, returnEmptyResult, { overwriteRoutes: true });

const { findByText, getByText, findAllByText } = render(<RootWrapper />);
const {
findByText, findAllByText, getByText, getByRole,
} = render(<RootWrapper />);

expect(await findByText('Content library')).toBeInTheDocument();
expect((await findAllByText(libraryData.title))[0]).toBeInTheDocument();

await waitFor(() => { expect(fetchMock).toHaveFetchedTimes(1, searchEndpoint, 'post'); });

fireEvent.click(getByRole('tab', { name: 'Collections' }));
expect(getByText('You have not added any collection to this library yet.')).toBeInTheDocument();

fireEvent.click(getByRole('tab', { name: 'Home' }));
expect(getByText('You have not added any content to this library yet.')).toBeInTheDocument();
});

Expand Down Expand Up @@ -332,6 +344,10 @@ describe('<LibraryAuthoringPage />', () => {
fireEvent.click(getByRole('tab', { name: 'Components' }));
expect(getByText('No matching components found in this library.')).toBeInTheDocument();

// Navigate to the collections tab
fireEvent.click(getByRole('tab', { name: 'Collections' }));
expect(getByText('No matching collections found in this library.')).toBeInTheDocument();

// Go back to Home tab
// This step is necessary to avoid the url change leak to other tests
fireEvent.click(getByRole('tab', { name: 'Home' }));
Expand Down Expand Up @@ -395,7 +411,7 @@ describe('<LibraryAuthoringPage />', () => {
expect(screen.queryByText('(Never Published)')).not.toBeInTheDocument();
});

it('show the "View All" button when viewing library with many components', async () => {
it('show the "View All" button when viewing library with many components and collections', async () => {
const {
getByRole, getByText, queryByText, getAllByText, findAllByText,
} = await renderLibraryPage();
Expand All @@ -405,20 +421,29 @@ describe('<LibraryAuthoringPage />', () => {

// "Recently Modified" header + sort shown
await waitFor(() => { expect(getAllByText('Recently Modified').length).toEqual(2); });
expect(getByText('Collections (0)')).toBeInTheDocument();
expect(getByText('Collections (6)')).toBeInTheDocument();
expect(getByText('Components (6)')).toBeInTheDocument();
expect(getAllByText('Test HTML Block')[0]).toBeInTheDocument();
expect(queryByText('You have not added any content to this library yet.')).not.toBeInTheDocument();

// There should only be one "View All" button, since the Components count
// There should be two "View All" button, since the Components and Collections count
// are above the preview limit (4)
expect(getByText('View All')).toBeInTheDocument();
expect(getAllByText('View All').length).toEqual(2);

// Clicking on "View All" button should navigate to the Components tab
fireEvent.click(getByText('View All'));
// Clicking on first "View All" button should navigate to the Collections tab
fireEvent.click(getAllByText('View All')[0]);
// "Recently Modified" default sort shown
expect(getAllByText('Recently Modified').length).toEqual(1);
expect(queryByText('Collections (6)')).not.toBeInTheDocument();
expect(queryByText('Components (6)')).not.toBeInTheDocument();
expect(getByText('Collection 1')).toBeInTheDocument();

fireEvent.click(getByRole('tab', { name: 'Home' }));
// Clicking on second "View All" button should navigate to the Components tab
fireEvent.click(getAllByText('View All')[1]);
// "Recently Modified" default sort shown
expect(getAllByText('Recently Modified').length).toEqual(1);
expect(queryByText('Collections (0)')).not.toBeInTheDocument();
expect(queryByText('Collections (6)')).not.toBeInTheDocument();
expect(queryByText('Components (6)')).not.toBeInTheDocument();
expect(getAllByText('Test HTML Block')[0]).toBeInTheDocument();

Expand All @@ -427,7 +452,7 @@ describe('<LibraryAuthoringPage />', () => {
fireEvent.click(getByRole('tab', { name: 'Home' }));
// "Recently Modified" header + sort shown
expect(getAllByText('Recently Modified').length).toEqual(2);
expect(getByText('Collections (0)')).toBeInTheDocument();
expect(getByText('Collections (6)')).toBeInTheDocument();
expect(getByText('Components (6)')).toBeInTheDocument();
});

Expand All @@ -447,7 +472,7 @@ describe('<LibraryAuthoringPage />', () => {

// "Recently Modified" header + sort shown
await waitFor(() => { expect(getAllByText('Recently Modified').length).toEqual(2); });
expect(getByText('Collections (0)')).toBeInTheDocument();
expect(getByText('Collections (2)')).toBeInTheDocument();
expect(getByText('Components (2)')).toBeInTheDocument();
expect(getAllByText('Test HTML Block')[0]).toBeInTheDocument();
expect(queryByText('You have not added any content to this library yet.')).not.toBeInTheDocument();
Expand Down
74 changes: 74 additions & 0 deletions src/library-authoring/components/CollectionCard.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
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 { render } from '@testing-library/react';
import MockAdapter from 'axios-mock-adapter';
import type { Store } from 'redux';

import { ToastProvider } from '../../generic/toast-context';
import { type CollectionHit } from '../../search-manager';
import initializeStore from '../../store';
import CollectionCard from './CollectionCard';

let store: Store;
let axiosMock: MockAdapter;

const CollectionHitSample: CollectionHit = {
id: '1',
type: 'collection',
contextKey: 'lb:org1:Demo_Course',
org: 'org1',
breadcrumbs: [{ displayName: 'Demo Lib' }],
displayName: 'Collection Display Name',
description: 'Collection description',
formatted: {
displayName: 'Collection Display Formated Name',
description: 'Collection description',
},
created: 1722434322294,
modified: 1722434322294,
accessId: 1,
tags: {},
};

const RootWrapper = () => (
<AppProvider store={store}>
<IntlProvider locale="en">
<ToastProvider>
<CollectionCard
collectionHit={CollectionHitSample}
/>
</ToastProvider>
</IntlProvider>
</AppProvider>
);

describe('<CollectionCard />', () => {
beforeEach(() => {
initializeMockApp({
authenticatedUser: {
userId: 3,
username: 'abc123',
administrator: true,
roles: [],
},
});
store = initializeStore();

axiosMock = new MockAdapter(getAuthenticatedHttpClient());
});

afterEach(() => {
jest.clearAllMocks();
axiosMock.restore();
});

it('should render the card with title and description', () => {
const { getByText } = render(<RootWrapper />);

expect(getByText('Collection Display Formated Name')).toBeInTheDocument();
expect(getByText('Collection description')).toBeInTheDocument();
});
});
8 changes: 5 additions & 3 deletions src/search-manager/data/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ export interface CollectionHit {
* - After that is the name and usage key of any parent Section/Subsection/Unit/etc.
*/
breadcrumbs: Array<{ displayName: string }>;
// tags: ContentHitTags;
tags: ContentHitTags;
/** Same fields with <mark>...</mark> highlights */
created: number;
modified: number;
Expand Down Expand Up @@ -193,6 +193,8 @@ export async function fetchSearchResults({
totalHits: number,
blockTypes: Record<string, number>,
problemTypes: Record<string, number>,
collectionHits: CollectionHit[],
totalCollectionHits: number,
}> {
const queries: MultiSearchQuery[] = [];

Expand Down Expand Up @@ -274,12 +276,12 @@ export async function fetchSearchResults({

const { results } = await client.multiSearch(({ queries }));
return {
hits: results[0].hits.map(formatSearchHit),
hits: results[0].hits.map(formatSearchHit) as ContentHit[],
totalHits: results[0].totalHits ?? results[0].estimatedTotalHits ?? results[0].hits.length,
blockTypes: results[1].facetDistribution?.block_type ?? {},
problemTypes: results[1].facetDistribution?.['content.problem_types'] ?? {},
nextOffset: results[0].hits.length === limit || results[2].hits.length === limit ? offset + limit : undefined,
collectionHits: results[2].hits.map(formatSearchHit),
collectionHits: results[2].hits.map(formatSearchHit) as CollectionHit[],
totalCollectionHits: results[2].totalHits ?? results[2].estimatedTotalHits ?? results[2].hits.length,
};
}
Expand Down
2 changes: 1 addition & 1 deletion src/search-manager/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,4 @@ export { default as SearchSortWidget } from './SearchSortWidget';
export { default as Stats } from './Stats';
export { HIGHLIGHT_PRE_TAG, HIGHLIGHT_POST_TAG } from './data/api';

export type { ContentHit } from './data/api';
export type { CollectionHit, ContentHit } from './data/api';
9 changes: 9 additions & 0 deletions src/search-modal/__mocks__/empty-search-result.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,15 @@
"block_type": {}
},
"facetStats": {}
},
{
"indexUid": "studio",
"hits": [],
"query": "noresult",
"processingTimeMs": 0,
"limit": 20,
"offset": 0,
"estimatedTotalHits": 0
}
]
}
Loading

0 comments on commit 4ef64a8

Please sign in to comment.