Skip to content

Commit 2ea4465

Browse files
committed
test: add test for collections card
1 parent 8a2e598 commit 2ea4465

File tree

6 files changed

+334
-19
lines changed

6 files changed

+334
-19
lines changed

src/library-authoring/LibraryAuthoringPage.test.tsx

Lines changed: 43 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,12 @@ const returnEmptyResult = (_url, req) => {
2727
// We have to replace the query (search keywords) in the mock results with the actual query,
2828
// because otherwise we may have an inconsistent state that causes more queries and unexpected results.
2929
mockEmptyResult.results[0].query = query;
30+
mockEmptyResult.results[2].query = query;
3031
// And fake the required '_formatted' fields; it contains the highlighting <mark>...</mark> around matched words
3132
// eslint-disable-next-line no-underscore-dangle, no-param-reassign
3233
mockEmptyResult.results[0]?.hits.forEach((hit) => { hit._formatted = { ...hit }; });
34+
// eslint-disable-next-line no-underscore-dangle, no-param-reassign
35+
mockEmptyResult.results[2]?.hits.forEach((hit) => { hit._formatted = { ...hit }; });
3336
return mockEmptyResult;
3437
};
3538

@@ -47,10 +50,14 @@ const returnLowNumberResults = (_url, req) => {
4750
newMockResult.results[0].query = query;
4851
// Limit number of results to just 2
4952
newMockResult.results[0].hits = mockResult.results[0]?.hits.slice(0, 2);
53+
newMockResult.results[2].hits = mockResult.results[2]?.hits.slice(0, 2);
5054
newMockResult.results[0].estimatedTotalHits = 2;
55+
newMockResult.results[2].estimatedTotalHits = 2;
5156
// And fake the required '_formatted' fields; it contains the highlighting <mark>...</mark> around matched words
5257
// eslint-disable-next-line no-underscore-dangle, no-param-reassign
5358
newMockResult.results[0]?.hits.forEach((hit) => { hit._formatted = { ...hit }; });
59+
// eslint-disable-next-line no-underscore-dangle, no-param-reassign
60+
newMockResult.results[2]?.hits.forEach((hit) => { hit._formatted = { ...hit }; });
5461
return newMockResult;
5562
};
5663

