feat: learners area post summary will show reported if any comment is reported (#325)

Co-authored-by: adeel.tajamul <adeel.tajamul@arbisoft.com>
This commit is contained in:
Muhammad Adeel Tajamul
2022-10-19 12:11:01 +05:00
committed by GitHub
parent d7d159dac2
commit 0a4f2a41c5
5 changed files with 164 additions and 7 deletions

View File

@@ -14,6 +14,7 @@ import { ArrowBack } from '@edx/paragon/icons';
import { RequestStatus, Routes } from '../../data/constants';
import { DiscussionContext } from '../common/context';
import { selectUserHasModerationPrivileges, selectUserIsStaff } from '../data/selectors';
import {
selectAllThreads,
selectThreadNextPage,
@@ -34,14 +35,19 @@ function LearnerPostsView({ intl }) {
const loadingStatus = useSelector(threadsLoadingStatus());
const { courseId, learnerUsername: username } = useContext(DiscussionContext);
const nextPage = useSelector(selectThreadNextPage());
const userHasModerationPrivileges = useSelector(selectUserHasModerationPrivileges);
const userIsStaff = useSelector(selectUserIsStaff);
const countFlagged = userHasModerationPrivileges || userIsStaff;
useEffect(() => {
dispatch(fetchUserPosts(courseId, username));
dispatch(fetchUserPosts(courseId, { username, countFlagged }));
}, [courseId, username]);
const loadMorePosts = () => (
dispatch(fetchUserPosts(courseId, username, {
dispatch(fetchUserPosts(courseId, {
username,
page: nextPage,
countFlagged,
}))
);

View File

@@ -0,0 +1,90 @@
import React from 'react';
import { render, screen } from '@testing-library/react';
import MockAdapter from 'axios-mock-adapter';
import { act } from 'react-dom/test-utils';
import { IntlProvider } from 'react-intl';
import { MemoryRouter, Route } from 'react-router';
import { Factory } from 'rosie';
import { initializeMockApp } from '@edx/frontend-platform';
import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
import { AppProvider } from '@edx/frontend-platform/react';
import { initializeStore } from '../../store';
import { executeThunk } from '../../test-utils';
import { DiscussionContext } from '../common/context';
import { courseConfigApiUrl } from '../data/api';
import { fetchCourseConfig } from '../data/thunks';
import { coursesApiUrl } from './data/api';
import LearnerPostsView from './LearnerPostsView';
import './data/__factories__';
let store;
let axiosMock;
const courseId = 'course-v1:edX+TestX+Test_Course';
const username = 'abc123';
function renderComponent(path = `/${courseId}/learners/${username}/posts`) {
return render(
<IntlProvider locale="en">
<AppProvider store={store}>
<DiscussionContext.Provider
value={{
learnerUsername: username,
courseId,
}}
>
<MemoryRouter initialEntries={[path]}>
<Route path={path}>
<LearnerPostsView />
</Route>
</MemoryRouter>
</DiscussionContext.Provider>
</AppProvider>
</IntlProvider>,
);
}
describe('LearnerPostsView', () => {
beforeEach(async () => {
initializeMockApp({
authenticatedUser: {
userId: 3,
username,
administrator: true,
roles: [],
},
});
store = initializeStore();
Factory.resetAll();
const learnerPosts = Factory.build('learnerPosts', {}, {
abuseFlaggedCount: 1,
});
const apiUrl = `${coursesApiUrl}${courseId}/learner/`;
axiosMock = new MockAdapter(getAuthenticatedHttpClient());
axiosMock.onGet(apiUrl, { username, count_flagged: true })
.reply(() => [200, learnerPosts]);
});
describe('Basic', () => {
test('Reported icon is visible to moderator for post with reported comment', async () => {
axiosMock.onGet(`${courseConfigApiUrl}${courseId}/`).reply(200, {
has_moderation_privileges: true,
});
axiosMock.onGet(`${courseConfigApiUrl}${courseId}/settings`).reply(200, {});
await executeThunk(fetchCourseConfig(courseId), store.dispatch, store.getState);
await act(async () => {
renderComponent();
});
expect(screen.queryAllByTestId('reported-post')[0]).toBeInTheDocument();
});
test('Reported icon is not visible to learner for post with reported comment', async () => {
await renderComponent();
expect(screen.queryByTestId('reported-post')).not.toBeInTheDocument();
});
});
});

View File

@@ -86,3 +86,63 @@ Factory.define('learnersProfile')
}));
return profiles;
});
Factory.define('learnerPosts')
.option('abuseFlaggedCount', null, null)
.option('courseId', null, 'course-v1:edX+TestX+Test_Course')
.attr(
'results',
['abuseFlaggedCount', 'courseId'],
(abuseFlaggedCount, courseId) => {
const threads = [];
for (let i = 0; i < 2; i++) {
threads.push({
created_at: new Date().toISOString(),
updated_at: new Date().toISOString(),
editable_fields: [
'abuse_flagged',
'following',
'group_id',
'raw_body',
'closed',
'read',
'title',
'topic_id',
'type',
'voted',
'pinned',
],
id: `post_id${i}`,
author: 'test_user',
author_label: 'Staff',
abuse_flagged: false,
can_delete: true,
voted: false,
vote_count: 1,
title: `Title ${i}`,
raw_body: `<p>body ${i}</p>`,
preview_body: `<p>body ${i}</p>`,
course_id: courseId,
group_id: null,
group_name: null,
abuse_flagged_count: abuseFlaggedCount,
following: false,
comment_count: 8,
unread_comment_count: 0,
endorsed_comment_list_url: null,
non_endorsed_comment_list_url: null,
read: false,
has_endorsed: false,
pinned: false,
topic_id: 'topic',
});
}
return threads;
},
)
.attr('pagination', [], () => ({
next: null,
prev: null,
count: 2,
num_pages: 1,
}));

View File

@@ -1,5 +1,5 @@
/* eslint-disable import/prefer-default-export */
import { ensureConfig, getConfig } from '@edx/frontend-platform';
import { ensureConfig, getConfig, snakeCaseObject } from '@edx/frontend-platform';
import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
ensureConfig([
@@ -45,10 +45,11 @@ export async function getUserProfiles(usernames) {
* pagination: {count, num_pages, next, previous}
* }
*/
export async function getUserPosts(courseId, username, { page }) {
export async function getUserPosts(courseId, { username, page, countFlagged }) {
const learnerPostsApiUrl = `${coursesApiUrl}${courseId}/learner/`;
const params = snakeCaseObject({ username, page, countFlagged });
const { data } = await getAuthenticatedHttpClient()
.get(learnerPostsApiUrl, { params: { username, page } });
.get(learnerPostsApiUrl, { params });
return data;
}

View File

@@ -67,12 +67,12 @@ export function fetchLearners(courseId, {
* @param page
* @returns a promise that will update the state with the learner's posts
*/
export function fetchUserPosts(courseId, username, { page = 1 } = {}) {
export function fetchUserPosts(courseId, { username, page = 1, countFlagged = false } = {}) {
return async (dispatch) => {
try {
dispatch(fetchLearnerThreadsRequest({ courseId, author: username }));
const data = await getUserPosts(courseId, username, { page });
const data = await getUserPosts(courseId, { username, page, countFlagged });
const normalisedData = normaliseThreads(camelCaseObject(data));
dispatch(fetchThreadsSuccess({ ...normalisedData, page, author: username }));