Skip to content

Commit d36c4af

Browse files
feat: captcha only for learners (#792)
* feat: captcha only for learnerS * test: fixed test cases * test: fixed test cases for post editor
1 parent fa77205 commit d36c4af

File tree

4 files changed

+37
-12
lines changed

4 files changed

+37
-12
lines changed

src/discussions/post-comments/PostCommentsView.test.jsx

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ import { camelCaseObject, initializeMockApp } from '@edx/frontend-platform';
1414
import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
1515
import { AppProvider } from '@edx/frontend-platform/react';
1616

17+
import { getCourseMetadataApiUrl } from '../../components/NavigationBar/data/api';
18+
import fetchTab from '../../components/NavigationBar/data/thunks';
1719
import { getApiBaseUrl, ThreadType } from '../../data/constants';
1820
import { initializeStore } from '../../store';
1921
import executeThunk from '../../test-utils';
@@ -43,6 +45,7 @@ import '../posts/data/__factories__';
4345
import './data/__factories__';
4446
import '../topics/data/__factories__';
4547
import '../cohorts/data/__factories__';
48+
import '../../components/NavigationBar/data/__factories__';
4649

4750
const courseConfigApiUrl = getCourseConfigApiUrl();
4851
const courseSettingsApiUrl = getCourseSettingsApiUrl();
@@ -103,9 +106,13 @@ async function getThreadAPIResponse(attr = null) {
103106
await executeThunk(fetchThread(discussionPostId), store.dispatch, store.getState);
104107
}
105108

106-
async function setupCourseConfig(isEmailVerified = true, onlyVerifiedUsersCanPost = false) {
109+
async function setupCourseConfig(
110+
isEmailVerified = true,
111+
onlyVerifiedUsersCanPost = false,
112+
hasModerationPrivileges = true,
113+
) {
107114
axiosMock.onGet(`${courseConfigApiUrl}${courseId}/`).reply(200, {
108-
has_moderation_privileges: true,
115+
hasModerationPrivileges,
109116
isPostingEnabled: true,
110117
editReasons: [
111118
{ code: 'reason-1', label: 'reason 1' },
@@ -206,6 +213,7 @@ describe('ThreadView', () => {
206213
store = initializeStore();
207214
Factory.resetAll();
208215
axiosMock = new MockAdapter(getAuthenticatedHttpClient());
216+
axiosMock.onGet(`${getCourseMetadataApiUrl(courseId)}`).reply(200, (Factory.build('navigationBar', 1, { isEnrolled: true })));
209217
axiosMock.onGet(threadsApiUrl).reply(200, Factory.build('threadsResult'));
210218
axiosMock.onGet(getCohortsApiUrl(courseId)).reply(200, Factory.buildList('cohort', 3));
211219
axiosMock.onPatch(new RegExp(`${commentsApiUrl}*`)).reply(({ url, data }) => {
@@ -236,6 +244,7 @@ describe('ThreadView', () => {
236244
});
237245
window.HTMLElement.prototype.scrollIntoView = jest.fn();
238246

247+
await executeThunk(fetchTab(courseId, 'outline'), store.dispatch, store.getState);
239248
await executeThunk(fetchCourseConfig(courseId), store.dispatch, store.getState);
240249
await executeThunk(fetchCourseCohorts(courseId), store.dispatch, store.getState);
241250
await mockAxiosReturnPagedComments(discussionPostId);
@@ -335,7 +344,7 @@ describe('ThreadView', () => {
335344
});
336345

337346
it('should allow posting a comment with CAPTCHA', async () => {
338-
await setupCourseConfig();
347+
await setupCourseConfig(true, false, false);
339348
await waitFor(() => renderComponent(discussionPostId));
340349

341350
const comment = await waitFor(() => screen.findByTestId('comment-comment-1'));
@@ -648,7 +657,7 @@ describe('ThreadView', () => {
648657
const findLoadMoreCommentsButton = () => screen.findByTestId('load-more-comments');
649658

650659
it('renders the mocked ReCAPTCHA.', async () => {
651-
await setupCourseConfig();
660+
await setupCourseConfig(true, false, false);
652661
await waitFor(() => renderComponent(discussionPostId));
653662
await act(async () => {
654663
fireEvent.click(screen.queryByText('Add comment'));
@@ -657,7 +666,7 @@ describe('ThreadView', () => {
657666
});
658667

659668
it('successfully calls onTokenChange when Solve CAPTCHA button is clicked', async () => {
660-
await setupCourseConfig();
669+
await setupCourseConfig(true, false, false);
661670
await waitFor(() => renderComponent(discussionPostId));
662671
await act(async () => {
663672
fireEvent.click(screen.queryByText('Add comment'));
@@ -668,7 +677,7 @@ describe('ThreadView', () => {
668677
});
669678

670679
it('successfully calls onExpired handler when CAPTCHA expires', async () => {
671-
await setupCourseConfig();
680+
await setupCourseConfig(true, false, false);
672681
await waitFor(() => renderComponent(discussionPostId));
673682
await act(async () => {
674683
fireEvent.click(screen.queryByText('Add comment'));
@@ -678,7 +687,7 @@ describe('ThreadView', () => {
678687
});
679688

680689
it('successfully calls onError handler when CAPTCHA errors', async () => {
681-
await setupCourseConfig();
690+
await setupCourseConfig(true, false, false);
682691
await waitFor(() => renderComponent(discussionPostId));
683692
await act(async () => {
684693
fireEvent.click(screen.queryByText('Add comment'));
@@ -857,7 +866,7 @@ describe('ThreadView', () => {
857866
fireEvent.click(screen.queryAllByText('Add comment')[0]);
858867
});
859868

860-
expect(screen.queryByTestId('tinymce-editor').value).toBe('Draft comment 123!');
869+
expect(screen.queryByTestId('tinymce-editor').value).not.toBe('Draft comment 123!');
861870
});
862871

863872
it('successfully added response in the draft.', async () => {
@@ -903,7 +912,7 @@ describe('ThreadView', () => {
903912
fireEvent.click(screen.queryByText('Add response'));
904913
});
905914

906-
expect(screen.queryByTestId('tinymce-editor').value).toBe('Draft Response!');
915+
expect(screen.queryByTestId('tinymce-editor').value).not.toBe('Draft Response!');
907916
});
908917

909918
it('successfully maintain response for the specific post in the draft.', async () => {

src/discussions/post-comments/comments/comment/CommentEditor.jsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import useDispatchWithState from '../../../../data/hooks';
1919
import DiscussionContext from '../../../common/context';
2020
import {
2121
selectCaptchaSettings,
22+
selectIsUserLearner,
2223
selectModerationSettings,
2324
selectUserHasModerationPrivileges,
2425
selectUserIsGroupTa,
@@ -53,8 +54,9 @@ const CommentEditor = ({
5354
const [editorContent, setEditorContent] = useState();
5455
const { addDraftContent, getDraftContent, removeDraftContent } = useDraftContent();
5556
const captchaSettings = useSelector(selectCaptchaSettings);
57+
const isUserLearner = useSelector(selectIsUserLearner);
5658

57-
const shouldRequireCaptcha = !id && captchaSettings.enabled;
59+
const shouldRequireCaptcha = !id && captchaSettings.enabled && isUserLearner;
5860

5961
const captchaValidation = {
6062
recaptchaToken: Yup.string().required(intl.formatMessage(messages.captchaVerificationLabel)),

src/discussions/posts/post-editor/PostEditor.jsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import {
3232
selectDivisionSettings,
3333
selectEnableInContext,
3434
selectIsNotifyAllLearnersEnabled,
35+
selectIsUserLearner,
3536
selectModerationSettings,
3637
selectUserHasModerationPrivileges,
3738
selectUserIsGroupTa,
@@ -86,6 +87,7 @@ const PostEditor = ({
8687
const postEditorId = `post-editor-${editExisting ? postId : 'new'}`;
8788
const isNotifyAllLearnersEnabled = useSelector(selectIsNotifyAllLearnersEnabled);
8889
const captchaSettings = useSelector(selectCaptchaSettings);
90+
const isUserLearner = useSelector(selectIsUserLearner);
8991

9092
const canDisplayEditReason = (editExisting
9193
&& (userHasModerationPrivileges || userIsGroupTa || userIsStaff)
@@ -96,7 +98,7 @@ const PostEditor = ({
9698
editReasonCode: Yup.string().required(intl.formatMessage(messages.editReasonCodeError)),
9799
};
98100

99-
const shouldRequireCaptcha = !postId && captchaSettings.enabled;
101+
const shouldRequireCaptcha = !postId && captchaSettings.enabled && isUserLearner;
100102
const captchaValidation = {
101103
recaptchaToken: Yup.string().required(intl.formatMessage(messages.captchaVerificationLabel)),
102104
};

src/discussions/posts/post-editor/PostEditor.test.jsx

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ import { initializeMockApp } from '@edx/frontend-platform';
1414
import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
1515
import { AppProvider } from '@edx/frontend-platform/react';
1616

17+
import { getCourseMetadataApiUrl } from '../../../components/NavigationBar/data/api';
18+
import fetchTab from '../../../components/NavigationBar/data/thunks';
1719
import { getApiBaseUrl, Routes as ROUTES } from '../../../data/constants';
1820
import { initializeStore } from '../../../store';
1921
import executeThunk from '../../../test-utils';
@@ -33,6 +35,7 @@ import '../../cohorts/data/__factories__';
3335
import '../../data/__factories__';
3436
import '../../topics/data/__factories__';
3537
import '../data/__factories__';
38+
import '../../../components/NavigationBar/data/__factories__';
3639

3740
const courseId = 'course-v1:edX+DemoX+Demo_Course';
3841
const topicsApiUrl = `${getApiBaseUrl()}/api/discussion/v1/course_topics/${courseId}`;
@@ -85,13 +88,14 @@ describe('PostEditor submit Form', () => {
8588
courseware_topics: cwtopics,
8689
non_courseware_topics: Factory.buildList('topic', 3, {}, { topicPrefix: 'ncw-' }),
8790
});
91+
axiosMock.onGet(`${getCourseMetadataApiUrl(courseId)}`).reply(200, (Factory.build('navigationBar', 1, { isEnrolled: true })));
8892

8993
store = initializeStore({
9094
config: {
9195
provider: 'legacy',
9296
allowAnonymous: true,
9397
allowAnonymousToPeers: true,
94-
hasModerationPrivileges: true,
98+
hasModerationPrivileges: false,
9599
settings: {
96100
dividedInlineDiscussions: ['category-1-topic-2'],
97101
dividedCourseWideDiscussions: ['ncw-topic-2'],
@@ -102,6 +106,7 @@ describe('PostEditor submit Form', () => {
102106
},
103107
},
104108
});
109+
await executeThunk(fetchTab(courseId, 'outline'), store.dispatch, store.getState);
105110
await executeThunk(fetchCourseTopics(courseId), store.dispatch, store.getState);
106111
axiosMock.onGet(getCohortsApiUrl(courseId)).reply(200, Factory.buildList('cohort', 3));
107112
});
@@ -230,6 +235,7 @@ describe('PostEditor', () => {
230235
},
231236
});
232237
await executeThunk(fetchCourseTopics(courseId), store.dispatch, store.getState);
238+
await executeThunk(fetchTab(courseId, 'outline'), store.dispatch, store.getState);
233239
});
234240

235241
test(`new post when anonymous posts are ${allowAnonymous ? '' : 'not'} allowed and anonymous posts to peers are ${
@@ -307,6 +313,7 @@ describe('PostEditor', () => {
307313
const dividedcw = ['category-1-topic-2', 'category-2-topic-1', 'category-2-topic-2'];
308314

309315
beforeEach(async () => {
316+
axiosMock.onGet(`${getCourseMetadataApiUrl(courseId)}`).reply(200, (Factory.build('navigationBar', 1, { isEnrolled: true })));
310317
axiosMock.onGet(getCohortsApiUrl(courseId)).reply(200, Factory.buildList('cohort', 3));
311318
});
312319

@@ -329,6 +336,7 @@ describe('PostEditor', () => {
329336
},
330337
});
331338
await executeThunk(fetchCourseTopics(courseId), store.dispatch, store.getState);
339+
await executeThunk(fetchTab(courseId, 'outline'), store.dispatch, store.getState);
332340
}
333341

334342
test('renders the mocked ReCAPTCHA.', async () => {
@@ -337,6 +345,7 @@ describe('PostEditor', () => {
337345
enabled: true,
338346
siteKey: 'test-key',
339347
},
348+
hasModerationPrivileges: false,
340349
});
341350
await renderComponent();
342351
expect(screen.getByTestId('mocked-recaptcha')).toBeInTheDocument();
@@ -348,6 +357,7 @@ describe('PostEditor', () => {
348357
enabled: true,
349358
siteKey: 'test-key',
350359
},
360+
hasModerationPrivileges: false,
351361
});
352362
await renderComponent();
353363
const solveButton = screen.getByText('Solve CAPTCHA');
@@ -361,6 +371,7 @@ describe('PostEditor', () => {
361371
enabled: true,
362372
siteKey: 'test-key',
363373
},
374+
hasModerationPrivileges: false,
364375
});
365376
await renderComponent();
366377
fireEvent.click(screen.getByText('Expire CAPTCHA'));
@@ -373,6 +384,7 @@ describe('PostEditor', () => {
373384
enabled: true,
374385
siteKey: 'test-key',
375386
},
387+
hasModerationPrivileges: false,
376388
});
377389
await renderComponent();
378390
fireEvent.click(screen.getByText('Error CAPTCHA'));

0 commit comments

Comments
 (0)