@openedx/frontend-app-authn 1.0.0-alpha.0

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 (177) hide show
  1. package/LICENSE +661 -0
  2. package/README.rst +204 -0
  3. package/package.json +79 -0
  4. package/src/Main.tsx +23 -0
  5. package/src/__mocks__/file.js +1 -0
  6. package/src/__mocks__/svg.js +1 -0
  7. package/src/app.scss +6 -0
  8. package/src/app.ts +43 -0
  9. package/src/base-container/components/default-layout/DefaultLayout.test.jsx +48 -0
  10. package/src/base-container/components/default-layout/LargeLayout.jsx +42 -0
  11. package/src/base-container/components/default-layout/MediumLayout.jsx +49 -0
  12. package/src/base-container/components/default-layout/SmallLayout.jsx +37 -0
  13. package/src/base-container/components/default-layout/index.jsx +3 -0
  14. package/src/base-container/components/default-layout/messages.js +16 -0
  15. package/src/base-container/components/image-layout/ExtraSmallLayout.jsx +31 -0
  16. package/src/base-container/components/image-layout/LargeLayout.jsx +32 -0
  17. package/src/base-container/components/image-layout/MediumLayout.jsx +32 -0
  18. package/src/base-container/components/image-layout/SmallLayout.jsx +31 -0
  19. package/src/base-container/components/image-layout/index.jsx +4 -0
  20. package/src/base-container/components/image-layout/index.scss +37 -0
  21. package/src/base-container/components/image-layout/messages.js +16 -0
  22. package/src/base-container/components/welcome-page-layout/LargeLayout.jsx +46 -0
  23. package/src/base-container/components/welcome-page-layout/MediumLayout.jsx +49 -0
  24. package/src/base-container/components/welcome-page-layout/SmallLayout.jsx +38 -0
  25. package/src/base-container/components/welcome-page-layout/index.jsx +3 -0
  26. package/src/base-container/components/welcome-page-layout/messages.js +21 -0
  27. package/src/base-container/index.jsx +70 -0
  28. package/src/base-container/tests/BaseContainer.test.jsx +44 -0
  29. package/src/common-components/EmbeddedRegistrationRoute.jsx +26 -0
  30. package/src/common-components/EnterpriseSSO.jsx +110 -0
  31. package/src/common-components/FormGroup.jsx +106 -0
  32. package/src/common-components/InstitutionLogistration.jsx +102 -0
  33. package/src/common-components/NotFoundPage.jsx +15 -0
  34. package/src/common-components/PasswordField.jsx +175 -0
  35. package/src/common-components/RedirectLogistration.jsx +94 -0
  36. package/src/common-components/RegisterFaIcons.jsx +8 -0
  37. package/src/common-components/SocialAuthProviders.jsx +75 -0
  38. package/src/common-components/ThirdPartyAuth.jsx +123 -0
  39. package/src/common-components/ThirdPartyAuthAlert.jsx +49 -0
  40. package/src/common-components/UnAuthOnlyRoute.jsx +41 -0
  41. package/src/common-components/data/actions.js +27 -0
  42. package/src/common-components/data/reducers.js +63 -0
  43. package/src/common-components/data/sagas.js +32 -0
  44. package/src/common-components/data/selectors.js +28 -0
  45. package/src/common-components/data/service.js +23 -0
  46. package/src/common-components/data/tests/reducer.test.js +82 -0
  47. package/src/common-components/data/tests/sagas.test.js +71 -0
  48. package/src/common-components/index.jsx +14 -0
  49. package/src/common-components/messages.jsx +127 -0
  50. package/src/common-components/tests/EmbeddedRegistrationRoute.test.jsx +76 -0
  51. package/src/common-components/tests/FormField.test.jsx +289 -0
  52. package/src/common-components/tests/SocialAuthProviders.test.jsx +75 -0
  53. package/src/common-components/tests/ThirdPartyAuthAlert.test.jsx +39 -0
  54. package/src/common-components/tests/UnAuthOnlyRoute.test.jsx +73 -0
  55. package/src/common-components/tests/__snapshots__/SocialAuthProviders.test.jsx.snap +156 -0
  56. package/src/common-components/tests/__snapshots__/ThirdPartyAuthAlert.test.jsx.snap +53 -0
  57. package/src/constants.ts +1 -0
  58. package/src/data/configureStore.js +33 -0
  59. package/src/data/constants.js +38 -0
  60. package/src/data/countries.ts +59 -0
  61. package/src/data/oneTrust.js +3 -0
  62. package/src/data/reducers.js +36 -0
  63. package/src/data/sagas.js +19 -0
  64. package/src/data/tests/cookies.test.js +42 -0
  65. package/src/data/tests/dataUtils.test.js +19 -0
  66. package/src/data/tests/reduxUtils.test.js +14 -0
  67. package/src/data/utils/cookies.js +12 -0
  68. package/src/data/utils/dataUtils.js +81 -0
  69. package/src/data/utils/index.js +11 -0
  70. package/src/data/utils/reduxUtils.js +34 -0
  71. package/src/data/utils/useMobileResponsive.js +24 -0
  72. package/src/field-renderer/FieldRenderer.jsx +156 -0
  73. package/src/field-renderer/index.jsx +2 -0
  74. package/src/field-renderer/tests/FieldRenderer.test.jsx +201 -0
  75. package/src/forgot-password/ForgotPasswordAlert.jsx +97 -0
  76. package/src/forgot-password/ForgotPasswordPage.jsx +193 -0
  77. package/src/forgot-password/data/actions.js +32 -0
  78. package/src/forgot-password/data/reducers.js +58 -0
  79. package/src/forgot-password/data/sagas.js +35 -0
  80. package/src/forgot-password/data/selectors.js +10 -0
  81. package/src/forgot-password/data/service.js +21 -0
  82. package/src/forgot-password/data/tests/reducers.test.js +34 -0
  83. package/src/forgot-password/data/tests/sagas.test.js +67 -0
  84. package/src/forgot-password/index.js +5 -0
  85. package/src/forgot-password/messages.js +128 -0
  86. package/src/forgot-password/tests/ForgotPasswordPage.test.jsx +269 -0
  87. package/src/i18n/index.js +25 -0
  88. package/src/index.ts +3 -0
  89. package/src/login/AccountActivationMessage.jsx +78 -0
  90. package/src/login/ChangePasswordPrompt.jsx +91 -0
  91. package/src/login/LoginFailure.jsx +231 -0
  92. package/src/login/LoginPage.jsx +368 -0
  93. package/src/login/data/actions.js +39 -0
  94. package/src/login/data/constants.js +20 -0
  95. package/src/login/data/reducers.js +76 -0
  96. package/src/login/data/sagas.js +45 -0
  97. package/src/login/data/service.js +24 -0
  98. package/src/login/data/tests/reducers.test.js +155 -0
  99. package/src/login/data/tests/sagas.test.js +110 -0
  100. package/src/login/index.js +5 -0
  101. package/src/login/messages.jsx +211 -0
  102. package/src/login/tests/AccountActivationMessage.test.jsx +122 -0
  103. package/src/login/tests/ChangePasswordPrompt.test.jsx +75 -0
  104. package/src/login/tests/LoginFailure.test.jsx +323 -0
  105. package/src/login/tests/LoginPage.test.jsx +839 -0
  106. package/src/logistration/Logistration.jsx +189 -0
  107. package/src/logistration/Logistration.test.jsx +275 -0
  108. package/src/progressive-profiling/ProgressiveProfiling.jsx +313 -0
  109. package/src/progressive-profiling/ProgressiveProfilingPageModal.jsx +56 -0
  110. package/src/progressive-profiling/data/actions.js +22 -0
  111. package/src/progressive-profiling/data/reducers.js +38 -0
  112. package/src/progressive-profiling/data/sagas.js +24 -0
  113. package/src/progressive-profiling/data/selectors.js +14 -0
  114. package/src/progressive-profiling/data/service.js +17 -0
  115. package/src/progressive-profiling/index.js +5 -0
  116. package/src/progressive-profiling/messages.jsx +57 -0
  117. package/src/progressive-profiling/tests/ProgressiveProfiling.test.jsx +322 -0
  118. package/src/register/RegistrationFields/CountryField/CountryField.jsx +164 -0
  119. package/src/register/RegistrationFields/CountryField/CountryField.test.jsx +204 -0
  120. package/src/register/RegistrationFields/CountryField/validator.js +32 -0
  121. package/src/register/RegistrationFields/EmailField/EmailField.jsx +140 -0
  122. package/src/register/RegistrationFields/EmailField/EmailField.test.jsx +236 -0
  123. package/src/register/RegistrationFields/EmailField/constants.js +131 -0
  124. package/src/register/RegistrationFields/EmailField/validator.js +126 -0
  125. package/src/register/RegistrationFields/EmailField/validator.test.js +39 -0
  126. package/src/register/RegistrationFields/HonorCodeField/HonorCode.jsx +104 -0
  127. package/src/register/RegistrationFields/HonorCodeField/HonorCode.test.jsx +70 -0
  128. package/src/register/RegistrationFields/NameField/NameField.jsx +68 -0
  129. package/src/register/RegistrationFields/NameField/NameField.test.jsx +146 -0
  130. package/src/register/RegistrationFields/NameField/validator.js +22 -0
  131. package/src/register/RegistrationFields/TermsOfServiceField/TermsOfService.jsx +58 -0
  132. package/src/register/RegistrationFields/TermsOfServiceField/TermsOfService.test.jsx +55 -0
  133. package/src/register/RegistrationFields/UsernameField/UsernameField.jsx +162 -0
  134. package/src/register/RegistrationFields/UsernameField/UsernameField.test.jsx +319 -0
  135. package/src/register/RegistrationFields/UsernameField/validator.js +16 -0
  136. package/src/register/RegistrationFields/index.js +6 -0
  137. package/src/register/RegistrationPage.jsx +428 -0
  138. package/src/register/RegistrationPage.test.jsx +854 -0
  139. package/src/register/components/ConfigurableRegistrationForm.jsx +244 -0
  140. package/src/register/components/RegistrationFailure.jsx +80 -0
  141. package/src/register/components/tests/ConfigurableRegistrationForm.test.jsx +401 -0
  142. package/src/register/components/tests/RegistrationFailure.test.jsx +212 -0
  143. package/src/register/components/tests/ThirdPartyAuth.test.jsx +439 -0
  144. package/src/register/data/actions.js +85 -0
  145. package/src/register/data/constants.js +13 -0
  146. package/src/register/data/reducers.js +140 -0
  147. package/src/register/data/sagas.js +58 -0
  148. package/src/register/data/selectors.js +34 -0
  149. package/src/register/data/service.js +45 -0
  150. package/src/register/data/tests/reducers.test.js +277 -0
  151. package/src/register/data/tests/sagas.test.js +239 -0
  152. package/src/register/data/tests/utils.test.js +98 -0
  153. package/src/register/data/utils.js +153 -0
  154. package/src/register/index.js +4 -0
  155. package/src/register/messages.jsx +206 -0
  156. package/src/reset-password/ResetPasswordFailure.jsx +55 -0
  157. package/src/reset-password/ResetPasswordPage.jsx +224 -0
  158. package/src/reset-password/ResetPasswordSuccess.jsx +19 -0
  159. package/src/reset-password/data/actions.js +50 -0
  160. package/src/reset-password/data/constants.js +16 -0
  161. package/src/reset-password/data/reducers.js +44 -0
  162. package/src/reset-password/data/sagas.js +67 -0
  163. package/src/reset-password/data/selectors.js +10 -0
  164. package/src/reset-password/data/service.js +61 -0
  165. package/src/reset-password/data/tests/sagas.test.js +185 -0
  166. package/src/reset-password/index.js +5 -0
  167. package/src/reset-password/messages.js +88 -0
  168. package/src/reset-password/tests/ResetPasswordPage.test.jsx +241 -0
  169. package/src/routes.jsx +79 -0
  170. package/src/sass/_base_component.scss +202 -0
  171. package/src/sass/_forgot_password.scss +3 -0
  172. package/src/sass/_login_page.scss +3 -0
  173. package/src/sass/_progressive_profiling_page.scss +30 -0
  174. package/src/sass/_registration.scss +123 -0
  175. package/src/sass/_reset_password.scss +3 -0
  176. package/src/sass/_style.scss +465 -0
  177. package/src/setupTest.js +68 -0
