From 27ab7512d1e56b7b1a22cc372a97671440a2ff14 Mon Sep 17 00:00:00 2001 From: sundasnoreen12 Date: Thu, 23 Feb 2023 17:55:00 +0500 Subject: [PATCH] refactor: optimized code for post view list --- .../in-context-topics/TopicPostsView.test.jsx | 128 +++++++----------- src/discussions/in-context-topics/data/api.js | 3 +- .../topic-search/TopicSearchBar.jsx | 1 + 3 files changed, 54 insertions(+), 78 deletions(-) diff --git a/src/discussions/in-context-topics/TopicPostsView.test.jsx b/src/discussions/in-context-topics/TopicPostsView.test.jsx index 79020684..4fef3e1e 100644 --- a/src/discussions/in-context-topics/TopicPostsView.test.jsx +++ b/src/discussions/in-context-topics/TopicPostsView.test.jsx @@ -16,9 +16,10 @@ import { Routes } from '../../data/constants'; import { initializeStore } from '../../store'; import { executeThunk } from '../../test-utils'; import { DiscussionContext } from '../common/context'; +import { getThreadsApiUrl } from '../posts/data/api'; import { fetchThreads } from '../posts/data/thunks'; -import { getCourseTopicsApiUrl, getThreadsApiUrl } from './data/api'; -import { selectCoursewareTopics, selectNonCoursewareTopics } from './data/selectors'; +import { getCourseTopicsApiUrl } from './data/api'; +import { selectCoursewareTopics } from './data/selectors'; import { fetchCourseTopicsV3 } from './data/thunks'; import TopicPostsView from './TopicPostsView'; import TopicsView from './TopicsView'; @@ -35,34 +36,29 @@ let lastLocation; let container; async function renderComponent({ - topicId, category, enableInContextSidebar = false, -} = { myPosts: false }) { - let path = `/${courseId}/topics/`; - let page = 'topics'; + topicId, category, +} = { }) { + let path = `/${courseId}/topics`; if (topicId) { path = generatePath(Routes.POSTS.PATH, { courseId, topicId }); - page = 'posts'; } else if (category) { path = generatePath(Routes.TOPICS.CATEGORY, { courseId, category }); - page = 'category'; } const wrapper = await render( - - - - + + + + + + @@ -72,8 +68,8 @@ async function renderComponent({ return null; }} /> - - + + , ); @@ -81,7 +77,6 @@ async function renderComponent({ } describe('InContext Topic Posts View', () => { - let nonCoursewareTopics; let coursewareTopics; beforeEach(() => { @@ -99,9 +94,7 @@ describe('InContext Topic Posts View', () => { enableInContext: true, provider: 'openedx', hasModerationPrivileges: true, - blackouts: ['2021-12-31T10:15', '2021-12-31T10:20'], - visibility: false, - status: 'successful', + blackouts: [], }, }); Factory.resetAll(); @@ -119,68 +112,51 @@ describe('InContext Topic Posts View', () => { courseware: false, discussionCount: 1, questionCount: 1, - }).concat(Factory.buildList('section', 2, null, { topicPrefix: 'courseware' }))) + }) + .concat(Factory.buildList('section', 2, null, { topicPrefix: 'courseware' }))) .concat(Factory.buildList('archived-topics', 2, null))); await executeThunk(fetchCourseTopicsV3(courseId), store.dispatch, store.getState); const state = store.getState(); - nonCoursewareTopics = selectNonCoursewareTopics(state); coursewareTopics = selectCoursewareTopics(state); } - async function setupPostsMockResponse(id, count = 3) { + async function setupPostsMockResponse(topicId, numOfResponses = 3) { axiosMock.onGet(threadsApiUrl) .reply(() => { const threadAttrs = { previewBody: 'thread preview body' }; return [200, Factory.build('threadsResult', {}, { - topicId: id, + topicId, threadAttrs, - count, + count: numOfResponses, })]; }); await executeThunk(fetchThreads(courseId), store.dispatch, store.getState); } test.each([ - { parent: nonCoursewareTopics[0], topicType: 'NonCourseware' }, - { parent: coursewareTopics[0].children[0].children[0], topicType: 'courseware' }, - ])('\'$topicType\' topic should have a required number of post lengths.', async ({ parent }) => { + { parentId: 'noncourseware-topic-1', parentTitle: 'general-topic-1', topicType: 'NonCourseware' }, + { parentId: 'courseware-topic-1-v3-1', parentTitle: 'Introduction Introduction 1-1-1', topicType: 'Courseware' }, + ])('\'$topicType\' topic should have a required number of post lengths.', async ({ parentId, parentTitle }) => { await setupTopicsMockResponse(); + await setupPostsMockResponse(parentId, 3); await act(async () => { - setupPostsMockResponse(parent.id, 3); - renderComponent({ topicId: parent.id }); + renderComponent({ topicId: parentId }); }); await waitFor(async () => { const posts = await container.querySelectorAll('.discussion-post'); + const backButton = screen.getByLabelText('Back to topics list'); + const parentHeader = await screen.findByText(parentTitle); - expect(lastLocation.pathname.endsWith(`/topics/${parent.id}`)).toBeTruthy(); - expect(posts).toHaveLength(6); + expect(lastLocation.pathname.endsWith(`/topics/${parentId}`)).toBeTruthy(); + expect(posts).toHaveLength(3); + expect(backButton).toBeInTheDocument(); + expect(parentHeader).toBeInTheDocument(); }); }); - test.each([ - { parent: nonCoursewareTopics[0], topicType: 'NonCourseware' }, - { parent: coursewareTopics[0].children[0].children[0], topicType: 'courseware' }, - ])('\'$topicType\' topic should have a Back navigation bar with selected \'$topicType\' topic title.', - async ({ parent }) => { - await setupTopicsMockResponse(); - - await act(async () => { - setupPostsMockResponse(parent.id, 3); - renderComponent({ topicId: parent.id }); - }); - - await waitFor(async () => { - const backButton = await screen.getByLabelText('Back to topics list'); - const parentHeader = await screen.findByText(parent.name); - - expect(backButton).toBeInTheDocument(); - expect(parentHeader).toBeInTheDocument(); - }); - }); - it('A back button should redirect from list of posts to list of units.', async () => { await setupTopicsMockResponse(); const subSection = coursewareTopics[0].children[0]; @@ -195,11 +171,11 @@ describe('InContext Topic Posts View', () => { await act(async () => fireEvent.click(backButton)); await waitFor(async () => { - renderComponent({ topicId: undefined, category: subSection.id }); + renderComponent({ category: subSection.id }); const subSectionList = await container.querySelector('.list-group'); - const units = await subSectionList.querySelectorAll('.discussion-topic'); - const unitHeader = await within(subSectionList).queryByText(unit.name); + const units = subSectionList.querySelectorAll('.discussion-topic'); + const unitHeader = within(subSectionList).queryByText(unit.name); expect(lastLocation.pathname.endsWith(`/category/${subSection.id}`)).toBeTruthy(); expect(unitHeader).toBeInTheDocument(); @@ -211,7 +187,7 @@ describe('InContext Topic Posts View', () => { await setupTopicsMockResponse(); const subSection = coursewareTopics[0].children[0]; - renderComponent({ topicId: undefined, category: subSection.id }); + renderComponent({ category: subSection.id }); const backButton = await screen.getByLabelText('Back to topics list'); @@ -220,36 +196,37 @@ describe('InContext Topic Posts View', () => { renderComponent(); const sectionList = await container.querySelector('.list-group'); - const subSections = await sectionList.querySelectorAll('.discussion-topic-group'); - const subSectionHeader = await within(sectionList).queryByText(subSection.displayName); + const subSections = sectionList.querySelectorAll('.discussion-topic-group'); + const subSectionHeader = within(sectionList).queryByText(subSection.displayName); - expect(lastLocation.pathname.endsWith('/topics/')).toBeTruthy(); + expect(lastLocation.pathname.endsWith('/topics')).toBeTruthy(); expect(subSectionHeader).toBeInTheDocument(); expect(subSections).toHaveLength(3); }); }); test.each([ - { searchText: 'hello world', output: 'Showing 0 results for' }, - { searchText: 'introduction', output: 'Showing 8 results for' }, + { searchText: 'hello world', output: 'Showing 0 results for', resultCount: 0 }, + { searchText: 'introduction', output: 'Showing 8 results for', resultCount: 8 }, ])('It should have a search bar with a clear button and \'$output\' results found text.', - async ({ searchText, output }) => { + async ({ searchText, output, resultCount }) => { await setupTopicsMockResponse(); await renderComponent(); const searchField = await within(container).getByPlaceholderText('Search topics'); - const search = await container.querySelector('.pointer-cursor-hover'); - const searchButton = await search.querySelector('.pgn__icon'); + const searchButton = container.querySelector('[data-test-id = "search-icon"]'); fireEvent.change(searchField, { target: { value: searchText } }); await waitFor(async () => expect(searchField).toHaveValue(searchText)); await act(async () => fireEvent.click(searchButton)); await waitFor(async () => { const clearButton = await within(container).queryByText('Clear results'); - const searchMessage = await within(container).queryByText(`${output} "${searchText}"`); + const searchMessage = within(container).queryByText(`${output} "${searchText}"`); + const units = container.querySelectorAll('.discussion-topic'); expect(searchMessage).toBeInTheDocument(); expect(clearButton).toBeInTheDocument(); + expect(units).toHaveLength(resultCount); }); }); @@ -259,8 +236,7 @@ describe('InContext Topic Posts View', () => { const searchText = 'hello world'; const searchField = await within(container).getByPlaceholderText('Search topics'); - const search = await container.querySelector('.pointer-cursor-hover'); - const searchButton = await search.querySelector('.pgn__icon'); + const searchButton = container.querySelector('[data-test-id = "search-icon"]'); fireEvent.change(searchField, { target: { value: searchText } }); await waitFor(async () => expect(searchField).toHaveValue(searchText)); diff --git a/src/discussions/in-context-topics/data/api.js b/src/discussions/in-context-topics/data/api.js index 2066df4b..4745c8ae 100644 --- a/src/discussions/in-context-topics/data/api.js +++ b/src/discussions/in-context-topics/data/api.js @@ -1,11 +1,10 @@ /* eslint-disable import/prefer-default-export */ -import { camelCaseObject, getConfig } from '@edx/frontend-platform'; +import { camelCaseObject } from '@edx/frontend-platform'; import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth'; import { getApiBaseUrl } from '../../../data/constants'; export const getCourseTopicsApiUrl = () => `${getApiBaseUrl()}/api/discussion/v3/course_topics/`; -export const getThreadsApiUrl = () => `${getConfig().LMS_BASE_URL}/api/discussion/v1/threads/`; export async function getCourseTopicsV3(courseId) { const url = `${getApiBaseUrl()}/api/discussion/v3/course_topics/${courseId}`; diff --git a/src/discussions/in-context-topics/topic-search/TopicSearchBar.jsx b/src/discussions/in-context-topics/topic-search/TopicSearchBar.jsx index 116e8336..efcbab27 100644 --- a/src/discussions/in-context-topics/topic-search/TopicSearchBar.jsx +++ b/src/discussions/in-context-topics/topic-search/TopicSearchBar.jsx @@ -50,6 +50,7 @@ function TopicSearchBar({ intl }) { onSubmit(searchValue)} + data-test-id="search-icon" />