@openedx/frontend-app-learner-dashboard 1.0.0-alpha.3 → 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,35 +1,20 @@
1
+ import { IntlProvider } from '@openedx/frontend-base';
2
+ import { render, screen } from '@testing-library/react';
1
3
  import { when } from 'jest-when';
2
- import * as ReactShare from 'react-share';
3
-
4
- import { useIntl } from '@openedx/frontend-base';
5
- import { formatMessage, shallow } from '@edx/react-unit-test-utils';
6
-
7
- import track from 'tracking';
8
- import { reduxHooks } from 'hooks';
4
+ import track from '@src/tracking';
5
+ import { reduxHooks } from '@src/hooks';
6
+ import MasqueradeUserContext from '@src/data/contexts/MasqueradeUserContext';
9
7
 
10
8
  import { useEmailSettings } from './hooks';
11
- import SocialShareMenu, { testIds } from './SocialShareMenu';
9
+ import SocialShareMenu from './SocialShareMenu';
12
10
  import messages from './messages';
13
11
 
14
- jest.mock('react-share', () => ({
15
- FacebookShareButton: () => 'FacebookShareButton',
16
- TwitterShareButton: () => 'TwitterShareButton',
17
- }));
18
-
19
- jest.mock('tracking', () => ({
12
+ jest.mock('@src/tracking', () => ({
20
13
  socialShare: 'test-social-share-key',
21
14
  }));
22
15
 
23
- jest.mock('@openedx/frontend-base', () => ({
24
- ...jest.requireActual('@openedx/frontend-base'),
25
- useIntl: jest.fn().mockReturnValue({
26
- formatMessage: jest.requireActual('@edx/react-unit-test-utils').formatMessage,
27
- }),
28
- }));
29
-
30
- jest.mock('hooks', () => ({
16
+ jest.mock('@src/hooks', () => ({
31
17
  reduxHooks: {
32
- useMasqueradeData: jest.fn(),
33
18
  useCardCourseData: jest.fn(),
34
19
  useCardEnrollmentData: jest.fn(),
35
20
  useCardSocialSettingsData: jest.fn(),
@@ -47,9 +32,9 @@ const props = {
47
32
 
48
33
  const mockHook = (fn, returnValue, options = {}) => {
49
34
  if (options.isCardHook) {
50
- when(fn).calledWith(props.cardId).mockReturnValueOnce(returnValue);
35
+ when(fn).calledWith(props.cardId).mockReturnValue(returnValue);
51
36
  } else {
52
- when(fn).calledWith().mockReturnValueOnce(returnValue);
37
+ when(fn).calledWith().mockReturnValue(returnValue);
53
38
  }
54
39
  };
55
40
 
@@ -78,7 +63,6 @@ const mockHooks = (returnVals = {}) => {
78
63
  { isCardHook: true },
79
64
  );
80
65
  mockHook(reduxHooks.useCardCourseData, { courseName }, { isCardHook: true });
81
- mockHook(reduxHooks.useMasqueradeData, { isMasquerading: !!returnVals.isMasquerading });
82
66
  mockHook(
83
67
  reduxHooks.useCardSocialSettingsData,
84
68
  {
@@ -89,19 +73,19 @@ const mockHooks = (returnVals = {}) => {
89
73
  );
90
74
  };
91
75
 
92
- let el;
93
- const render = () => {
94
- el = shallow(<SocialShareMenu {...props} />);
95
- };
76
+ const renderComponent = (isMasquerading = false) => render(
77
+ <IntlProvider locale="en">
78
+ <MasqueradeUserContext.Provider value={{ isMasquerading }}>
79
+ <SocialShareMenu {...props} />
80
+ </MasqueradeUserContext.Provider>
81
+ </IntlProvider>,
82
+ );
96
83
 
97
84
  describe('SocialShareMenu', () => {
98
85
  describe('behavior', () => {
99
86
  beforeEach(() => {
100
87
  mockHooks();
101
- render();
102
- });
103
- it('initializes intl hook', () => {
104
- expect(useIntl).toHaveBeenCalledWith();
88
+ renderComponent();
105
89
  });
106
90
  it('initializes local hooks', () => {
107
91
  when(useEmailSettings).expectCalledWith();
@@ -110,7 +94,6 @@ describe('SocialShareMenu', () => {
110
94
  when(reduxHooks.useCardEnrollmentData).expectCalledWith(props.cardId);
111
95
  when(reduxHooks.useCardCourseData).expectCalledWith(props.cardId);
112
96
  when(reduxHooks.useCardSocialSettingsData).expectCalledWith(props.cardId);
113
- when(reduxHooks.useMasqueradeData).expectCalledWith();
114
97
  when(reduxHooks.useTrackCourseEvent).expectCalledWith(track.socialShare, props.cardId, 'twitter');
115
98
  when(reduxHooks.useTrackCourseEvent).expectCalledWith(track.socialShare, props.cardId, 'facebook');
116
99
  });
@@ -118,53 +101,45 @@ describe('SocialShareMenu', () => {
118
101
  describe('render', () => {
119
102
  it('renders null if exec ed course', () => {
120
103
  mockHooks({ isExecEd2UCourse: true });
121
- render();
122
- expect(el.isEmptyRender()).toEqual(true);
104
+ renderComponent();
105
+ const emailSettingsButton = screen.queryByRole('button', { name: messages.emailSettings.defaultMessage });
106
+ expect(emailSettingsButton).toBeNull();
107
+ const facebookShareButton = screen.queryByRole('button', { name: 'facebook' });
108
+ expect(facebookShareButton).toBeNull();
109
+ const twitterShareButton = screen.queryByRole('button', { name: 'twitter' });
110
+ expect(twitterShareButton).toBeNull();
123
111
  });
124
112
  const testEmailSettingsDropdown = (isMasquerading = false) => {
125
113
  describe('email settings dropdown', () => {
126
- const loadToggle = () => el.instance.findByTestId(testIds.emailSettingsModalToggle)[0];
127
114
  it('renders', () => {
128
- expect(el.instance.findByTestId(testIds.emailSettingsModalToggle).length).toEqual(1);
115
+ const emailSettingsButton = screen.getByRole('button', { name: messages.emailSettings.defaultMessage });
116
+ expect(emailSettingsButton).toBeInTheDocument();
129
117
  });
130
118
  if (isMasquerading) {
131
119
  it('is disabled', () => {
132
- expect(loadToggle().props.disabled).toEqual(true);
120
+ const emailSettingsButton = screen.getByRole('button', { name: messages.emailSettings.defaultMessage });
121
+ expect(emailSettingsButton).toBeInTheDocument();
122
+ expect(emailSettingsButton).toHaveAttribute('aria-disabled', 'true');
123
+ expect(emailSettingsButton).toHaveClass('disabled');
133
124
  });
134
125
  } else {
135
126
  it('is enabled', () => {
136
- expect(loadToggle().props.disabled).toEqual(false);
127
+ const emailSettingsButton = screen.getByRole('button', { name: messages.emailSettings.defaultMessage });
128
+ expect(emailSettingsButton).toBeEnabled();
137
129
  });
138
130
  }
139
- test('show email settings modal on click', () => {
140
- expect(loadToggle().props.onClick).toEqual(props.emailSettings.show);
141
- });
142
131
  });
143
132
  };
144
133
  const testFacebookShareButton = () => {
145
- test('renders facebook share button with courseName and brand', () => {
146
- const button = el.instance.findByType(ReactShare.FacebookShareButton)[0];
147
- expect(button.props.url).toEqual(socialShare.facebook.shareUrl);
148
- expect(button.props.onClick).toEqual(
149
- reduxHooks.useTrackCourseEvent(track.socialShare, props.cardId, 'facebook'),
150
- );
151
- expect(button.props.title).toEqual(formatMessage(messages.shareQuote, {
152
- courseName,
153
- socialBrand: socialShare.facebook.socialBrand,
154
- }));
134
+ it('renders facebook share button', () => {
135
+ const facebookShareButton = screen.getByRole('button', { name: 'facebook' });
136
+ expect(facebookShareButton).toBeInTheDocument();
155
137
  });
156
138
  };
157
139
  const testTwitterShareButton = () => {
158
- test('renders twitter share button with courseName and brand', () => {
159
- const button = el.instance.findByType(ReactShare.TwitterShareButton)[0];
160
- expect(button.props.url).toEqual(socialShare.twitter.shareUrl);
161
- expect(button.props.onClick).toEqual(
162
- reduxHooks.useTrackCourseEvent(track.socialShare, props.cardId, 'twitter'),
163
- );
164
- expect(button.props.title).toEqual(formatMessage(messages.shareQuote, {
165
- courseName,
166
- socialBrand: socialShare.twitter.socialBrand,
167
- }));
140
+ it('renders twitter share button', () => {
141
+ const twitterShareButton = screen.getByRole('button', { name: 'twitter' });
142
+ expect(twitterShareButton).toBeInTheDocument();
168
143
  });
169
144
  };
170
145
  describe('all enabled', () => {
@@ -174,19 +149,7 @@ describe('SocialShareMenu', () => {
174
149
  twitter: { isEnabled: true },
175
150
  isEmailEnabled: true,
176
151
  });
177
- render();
178
- });
179
- describe('email settings dropdown', () => {
180
- const loadToggle = () => el.instance.findByTestId(testIds.emailSettingsModalToggle)[0];
181
- it('renders', () => {
182
- expect(el.instance.findByTestId(testIds.emailSettingsModalToggle).length).toEqual(1);
183
- });
184
- it('is enabled', () => {
185
- expect(loadToggle().props.disabled).toEqual(false);
186
- });
187
- test('show email settings modal on click', () => {
188
- expect(loadToggle().props.onClick).toEqual(props.emailSettings.show);
189
- });
152
+ renderComponent();
190
153
  });
191
154
  testEmailSettingsDropdown();
192
155
  testFacebookShareButton();
@@ -194,42 +157,49 @@ describe('SocialShareMenu', () => {
194
157
  });
195
158
  describe('only email enabled', () => {
196
159
  beforeEach(() => {
160
+ jest.clearAllMocks();
197
161
  mockHooks({ isEmailEnabled: true });
198
- render();
162
+ renderComponent();
199
163
  });
200
164
  testEmailSettingsDropdown();
201
165
  it('does not render facebook or twitter controls', () => {
202
- expect(el.instance.findByType(ReactShare.FacebookShareButton).length).toEqual(0);
203
- expect(el.instance.findByType(ReactShare.TwitterShareButton).length).toEqual(0);
166
+ const facebookShareButton = screen.queryByRole('button', { name: 'facebook' });
167
+ expect(facebookShareButton).toBeNull();
168
+ const twitterShareButton = screen.queryByRole('button', { name: 'twitter' });
169
+ expect(twitterShareButton).toBeNull();
204
170
  });
205
- describe('masquerading', () => {
206
- beforeEach(() => {
207
- mockHooks({ isEmailEnabled: true, isMasquerading: true });
208
- render();
209
- });
210
- testEmailSettingsDropdown(true);
171
+ });
172
+ describe('masquerading', () => {
173
+ beforeEach(() => {
174
+ mockHooks({ isEmailEnabled: true });
175
+ renderComponent(true);
211
176
  });
177
+ testEmailSettingsDropdown(true);
212
178
  });
213
179
  describe('only facebook enabled', () => {
214
180
  beforeEach(() => {
215
181
  mockHooks({ facebook: { isEnabled: true } });
216
- render();
182
+ renderComponent();
217
183
  });
218
184
  testFacebookShareButton();
219
185
  it('does not render email or twitter controls', () => {
220
- expect(el.instance.findByTestId(testIds.emailSettingsModalToggle).length).toEqual(0);
221
- expect(el.instance.findByType(ReactShare.TwitterShareButton).length).toEqual(0);
186
+ const emailSettingsButton = screen.queryByRole('button', { name: messages.emailSettings.defaultMessage });
187
+ expect(emailSettingsButton).toBeNull();
188
+ const twitterShareButton = screen.queryByRole('button', { name: 'twitter' });
189
+ expect(twitterShareButton).toBeNull();
222
190
  });
223
191
  });
224
192
  describe('only twitter enabled', () => {
225
193
  beforeEach(() => {
226
194
  mockHooks({ twitter: { isEnabled: true } });
227
- render();
195
+ renderComponent();
228
196
  });
229
197
  testTwitterShareButton();
230
198
  it('does not render email or facebook controls', () => {
231
- expect(el.instance.findByTestId(testIds.emailSettingsModalToggle).length).toEqual(0);
232
- expect(el.instance.findByType(ReactShare.FacebookShareButton).length).toEqual(0);
199
+ const emailSettingsButton = screen.queryByRole('button', { name: messages.emailSettings.defaultMessage });
200
+ expect(emailSettingsButton).toBeNull();
201
+ const facebookShareButton = screen.queryByRole('button', { name: 'facebook' });
202
+ expect(facebookShareButton).toBeNull();
233
203
  });
234
204
  });
235
205
  });
@@ -1,15 +1,16 @@
1
- import { useKeyedState, StrictDict } from '@edx/react-unit-test-utils';
1
+ import { useState } from 'react';
2
2
 
3
- import track from '../../../../tracking';
4
3
  import { reduxHooks } from '../../../../hooks';
4
+ import track from '../../../../tracking';
5
+ import { StrictDict } from '../../../../utils';
5
6
 
6
- export const stateKeys = StrictDict({
7
- isUnenrollConfirmVisible: 'isUnenrollConfirmVisible',
8
- isEmailSettingsVisible: 'isEmailSettingsVisible',
7
+ export const state = StrictDict({
8
+ isUnenrollConfirmVisible: (val) => useState(val), // eslint-disable-line
9
+ isEmailSettingsVisible: (val) => useState(val), // eslint-disable-line
9
10
  });
10
11
 
11
12
  export const useUnenrollData = () => {
12
- const [isVisible, setIsVisible] = useKeyedState(stateKeys.isUnenrollConfirmVisible, false);
13
+ const [isVisible, setIsVisible] = state.isUnenrollConfirmVisible(false);
13
14
  return {
14
15
  show: () => setIsVisible(true),
15
16
  hide: () => setIsVisible(false),
@@ -18,7 +19,7 @@ export const useUnenrollData = () => {
18
19
  };
19
20
 
20
21
  export const useEmailSettings = () => {
21
- const [isVisible, setIsVisible] = useKeyedState(stateKeys.isEmailSettingsVisible, false);
22
+ const [isVisible, setIsVisible] = state.isEmailSettingsVisible(false);
22
23
  return {
23
24
  show: () => setIsVisible(true),
24
25
  hide: () => setIsVisible(false),
@@ -1,11 +1,10 @@
1
- import { mockUseKeyedState } from '@edx/react-unit-test-utils';
2
-
3
- import { reduxHooks } from 'hooks';
4
- import track from 'tracking';
1
+ import { reduxHooks } from '@src/hooks';
2
+ import track from '@src/tracking';
3
+ import { MockUseState } from '@src/testUtils';
5
4
 
6
5
  import * as hooks from './hooks';
7
6
 
8
- jest.mock('hooks', () => ({
7
+ jest.mock('@src/hooks', () => ({
9
8
  reduxHooks: {
10
9
  useCardCertificateData: jest.fn(),
11
10
  useCardEnrollmentData: jest.fn(),
@@ -19,7 +18,7 @@ reduxHooks.useTrackCourseEvent.mockReturnValue(trackCourseEvent);
19
18
  const cardId = 'test-card-id';
20
19
  let out;
21
20
 
22
- const state = mockUseKeyedState(hooks.stateKeys);
21
+ const state = new MockUseState(hooks);
23
22
 
24
23
  describe('CourseCardMenu hooks', () => {
25
24
  beforeEach(() => {
@@ -28,7 +27,6 @@ describe('CourseCardMenu hooks', () => {
28
27
  });
29
28
  describe('useUnenrollData', () => {
30
29
  beforeEach(() => {
31
- state.mockVals({ isUnenrollConfirmVisible: true });
32
30
  out = hooks.useUnenrollData();
33
31
  });
34
32
  describe('behavior', () => {
@@ -37,9 +35,6 @@ describe('CourseCardMenu hooks', () => {
37
35
  });
38
36
  });
39
37
  describe('output', () => {
40
- test('state is loaded from current state value', () => {
41
- expect(out.isVisible).toEqual(true);
42
- });
43
38
  test('show sets state value to true', () => {
44
39
  out.show();
45
40
  expect(state.setState.isUnenrollConfirmVisible).toHaveBeenCalledWith(true);
@@ -53,7 +48,6 @@ describe('CourseCardMenu hooks', () => {
53
48
 
54
49
  describe('useEmailSettings', () => {
55
50
  beforeEach(() => {
56
- state.mockVals({ isEmailSettingsVisible: true });
57
51
  out = hooks.useEmailSettings();
58
52
  });
59
53
  describe('behavior', () => {
@@ -62,9 +56,6 @@ describe('CourseCardMenu hooks', () => {
62
56
  });
63
57
  });
64
58
  describe('output', () => {
65
- test('state is loaded from current state value', () => {
66
- expect(out.isVisible).toEqual(state.values.isEmailSettingsVisible);
67
- });
68
59
  test('show sets state value to true', () => {
69
60
  out.show();
70
61
  expect(state.setState.isEmailSettingsVisible).toHaveBeenCalledWith(true);
@@ -77,7 +68,9 @@ describe('CourseCardMenu hooks', () => {
77
68
  });
78
69
 
79
70
  describe('useHandleToggleDropdown', () => {
80
- beforeEach(() => { out = hooks.useHandleToggleDropdown(cardId); });
71
+ beforeEach(() => {
72
+ out = hooks.useHandleToggleDropdown(cardId);
73
+ });
81
74
  describe('behavior', () => {
82
75
  it('initializes course event tracker with event name and card ID', () => {
83
76
  expect(reduxHooks.useTrackCourseEvent).toHaveBeenCalledWith(
@@ -4,7 +4,6 @@ import PropTypes from 'prop-types';
4
4
  import { useIntl } from '@openedx/frontend-base';
5
5
  import { Dropdown, Icon, IconButton } from '@openedx/paragon';
6
6
  import { MoreVert } from '@openedx/paragon/icons';
7
- import { StrictDict } from '@edx/react-unit-test-utils';
8
7
 
9
8
  import MasqueradeUserContext from '../../../../data/contexts/MasqueradeUserContext';
10
9
  import EmailSettingsModal from '../../../../containers/EmailSettingsModal';
@@ -21,9 +20,9 @@ import {
21
20
 
22
21
  import messages from './messages';
23
22
 
24
- export const testIds = StrictDict({
23
+ export const testIds = {
25
24
  unenrollModalToggle: 'unenrollModalToggle',
26
- });
25
+ };
27
26
 
28
27
  export const CourseCardMenu = ({ cardId }) => {
29
28
  const { formatMessage } = useIntl();
@@ -1,26 +1,21 @@
1
1
  import { when } from 'jest-when';
2
-
3
- import { Dropdown } from '@openedx/paragon';
4
- import { shallow } from '@edx/react-unit-test-utils';
5
- import { useIntl } from '@openedx/frontend-base';
6
-
7
- import EmailSettingsModal from 'containers/EmailSettingsModal';
8
- import UnenrollConfirmModal from 'containers/UnenrollConfirmModal';
9
- import { reduxHooks } from 'hooks';
10
- import SocialShareMenu from './SocialShareMenu';
2
+ import { render, screen } from '@testing-library/react';
3
+ import userEvent from '@testing-library/user-event';
4
+ import { IntlProvider } from '@openedx/frontend-base';
5
+ import { reduxHooks } from '@src/hooks';
6
+ import MasqueradeUserContext from '@src/data/contexts/MasqueradeUserContext';
11
7
  import * as hooks from './hooks';
12
- import CourseCardMenu, { testIds } from '.';
8
+ import CourseCardMenu from '.';
9
+ import messages from './messages';
13
10
 
14
- jest.mock('@openedx/frontend-base', () => ({
15
- ...jest.requireActual('@openedx/frontend-base'),
16
- useIntl: jest.fn().mockReturnValue({
17
- formatMessage: jest.requireActual('@edx/react-unit-test-utils').formatMessage,
18
- }),
11
+ jest.mock('@src/hooks', () => ({
12
+ reduxHooks: {
13
+ useCardEnrollmentData: jest.fn(),
14
+ },
19
15
  }));
20
- jest.mock('hooks', () => ({
21
- reduxHooks: { useMasqueradeData: jest.fn(), useCardEnrollmentData: jest.fn() },
22
- }));
23
- jest.mock('./SocialShareMenu', () => 'SocialShareMenu');
16
+ jest.mock('./SocialShareMenu', () => jest.fn(() => <div>SocialShareMenu</div>));
17
+ jest.mock('@src/containers/EmailSettingsModal', () => jest.fn(() => <div>EmailSettingsModal</div>));
18
+ jest.mock('@src/containers/UnenrollConfirmModal', () => jest.fn(() => <div>UnenrollConfirmModal</div>));
24
19
  jest.mock('./hooks', () => ({
25
20
  useEmailSettings: jest.fn(),
26
21
  useUnenrollData: jest.fn(),
@@ -44,13 +39,11 @@ const unenrollData = {
44
39
  hide: jest.fn().mockName('unenrollHide'),
45
40
  };
46
41
 
47
- let el;
48
-
49
42
  const mockHook = (fn, returnValue, options = {}) => {
50
43
  if (options.isCardHook) {
51
- when(fn).calledWith(props.cardId).mockReturnValueOnce(returnValue);
44
+ when(fn).calledWith(props.cardId).mockReturnValue(returnValue);
52
45
  } else {
53
- when(fn).calledWith().mockReturnValueOnce(returnValue);
46
+ when(fn).calledWith().mockReturnValue(returnValue);
54
47
  }
55
48
  };
56
49
 
@@ -74,7 +67,6 @@ const mockHooks = (returnVals = {}) => {
74
67
  },
75
68
  { isCardHook: true },
76
69
  );
77
- mockHook(reduxHooks.useMasqueradeData, { isMasquerading: !!returnVals.isMasquerading });
78
70
  mockHook(
79
71
  reduxHooks.useCardEnrollmentData,
80
72
  { isEmailEnabled: !!returnVals.isEmailEnabled },
@@ -82,18 +74,19 @@ const mockHooks = (returnVals = {}) => {
82
74
  );
83
75
  };
84
76
 
85
- const render = () => {
86
- el = shallow(<CourseCardMenu {...props} />);
87
- };
77
+ const renderComponent = (isMasquerading = false) => render(
78
+ <IntlProvider locale="en">
79
+ <MasqueradeUserContext.Provider value={{ isMasquerading }}>
80
+ <CourseCardMenu {...props} />
81
+ </MasqueradeUserContext.Provider>
82
+ </IntlProvider>
83
+ );
88
84
 
89
85
  describe('CourseCardMenu', () => {
90
- describe('behavior', () => {
86
+ describe('hooks', () => {
91
87
  beforeEach(() => {
92
88
  mockHooks();
93
- render();
94
- });
95
- it('initializes intl hook', () => {
96
- expect(useIntl).toHaveBeenCalledWith();
89
+ renderComponent();
97
90
  });
98
91
  it('initializes local hooks', () => {
99
92
  when(hooks.useEmailSettings).expectCalledWith();
@@ -102,54 +95,36 @@ describe('CourseCardMenu', () => {
102
95
  when(hooks.useOptionVisibility).expectCalledWith(props.cardId);
103
96
  });
104
97
  it('initializes redux hook data ', () => {
105
- when(reduxHooks.useMasqueradeData).expectCalledWith();
106
98
  when(reduxHooks.useCardEnrollmentData).expectCalledWith(props.cardId);
107
99
  });
108
100
  });
109
101
  describe('render', () => {
110
102
  it('renders null if showDropdown is false', () => {
111
103
  mockHooks();
112
- render();
113
- expect(el.isEmptyRender()).toEqual(true);
104
+ renderComponent();
105
+ const dropdown = screen.queryByRole('button', { name: messages.dropdownAlt.defaultMessage });
106
+ expect(dropdown).toBeNull();
114
107
  });
115
- const testHandleToggle = () => {
116
- it('displays Dropdown with onToggle=handleToggleDropdown', () => {
117
- expect(el.instance.findByType(Dropdown)[0].props.onToggle).toEqual(handleToggleDropdown);
118
- });
119
- };
120
- const testUnenrollConfirmModal = () => {
121
- it('displays UnenrollConfirmModal with cardId and unenrollModal data', () => {
122
- const modal = el.instance.findByType(UnenrollConfirmModal)[0];
123
- expect(modal.props.show).toEqual(unenrollData.isVisible);
124
- expect(modal.props.closeModal).toEqual(unenrollData.hide);
125
- expect(modal.props.cardId).toEqual(props.cardId);
126
- });
127
- };
128
- const testSocialShareMenu = () => {
129
- it('displays SocialShareMenu with cardID and emailSettings', () => {
130
- const menu = el.instance.findByType(SocialShareMenu)[0];
131
- expect(menu.props.cardId).toEqual(props.cardId);
132
- expect(menu.props.emailSettings).toEqual(emailSettings);
133
- });
134
- };
135
108
  describe('show dropdown', () => {
136
109
  describe('hide unenroll item and disable email', () => {
137
- beforeEach(() => {
138
- mockHooks({ shouldShowDropdown: true });
139
- render();
140
- });
141
- test('snapshot', () => {
142
- expect(el.snapshot).toMatchSnapshot();
143
- });
144
- testHandleToggle();
145
- testSocialShareMenu();
146
- it('does not render unenroll modal toggle', () => {
147
- expect(el.instance.findByTestId(testIds.unenrollModalToggle).length).toEqual(0);
148
- });
149
- it('does not render EmailSettingsModal', () => {
150
- expect(el.instance.findByType(EmailSettingsModal).length).toEqual(0);
110
+ it('displays Dropdown and renders SocialShareMenu and UnenrollConfirmModal', async () => {
111
+ mockHooks({
112
+ shouldShowDropdown: true,
113
+ });
114
+ renderComponent();
115
+ const user = userEvent.setup();
116
+ const dropdown = screen.getByRole('button', { name: messages.dropdownAlt.defaultMessage });
117
+ expect(dropdown).toBeInTheDocument();
118
+ await user.click(dropdown);
119
+ const unenrollOption = screen.queryByRole('button', { name: messages.unenroll.defaultMessage });
120
+ expect(unenrollOption).toBeNull();
121
+ const socialShareMenu = screen.getByText('SocialShareMenu');
122
+ expect(socialShareMenu).toBeInTheDocument();
123
+ const unenrollConfirmModal = screen.getByText('UnenrollConfirmModal');
124
+ expect(unenrollConfirmModal).toBeInTheDocument();
125
+ const emailSettingsModal = screen.queryByText('EmailSettingsModal');
126
+ expect(emailSettingsModal).toBeNull();
151
127
  });
152
- testUnenrollConfirmModal();
153
128
  });
154
129
  describe('show unenroll and enable email', () => {
155
130
  const hookProps = {
@@ -157,57 +132,48 @@ describe('CourseCardMenu', () => {
157
132
  isEmailEnabled: true,
158
133
  shouldShowUnenrollItem: true,
159
134
  };
160
- beforeEach(() => {
161
- mockHooks(hookProps);
162
- render();
163
- });
164
- test('snapshot', () => {
165
- expect(el.snapshot).toMatchSnapshot();
166
- });
167
- testHandleToggle();
168
- testSocialShareMenu();
169
135
  describe('unenroll modal toggle', () => {
170
- let toggle;
171
136
  describe('not masquerading', () => {
172
- beforeEach(() => {
137
+ it('renders all components', async () => {
173
138
  mockHooks(hookProps);
174
- render();
175
- [toggle] = el.instance.findByTestId(testIds.unenrollModalToggle);
176
- });
177
- it('renders unenroll modal toggle', () => {
178
- expect(el.instance.findByTestId(testIds.unenrollModalToggle).length).toEqual(1);
179
- });
180
- test('onClick from unenroll modal hook', () => {
181
- expect(toggle.props.onClick).toEqual(unenrollData.show);
182
- });
183
- test('disabled', () => {
184
- expect(toggle.props.disabled).toEqual(false);
139
+ renderComponent();
140
+
141
+ const user = userEvent.setup();
142
+ const dropdown = screen.getByRole('button', { name: messages.dropdownAlt.defaultMessage });
143
+ expect(dropdown).toBeInTheDocument();
144
+ await user.click(dropdown);
145
+
146
+ const unenrollOption = screen.getByRole('button', { name: messages.unenroll.defaultMessage });
147
+ expect(unenrollOption).toBeInTheDocument();
148
+ const socialShareMenu = screen.getByText('SocialShareMenu');
149
+ expect(socialShareMenu).toBeInTheDocument();
150
+ const unenrollConfirmModal = screen.getByText('UnenrollConfirmModal');
151
+ expect(unenrollConfirmModal).toBeInTheDocument();
152
+ const emailSettingsModal = screen.getByText('EmailSettingsModal');
153
+ expect(emailSettingsModal).toBeInTheDocument();
185
154
  });
186
155
  });
187
156
  describe('masquerading', () => {
188
- beforeEach(() => {
189
- mockHooks({ ...hookProps, isMasquerading: true });
190
- render();
191
- [toggle] = el.instance.findByTestId(testIds.unenrollModalToggle);
192
- });
193
- it('renders', () => {
194
- expect(el.instance.findByTestId(testIds.unenrollModalToggle).length).toEqual(1);
195
- });
196
- test('onClick from unenroll modal hook', () => {
197
- expect(toggle.props.onClick).toEqual(unenrollData.show);
198
- });
199
- test('disabled', () => {
200
- expect(toggle.props.disabled).toEqual(true);
157
+ it('renders but unenroll is disabled', async () => {
158
+ mockHooks({ ...hookProps });
159
+ renderComponent(true);
160
+
161
+ const user = userEvent.setup();
162
+ const dropdown = screen.getByRole('button', { name: messages.dropdownAlt.defaultMessage });
163
+ expect(dropdown).toBeInTheDocument();
164
+ await user.click(dropdown);
165
+ const unenrollOption = screen.getByRole('button', { name: messages.unenroll.defaultMessage });
166
+ expect(unenrollOption).toBeInTheDocument();
167
+ expect(unenrollOption).toHaveAttribute('aria-disabled', 'true');
168
+ const socialShareMenu = screen.getByText('SocialShareMenu');
169
+ expect(socialShareMenu).toBeInTheDocument();
170
+ const unenrollConfirmModal = screen.getByText('UnenrollConfirmModal');
171
+ expect(unenrollConfirmModal).toBeInTheDocument();
172
+ const emailSettingsModal = screen.getByText('EmailSettingsModal');
173
+ expect(emailSettingsModal).toBeInTheDocument();
201
174
  });
202
175
  });
203
176
  });
204
- testUnenrollConfirmModal();
205
- it('displays EmaiSettingsModal with cardId and emailSettingsModal data', () => {
206
- const modal = el.instance.findByType(EmailSettingsModal)[0];
207
- expect(modal.props.show).toEqual(emailSettings.isVisible);
208
- expect(modal.props.closeModal).toEqual(emailSettings.hide);
209
- expect(modal.props.cardId).toEqual(props.cardId);
210
- });
211
177
  });
212
178
  });
213
179
  });