test: fixed test cases after MFE optimization (#521)
* test: fixed ActionDropDown test cases * test: fixed AlertBanner and EndorsedAlertBanner test cases * test: fixed LearnerFooter test cases * test: fixed LearnersView failed test cases * test: fixed PostFooter failed test cases * test: fixed PostLink failed test cases * test: fixed LearnerPostsView failed test cases * test: fixed console error and warnings in PostEditor * test: fixed Post anonymously test condition
This commit is contained in:
@@ -1,16 +1,24 @@
|
||||
import {
|
||||
fireEvent, render, screen, waitFor,
|
||||
} from '@testing-library/react';
|
||||
import MockAdapter from 'axios-mock-adapter';
|
||||
import { act } from 'react-dom/test-utils';
|
||||
import { IntlProvider } from 'react-intl';
|
||||
import { Factory } from 'rosie';
|
||||
|
||||
import { camelCaseObject, initializeMockApp, snakeCaseObject } from '@edx/frontend-platform';
|
||||
import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
|
||||
import { AppProvider } from '@edx/frontend-platform/react';
|
||||
|
||||
import { ContentActions } from '../../data/constants';
|
||||
import { initializeStore } from '../../store';
|
||||
import { executeThunk } from '../../test-utils';
|
||||
import messages from '../messages';
|
||||
import { getCommentsApiUrl } from '../post-comments/data/api';
|
||||
import { addComment, fetchThreadComments } from '../post-comments/data/thunks';
|
||||
import { PostCommentsContext } from '../post-comments/postCommentsContext';
|
||||
import { getThreadsApiUrl } from '../posts/data/api';
|
||||
import { fetchThread } from '../posts/data/thunks';
|
||||
import { ACTIONS_LIST } from '../utils';
|
||||
import ActionsDropdown from './ActionsDropdown';
|
||||
|
||||
@@ -18,107 +26,151 @@ import '../post-comments/data/__factories__';
|
||||
import '../posts/data/__factories__';
|
||||
|
||||
let store;
|
||||
let axiosMock;
|
||||
const commentsApiUrl = getCommentsApiUrl();
|
||||
const threadsApiUrl = getThreadsApiUrl();
|
||||
const discussionThreadId = 'thread-1';
|
||||
const questionThreadId = 'thread-2';
|
||||
const commentContent = 'This is a comment for thread-1';
|
||||
let discussionThread;
|
||||
let questionThread;
|
||||
let comment;
|
||||
|
||||
function buildTestContent(buildParams, testMeta) {
|
||||
const buildTestContent = (buildParams, testMeta) => {
|
||||
const buildParamsSnakeCase = snakeCaseObject(buildParams);
|
||||
return [
|
||||
{
|
||||
testFor: 'comments',
|
||||
...camelCaseObject(Factory.build('comment', { ...buildParamsSnakeCase }, null)),
|
||||
...testMeta,
|
||||
},
|
||||
{
|
||||
testFor: 'question threads',
|
||||
...camelCaseObject(Factory.build('thread', { ...buildParamsSnakeCase, type: 'question' }, null)),
|
||||
...testMeta,
|
||||
},
|
||||
{
|
||||
discussionThread = Factory.build('thread', { ...buildParamsSnakeCase, id: discussionThreadId }, null);
|
||||
questionThread = Factory.build('thread', { ...buildParamsSnakeCase, id: questionThreadId }, null);
|
||||
comment = Factory.build('comment', { ...buildParamsSnakeCase, thread_id: discussionThreadId }, null);
|
||||
|
||||
return {
|
||||
discussion: {
|
||||
testFor: 'discussion threads',
|
||||
...camelCaseObject(Factory.build('thread', { ...buildParamsSnakeCase, type: 'discussion' }, null)),
|
||||
contentType: 'POST',
|
||||
...camelCaseObject(discussionThread),
|
||||
...testMeta,
|
||||
},
|
||||
];
|
||||
}
|
||||
question: {
|
||||
testFor: 'question threads',
|
||||
contentType: 'POST',
|
||||
...camelCaseObject(questionThread),
|
||||
...testMeta,
|
||||
},
|
||||
comment: {
|
||||
testFor: 'comments',
|
||||
contentType: 'COMMENT',
|
||||
type: 'discussion',
|
||||
...camelCaseObject(comment),
|
||||
...testMeta,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
const canPerformActionTestData = ACTIONS_LIST
|
||||
.map(({ action, conditions, label: { defaultMessage } }) => {
|
||||
const buildParams = {
|
||||
const mockThreadAndComment = async (response) => {
|
||||
axiosMock.onGet(`${threadsApiUrl}${discussionThreadId}/`).reply(200, response);
|
||||
axiosMock.onGet(`${threadsApiUrl}${questionThreadId}/`).reply(200, response);
|
||||
axiosMock.onGet(commentsApiUrl).reply(200, Factory.build('commentsResult'));
|
||||
axiosMock.onPost(commentsApiUrl).reply(200, response);
|
||||
|
||||
await executeThunk(fetchThread(discussionThreadId), store.dispatch, store.getState);
|
||||
await executeThunk(fetchThread(questionThreadId), store.dispatch, store.getState);
|
||||
await executeThunk(fetchThreadComments(discussionThreadId), store.dispatch, store.getState);
|
||||
await executeThunk(addComment(commentContent, discussionThreadId, null), store.dispatch, store.getState);
|
||||
};
|
||||
|
||||
const canPerformActionTestData = ACTIONS_LIST.flatMap(({
|
||||
id, action, conditions, label: { defaultMessage },
|
||||
}) => {
|
||||
const buildParams = { editable_fields: [action] };
|
||||
|
||||
if (conditions) {
|
||||
Object.entries(conditions).forEach(([conditionKey, conditionValue]) => {
|
||||
buildParams[conditionKey] = conditionValue;
|
||||
});
|
||||
}
|
||||
|
||||
const testContent = buildTestContent(buildParams, { label: defaultMessage, action });
|
||||
|
||||
switch (id) {
|
||||
case 'answer':
|
||||
case 'unanswer':
|
||||
return [testContent.question];
|
||||
case 'endorse':
|
||||
case 'unendorse':
|
||||
return [testContent.comment, testContent.discussion];
|
||||
default:
|
||||
return [testContent.discussion, testContent.question, testContent.comment];
|
||||
}
|
||||
});
|
||||
|
||||
const canNotPerformActionTestData = ACTIONS_LIST.flatMap(({ action, conditions, label: { defaultMessage } }) => {
|
||||
const label = defaultMessage;
|
||||
|
||||
if (!conditions) {
|
||||
const content = buildTestContent({ editable_fields: [] }, { reason: 'field is not editable', label: defaultMessage });
|
||||
return [content.discussion, content.question, content.comment];
|
||||
}
|
||||
|
||||
const reversedConditions = Object.fromEntries(Object.entries(conditions).map(([key, value]) => [key, !value]));
|
||||
|
||||
const content = {
|
||||
// can edit field, but doesn't pass conditions
|
||||
...buildTestContent({
|
||||
editable_fields: [action],
|
||||
};
|
||||
if (conditions) {
|
||||
Object.entries(conditions)
|
||||
.forEach(([conditionKey, conditionValue]) => {
|
||||
buildParams[conditionKey] = conditionValue;
|
||||
});
|
||||
}
|
||||
return buildTestContent(buildParams, { label: defaultMessage, action });
|
||||
})
|
||||
.flat();
|
||||
...reversedConditions,
|
||||
}, { reason: 'field is editable but does not pass condition', label, action }),
|
||||
|
||||
const canNotPerformActionTestData = ACTIONS_LIST
|
||||
.map(({ action, conditions, label: { defaultMessage } }) => {
|
||||
const label = defaultMessage;
|
||||
let content;
|
||||
if (!conditions) {
|
||||
content = buildTestContent({ editable_fields: [] }, { reason: 'field is not editable', label: defaultMessage });
|
||||
} else {
|
||||
const reversedConditions = Object.keys(conditions)
|
||||
.reduce(
|
||||
(results, key) => ({
|
||||
...results,
|
||||
[key]: !conditions[key],
|
||||
}),
|
||||
{},
|
||||
);
|
||||
// passes conditions, but can't edit field
|
||||
...(action === ContentActions.DELETE ? {} : buildTestContent({
|
||||
editable_fields: [],
|
||||
...conditions,
|
||||
}, { reason: 'passes conditions but field is not editable', label, action })),
|
||||
|
||||
content = [
|
||||
// can edit field, but doesn't pass conditions
|
||||
...buildTestContent({
|
||||
editable_fields: [action],
|
||||
...reversedConditions,
|
||||
}, { reason: 'field is editable but does not pass condition', label, action }),
|
||||
// passes conditions, but can't edit field
|
||||
...(action === ContentActions.DELETE
|
||||
? []
|
||||
: buildTestContent({
|
||||
editable_fields: [],
|
||||
...conditions,
|
||||
}, { reason: 'passes conditions but field is not editable', label, action })
|
||||
),
|
||||
// can't edit field, and doesn't pass conditions
|
||||
...buildTestContent({
|
||||
editable_fields: [],
|
||||
...reversedConditions,
|
||||
}, { reason: 'can not edit field and does not match conditions', label, action }),
|
||||
];
|
||||
}
|
||||
return content;
|
||||
})
|
||||
.flat();
|
||||
// can't edit field, and doesn't pass conditions
|
||||
...buildTestContent({
|
||||
editable_fields: [],
|
||||
...reversedConditions,
|
||||
}, { reason: 'can not edit field and does not match conditions', label, action }),
|
||||
};
|
||||
|
||||
function renderComponent(
|
||||
commentOrPost,
|
||||
{ disabled = false, actionHandlers = {} } = {},
|
||||
) {
|
||||
return [content.discussion, content.question, content.comment];
|
||||
});
|
||||
|
||||
const renderComponent = ({
|
||||
id = '',
|
||||
contentType = 'POST',
|
||||
closed = false,
|
||||
type = 'discussion',
|
||||
postId = '',
|
||||
disabled = false,
|
||||
actionHandlers = {},
|
||||
} = {}) => {
|
||||
render(
|
||||
<IntlProvider locale="en">
|
||||
<AppProvider store={store}>
|
||||
<ActionsDropdown
|
||||
commentOrPost={commentOrPost}
|
||||
disabled={disabled}
|
||||
actionHandlers={actionHandlers}
|
||||
/>
|
||||
<PostCommentsContext.Provider value={{
|
||||
isClosed: closed,
|
||||
postType: type,
|
||||
postId,
|
||||
}}
|
||||
>
|
||||
<ActionsDropdown
|
||||
id={id}
|
||||
disabled={disabled}
|
||||
actionHandlers={actionHandlers}
|
||||
contentType={contentType}
|
||||
/>
|
||||
</PostCommentsContext.Provider>
|
||||
</AppProvider>
|
||||
</IntlProvider>,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const findOpenActionsDropdownButton = async () => (
|
||||
screen.findByRole('button', { name: messages.actionsAlt.defaultMessage })
|
||||
);
|
||||
|
||||
describe('ActionsDropdown', () => {
|
||||
beforeEach(async () => {
|
||||
beforeEach(() => {
|
||||
initializeMockApp({
|
||||
authenticatedUser: {
|
||||
userId: 3,
|
||||
@@ -128,10 +180,13 @@ describe('ActionsDropdown', () => {
|
||||
},
|
||||
});
|
||||
store = initializeStore();
|
||||
Factory.resetAll();
|
||||
axiosMock = new MockAdapter(getAuthenticatedHttpClient());
|
||||
});
|
||||
|
||||
it.each(buildTestContent())('can open drop down if enabled', async (commentOrPost) => {
|
||||
renderComponent(commentOrPost, { disabled: false });
|
||||
it.each(Object.values(buildTestContent()))('can open drop down if enabled', async (commentOrPost) => {
|
||||
await mockThreadAndComment(commentOrPost);
|
||||
renderComponent({ ...commentOrPost });
|
||||
|
||||
const openButton = await findOpenActionsDropdownButton();
|
||||
await act(async () => {
|
||||
@@ -141,8 +196,9 @@ describe('ActionsDropdown', () => {
|
||||
await waitFor(() => expect(screen.queryByTestId('actions-dropdown-modal-popup')).toBeInTheDocument());
|
||||
});
|
||||
|
||||
it.each(buildTestContent())('can not open drop down if disabled', async (commentOrPost) => {
|
||||
renderComponent(commentOrPost, { disabled: true });
|
||||
it.each(Object.values(buildTestContent()))('can not open drop down if disabled', async (commentOrPost) => {
|
||||
await mockThreadAndComment(commentOrPost);
|
||||
renderComponent({ ...commentOrPost, disabled: true });
|
||||
|
||||
const openButton = await findOpenActionsDropdownButton();
|
||||
await act(async () => {
|
||||
@@ -153,11 +209,9 @@ describe('ActionsDropdown', () => {
|
||||
});
|
||||
|
||||
it('copy link action should be visible on posts', async () => {
|
||||
const commentOrPost = {
|
||||
testFor: 'thread',
|
||||
...camelCaseObject(Factory.build('thread', { editable_fields: ['copy_link'] }, null)),
|
||||
};
|
||||
renderComponent(commentOrPost, { disabled: false });
|
||||
const discussionObject = buildTestContent({ editable_fields: ['copy_link'] }).discussion;
|
||||
await mockThreadAndComment(discussionObject);
|
||||
renderComponent({ ...camelCaseObject(discussionObject) });
|
||||
|
||||
const openButton = await findOpenActionsDropdownButton();
|
||||
await act(async () => {
|
||||
@@ -168,11 +222,9 @@ describe('ActionsDropdown', () => {
|
||||
});
|
||||
|
||||
it('copy link action should not be visible on a comment', async () => {
|
||||
const commentOrPost = {
|
||||
testFor: 'comments',
|
||||
...camelCaseObject(Factory.build('comment', {}, null)),
|
||||
};
|
||||
renderComponent(commentOrPost, { disabled: false });
|
||||
const commentObject = buildTestContent().comment;
|
||||
await mockThreadAndComment(commentObject);
|
||||
renderComponent({ ...camelCaseObject(commentObject) });
|
||||
|
||||
const openButton = await findOpenActionsDropdownButton();
|
||||
await act(async () => {
|
||||
@@ -183,15 +235,13 @@ describe('ActionsDropdown', () => {
|
||||
});
|
||||
|
||||
describe.each(canPerformActionTestData)('Actions', ({
|
||||
testFor, action, label, reason, ...commentOrPost
|
||||
testFor, action, label, ...commentOrPost
|
||||
}) => {
|
||||
describe(`for ${testFor}`, () => {
|
||||
it(`can "${label}" when allowed`, async () => {
|
||||
await mockThreadAndComment(commentOrPost);
|
||||
const mockHandler = jest.fn();
|
||||
renderComponent(
|
||||
commentOrPost,
|
||||
{ actionHandlers: { [action]: mockHandler } },
|
||||
);
|
||||
renderComponent({ ...commentOrPost, actionHandlers: { [action]: mockHandler } });
|
||||
|
||||
const openButton = await findOpenActionsDropdownButton();
|
||||
await act(async () => {
|
||||
@@ -214,7 +264,8 @@ describe('ActionsDropdown', () => {
|
||||
}) => {
|
||||
describe(`for ${testFor}`, () => {
|
||||
it(`can't "${label}" when ${reason}`, async () => {
|
||||
renderComponent(commentOrPost);
|
||||
await mockThreadAndComment(commentOrPost);
|
||||
renderComponent({ ...commentOrPost });
|
||||
|
||||
const openButton = await findOpenActionsDropdownButton();
|
||||
await act(async () => {
|
||||
|
||||
@@ -31,7 +31,14 @@ function renderComponent(
|
||||
value={{ courseId: 'course-v1:edX+TestX+Test_Course' }}
|
||||
>
|
||||
<AlertBanner
|
||||
content={content}
|
||||
author={content.author}
|
||||
abuseFlagged={content.abuseFlagged}
|
||||
lastEdit={content.lastEdit}
|
||||
closed={content.closed}
|
||||
closedBy={content.closedBy}
|
||||
closeReason={content.closeReason}
|
||||
editByLabel={content.editByLabel}
|
||||
closedByLabel={content.closedByLabel}
|
||||
/>
|
||||
</DiscussionContext.Provider>
|
||||
</AppProvider>
|
||||
|
||||
@@ -8,6 +8,7 @@ import { AppProvider } from '@edx/frontend-platform/react';
|
||||
import { ThreadType } from '../../data/constants';
|
||||
import { initializeStore } from '../../store';
|
||||
import messages from '../post-comments/messages';
|
||||
import { PostCommentsContext } from '../post-comments/postCommentsContext';
|
||||
import { DiscussionContext } from './context';
|
||||
import EndorsedAlertBanner from './EndorsedAlertBanner';
|
||||
|
||||
@@ -30,10 +31,18 @@ function renderComponent(
|
||||
<DiscussionContext.Provider
|
||||
value={{ courseId: 'course-v1:edX+DemoX+Demo_Course' }}
|
||||
>
|
||||
<EndorsedAlertBanner
|
||||
content={content}
|
||||
postType={postType}
|
||||
/>
|
||||
<PostCommentsContext.Provider value={{
|
||||
postType,
|
||||
}}
|
||||
>
|
||||
<EndorsedAlertBanner
|
||||
endorsed={content.endorsed}
|
||||
endorsedAt={content.endorsedAt}
|
||||
endorsedBy={content.endorsedBy}
|
||||
endorsedByLabel={content.endorsedByLabel}
|
||||
/>
|
||||
</PostCommentsContext.Provider>
|
||||
|
||||
</DiscussionContext.Provider>
|
||||
</AppProvider>
|
||||
</IntlProvider>,
|
||||
|
||||
@@ -82,6 +82,10 @@ describe('Learner Posts View', () => {
|
||||
await executeThunk(fetchUserPosts(courseId), store.dispatch, store.getState);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
axiosMock.reset();
|
||||
});
|
||||
|
||||
test('Reported icon is visible to moderator for post with reported comment', async () => {
|
||||
await setUpPrivilages(axiosMock, store, true);
|
||||
await waitFor(() => { renderComponent(); });
|
||||
@@ -112,7 +116,9 @@ describe('Learner Posts View', () => {
|
||||
|
||||
const backButton = screen.getByLabelText('Back');
|
||||
|
||||
await act(() => fireEvent.click(backButton));
|
||||
await act(() => {
|
||||
fireEvent.click(backButton);
|
||||
});
|
||||
await waitFor(() => {
|
||||
expect(lastLocation.pathname.endsWith('/learners')).toBeTruthy();
|
||||
});
|
||||
@@ -128,15 +134,13 @@ describe('Learner Posts View', () => {
|
||||
expect(recentActivity).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it(`should display a list of the interactive posts of a selected learner and the posts count
|
||||
should be equal to the API response count.`, async () => {
|
||||
it('should display a list of the interactive posts of a selected learner', async () => {
|
||||
await waitFor(() => {
|
||||
renderComponent();
|
||||
});
|
||||
const posts = await container.querySelectorAll('.discussion-post');
|
||||
|
||||
expect(posts).toHaveLength(2);
|
||||
expect(posts).toHaveLength(Object.values(store.getState().threads.threadsById).length);
|
||||
});
|
||||
|
||||
it.each([
|
||||
@@ -162,9 +166,9 @@ describe('Learner Posts View', () => {
|
||||
await act(async () => {
|
||||
fireEvent.click(activity);
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
const learners = container.querySelectorAll('.discussion-post');
|
||||
|
||||
expect(learners).toHaveLength(result);
|
||||
});
|
||||
});
|
||||
@@ -175,8 +179,7 @@ describe('Learner Posts View', () => {
|
||||
{ searchBy: 'cohort-1', result: 2 },
|
||||
])('successfully display learners by %s.', async ({ searchBy, result }) => {
|
||||
await setUpPrivilages(axiosMock, store, true);
|
||||
axiosMock.onGet(getCohortsApiUrl(courseId))
|
||||
.reply(200, Factory.buildList('cohort', 3));
|
||||
axiosMock.onGet(getCohortsApiUrl(courseId)).reply(200, Factory.buildList('cohort', 3));
|
||||
|
||||
await executeThunk(fetchCourseCohorts(courseId), store.dispatch, store.getState);
|
||||
await renderComponent();
|
||||
@@ -191,9 +194,9 @@ describe('Learner Posts View', () => {
|
||||
await act(async () => {
|
||||
fireEvent.click(cohort);
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
const learners = container.querySelectorAll('.discussion-post');
|
||||
|
||||
expect(learners).toHaveLength(result);
|
||||
});
|
||||
});
|
||||
@@ -211,6 +214,6 @@ describe('Learner Posts View', () => {
|
||||
});
|
||||
|
||||
expect(loadMoreButton).not.toBeInTheDocument();
|
||||
expect(container.querySelectorAll('.discussion-post')).toHaveLength(2);
|
||||
expect(container.querySelectorAll('.discussion-post')).toHaveLength(4);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -37,6 +37,7 @@ function renderComponent() {
|
||||
<DiscussionContext.Provider value={{
|
||||
page: 'learners',
|
||||
learnerUsername: 'learner-1',
|
||||
courseId,
|
||||
}}
|
||||
>
|
||||
<MemoryRouter initialEntries={[`/${courseId}/`]}>
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { Factory } from 'rosie';
|
||||
|
||||
import '../../../posts/data/__factories__';
|
||||
|
||||
Factory.define('learner')
|
||||
.sequence('id')
|
||||
.attr('username', ['id'], (id) => `learner-${id}`)
|
||||
@@ -91,50 +93,8 @@ Factory.define('learnerPosts')
|
||||
'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;
|
||||
const attrs = { course_id: courseId, abuse_flagged_count: abuseFlaggedCount };
|
||||
return Factory.buildList('thread', 2, attrs);
|
||||
},
|
||||
)
|
||||
.attr('pagination', [], () => ({
|
||||
|
||||
@@ -23,8 +23,9 @@ const LearnerFooter = ({
|
||||
<div className="d-flex align-items-center pt-1 mt-2.5" style={{ marginBottom: '2px' }}>
|
||||
<OverlayTrigger
|
||||
placement="right"
|
||||
id={`learner-${username}-responses`}
|
||||
overlay={(
|
||||
<Tooltip>
|
||||
<Tooltip id={`learner-${username}-responses`}>
|
||||
<div className="d-flex flex-column align-items-start">
|
||||
{intl.formatMessage(messages.allActivity)}
|
||||
</div>
|
||||
@@ -38,8 +39,9 @@ const LearnerFooter = ({
|
||||
</OverlayTrigger>
|
||||
<OverlayTrigger
|
||||
placement="right"
|
||||
id={`learner-${username}-posts`}
|
||||
overlay={(
|
||||
<Tooltip>
|
||||
<Tooltip id={`learner-${username}-posts`}>
|
||||
<div className="d-flex flex-column align-items-start">
|
||||
{intl.formatMessage(messages.posts)}
|
||||
</div>
|
||||
@@ -54,8 +56,9 @@ const LearnerFooter = ({
|
||||
{Boolean(canSeeLearnerReportedStats) && (
|
||||
<OverlayTrigger
|
||||
placement="right"
|
||||
id={`learner-${username}-flags`}
|
||||
overlay={(
|
||||
<Tooltip id={`learner-${username}`}>
|
||||
<Tooltip id={`learner-${username}-flags`}>
|
||||
<div className="d-flex flex-column align-items-start">
|
||||
{Boolean(activeFlags)
|
||||
&& (
|
||||
|
||||
@@ -16,7 +16,15 @@ function renderComponent(learner) {
|
||||
return render(
|
||||
<IntlProvider locale="en">
|
||||
<AppProvider store={store}>
|
||||
<LearnerFooter learner={learner} />
|
||||
<LearnerFooter
|
||||
learner={learner}
|
||||
inactiveFlags={learner.inactiveFlags}
|
||||
activeFlags={learner.activeFlags}
|
||||
threads={learner.threads}
|
||||
responses={learner.responses}
|
||||
replies={learner.replies}
|
||||
username={learner.username}
|
||||
/>
|
||||
</AppProvider>
|
||||
</IntlProvider>,
|
||||
);
|
||||
|
||||
@@ -5,6 +5,7 @@ Factory.define('thread')
|
||||
.sequence('title', ['topic_id'], (idx, topicId) => `This is Thread-${idx} in topic ${topicId}`)
|
||||
.sequence('raw_body', (idx) => `Some contents for **thread number ${idx}**.`)
|
||||
.sequence('rendered_body', (idx) => `Some contents for <b>thread number ${idx}</b>.`)
|
||||
.sequence('preview_body', (idx) => `Some contents for <b>thread number ${idx}</b>.`)
|
||||
.sequence('type', (idx) => (idx % 2 === 1 ? 'discussion' : 'question'))
|
||||
.sequence('pinned', idx => (idx < 3))
|
||||
.sequence('topic_id', idx => `test-topic-${(idx % 3)}`)
|
||||
|
||||
@@ -71,13 +71,12 @@ describe('PostEditor', () => {
|
||||
axiosMock = new MockAdapter(getAuthenticatedHttpClient());
|
||||
const cwtopics = Factory.buildList('category', 2);
|
||||
Factory.reset('topic');
|
||||
axiosMock
|
||||
.onGet(topicsApiUrl)
|
||||
.reply(200, {
|
||||
courseware_topics: cwtopics,
|
||||
non_courseware_topics: Factory.buildList('topic', 3, {}, { topicPrefix: 'ncw-' }),
|
||||
});
|
||||
axiosMock.onGet(topicsApiUrl).reply(200, {
|
||||
courseware_topics: cwtopics,
|
||||
non_courseware_topics: Factory.buildList('topic', 3, {}, { topicPrefix: 'ncw-' }),
|
||||
});
|
||||
});
|
||||
|
||||
describe.each([
|
||||
{
|
||||
allowAnonymous: false,
|
||||
@@ -110,50 +109,35 @@ describe('PostEditor', () => {
|
||||
});
|
||||
await executeThunk(fetchCourseTopics(courseId), store.dispatch, store.getState);
|
||||
});
|
||||
test(
|
||||
`new post when anonymous posts are ${allowAnonymous ? '' : 'not '}allowed and anonymous posts to peers are ${allowAnonymousToPeers ? '' : 'not '}allowed`,
|
||||
async () => {
|
||||
await renderComponent();
|
||||
|
||||
expect(screen.queryByRole('heading'))
|
||||
.toHaveTextContent('Add a post');
|
||||
expect(screen.queryAllByRole('radio'))
|
||||
.toHaveLength(2);
|
||||
// 2 categories with 4 subcategories each
|
||||
expect(screen.queryAllByText(/category-\d-topic \d/))
|
||||
.toHaveLength(8);
|
||||
// 3 non courseare topics
|
||||
expect(screen.queryAllByText(/ncw-topic \d/))
|
||||
.toHaveLength(3);
|
||||
test(`new post when anonymous posts are ${allowAnonymous ? '' : 'not'} allowed and anonymous posts to peers are ${
|
||||
allowAnonymousToPeers ? '' : 'not'} allowed`, async () => {
|
||||
await renderComponent();
|
||||
|
||||
expect(screen.queryByRole('heading')).toHaveTextContent('Add a post');
|
||||
expect(screen.queryAllByRole('radio')).toHaveLength(2);
|
||||
// 2 categories with 4 subcategories each
|
||||
expect(screen.queryAllByText(/category-\d-topic \d/)).toHaveLength(8);
|
||||
// 3 non courseare topics
|
||||
expect(screen.queryAllByText(/ncw-topic \d/)).toHaveLength(3);
|
||||
expect(screen.queryByText('cohort', { exact: false })).not.toBeInTheDocument();
|
||||
expect(screen.queryByText('Post anonymously')).not.toBeInTheDocument();
|
||||
|
||||
if (allowAnonymousToPeers) {
|
||||
expect(screen.queryByText('Post anonymously to peers')).toBeInTheDocument();
|
||||
} else {
|
||||
expect(screen.queryByText('Post anonymously to peers')).not.toBeInTheDocument();
|
||||
}
|
||||
});
|
||||
|
||||
expect(screen.queryByText('cohort', { exact: false }))
|
||||
.not.toBeInTheDocument();
|
||||
if (allowAnonymous) {
|
||||
expect(screen.queryByText('Post anonymously'))
|
||||
.not.toBeInTheDocument();
|
||||
} else {
|
||||
expect(screen.queryByText('Post anonymously'))
|
||||
.not.toBeInTheDocument();
|
||||
}
|
||||
if (allowAnonymousToPeers) {
|
||||
expect(screen.queryByText('Post anonymously to peers'))
|
||||
.toBeInTheDocument();
|
||||
} else {
|
||||
expect(screen.queryByText('Post anonymously to peers'))
|
||||
.not
|
||||
.toBeInTheDocument();
|
||||
}
|
||||
},
|
||||
);
|
||||
test('selectThread is not called while creating a new post', async () => {
|
||||
const mockSelectThread = jest.fn();
|
||||
jest.mock('../data/selectors', () => ({
|
||||
selectThread: mockSelectThread,
|
||||
}));
|
||||
await renderComponent();
|
||||
expect(mockSelectThread)
|
||||
.not
|
||||
.toHaveBeenCalled();
|
||||
|
||||
expect(mockSelectThread).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -162,8 +146,7 @@ describe('PostEditor', () => {
|
||||
const dividedcw = ['category-1-topic-2', 'category-2-topic-1', 'category-2-topic-2'];
|
||||
|
||||
beforeEach(async () => {
|
||||
axiosMock.onGet(getCohortsApiUrl(courseId))
|
||||
.reply(200, Factory.buildList('cohort', 3));
|
||||
axiosMock.onGet(getCohortsApiUrl(courseId)).reply(200, Factory.buildList('cohort', 3));
|
||||
});
|
||||
|
||||
async function setupData(config = {}, settings = {}) {
|
||||
@@ -187,69 +170,46 @@ describe('PostEditor', () => {
|
||||
await setupData();
|
||||
await renderComponent();
|
||||
// Initially the user can't select a cohort
|
||||
expect(screen.queryByRole('combobox', {
|
||||
name: /cohort visibility/i,
|
||||
}))
|
||||
.not
|
||||
.toBeInTheDocument();
|
||||
expect(screen.queryByRole('combobox', { name: /cohort visibility/i })).not.toBeInTheDocument();
|
||||
// If a cohorted topic is selected, the cohort visibility selector is displayed
|
||||
['ncw-topic 2', 'category-1-topic 2', 'category-2-topic 1', 'category-2-topic 2'].forEach((topicName) => {
|
||||
['ncw-topic 2', 'category-1-topic 2', 'category-2-topic 1', 'category-2-topic 2'].forEach(async (topicName) => {
|
||||
act(() => {
|
||||
userEvent.selectOptions(
|
||||
screen.getByRole('combobox', {
|
||||
name: /topic area/i,
|
||||
}),
|
||||
screen.getByRole('combobox', { name: /topic area/i }),
|
||||
screen.getByRole('option', { name: topicName }),
|
||||
);
|
||||
});
|
||||
|
||||
expect(screen.queryByRole('combobox', {
|
||||
name: /cohort visibility/i,
|
||||
}))
|
||||
.toBeInTheDocument();
|
||||
expect(screen.queryByRole('combobox', { name: /cohort visibility/i })).toBeInTheDocument();
|
||||
});
|
||||
// Now if a non-cohorted topic is selected, the cohort visibility selector is hidden
|
||||
['ncw-topic 1', 'category-1-topic 1', 'category-2-topic 4'].forEach((topicName) => {
|
||||
['ncw-topic 1', 'category-1-topic 1', 'category-2-topic 4'].forEach(async (topicName) => {
|
||||
act(() => {
|
||||
userEvent.selectOptions(
|
||||
screen.getByRole('combobox', {
|
||||
name: /topic area/i,
|
||||
}),
|
||||
screen.getByRole('combobox', { name: /topic area/i }),
|
||||
screen.getByRole('option', { name: topicName }),
|
||||
);
|
||||
});
|
||||
expect(screen.queryByRole('combobox', {
|
||||
name: /cohort visibility/i,
|
||||
}))
|
||||
.not
|
||||
.toBeInTheDocument();
|
||||
expect(screen.queryByRole('combobox', { name: /cohort visibility/i })).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
test('test always divided inline', async () => {
|
||||
await setupData({}, { alwaysDivideInlineDiscussions: true });
|
||||
await renderComponent();
|
||||
// Initially the user can't select a cohort
|
||||
expect(screen.queryByRole('combobox', {
|
||||
name: /cohort visibility/i,
|
||||
}))
|
||||
.not
|
||||
.toBeInTheDocument();
|
||||
expect(screen.queryByRole('combobox', { name: /cohort visibility/i })).not.toBeInTheDocument();
|
||||
// All coursweare topics are divided
|
||||
[1, 2].forEach(catId => {
|
||||
[1, 2, 3, 4].forEach((topicId) => {
|
||||
act(() => {
|
||||
userEvent.selectOptions(
|
||||
screen.getByRole('combobox', {
|
||||
name: /topic area/i,
|
||||
}),
|
||||
screen.getByRole('combobox', { name: /topic area/i }),
|
||||
screen.getByRole('option', { name: `category-${catId}-topic ${topicId}` }),
|
||||
);
|
||||
});
|
||||
|
||||
expect(screen.queryByRole('combobox', {
|
||||
name: /cohort visibility/i,
|
||||
}))
|
||||
.toBeInTheDocument();
|
||||
expect(screen.queryByRole('combobox', { name: /cohort visibility/i })).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -257,46 +217,38 @@ describe('PostEditor', () => {
|
||||
['ncw-topic 1', 'ncw-topic 3'].forEach((topicName) => {
|
||||
act(() => {
|
||||
userEvent.selectOptions(
|
||||
screen.getByRole('combobox', {
|
||||
name: /topic area/i,
|
||||
}),
|
||||
screen.getByRole('combobox', { name: /topic area/i }),
|
||||
screen.getByRole('option', { name: topicName }),
|
||||
);
|
||||
});
|
||||
expect(screen.queryByRole('combobox', {
|
||||
name: /cohort visibility/i,
|
||||
}))
|
||||
.not
|
||||
.toBeInTheDocument();
|
||||
expect(screen.queryByRole('combobox', { name: /cohort visibility/i })).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
test('test unprivileged user', async () => {
|
||||
await setupData({ hasModerationPrivileges: false });
|
||||
await renderComponent();
|
||||
|
||||
['ncw-topic 1', 'ncw-topic 2', 'category-1-topic 1', 'category-2-topic 1'].forEach((topicName) => {
|
||||
act(() => {
|
||||
userEvent.selectOptions(
|
||||
screen.getByRole('combobox', {
|
||||
name: /topic area/i,
|
||||
}),
|
||||
screen.getByRole('combobox', { name: /topic area/i }),
|
||||
screen.getByRole('option', { name: topicName }),
|
||||
);
|
||||
});
|
||||
// If a cohorted topic is selected, the cohort visibility selector is displayed
|
||||
expect(screen.queryByRole('combobox', {
|
||||
name: /cohort visibility/i,
|
||||
}))
|
||||
.not
|
||||
.toBeInTheDocument();
|
||||
expect(screen.queryByRole('combobox', { name: /cohort visibility/i })).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
test('edit existing post should not show cohort selector to unprivileged users', async () => {
|
||||
const threadId = 'thread-1';
|
||||
await setupData({ hasModerationPrivileges: false });
|
||||
axiosMock.onGet(`${threadsApiUrl}${threadId}/`)
|
||||
.reply(200, Factory.build('thread'));
|
||||
await executeThunk(fetchThread(threadId), store.dispatch, store.getState);
|
||||
await renderComponent(true, `/${courseId}/posts/${threadId}/edit`);
|
||||
await act(async () => {
|
||||
axiosMock.onGet(`${threadsApiUrl}${threadId}/`).reply(200, Factory.build('thread'));
|
||||
await executeThunk(fetchThread(threadId), store.dispatch, store.getState);
|
||||
await renderComponent(true, `/${courseId}/posts/${threadId}/edit`);
|
||||
});
|
||||
|
||||
['ncw-topic 1', 'ncw-topic 2', 'category-1-topic 1', 'category-2-topic 1'].forEach((topicName) => {
|
||||
act(() => {
|
||||
@@ -308,20 +260,18 @@ describe('PostEditor', () => {
|
||||
);
|
||||
});
|
||||
// If a cohorted topic is selected, the cohort visibility selector is displayed
|
||||
expect(screen.queryByRole('combobox', {
|
||||
name: /cohort visibility/i,
|
||||
}))
|
||||
.not
|
||||
.toBeInTheDocument();
|
||||
expect(screen.queryByRole('combobox', { name: /cohort visibility/i })).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
test('cancel posting of existing post', async () => {
|
||||
const threadId = 'thread-1';
|
||||
await setupData();
|
||||
axiosMock.onGet(`${threadsApiUrl}${threadId}/`)
|
||||
.reply(200, Factory.build('thread'));
|
||||
await executeThunk(fetchThread(threadId), store.dispatch, store.getState);
|
||||
await renderComponent(true, `/${courseId}/posts/${threadId}/edit`);
|
||||
await act(async () => {
|
||||
axiosMock.onGet(`${threadsApiUrl}${threadId}/`).reply(200, Factory.build('thread'));
|
||||
await executeThunk(fetchThread(threadId), store.dispatch, store.getState);
|
||||
await renderComponent(true, `/${courseId}/posts/${threadId}/edit`);
|
||||
});
|
||||
|
||||
const cancelButton = screen.getByRole('button', { name: /cancel/i });
|
||||
await act(async () => {
|
||||
@@ -333,6 +283,7 @@ describe('PostEditor', () => {
|
||||
|
||||
describe('Edit codes', () => {
|
||||
const threadId = 'thread-1';
|
||||
|
||||
beforeEach(async () => {
|
||||
const dividedncw = ['ncw-topic-2'];
|
||||
const dividedcw = ['category-1-topic-2', 'category-2-topic-1', 'category-2-topic-2'];
|
||||
@@ -359,26 +310,26 @@ describe('PostEditor', () => {
|
||||
},
|
||||
});
|
||||
await executeThunk(fetchCourseTopics(courseId), store.dispatch, store.getState);
|
||||
axiosMock.onGet(`${threadsApiUrl}${threadId}/`)
|
||||
.reply(200, Factory.build('thread'));
|
||||
axiosMock.onGet(`${threadsApiUrl}${threadId}/`).reply(200, Factory.build('thread'));
|
||||
await executeThunk(fetchThread(threadId), store.dispatch, store.getState);
|
||||
});
|
||||
test('Edit post and see reasons', async () => {
|
||||
await renderComponent(true, `/${courseId}/posts/${threadId}/edit`);
|
||||
|
||||
expect(screen.queryByRole('combobox', {
|
||||
name: /reason for editing/i,
|
||||
}))
|
||||
.toBeInTheDocument();
|
||||
expect(screen.getAllByRole('option', {
|
||||
name: /reason \d/i,
|
||||
})).toHaveLength(2);
|
||||
test('Edit post and see reasons', async () => {
|
||||
await act(async () => {
|
||||
await renderComponent(true, `/${courseId}/posts/${threadId}/edit`);
|
||||
});
|
||||
|
||||
expect(screen.queryByRole('combobox', { name: /reason for editing/i })).toBeInTheDocument();
|
||||
expect(screen.getAllByRole('option', { name: /reason \d/i })).toHaveLength(2);
|
||||
});
|
||||
|
||||
it('should show Preview Panel', async () => {
|
||||
await renderComponent(true, `/${courseId}/posts/${threadId}/edit`);
|
||||
|
||||
await act(() => fireEvent.click(container.querySelector('[data-testid="show-preview-button"]')));
|
||||
await act(async () => {
|
||||
fireEvent.click(container.querySelector('[data-testid="show-preview-button"]'));
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(container.querySelector('[data-testid="hide-preview-button"]')).toBeInTheDocument();
|
||||
expect(container.querySelector('[data-testid="show-preview-button"]')).not.toBeInTheDocument();
|
||||
@@ -388,12 +339,18 @@ describe('PostEditor', () => {
|
||||
it('should hide Preview Panel', async () => {
|
||||
await renderComponent(true, `/${courseId}/posts/${threadId}/edit`);
|
||||
|
||||
await act(() => fireEvent.click(container.querySelector('[data-testid="show-preview-button"]')));
|
||||
await act(async () => {
|
||||
fireEvent.click(container.querySelector('[data-testid="show-preview-button"]'));
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(container.querySelector('[data-testid="hide-preview-button"]')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
await act(() => fireEvent.click(container.querySelector('[data-testid="hide-preview-button"]')));
|
||||
await act(async () => {
|
||||
fireEvent.click(container.querySelector('[data-testid="hide-preview-button"]'));
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(container.querySelector('[data-testid="show-preview-button"]')).toBeInTheDocument();
|
||||
expect(container.querySelector('[data-testid="hide-preview-button"]')).not.toBeInTheDocument();
|
||||
|
||||
@@ -18,7 +18,17 @@ function renderComponent(post, userHasModerationPrivileges = false) {
|
||||
return render(
|
||||
<IntlProvider locale="en">
|
||||
<AppProvider store={store}>
|
||||
<PostFooter post={post} userHasModerationPrivileges={userHasModerationPrivileges} />
|
||||
<PostFooter
|
||||
post={post}
|
||||
userHasModerationPrivileges={userHasModerationPrivileges}
|
||||
closed={post.closed}
|
||||
following={post.following}
|
||||
groupId={toString(post.groupId)}
|
||||
groupName={post.groupName}
|
||||
id={post.id}
|
||||
voted={post.voted}
|
||||
voteCount={post.voteCount}
|
||||
/>
|
||||
</AppProvider>
|
||||
</IntlProvider>,
|
||||
);
|
||||
@@ -88,7 +98,7 @@ describe('PostFooter', () => {
|
||||
expect(store.getState().threads.status === RequestStatus.IN_PROGRESS).toBeTruthy();
|
||||
});
|
||||
|
||||
it('test follow button when following=false', async () => {
|
||||
it('test follow button when following = false', async () => {
|
||||
renderComponent({ ...mockPost, following: false });
|
||||
expect(screen.queryByRole('button', { name: /follow/i })).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
@@ -3,6 +3,7 @@ import React from 'react';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import MockAdapter from 'axios-mock-adapter';
|
||||
import { IntlProvider } from 'react-intl';
|
||||
import { Factory } from 'rosie';
|
||||
|
||||
import { initializeMockApp } from '@edx/frontend-platform';
|
||||
import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
|
||||
@@ -13,48 +14,52 @@ import { executeThunk } from '../../../test-utils';
|
||||
import { DiscussionContext } from '../../common/context';
|
||||
import { getCourseConfigApiUrl } from '../../data/api';
|
||||
import { fetchCourseConfig } from '../../data/thunks';
|
||||
import { getThreadsApiUrl } from '../data/api';
|
||||
import { fetchThread } from '../data/thunks';
|
||||
import PostLink from './PostLink';
|
||||
|
||||
import '../data/__factories__';
|
||||
|
||||
const courseId = 'course-v1:edX+DemoX+Demo_Course';
|
||||
const courseConfigApiUrl = getCourseConfigApiUrl();
|
||||
const threadsApiUrl = getThreadsApiUrl();
|
||||
const postId = 'thread-1';
|
||||
let store;
|
||||
let axiosMock;
|
||||
|
||||
function renderComponent(post) {
|
||||
const mockThread = async (id, abuseFlagged) => {
|
||||
store = initializeStore();
|
||||
Factory.resetAll();
|
||||
|
||||
axiosMock = new MockAdapter(getAuthenticatedHttpClient());
|
||||
axiosMock.onGet(`${courseConfigApiUrl}${courseId}/settings`).reply(200, {});
|
||||
axiosMock.onGet(`${courseConfigApiUrl}${courseId}/`).reply(200, {
|
||||
learners_tab_enabled: true,
|
||||
has_moderation_privileges: true,
|
||||
});
|
||||
axiosMock.onGet(`${threadsApiUrl}${id}/`).reply(200, Factory.build('thread', {
|
||||
id, abuse_flagged: abuseFlagged,
|
||||
}));
|
||||
|
||||
await executeThunk(fetchCourseConfig(courseId), store.dispatch, store.getState);
|
||||
await executeThunk(fetchThread(id), store.dispatch, store.getState);
|
||||
};
|
||||
|
||||
function renderComponent(id) {
|
||||
return render(
|
||||
<IntlProvider locale="en">
|
||||
<AppProvider store={store}>
|
||||
<DiscussionContext.Provider value={{ courseId }}>
|
||||
<PostLink post={post} key={post.id} isSelected={() => true} />
|
||||
<PostLink
|
||||
key={id}
|
||||
postId={id}
|
||||
/>
|
||||
</DiscussionContext.Provider>
|
||||
</AppProvider>
|
||||
</IntlProvider>,
|
||||
);
|
||||
}
|
||||
|
||||
const mockPost = {
|
||||
abuseFlagged: false,
|
||||
author: 'test-user',
|
||||
commentCount: 5,
|
||||
courseId: 'course-v1:edX+DemoX+Demo_Course',
|
||||
following: false,
|
||||
id: 'test-id',
|
||||
pinned: false,
|
||||
rawBody: '<p>a test post</p>',
|
||||
hasEndorsed: false,
|
||||
voted: false,
|
||||
voteCount: 10,
|
||||
previewBody: '<p>a test post</p>',
|
||||
read: false,
|
||||
title: 'test post',
|
||||
topicId: 'i4x-edx-eiorguegnru-course-foobarbaz',
|
||||
unreadCommentCount: 2,
|
||||
groupName: null,
|
||||
groupId: null,
|
||||
createdAt: '2022-02-25T09:17:17Z',
|
||||
closed: false,
|
||||
};
|
||||
|
||||
describe('PostFooter', () => {
|
||||
beforeEach(async () => {
|
||||
initializeMockApp({
|
||||
@@ -65,21 +70,17 @@ describe('PostFooter', () => {
|
||||
roles: [],
|
||||
},
|
||||
});
|
||||
store = initializeStore();
|
||||
axiosMock = new MockAdapter(getAuthenticatedHttpClient());
|
||||
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);
|
||||
});
|
||||
|
||||
it('has reported text only when abuseFlagged is true', async () => {
|
||||
renderComponent(mockPost);
|
||||
expect(screen.queryByTestId('reported-post')).toBeFalsy();
|
||||
it.each([true, false])('has reported text only when abuseFlagged is %s', async (abuseFlagged) => {
|
||||
await mockThread(postId, abuseFlagged);
|
||||
renderComponent(postId);
|
||||
|
||||
renderComponent({ ...mockPost, abuseFlagged: true });
|
||||
expect(screen.getByTestId('reported-post')).toBeTruthy();
|
||||
if (abuseFlagged) {
|
||||
expect(screen.getByText('Reported')).toBeTruthy();
|
||||
} else {
|
||||
expect(screen.queryByTestId('reported-post')).toBeFalsy();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -93,21 +94,15 @@ describe('Post username', () => {
|
||||
roles: [],
|
||||
},
|
||||
});
|
||||
store = initializeStore();
|
||||
axiosMock = new MockAdapter(getAuthenticatedHttpClient());
|
||||
axiosMock.onGet(`${courseConfigApiUrl}${courseId}/`).reply(200, {
|
||||
learners_tab_enabled: true,
|
||||
has_moderation_privileges: true,
|
||||
});
|
||||
axiosMock.onGet(`${courseConfigApiUrl}${courseId}/settings`).reply(200, {});
|
||||
await executeThunk(fetchCourseConfig(courseId), store.dispatch, store.getState);
|
||||
});
|
||||
|
||||
it.each([
|
||||
'anonymous',
|
||||
'test-user',
|
||||
])('is not clickable for %s user', async (user) => {
|
||||
renderComponent({ ...mockPost, author: user });
|
||||
])('is not clickable for %s user', async () => {
|
||||
await mockThread(postId, false);
|
||||
renderComponent(postId);
|
||||
|
||||
expect(screen.queryByTestId('learner-posts-link')).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user