Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 18 additions & 9 deletions src/discussions/post-comments/PostCommentsView.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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' },
Expand Down Expand Up @@ -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 }) => {
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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'));
Expand Down Expand Up @@ -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'));
Expand All @@ -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'));
Expand All @@ -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'));
Expand All @@ -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'));
Expand Down Expand Up @@ -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 () => {
Expand Down Expand Up @@ -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 () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import useDispatchWithState from '../../../../data/hooks';
import DiscussionContext from '../../../common/context';
import {
selectCaptchaSettings,
selectIsUserLearner,
selectModerationSettings,
selectUserHasModerationPrivileges,
selectUserIsGroupTa,
Expand Down Expand Up @@ -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)),
Expand Down
4 changes: 3 additions & 1 deletion src/discussions/posts/post-editor/PostEditor.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import {
selectDivisionSettings,
selectEnableInContext,
selectIsNotifyAllLearnersEnabled,
selectIsUserLearner,
selectModerationSettings,
selectUserHasModerationPrivileges,
selectUserIsGroupTa,
Expand Down Expand Up @@ -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)
Expand All @@ -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)),
};
Expand Down
14 changes: 13 additions & 1 deletion src/discussions/posts/post-editor/PostEditor.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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}`;
Expand Down Expand Up @@ -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'],
Expand All @@ -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));
});
Expand Down Expand Up @@ -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 ${
Expand Down Expand Up @@ -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));
});

Expand All @@ -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 () => {
Expand All @@ -337,6 +345,7 @@ describe('PostEditor', () => {
enabled: true,
siteKey: 'test-key',
},
hasModerationPrivileges: false,
});
await renderComponent();
expect(screen.getByTestId('mocked-recaptcha')).toBeInTheDocument();
Expand All @@ -348,6 +357,7 @@ describe('PostEditor', () => {
enabled: true,
siteKey: 'test-key',
},
hasModerationPrivileges: false,
});
await renderComponent();
const solveButton = screen.getByText('Solve CAPTCHA');
Expand All @@ -361,6 +371,7 @@ describe('PostEditor', () => {
enabled: true,
siteKey: 'test-key',
},
hasModerationPrivileges: false,
});
await renderComponent();
fireEvent.click(screen.getByText('Expire CAPTCHA'));
Expand All @@ -373,6 +384,7 @@ describe('PostEditor', () => {
enabled: true,
siteKey: 'test-key',
},
hasModerationPrivileges: false,
});
await renderComponent();
fireEvent.click(screen.getByText('Error CAPTCHA'));
Expand Down