@openedx/frontend-app-learner-dashboard 1.0.0-alpha.4 → 1.0.0-alpha.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (159) hide show
  1. package/package.json +4 -5
  2. package/src/app.scss +2 -5
  3. package/src/components/Banner.test.jsx +21 -21
  4. package/src/containers/CourseCard/CourseCard.scss +4 -6
  5. package/src/containers/CourseCard/components/CourseCardActions/ActionButton/hooks.test.js +27 -0
  6. package/src/containers/CourseCard/components/CourseCardActions/ActionButton/index.test.jsx +17 -14
  7. package/src/containers/CourseCard/components/CourseCardActions/BeginCourseButton.test.jsx +37 -34
  8. package/src/containers/CourseCard/components/CourseCardActions/ResumeButton.test.jsx +28 -28
  9. package/src/containers/CourseCard/components/CourseCardActions/SelectSessionButton.test.jsx +26 -17
  10. package/src/containers/CourseCard/components/CourseCardActions/ViewCourseButton.test.jsx +29 -19
  11. package/src/containers/CourseCard/components/CourseCardActions/index.test.jsx +32 -34
  12. package/src/containers/CourseCard/components/CourseCardBanners/CertificateBanner.test.jsx +205 -190
  13. package/src/containers/CourseCard/components/CourseCardBanners/CourseBanner.test.jsx +35 -62
  14. package/src/containers/CourseCard/components/CourseCardBanners/CreditBanner/hooks.test.js +3 -3
  15. package/src/containers/CourseCard/components/CourseCardBanners/CreditBanner/index.test.jsx +49 -79
  16. package/src/containers/CourseCard/components/CourseCardBanners/CreditBanner/views/ApprovedContent.jsx +1 -2
  17. package/src/containers/CourseCard/components/CourseCardBanners/CreditBanner/views/ApprovedContent.test.jsx +51 -34
  18. package/src/containers/CourseCard/components/CourseCardBanners/CreditBanner/views/EligibleContent.test.jsx +36 -44
  19. package/src/containers/CourseCard/components/CourseCardBanners/CreditBanner/views/MustRequestContent.jsx +1 -2
  20. package/src/containers/CourseCard/components/CourseCardBanners/CreditBanner/views/MustRequestContent.test.jsx +74 -44
  21. package/src/containers/CourseCard/components/CourseCardBanners/CreditBanner/views/PendingContent.jsx +1 -2
  22. package/src/containers/CourseCard/components/CourseCardBanners/CreditBanner/views/PendingContent.test.jsx +40 -34
  23. package/src/containers/CourseCard/components/CourseCardBanners/CreditBanner/views/RejectedContent.test.jsx +16 -26
  24. package/src/containers/CourseCard/components/CourseCardBanners/CreditBanner/views/components/CreditContent.test.jsx +38 -28
  25. package/src/containers/CourseCard/components/CourseCardBanners/CreditBanner/views/components/CreditRequestForm/hooks.test.js +6 -0
  26. package/src/containers/CourseCard/components/CourseCardBanners/CreditBanner/views/components/CreditRequestForm/index.test.jsx +25 -24
  27. package/src/containers/CourseCard/components/CourseCardBanners/CreditBanner/views/components/CreditRequestForm/ref.test.jsx +0 -3
  28. package/src/containers/CourseCard/components/CourseCardBanners/CreditBanner/views/components/ProviderLink.test.jsx +15 -13
  29. package/src/containers/CourseCard/components/CourseCardBanners/CreditBanner/views/hooks.test.js +3 -3
  30. package/src/containers/CourseCard/components/CourseCardBanners/EntitlementBanner.test.jsx +33 -24
  31. package/src/containers/CourseCard/components/CourseCardBanners/RelatedProgramsBanner/ProgramsList.test.jsx +13 -5
  32. package/src/containers/CourseCard/components/CourseCardBanners/RelatedProgramsBanner/index.test.jsx +34 -27
  33. package/src/containers/CourseCard/components/CourseCardBanners/index.test.jsx +44 -15
  34. package/src/containers/CourseCard/components/CourseCardDetails/hooks.test.js +19 -9
  35. package/src/containers/CourseCard/components/CourseCardDetails/index.scss +1 -5
  36. package/src/containers/CourseCard/components/CourseCardDetails/index.test.jsx +20 -8
  37. package/src/containers/CourseCard/components/CourseCardImage.test.jsx +48 -41
  38. package/src/containers/CourseCard/components/CourseCardMenu/SocialShareMenu.jsx +3 -4
  39. package/src/containers/CourseCard/components/CourseCardMenu/SocialShareMenu.test.jsx +62 -92
  40. package/src/containers/CourseCard/components/CourseCardMenu/hooks.js +8 -7
  41. package/src/containers/CourseCard/components/CourseCardMenu/hooks.test.js +8 -15
  42. package/src/containers/CourseCard/components/CourseCardMenu/index.jsx +2 -3
  43. package/src/containers/CourseCard/components/CourseCardMenu/index.test.jsx +78 -112
  44. package/src/containers/CourseCard/components/CourseCardTitle.test.jsx +51 -45
  45. package/src/containers/CourseCard/components/RelatedProgramsBadge/hooks.jsx +2 -2
  46. package/src/containers/CourseCard/components/RelatedProgramsBadge/hooks.test.js +18 -4
  47. package/src/containers/CourseCard/components/RelatedProgramsBadge/index.jsx +1 -1
  48. package/src/containers/CourseCard/components/RelatedProgramsBadge/index.test.jsx +17 -13
  49. package/src/containers/CourseCard/components/hooks.test.js +19 -8
  50. package/src/containers/CourseCard/hooks.test.js +12 -2
  51. package/src/containers/CourseCard/index.test.jsx +33 -12
  52. package/src/containers/CourseFilterControls/ActiveCourseFilters.test.jsx +19 -8
  53. package/src/containers/CourseFilterControls/CourseFilterControls.test.jsx +51 -36
  54. package/src/containers/CourseFilterControls/components/Checkbox.test.jsx +8 -5
  55. package/src/containers/CourseFilterControls/components/FilterForm.test.jsx +45 -20
  56. package/src/containers/CourseFilterControls/components/SortForm.test.jsx +19 -9
  57. package/src/containers/CourseFilterControls/hooks.test.js +25 -13
  58. package/src/containers/CoursesPanel/CourseList/index.test.jsx +39 -22
  59. package/src/containers/CoursesPanel/NoCoursesView/index.scss +5 -7
  60. package/src/containers/CoursesPanel/NoCoursesView/index.test.jsx +26 -6
  61. package/src/containers/CoursesPanel/hooks.test.js +4 -4
  62. package/src/containers/CoursesPanel/index.scss +5 -7
  63. package/src/containers/CoursesPanel/index.test.jsx +55 -21
  64. package/src/containers/Dashboard/DashboardLayout.test.jsx +43 -52
  65. package/src/containers/Dashboard/LoadingView.test.jsx +5 -10
  66. package/src/containers/Dashboard/hooks.test.js +21 -19
  67. package/src/containers/Dashboard/index.scss +8 -10
  68. package/src/containers/Dashboard/index.test.jsx +51 -92
  69. package/src/containers/EmailSettingsModal/hooks.test.js +3 -3
  70. package/src/containers/EmailSettingsModal/index.test.jsx +27 -10
  71. package/src/containers/RelatedProgramsModal/components/ProgramCard.test.jsx +31 -11
  72. package/src/containers/RelatedProgramsModal/index.test.jsx +27 -15
  73. package/src/containers/SelectSessionModal/constants.js +0 -1
  74. package/src/containers/SelectSessionModal/hooks.test.js +15 -5
  75. package/src/containers/SelectSessionModal/index.test.jsx +24 -11
  76. package/src/containers/UnenrollConfirmModal/components/ConfirmPane.test.jsx +24 -8
  77. package/src/containers/UnenrollConfirmModal/components/FinishedPane.test.jsx +39 -14
  78. package/src/containers/UnenrollConfirmModal/components/ReasonPane.test.jsx +23 -6
  79. package/src/containers/UnenrollConfirmModal/hooks/index.test.js +74 -26
  80. package/src/containers/UnenrollConfirmModal/hooks/reasons.test.js +4 -4
  81. package/src/containers/UnenrollConfirmModal/index.test.jsx +34 -17
  82. package/src/data/constants/app.js +0 -3
  83. package/src/data/constants/app.test.js +0 -12
  84. package/src/data/redux/app/selectors/courseCard.test.js +4 -4
  85. package/src/data/redux/app/selectors/currentList.test.js +5 -3
  86. package/src/data/redux/app/selectors/simpleSelectors.test.js +1 -1
  87. package/src/data/redux/requests/reducer.test.js +1 -1
  88. package/src/data/redux/requests/selectors.js +1 -2
  89. package/src/data/redux/requests/selectors.test.js +1 -29
  90. package/src/data/services/lms/api.test.js +6 -6
  91. package/src/data/services/lms/urls.test.js +1 -1
  92. package/src/data/services/segment/utils.test.js +1 -1
  93. package/src/data/store.test.js +2 -2
  94. package/src/hooks/api.test.js +59 -64
  95. package/src/hooks/utils.test.js +4 -0
  96. package/src/segment.js +1 -2
  97. package/src/setupTest.jsx +0 -206
  98. package/src/slots/WidgetSidebarSlot/index.test.jsx +21 -11
  99. package/src/test/app.test.jsx +65 -75
  100. package/src/test/inspector.js +0 -6
  101. package/src/test/messages.js +1 -1
  102. package/src/testUtils.js +1 -1
  103. package/src/tracking/trackers/course.test.js +4 -4
  104. package/src/tracking/trackers/credit.test.js +5 -5
  105. package/src/tracking/trackers/engagement.test.js +3 -3
  106. package/src/tracking/trackers/entitlements.test.js +3 -3
  107. package/src/tracking/trackers/filter.test.js +3 -3
  108. package/src/tracking/trackers/findCourses.test.js +3 -3
  109. package/src/tracking/trackers/socialShare.test.js +2 -2
  110. package/src/utils/StrictDict.test.js +4 -12
  111. package/src/widgets/LearnerDashboardHeader/ConfirmEmailBanner/messages.js +1 -1
  112. package/src/widgets/LearnerDashboardHeader/MasqueradeBar/index.scss +10 -12
  113. package/src/widgets/LearnerDashboardHeader/hooks.js +1 -1
  114. package/src/widgets/LookingForChallengeWidget/index.test.jsx +22 -8
  115. package/src/__snapshots__/App.test.jsx.snap +0 -83
  116. package/src/__snapshots__/index.test.jsx.snap +0 -43
  117. package/src/components/__snapshots__/Banner.test.jsx.snap +0 -31
  118. package/src/containers/CourseCard/__snapshots__/index.test.jsx.snap +0 -111
  119. package/src/containers/CourseCard/components/CourseCardActions/ActionButton/__snapshots__/index.test.jsx.snap +0 -14
  120. package/src/containers/CourseCard/components/CourseCardActions/__snapshots__/BeginCourseButton.test.jsx.snap +0 -39
  121. package/src/containers/CourseCard/components/CourseCardActions/__snapshots__/ResumeButton.test.jsx.snap +0 -39
  122. package/src/containers/CourseCard/components/CourseCardActions/__snapshots__/SelectSessionButton.test.jsx.snap +0 -19
  123. package/src/containers/CourseCard/components/CourseCardActions/__snapshots__/ViewCourseButton.test.jsx.snap +0 -39
  124. package/src/containers/CourseCard/components/CourseCardBanners/CreditBanner/__snapshots__/index.test.jsx.snap +0 -58
  125. package/src/containers/CourseCard/components/CourseCardBanners/CreditBanner/views/components/CreditRequestForm/__snapshots__/index.test.jsx.snap +0 -32
  126. package/src/containers/CourseCard/components/CourseCardBanners/CreditBanner/views/components/__snapshots__/CreditContent.test.jsx.snap +0 -60
  127. package/src/containers/CourseCard/components/CourseCardBanners/CreditBanner/views/components/__snapshots__/ProviderLink.test.jsx.snap +0 -11
  128. package/src/containers/CourseCard/components/CourseCardBanners/RelatedProgramsBanner/__snapshots__/ProgramsList.test.jsx.snap +0 -28
  129. package/src/containers/CourseCard/components/CourseCardBanners/RelatedProgramsBanner/__snapshots__/index.test.jsx.snap +0 -29
  130. package/src/containers/CourseCard/components/CourseCardBanners/__snapshots__/CertificateBanner.test.jsx.snap +0 -205
  131. package/src/containers/CourseCard/components/CourseCardBanners/__snapshots__/CourseBanner.test.jsx.snap +0 -38
  132. package/src/containers/CourseCard/components/CourseCardBanners/__snapshots__/EntitlementBanner.test.jsx.snap +0 -53
  133. package/src/containers/CourseCard/components/CourseCardBanners/__snapshots__/index.test.jsx.snap +0 -41
  134. package/src/containers/CourseCard/components/CourseCardDetails/__snapshots__/index.test.jsx.snap +0 -56
  135. package/src/containers/CourseCard/components/CourseCardMenu/__snapshots__/index.test.jsx.snap +0 -81
  136. package/src/containers/CourseCard/components/RelatedProgramsBadge/__snapshots__/index.test.jsx.snap +0 -25
  137. package/src/containers/CourseCard/components/__snapshots__/CourseCardImage.test.jsx.snap +0 -72
  138. package/src/containers/CourseCard/components/__snapshots__/CourseCardTitle.test.jsx.snap +0 -33
  139. package/src/containers/CourseFilterControls/__snapshots__/ActiveCourseFilters.test.jsx.snap +0 -39
  140. package/src/containers/CourseFilterControls/__snapshots__/CourseFilterControls.test.jsx.snap +0 -169
  141. package/src/containers/CourseFilterControls/components/__snapshots__/Checkbox.test.jsx.snap +0 -46
  142. package/src/containers/CourseFilterControls/components/__snapshots__/FilterForm.test.jsx.snap +0 -41
  143. package/src/containers/CourseFilterControls/components/__snapshots__/SortForm.test.jsx.snap +0 -29
  144. package/src/containers/CoursesPanel/CourseList/__snapshots__/index.test.jsx.snap +0 -70
  145. package/src/containers/CoursesPanel/NoCoursesView/__snapshots__/index.test.jsx.snap +0 -29
  146. package/src/containers/CoursesPanel/__snapshots__/index.test.jsx.snap +0 -55
  147. package/src/containers/Dashboard/__snapshots__/DashboardLayout.test.jsx.snap +0 -197
  148. package/src/containers/Dashboard/__snapshots__/LoadingView.test.jsx.snap +0 -13
  149. package/src/containers/Dashboard/__snapshots__/index.test.jsx.snap +0 -69
  150. package/src/containers/EmailSettingsModal/__snapshots__/index.test.jsx.snap +0 -133
  151. package/src/containers/RelatedProgramsModal/__snapshots__/index.test.jsx.snap +0 -169
  152. package/src/containers/RelatedProgramsModal/components/__snapshots__/ProgramCard.test.jsx.snap +0 -60
  153. package/src/containers/SelectSessionModal/__snapshots__/index.test.jsx.snap +0 -176
  154. package/src/containers/UnenrollConfirmModal/__snapshots__/index.test.jsx.snap +0 -101
  155. package/src/containers/UnenrollConfirmModal/components/__snapshots__/ConfirmPane.test.jsx.snap +0 -22
  156. package/src/containers/UnenrollConfirmModal/components/__snapshots__/FinishedPane.test.jsx.snap +0 -38
  157. package/src/containers/UnenrollConfirmModal/components/__snapshots__/ReasonPane.test.jsx.snap +0 -183
  158. package/src/slots/WidgetSidebarSlot/__snapshots__/index.test.jsx.snap +0 -14
  159. package/src/widgets/LookingForChallengeWidget/__snapshots__/index.test.jsx.snap +0 -45
