Skip to content

Commit 0c909ac

Browse files
committed
test: add tests
1 parent ab0e8eb commit 0c909ac

22 files changed

Lines changed: 280 additions & 23 deletions

src/CourseAuthoringContext.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ export const CourseAuthoringProvider = ({
5757

5858
const getUnitUrl = (locator: string) => {
5959
if (getConfig().ENABLE_UNIT_PAGE === 'true' && waffleFlags.useNewUnitPage) {
60+
// instanbul ignore next
6061
return `/course/${courseId}/container/${locator}`;
6162
}
6263
return `${getConfig().STUDIO_BASE_URL}/container/${locator}`;
@@ -68,6 +69,7 @@ export const CourseAuthoringProvider = ({
6869
const openUnitPage = (locator: string) => {
6970
const url = getUnitUrl(locator);
7071
if (getConfig().ENABLE_UNIT_PAGE === 'true' && waffleFlags.useNewUnitPage) {
72+
// instanbul ignore next
7173
navigate(url);
7274
} else {
7375
window.location.assign(url);
@@ -89,6 +91,7 @@ export const CourseAuthoringProvider = ({
8991
const handleAddSectionFromLibrary = useCreateCourseBlock(async (locator) => {
9092
try {
9193
const data = await getCourseItem(locator);
94+
// instanbul ignore next
9295
// Page should scroll to newly added section.
9396
data.shouldScroll = true;
9497
dispatch(addSection(data));
Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
import { courseOutlineIndexMock } from '@src/course-outline/__mocks__';
2+
import { initializeMocks, render, screen } from '@src/testUtils';
3+
import { userEvent } from '@testing-library/user-event';
4+
import mockResult from '@src/library-authoring/__mocks__/library-search.json';
5+
import { mockContentSearchConfig, mockSearchResult } from '@src/search-manager/data/api.mock';
6+
import {
7+
mockContentLibrary,
8+
mockGetCollectionMetadata,
9+
mockGetContainerMetadata,
10+
mockGetContentLibraryV2List,
11+
mockLibraryBlockMetadata,
12+
} from '@src/library-authoring/data/api.mocks';
13+
import { AddSidebar } from './AddSidebar';
14+
15+
const handleNewSectionSubmit = jest.fn();
16+
const handleNewSubsectionSubmit = jest.fn();
17+
const handleNewUnitSubmit = jest.fn();
18+
const handleAddSectionFromLibrary = { mutateAsync: jest.fn() };
19+
const handleAddSubsectionFromLibrary = { mutateAsync: jest.fn() };
20+
const handleAddUnitFromLibrary = { mutateAsync: jest.fn() };
21+
mockContentSearchConfig.applyMock();
22+
mockContentLibrary.applyMock();
23+
mockGetCollectionMetadata.applyMock();
24+
mockGetContentLibraryV2List.applyMock();
25+
mockLibraryBlockMetadata.applyMock();
26+
mockGetContainerMetadata.applyMock();
27+
28+
jest.mock('@src/CourseAuthoringContext', () => ({
29+
useCourseAuthoringContext: () => ({
30+
courseId: 5,
31+
courseUsageKey: 'course-usage-key',
32+
courseDetails: { name: 'Test course' },
33+
handleNewSubsectionSubmit,
34+
handleNewUnitSubmit,
35+
handleNewSectionSubmit,
36+
handleAddSectionFromLibrary,
37+
handleAddSubsectionFromLibrary,
38+
handleAddUnitFromLibrary,
39+
}),
40+
}));
41+
42+
jest.mock('react-redux', () => ({
43+
...jest.requireActual('react-redux'),
44+
useSelector: jest.fn().mockReturnValue(courseOutlineIndexMock.courseStructure.childInfo.children),
45+
}));
46+
47+
jest.mock('@src/studio-home/hooks', () => ({
48+
useStudioHome: () => ({
49+
isLoadingPage: false,
50+
isFailedLoadingPage: false,
51+
librariesV2Enabled: true,
52+
}),
53+
}));
54+
55+
const renderComponent = () => render(<AddSidebar />);
56+
const searchResult = {
57+
...mockResult,
58+
results: [
59+
{
60+
...mockResult.results[0],
61+
hits: [
62+
...mockResult.results[0].hits.slice(16, 19),
63+
],
64+
},
65+
{
66+
...mockResult.results[1],
67+
},
68+
],
69+
};
70+
71+
describe('AddSidebar component', () => {
72+
beforeEach(() => {
73+
initializeMocks();
74+
mockSearchResult({
75+
...searchResult,
76+
});
77+
});
78+
79+
it('renders the AddSidebar component without any errors', async () => {
80+
const user = userEvent.setup();
81+
renderComponent();
82+
// Check add new tab content
83+
expect(await screen.findByRole('button', { name: 'Section' })).toBeInTheDocument();
84+
expect(await screen.findByRole('button', { name: 'Subsection' })).toBeInTheDocument();
85+
expect(await screen.findByRole('button', { name: 'Unit' })).toBeInTheDocument();
86+
expect(await screen.findByRole('tab', { name: 'Add New' })).toBeInTheDocument();
87+
const existingTab = await screen.findByRole('tab', { name: 'Add Existing' });
88+
expect(existingTab).toBeInTheDocument();
89+
90+
// Check existing tab content
91+
await user.click(existingTab);
92+
expect(await screen.findByRole('button', { name: 'All libraries' })).toBeInTheDocument();
93+
expect(await screen.findByRole('button', { name: 'See more' })).toBeInTheDocument();
94+
expect(await screen.findByRole('search')).toBeInTheDocument();
95+
});
96+
97+
it('show hide extra filters', async () => {
98+
const user = userEvent.setup();
99+
renderComponent();
100+
const existingTab = await screen.findByRole('tab', { name: 'Add Existing' });
101+
expect(existingTab).toBeInTheDocument();
102+
await user.click(existingTab);
103+
const toggleBtn = await screen.findByRole('button', { name: 'See more' });
104+
expect(toggleBtn).toBeInTheDocument();
105+
// hidden by default
106+
expect(screen.queryByRole('button', { name: 'Type' })).not.toBeInTheDocument();
107+
// show when clicked
108+
await user.click(toggleBtn);
109+
expect(await screen.findByRole('button', { name: 'Type' })).toBeInTheDocument();
110+
expect(await screen.findByRole('button', { name: 'Tags' })).toBeInTheDocument();
111+
expect(await screen.findByTitle('Sort search results')).toBeInTheDocument();
112+
});
113+
114+
it('calls appropriate handlers on new button click', async () => {
115+
const user = userEvent.setup();
116+
renderComponent();
117+
118+
// Validate handler for adding section, subsection and unit
119+
const section = await screen.findByRole('button', { name: 'Section' });
120+
const subsection = await screen.findByRole('button', { name: 'Subsection' });
121+
const unit = await screen.findByRole('button', { name: 'Unit' });
122+
await user.click(section);
123+
expect(handleNewSectionSubmit).toHaveBeenCalled();
124+
await user.click(subsection);
125+
expect(handleNewSubsectionSubmit).toHaveBeenCalled();
126+
await user.click(unit);
127+
expect(handleNewUnitSubmit).toHaveBeenCalled();
128+
});
129+
130+
it('calls appropriate handlers on existing button click', async () => {
131+
const user = userEvent.setup();
132+
const sectionList = courseOutlineIndexMock.courseStructure.childInfo.children;
133+
const lastSection = sectionList[3];
134+
const lastSubsection = lastSection.childInfo.children[0];
135+
renderComponent();
136+
// Check existing tab content
137+
await user.click(await screen.findByRole('tab', { name: 'Add Existing' }));
138+
139+
// Validate handler for adding section, subsection and unit
140+
const addBtns = await screen.findAllByRole('button', { name: 'Add' });
141+
// first one is unit as per mock
142+
await user.click(addBtns[0]);
143+
expect(handleAddUnitFromLibrary.mutateAsync).toHaveBeenCalledWith({
144+
type: 'library_v2',
145+
category: 'vertical',
146+
parentLocator: lastSubsection.id,
147+
libraryContentKey: searchResult.results[0].hits[0].usage_key,
148+
});
149+
// second one is subsection as per mock
150+
await user.click(addBtns[1]);
151+
expect(handleAddSubsectionFromLibrary.mutateAsync).toHaveBeenCalledWith({
152+
type: 'library_v2',
153+
category: 'sequential',
154+
parentLocator: lastSection.id,
155+
libraryContentKey: searchResult.results[0].hits[1].usage_key,
156+
});
157+
// third one is section as per mock
158+
await user.click(addBtns[2]);
159+
expect(handleAddSectionFromLibrary.mutateAsync).toHaveBeenCalledWith({
160+
type: 'library_v2',
161+
category: 'chapter',
162+
parentLocator: 'course-usage-key',
163+
libraryContentKey: searchResult.results[0].hits[2].usage_key,
164+
});
165+
});
166+
});

src/course-outline/outline-sidebar/AddSidebar.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ type AddContentButtonProps = {
3131
const getLastEditableParent = (blockList: Array<XBlock>) => {
3232
let index = 1;
3333
let lastBlock: XBlock;
34-
while (index < blockList.length) {
34+
while (index <= blockList.length) {
3535
lastBlock = blockList[blockList.length - index];
3636
if (lastBlock.actions.childAddable) {
3737
return lastBlock;

src/library-authoring/EmptyStates.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ export const NoComponents = ({
1717
addBtnText?: MessageDescriptor;
1818
handleBtnClick: () => void;
1919
}) => {
20-
const { readOnly } = useLibraryContext();
20+
const { readOnly } = useLibraryContext(false);
2121

2222
return (
2323
<Stack direction="horizontal" gap={3} className="mt-6 justify-content-center">

src/library-authoring/__mocks__/library-search.json

Lines changed: 89 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -525,13 +525,101 @@
525525
"published": {
526526
"display_name": "Published Test Unit"
527527
}
528+
},
529+
{
530+
"display_name": "Test subsection",
531+
"block_id": "test-subsection-9284e2",
532+
"id": "lctAximTESTunittest-subsection-9284e2-a9a4386e",
533+
"type": "library_container",
534+
"breadcrumbs": [
535+
{
536+
"display_name": "Test Library"
537+
}
538+
],
539+
"created": 1742221203.895054,
540+
"modified": 1742221203.895054,
541+
"usage_key": "lct:org:lib:subsection:test-unit-9a207",
542+
"block_type": "subsection",
543+
"context_key": "lib:Axim:TEST",
544+
"org": "Axim",
545+
"access_id": 15,
546+
"num_children": 0,
547+
"_formatted": {
548+
"display_name": "Test subsection",
549+
"block_id": "test-subsection-9284e2",
550+
"id": "lctAximTESTunittest-subsection-9284e2-a9a4386e",
551+
"type": "library_container",
552+
"breadcrumbs": [
553+
{
554+
"display_name": "Test Library"
555+
}
556+
],
557+
"created": "1742221203.895054",
558+
"modified": "1742221203.895054",
559+
"usage_key": "lct:org:lib:unit:test-subsection-9a207",
560+
"block_type": "subsection",
561+
"context_key": "lib:Axim:TEST",
562+
"org": "Axim",
563+
"access_id": "15",
564+
"num_children": "0",
565+
"published": {
566+
"display_name": "Published Test subsection"
567+
}
568+
},
569+
"published": {
570+
"display_name": "Published Test subsection"
571+
}
572+
},
573+
{
574+
"display_name": "Test section",
575+
"block_id": "test-section-9284e2",
576+
"id": "lctAximTESTunittest-section-9284e2-a9a4386e",
577+
"type": "library_container",
578+
"breadcrumbs": [
579+
{
580+
"display_name": "Test Library"
581+
}
582+
],
583+
"created": 1742221203.895054,
584+
"modified": 1742221203.895054,
585+
"usage_key": "lct:org:lib:section:test-unit-9a207",
586+
"block_type": "section",
587+
"context_key": "lib:Axim:TEST",
588+
"org": "Axim",
589+
"access_id": 15,
590+
"num_children": 0,
591+
"_formatted": {
592+
"display_name": "Test section",
593+
"block_id": "test-section-9284e2",
594+
"id": "lctAximTESTunittest-section-9284e2-a9a4386e",
595+
"type": "library_container",
596+
"breadcrumbs": [
597+
{
598+
"display_name": "Test Library"
599+
}
600+
],
601+
"created": "1742221203.895054",
602+
"modified": "1742221203.895054",
603+
"usage_key": "lct:org:lib:unit:test-section-9a207",
604+
"block_type": "section",
605+
"context_key": "lib:Axim:TEST",
606+
"org": "Axim",
607+
"access_id": "15",
608+
"num_children": "0",
609+
"published": {
610+
"display_name": "Published Test section"
611+
}
612+
},
613+
"published": {
614+
"display_name": "Published Test section"
615+
}
528616
}
529617
],
530618
"query": "",
531619
"processingTimeMs": 1,
532620
"limit": 20,
533621
"offset": 0,
534-
"estimatedTotalHits": 10
622+
"estimatedTotalHits": 19
535623
},
536624
{
537625
"indexUid": "studio_content",

src/library-authoring/collections/LibraryCollectionPage.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ const HeaderActions = () => {
4040
const intl = useIntl();
4141

4242
const { componentPickerMode } = useComponentPickerContext();
43-
const { collectionId, readOnly } = useLibraryContext();
43+
const { collectionId, readOnly } = useLibraryContext(false);
4444
const {
4545
closeLibrarySidebar,
4646
openAddContentSidebar,

src/library-authoring/component-info/ComponentAdvancedAssets.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import messages from './messages';
1818

1919
export const ComponentAdvancedAssets: React.FC<Record<never, never>> = () => {
2020
const intl = useIntl();
21-
const { readOnly } = useLibraryContext();
21+
const { readOnly } = useLibraryContext(false);
2222
const { sidebarItemInfo } = useSidebarContext();
2323

2424
const usageKey = sidebarItemInfo?.id;

src/library-authoring/component-info/ComponentAdvancedInfo.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import { ComponentAdvancedAssets } from './ComponentAdvancedAssets';
2222

2323
const ComponentAdvancedInfoInner: React.FC<Record<never, never>> = () => {
2424
const intl = useIntl();
25-
const { readOnly, showOnlyPublished } = useLibraryContext();
25+
const { readOnly, showOnlyPublished } = useLibraryContext(false);
2626
const { sidebarItemInfo } = useSidebarContext();
2727

2828
const usageKey = sidebarItemInfo?.id;

src/library-authoring/component-info/ComponentInfo.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ const ComponentActions = ({
107107
hasUnpublishedChanges: boolean,
108108
}) => {
109109
const intl = useIntl();
110-
const { openComponentEditor } = useLibraryContext();
110+
const { openComponentEditor } = useLibraryContext(false);
111111
const [isPublisherOpen, openPublisher, closePublisher] = useToggle(false);
112112
const canEdit = canEditComponent(componentId);
113113

@@ -151,7 +151,7 @@ const ComponentActions = ({
151151

152152
const ComponentInfo = () => {
153153
const intl = useIntl();
154-
const { readOnly } = useLibraryContext();
154+
const { readOnly } = useLibraryContext(false);
155155

156156
const {
157157
sidebarTab,

src/library-authoring/component-info/ComponentInfoHeader.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import messages from './messages';
1111
const ComponentInfoHeader = () => {
1212
const intl = useIntl();
1313

14-
const { readOnly, showOnlyPublished } = useLibraryContext();
14+
const { readOnly, showOnlyPublished } = useLibraryContext(false);
1515
const { sidebarItemInfo } = useSidebarContext();
1616

1717
const usageKey = sidebarItemInfo?.id;

0 commit comments

Comments
 (0)