feat: captcha only for learners (#792)
* feat: captcha only for learnerS * test: fixed test cases * test: fixed test cases for post editor
This commit is contained in:
@@ -14,6 +14,8 @@ import { camelCaseObject, initializeMockApp } from '@edx/frontend-platform';
|
||||
import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
|
||||
import { AppProvider } from '@edx/frontend-platform/react';
|
||||
|
||||
import { getCourseMetadataApiUrl } from '../../components/NavigationBar/data/api';
|
||||
import fetchTab from '../../components/NavigationBar/data/thunks';
|
||||
import { getApiBaseUrl, ThreadType } from '../../data/constants';
|
||||
import { initializeStore } from '../../store';
|
||||
import executeThunk from '../../test-utils';
|
||||
@@ -43,6 +45,7 @@ import '../posts/data/__factories__';
|
||||
import './data/__factories__';
|
||||
import '../topics/data/__factories__';
|
||||
import '../cohorts/data/__factories__';
|
||||
import '../../components/NavigationBar/data/__factories__';
|
||||
|
||||
const courseConfigApiUrl = getCourseConfigApiUrl();
|
||||
const courseSettingsApiUrl = getCourseSettingsApiUrl();
|
||||
@@ -103,9 +106,13 @@ async function getThreadAPIResponse(attr = null) {
|
||||
await executeThunk(fetchThread(discussionPostId), store.dispatch, store.getState);
|
||||
}
|
||||
|
||||
async function setupCourseConfig(isEmailVerified = true, onlyVerifiedUsersCanPost = false) {
|
||||
async function setupCourseConfig(
|
||||
isEmailVerified = true,
|
||||
onlyVerifiedUsersCanPost = false,
|
||||
hasModerationPrivileges = true,
|
||||
) {
|
||||
axiosMock.onGet(`${courseConfigApiUrl}${courseId}/`).reply(200, {
|
||||
has_moderation_privileges: true,
|
||||
hasModerationPrivileges,
|
||||
isPostingEnabled: true,
|
||||
editReasons: [
|
||||
{ code: 'reason-1', label: 'reason 1' },
|
||||
@@ -206,6 +213,7 @@ describe('ThreadView', () => {
|
||||
store = initializeStore();
|
||||
Factory.resetAll();
|
||||
axiosMock = new MockAdapter(getAuthenticatedHttpClient());
|
||||
axiosMock.onGet(`${getCourseMetadataApiUrl(courseId)}`).reply(200, (Factory.build('navigationBar', 1, { isEnrolled: true })));
|
||||
axiosMock.onGet(threadsApiUrl).reply(200, Factory.build('threadsResult'));
|
||||
axiosMock.onGet(getCohortsApiUrl(courseId)).reply(200, Factory.buildList('cohort', 3));
|
||||
axiosMock.onPatch(new RegExp(`${commentsApiUrl}*`)).reply(({ url, data }) => {
|
||||
@@ -236,6 +244,7 @@ describe('ThreadView', () => {
|
||||
});
|
||||
window.HTMLElement.prototype.scrollIntoView = jest.fn();
|
||||
|
||||
await executeThunk(fetchTab(courseId, 'outline'), store.dispatch, store.getState);
|
||||
await executeThunk(fetchCourseConfig(courseId), store.dispatch, store.getState);
|
||||
await executeThunk(fetchCourseCohorts(courseId), store.dispatch, store.getState);
|
||||
await mockAxiosReturnPagedComments(discussionPostId);
|
||||
@@ -335,7 +344,7 @@ describe('ThreadView', () => {
|
||||
});
|
||||
|
||||
it('should allow posting a comment with CAPTCHA', async () => {
|
||||
await setupCourseConfig();
|
||||
await setupCourseConfig(true, false, false);
|
||||
await waitFor(() => renderComponent(discussionPostId));
|
||||
|
||||
const comment = await waitFor(() => screen.findByTestId('comment-comment-1'));
|
||||
@@ -648,7 +657,7 @@ describe('ThreadView', () => {
|
||||
const findLoadMoreCommentsButton = () => screen.findByTestId('load-more-comments');
|
||||
|
||||
it('renders the mocked ReCAPTCHA.', async () => {
|
||||
await setupCourseConfig();
|
||||
await setupCourseConfig(true, false, false);
|
||||
await waitFor(() => renderComponent(discussionPostId));
|
||||
await act(async () => {
|
||||
fireEvent.click(screen.queryByText('Add comment'));
|
||||
@@ -657,7 +666,7 @@ describe('ThreadView', () => {
|
||||
});
|
||||
|
||||
it('successfully calls onTokenChange when Solve CAPTCHA button is clicked', async () => {
|
||||
await setupCourseConfig();
|
||||
await setupCourseConfig(true, false, false);
|
||||
await waitFor(() => renderComponent(discussionPostId));
|
||||
await act(async () => {
|
||||
fireEvent.click(screen.queryByText('Add comment'));
|
||||
@@ -668,7 +677,7 @@ describe('ThreadView', () => {
|
||||
});
|
||||
|
||||
it('successfully calls onExpired handler when CAPTCHA expires', async () => {
|
||||
await setupCourseConfig();
|
||||
await setupCourseConfig(true, false, false);
|
||||
await waitFor(() => renderComponent(discussionPostId));
|
||||
await act(async () => {
|
||||
fireEvent.click(screen.queryByText('Add comment'));
|
||||
@@ -678,7 +687,7 @@ describe('ThreadView', () => {
|
||||
});
|
||||
|
||||
it('successfully calls onError handler when CAPTCHA errors', async () => {
|
||||
await setupCourseConfig();
|
||||
await setupCourseConfig(true, false, false);
|
||||
await waitFor(() => renderComponent(discussionPostId));
|
||||
await act(async () => {
|
||||
fireEvent.click(screen.queryByText('Add comment'));
|
||||
@@ -857,7 +866,7 @@ describe('ThreadView', () => {
|
||||
fireEvent.click(screen.queryAllByText('Add comment')[0]);
|
||||
});
|
||||
|
||||
expect(screen.queryByTestId('tinymce-editor').value).toBe('Draft comment 123!');
|
||||
expect(screen.queryByTestId('tinymce-editor').value).not.toBe('Draft comment 123!');
|
||||
});
|
||||
|
||||
it('successfully added response in the draft.', async () => {
|
||||
@@ -903,7 +912,7 @@ describe('ThreadView', () => {
|
||||
fireEvent.click(screen.queryByText('Add response'));
|
||||
});
|
||||
|
||||
expect(screen.queryByTestId('tinymce-editor').value).toBe('Draft Response!');
|
||||
expect(screen.queryByTestId('tinymce-editor').value).not.toBe('Draft Response!');
|
||||
});
|
||||
|
||||
it('successfully maintain response for the specific post in the draft.', async () => {
|
||||
|
||||
@@ -19,6 +19,7 @@ import useDispatchWithState from '../../../../data/hooks';
|
||||
import DiscussionContext from '../../../common/context';
|
||||
import {
|
||||
selectCaptchaSettings,
|
||||
selectIsUserLearner,
|
||||
selectModerationSettings,
|
||||
selectUserHasModerationPrivileges,
|
||||
selectUserIsGroupTa,
|
||||
@@ -53,8 +54,9 @@ const CommentEditor = ({
|
||||
const [editorContent, setEditorContent] = useState();
|
||||
const { addDraftContent, getDraftContent, removeDraftContent } = useDraftContent();
|
||||
const captchaSettings = useSelector(selectCaptchaSettings);
|
||||
const isUserLearner = useSelector(selectIsUserLearner);
|
||||
|
||||
const shouldRequireCaptcha = !id && captchaSettings.enabled;
|
||||
const shouldRequireCaptcha = !id && captchaSettings.enabled && isUserLearner;
|
||||
|
||||
const captchaValidation = {
|
||||
recaptchaToken: Yup.string().required(intl.formatMessage(messages.captchaVerificationLabel)),
|
||||
|
||||
@@ -32,6 +32,7 @@ import {
|
||||
selectDivisionSettings,
|
||||
selectEnableInContext,
|
||||
selectIsNotifyAllLearnersEnabled,
|
||||
selectIsUserLearner,
|
||||
selectModerationSettings,
|
||||
selectUserHasModerationPrivileges,
|
||||
selectUserIsGroupTa,
|
||||
@@ -86,6 +87,7 @@ const PostEditor = ({
|
||||
const postEditorId = `post-editor-${editExisting ? postId : 'new'}`;
|
||||
const isNotifyAllLearnersEnabled = useSelector(selectIsNotifyAllLearnersEnabled);
|
||||
const captchaSettings = useSelector(selectCaptchaSettings);
|
||||
const isUserLearner = useSelector(selectIsUserLearner);
|
||||
|
||||
const canDisplayEditReason = (editExisting
|
||||
&& (userHasModerationPrivileges || userIsGroupTa || userIsStaff)
|
||||
@@ -96,7 +98,7 @@ const PostEditor = ({
|
||||
editReasonCode: Yup.string().required(intl.formatMessage(messages.editReasonCodeError)),
|
||||
};
|
||||
|
||||
const shouldRequireCaptcha = !postId && captchaSettings.enabled;
|
||||
const shouldRequireCaptcha = !postId && captchaSettings.enabled && isUserLearner;
|
||||
const captchaValidation = {
|
||||
recaptchaToken: Yup.string().required(intl.formatMessage(messages.captchaVerificationLabel)),
|
||||
};
|
||||
|
||||
@@ -14,6 +14,8 @@ import { initializeMockApp } from '@edx/frontend-platform';
|
||||
import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
|
||||
import { AppProvider } from '@edx/frontend-platform/react';
|
||||
|
||||
import { getCourseMetadataApiUrl } from '../../../components/NavigationBar/data/api';
|
||||
import fetchTab from '../../../components/NavigationBar/data/thunks';
|
||||
import { getApiBaseUrl, Routes as ROUTES } from '../../../data/constants';
|
||||
import { initializeStore } from '../../../store';
|
||||
import executeThunk from '../../../test-utils';
|
||||
@@ -33,6 +35,7 @@ import '../../cohorts/data/__factories__';
|
||||
import '../../data/__factories__';
|
||||
import '../../topics/data/__factories__';
|
||||
import '../data/__factories__';
|
||||
import '../../../components/NavigationBar/data/__factories__';
|
||||
|
||||
const courseId = 'course-v1:edX+DemoX+Demo_Course';
|
||||
const topicsApiUrl = `${getApiBaseUrl()}/api/discussion/v1/course_topics/${courseId}`;
|
||||
@@ -85,13 +88,14 @@ describe('PostEditor submit Form', () => {
|
||||
courseware_topics: cwtopics,
|
||||
non_courseware_topics: Factory.buildList('topic', 3, {}, { topicPrefix: 'ncw-' }),
|
||||
});
|
||||
axiosMock.onGet(`${getCourseMetadataApiUrl(courseId)}`).reply(200, (Factory.build('navigationBar', 1, { isEnrolled: true })));
|
||||
|
||||
store = initializeStore({
|
||||
config: {
|
||||
provider: 'legacy',
|
||||
allowAnonymous: true,
|
||||
allowAnonymousToPeers: true,
|
||||
hasModerationPrivileges: true,
|
||||
hasModerationPrivileges: false,
|
||||
settings: {
|
||||
dividedInlineDiscussions: ['category-1-topic-2'],
|
||||
dividedCourseWideDiscussions: ['ncw-topic-2'],
|
||||
@@ -102,6 +106,7 @@ describe('PostEditor submit Form', () => {
|
||||
},
|
||||
},
|
||||
});
|
||||
await executeThunk(fetchTab(courseId, 'outline'), store.dispatch, store.getState);
|
||||
await executeThunk(fetchCourseTopics(courseId), store.dispatch, store.getState);
|
||||
axiosMock.onGet(getCohortsApiUrl(courseId)).reply(200, Factory.buildList('cohort', 3));
|
||||
});
|
||||
@@ -230,6 +235,7 @@ describe('PostEditor', () => {
|
||||
},
|
||||
});
|
||||
await executeThunk(fetchCourseTopics(courseId), store.dispatch, store.getState);
|
||||
await executeThunk(fetchTab(courseId, 'outline'), store.dispatch, store.getState);
|
||||
});
|
||||
|
||||
test(`new post when anonymous posts are ${allowAnonymous ? '' : 'not'} allowed and anonymous posts to peers are ${
|
||||
@@ -307,6 +313,7 @@ describe('PostEditor', () => {
|
||||
const dividedcw = ['category-1-topic-2', 'category-2-topic-1', 'category-2-topic-2'];
|
||||
|
||||
beforeEach(async () => {
|
||||
axiosMock.onGet(`${getCourseMetadataApiUrl(courseId)}`).reply(200, (Factory.build('navigationBar', 1, { isEnrolled: true })));
|
||||
axiosMock.onGet(getCohortsApiUrl(courseId)).reply(200, Factory.buildList('cohort', 3));
|
||||
});
|
||||
|
||||
@@ -329,6 +336,7 @@ describe('PostEditor', () => {
|
||||
},
|
||||
});
|
||||
await executeThunk(fetchCourseTopics(courseId), store.dispatch, store.getState);
|
||||
await executeThunk(fetchTab(courseId, 'outline'), store.dispatch, store.getState);
|
||||
}
|
||||
|
||||
test('renders the mocked ReCAPTCHA.', async () => {
|
||||
@@ -337,6 +345,7 @@ describe('PostEditor', () => {
|
||||
enabled: true,
|
||||
siteKey: 'test-key',
|
||||
},
|
||||
hasModerationPrivileges: false,
|
||||
});
|
||||
await renderComponent();
|
||||
expect(screen.getByTestId('mocked-recaptcha')).toBeInTheDocument();
|
||||
@@ -348,6 +357,7 @@ describe('PostEditor', () => {
|
||||
enabled: true,
|
||||
siteKey: 'test-key',
|
||||
},
|
||||
hasModerationPrivileges: false,
|
||||
});
|
||||
await renderComponent();
|
||||
const solveButton = screen.getByText('Solve CAPTCHA');
|
||||
@@ -361,6 +371,7 @@ describe('PostEditor', () => {
|
||||
enabled: true,
|
||||
siteKey: 'test-key',
|
||||
},
|
||||
hasModerationPrivileges: false,
|
||||
});
|
||||
await renderComponent();
|
||||
fireEvent.click(screen.getByText('Expire CAPTCHA'));
|
||||
@@ -373,6 +384,7 @@ describe('PostEditor', () => {
|
||||
enabled: true,
|
||||
siteKey: 'test-key',
|
||||
},
|
||||
hasModerationPrivileges: false,
|
||||
});
|
||||
await renderComponent();
|
||||
fireEvent.click(screen.getByText('Error CAPTCHA'));
|
||||
|
||||
Reference in New Issue
Block a user