fix: fix topic info for course-wide discussion topics (#458)

* fix: fix topic info for course-wide discussion topics

* refactor: removed const and used url directly

* test: adds test cases for topic info

* test: updated test cases
This commit is contained in:
ayesha waris
2023-03-06 21:10:49 +05:00
committed by GitHub
parent f66cdda1b6
commit 24d02350a8
5 changed files with 67 additions and 13 deletions

View File

@@ -10,6 +10,7 @@ import { camelCaseObject, initializeMockApp } from '@edx/frontend-platform';
import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
import { AppProvider } from '@edx/frontend-platform/react';
import { getApiBaseUrl } from '../../data/constants';
import { initializeStore } from '../../store';
import { executeThunk } from '../../test-utils';
import { DiscussionContext } from '../common/context';
@@ -17,11 +18,13 @@ import { getCourseConfigApiUrl } from '../data/api';
import { fetchCourseConfig } from '../data/thunks';
import DiscussionContent from '../discussions-home/DiscussionContent';
import { getThreadsApiUrl } from '../posts/data/api';
import { fetchThreads } from '../posts/data/thunks';
import { fetchThread, fetchThreads } from '../posts/data/thunks';
import { fetchCourseTopics } from '../topics/data/thunks';
import { getCommentsApiUrl } from './data/api';
import '../posts/data/__factories__';
import './data/__factories__';
import '../topics/data/__factories__';
const courseConfigApiUrl = getCourseConfigApiUrl();
const commentsApiUrl = getCommentsApiUrl();
@@ -30,6 +33,7 @@ const discussionPostId = 'thread-1';
const questionPostId = 'thread-2';
const closedPostId = 'thread-2';
const courseId = 'course-v1:edX+TestX+Test_Course';
const topicsApiUrl = `${getApiBaseUrl()}/api/discussion/v1/course_topics/${courseId}`;
const reverseOrder = false;
let store;
let axiosMock;
@@ -83,6 +87,12 @@ function mockAxiosReturnPagedCommentsResponses() {
}
}
async function getThreadAPIResponse(threadId, topicId) {
axiosMock.onGet(`${threadsApiUrl}${discussionPostId}/`)
.reply(200, Factory.build('thread', { id: threadId, topic_id: topicId }));
await executeThunk(fetchThread(discussionPostId), store.dispatch, store.getState);
}
function renderComponent(postId) {
const wrapper = render(
<IntlProvider locale="en">
@@ -107,6 +117,45 @@ function renderComponent(postId) {
return wrapper;
}
describe('PostView', () => {
beforeEach(() => {
initializeMockApp({
authenticatedUser: {
userId: 3,
username: 'abc123',
administrator: true,
roles: [],
},
});
store = initializeStore();
Factory.resetAll();
axiosMock = new MockAdapter(getAuthenticatedHttpClient());
axiosMock.onGet(topicsApiUrl)
.reply(200, {
non_courseware_topics: Factory.buildList('topic', 1, {}, { topicPrefix: 'non-courseware-' }),
courseware_topics: Factory.buildList('category', 1, {}, { name: 'courseware' }),
});
executeThunk(fetchCourseTopics(courseId), store.dispatch, store.getState);
});
it('should show Topic Info for non-courseware topics', async () => {
await getThreadAPIResponse('thread-1', 'non-courseware-topic-1');
renderComponent(discussionPostId);
expect(await screen.findByText('Related to')).toBeInTheDocument();
expect(await screen.findByText('non-courseware-topic 1')).toBeInTheDocument();
});
it('should show Topic Info for courseware topics with category', async () => {
await getThreadAPIResponse('thread-2', 'courseware-topic-2');
renderComponent('thread-2');
expect(await screen.findByText('Related to')).toBeInTheDocument();
expect(await screen.findByText('category-1 / courseware-topic 2')).toBeInTheDocument();
});
});
describe('ThreadView', () => {
beforeEach(() => {
initializeMockApp({

View File

@@ -160,9 +160,9 @@ describe('PostsView', () => {
test('displays a list of posts in a topic', async () => {
setupStore();
await act(async () => {
await renderComponent({ topicId: 'some-topic-1' });
await renderComponent({ topicId: 'test-topic-1' });
});
expect(screen.getAllByText(/this is thread-\d+ in topic some-topic-1/i)).toHaveLength(Math.ceil(threadCount / 3));
expect(screen.getAllByText(/this is thread-\d+ in topic test-topic-1/i)).toHaveLength(Math.ceil(threadCount / 3));
});
test.each([true, false])(
@@ -173,10 +173,10 @@ describe('PostsView', () => {
blocks: {
'test-usage-key': {
type: 'vertical',
topics: ['some-topic-2', 'some-topic-0'],
topics: ['test-topic-2', 'test-topic-0'],
parent: 'test-seq-key',
},
'test-seq-key': { type: 'sequential', topics: ['some-topic-0', 'some-topic-1', 'some-topic-2'] },
'test-seq-key': { type: 'sequential', topics: ['test-topic-0', 'test-topic-1', 'test-topic-2'] },
},
},
config: { groupAtSubsection: grouping, hasModerationPrivileges: true, provider: 'openedx' },
@@ -185,12 +185,12 @@ describe('PostsView', () => {
await renderComponent({ category: 'test-usage-key', enableInContextSidebar: true, p: true });
});
const topicThreadCount = Math.ceil(threadCount / 3);
expect(screen.queryAllByText(/this is thread-\d+ in topic some-topic-2/i))
expect(screen.queryAllByText(/this is thread-\d+ in topic test-topic-2/i))
.toHaveLength(topicThreadCount);
expect(screen.queryAllByText(/this is thread-\d+ in topic some-topic-0/i))
expect(screen.queryAllByText(/this is thread-\d+ in topic test-topic-0/i))
.toHaveLength(topicThreadCount);
// When grouping is enabled, topic 1 will be shown, but not otherwise.
expect(screen.queryAllByText(/this is thread-\d+ in topic some-topic-1/i))
expect(screen.queryAllByText(/this is thread-\d+ in topic test-topic-1/i))
.toHaveLength(grouping ? topicThreadCount : 2);
},
);

View File

@@ -7,7 +7,7 @@ Factory.define('thread')
.sequence('rendered_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 => `some-topic-${(idx % 3)}`)
.sequence('topic_id', idx => `test-topic-${(idx % 3)}`)
.sequence('closed', idx => Boolean(idx % 3 === 2)) // Mark every 3rd post closed
.attr('comment_list_url', ['id'], (threadId) => `http://test.site/api/discussion/v1/comments/?thread_id=${threadId}`)
.attrs({

View File

@@ -102,7 +102,7 @@ describe('Threads/Posts data layer tests', () => {
expect(store.getState().threads.threadsById['thread-1'])
.toHaveProperty('topicId');
expect(store.getState().threads.threadsById['thread-1'].topicId)
.toEqual('some-topic-1');
.toEqual('test-topic-1');
});
test('successfully handles thread creation', async () => {

View File

@@ -5,6 +5,7 @@ import classNames from 'classnames';
import { useDispatch, useSelector } from 'react-redux';
import { useHistory, useLocation } from 'react-router-dom';
import { getConfig } from '@edx/frontend-platform';
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
import { Hyperlink, useToggle } from '@edx/paragon';
@@ -87,6 +88,10 @@ function Post({
topicData.usageKey ? getTopicSubsection(topicData.usageKey)?.displayName : topicData.categoryId
);
const getTopicInfo = topicData => (
getTopicCategoryName(topicData) ? `${getTopicCategoryName(topicData)} / ${topicData.name}` : `${topicData.name}`
);
return (
<div
className="d-flex flex-column w-100 mw-100 post-card-comment"
@@ -127,7 +132,7 @@ function Post({
<div className="d-flex mt-14px text-break font-style text-primary-500">
<HTMLLoader htmlNode={post.renderedBody} componentId="post" cssClassName="html-loader" testId={post.id} />
</div>
{topicContext && (
{(topicContext || topic) && (
<div
className={classNames('mt-14px mb-1 font-style font-size-12',
{ 'w-100': enableInContextSidebar })}
@@ -135,7 +140,7 @@ function Post({
>
<span className="text-gray-500" style={{ lineHeight: '20px' }}>{intl.formatMessage(messages.relatedTo)}{' '}</span>
<Hyperlink
destination={topicContext.unitLink}
destination={topicContext ? topicContext.unitLink : `${getConfig().BASE_URL}/${courseId}/topics/${post.topicId}`}
target="_top"
>
{(topicContext && !topic)
@@ -148,7 +153,7 @@ function Post({
<span className="w-auto">{topicContext.unitName}</span>
</>
)
: `${getTopicCategoryName(topic)} / ${topic.name}`}
: getTopicInfo(topic)}
</Hyperlink>
</div>
)}