test: added test cases for new sidebar (#1267)
* test: added test cases for new sidebar * test: added factory for verified user * refactor: updated description for notification widget
This commit is contained in:
@@ -1,18 +1,16 @@
|
||||
import React from 'react';
|
||||
|
||||
import { Factory } from 'rosie';
|
||||
import { getConfig } from '@edx/frontend-platform';
|
||||
import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
|
||||
import MockAdapter from 'axios-mock-adapter';
|
||||
|
||||
import { breakpoints } from '@edx/paragon';
|
||||
|
||||
import {
|
||||
act, fireEvent, getByRole, initializeTestStore, loadUnit, render, screen, waitFor,
|
||||
} from '../../setupTest';
|
||||
import { buildTopicsFromUnits } from '../data/__factories__/discussionTopics.factory';
|
||||
import { handleNextSectionCelebration } from './celebration';
|
||||
import * as celebrationUtils from './celebration/utils';
|
||||
import { handleNextSectionCelebration } from './celebration';
|
||||
import Course from './Course';
|
||||
import { executeThunk } from '../../utils';
|
||||
import * as thunks from '../data/thunks';
|
||||
import setupDiscussionSidebar from './test-utils';
|
||||
|
||||
jest.mock('@edx/frontend-platform/analytics');
|
||||
jest.mock('@edx/frontend-lib-special-exams/dist/data/thunks.js', () => ({
|
||||
@@ -51,26 +49,6 @@ describe('Course', () => {
|
||||
setItemSpy.mockRestore();
|
||||
});
|
||||
|
||||
const setupDiscussionSidebar = async () => {
|
||||
const courseHomeMetadata = Factory.build('courseHomeMetadata', { verified_mode: null });
|
||||
const testStore = await initializeTestStore({ provider: 'openedx', courseHomeMetadata });
|
||||
const state = testStore.getState();
|
||||
const { courseware: { courseId } } = state;
|
||||
const axiosMock = new MockAdapter(getAuthenticatedHttpClient());
|
||||
axiosMock.onGet(`${getConfig().LMS_BASE_URL}/api/discussion/v1/courses/${courseId}`).reply(200, { provider: 'openedx' });
|
||||
const topicsResponse = buildTopicsFromUnits(state.models.units);
|
||||
axiosMock.onGet(`${getConfig().LMS_BASE_URL}/api/discussion/v2/course_topics/${courseId}`)
|
||||
.reply(200, topicsResponse);
|
||||
|
||||
await executeThunk(thunks.getCourseDiscussionTopics(courseId), testStore.dispatch);
|
||||
const [firstUnitId] = Object.keys(state.models.units);
|
||||
mockData.unitId = firstUnitId;
|
||||
const [firstSequenceId] = Object.keys(state.models.sequences);
|
||||
mockData.sequenceId = firstSequenceId;
|
||||
|
||||
await render(<Course {...mockData} />, { store: testStore, wrapWithRouter: true });
|
||||
};
|
||||
|
||||
it('loads learning sequence', async () => {
|
||||
render(<Course {...mockData} />, { wrapWithRouter: true });
|
||||
expect(screen.getByRole('navigation', { name: 'breadcrumb' })).toBeInTheDocument();
|
||||
@@ -183,7 +161,7 @@ describe('Course', () => {
|
||||
});
|
||||
|
||||
it('handles click to open/close notification tray', async () => {
|
||||
render(<Course {...mockData} />, { wrapWithRouter: true });
|
||||
await setupDiscussionSidebar();
|
||||
const notificationShowButton = await screen.findByRole('button', { name: /Show notification tray/i });
|
||||
expect(screen.queryByRole('region', { name: /notification tray/i })).toHaveClass('d-none');
|
||||
fireEvent.click(notificationShowButton);
|
||||
|
||||
@@ -91,7 +91,7 @@ const SidebarBase = ({
|
||||
};
|
||||
|
||||
SidebarBase.propTypes = {
|
||||
title: PropTypes.string.isRequired,
|
||||
title: PropTypes.string,
|
||||
ariaLabel: PropTypes.string.isRequired,
|
||||
sidebarId: PropTypes.string.isRequired,
|
||||
className: PropTypes.string,
|
||||
@@ -103,6 +103,7 @@ SidebarBase.propTypes = {
|
||||
};
|
||||
|
||||
SidebarBase.defaultProps = {
|
||||
title: '',
|
||||
width: '50rem',
|
||||
allowFullHeight: false,
|
||||
showTitleBar: true,
|
||||
|
||||
@@ -12,6 +12,7 @@ import { executeThunk } from '../../../../../../utils';
|
||||
import { buildTopicsFromUnits } from '../../../../../data/__factories__/discussionTopics.factory';
|
||||
import { getCourseDiscussionTopics } from '../../../../../data/thunks';
|
||||
import SidebarContext from '../../../SidebarContext';
|
||||
import DiscussionsNotificationsSidebar from '../DiscussionsNotificationsSidebar';
|
||||
import DiscussionsWidget from './DiscussionsWidget';
|
||||
|
||||
initializeMockApp();
|
||||
@@ -51,24 +52,29 @@ describe('DiscussionsWidget', () => {
|
||||
await executeThunk(getCourseDiscussionTopics(courseId), store.dispatch);
|
||||
});
|
||||
|
||||
function renderWithProvider(testData = {}) {
|
||||
function renderWithProvider(Component, testData = {}) {
|
||||
const { container } = render(
|
||||
<SidebarContext.Provider value={{ ...mockData, ...testData }}>
|
||||
<DiscussionsWidget />
|
||||
<Component />
|
||||
</SidebarContext.Provider>,
|
||||
);
|
||||
return container;
|
||||
}
|
||||
|
||||
it('should show up if unit discussions associated with it', async () => {
|
||||
renderWithProvider();
|
||||
renderWithProvider(DiscussionsWidget);
|
||||
expect(screen.queryByTitle('Discussions')).toBeInTheDocument();
|
||||
expect(screen.queryByTitle('Discussions'))
|
||||
.toHaveAttribute('src', `http://localhost:2002/${courseId}/category/${unitId}?inContextSidebar`);
|
||||
});
|
||||
|
||||
it('should show nothing if unit has no discussions associated with it', async () => {
|
||||
renderWithProvider({ isDiscussionbarAvailable: false });
|
||||
renderWithProvider(DiscussionsWidget, { isDiscussionbarAvailable: false });
|
||||
expect(screen.queryByTitle('Discussions')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should display the Back to course button on small screens.', async () => {
|
||||
renderWithProvider(DiscussionsNotificationsSidebar, { shouldDisplayFullScreen: true });
|
||||
expect(screen.queryByText('Back to course')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -38,7 +38,7 @@ const NotificationsWidget = () => {
|
||||
if (hideNotificationbar || !isNotificationbarAvailable) { return null; }
|
||||
|
||||
return (
|
||||
<div className="border border-light-400 rounded-sm">
|
||||
<div className="border border-light-400 rounded-sm" data-testid="notification-widget">
|
||||
<UpgradeNotification
|
||||
offer={offer}
|
||||
verifiedMode={verifiedMode}
|
||||
|
||||
@@ -4,16 +4,19 @@ import React from 'react';
|
||||
import MockAdapter from 'axios-mock-adapter';
|
||||
import { Factory } from 'rosie';
|
||||
|
||||
import { getConfig } from '@edx/frontend-platform';
|
||||
import { mergeConfig, getConfig } from '@edx/frontend-platform';
|
||||
import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
|
||||
import { breakpoints } from '@edx/paragon';
|
||||
|
||||
import { initializeMockApp, render, screen } from '../../../../../../setupTest';
|
||||
import {
|
||||
initializeMockApp, render, screen, within, act, fireEvent, waitFor,
|
||||
} from '../../../../../../setupTest';
|
||||
import initializeStore from '../../../../../../store';
|
||||
import { appendBrowserTimezoneToUrl, executeThunk } from '../../../../../../utils';
|
||||
import { fetchCourse } from '../../../../../data';
|
||||
import SidebarContext from '../../../SidebarContext';
|
||||
import NotificationsWidget from './NotificationsWidget';
|
||||
import setupDiscussionSidebar from '../../../../test-utils';
|
||||
|
||||
initializeMockApp();
|
||||
jest.mock('@edx/frontend-platform/analytics');
|
||||
@@ -22,7 +25,6 @@ describe('NotificationsWidget', () => {
|
||||
let axiosMock;
|
||||
let store;
|
||||
const ID = 'NEWSIDEBAR';
|
||||
|
||||
const defaultMetadata = Factory.build('courseMetadata');
|
||||
const courseId = defaultMetadata.id;
|
||||
let courseMetadataUrl = `${getConfig().LMS_BASE_URL}/api/courseware/course/${defaultMetadata.id}`;
|
||||
@@ -47,6 +49,35 @@ describe('NotificationsWidget', () => {
|
||||
axiosMock = new MockAdapter(getAuthenticatedHttpClient());
|
||||
axiosMock.onGet(courseMetadataUrl).reply(200, defaultMetadata);
|
||||
axiosMock.onGet(courseHomeMetadataUrl).reply(200, courseHomeMetadata);
|
||||
mergeConfig({ ENABLE_NEW_SIDEBAR: 'true' }, 'Custom app config');
|
||||
});
|
||||
|
||||
it('successfully Open/Hide sidebar tray.', async () => {
|
||||
const userVerifiedMode = Factory.build('verifiedMode');
|
||||
|
||||
await setupDiscussionSidebar(userVerifiedMode);
|
||||
|
||||
const sidebarButton = await screen.getByRole('button', { name: /Show sidebar tray/i });
|
||||
|
||||
await act(async () => {
|
||||
fireEvent.click(sidebarButton);
|
||||
});
|
||||
|
||||
await waitFor(async () => {
|
||||
expect(screen.queryByTestId('sidebar-DISCUSSIONS_NOTIFICATIONS')).toBeInTheDocument();
|
||||
expect(screen.queryByTestId('notification-widget')).toBeInTheDocument();
|
||||
expect(screen.queryByTitle('Discussions')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
await act(async () => {
|
||||
fireEvent.click(sidebarButton);
|
||||
});
|
||||
|
||||
await waitFor(async () => {
|
||||
expect(screen.queryByTestId('sidebar-DISCUSSIONS_NOTIFICATIONS')).not.toBeInTheDocument();
|
||||
expect(screen.queryByTestId('notification-widget')).not.toBeInTheDocument();
|
||||
expect(screen.queryByTitle('Discussions')).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
it('renders upgrade card', async () => {
|
||||
@@ -90,6 +121,41 @@ describe('NotificationsWidget', () => {
|
||||
.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it.each([
|
||||
{
|
||||
description: 'close the notification widget.',
|
||||
enabledInContext: true,
|
||||
testId:
|
||||
'notification-widget',
|
||||
},
|
||||
{
|
||||
description: 'close the sidebar when the notification widget is closed, and the discussion widget is unavailable.',
|
||||
enabledInContext: false,
|
||||
testId: 'sidebar-DISCUSSIONS_NOTIFICATIONS',
|
||||
},
|
||||
])('successfully %s', async ({ enabledInContext, testId }) => {
|
||||
const userVerifiedMode = Factory.build('verifiedMode');
|
||||
|
||||
await setupDiscussionSidebar(userVerifiedMode, enabledInContext);
|
||||
|
||||
const sidebarButton = screen.getByRole('button', { name: /Show sidebar tray/i });
|
||||
|
||||
await act(async () => {
|
||||
fireEvent.click(sidebarButton);
|
||||
});
|
||||
|
||||
const notificationWidget = await waitFor(() => screen.getByTestId('notification-widget'));
|
||||
const closeNotificationButton = within(notificationWidget).getByRole('button', { name: /Close/i });
|
||||
|
||||
await act(async () => {
|
||||
fireEvent.click(closeNotificationButton);
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.queryByTestId(testId)).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
it('marks notification as seen 3 seconds later', async () => {
|
||||
jest.useFakeTimers();
|
||||
const onNotificationSeen = jest.fn();
|
||||
|
||||
49
src/courseware/course/test-utils.jsx
Normal file
49
src/courseware/course/test-utils.jsx
Normal file
@@ -0,0 +1,49 @@
|
||||
import React from 'react';
|
||||
import { Factory } from 'rosie';
|
||||
import { getConfig } from '@edx/frontend-platform';
|
||||
import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
|
||||
import MockAdapter from 'axios-mock-adapter';
|
||||
import { breakpoints } from '@edx/paragon';
|
||||
import { initializeTestStore, render } from '../../setupTest';
|
||||
import { buildTopicsFromUnits } from '../data/__factories__/discussionTopics.factory';
|
||||
import { executeThunk } from '../../utils';
|
||||
import * as thunks from '../data/thunks';
|
||||
import Course from './Course';
|
||||
|
||||
const mockData = {
|
||||
nextSequenceHandler: () => {},
|
||||
previousSequenceHandler: () => {},
|
||||
unitNavigationHandler: () => {},
|
||||
};
|
||||
|
||||
const setupDiscussionSidebar = async (verifiedMode = null, enabledInContext = true) => {
|
||||
const store = await initializeTestStore();
|
||||
const { courseware, models } = store.getState();
|
||||
const { courseId, sequenceId } = courseware;
|
||||
Object.assign(mockData, {
|
||||
courseId,
|
||||
sequenceId,
|
||||
unitId: Object.values(models.units)[0].id,
|
||||
});
|
||||
global.innerWidth = breakpoints.extraLarge.minWidth;
|
||||
|
||||
const courseHomeMetadata = Factory.build('courseHomeMetadata', { verified_mode: verifiedMode });
|
||||
const testStore = await initializeTestStore({ provider: 'openedx', courseHomeMetadata });
|
||||
const state = testStore.getState();
|
||||
const axiosMock = new MockAdapter(getAuthenticatedHttpClient());
|
||||
axiosMock.onGet(`${getConfig().LMS_BASE_URL}/api/discussion/v1/courses/${courseId}`).reply(200, { provider: 'openedx' });
|
||||
const topicsResponse = buildTopicsFromUnits(state.models.units, enabledInContext);
|
||||
axiosMock.onGet(`${getConfig().LMS_BASE_URL}/api/discussion/v2/course_topics/${courseId}`)
|
||||
.reply(200, topicsResponse);
|
||||
|
||||
await executeThunk(thunks.getCourseDiscussionTopics(courseId), testStore.dispatch);
|
||||
const [firstUnitId] = Object.keys(state.models.units);
|
||||
mockData.unitId = firstUnitId;
|
||||
const [firstSequenceId] = Object.keys(state.models.sequences);
|
||||
mockData.sequenceId = firstSequenceId;
|
||||
|
||||
const wrapper = await render(<Course {...mockData} />, { store: testStore, wrapWithRouter: true });
|
||||
return wrapper;
|
||||
};
|
||||
|
||||
export default setupDiscussionSidebar;
|
||||
@@ -1,6 +1,13 @@
|
||||
/* eslint-disable import/prefer-default-export */
|
||||
import { Factory } from 'rosie'; // eslint-disable-line import/no-extraneous-dependencies
|
||||
|
||||
Factory.define('verifiedMode')
|
||||
.attr('currency', 'USD')
|
||||
.attr('currencySymbol', '$')
|
||||
.attr('price', '$149')
|
||||
.attr('sku', '8CF08E5')
|
||||
.attr('upgradeUrl', 'http://localhost:18130/basket/add/?sku=8CF08E5');
|
||||
|
||||
Factory.define('discussionTopic')
|
||||
.option('topicPrefix', null, '')
|
||||
.option('courseId', null, 'course-v1:edX+DemoX+Demo_Course')
|
||||
@@ -11,13 +18,14 @@ Factory.define('discussionTopic')
|
||||
['id', 'courseId'],
|
||||
(idx, id, courseId) => `block-v1:${courseId.replace('course-v1:', '')}+type@vertical+block@${id}`,
|
||||
)
|
||||
.attr('enabled_in_context', null, true)
|
||||
.attr('enabled_in_context', ['enabled_in_context'], (enabledInContext) => Boolean(enabledInContext))
|
||||
|
||||
.attr('thread_counts', [], {
|
||||
discussion: 0,
|
||||
question: 0,
|
||||
});
|
||||
|
||||
// Given a pre-build units state, build topics from it.
|
||||
export function buildTopicsFromUnits(units) {
|
||||
return Object.values(units).map(unit => Factory.build('discussionTopic', { usage_key: unit.id }));
|
||||
export function buildTopicsFromUnits(units, enabledInContext = true) {
|
||||
return Object.values(units).map(unit => Factory.build('discussionTopic', { usage_key: unit.id, enabled_in_context: enabledInContext }));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user