|
| 1 | +import userEvent from '@testing-library/user-event'; |
| 2 | +import { |
| 3 | + initializeMocks, |
| 4 | + render, |
| 5 | + screen, |
| 6 | + fireEvent, |
| 7 | + waitFor, |
| 8 | +} from '@src/testUtils'; |
| 9 | +import { initialState } from '@src/studio-home/factories/mockApiResponses'; |
| 10 | +import { RequestStatus } from '@src/data/constants'; |
| 11 | +import { type DeprecatedReduxState } from '@src/store'; |
| 12 | +import studioHomeMock from '@src/studio-home/__mocks__/studioHomeMock'; |
| 13 | +import { mockGetMigrationInfo } from '@src/studio-home/data/api.mocks'; |
| 14 | +import { getCourseDetailsApiUrl } from '@src/course-outline/data/api'; |
| 15 | +import { ImportStepperModal } from './ImportStepperModal'; |
| 16 | + |
| 17 | +let axiosMock; |
| 18 | +mockGetMigrationInfo.applyMock(); |
| 19 | +type StudioHomeState = DeprecatedReduxState['studioHome']; |
| 20 | + |
| 21 | +const libraryKey = 'lib:org:lib1'; |
| 22 | +const mockOnClose = jest.fn(); |
| 23 | +const numPages = 1; |
| 24 | +const coursesCount = studioHomeMock.courses.length; |
| 25 | + |
| 26 | +const renderComponent = (studioHomeState: Partial<StudioHomeState> = {}) => { |
| 27 | + // Generate a custom initial state based on studioHomeCoursesRequestParams |
| 28 | + const customInitialState: Partial<DeprecatedReduxState> = { |
| 29 | + ...initialState, |
| 30 | + studioHome: { |
| 31 | + ...initialState.studioHome, |
| 32 | + studioHomeData: { |
| 33 | + courses: studioHomeMock.courses, |
| 34 | + numPages, |
| 35 | + coursesCount, |
| 36 | + }, |
| 37 | + loadingStatuses: { |
| 38 | + ...initialState.studioHome.loadingStatuses, |
| 39 | + courseLoadingStatus: RequestStatus.SUCCESSFUL, |
| 40 | + }, |
| 41 | + ...studioHomeState, |
| 42 | + }, |
| 43 | + }; |
| 44 | + |
| 45 | + // Initialize the store with the custom initial state |
| 46 | + const newMocks = initializeMocks({ initialState: customInitialState }); |
| 47 | + const store = newMocks.reduxStore; |
| 48 | + axiosMock = newMocks.axiosMock; |
| 49 | + |
| 50 | + return { |
| 51 | + ...render( |
| 52 | + <ImportStepperModal |
| 53 | + libraryKey={libraryKey} |
| 54 | + onClose={mockOnClose} |
| 55 | + isOpen |
| 56 | + />, |
| 57 | + ), |
| 58 | + store, |
| 59 | + }; |
| 60 | +}; |
| 61 | + |
| 62 | +describe('<ImportStepperModal />', () => { |
| 63 | + it('should render correctly', async () => { |
| 64 | + renderComponent(); |
| 65 | + // Renders the stepper header |
| 66 | + expect(await screen.findByText('Select Course')).toBeInTheDocument(); |
| 67 | + expect(await screen.findByText('Review Import Details')).toBeInTheDocument(); |
| 68 | + |
| 69 | + // Renders the course list and previously imported chip |
| 70 | + expect(screen.getByText(/managing risk in the information age/i)).toBeInTheDocument(); |
| 71 | + expect(screen.getByText(/run 0/i)).toBeInTheDocument(); |
| 72 | + expect(await screen.findByText('Previously Imported')).toBeInTheDocument(); |
| 73 | + |
| 74 | + // Renders cancel and next step buttons |
| 75 | + expect(screen.getByRole('button', { name: /cancel/i })).toBeInTheDocument(); |
| 76 | + expect(screen.getByRole('button', { name: /next step/i })).toBeInTheDocument(); |
| 77 | + }); |
| 78 | + |
| 79 | + it('should cancel the import', async () => { |
| 80 | + const user = userEvent.setup(); |
| 81 | + renderComponent(); |
| 82 | + |
| 83 | + const cancelButon = await screen.findByRole('button', { name: /cancel/i }); |
| 84 | + await user.click(cancelButon); |
| 85 | + |
| 86 | + expect(mockOnClose).toHaveBeenCalled(); |
| 87 | + }); |
| 88 | + |
| 89 | + it('should go to review import details step', async () => { |
| 90 | + const user = userEvent.setup(); |
| 91 | + renderComponent(); |
| 92 | + axiosMock.onGet(getCourseDetailsApiUrl('course-v1:HarvardX+123+2023')).reply(200, { |
| 93 | + courseId: 'course-v1:HarvardX+123+2023', |
| 94 | + title: 'Managing Risk in the Information Age', |
| 95 | + subtitle: '', |
| 96 | + org: 'HarvardX', |
| 97 | + description: 'This is a test course', |
| 98 | + }); |
| 99 | + |
| 100 | + const nextButton = await screen.findByRole('button', { name: /next step/i }); |
| 101 | + expect(nextButton).toBeDisabled(); |
| 102 | + |
| 103 | + // Select a course |
| 104 | + const courseCard = screen.getAllByRole('radio')[0]; |
| 105 | + await fireEvent.click(courseCard); |
| 106 | + expect(courseCard).toBeChecked(); |
| 107 | + |
| 108 | + // Click next |
| 109 | + expect(nextButton).toBeEnabled(); |
| 110 | + await user.click(nextButton); |
| 111 | + |
| 112 | + await waitFor(async () => expect(await screen.findByText( |
| 113 | + /managing risk in the information age is being analyzed for review prior to import/i, |
| 114 | + )).toBeInTheDocument()); |
| 115 | + |
| 116 | + expect(screen.getByText('Analysis Summary')).toBeInTheDocument(); |
| 117 | + expect(screen.getByText('Import Details')).toBeInTheDocument(); |
| 118 | + // The import details is loading |
| 119 | + expect(screen.getByText('The selected course is being analyzed for import and review')).toBeInTheDocument(); |
| 120 | + }); |
| 121 | + |
| 122 | + it('the course should remain selected on back', async () => { |
| 123 | + const user = userEvent.setup(); |
| 124 | + renderComponent(); |
| 125 | + |
| 126 | + const nextButton = await screen.findByRole('button', { name: /next step/i }); |
| 127 | + expect(nextButton).toBeDisabled(); |
| 128 | + |
| 129 | + // Select a course |
| 130 | + const courseCard = screen.getAllByRole('radio')[0]; |
| 131 | + await fireEvent.click(courseCard); |
| 132 | + expect(courseCard).toBeChecked(); |
| 133 | + |
| 134 | + // Click next |
| 135 | + expect(nextButton).toBeEnabled(); |
| 136 | + await user.click(nextButton); |
| 137 | + |
| 138 | + const backButton = await screen.getByRole('button', { name: /back/i }); |
| 139 | + await user.click(backButton); |
| 140 | + |
| 141 | + expect(screen.getByText(/managing risk in the information age/i)).toBeInTheDocument(); |
| 142 | + expect(courseCard).toBeChecked(); |
| 143 | + }); |
| 144 | +}); |
0 commit comments