@@ -1,29 +1,54 @@
1
- import { shallow } from '@edx/react-unit-test-utils';
1
+ import { render, screen, fireEvent } from '@testing-library/react';
2
+ import { IntlProvider } from '@openedx/frontend-base';
3
+ import { formatMessage } from '@src/testUtils';
4
+ import { FilterKeys } from '@src/data/constants/app';
5
+ import { FilterForm, filterOrder } from './FilterForm';
6
+ import messages from '../messages';
2
7
 
3
- import { FilterKeys } from 'data/constants/app';
4
- import FilterForm, { filterOrder } from './FilterForm';
8
+ const mockHandleFilterChange = jest.fn();
5
9
 
6
- jest.mock('./Checkbox', () => 'Checkbox');
10
+ const defaultProps = {
11
+ filters: [FilterKeys.inProgress],
12
+ handleFilterChange: mockHandleFilterChange,
13
+ };
14
+
15
+ const renderComponent = (props = defaultProps) => render(
16
+ <IntlProvider messages={{}}>
17
+ <FilterForm {...props} />
18
+ </IntlProvider>,
19
+ );
7
20
 
8
21
  describe('FilterForm', () => {
9
- const props = {
10
- filters: ['test-filter'],
11
- handleFilterChange: jest.fn().mockName('handleFilterChange'),
12
- };
13
- describe('snapshot', () => {
14
- test('renders', () => {
15
- const wrapper = shallow(<FilterForm {...props} />);
16
- expect(wrapper.snapshot).toMatchSnapshot();
22
+ beforeEach(() => {
23
+ jest.clearAllMocks();
24
+ });
25
+
26
+ it('renders all filter checkboxes in correct order', () => {
27
+ renderComponent();
28
+ const checkboxes = screen.getAllByRole('checkbox');
29
+ expect(checkboxes).toHaveLength(filterOrder.length);
30
+ checkboxes.forEach((checkbox, index) => {
31
+ expect(checkbox).toHaveAttribute('value', filterOrder[index]);
32
+ });
33
+ });
34
+
35
+ it('checks boxes based on filters prop', () => {
36
+ const filters = [FilterKeys.inProgress, FilterKeys.done];
37
+ renderComponent({ ...defaultProps, filters });
38
+ filters.forEach(filter => {
39
+ expect(screen.getByRole('checkbox', { name: formatMessage(messages[filter]) })).toBeChecked();
17
40
  });
18
41
  });
19
42
 
20
- test('filterOrder', () => {
21
- expect(filterOrder).toEqual([
22
- FilterKeys.inProgress,
23
- FilterKeys.notStarted,
24
- FilterKeys.done,
25
- FilterKeys.notEnrolled,
26
- FilterKeys.upgraded,
27
- ]);
43
+ it('calls handleFilterChange when checkbox is clicked', () => {
44
+ renderComponent();
45
+ const checkbox = screen.getByRole('checkbox', { name: formatMessage(messages.notStarted) });
46
+ fireEvent.click(checkbox);
47
+ expect(mockHandleFilterChange).toHaveBeenCalled();
48
+ });
49
+
50
+ it('displays course status heading', () => {
51
+ renderComponent();
52
+ expect(screen.getByText(/course status/i)).toBeInTheDocument();
28
53
  });
29
54
  });
@@ -1,19 +1,29 @@
1
- import { shallow } from '@edx/react-unit-test-utils';
1
+ import { render, screen } from '@testing-library/react';
2
+ import { IntlProvider } from '@openedx/frontend-base';
3
+ import { formatMessage } from '@src/testUtils';
2
4
 
3
- import { SortKeys } from 'data/constants/app';
5
+ import { SortKeys } from '@src/data/constants/app';
4
6
  import SortForm from './SortForm';
5
-
6
- jest.mock('./Checkbox', () => 'Checkbox');
7
+ import messages from '../messages';
7
8
 
8
9
  describe('SortForm', () => {
9
10
  const props = {
10
11
  handleSortChange: jest.fn().mockName('handleSortChange'),
11
12
  sortBy: SortKeys.enrolled,
12
13
  };
13
- describe('snapshot', () => {
14
- test('renders', () => {
15
- const wrapper = shallow(<SortForm {...props} />);
16
- expect(wrapper.snapshot).toMatchSnapshot();
17
- });
14
+ it('renders heading', () => {
15
+ render(<IntlProvider locale="en"><SortForm {...props} /></IntlProvider>);
16
+ const heading = screen.getByText(formatMessage(messages.sort));
17
+ expect(heading).toBeInTheDocument();
18
+ });
19
+ it('renders radio enrolled', () => {
20
+ render(<IntlProvider locale="en"><SortForm {...props} /></IntlProvider>);
21
+ const enrolled = screen.getByRole('radio', { name: formatMessage(messages.sortLastEnrolled) });
22
+ expect(enrolled).toBeInTheDocument();
23
+ });
24
+ it('renders radio title', () => {
25
+ render(<IntlProvider locale="en"><SortForm {...props} /></IntlProvider>);
26
+ const title = screen.getByRole('radio', { name: formatMessage(messages.sortTitle) });
27
+ expect(title).toBeInTheDocument();
18
28
  });
19
29
  });
@@ -1,26 +1,35 @@
1
1
  import { useToggle } from '@openedx/paragon';
2
2
 
3
- import { MockUseState } from 'testUtils';
4
- import { reduxHooks } from 'hooks';
3
+ import { MockUseState } from '@src/testUtils';
5
4
 
6
- import track from 'tracking';
5
+ import track from '@src/tracking';
6
+ import { reduxHooks } from '@src/hooks';
7
7
 
8
8
  import * as hooks from './hooks';
9
9
 
10
- jest.mock('tracking', () => ({
11
- filter: {
12
- filterClicked: jest.fn(),
13
- filterOptionSelected: jest.fn(),
14
- },
15
- }));
16
-
17
- jest.mock('hooks', () => ({
10
+ jest.mock('@src/hooks', () => ({
18
11
  reduxHooks: {
19
12
  useAddFilter: jest.fn(),
20
13
  useRemoveFilter: jest.fn(),
21
14
  },
22
15
  }));
23
16
 
17
+ jest.mock('@openedx/paragon', () => ({
18
+ ...jest.requireActual('@openedx/paragon'),
19
+ useToggle: jest.fn().mockImplementation((val) => [
20
+ val,
21
+ jest.fn().mockName('useToggle.setTrue'),
22
+ jest.fn().mockName('useToggle.setFalse'),
23
+ ]),
24
+ }));
25
+
26
+ jest.mock('@src/tracking', () => ({
27
+ filter: {
28
+ filterClicked: jest.fn(),
29
+ filterOptionSelected: jest.fn(),
30
+ },
31
+ }));
32
+
24
33
  const state = new MockUseState(hooks);
25
34
 
26
35
  describe('CourseFilterControls hooks', () => {
@@ -29,9 +38,12 @@ describe('CourseFilterControls hooks', () => {
29
38
  const setSortBy = jest.fn();
30
39
 
31
40
  const removeFilter = jest.fn();
32
- reduxHooks.useRemoveFilter.mockReturnValue(removeFilter);
33
41
  const addFilter = jest.fn();
34
- reduxHooks.useAddFilter.mockReturnValue(addFilter);
42
+
43
+ beforeEach(() => {
44
+ reduxHooks.useRemoveFilter.mockReturnValue(removeFilter);
45
+ reduxHooks.useAddFilter.mockReturnValue(addFilter);
46
+ });
35
47
 
36
48
  const toggleOpen = jest.fn();
37
49
  const toggleClose = jest.fn();
@@ -1,4 +1,4 @@
1
- import { shallow } from '@edx/react-unit-test-utils';
1
+ import { render, screen } from '@testing-library/react';
2
2
 
3
3
  import { useIsCollapsed } from './hooks';
4
4
  import CourseList from '.';
@@ -7,9 +7,9 @@ jest.mock('./hooks', () => ({
7
7
  useIsCollapsed: jest.fn(),
8
8
  }));
9
9
 
10
- jest.mock('containers/CourseCard', () => 'CourseCard');
11
- jest.mock('containers/CourseFilterControls', () => ({
12
- ActiveCourseFilters: 'ActiveCourseFilters',
10
+ jest.mock('../../CourseCard', () => jest.fn(() => <div>CourseCard</div>));
11
+ jest.mock('../../CourseFilterControls', () => ({
12
+ ActiveCourseFilters: jest.fn(() => <div>ActiveCourseFilters</div>),
13
13
  }));
14
14
 
15
15
  describe('CourseList', () => {
@@ -22,43 +22,60 @@ describe('CourseList', () => {
22
22
  };
23
23
  useIsCollapsed.mockReturnValue(false);
24
24
 
25
- const createWrapper = (courseListData = defaultCourseListData) => (
26
- shallow(<CourseList courseListData={courseListData} />)
25
+ const renderList = (courseListData = defaultCourseListData) => (
26
+ render(<CourseList courseListData={courseListData} />)
27
27
  );
28
28
 
29
29
  describe('no courses or filters', () => {
30
- test('snapshot', () => {
31
- const wrapper = createWrapper();
32
- expect(wrapper.snapshot).toMatchSnapshot();
30
+ it('should not render related components', () => {
31
+ renderList();
32
+ const filterControls = screen.queryByText('ActiveCourseFilters');
33
+ const courseCard = screen.queryByText('CourseCard');
34
+ const prevButton = screen.queryByRole('button', { name: 'Previous' });
35
+ expect(filterControls).toBeNull();
36
+ expect(courseCard).toBeNull();
37
+ expect(prevButton).toBeNull();
33
38
  });
34
39
  });
35
40
  describe('with filters', () => {
36
- test('snapshot', () => {
37
- const wrapper = createWrapper({
38
- filterOptions: { abitary: 'filter' },
41
+ it('should render filter component', () => {
42
+ renderList({
43
+ ...defaultCourseListData,
39
44
  showFilters: true,
40
45
  });
41
- expect(wrapper.snapshot).toMatchSnapshot();
46
+ const filterControls = screen.getByText('ActiveCourseFilters');
47
+ expect(filterControls).toBeInTheDocument();
42
48
  });
43
49
  });
44
50
  describe('with multiple courses and pages', () => {
45
- test('snapshot', () => {
46
- const wrapper = createWrapper({
47
- visibleList: [{ cardId: 'foo' }, { cardId: 'bar' }, { cardId: 'baz' }],
48
- numPages: 3,
51
+ it('render Course Cards and pagination', () => {
52
+ const visibleList = [{ cardId: 'foo' }, { cardId: 'bar' }, { cardId: 'baz' }];
53
+ const numPages = 3;
54
+ renderList({
55
+ ...defaultCourseListData,
56
+ visibleList,
57
+ numPages,
49
58
  });
50
- expect(wrapper.snapshot).toMatchSnapshot();
59
+ const courseCards = screen.getAllByText('CourseCard');
60
+ expect(courseCards.length).toEqual(visibleList.length);
61
+ const pageButtons = screen.getAllByRole('button', { name: /^Page/i });
62
+ expect(pageButtons.length).toBe(numPages);
51
63
  });
52
64
  });
53
65
  describe('collapsed with multiple courses and pages', () => {
54
- test('snapshot', () => {
66
+ it('should render correct components', () => {
67
+ const visibleList = [{ cardId: 'foo' }, { cardId: 'bar' }, { cardId: 'baz' }];
55
68
  useIsCollapsed.mockReturnValueOnce(true);
56
- const wrapper = createWrapper({
57
- visibleList: [{ cardId: 'foo' }, { cardId: 'bar' }, { cardId: 'baz' }],
69
+ renderList({
70
+ ...defaultCourseListData,
71
+ visibleList,
58
72
  numPages: 3,
59
73
  showFilters: true,
60
74
  });
61
- expect(wrapper.snapshot).toMatchSnapshot();
75
+ const courseCards = screen.getAllByText('CourseCard');
76
+ expect(courseCards.length).toEqual(visibleList.length);
77
+ const reducedPagination = screen.getByRole('button', { name: '1 of 3' });
78
+ expect(reducedPagination).toBeInTheDocument();
62
79
  });
63
80
  });
64
81
  });
@@ -1,15 +1,13 @@
1
- @import "@openedx/paragon/scss/core/core";
2
-
3
1
  #no-courses-content-view {
4
- border: 2px solid $light-400;
2
+ border: 2px solid var(--pgn-color-light-400);
5
3
  flex-direction: column;
6
- padding-bottom: map-get($spacers, 5);
7
- padding-top: map-get($spacers, 5);
4
+ padding-bottom: var(--pgn-spacing-spacer-5);
5
+ padding-top: var(--pgn-spacing-spacer-5);
8
6
  height: 100%;
9
7
 
10
8
  & > * {
11
- margin-top: map-get($spacers, 3);
12
- margin-bottom: map-get($spacers, 3);
9
+ margin-top: var(--pgn-spacing-spacer-3);
10
+ margin-bottom: var(--pgn-spacing-spacer-3);
13
11
  }
14
12
  }
15
13
 
@@ -1,18 +1,38 @@
1
- import React from 'react';
2
- import { shallow } from '@edx/react-unit-test-utils';
1
+ import { render, screen } from '@testing-library/react';
2
+ import { IntlProvider } from '@openedx/frontend-base';
3
+ import { formatMessage } from '@src/testUtils';
4
+ import { baseAppUrl } from '@src/data/services/lms/urls';
3
5
 
4
6
  import EmptyCourse from '.';
7
+ import messages from './messages';
5
8
 
6
- jest.mock('hooks', () => ({
9
+ const courseSearchUrl = '/course-search-url';
10
+
11
+ jest.mock('@src/hooks', () => ({
7
12
  reduxHooks: {
8
13
  usePlatformSettingsData: jest.fn(() => ({
9
- courseSearchUrl: '/course-search-url',
14
+ courseSearchUrl,
10
15
  })),
11
16
  },
12
17
  }));
13
18
 
14
19
  describe('NoCoursesView', () => {
15
- test('snapshot', () => {
16
- expect(shallow(<EmptyCourse />).snapshot).toMatchSnapshot();
20
+ it('should display image, heading and button', () => {
21
+ render(<IntlProvider locale="en"><EmptyCourse /></IntlProvider>);
22
+ const image = screen.getByRole('img', { alt: formatMessage(messages.bannerAlt) });
23
+ expect(image).toBeInTheDocument();
24
+ });
25
+ it('should display heading and prompt', () => {
26
+ render(<IntlProvider locale="en"><EmptyCourse /></IntlProvider>);
27
+ const heading = screen.getByText(formatMessage(messages.lookingForChallengePrompt));
28
+ const prompt = screen.getByText(formatMessage(messages.exploreCoursesPrompt));
29
+ expect(heading).toBeInTheDocument();
30
+ expect(prompt).toBeInTheDocument();
31
+ });
32
+ it('should display button', () => {
33
+ render(<IntlProvider locale="en"><EmptyCourse /></IntlProvider>);
34
+ const button = screen.getByRole('link', { name: formatMessage(messages.exploreCoursesButton) });
35
+ expect(button).toBeInTheDocument();
36
+ expect(button.href).toBe(baseAppUrl(courseSearchUrl));
17
37
  });
18
38
  });
@@ -1,9 +1,9 @@
1
- import { MockUseState } from 'testUtils';
2
- import { reduxHooks } from 'hooks';
3
- import { ListPageSize, SortKeys } from 'data/constants/app';
1
+ import { MockUseState } from '@src/testUtils';
2
+ import { reduxHooks } from '@src/hooks';
3
+ import { ListPageSize, SortKeys } from '@src/data/constants/app';
4
4
  import * as hooks from './hooks';
5
5
 
6
- jest.mock('hooks', () => ({
6
+ jest.mock('@src/hooks', () => ({
7
7
  reduxHooks: {
8
8
  useCurrentCourseList: jest.fn(),
9
9
  usePageNumber: jest.fn(() => 23),
@@ -1,5 +1,3 @@
1
- @import "@openedx/paragon/scss/core/core";
2
-
3
1
  .course-list-heading-container {
4
2
  display: flex;
5
3
  flex-direction: row;
@@ -8,8 +6,8 @@
8
6
  }
9
7
 
10
8
  .course-list-title {
11
- font-size: $h2-font-size;
12
- margin: map-get($spacers, 3) 0;
9
+ font-size: var(--pgn-typography-font-size-h2-base);
10
+ margin: var(--pgn-spacing-spacer-3) 0;
13
11
  }
14
12
 
15
13
  .course-list-loading {
@@ -24,7 +22,7 @@
24
22
  align-self: center;
25
23
  }
26
24
 
27
- @include media-breakpoint-down(md) {
25
+ @media (--pgn-size-breakpoint-max-width-md) {
28
26
  .course-list-heading-container {
29
27
  flex-direction: column-reverse;
30
28
  align-items: flex-start;
@@ -36,7 +34,7 @@
36
34
  }
37
35
 
38
36
  .course-list-title {
39
- font-size: $h3-font-size;
40
- margin: map-get($spacers, 2) 0;
37
+ font-size: var(--pgn-typography-font-size-h3-base);
38
+ margin: var(--pgn-spacing-spacer-2) 0;
41
39
  }
42
40
  }
@@ -1,32 +1,42 @@
1
- import { shallow } from '@edx/react-unit-test-utils';
1
+ import { render, screen } from '@testing-library/react';
2
+ import { IntlProvider } from '@openedx/frontend-base';
3
+ import { MemoryRouter } from 'react-router-dom';
4
+ import { FilterKeys } from '@src/data/constants/app';
5
+ import { reduxHooks } from '@src/hooks';
2
6
 
3
- import { reduxHooks } from 'hooks';
7
+ import messagesNoCourses from '@src/containers/CoursesPanel/NoCoursesView/messages';
4
8
  import { useCourseListData } from './hooks';
5
9
  import CoursesPanel from '.';
10
+ import messages from './messages';
6
11
 
7
- jest.mock('hooks', () => ({
8
- reduxHooks: { useHasCourses: jest.fn() },
12
+ const courseSearchUrl = '/course-search-url';
13
+
14
+ jest.mock('@src/hooks', () => ({
15
+ reduxHooks: {
16
+ useHasCourses: jest.fn(),
17
+ usePlatformSettingsData: jest.fn(() => ({
18
+ courseSearchUrl,
19
+ })),
20
+ },
9
21
  }));
10
22
 
11
23
  jest.mock('./hooks', () => ({
12
24
  useCourseListData: jest.fn(),
13
25
  }));
14
26
 
15
- jest.mock('containers/CourseCard', () => 'CourseCard');
16
- jest.mock('containers/CourseFilterControls', () => ({
17
- ActiveCourseFilters: 'ActiveCourseFilters',
18
- CourseFilterControls: 'CourseFilterControls',
19
- }));
20
- jest.mock('@openedx/frontend-plugin-framework', () => ({
21
- PluginSlot: 'PluginSlot',
27
+ jest.mock('@src/containers/CourseCard', () => jest.fn(() => <div>CourseCard</div>));
28
+ jest.mock('@src/containers/CourseFilterControls', () => ({
29
+ ActiveCourseFilters: jest.fn(() => <div>ActiveCourseFilters</div>),
30
+ CourseFilterControls: jest.fn(() => <div>CourseFilterControls</div>),
22
31
  }));
23
- jest.mock('./CourseList', () => 'CourseList');
32
+
33
+ const filters = Object.values(FilterKeys);
24
34
 
25
35
  reduxHooks.useHasCourses.mockReturnValue(true);
26
36
 
27
37
  describe('CoursesPanel', () => {
28
38
  const defaultCourseListData = {
29
- filterOptions: {},
39
+ filterOptions: { filters, handleRemoveFilter: jest.fn() },
30
40
  numPages: 1,
31
41
  setPageNumber: jest.fn().mockName('setPageNumber'),
32
42
  showFilters: false,
@@ -34,25 +44,49 @@ describe('CoursesPanel', () => {
34
44
  };
35
45
 
36
46
  const createWrapper = (courseListData) => {
37
- useCourseListData.mockReturnValueOnce({
47
+ useCourseListData.mockReturnValue({
38
48
  ...defaultCourseListData,
39
49
  ...courseListData,
40
50
  });
41
- return shallow(<CoursesPanel />);
51
+ return render(
52
+ <MemoryRouter>
53
+ <IntlProvider locale="en">
54
+ <CoursesPanel />
55
+ </IntlProvider>
56
+ </MemoryRouter>
57
+ );
42
58
  };
43
59
 
44
60
  describe('no courses', () => {
45
- test('snapshot', () => {
61
+ it('should render no courses view slot', () => {
46
62
  reduxHooks.useHasCourses.mockReturnValue(false);
47
- const wrapper = createWrapper();
48
- expect(wrapper.snapshot).toMatchSnapshot();
63
+ createWrapper();
64
+ const imgNoCourses = screen.getByRole('img', { name: messagesNoCourses.bannerAlt.defaultMessage });
65
+ expect(imgNoCourses).toBeInTheDocument();
66
+ const courseCard = screen.queryByText('CourseCard');
67
+ expect(courseCard).toBeNull();
49
68
  });
50
69
  });
51
70
  describe('with courses', () => {
52
- test('snapshot', () => {
71
+ it('should render courselist', () => {
72
+ const visibleList = [{ cardId: 'foo' }, { cardId: 'bar' }, { cardId: 'baz' }];
73
+ reduxHooks.useHasCourses.mockReturnValue(true);
74
+ createWrapper({ visibleList });
75
+ const courseCards = screen.getAllByText('CourseCard');
76
+ expect(courseCards.length).toEqual(visibleList.length);
77
+ });
78
+ it('displays course filter controls', () => {
79
+ reduxHooks.useHasCourses.mockReturnValue(true);
80
+ createWrapper();
81
+ expect(screen.getByText('CourseFilterControls')).toBeInTheDocument();
82
+ });
83
+
84
+ it('displays course list slot when courses exist', () => {
53
85
  reduxHooks.useHasCourses.mockReturnValue(true);
54
- const wrapper = createWrapper();
55
- expect(wrapper.snapshot).toMatchSnapshot();
86
+ const visibleList = [{ cardId: 'foo' }, { cardId: 'bar' }, { cardId: 'baz' }];
87
+ createWrapper({ visibleList });
88
+ const heading = screen.getByText(messages.myCourses.defaultMessage);
89
+ expect(heading).toBeInTheDocument();
56
90
  });
57
91
  });
58
92
  });
@@ -1,9 +1,8 @@
1
- import React from 'react';
2
- import { shallow } from '@edx/react-unit-test-utils';
3
- import { Col, Row } from '@openedx/paragon';
4
-
1
+ import { render, screen } from '@testing-library/react';
2
+ import { IntlProvider } from '@openedx/frontend-base';
3
+ import { MemoryRouter } from 'react-router-dom';
5
4
  import hooks from './hooks';
6
- import DashboardLayout, { columnConfig } from './DashboardLayout';
5
+ import DashboardLayout from './DashboardLayout';
7
6
 
8
7
  jest.mock('./hooks', () => ({
9
8
  useDashboardLayoutData: jest.fn(),
@@ -16,50 +15,55 @@ const hookProps = {
16
15
  };
17
16
  hooks.useDashboardLayoutData.mockReturnValue(hookProps);
18
17
 
19
- const children = 'test-children';
18
+ const children = <div>test children</div>;
20
19
 
21
- let el;
22
20
  describe('DashboardLayout', () => {
23
21
  beforeEach(() => {
24
22
  jest.clearAllMocks();
25
- el = shallow(<DashboardLayout>{children}</DashboardLayout>);
23
+ render(
24
+ <MemoryRouter>
25
+ <IntlProvider locale="en">
26
+ <DashboardLayout>{children}</DashboardLayout>
27
+ </IntlProvider>
28
+ </MemoryRouter>
29
+ );
26
30
  });
27
31
 
28
32
  const testColumns = () => {
29
- it('loads courseList and sidebar column layout', () => {
30
- const columns = el.instance.findByType(Row)[0].findByType(Col);
31
- Object.keys(columnConfig.sidebar).forEach(size => {
32
- expect(columns[1].props[size]).toEqual(columnConfig.sidebar[size]);
33
- });
33
+ it('loads courseList and sidebar column layout with corresponding children', () => {
34
+ const courseChildren = screen.getByText('test children');
35
+ const courseListCol = courseChildren.parentElement;
36
+ const sidebarCol = courseListCol.nextSibling;
37
+ expect(courseListCol).toHaveClass('course-list-column');
38
+ expect(sidebarCol).toHaveClass('sidebar-column');
34
39
  });
35
40
  it('displays children in first column', () => {
36
- const columns = el.instance.findByType(Row)[0].findByType(Col);
37
- expect(columns[0].children).not.toHaveLength(0);
41
+ const courseChildren = screen.getByText('test children');
42
+ const courseListCol = courseChildren.parentElement;
43
+ expect(courseChildren).toBeInTheDocument();
44
+ expect(courseListCol).toHaveClass('course-list-column');
38
45
  });
39
46
  it('displays WidgetSidebarSlot in second column', () => {
40
- const columns = el.instance.findByType(Row)[0].findByType(Col);
41
- expect(columns[1].findByType('WidgetSidebarSlot')).toHaveLength(1);
47
+ const courseListCol = screen.getByText('test children').parentElement;
48
+ const sidebarCol = courseListCol.nextSibling;
49
+ expect(sidebarCol).toHaveClass('sidebar-column');
50
+ expect(sidebarCol.children[0]).toHaveAttribute('id', 'looking-for-challenge-widget');
42
51
  });
43
52
  };
44
- const testSidebarLayout = () => {
53
+ const testSidebarLayout = ({ isCollapsed }) => {
45
54
  it('displays withSidebar width for course list column', () => {
46
- const columns = el.instance.findByType(Row)[0].findByType(Col);
47
- Object.keys(columnConfig.courseList.withSidebar).forEach(size => {
48
- expect(columns[0].props[size]).toEqual(columnConfig.courseList.withSidebar[size]);
49
- });
55
+ const courseListCol = screen.getByText('test children').parentElement;
56
+ expect(courseListCol).toHaveClass('col-xl-8');
57
+ const sidebarCol = courseListCol.nextSibling;
58
+ expect(sidebarCol).toHaveClass('sidebar-column', !isCollapsed && 'not-collapsed');
50
59
  });
51
60
  };
52
- const testNoSidebarLayout = () => {
61
+ const testNoSidebarLayout = ({ isCollapsed }) => {
53
62
  it('displays noSidebar width for course list column', () => {
54
- const columns = el.instance.findByType(Row)[0].findByType(Col);
55
- Object.keys(columnConfig.courseList.noSidebar).forEach(size => {
56
- expect(columns[0].props[size]).toEqual(columnConfig.courseList.noSidebar[size]);
57
- });
58
- });
59
- };
60
- const testSnapshot = () => {
61
- test('snapshot', () => {
62
- expect(el.snapshot).toMatchSnapshot();
63
+ const courseListCol = screen.getByText('test children').parentElement;
64
+ expect(courseListCol).toHaveClass('col-xl-12');
65
+ const sidebarCol = courseListCol.nextSibling;
66
+ expect(sidebarCol).toHaveClass('sidebar-column', !isCollapsed && 'not-collapsed');
63
67
  });
64
68
  };
65
69
  describe('collapsed', () => {
@@ -68,27 +72,18 @@ describe('DashboardLayout', () => {
68
72
  hooks.useDashboardLayoutData.mockReturnValueOnce({ ...hookProps, sidebarShowing: true });
69
73
  });
70
74
  testColumns();
71
- testSnapshot();
72
- testSidebarLayout();
75
+ testSidebarLayout({ isCollapsed: true });
73
76
  });
74
77
  describe('sidebar not showing', () => {
78
+ beforeEach(() => {
79
+ hooks.useDashboardLayoutData.mockReturnValueOnce({ ...hookProps });
80
+ });
75
81
  testColumns();
76
- testSnapshot();
77
- testNoSidebarLayout();
78
- });
79
- it('does not show spacer component above widget sidebar', () => {
80
- const columns = el.instance.findByType(Col);
81
- expect(columns[1].findByType('h2').length).toEqual(0);
82
+ testNoSidebarLayout({ isCollapsed: true });
82
83
  });
83
84
  });
84
85
 
85
86
  describe('not collapsed', () => {
86
- const testWidgetSpacing = () => {
87
- it('shows not-collapsed class on widget sidebar', () => {
88
- const columns = el.instance.findByType(Col);
89
- expect(columns[1].props.className).toContain('not-collapsed');
90
- });
91
- };
92
87
  describe('sidebar showing', () => {
93
88
  beforeEach(() => {
94
89
  hooks.useDashboardLayoutData.mockReturnValueOnce({
@@ -98,18 +93,14 @@ describe('DashboardLayout', () => {
98
93
  });
99
94
  });
100
95
  testColumns();
101
- testSnapshot();
102
- testSidebarLayout();
103
- testWidgetSpacing();
96
+ testSidebarLayout({ isCollapsed: false });
104
97
  });
105
98
  describe('sidebar not showing', () => {
106
99
  beforeEach(() => {
107
100
  hooks.useDashboardLayoutData.mockReturnValueOnce({ ...hookProps, isCollapsed: false });
108
101
  });
109
102
  testColumns();
110
- testSnapshot();
111
- testNoSidebarLayout();
112
- testWidgetSpacing();
103
+ testNoSidebarLayout({ isCollapsed: false });
113
104
  });
114
105
  });
115
106
  });