Skip to content

Commit 42f26e7

Browse files
authored
refactor: Move redux to react query in course checklist (#2870)
1 parent 01ddf0d commit 42f26e7

9 files changed

Lines changed: 230 additions & 232 deletions

File tree

Lines changed: 14 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,12 @@
11
import {
22
render,
3-
waitFor,
43
screen,
54
initializeMocks,
65
} from '@src/testUtils';
76
import '@testing-library/jest-dom';
87
import { getConfig, setConfig } from '@edx/frontend-platform';
98
import { CourseAuthoringProvider } from '@src/CourseAuthoringContext';
10-
import { RequestStatus } from '../data/constants';
11-
import { executeThunk } from '../utils';
129
import { getCourseLaunchApiUrl, getCourseBestPracticesApiUrl } from './data/api';
13-
import { fetchCourseLaunchQuery, fetchCourseBestPracticesQuery } from './data/thunks';
1410
import {
1511
courseId,
1612
generateCourseLaunchData,
@@ -20,7 +16,6 @@ import messages from './messages';
2016
import CourseChecklist from './index';
2117

2218
let axiosMock;
23-
let store;
2419

2520
const renderComponent = () => {
2621
render(
@@ -33,62 +28,46 @@ const renderComponent = () => {
3328
const mockStore = async (status) => {
3429
axiosMock.onGet(getCourseLaunchApiUrl(courseId)).reply(status, generateCourseLaunchData());
3530
axiosMock.onGet(getCourseBestPracticesApiUrl(courseId)).reply(status, generateCourseBestPracticesData());
36-
37-
await executeThunk(fetchCourseLaunchQuery(courseId), store.dispatch);
38-
await executeThunk(fetchCourseBestPracticesQuery(courseId), store.dispatch);
3931
};
4032

4133
describe('CourseChecklistPage', () => {
4234
beforeEach(async () => {
4335
const mocks = initializeMocks();
44-
store = mocks.reduxStore;
4536
axiosMock = mocks.axiosMock;
4637
});
4738
describe('renders', () => {
4839
describe('if enable_quality prop is true', () => {
4940
it('two checklist components ', async () => {
50-
renderComponent();
5141
await mockStore(200);
42+
renderComponent();
5243

5344
expect(screen.getByText(messages.launchChecklistLabel.defaultMessage)).toBeVisible();
5445

5546
expect(screen.getByText(messages.bestPracticesChecklistLabel.defaultMessage)).toBeVisible();
5647
});
5748

5849
describe('an aria-live region with', () => {
59-
it('an aria-live region', () => {
50+
it('an aria-live region', async () => {
6051
renderComponent();
61-
const ariaLiveRegion = screen.getByRole('status');
52+
const ariaLiveRegion = await screen.findByRole('status');
6253

6354
expect(ariaLiveRegion).toBeDefined();
6455

6556
expect(ariaLiveRegion.classList.contains('sr-only')).toBe(true);
6657
});
6758

6859
it('correct content when the launch checklist has loaded', async () => {
69-
renderComponent();
7060
await mockStore(404);
71-
await waitFor(() => {
72-
const { launchChecklistStatus } = store.getState().courseChecklist.loadingStatus;
73-
74-
expect(launchChecklistStatus).not.toEqual(RequestStatus.SUCCESSFUL);
75-
76-
expect(screen.getByText(messages.launchChecklistDoneLoadingLabel.defaultMessage)).toBeInTheDocument();
77-
});
61+
renderComponent();
62+
expect(await screen.findByText(messages.launchChecklistDoneLoadingLabel.defaultMessage)).toBeInTheDocument();
7863
});
7964

8065
it('correct content when the best practices checklist is loading', async () => {
81-
renderComponent();
8266
await mockStore(404);
83-
await waitFor(() => {
84-
const { bestPracticeChecklistStatus } = store.getState().courseChecklist.loadingStatus;
85-
86-
expect(bestPracticeChecklistStatus).not.toEqual(RequestStatus.IN_PROGRESS);
87-
88-
expect(
89-
screen.getByText(messages.bestPracticesChecklistDoneLoadingLabel.defaultMessage),
90-
).toBeInTheDocument();
91-
});
67+
renderComponent();
68+
expect(
69+
await screen.findByText(messages.bestPracticesChecklistDoneLoadingLabel.defaultMessage),
70+
).toBeInTheDocument();
9271
});
9372
});
9473
});
@@ -111,27 +90,15 @@ describe('CourseChecklistPage', () => {
11190

11291
describe('an aria-live region with', () => {
11392
it('correct content when the launch checklist has loaded', async () => {
114-
renderComponent();
11593
await mockStore(404);
116-
await waitFor(() => {
117-
const { launchChecklistStatus } = store.getState().courseChecklist.loadingStatus;
118-
119-
expect(launchChecklistStatus).not.toEqual(RequestStatus.SUCCESSFUL);
120-
121-
expect(screen.getByText(messages.launchChecklistDoneLoadingLabel.defaultMessage)).toBeInTheDocument();
122-
});
94+
renderComponent();
95+
expect(await screen.findByText(messages.launchChecklistDoneLoadingLabel.defaultMessage)).toBeInTheDocument();
12396
});
12497

12598
it('correct content when the best practices checklist is loading', async () => {
126-
renderComponent();
12799
await mockStore(404);
128-
await waitFor(() => {
129-
const { bestPracticeChecklistStatus } = store.getState().courseChecklist.loadingStatus;
130-
131-
expect(bestPracticeChecklistStatus).not.toEqual(RequestStatus.IN_PROGRESS);
132-
133-
expect(screen.queryByText(messages.bestPracticesChecklistDoneLoadingLabel.defaultMessage)).toBeNull();
134-
});
100+
renderComponent();
101+
expect(screen.queryByText(messages.bestPracticesChecklistDoneLoadingLabel.defaultMessage)).toBeNull();
135102
});
136103
});
137104
});
@@ -144,11 +111,7 @@ describe('CourseChecklistPage', () => {
144111

145112
renderComponent();
146113

147-
await waitFor(() => {
148-
const { launchChecklistStatus } = store.getState().courseChecklist.loadingStatus;
149-
expect(launchChecklistStatus).toEqual(RequestStatus.DENIED);
150-
expect(screen.getByRole('alert')).toBeInTheDocument();
151-
});
114+
expect(await screen.findByRole('alert')).toBeInTheDocument();
152115
});
153116
});
154117
});

src/course-checklist/CourseChecklist.tsx

Lines changed: 14 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,33 @@
1-
import { useEffect } from 'react';
21
import { getConfig } from '@edx/frontend-platform';
32
import { useIntl } from '@edx/frontend-platform/i18n';
43
import { Helmet } from 'react-helmet';
5-
import { useDispatch, useSelector } from 'react-redux';
64
import { Container, Stack } from '@openedx/paragon';
75
import { useCourseAuthoringContext } from '@src/CourseAuthoringContext';
8-
import { DeprecatedReduxState } from '@src/store';
96

107
import SubHeader from '../generic/sub-header/SubHeader';
118
import messages from './messages';
129
import AriaLiveRegion from './AriaLiveRegion';
13-
import { RequestStatus } from '../data/constants';
1410
import ChecklistSection from './ChecklistSection';
15-
import { fetchCourseLaunchQuery, fetchCourseBestPracticesQuery } from './data/thunks';
1611
import ConnectionErrorAlert from '../generic/ConnectionErrorAlert';
12+
import { useCourseBestPractices, useCourseLaunch } from './data/apiHooks';
1713

1814
const CourseChecklist = () => {
1915
const intl = useIntl();
20-
const dispatch = useDispatch();
2116
const { courseId, courseDetails } = useCourseAuthoringContext();
2217
const enableQuality = getConfig().ENABLE_CHECKLIST_QUALITY === 'true';
2318

24-
useEffect(() => {
25-
dispatch(fetchCourseLaunchQuery({ courseId }));
26-
dispatch(fetchCourseBestPracticesQuery({ courseId }));
27-
}, [courseId]);
28-
2919
const {
30-
loadingStatus,
31-
launchData,
32-
bestPracticeData,
33-
} = useSelector((state: DeprecatedReduxState) => state.courseChecklist);
20+
data: bestPracticeData,
21+
isPending: isPendingBestPacticeData,
22+
} = useCourseBestPractices({ courseId });
3423

35-
const { bestPracticeChecklistLoadingStatus, launchChecklistLoadingStatus, launchChecklistStatus } = loadingStatus;
24+
const {
25+
data: launchData,
26+
isPending: isPendingLaunchData,
27+
failureReason: launchError,
28+
} = useCourseLaunch({ courseId });
3629

37-
const isCourseLaunchChecklistLoading = bestPracticeChecklistLoadingStatus === RequestStatus.IN_PROGRESS;
38-
const isCourseBestPracticeChecklistLoading = launchChecklistLoadingStatus === RequestStatus.IN_PROGRESS;
39-
const isLoadingDenied = launchChecklistStatus === RequestStatus.DENIED;
30+
const isLoadingDenied = launchError?.response?.status === 403;
4031

4132
if (isLoadingDenied) {
4233
return (
@@ -64,8 +55,8 @@ const CourseChecklist = () => {
6455
/>
6556
<AriaLiveRegion
6657
{...{
67-
isCourseLaunchChecklistLoading,
68-
isCourseBestPracticeChecklistLoading,
58+
isCourseLaunchChecklistLoading: isPendingLaunchData,
59+
isCourseBestPracticeChecklistLoading: isPendingBestPacticeData,
6960
enableQuality,
7061
}}
7162
/>
@@ -75,15 +66,15 @@ const CourseChecklist = () => {
7566
dataHeading={intl.formatMessage(messages.launchChecklistLabel)}
7667
data={launchData}
7768
idPrefix="launchChecklist"
78-
isLoading={isCourseLaunchChecklistLoading}
69+
isLoading={isPendingLaunchData}
7970
/>
8071
{enableQuality && (
8172
<ChecklistSection
8273
courseId={courseId}
8374
dataHeading={intl.formatMessage(messages.bestPracticesChecklistLabel)}
8475
data={bestPracticeData}
8576
idPrefix="bestPracticesChecklist"
86-
isLoading={isCourseBestPracticeChecklistLoading}
77+
isLoading={isPendingBestPacticeData}
8778
/>
8879
)}
8980
</Stack>

src/course-checklist/data/api.js

Lines changed: 0 additions & 64 deletions
This file was deleted.
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import { initializeMocks } from '@src/testUtils';
2+
import {
3+
CourseBestPracticesRequest,
4+
CourseLaunchRequest,
5+
getCourseBestPractices,
6+
getCourseBestPracticesApiUrl,
7+
getCourseLaunch,
8+
getCourseLaunchApiUrl,
9+
} from './api';
10+
11+
let axiosMock;
12+
13+
describe('course checklist data API', () => {
14+
beforeEach(() => {
15+
({ axiosMock } = initializeMocks());
16+
});
17+
18+
describe('getCourseBestPractices', () => {
19+
it('should fetch course best practices', async () => {
20+
const params: CourseBestPracticesRequest = {
21+
courseId: 'course-v1:edX+DemoX+Demo_Course',
22+
excludeGraded: true,
23+
all: true,
24+
};
25+
const url = getCourseBestPracticesApiUrl(params);
26+
axiosMock.onGet(url).reply(200, { is_self_paced: false });
27+
28+
const result = await getCourseBestPractices(params);
29+
30+
expect(axiosMock.history.get[0].url).toEqual(url);
31+
expect(result).toEqual({ isSelfPaced: false });
32+
});
33+
});
34+
35+
describe('getCourseLaunch', () => {
36+
it('should fetch course launch validation', async () => {
37+
const params: CourseLaunchRequest = {
38+
courseId: 'course-v1:edX+DemoX+Demo_Course',
39+
gradedOnly: true,
40+
validateOras: true,
41+
all: true,
42+
};
43+
const url = getCourseLaunchApiUrl(params);
44+
axiosMock.onGet(url).reply(200, { is_self_paced: false });
45+
46+
const result = await getCourseLaunch(params);
47+
48+
expect(axiosMock.history.get[0].url).toEqual(url);
49+
expect(result).toEqual({ isSelfPaced: false });
50+
});
51+
});
52+
});

0 commit comments

Comments
 (0)