@@ -0,0 +1,189 @@
1
+ import { useEffect, useState } from 'react';
2
+ import { connect } from 'react-redux';
3
+
4
+ import {
5
+ useAppConfig, getAuthService, getSiteConfig, sendPageEvent, sendTrackEvent, useIntl
6
+ } from '@openedx/frontend-base';
7
+ import {
8
+ Icon,
9
+ Tab,
10
+ Tabs,
11
+ } from '@openedx/paragon';
12
+ import { ChevronLeft } from '@openedx/paragon/icons';
13
+ import PropTypes from 'prop-types';
14
+ import { Navigate, useNavigate } from 'react-router-dom';
15
+
16
+ import BaseContainer from '../base-container';
17
+ import { clearThirdPartyAuthContextErrorMessage } from '../common-components/data/actions';
18
+ import {
19
+ tpaProvidersSelector,
20
+ } from '../common-components/data/selectors';
21
+ import messages from '../common-components/messages';
22
+ import { LOGIN_PAGE, REGISTER_PAGE } from '../data/constants';
23
+ import {
24
+ getTpaHint, getTpaProvider, updatePathWithQueryParams,
25
+ } from '../data/utils';
26
+ import { LoginPage } from '../login';
27
+ import { backupLoginForm } from '../login/data/actions';
28
+ import { RegistrationPage } from '../register';
29
+ import { backupRegistrationForm } from '../register/data/actions';
30
+
31
+ const Logistration = (props) => {
32
+ const { selectedPage, tpaProviders } = props;
33
+ const tpaHint = getTpaHint();
34
+ const {
35
+ providers, secondaryProviders,
36
+ } = tpaProviders;
37
+ const { formatMessage } = useIntl();
38
+ const [institutionLogin, setInstitutionLogin] = useState(false);
39
+ const [key, setKey] = useState('');
40
+ const navigate = useNavigate();
41
+ const disablePublicAccountCreation = useAppConfig().ALLOW_PUBLIC_ACCOUNT_CREATION === false;
42
+ const hideRegistrationLink = useAppConfig().SHOW_REGISTRATION_LINKS === false;
43
+
44
+ useEffect(() => {
45
+ const authService = getAuthService();
46
+ if (authService) {
47
+ authService.getCsrfTokenService().getCsrfToken(getSiteConfig().lmsBaseUrl);
48
+ }
49
+ });
50
+
51
+ useEffect(() => {
52
+ if (disablePublicAccountCreation) {
53
+ navigate(updatePathWithQueryParams(LOGIN_PAGE));
54
+ }
55
+ }, [navigate, disablePublicAccountCreation]);
56
+
57
+ const handleInstitutionLogin = (e) => {
58
+ sendTrackEvent('edx.bi.institution_login_form.toggled', { category: 'user-engagement' });
59
+ if (typeof e === 'string') {
60
+ sendPageEvent('login_and_registration', e === '/login' ? 'login' : 'register');
61
+ } else {
62
+ sendPageEvent('login_and_registration', e.target.dataset.eventName);
63
+ }
64
+
65
+ setInstitutionLogin(!institutionLogin);
66
+ };
67
+
68
+ const handleOnSelect = (tabKey, currentTab) => {
69
+ if (tabKey === currentTab) {
70
+ return;
71
+ }
72
+ sendTrackEvent(`edx.bi.${tabKey.replace('/', '')}_form.toggled`, { category: 'user-engagement' });
73
+ props.clearThirdPartyAuthContextErrorMessage();
74
+ if (tabKey === LOGIN_PAGE) {
75
+ props.backupRegistrationForm();
76
+ } else if (tabKey === REGISTER_PAGE) {
77
+ props.backupLoginForm();
78
+ }
79
+ setKey(tabKey);
80
+ };
81
+
82
+ const tabTitle = (
83
+ <div className="d-flex">
84
+ <Icon src={ChevronLeft} className="left-icon" />
85
+ <span className="ml-2">
86
+ {selectedPage === LOGIN_PAGE
87
+ ? formatMessage(messages['logistration.sign.in'])
88
+ : formatMessage(messages['logistration.register'])}
89
+ </span>
90
+ </div>
91
+ );
92
+
93
+ const isValidTpaHint = () => {
94
+ const { provider } = getTpaProvider(tpaHint, providers, secondaryProviders);
95
+ return !!provider;
96
+ };
97
+
98
+ return (
99
+ <BaseContainer>
100
+ <div>
101
+ {disablePublicAccountCreation
102
+ ? (
103
+ <>
104
+ {institutionLogin && (
105
+ <Tabs defaultActiveKey="" id="controlled-tab" onSelect={handleInstitutionLogin}>
106
+ <Tab title={tabTitle} eventKey={LOGIN_PAGE} />
107
+ </Tabs>
108
+ )}
109
+ <div id="main-content" className="main-content">
110
+ {!institutionLogin && (
111
+ <h3 className="mb-4.5">{formatMessage(messages['logistration.sign.in'])}</h3>
112
+ )}
113
+ <LoginPage institutionLogin={institutionLogin} handleInstitutionLogin={handleInstitutionLogin} />
114
+ </div>
115
+ </>
116
+ )
117
+ : (
118
+ <div>
119
+ {institutionLogin
120
+ ? (
121
+ <Tabs defaultActiveKey="" id="controlled-tab" onSelect={handleInstitutionLogin}>
122
+ <Tab title={tabTitle} eventKey={selectedPage === LOGIN_PAGE ? LOGIN_PAGE : REGISTER_PAGE} />
123
+ </Tabs>
124
+ )
125
+ : (!isValidTpaHint() && !hideRegistrationLink && (
126
+ <Tabs defaultActiveKey={selectedPage} id="controlled-tab" onSelect={(tabKey) => handleOnSelect(tabKey, selectedPage)}>
127
+ <Tab title={formatMessage(messages['logistration.register'])} eventKey={REGISTER_PAGE} />
128
+ <Tab title={formatMessage(messages['logistration.sign.in'])} eventKey={LOGIN_PAGE} />
129
+ </Tabs>
130
+ ))}
131
+ {key && (
132
+ <Navigate to={updatePathWithQueryParams(key)} replace />
133
+ )}
134
+ <div id="main-content" className="main-content">
135
+ {!institutionLogin && !isValidTpaHint() && hideRegistrationLink && (
136
+ <h3 className="mb-4.5">
137
+ {formatMessage(messages[selectedPage === LOGIN_PAGE ? 'logistration.sign.in' : 'logistration.register'])}
138
+ </h3>
139
+ )}
140
+ {selectedPage === LOGIN_PAGE
141
+ ? <LoginPage institutionLogin={institutionLogin} handleInstitutionLogin={handleInstitutionLogin} />
142
+ : (
143
+ <RegistrationPage
144
+ institutionLogin={institutionLogin}
145
+ handleInstitutionLogin={handleInstitutionLogin}
146
+ />
147
+ )}
148
+ </div>
149
+ </div>
150
+ )}
151
+ </div>
152
+ </BaseContainer>
153
+ );
154
+ };
155
+
156
+ Logistration.propTypes = {
157
+ selectedPage: PropTypes.string,
158
+ backupLoginForm: PropTypes.func.isRequired,
159
+ backupRegistrationForm: PropTypes.func.isRequired,
160
+ clearThirdPartyAuthContextErrorMessage: PropTypes.func.isRequired,
161
+ tpaProviders: PropTypes.shape({
162
+ providers: PropTypes.arrayOf(PropTypes.shape({})),
163
+ secondaryProviders: PropTypes.arrayOf(PropTypes.shape({})),
164
+ }),
165
+ };
166
+
167
+ Logistration.defaultProps = {
168
+ tpaProviders: {
169
+ providers: [],
170
+ secondaryProviders: [],
171
+ },
172
+ };
173
+
174
+ Logistration.defaultProps = {
175
+ selectedPage: REGISTER_PAGE,
176
+ };
177
+
178
+ const mapStateToProps = state => ({
179
+ tpaProviders: tpaProvidersSelector(state),
180
+ });
181
+
182
+ export default connect(
183
+ mapStateToProps,
184
+ {
185
+ backupLoginForm,
186
+ backupRegistrationForm,
187
+ clearThirdPartyAuthContextErrorMessage,
188
+ },
189
+ )(Logistration);
@@ -0,0 +1,275 @@
1
+ import { Provider } from 'react-redux';
2
+
3
+ import {
4
+ CurrentAppProvider, configureI18n, getSiteConfig, injectIntl, IntlProvider, mergeAppConfig, sendPageEvent, sendTrackEvent
5
+ } from '@openedx/frontend-base';
6
+ import { fireEvent, render, screen } from '@testing-library/react';
7
+ import { MemoryRouter } from 'react-router-dom';
8
+ import configureStore from 'redux-mock-store';
9
+
10
+ import { appId } from '../constants';
11
+ import { clearThirdPartyAuthContextErrorMessage } from '../common-components/data/actions';
12
+ import {
13
+ COMPLETE_STATE, LOGIN_PAGE, REGISTER_PAGE,
14
+ } from '../data/constants';
15
+ import { backupLoginForm } from '../login/data/actions';
16
+ import { backupRegistrationForm } from '../register/data/actions';
17
+ import Logistration from './Logistration';
18
+
19
+ jest.mock('@openedx/frontend-base', () => ({
20
+ ...jest.requireActual('@openedx/frontend-base'),
21
+ sendPageEvent: jest.fn(),
22
+ sendTrackEvent: jest.fn(),
23
+ getAuthenticatedUser: jest.fn(() => ({
24
+ userId: 3,
25
+ username: 'test-user',
26
+ })),
27
+ getAuthService: jest.fn(() => null),
28
+ }));
29
+
30
+ const mockStore = configureStore();
31
+ const IntlLogistration = injectIntl(Logistration);
32
+
33
+ describe('Logistration', () => {
34
+ let store = {};
35
+
36
+ const secondaryProviders = {
37
+ id: 'saml-test',
38
+ name: 'Test University',
39
+ loginUrl: '/dummy-auth',
40
+ registerUrl: '/dummy_auth',
41
+ };
42
+
43
+ const reduxWrapper = children => (
44
+ <IntlProvider locale="en">
45
+ <MemoryRouter>
46
+ <CurrentAppProvider appId={appId}>
47
+ <Provider store={store}>{children}</Provider>
48
+ </CurrentAppProvider>
49
+ </MemoryRouter>
50
+ </IntlProvider>
51
+ );
52
+
53
+ const initialState = {
54
+ register: {
55
+ registrationFormData: {
56
+ configurableFormFields: {
57
+ marketingEmailsOptIn: true,
58
+ },
59
+ formFields: {
60
+ name: '', email: '', username: '', password: '',
61
+ },
62
+ emailSuggestion: {
63
+ suggestion: '', type: '',
64
+ },
65
+ errors: {
66
+ name: '', email: '', username: '', password: '',
67
+ },
68
+ },
69
+ registrationResult: { success: false, redirectUrl: '' },
70
+ registrationError: {},
71
+ usernameSuggestions: [],
72
+ validationApiRateLimited: false,
73
+ },
74
+ commonComponents: {
75
+ thirdPartyAuthContext: {
76
+ providers: [],
77
+ secondaryProviders: [],
78
+ },
79
+ },
80
+ login: {
81
+ loginResult: { success: false, redirectUrl: '' },
82
+ },
83
+ };
84
+
85
+ beforeEach(() => {
86
+ store = mockStore(initialState);
87
+
88
+ configureI18n({
89
+ messages: { 'es-419': {}, de: {}, 'en-us': {} },
90
+ });
91
+ });
92
+
93
+ it('should render registration page', () => {
94
+ mergeAppConfig(appId, {
95
+ ALLOW_PUBLIC_ACCOUNT_CREATION: true,
96
+ });
97
+
98
+ const { container } = render(reduxWrapper(<IntlLogistration />));
99
+
100
+ expect(container.querySelector('RegistrationPage')).toBeDefined();
101
+ });
102
+
103
+ it('should render login page', () => {
104
+ const props = { selectedPage: LOGIN_PAGE };
105
+ const { container } = render(reduxWrapper(<IntlLogistration {...props} />));
106
+
107
+ expect(container.querySelector('LoginPage')).toBeDefined();
108
+ });
109
+
110
+ it('should render login/register headings when show registration links is disabled', () => {
111
+ mergeAppConfig(appId, {
112
+ ALLOW_PUBLIC_ACCOUNT_CREATION: true,
113
+ SHOW_REGISTRATION_LINKS: false,
114
+ });
115
+
116
+ let props = { selectedPage: LOGIN_PAGE };
117
+ const { rerender } = render(reduxWrapper(<IntlLogistration {...props} />));
118
+
119
+ // verifying sign in heading
120
+ expect(screen.getByRole('heading', { level: 3 }).textContent).toEqual('Sign in');
121
+
122
+ // register page is still accessible when SHOW_REGISTRATION_LINKS is false
123
+ // but it needs to be accessed directly
124
+ props = { selectedPage: REGISTER_PAGE };
125
+ rerender(reduxWrapper(<IntlLogistration {...props} />));
126
+
127
+ // verifying register heading
128
+ expect(screen.getByRole('heading', { level: 3 }).textContent).toEqual('Register');
129
+ });
130
+
131
+ it('should render only login page when public account creation is disabled', () => {
132
+ mergeAppConfig(appId, {
133
+ ALLOW_PUBLIC_ACCOUNT_CREATION: false,
134
+ DISABLE_ENTERPRISE_LOGIN: 'true',
135
+ SHOW_REGISTRATION_LINKS: 'true',
136
+ });
137
+
138
+ store = mockStore({
139
+ ...initialState,
140
+ commonComponents: {
141
+ thirdPartyAuthContext: {
142
+ currentProvider: null,
143
+ finishAuthUrl: null,
144
+ providers: [],
145
+ secondaryProviders: [secondaryProviders],
146
+ },
147
+ thirdPartyAuthApiStatus: COMPLETE_STATE,
148
+ },
149
+ });
150
+
151
+ const props = { selectedPage: LOGIN_PAGE };
152
+ const { container } = render(reduxWrapper(<IntlLogistration {...props} />));
153
+
154
+ // verifying sign in heading for institution login false
155
+ expect(screen.getByRole('heading', { level: 3 }).textContent).toEqual('Sign in');
156
+
157
+ // verifying tabs heading for institution login true
158
+ fireEvent.click(screen.getByRole('link'));
159
+ expect(container.querySelector('#controlled-tab')).toBeDefined();
160
+ });
161
+
162
+ it('should display institution login option when secondary providers are present', () => {
163
+ mergeAppConfig(appId, {
164
+ DISABLE_ENTERPRISE_LOGIN: 'true',
165
+ ALLOW_PUBLIC_ACCOUNT_CREATION: 'true',
166
+ });
167
+
168
+ store = mockStore({
169
+ ...initialState,
170
+ commonComponents: {
171
+ thirdPartyAuthContext: {
172
+ currentProvider: null,
173
+ finishAuthUrl: null,
174
+ providers: [],
175
+ secondaryProviders: [secondaryProviders],
176
+ },
177
+ thirdPartyAuthApiStatus: COMPLETE_STATE,
178
+ },
179
+ });
180
+
181
+ const props = { selectedPage: LOGIN_PAGE };
182
+ render(reduxWrapper(<IntlLogistration {...props} />));
183
+ expect(screen.getByText('Institution/campus credentials')).toBeDefined();
184
+
185
+ // on clicking "Institution/campus credentials" button, it should display institution login page
186
+ fireEvent.click(screen.getByText('Institution/campus credentials'));
187
+ expect(screen.getByText('Test University')).toBeDefined();
188
+
189
+ mergeAppConfig(appId, {
190
+ DISABLE_ENTERPRISE_LOGIN: '',
191
+ });
192
+ });
193
+
194
+ it('send tracking and page events when institutional login button is clicked', () => {
195
+ mergeAppConfig(appId, {
196
+ DISABLE_ENTERPRISE_LOGIN: 'true',
197
+ });
198
+
199
+ store = mockStore({
200
+ ...initialState,
201
+ commonComponents: {
202
+ thirdPartyAuthContext: {
203
+ currentProvider: null,
204
+ finishAuthUrl: null,
205
+ providers: [],
206
+ secondaryProviders: [secondaryProviders],
207
+ },
208
+ thirdPartyAuthApiStatus: COMPLETE_STATE,
209
+ },
210
+ });
211
+
212
+ const props = { selectedPage: LOGIN_PAGE };
213
+ render(reduxWrapper(<IntlLogistration {...props} />));
214
+ fireEvent.click(screen.getByText('Institution/campus credentials'));
215
+
216
+ expect(sendTrackEvent).toHaveBeenCalledWith('edx.bi.institution_login_form.toggled', { category: 'user-engagement' });
217
+ expect(sendPageEvent).toHaveBeenCalledWith('login_and_registration', 'institution_login');
218
+
219
+ mergeAppConfig(appId, {
220
+ DISABLE_ENTERPRISE_LOGIN: '',
221
+ });
222
+ });
223
+
224
+ it('should not display institution register button', () => {
225
+ mergeAppConfig(appId, {
226
+ DISABLE_ENTERPRISE_LOGIN: 'true',
227
+ });
228
+
229
+ store = mockStore({
230
+ ...initialState,
231
+ commonComponents: {
232
+ thirdPartyAuthContext: {
233
+ currentProvider: null,
234
+ finishAuthUrl: null,
235
+ providers: [],
236
+ secondaryProviders: [secondaryProviders],
237
+ },
238
+ thirdPartyAuthApiStatus: COMPLETE_STATE,
239
+ },
240
+ });
241
+
242
+ delete window.location;
243
+ window.location = { hostname: getSiteConfig().siteName, href: getSiteConfig().baseUrl };
244
+
245
+ render(reduxWrapper(<IntlLogistration />));
246
+ fireEvent.click(screen.getByText('Institution/campus credentials'));
247
+ expect(screen.getByText('Test University')).toBeDefined();
248
+
249
+ mergeAppConfig(appId, {
250
+ DISABLE_ENTERPRISE_LOGIN: '',
251
+ });
252
+ });
253
+
254
+ it('should fire action to backup registration form on tab click', () => {
255
+ store.dispatch = jest.fn(store.dispatch);
256
+ const { container } = render(reduxWrapper(<IntlLogistration />));
257
+ fireEvent.click(container.querySelector('a[data-rb-event-key="/login"]'));
258
+ expect(store.dispatch).toHaveBeenCalledWith(backupRegistrationForm());
259
+ });
260
+
261
+ it('should fire action to backup login form on tab click', () => {
262
+ store.dispatch = jest.fn(store.dispatch);
263
+ const props = { selectedPage: LOGIN_PAGE };
264
+ const { container } = render(reduxWrapper(<IntlLogistration {...props} />));
265
+ fireEvent.click(container.querySelector('a[data-rb-event-key="/register"]'));
266
+ expect(store.dispatch).toHaveBeenCalledWith(backupLoginForm());
267
+ });
268
+
269
+ it('should clear tpa context errorMessage tab click', () => {
270
+ store.dispatch = jest.fn(store.dispatch);
271
+ const { container } = render(reduxWrapper(<IntlLogistration />));
272
+ fireEvent.click(container.querySelector('a[data-rb-event-key="/login"]'));
273
+ expect(store.dispatch).toHaveBeenCalledWith(clearThirdPartyAuthContextErrorMessage());
274
+ });
275
+ });