Skip to content

Commit 292fc22

Browse files
committed
test: improve coverage
1 parent 17defd1 commit 292fc22

4 files changed

Lines changed: 81 additions & 9 deletions

File tree

src/library-authoring/import-course/stepper/ImportStepperPage.test.tsx

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@ import studioHomeMock from '@src/studio-home/__mocks__/studioHomeMock';
1212
import { getCourseDetailsApiUrl } from '@src/course-outline/data/api';
1313
import { LibraryProvider } from '@src/library-authoring/common/context/LibraryContext';
1414
import { mockContentLibrary, mockGetMigrationInfo } from '@src/library-authoring/data/api.mocks';
15+
import { useGetBlockTypes } from '@src/search-manager';
1516
import { ImportStepperPage } from './ImportStepperPage';
17+
import { bulkMigrateContentToLibrariesUrl } from '../../../data/api';
1618

1719
let axiosMock;
1820
mockGetMigrationInfo.applyMock();
@@ -29,6 +31,11 @@ jest.mock('react-router-dom', () => ({
2931
useNavigate: () => mockNavigate,
3032
}));
3133

34+
// Mock the useGetBlockTypes hook
35+
jest.mock('@src/search-manager', () => ({
36+
useGetBlockTypes: jest.fn().mockReturnValue({ isPending: true, data: null }),
37+
}));
38+
3239
const renderComponent = (studioHomeState: Partial<StudioHomeState> = {}) => {
3340
// Generate a custom initial state based on studioHomeCoursesRequestParams
3441
const customInitialState: Partial<DeprecatedReduxState> = {
@@ -151,4 +158,52 @@ describe('<ImportStepperModal />', () => {
151158
expect(screen.getByText(/managing risk in the information age/i)).toBeInTheDocument();
152159
expect(courseCard).toBeChecked();
153160
});
161+
162+
it('should import select course on button click', async () => {
163+
(useGetBlockTypes as jest.Mock).mockReturnValue({
164+
isPending: false,
165+
data: {
166+
chapter: 1,
167+
sequential: 2,
168+
vertical: 3,
169+
html: 5,
170+
problem: 3,
171+
},
172+
});
173+
const user = userEvent.setup();
174+
renderComponent();
175+
axiosMock.onPost(bulkMigrateContentToLibrariesUrl()).reply(200);
176+
axiosMock.onGet(getCourseDetailsApiUrl('course-v1:HarvardX+123+2023')).reply(200, {
177+
courseId: 'course-v1:HarvardX+123+2023',
178+
title: 'Managing Risk in the Information Age',
179+
subtitle: '',
180+
org: 'HarvardX',
181+
description: 'This is a test course',
182+
});
183+
184+
const nextButton = await screen.findByRole('button', { name: /next step/i });
185+
expect(nextButton).toBeDisabled();
186+
187+
// Select a course
188+
const courseCard = screen.getAllByRole('radio')[0];
189+
await user.click(courseCard);
190+
expect(courseCard).toBeChecked();
191+
192+
// Click next
193+
expect(nextButton).toBeEnabled();
194+
await user.click(nextButton);
195+
196+
expect(await screen.findByText('Analysis Summary')).toBeInTheDocument();
197+
// Analysis completed
198+
expect(await screen.findByText('Import Analysis Completed: Reimport')).toBeInTheDocument();
199+
const importBtn = await screen.findByRole('button', { name: 'Import Course' });
200+
await user.click(importBtn);
201+
202+
await waitFor(() => {
203+
expect(axiosMock.history.post.length).toBe(1);
204+
});
205+
expect(axiosMock.history.post[0].data).toBe(
206+
'{"sources":["course-v1:HarvardX+123+2023"],"target":"lib:Axim:TEST","create_collections":true,"repeat_handling_strategy":"fork","composition_level":"section"}',
207+
);
208+
});
154209
});