@@ -132,42 +139,46 @@ describe('<LibraryAuthoringPage />', () => {
132139

133140
// "Recently Modified" header + sort shown
134141
expect(getAllByText('Recently Modified').length).toEqual(2);
135-
expect(getByText('Collections (0)')).toBeInTheDocument();
142+
expect(getByText('Collections (6)')).toBeInTheDocument();
136143
expect(getByText('Components (10)')).toBeInTheDocument();
137144
expect((await findAllByText('Introduction to Testing'))[0]).toBeInTheDocument();
138145

139146
// Navigate to the components tab
140147
fireEvent.click(getByRole('tab', { name: 'Components' }));
141148
// "Recently Modified" default sort shown
142149
expect(getAllByText('Recently Modified').length).toEqual(1);
143-
expect(queryByText('Collections (0)')).not.toBeInTheDocument();
150+
expect(queryByText('Collections (6)')).not.toBeInTheDocument();
144151
expect(queryByText('Components (10)')).not.toBeInTheDocument();
145152

146153
// Navigate to the collections tab
147154
fireEvent.click(getByRole('tab', { name: 'Collections' }));
148155
// "Recently Modified" default sort shown
149156
expect(getAllByText('Recently Modified').length).toEqual(1);
150-
expect(queryByText('Collections (0)')).not.toBeInTheDocument();
157+
expect(queryByText('Collections (6)')).not.toBeInTheDocument();
151158
expect(queryByText('Components (10)')).not.toBeInTheDocument();
152159
expect(queryByText('There are 10 components in this library')).not.toBeInTheDocument();
153-
expect(getByText('Coming soon!')).toBeInTheDocument();
160+
expect((await findAllByText('Collection 1'))[0]).toBeInTheDocument();
154161

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

164-
it('shows a library without components', async () => {
171+
it('shows a library without components and collections', async () => {
165172
fetchMock.post(searchEndpoint, returnEmptyResult, { overwriteRoutes: true });
166173
const doc = await renderLibraryPage();
167174

168175
expect(await doc.findByText('Content library')).toBeInTheDocument();
169176
expect((await doc.findAllByText(libraryTitle))[0]).toBeInTheDocument();
170177

178+
fireEvent.click(doc.getByRole('tab', { name: 'Collections' }));
179+
expect(doc.getByText('You have not added any collection to this library yet.')).toBeInTheDocument();
180+
181+
fireEvent.click(doc.getByRole('tab', { name: 'Home' }));
171182
expect(doc.getByText('You have not added any content to this library yet.')).toBeInTheDocument();
172183
});
173184

@@ -214,6 +225,14 @@ describe('<LibraryAuthoringPage />', () => {
214225
// Navigate to the components tab
215226
fireEvent.click(doc.getByRole('tab', { name: 'Components' }));
216227
expect(doc.getByText('No matching components found in this library.')).toBeInTheDocument();
228+
229+
// Navigate to the collections tab
230+
fireEvent.click(doc.getByRole('tab', { name: 'Collections' }));
231+
expect(getByText('No matching collections found in this library.')).toBeInTheDocument();
232+
233+
// Go back to Home tab
234+
// This step is necessary to avoid the url change leak to other tests
235+
fireEvent.click(doc.getByRole('tab', { name: 'Home' }));
217236
});
218237

219238
it('should open and close new content sidebar', async () => {
@@ -277,28 +296,37 @@ describe('<LibraryAuthoringPage />', () => {
277296
expect(doc.queryByText('(Never Published)')).not.toBeInTheDocument();
278297
});
279298

280-
it('show the "View All" button when viewing library with many components', async () => {
299+
it('show the "View All" button when viewing library with many components and collections', async () => {
281300
const doc = await renderLibraryPage();
282301

283302
expect(doc.getByText('Content library')).toBeInTheDocument();
284303
expect((await doc.findAllByText(libraryTitle))[0]).toBeInTheDocument();
285304

286305
// "Recently Modified" header + sort shown
287306
await waitFor(() => { expect(doc.getAllByText('Recently Modified').length).toEqual(2); });
288-
expect(doc.getByText('Collections (0)')).toBeInTheDocument();
307+
expect(doc.getByText('Collections (6)')).toBeInTheDocument();
289308
expect(doc.getByText('Components (10)')).toBeInTheDocument();
290309
expect(doc.getAllByText('Introduction to Testing')[0]).toBeInTheDocument();
291310
expect(doc.queryByText('You have not added any content to this library yet.')).not.toBeInTheDocument();
292311

293-
// There should only be one "View All" button, since the Components count
312+
// There should be two "View All" button, since the Components and Collections count
294313
// are above the preview limit (4)
295-
expect(doc.getByText('View All')).toBeInTheDocument();
314+
expect(doc.getAllByText('View All').length).toEqual(2);
296315

297-
// Clicking on "View All" button should navigate to the Components tab
298-
fireEvent.click(doc.getByText('View All'));
316+
// Clicking on first "View All" button should navigate to the Collections tab
317+
fireEvent.click(doc.getAllByText('View All')[0]);
318+
// "Recently Modified" default sort shown
319+
expect(doc.getAllByText('Recently Modified').length).toEqual(1);
320+
expect(doc.queryByText('Collections (6)')).not.toBeInTheDocument();
321+
expect(doc.queryByText('Components (10)')).not.toBeInTheDocument();
322+
expect(doc.getByText('Collection 1')).toBeInTheDocument();
323+
324+
fireEvent.click(doc.getByRole('tab', { name: 'Home' }));
325+
// Clicking on second "View All" button should navigate to the Components tab
326+
fireEvent.click(doc.getAllByText('View All')[1]);
299327
// "Recently Modified" default sort shown
300328
expect(doc.getAllByText('Recently Modified').length).toEqual(1);
301-
expect(doc.queryByText('Collections (0)')).not.toBeInTheDocument();
329+
expect(doc.queryByText('Collections (6)')).not.toBeInTheDocument();
302330
expect(doc.queryByText('Components (10)')).not.toBeInTheDocument();
303331
expect(doc.getAllByText('Introduction to Testing')[0]).toBeInTheDocument();
304332

@@ -307,7 +335,7 @@ describe('<LibraryAuthoringPage />', () => {
307335
fireEvent.click(doc.getByRole('tab', { name: 'Home' }));
308336
// "Recently Modified" header + sort shown
309337
expect(doc.getAllByText('Recently Modified').length).toEqual(2);
310-
expect(doc.getByText('Collections (0)')).toBeInTheDocument();
338+
expect(doc.getByText('Collections (6)')).toBeInTheDocument();
311339
expect(doc.getByText('Components (10)')).toBeInTheDocument();
312340
});
313341

@@ -320,7 +348,7 @@ describe('<LibraryAuthoringPage />', () => {
320348

321349
// "Recently Modified" header + sort shown
322350
await waitFor(() => { expect(doc.getAllByText('Recently Modified').length).toEqual(2); });
323-
expect(doc.getByText('Collections (0)')).toBeInTheDocument();
351+
expect(doc.getByText('Collections (2)')).toBeInTheDocument();
324352
expect(doc.getByText('Components (2)')).toBeInTheDocument();
325353
expect(doc.getAllByText('Introduction to Testing')[0]).toBeInTheDocument();
326354
expect(doc.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)