From fc2c528bf725b8509ad96f76c46e01e6b573c760 Mon Sep 17 00:00:00 2001 From: Maxim Beder Date: Sat, 23 Oct 2021 21:40:39 +0200 Subject: [PATCH] test: add tests for TopicsView component --- src/discussions/topics/TopicsView.jsx | 2 +- src/discussions/topics/TopicsView.test.jsx | 186 ++++++++++++++++++ .../topics/data/__factories__/index.js | 1 + .../data/__factories__/topics.factory.js | 6 + .../topics/topic-group/TopicGroup.jsx | 6 +- 5 files changed, 199 insertions(+), 2 deletions(-) create mode 100644 src/discussions/topics/TopicsView.test.jsx create mode 100644 src/discussions/topics/data/__factories__/index.js create mode 100644 src/discussions/topics/data/__factories__/topics.factory.js diff --git a/src/discussions/topics/TopicsView.jsx b/src/discussions/topics/TopicsView.jsx index d71bed21..50f969a4 100644 --- a/src/discussions/topics/TopicsView.jsx +++ b/src/discussions/topics/TopicsView.jsx @@ -38,7 +38,7 @@ function TopicsView() { ), ); - if (nonCoursewareTopics && category === undefined) { + if (nonCoursewareTopics?.length > 1 && category === undefined) { topicElements.unshift(); } diff --git a/src/discussions/topics/TopicsView.test.jsx b/src/discussions/topics/TopicsView.test.jsx new file mode 100644 index 00000000..9798afbe --- /dev/null +++ b/src/discussions/topics/TopicsView.test.jsx @@ -0,0 +1,186 @@ +import { + fireEvent, queryByText, render, screen, waitFor, +} from '@testing-library/react'; +import MockAdapter from 'axios-mock-adapter'; +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 { API_BASE_URL } from '../../data/constants'; +import { initializeStore } from '../../store'; +import TopicsView from './TopicsView'; + +import './data/__factories__'; + +const courseId = 'course-v1:edX+TestX+Test_Course'; +const topicsApiUrl = `${API_BASE_URL}/api/discussion/v1/course_topics/${courseId}`; +let store; +let axiosMock; +let lastLocation; + +function renderComponent() { + render( + + + + + + + + + + { + lastLocation = location; + return null; + }} + /> + + + , + ); +} + +describe('TopicsView', () => { + beforeEach(() => { + initializeMockApp({ + authenticatedUser: { + userId: 3, + username: 'abc123', + administrator: true, + roles: [], + }, + }); + + store = initializeStore(); + Factory.resetAll(); + axiosMock = new MockAdapter(getAuthenticatedHttpClient()); + + lastLocation = undefined; + }); + + it('displays non-courseware topics', async () => { + axiosMock + .onGet(topicsApiUrl) + .reply(200, { + courseware_topics: [], + non_courseware_topics: Factory.buildList('topic', 3), + }); + renderComponent(); + + await screen.findByText('topic 1'); + expect(screen.queryByText('topic 2')).toBeInTheDocument(); + expect(screen.queryByText('topic 3')).toBeInTheDocument(); + }); + + it('displays non-courseware in one topic group', async () => { + axiosMock + .onGet(topicsApiUrl) + .reply(200, { + courseware_topics: [], + non_courseware_topics: Factory.buildList('topic', 3), + }); + renderComponent(); + + await screen.findByText('topic 1'); + const topicGroups = screen.queryAllByTestId('topic-group'); + expect(topicGroups).toHaveLength(1); + }); + + it('displays courseware topics', async () => { + axiosMock + .onGet(topicsApiUrl) + .reply(200, { + courseware_topics: Factory.buildList('topic', 3), + non_courseware_topics: [], + }); + renderComponent(); + + await screen.findByText('topic 1'); + expect(screen.queryByText('topic 2')).toBeInTheDocument(); + expect(screen.queryByText('topic 3')).toBeInTheDocument(); + }); + + it('displays courseware topics in individual topic groups', async () => { + axiosMock + .onGet(topicsApiUrl) + .reply(200, { + courseware_topics: Factory.buildList('topic', 3), + non_courseware_topics: [], + }); + renderComponent(); + + await screen.findByText('topic 1'); + const topicGroups = screen.queryAllByTestId('topic-group'); + expect(topicGroups).toHaveLength(3); + }); + + it('displays non-courseware topics before courseware topics', async () => { + axiosMock + .onGet(topicsApiUrl) + .reply(200, { + courseware_topics: Factory.buildList('topic', 3), + non_courseware_topics: Factory.buildList('topic', 3), + }); + renderComponent(); + + await screen.findByText('topic 1'); + const topicGroups = screen.queryAllByTestId('topic-group'); + expect(queryByText(topicGroups[0], 'topic 4')).toBeInTheDocument(); + expect(queryByText(topicGroups[0], 'topic 5')).toBeInTheDocument(); + expect(queryByText(topicGroups[0], 'topic 6')).toBeInTheDocument(); + expect(queryByText(topicGroups[1], 'topic 1')).toBeInTheDocument(); + expect(queryByText(topicGroups[2], 'topic 2')).toBeInTheDocument(); + expect(queryByText(topicGroups[3], 'topic 3')).toBeInTheDocument(); + }); + + it('clicking on courseware topic (category) takes to category page', async () => { + axiosMock + .onGet(topicsApiUrl) + .reply(200, { + courseware_topics: Factory.buildList('topic', 3), + non_courseware_topics: Factory.buildList('topic', 3), + }); + renderComponent(); + + const topic = await screen.findByText('topic 1'); + fireEvent.click(topic); + + await waitFor(() => expect(screen.queryByText('topic 2')).not.toBeInTheDocument()); + expect(lastLocation.pathname.endsWith('/category/topic 1')).toBeTruthy(); + }); + + it('on category page only selected category and it\'s children are displayed', async () => { + const category = Factory.build('topic'); + category.children = Factory.buildList('topic', 3); + axiosMock + .onGet(topicsApiUrl) + .reply(200, { + courseware_topics: [ + category, + Factory.build('topic'), + ], + non_courseware_topics: Factory.buildList('topic', 3), + }); + renderComponent(); + + const topic = await screen.findByText('topic 1'); + fireEvent.click(topic); + + await waitFor(() => expect(screen.queryByText('topic 6')).not.toBeInTheDocument()); + // children + expect(screen.queryByText('topic 2')).toBeInTheDocument(); + expect(screen.queryByText('topic 3')).toBeInTheDocument(); + expect(screen.queryByText('topic 4')).toBeInTheDocument(); + + // other courseware topics (categories) and non-courseware topics + expect(screen.queryByText('topic 5')).not.toBeInTheDocument(); + expect(screen.queryByText('topic 6')).not.toBeInTheDocument(); + expect(screen.queryByText('topic 7')).not.toBeInTheDocument(); + expect(screen.queryByText('topic 8')).not.toBeInTheDocument(); + }); +}); diff --git a/src/discussions/topics/data/__factories__/index.js b/src/discussions/topics/data/__factories__/index.js new file mode 100644 index 00000000..4a487958 --- /dev/null +++ b/src/discussions/topics/data/__factories__/index.js @@ -0,0 +1 @@ +import './topics.factory'; diff --git a/src/discussions/topics/data/__factories__/topics.factory.js b/src/discussions/topics/data/__factories__/topics.factory.js new file mode 100644 index 00000000..68a8eaaa --- /dev/null +++ b/src/discussions/topics/data/__factories__/topics.factory.js @@ -0,0 +1,6 @@ +import { Factory } from 'rosie'; + +Factory.define('topic') + .sequence('id', (idx) => `topic-${idx}`) + .sequence('name', (idx) => `topic ${idx}`) + .attr('children', []); diff --git a/src/discussions/topics/topic-group/TopicGroup.jsx b/src/discussions/topics/topic-group/TopicGroup.jsx index fd5e61f6..8f86bfb5 100644 --- a/src/discussions/topics/topic-group/TopicGroup.jsx +++ b/src/discussions/topics/topic-group/TopicGroup.jsx @@ -42,7 +42,11 @@ function TopicGroup({ } return ( -
+
{name && (