Skip to content

Commit 4ef64a8

Browse files
committed
test: add test for collections card
1 parent 7810833 commit 4ef64a8

File tree

6 files changed

+333
-21
lines changed

6 files changed

+333
-21
lines changed

src/library-authoring/LibraryAuthoringPage.test.tsx

Lines changed: 42 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,12 @@ const returnEmptyResult = (_url, req) => {
4848
// We have to replace the query (search keywords) in the mock results with the actual query,
4949
// because otherwise we may have an inconsistent state that causes more queries and unexpected results.
5050
mockEmptyResult.results[0].query = query;
51+
mockEmptyResult.results[2].query = query;
5152
// And fake the required '_formatted' fields; it contains the highlighting <mark>...</mark> around matched words
5253
// eslint-disable-next-line no-underscore-dangle, no-param-reassign
5354
mockEmptyResult.results[0]?.hits.forEach((hit) => { hit._formatted = { ...hit }; });
55+
// eslint-disable-next-line no-underscore-dangle, no-param-reassign
56+
mockEmptyResult.results[2]?.hits.forEach((hit) => { hit._formatted = { ...hit }; });
5457
return mockEmptyResult;
5558
};
5659

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

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

221228
// "Recently Modified" header + sort shown
222229
expect(getAllByText('Recently Modified').length).toEqual(2);
223-
expect(getByText('Collections (0)')).toBeInTheDocument();
230+
expect(getByText('Collections (6)')).toBeInTheDocument();
224231
expect(getByText('Components (6)')).toBeInTheDocument();
225232
expect((await findAllByText('Test HTML Block'))[0]).toBeInTheDocument();
226233

227234
// Navigate to the components tab
228235
fireEvent.click(getByRole('tab', { name: 'Components' }));
229236
// "Recently Modified" default sort shown
230237
expect(getAllByText('Recently Modified').length).toEqual(1);
231-
expect(queryByText('Collections (0)')).not.toBeInTheDocument();
238+
expect(queryByText('Collections (6)')).not.toBeInTheDocument();
232239
expect(queryByText('Components (6)')).not.toBeInTheDocument();
233240

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

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

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

257-
const { findByText, getByText, findAllByText } = render(<RootWrapper />);
263+
const {
264+
findByText, findAllByText, getByText, getByRole,
265+
} = render(<RootWrapper />);
258266

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

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

272+
fireEvent.click(getByRole('tab', { name: 'Collections' }));
273+
expect(getByText('You have not added any collection to this library yet.')).toBeInTheDocument();
274+
275+
fireEvent.click(getByRole('tab', { name: 'Home' }));
264276
expect(getByText('You have not added any content to this library yet.')).toBeInTheDocument();
265277
});
266278

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

347+
// Navigate to the collections tab
348+
fireEvent.click(getByRole('tab', { name: 'Collections' }));
349+
expect(getByText('No matching collections found in this library.')).toBeInTheDocument();
350+
335351
// Go back to Home tab
336352
// This step is necessary to avoid the url change leak to other tests
337353
fireEvent.click(getByRole('tab', { name: 'Home' }));
@@ -395,7 +411,7 @@ describe('<LibraryAuthoringPage />', () => {
395411
expect(screen.queryByText('(Never Published)')).not.toBeInTheDocument();
396412
});
397413

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

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

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

417-
// Clicking on "View All" button should navigate to the Components tab
418-
fireEvent.click(getByText('View All'));
433+
// Clicking on first "View All" button should navigate to the Collections tab
434+
fireEvent.click(getAllByText('View All')[0]);
435+
// "Recently Modified" default sort shown
436+
expect(getAllByText('Recently Modified').length).toEqual(1);
437+
expect(queryByText('Collections (6)')).not.toBeInTheDocument();
438+
expect(queryByText('Components (6)')).not.toBeInTheDocument();
439+
expect(getByText('Collection 1')).toBeInTheDocument();
440+
441+
fireEvent.click(getByRole('tab', { name: 'Home' }));
442+
// Clicking on second "View All" button should navigate to the Components tab
443+
fireEvent.click(getAllByText('View All')[1]);
419444
// "Recently Modified" default sort shown
420445
expect(getAllByText('Recently Modified').length).toEqual(1);
421-
expect(queryByText('Collections (0)')).not.toBeInTheDocument();
446+
expect(queryByText('Collections (6)')).not.toBeInTheDocument();
422447
expect(queryByText('Components (6)')).not.toBeInTheDocument();
423448
expect(getAllByText('Test HTML Block')[0]).toBeInTheDocument();
424449

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

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

