1- import { getConfig , setConfig } from '@edx/frontend-platform' ;
1+ import { getConfig } from '@edx/frontend-platform' ;
22import { cloneDeep } from 'lodash' ;
33import { closestCorners } from '@dnd-kit/core' ;
44import { logError } from '@edx/frontend-platform/logging' ;
@@ -9,8 +9,7 @@ import { executeThunk } from '@src/utils';
99import configureModalMessages from '@src/generic/configure-modal/messages' ;
1010import pasteButtonMessages from '@src/generic/clipboard/paste-component/messages' ;
1111import { 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' ;
1413import { getDownstreamApiUrl } from '@src/generic/unlink-modal/data/api' ;
1514import { CourseAuthoringProvider } from '@src/CourseAuthoringContext' ;
1615import {
@@ -74,9 +73,8 @@ let axiosMock: import('axios-mock-adapter/types');
7473let store ;
7574const mockPathname = '/foo-bar' ;
7675const courseId = '123' ;
77- const getContainerKey = jest . fn ( ) . mockReturnValue ( 'lct:org:lib:unit:1' ) ;
78- const getContainerType = jest . fn ( ) . mockReturnValue ( 'unit' ) ;
7976const clearSelection = jest . fn ( ) ;
77+ const startCurrentFlow = jest . fn ( ) ;
8078let selectedContainerId : string | undefined ;
8179let courseOutlineIndexMock = cloneDeep ( originalCourseOutlineIndexMock ) ;
8280
@@ -85,6 +83,7 @@ jest.mock('@src/course-outline/outline-sidebar/OutlineSidebarContext', () => ({
8583 ...jest . requireActual ( '@src/course-outline/outline-sidebar/OutlineSidebarContext' ) ,
8684 useOutlineSidebarContext : ( ) => ( {
8785 ...jest . requireActual ( '@src/course-outline/outline-sidebar/OutlineSidebarContext' ) . useOutlineSidebarContext ( ) ,
86+ startCurrentFlow,
8887 clearSelection,
8988 selectedContainerState : ( ( ) => ( selectedContainerId ? { currentId : selectedContainerId } : undefined ) ) ( ) ,
9089 } ) ,
@@ -109,24 +108,6 @@ jest.mock('./data/api', () => ({
109108 getTagsCount : ( ) => jest . fn ( ) . mockResolvedValue ( { } ) ,
110109} ) ) ;
111110
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-
130111jest . mock ( '@edx/frontend-platform/logging' , ( ) => ( {
131112 logError : jest . fn ( ) ,
132113} ) ) ;
@@ -480,118 +461,66 @@ describe('<CourseOutline />', () => {
480461 } ) ;
481462
482463 it ( 'adds a unit from library correctly' , async ( ) => {
483- getContainerKey . mockReturnValue ( 'lct:org:lib:unit:1' ) ;
484- getContainerKey . mockReturnValue ( 'unit' ) ;
464+ const user = userEvent . setup ( ) ;
485465 renderComponent ( ) ;
486466 const [ sectionElement ] = await screen . findAllByTestId ( 'section-card' ) ;
487467 const [ subsectionElement ] = await within ( sectionElement ) . findAllByTestId ( 'subsection-card' ) ;
488468 const units = await within ( subsectionElement ) . findAllByTestId ( 'unit-card' ) ;
489469 expect ( units . length ) . toBe ( 1 ) ;
490470
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-
498471 const addUnitFromLibraryButton = within ( subsectionElement ) . getByRole ( 'button' , {
499472 name : / u s e u n i t f r o m l i b r a r y / i,
500473 } ) ;
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 ) ) ;
474+ await user . click ( addUnitFromLibraryButton ) ;
508475
476+ // Start the add existing unit flow
509477 const [ section ] = courseOutlineIndexMock . courseStructure . childInfo . children ;
510478 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- } ) ) ;
479+ expect ( startCurrentFlow ) . toHaveBeenCalledWith ( {
480+ flowType : ContainerType . Unit ,
481+ parentLocator : subsection . id ,
482+ grandParentLocator : section . id ,
518483 } ) ;
519484 } ) ;
520485
521486 it ( 'adds a subsection from library correctly' , async ( ) => {
522- getContainerKey . mockReturnValue ( 'lct:org:lib:subsection:1' ) ;
523- getContainerKey . mockReturnValue ( 'subsection' ) ;
487+ const user = userEvent . setup ( ) ;
524488 renderComponent ( ) ;
525489 const [ sectionElement ] = await screen . findAllByTestId ( 'section-card' ) ;
526490 const subsections = await within ( sectionElement ) . findAllByTestId ( 'subsection-card' ) ;
527491 expect ( subsections . length ) . toBe ( 2 ) ;
528492
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-
536493 const addSubsectionFromLibraryButton = within ( sectionElement ) . getByRole ( 'button' , {
537494 name : / u s e s u b s e c t i o n f r o m l i b r a r y / i,
538495 } ) ;
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 ) ) ;
496+ await user . click ( addSubsectionFromLibraryButton ) ;
546497
498+ // Start the add existing subsection flow
547499 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- } ) ) ;
500+ expect ( startCurrentFlow ) . toHaveBeenCalledWith ( {
501+ flowType : ContainerType . Subsection ,
502+ parentLocator : section . id ,
503+ grandParentLocator : undefined ,
555504 } ) ;
556505 } ) ;
557506
558507 it ( 'adds a section from library correctly' , async ( ) => {
559508 const user = userEvent . setup ( ) ;
560- getContainerKey . mockReturnValue ( 'lct:org:lib:section:1' ) ;
561- getContainerKey . mockReturnValue ( 'section' ) ;
562509 renderComponent ( ) ;
563510 const sections = await screen . findAllByTestId ( 'section-card' ) ;
564511 expect ( sections . length ) . toBe ( 4 ) ;
565512
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-
576513 const addSectionFromLibraryButton = await screen . findByRole ( 'button' , {
577514 name : / u s e s e c t i o n f r o m l i b r a r y / i,
578515 } ) ;
579516 await user . click ( addSectionFromLibraryButton ) ;
580517
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- } ) ) ;
518+ // Start the add existing section flow
519+ const courseBlockId = courseOutlineIndexMock . courseStructure . id ;
520+ expect ( startCurrentFlow ) . toHaveBeenCalledWith ( {
521+ flowType : ContainerType . Section ,
522+ parentLocator : courseBlockId ,
523+ grandParentLocator : undefined ,
595524 } ) ;
596525 } ) ;
597526
@@ -820,6 +749,7 @@ describe('<CourseOutline />', () => {
820749 } ) ;
821750
822751 it ( 'check whether section, subsection and unit is deleted when corresponding delete button is clicked' , async ( ) => {
752+ const user = userEvent . setup ( ) ;
823753 renderComponent ( ) ;
824754 // get section, subsection and unit
825755 const [ section ] = courseOutlineIndexMock . courseStructure . childInfo . children ;
@@ -831,22 +761,18 @@ describe('<CourseOutline />', () => {
831761 selectedContainerId = section . id ;
832762
833763 const checkDeleteBtn = async ( item , element , elementName ) => {
834- await waitFor ( ( ) => {
835- expect ( screen . queryByText ( item . displayName ) ) . toBeInTheDocument ( ) ;
836- } ) ;
764+ expect ( within ( element ) . getByText ( item . displayName ) ) . toBeInTheDocument ( ) ;
837765
838766 axiosMock . onDelete ( getCourseItemApiUrl ( item . id ) ) . reply ( 200 ) ;
839767
840768 const menu = await within ( element ) . findByTestId ( `${ elementName } -card-header__menu-button` ) ;
841- fireEvent . click ( menu ) ;
769+ await user . click ( menu ) ;
842770 const deleteButton = await within ( element ) . findByTestId ( `${ elementName } -card-header__menu-delete-button` ) ;
843- fireEvent . click ( deleteButton ) ;
771+ await user . click ( deleteButton ) ;
844772 const confirmButton = await screen . findByRole ( 'button' , { name : 'Delete' } ) ;
845- fireEvent . click ( confirmButton ) ;
773+ await user . click ( confirmButton ) ;
846774
847- await waitFor ( ( ) => {
848- expect ( screen . queryByText ( item . displayName ) ) . not . toBeInTheDocument ( ) ;
849- } ) ;
775+ expect ( element ) . not . toBeInTheDocument ( ) ;
850776 } ;
851777
852778 // delete unit, subsection and then section in order.
@@ -2614,10 +2540,6 @@ describe('<CourseOutline />', () => {
26142540 } ) ;
26152541
26162542 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- } ) ;
26212543 renderComponent ( ) ;
26222544 const btn = await screen . findByRole ( 'button' , { name : 'Collapse all' } ) ;
26232545 expect ( btn ) . toBeInTheDocument ( ) ;
0 commit comments