Skip to content

Commit 4dce4ea

Browse files
committed
feat: remove ENABLE_COURSE_OUTLINE_NEW_DESIGN
1 parent 9a416a2 commit 4dce4ea

31 files changed

Lines changed: 257 additions & 1427 deletions

src/course-outline/CourseOutline.test.tsx

Lines changed: 31 additions & 163 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { getConfig, setConfig } from '@edx/frontend-platform';
1+
import { getConfig } from '@edx/frontend-platform';
22
import { cloneDeep } from 'lodash';
33
import { closestCorners } from '@dnd-kit/core';
44
import { logError } from '@edx/frontend-platform/logging';
@@ -9,8 +9,7 @@ import { executeThunk } from '@src/utils';
99
import configureModalMessages from '@src/generic/configure-modal/messages';
1010
import pasteButtonMessages from '@src/generic/clipboard/paste-component/messages';
1111
import { getApiBaseUrl, getClipboardUrl } from '@src/generic/data/api';
12-
import { postXBlockBaseApiUrl } from '@src/course-unit/data/api';
13-
import { COMPONENT_TYPES } from '@src/generic/block-type-utils/constants';
12+
import { ContainerType } from '@src/generic/key-utils';
1413
import { getDownstreamApiUrl } from '@src/generic/unlink-modal/data/api';
1514
import { CourseAuthoringProvider } from '@src/CourseAuthoringContext';
1615
import {
@@ -53,14 +52,13 @@ import {
5352
courseSectionMock,
5453
courseSubsectionMock,
5554
} from './__mocks__';
56-
import { COURSE_BLOCK_NAMES, VIDEO_SHARING_OPTIONS } from './constants';
55+
import { COURSE_BLOCK_NAMES } from './constants';
5756
import CourseOutline from './CourseOutline';
5857

5958
import messages from './messages';
6059
import headerMessages from './header-navigations/messages';
6160
import cardHeaderMessages from './card-header/messages';
6261
import enableHighlightsModalMessages from './enable-highlights-modal/messages';
63-
import statusBarMessages from './status-bar/messages';
6462
import subsectionMessages from './subsection-card/messages';
6563
import pageAlertMessages from './page-alerts/messages';
6664
import {
@@ -74,9 +72,8 @@ let axiosMock: import('axios-mock-adapter/types');
7472
let store;
7573
const mockPathname = '/foo-bar';
7674
const courseId = '123';
77-
const getContainerKey = jest.fn().mockReturnValue('lct:org:lib:unit:1');
78-
const getContainerType = jest.fn().mockReturnValue('unit');
7975
const clearSelection = jest.fn();
76+
const startCurrentFlow = jest.fn();
8077
let selectedContainerId: string | undefined;
8178
let courseOutlineIndexMock = cloneDeep(originalCourseOutlineIndexMock);
8279

@@ -85,6 +82,7 @@ jest.mock('@src/course-outline/outline-sidebar/OutlineSidebarContext', () => ({
8582
...jest.requireActual('@src/course-outline/outline-sidebar/OutlineSidebarContext'),
8683
useOutlineSidebarContext: () => ({
8784
...jest.requireActual('@src/course-outline/outline-sidebar/OutlineSidebarContext').useOutlineSidebarContext(),
85+
startCurrentFlow,
8886
clearSelection,
8987
selectedContainerState: (() => (selectedContainerId ? { currentId: selectedContainerId } : undefined))(),
9088
}),
@@ -109,24 +107,6 @@ jest.mock('./data/api', () => ({
109107
getTagsCount: () => jest.fn().mockResolvedValue({}),
110108
}));
111109

112-
// Mock LibraryAndComponentPicker to call onComponentSelected on click
113-
jest.mock('@src/library-authoring/component-picker', () => ({
114-
LibraryAndComponentPicker: (props) => {
115-
const onClick = () => {
116-
// eslint-disable-next-line react/prop-types
117-
props.onComponentSelected({
118-
usageKey: getContainerKey(),
119-
blockType: getContainerType(),
120-
});
121-
};
122-
return (
123-
<button type="submit" onClick={onClick}>
124-
Dummy button
125-
</button>
126-
);
127-
},
128-
}));
129-
130110
jest.mock('@edx/frontend-platform/logging', () => ({
131111
logError: jest.fn(),
132112
}));
@@ -262,59 +242,6 @@ describe('<CourseOutline />', () => {
262242
expect(await findByText(messages.alertSuccessDescription.defaultMessage)).toBeInTheDocument();
263243
});
264244

265-
it('check video sharing option udpates correctly', async () => {
266-
const { findByLabelText } = renderComponent();
267-
268-
axiosMock
269-
.onPost(getCourseBlockApiUrl(courseId), {
270-
metadata: {
271-
video_sharing_options: VIDEO_SHARING_OPTIONS.allOff,
272-
},
273-
})
274-
.reply(200);
275-
const optionDropdown = await findByLabelText(statusBarMessages.videoSharingTitle.defaultMessage);
276-
await act(
277-
async () => fireEvent.change(optionDropdown, { target: { value: VIDEO_SHARING_OPTIONS.allOff } }),
278-
);
279-
280-
expect(axiosMock.history.post.length).toBe(3);
281-
expect(axiosMock.history.post[2].data).toBe(JSON.stringify({
282-
metadata: {
283-
video_sharing_options: VIDEO_SHARING_OPTIONS.allOff,
284-
},
285-
}));
286-
});
287-
288-
it('check video sharing option shows error on failure', async () => {
289-
renderComponent();
290-
291-
axiosMock
292-
.onPost(getCourseBlockApiUrl(courseId), {
293-
metadata: {
294-
video_sharing_options: VIDEO_SHARING_OPTIONS.allOff,
295-
},
296-
})
297-
.reply(500);
298-
const optionDropdown = await screen.findByLabelText(statusBarMessages.videoSharingTitle.defaultMessage);
299-
await act(
300-
async () => fireEvent.change(optionDropdown, { target: { value: VIDEO_SHARING_OPTIONS.allOff } }),
301-
);
302-
303-
expect(axiosMock.history.post.length).toBe(3);
304-
expect(axiosMock.history.post[2].data).toBe(JSON.stringify({
305-
metadata: {
306-
video_sharing_options: VIDEO_SHARING_OPTIONS.allOff,
307-
},
308-
}));
309-
310-
const alertElements = screen.queryAllByRole('alert');
311-
expect(alertElements.find(
312-
(el) => el.classList.contains('alert-content'),
313-
)).toHaveTextContent(
314-
'Unable to save changes. Please try again.',
315-
);
316-
});
317-
318245
it('render error alert after failed reindex correctly', async () => {
319246
const { findByText, findByTestId } = renderComponent();
320247

@@ -480,118 +407,66 @@ describe('<CourseOutline />', () => {
480407
});
481408

482409
it('adds a unit from library correctly', async () => {
483-
getContainerKey.mockReturnValue('lct:org:lib:unit:1');
484-
getContainerKey.mockReturnValue('unit');
410+
const user = userEvent.setup();
485411
renderComponent();
486412
const [sectionElement] = await screen.findAllByTestId('section-card');
487413
const [subsectionElement] = await within(sectionElement).findAllByTestId('subsection-card');
488414
const units = await within(subsectionElement).findAllByTestId('unit-card');
489415
expect(units.length).toBe(1);
490416

491-
axiosMock
492-
.onPost(postXBlockBaseApiUrl())
493-
.reply(200, {
494-
locator: 'block-v1:UNIX+UX1+2025_T3+type@vertical+block@vertical1e842129',
495-
parent_locator: 'parent',
496-
});
497-
498417
const addUnitFromLibraryButton = within(subsectionElement).getByRole('button', {
499418
name: /use unit from library/i,
500419
});
501-
fireEvent.click(addUnitFromLibraryButton);
502-
503-
// click dummy button to execute onComponentSelected prop.
504-
const dummyBtn = await screen.findByRole('button', { name: 'Dummy button' });
505-
fireEvent.click(dummyBtn);
506-
507-
await waitFor(() => expect(axiosMock.history.post.length).toBe(3));
420+
await user.click(addUnitFromLibraryButton);
508421

422+
// Start the add existing unit flow
509423
const [section] = courseOutlineIndexMock.courseStructure.childInfo.children;
510424
const [subsection] = section.childInfo.children;
511-
await waitFor(() => {
512-
expect(axiosMock.history.post[2].data).toBe(JSON.stringify({
513-
type: COMPONENT_TYPES.libraryV2,
514-
category: 'vertical',
515-
parent_locator: subsection.id,
516-
library_content_key: getContainerKey(),
517-
}));
425+
expect(startCurrentFlow).toHaveBeenCalledWith({
426+
flowType: ContainerType.Unit,
427+
parentLocator: subsection.id,
428+
grandParentLocator: section.id,
518429
});
519430
});
520431

521432
it('adds a subsection from library correctly', async () => {
522-
getContainerKey.mockReturnValue('lct:org:lib:subsection:1');
523-
getContainerKey.mockReturnValue('subsection');
433+
const user = userEvent.setup();
524434
renderComponent();
525435
const [sectionElement] = await screen.findAllByTestId('section-card');
526436
const subsections = await within(sectionElement).findAllByTestId('subsection-card');
527437
expect(subsections.length).toBe(2);
528438

529-
axiosMock
530-
.onPost(postXBlockBaseApiUrl())
531-
.reply(200, {
532-
locator: 'block-v1:UNIX+UX1+2025_T3+type@sequential+block@sequential45d4d95a',
533-
parent_locator: 'block-v1:UNIX+UX1+2025_T3+type@chapter+block@chaptersda1',
534-
});
535-
536439
const addSubsectionFromLibraryButton = within(sectionElement).getByRole('button', {
537440
name: /use subsection from library/i,
538441
});
539-
fireEvent.click(addSubsectionFromLibraryButton);
540-
541-
// click dummy button to execute onComponentSelected prop.
542-
const dummyBtn = await screen.findByRole('button', { name: 'Dummy button' });
543-
fireEvent.click(dummyBtn);
544-
545-
await waitFor(() => expect(axiosMock.history.post.length).toBe(3));
442+
await user.click(addSubsectionFromLibraryButton);
546443

444+
// Start the add existing subsection flow
547445
const [section] = courseOutlineIndexMock.courseStructure.childInfo.children;
548-
await waitFor(() => {
549-
expect(axiosMock.history.post[2].data).toBe(JSON.stringify({
550-
type: COMPONENT_TYPES.libraryV2,
551-
category: 'sequential',
552-
parent_locator: section.id,
553-
library_content_key: getContainerKey(),
554-
}));
446+
expect(startCurrentFlow).toHaveBeenCalledWith({
447+
flowType: ContainerType.Subsection,
448+
parentLocator: section.id,
449+
grandParentLocator: undefined,
555450
});
556451
});
557452

558453
it('adds a section from library correctly', async () => {
559454
const user = userEvent.setup();
560-
getContainerKey.mockReturnValue('lct:org:lib:section:1');
561-
getContainerKey.mockReturnValue('section');
562455
renderComponent();
563456
const sections = await screen.findAllByTestId('section-card');
564457
expect(sections.length).toBe(4);
565458

566-
axiosMock
567-
.onPost(postXBlockBaseApiUrl())
568-
.reply(200, {
569-
locator: 'block-v1:UNIX+UX1+2025_T3+type@chapter+block@chaptersdafdd',
570-
courseKey: 'course-v1:UNIX+UX1+2025_T3',
571-
});
572-
axiosMock
573-
.onGet(getXBlockApiUrl('block-v1:UNIX+UX1+2025_T3+type@chapter+block@chaptersdafdd'))
574-
.reply(200, courseSectionMock);
575-
576459
const addSectionFromLibraryButton = await screen.findByRole('button', {
577460
name: /use section from library/i,
578461
});
579462
await user.click(addSectionFromLibraryButton);
580463

581-
// click dummy button to execute onComponentSelected prop.
582-
const dummyBtn = await screen.findByRole('button', { name: 'Dummy button' });
583-
fireEvent.click(dummyBtn);
584-
585-
await waitFor(() => expect(axiosMock.history.post.length).toBe(3));
586-
587-
const courseUsageKey = courseOutlineIndexMock.courseStructure.id;
588-
await waitFor(() => {
589-
expect(axiosMock.history.post[2].data).toBe(JSON.stringify({
590-
type: COMPONENT_TYPES.libraryV2,
591-
category: 'chapter',
592-
parent_locator: courseUsageKey,
593-
library_content_key: getContainerKey(),
594-
}));
464+
// Start the add existring section flow
465+
const courseBlockId = courseOutlineIndexMock.courseStructure.id;
466+
expect(startCurrentFlow).toHaveBeenCalledWith({
467+
flowType: ContainerType.Section,
468+
parentLocator: courseBlockId,
469+
grandParentLocator: undefined,
595470
});
596471
});
597472

@@ -820,6 +695,7 @@ describe('<CourseOutline />', () => {
820695
});
821696

822697
it('check whether section, subsection and unit is deleted when corresponding delete button is clicked', async () => {
698+
const user = userEvent.setup();
823699
renderComponent();
824700
// get section, subsection and unit
825701
const [section] = courseOutlineIndexMock.courseStructure.childInfo.children;
@@ -831,22 +707,18 @@ describe('<CourseOutline />', () => {
831707
selectedContainerId = section.id;
832708

833709
const checkDeleteBtn = async (item, element, elementName) => {
834-
await waitFor(() => {
835-
expect(screen.queryByText(item.displayName)).toBeInTheDocument();
836-
});
710+
expect(within(element).getByText(item.displayName)).toBeInTheDocument();
837711

838712
axiosMock.onDelete(getCourseItemApiUrl(item.id)).reply(200);
839713

840714
const menu = await within(element).findByTestId(`${elementName}-card-header__menu-button`);
841-
fireEvent.click(menu);
715+
await user.click(menu);
842716
const deleteButton = await within(element).findByTestId(`${elementName}-card-header__menu-delete-button`);
843-
fireEvent.click(deleteButton);
717+
await user.click(deleteButton);
844718
const confirmButton = await screen.findByRole('button', { name: 'Delete' });
845-
fireEvent.click(confirmButton);
719+
await user.click(confirmButton);
846720

847-
await waitFor(() => {
848-
expect(screen.queryByText(item.displayName)).not.toBeInTheDocument();
849-
});
721+
expect(element).not.toBeInTheDocument();
850722
};
851723

852724
// delete unit, subsection and then section in order.
@@ -2614,10 +2486,6 @@ describe('<CourseOutline />', () => {
26142486
});
26152487

26162488
it('check that the new status bar and expand bar is shown when flag is set', async () => {
2617-
setConfig({
2618-
...getConfig(),
2619-
ENABLE_COURSE_OUTLINE_NEW_DESIGN: 'true',
2620-
});
26212489
renderComponent();
26222490
const btn = await screen.findByRole('button', { name: 'Collapse all' });
26232491
expect(btn).toBeInTheDocument();

0 commit comments

Comments
 (0)