({
+ useCourseListData: jest.fn(),
+}));
+
+jest.mock('containers/CourseCard', () => 'CourseCard');
+jest.mock('containers/CourseFilterControls', () => ({
+ ActiveCourseFilters: 'ActiveCourseFilters',
+ CourseFilterControls: 'CourseFilterControls',
+}));
+
+describe('CourseList', () => {
+ const defaultCourseListData = {
+ filterOptions: {},
+ numPages: 1,
+ setPageNumber: jest.fn().mockName('setPageNumber'),
+ showFilters: false,
+ visibleList: [],
+ };
+ const createWrapper = (courseListData) => {
+ useCourseListData.mockReturnValueOnce({
+ ...defaultCourseListData,
+ ...courseListData,
+ });
+ return shallow(
);
+ };
+
+ describe('snapshots', () => {
+ test('with no filters', () => {
+ const wrapper = createWrapper();
+ expect(wrapper).toMatchSnapshot();
+ });
+ test('with filters', () => {
+ const wrapper = createWrapper({
+ filterOptions: {
+ abitary: 'filter',
+ },
+ showFilters: true,
+ });
+ expect(wrapper).toMatchSnapshot();
+ });
+ test('with multiple courses and pages', () => {
+ const wrapper = createWrapper({
+ visibleList: [{ cardId: 'foo' }, { cardId: 'bar' }, { cardId: 'baz' }],
+ numPages: 3,
+ });
+ expect(wrapper).toMatchSnapshot();
+ });
+ });
+});
diff --git a/src/containers/Dashboard/__snapshots__/index.test.jsx.snap b/src/containers/Dashboard/__snapshots__/index.test.jsx.snap
new file mode 100644
index 0000000..fb763df
--- /dev/null
+++ b/src/containers/Dashboard/__snapshots__/index.test.jsx.snap
@@ -0,0 +1,86 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`Dashboard snapshots there are available dashboards 1`] = `
+
+
+
+
+`;
+
+exports[`Dashboard snapshots there are courses 1`] = `
+
+
+
+
+
+
+
+
+
+
+
+
+`;
+
+exports[`Dashboard snapshots there are no courses 1`] = `
+
+
+
+`;
+
+exports[`Dashboard snapshots there is a select session modal 1`] = `
+
+
+
+`;
diff --git a/src/containers/Dashboard/index.test.jsx b/src/containers/Dashboard/index.test.jsx
new file mode 100644
index 0000000..551b0e7
--- /dev/null
+++ b/src/containers/Dashboard/index.test.jsx
@@ -0,0 +1,120 @@
+import { shallow } from 'enzyme';
+
+import { hooks } from 'data/redux';
+
+import CourseList from 'containers/CourseList';
+import WidgetSidebar from 'containers/WidgetSidebar';
+import EmptyCourse from 'containers/EmptyCourse';
+import SelectSessionModal from 'containers/SelectSessionModal';
+import EnterpriseDashboardModal from 'containers/EnterpriseDashboardModal';
+
+import Dashboard from '.';
+
+jest.mock('data/redux', () => ({
+ thunkActions: {
+ app: {
+ initialize: jest.fn(),
+ },
+ },
+ hooks: {
+ useHasCourses: jest.fn(),
+ useHasAvailableDashboards: jest.fn(),
+ useShowSelectSessionModal: jest.fn(),
+ },
+}));
+
+jest.mock('containers/CourseList', () => 'CourseList');
+jest.mock('containers/WidgetSidebar', () => 'WidgetSidebar');
+jest.mock('containers/EmptyCourse', () => 'EmptyCourse');
+jest.mock('containers/SelectSessionModal', () => 'SelectSessionModal');
+jest.mock('containers/EnterpriseDashboardModal', () => 'EnterpriseDashboardModal');
+
+describe('Dashboard', () => {
+ const createWrapper = ({
+ hasCourses,
+ hasAvailableDashboards,
+ showSelectSessionModal,
+ }) => {
+ hooks.useHasCourses.mockReturnValueOnce(hasCourses);
+ hooks.useHasAvailableDashboards.mockReturnValueOnce(hasAvailableDashboards);
+ hooks.useShowSelectSessionModal.mockReturnValueOnce(showSelectSessionModal);
+ return shallow(
);
+ };
+
+ describe('snapshots', () => {
+ test('there are courses', () => {
+ const wrapper = createWrapper({
+ hasCourses: true,
+ hasAvailableDashboards: false,
+ showSelectSessionModal: false,
+ });
+ expect(wrapper).toMatchSnapshot();
+ });
+
+ test('there are no courses', () => {
+ const wrapper = createWrapper({
+ hasCourses: false,
+ hasAvailableDashboards: false,
+ showSelectSessionModal: false,
+ });
+ expect(wrapper).toMatchSnapshot();
+ });
+
+ test('there are available dashboards', () => {
+ const wrapper = createWrapper({
+ hasCourses: false,
+ hasAvailableDashboards: true,
+ showSelectSessionModal: false,
+ });
+ expect(wrapper).toMatchSnapshot();
+ });
+
+ test('there is a select session modal', () => {
+ const wrapper = createWrapper({
+ hasCourses: false,
+ hasAvailableDashboards: false,
+ showSelectSessionModal: true,
+ });
+ expect(wrapper).toMatchSnapshot();
+ });
+ });
+
+ describe('behavior', () => {
+ it('initializes the app without courses', () => {
+ const wrapper = createWrapper({
+ hasCourses: false,
+ hasAvailableDashboards: false,
+ showSelectSessionModal: false,
+ });
+ expect(wrapper.find(EmptyCourse).length).toEqual(1);
+ expect(wrapper.find(CourseList).length).toEqual(0);
+ expect(wrapper.find(WidgetSidebar).length).toEqual(0);
+ expect(wrapper.find(SelectSessionModal).length).toEqual(0);
+ expect(wrapper.find(EnterpriseDashboardModal).length).toEqual(0);
+ });
+ it('initializes the app with courses, dashboard and select', () => {
+ const wrapper = createWrapper({
+ hasCourses: true,
+ hasAvailableDashboards: true,
+ showSelectSessionModal: true,
+ });
+ expect(wrapper.find(EmptyCourse).length).toEqual(0);
+ expect(wrapper.find(CourseList).length).toEqual(1);
+ expect(wrapper.find(WidgetSidebar).length).toEqual(1);
+ expect(wrapper.find(SelectSessionModal).length).toEqual(1);
+ expect(wrapper.find(EnterpriseDashboardModal).length).toEqual(1);
+ });
+ it('initializes the app with courses, dashboard and no select', () => {
+ const wrapper = createWrapper({
+ hasCourses: true,
+ hasAvailableDashboards: true,
+ showSelectSessionModal: false,
+ });
+ expect(wrapper.find(EmptyCourse).length).toEqual(0);
+ expect(wrapper.find(CourseList).length).toEqual(1);
+ expect(wrapper.find(WidgetSidebar).length).toEqual(1);
+ expect(wrapper.find(SelectSessionModal).length).toEqual(0);
+ expect(wrapper.find(EnterpriseDashboardModal).length).toEqual(1);
+ });
+ });
+});
diff --git a/src/containers/LearnerDashboardHeader/AuthenticatedUserDropdown.test.jsx b/src/containers/LearnerDashboardHeader/AuthenticatedUserDropdown.test.jsx
new file mode 100644
index 0000000..0a8a9e8
--- /dev/null
+++ b/src/containers/LearnerDashboardHeader/AuthenticatedUserDropdown.test.jsx
@@ -0,0 +1,46 @@
+import { shallow } from 'enzyme';
+
+import { hooks as appHooks } from 'data/redux';
+import { AuthenticatedUserDropdown } from './AuthenticatedUserDropdown';
+import { useIsCollapsed } from './hooks';
+
+jest.mock('@edx/frontend-platform/react', () => ({
+ AppContext: {
+ authenticatedUser: {
+ profileImage: 'profileImage',
+ },
+ },
+}));
+jest.mock('data/redux', () => ({
+ hooks: {
+ useEnterpriseDashboardData: jest.fn(),
+ },
+}));
+jest.mock('containers/LearnerDashboardHeader/hooks', () => ({
+ useIsCollapsed: jest.fn(),
+}));
+
+describe('AuthenticatedUserDropdown', () => {
+ const props = {
+ username: 'username',
+ };
+ const defaultDashboardData = {
+ label: 'label',
+ url: 'url',
+ };
+
+ describe('snapshots', () => {
+ test('with enterprise dashboard', () => {
+ appHooks.useEnterpriseDashboardData.mockReturnValueOnce(defaultDashboardData);
+ useIsCollapsed.mockReturnValueOnce(true);
+ const wrapper = shallow(
);
+ expect(wrapper).toMatchSnapshot();
+ });
+ test('without enterprise dashboard and expanded', () => {
+ appHooks.useEnterpriseDashboardData.mockReturnValueOnce(null);
+ useIsCollapsed.mockReturnValueOnce(false);
+ const wrapper = shallow(
);
+ expect(wrapper).toMatchSnapshot();
+ });
+ });
+});
diff --git a/src/containers/LearnerDashboardHeader/GreetingBanner.jsx b/src/containers/LearnerDashboardHeader/GreetingBanner.jsx
index 1609b25..cddf1dd 100644
--- a/src/containers/LearnerDashboardHeader/GreetingBanner.jsx
+++ b/src/containers/LearnerDashboardHeader/GreetingBanner.jsx
@@ -3,12 +3,14 @@ import classNames from 'classnames';
import PropTypes from 'prop-types';
import { getConfig } from '@edx/frontend-platform';
-import { FormattedMessage } from '@edx/frontend-platform/i18n';
+import { useIntl } from '@edx/frontend-platform/i18n';
import { Image } from '@edx/paragon';
import messages from './messages';
export const GreetingBanner = ({ size }) => {
+ const { formatMessage } = useIntl();
+
let greetMessage;
const hour = new Date().getHours();
@@ -39,11 +41,11 @@ export const GreetingBanner = ({ size }) => {
{isSmall
? (
-
+ {formatMessage(greetMessage)}
) : (
-
+ {formatMessage(greetMessage)}
)}
diff --git a/src/containers/LearnerDashboardHeader/GreetingBanner.test.jsx b/src/containers/LearnerDashboardHeader/GreetingBanner.test.jsx
new file mode 100644
index 0000000..a6aafa7
--- /dev/null
+++ b/src/containers/LearnerDashboardHeader/GreetingBanner.test.jsx
@@ -0,0 +1,29 @@
+import { shallow } from 'enzyme';
+
+import { GreetingBanner } from './GreetingBanner';
+
+describe('GreetingBanner', () => {
+ const morning = new Date('2021-01-01T11:59:59.999');
+ const afternoon = new Date('2021-01-01T16:59:59.999');
+ const evening = new Date('2021-01-01T18:00:00');
+ afterAll(() => jest.useRealTimers());
+ describe('snapshots', () => {
+ ['small', 'large'].forEach((size) => {
+ test(`with size ${size} and morning`, () => {
+ jest.useFakeTimers('modern').setSystemTime(morning);
+ const wrapper = shallow(
-
-
-
-
-
-
-
-
-
-
-
-
+export const WidgetSidebar = () => {
+ const { formatMessage } = useIntl();
+ return (
+
+
+
+
+
+
+ {formatMessage(messages.lookingForChallengePrompt)}
+
+
+ {formatMessage(messages.findCoursesButton)}
+
+
+
+
-
-);
+ );
+};
export default WidgetSidebar;
diff --git a/src/containers/WidgetSidebar/index.test.jsx b/src/containers/WidgetSidebar/index.test.jsx
new file mode 100644
index 0000000..cb6bb21
--- /dev/null
+++ b/src/containers/WidgetSidebar/index.test.jsx
@@ -0,0 +1,12 @@
+import { shallow } from 'enzyme';
+
+import WidgetSidebar from '.';
+
+describe('WidgetSidebar', () => {
+ describe('snapshots', () => {
+ test('default', () => {
+ const wrapper = shallow(
);
+ expect(wrapper).toMatchSnapshot();
+ });
+ });
+});
diff --git a/src/setupTest.jsx b/src/setupTest.jsx
index f426f93..922eeb5 100755
--- a/src/setupTest.jsx
+++ b/src/setupTest.jsx
@@ -12,6 +12,7 @@ jest.mock('react', () => ({
useRef: jest.fn((val) => ({ current: val, useRef: true })),
useCallback: jest.fn((cb, prereqs) => ({ useCallback: { cb, prereqs } })),
useEffect: jest.fn((cb, prereqs) => ({ useEffect: { cb, prereqs } })),
+ useMemo: jest.fn((cb, prereqs) => cb(prereqs)),
useContext: jest.fn(context => context),
}));
@@ -72,8 +73,12 @@ jest.mock('@edx/paragon', () => jest.requireActual('testUtils').mockNestedCompon
Item: 'Dropdown.Item',
Menu: 'Dropdown.Menu',
Toggle: 'Dropdown.Toggle',
+ Header: 'Dropdown.Header',
+ Divider: 'Dropdown.Divider',
},
Form: {
+ Checkbox: 'Form.Checkbox',
+ CheckboxSet: 'Form.CheckboxSet',
Control: {
Feedback: 'Form.Control.Feedback',
},
@@ -98,16 +103,53 @@ jest.mock('@edx/paragon', () => jest.requireActual('testUtils').mockNestedCompon
Body: 'ModalDialog.Body',
Hero: 'ModalDialog.Hero',
},
+ ModalPopup: 'ModalPopup',
+ ModalCloseButton: 'ModalCloseButton',
MultiSelectDropdownFilter: 'MultiSelectDropdownFilter',
OverlayTrigger: 'OverlayTrigger',
Popover: {
Content: 'Popover.Content',
},
Row: 'Row',
+ Sheet: 'Sheet',
StatefulButton: 'StatefulButton',
TextFilter: 'TextFilter',
Spinner: 'Spinner',
PageBanner: 'PageBanner',
+ Pagination: 'Pagination',
+
+ useWindowSize: () => jest.fn(),
+ useToggle: () => jest.fn().mockImplementation((val) => [
+ val,
+ jest.fn().mockName('useToggle.setTrue'),
+ jest.fn().mockName('useToggle.setFalse'),
+ ]),
+ useCheckboxSetValues: () => jest.fn().mockImplementation((values) => ([values, {
+ add: jest.fn().mockName('useCheckboxSetValues.add'),
+ remove: jest.fn().mockName('useCheckboxSetValues.remove'),
+ }])),
+ breakpoints: () => ({
+ extraSmall: {
+ minWidth: 0,
+ maxWidth: 575,
+ },
+ small: {
+ minWidth: 576,
+ maxWidth: 767,
+ },
+ medium: {
+ minWidth: 768,
+ maxWidth: 991,
+ },
+ large: {
+ minWidth: 992,
+ maxWidth: 1199,
+ },
+ extraLarge: {
+ minWidth: 1200,
+ maxWidth: 100000,
+ },
+ }),
}));
jest.mock('@fortawesome/react-fontawesome', () => ({
diff --git a/src/testUtils.js b/src/testUtils.js
index 2b4b7d4..e39c8c3 100644
--- a/src/testUtils.js
+++ b/src/testUtils.js
@@ -35,6 +35,9 @@ export const formatMessage = (msg, values) => {
* mockNestedComponent('IconButton', 'IconButton');
*/
export const mockNestedComponent = (name, contents) => {
+ if (typeof contents === 'function') {
+ return contents();
+ }
if (typeof contents !== 'object') {
return contents;
}
@@ -169,6 +172,10 @@ export class MockUseState {
expect(this.hooks.state[key]).toHaveBeenCalledWith(value);
}
+ expectSetStateCalledWith(key, value) {
+ expect(this.setState[key]).toHaveBeenCalledWith(value);
+ }
+
/**
* Restore the hook module's state object to the actual code.
*/