448473
// "Recently Modified" header + sort shown
449474
await waitFor(() => { expect(getAllByText('Recently Modified').length).toEqual(2); });
450-
expect(getByText('Collections (0)')).toBeInTheDocument();
475+
expect(getByText('Collections (2)')).toBeInTheDocument();
451476
expect(getByText('Components (2)')).toBeInTheDocument();
452477
expect(getAllByText('Test HTML Block')[0]).toBeInTheDocument();
453478
expect(queryByText('You have not added any content to this library yet.')).not.toBeInTheDocument();
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import React from 'react';
2+
import { AppProvider } from '@edx/frontend-platform/react';
3+
import { initializeMockApp } from '@edx/frontend-platform';
4+
import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
5+
import { IntlProvider } from '@edx/frontend-platform/i18n';
6+
import { render } from '@testing-library/react';
7+
import MockAdapter from 'axios-mock-adapter';
8+
import type { Store } from 'redux';
9+
10+
import { ToastProvider } from '../../generic/toast-context';
11+
import { type CollectionHit } from '../../search-manager';
12+
import initializeStore from '../../store';
13+
import CollectionCard from './CollectionCard';
14+
15+
let store: Store;
16+
let axiosMock: MockAdapter;
17+
18+
const CollectionHitSample: CollectionHit = {
19+
id: '1',
20+
type: 'collection',
21+
contextKey: 'lb:org1:Demo_Course',
22+
org: 'org1',
23+
breadcrumbs: [{ displayName: 'Demo Lib' }],
24+
displayName: 'Collection Display Name',
25+
description: 'Collection description',
26+
formatted: {
27+
displayName: 'Collection Display Formated Name',
28+
description: 'Collection description',
29+
},
30+
created: 1722434322294,
31+
modified: 1722434322294,
32+
accessId: 1,
33+
tags: {},
34+
};
35+
36+
const RootWrapper = () => (
37+
<AppProvider store={store}>
38+
<IntlProvider locale="en">
39+
<ToastProvider>
40+
<CollectionCard
41+
collectionHit={CollectionHitSample}
42+
/>
43+
</ToastProvider>
44+
</IntlProvider>
45+
</AppProvider>
46+
);
47+
48+
describe('<CollectionCard />', () => {
49+
beforeEach(() => {
50+
initializeMockApp({
51+
authenticatedUser: {
52+
userId: 3,
53+
username: 'abc123',
54+
administrator: true,
55+
roles: [],
56+
},
57+
});
58+
store = initializeStore();
59+
60+
axiosMock = new MockAdapter(getAuthenticatedHttpClient());
61+
});
62+
63+
afterEach(() => {
64+
jest.clearAllMocks();
65+
axiosMock.restore();
66+
});
67+
68+
it('should render the card with title and description', () => {
69+
const { getByText } = render(<RootWrapper />);
70+
71+
expect(getByText('Collection Display Formated Name')).toBeInTheDocument();
72+
expect(getByText('Collection description')).toBeInTheDocument();
73+
});
74+
});

src/search-manager/data/api.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ export interface CollectionHit {
139139
* - After that is the name and usage key of any parent Section/Subsection/Unit/etc.
140140
*/
141141
breadcrumbs: Array<{ displayName: string }>;
142-
// tags: ContentHitTags;
142+
tags: ContentHitTags;
143143
/** Same fields with <mark>...</mark> highlights */
144144
created: number;
145145
modified: number;
@@ -193,6 +193,8 @@ export async function fetchSearchResults({
193193
totalHits: number,
194194
blockTypes: Record<string, number>,
195195
problemTypes: Record<string, number>,
196+
collectionHits: CollectionHit[],
197+
totalCollectionHits: number,
196198
}> {
197199
const queries: MultiSearchQuery[] = [];
198200

@@ -274,12 +276,12 @@ export async function fetchSearchResults({
274276

275277
const { results } = await client.multiSearch(({ queries }));
276278
return {
277-
hits: results[0].hits.map(formatSearchHit),
279+
hits: results[0].hits.map(formatSearchHit) as ContentHit[],
278280
totalHits: results[0].totalHits ?? results[0].estimatedTotalHits ?? results[0].hits.length,
279281
blockTypes: results[1].facetDistribution?.block_type ?? {},
280282
problemTypes: results[1].facetDistribution?.['content.problem_types'] ?? {},
281283
nextOffset: results[0].hits.length === limit || results[2].hits.length === limit ? offset + limit : undefined,
282-
collectionHits: results[2].hits.map(formatSearchHit),
284+
collectionHits: results[2].hits.map(formatSearchHit) as CollectionHit[],
283285
totalCollectionHits: results[2].totalHits ?? results[2].estimatedTotalHits ?? results[2].hits.length,
284286
};
285287
}

src/search-manager/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,4 @@ export { default as SearchSortWidget } from './SearchSortWidget';
88
export { default as Stats } from './Stats';
99
export { HIGHLIGHT_PRE_TAG, HIGHLIGHT_POST_TAG } from './data/api';
1010

11-
export type { ContentHit } from './data/api';
11+
export type { CollectionHit, ContentHit } from './data/api';

src/search-modal/__mocks__/empty-search-result.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,15 @@
2222
"block_type": {}
2323
},
2424
"facetStats": {}
25+
},
26+
{
27+
"indexUid": "studio",
28+
"hits": [],
29+
"query": "noresult",
30+
"processingTimeMs": 0,
31+
"limit": 20,
32+
"offset": 0,
33+
"estimatedTotalHits": 0
2534
}
2635
]
2736
}

0 commit comments

Comments
 (0)