src/library-authoring/import-course/stepper/ImportStepperPage.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ export const ImportStepperPage = () => {
7171
const navigate = useNavigate();
7272
const [currentStep, setCurrentStep] = useState<MigrationStep>('select-course');
7373
const [selectedCourseId, setSelectedCourseId] = useState<string>();
74+
const [analysisCompleted, setAnalysisCompleted] = useState<boolean>(false);
7475
const { data: courseData } = useCourseDetails(selectedCourseId);
7576
const { libraryId, libraryData, readOnly } = useLibraryContext();
7677
const { showToast } = useContext(ToastContext);
@@ -151,7 +152,10 @@ export const ImportStepperPage = () => {
151152
eventKey="review-details"
152153
title={intl.formatMessage(messages.importCourseReviewDetailsStep)}
153154
>
154-
<ReviewImportDetails courseId={selectedCourseId} />
155+
<ReviewImportDetails
156+
markAnalysisComplete={setAnalysisCompleted}
157+
courseId={selectedCourseId}
158+
/>
155159
</Stepper.Step>
156160
</Stepper>
157161
<div className="mt-4">
@@ -176,6 +180,7 @@ export const ImportStepperPage = () => {
176180
onClick={handleImportCourse}
177181
label={intl.formatMessage(messages.importCourseButton)}
178182
variant="primary"
183+
disabled={!analysisCompleted}
179184
/>
180185
</ActionRow>
181186
)}

src/library-authoring/import-course/stepper/ReviewImportDetails.test.tsx

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import messages from '../messages';
99

1010
mockContentLibrary.applyMock();
1111
const { libraryId } = mockContentLibrary;
12+
const markAnalysisComplete = jest.fn();
1213

1314
// Mock the useCourseDetails hook
1415
jest.mock('@src/course-outline/data/apiHooks', () => ({
@@ -47,10 +48,11 @@ describe('ReviewImportDetails', () => {
4748
});
4849

4950
it('renders loading spinner when isPending is true', async () => {
50-
render(<ReviewImportDetails courseId="test-course-id" />);
51+
render(<ReviewImportDetails markAnalysisComplete={markAnalysisComplete} courseId="test-course-id" />);
5152

5253
const spinners = await screen.findAllByRole('status');
5354
spinners.every((spinner) => expect(spinner.textContent).toEqual('Loading...'));
55+
expect(markAnalysisComplete).toHaveBeenCalledWith(false);
5456
});
5557

5658
it('renders import progress status when isBlockDataPending or migrationInfoIsPending is true', async () => {
@@ -60,10 +62,11 @@ describe('ReviewImportDetails', () => {
6062
data: null,
6163
});
6264

63-
render(<ReviewImportDetails courseId="test-course-id" />);
65+
render(<ReviewImportDetails markAnalysisComplete={markAnalysisComplete} courseId="test-course-id" />);
6466

6567
expect(await screen.findByRole('alert')).toBeInTheDocument();
6668
expect(await screen.findByText(/Import Analysis in Progress/i)).toBeInTheDocument();
69+
expect(markAnalysisComplete).toHaveBeenCalledWith(false);
6770
});
6871

6972
it('renders warning when reimport', async () => {
@@ -82,7 +85,7 @@ describe('ReviewImportDetails', () => {
8285
data: { html: 1 },
8386
});
8487

85-
render(<ReviewImportDetails courseId="test-course-id" />);
88+
render(<ReviewImportDetails markAnalysisComplete={markAnalysisComplete} courseId="test-course-id" />);
8689

8790
expect(await screen.findByRole('alert')).toBeInTheDocument();
8891
expect(await screen.findByText(/Import Analysis Completed: Reimport/i)).toBeInTheDocument();
@@ -91,6 +94,7 @@ describe('ReviewImportDetails', () => {
9194
.replace('{courseName}', 'Test Course')
9295
.replace('{libraryName}', 'Library title'),
9396
)).toBeInTheDocument();
97+
expect(markAnalysisComplete).toHaveBeenCalledWith(true);
9498
});
9599

96100
it('renders warning when unsupportedBlockPercentage > 0', async () => {
@@ -110,7 +114,7 @@ describe('ReviewImportDetails', () => {
110114
},
111115
});
112116

113-
render(<ReviewImportDetails courseId="test-course-id" />);
117+
render(<ReviewImportDetails markAnalysisComplete={markAnalysisComplete} courseId="test-course-id" />);
114118

115119
expect(await screen.findByRole('alert')).toBeInTheDocument();
116120
expect(await screen.findByText(/Import Analysis Complete/i)).toBeInTheDocument();
@@ -127,6 +131,7 @@ describe('ReviewImportDetails', () => {
127131
expect(await screen.findByText('3')).toBeInTheDocument();
128132
expect(await screen.findByText('Components')).toBeInTheDocument();
129133
expect(await screen.findByText('1/2')).toBeInTheDocument();
134+
expect(markAnalysisComplete).toHaveBeenCalledWith(true);
130135
});
131136

132137
it('renders success alert when no unsupported blocks', async () => {
@@ -146,7 +151,7 @@ describe('ReviewImportDetails', () => {
146151
},
147152
});
148153

149-
render(<ReviewImportDetails courseId="test-course-id" />);
154+
render(<ReviewImportDetails markAnalysisComplete={markAnalysisComplete} courseId="test-course-id" />);
150155

151156
expect(await screen.findByRole('alert')).toBeInTheDocument();
152157
expect(await screen.findByText(
@@ -163,5 +168,6 @@ describe('ReviewImportDetails', () => {
163168
expect(await screen.findByText('3')).toBeInTheDocument();
164169
expect(await screen.findByText('Components')).toBeInTheDocument();
165170
expect(await screen.findByText('8')).toBeInTheDocument();
171+
expect(markAnalysisComplete).toHaveBeenCalledWith(true);
166172
});
167173
});

src/library-authoring/import-course/stepper/ReviewImportDetails.tsx

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { Alert, Stack } from '@openedx/paragon';
44
import { LoadingSpinner } from '@src/generic/Loading';
55
import { useCourseDetails } from '@src/course-outline/data/apiHooks';
66

7-
import { useMemo } from 'react';
7+
import { useEffect, useMemo } from 'react';
88
import { CheckCircle, Warning } from '@openedx/paragon/icons';
99
import { useLibraryContext } from '@src/library-authoring/common/context/LibraryContext';
1010
import { useMigrationInfo } from '@src/library-authoring/data/apiHooks';
@@ -14,9 +14,11 @@ import messages from '../messages';
1414

1515
interface Props {
1616
courseId?: string;
17+
markAnalysisComplete: (analysisCompleted: boolean) => void;
1718
}
1819

19-
interface BannerProps extends Props {
20+
interface BannerProps {
21+
courseId?: string;
2022
isBlockDataPending?: boolean;
2123
unsupportedBlockPercentage: number;
2224
}
@@ -112,11 +114,15 @@ const Banner = ({ courseId, isBlockDataPending, unsupportedBlockPercentage }: Ba
112114
);
113115
};
114116

115-
export const ReviewImportDetails = ({ courseId }: Props) => {
117+
export const ReviewImportDetails = ({ courseId, markAnalysisComplete }: Props) => {
116118
const { data: blockTypes, isPending: isBlockDataPending } = useGetBlockTypes([
117119
`context_key = "${courseId}"`,
118120
]);
119121

122+
useEffect(() => {
123+
markAnalysisComplete(!isBlockDataPending);
124+
}, [isBlockDataPending]);
125+
120126
const totalUnsupportedBlocks = useMemo(() => {
121127
if (!blockTypes) {
122128
return 0;

0 commit comments

Comments
 (0)