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:
@@ -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({
|
||||
|
||||
@@ -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);
|
||||
},
|
||||
);
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -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 () => {
|
||||
|
||||
@@ -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>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user