@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.
- package/LICENSE +661 -0
- package/README.rst +204 -0
- package/package.json +79 -0
- package/src/Main.tsx +23 -0
- package/src/__mocks__/file.js +1 -0
- package/src/__mocks__/svg.js +1 -0
- package/src/app.scss +6 -0
- package/src/app.ts +43 -0
- package/src/base-container/components/default-layout/DefaultLayout.test.jsx +48 -0
- package/src/base-container/components/default-layout/LargeLayout.jsx +42 -0
- package/src/base-container/components/default-layout/MediumLayout.jsx +49 -0
- package/src/base-container/components/default-layout/SmallLayout.jsx +37 -0
- package/src/base-container/components/default-layout/index.jsx +3 -0
- package/src/base-container/components/default-layout/messages.js +16 -0
- package/src/base-container/components/image-layout/ExtraSmallLayout.jsx +31 -0
- package/src/base-container/components/image-layout/LargeLayout.jsx +32 -0
- package/src/base-container/components/image-layout/MediumLayout.jsx +32 -0
- package/src/base-container/components/image-layout/SmallLayout.jsx +31 -0
- package/src/base-container/components/image-layout/index.jsx +4 -0
- package/src/base-container/components/image-layout/index.scss +37 -0
- package/src/base-container/components/image-layout/messages.js +16 -0
- package/src/base-container/components/welcome-page-layout/LargeLayout.jsx +46 -0
- package/src/base-container/components/welcome-page-layout/MediumLayout.jsx +49 -0
- package/src/base-container/components/welcome-page-layout/SmallLayout.jsx +38 -0
- package/src/base-container/components/welcome-page-layout/index.jsx +3 -0
- package/src/base-container/components/welcome-page-layout/messages.js +21 -0
- package/src/base-container/index.jsx +70 -0
- package/src/base-container/tests/BaseContainer.test.jsx +44 -0
- package/src/common-components/EmbeddedRegistrationRoute.jsx +26 -0
- package/src/common-components/EnterpriseSSO.jsx +110 -0
- package/src/common-components/FormGroup.jsx +106 -0
- package/src/common-components/InstitutionLogistration.jsx +102 -0
- package/src/common-components/NotFoundPage.jsx +15 -0
- package/src/common-components/PasswordField.jsx +175 -0
- package/src/common-components/RedirectLogistration.jsx +94 -0
- package/src/common-components/RegisterFaIcons.jsx +8 -0
- package/src/common-components/SocialAuthProviders.jsx +75 -0
- package/src/common-components/ThirdPartyAuth.jsx +123 -0
- package/src/common-components/ThirdPartyAuthAlert.jsx +49 -0
- package/src/common-components/UnAuthOnlyRoute.jsx +41 -0
- package/src/common-components/data/actions.js +27 -0
- package/src/common-components/data/reducers.js +63 -0
- package/src/common-components/data/sagas.js +32 -0
- package/src/common-components/data/selectors.js +28 -0
- package/src/common-components/data/service.js +23 -0
- package/src/common-components/data/tests/reducer.test.js +82 -0
- package/src/common-components/data/tests/sagas.test.js +71 -0
- package/src/common-components/index.jsx +14 -0
- package/src/common-components/messages.jsx +127 -0
- package/src/common-components/tests/EmbeddedRegistrationRoute.test.jsx +76 -0
- package/src/common-components/tests/FormField.test.jsx +289 -0
- package/src/common-components/tests/SocialAuthProviders.test.jsx +75 -0
- package/src/common-components/tests/ThirdPartyAuthAlert.test.jsx +39 -0
- package/src/common-components/tests/UnAuthOnlyRoute.test.jsx +73 -0
- package/src/common-components/tests/__snapshots__/SocialAuthProviders.test.jsx.snap +156 -0
- package/src/common-components/tests/__snapshots__/ThirdPartyAuthAlert.test.jsx.snap +53 -0
- package/src/constants.ts +1 -0
- package/src/data/configureStore.js +33 -0
- package/src/data/constants.js +38 -0
- package/src/data/countries.ts +59 -0
- package/src/data/oneTrust.js +3 -0
- package/src/data/reducers.js +36 -0
- package/src/data/sagas.js +19 -0
- package/src/data/tests/cookies.test.js +42 -0
- package/src/data/tests/dataUtils.test.js +19 -0
- package/src/data/tests/reduxUtils.test.js +14 -0
- package/src/data/utils/cookies.js +12 -0
- package/src/data/utils/dataUtils.js +81 -0
- package/src/data/utils/index.js +11 -0
- package/src/data/utils/reduxUtils.js +34 -0
- package/src/data/utils/useMobileResponsive.js +24 -0
- package/src/field-renderer/FieldRenderer.jsx +156 -0
- package/src/field-renderer/index.jsx +2 -0
- package/src/field-renderer/tests/FieldRenderer.test.jsx +201 -0
- package/src/forgot-password/ForgotPasswordAlert.jsx +97 -0
- package/src/forgot-password/ForgotPasswordPage.jsx +193 -0
- package/src/forgot-password/data/actions.js +32 -0
- package/src/forgot-password/data/reducers.js +58 -0
- package/src/forgot-password/data/sagas.js +35 -0
- package/src/forgot-password/data/selectors.js +10 -0
- package/src/forgot-password/data/service.js +21 -0
- package/src/forgot-password/data/tests/reducers.test.js +34 -0
- package/src/forgot-password/data/tests/sagas.test.js +67 -0
- package/src/forgot-password/index.js +5 -0
- package/src/forgot-password/messages.js +128 -0
- package/src/forgot-password/tests/ForgotPasswordPage.test.jsx +269 -0
- package/src/i18n/index.js +25 -0
- package/src/index.ts +3 -0
- package/src/login/AccountActivationMessage.jsx +78 -0
- package/src/login/ChangePasswordPrompt.jsx +91 -0
- package/src/login/LoginFailure.jsx +231 -0
- package/src/login/LoginPage.jsx +368 -0
- package/src/login/data/actions.js +39 -0
- package/src/login/data/constants.js +20 -0
- package/src/login/data/reducers.js +76 -0
- package/src/login/data/sagas.js +45 -0
- package/src/login/data/service.js +24 -0
- package/src/login/data/tests/reducers.test.js +155 -0
- package/src/login/data/tests/sagas.test.js +110 -0
- package/src/login/index.js +5 -0
- package/src/login/messages.jsx +211 -0
- package/src/login/tests/AccountActivationMessage.test.jsx +122 -0
- package/src/login/tests/ChangePasswordPrompt.test.jsx +75 -0
- package/src/login/tests/LoginFailure.test.jsx +323 -0
- package/src/login/tests/LoginPage.test.jsx +839 -0
- package/src/logistration/Logistration.jsx +189 -0
- package/src/logistration/Logistration.test.jsx +275 -0
- package/src/progressive-profiling/ProgressiveProfiling.jsx +313 -0
- package/src/progressive-profiling/ProgressiveProfilingPageModal.jsx +56 -0
- package/src/progressive-profiling/data/actions.js +22 -0
- package/src/progressive-profiling/data/reducers.js +38 -0
- package/src/progressive-profiling/data/sagas.js +24 -0
- package/src/progressive-profiling/data/selectors.js +14 -0
- package/src/progressive-profiling/data/service.js +17 -0
- package/src/progressive-profiling/index.js +5 -0
- package/src/progressive-profiling/messages.jsx +57 -0
- package/src/progressive-profiling/tests/ProgressiveProfiling.test.jsx +322 -0
- package/src/register/RegistrationFields/CountryField/CountryField.jsx +164 -0
- package/src/register/RegistrationFields/CountryField/CountryField.test.jsx +204 -0
- package/src/register/RegistrationFields/CountryField/validator.js +32 -0
- package/src/register/RegistrationFields/EmailField/EmailField.jsx +140 -0
- package/src/register/RegistrationFields/EmailField/EmailField.test.jsx +236 -0
- package/src/register/RegistrationFields/EmailField/constants.js +131 -0
- package/src/register/RegistrationFields/EmailField/validator.js +126 -0
- package/src/register/RegistrationFields/EmailField/validator.test.js +39 -0
- package/src/register/RegistrationFields/HonorCodeField/HonorCode.jsx +104 -0
- package/src/register/RegistrationFields/HonorCodeField/HonorCode.test.jsx +70 -0
- package/src/register/RegistrationFields/NameField/NameField.jsx +68 -0
- package/src/register/RegistrationFields/NameField/NameField.test.jsx +146 -0
- package/src/register/RegistrationFields/NameField/validator.js +22 -0
- package/src/register/RegistrationFields/TermsOfServiceField/TermsOfService.jsx +58 -0
- package/src/register/RegistrationFields/TermsOfServiceField/TermsOfService.test.jsx +55 -0
- package/src/register/RegistrationFields/UsernameField/UsernameField.jsx +162 -0
- package/src/register/RegistrationFields/UsernameField/UsernameField.test.jsx +319 -0
- package/src/register/RegistrationFields/UsernameField/validator.js +16 -0
- package/src/register/RegistrationFields/index.js +6 -0
- package/src/register/RegistrationPage.jsx +428 -0
- package/src/register/RegistrationPage.test.jsx +854 -0
- package/src/register/components/ConfigurableRegistrationForm.jsx +244 -0
- package/src/register/components/RegistrationFailure.jsx +80 -0
- package/src/register/components/tests/ConfigurableRegistrationForm.test.jsx +401 -0
- package/src/register/components/tests/RegistrationFailure.test.jsx +212 -0
- package/src/register/components/tests/ThirdPartyAuth.test.jsx +439 -0
- package/src/register/data/actions.js +85 -0
- package/src/register/data/constants.js +13 -0
- package/src/register/data/reducers.js +140 -0
- package/src/register/data/sagas.js +58 -0
- package/src/register/data/selectors.js +34 -0
- package/src/register/data/service.js +45 -0
- package/src/register/data/tests/reducers.test.js +277 -0
- package/src/register/data/tests/sagas.test.js +239 -0
- package/src/register/data/tests/utils.test.js +98 -0
- package/src/register/data/utils.js +153 -0
- package/src/register/index.js +4 -0
- package/src/register/messages.jsx +206 -0
- package/src/reset-password/ResetPasswordFailure.jsx +55 -0
- package/src/reset-password/ResetPasswordPage.jsx +224 -0
- package/src/reset-password/ResetPasswordSuccess.jsx +19 -0
- package/src/reset-password/data/actions.js +50 -0
- package/src/reset-password/data/constants.js +16 -0
- package/src/reset-password/data/reducers.js +44 -0
- package/src/reset-password/data/sagas.js +67 -0
- package/src/reset-password/data/selectors.js +10 -0
- package/src/reset-password/data/service.js +61 -0
- package/src/reset-password/data/tests/sagas.test.js +185 -0
- package/src/reset-password/index.js +5 -0
- package/src/reset-password/messages.js +88 -0
- package/src/reset-password/tests/ResetPasswordPage.test.jsx +241 -0
- package/src/routes.jsx +79 -0
- package/src/sass/_base_component.scss +202 -0
- package/src/sass/_forgot_password.scss +3 -0
- package/src/sass/_login_page.scss +3 -0
- package/src/sass/_progressive_profiling_page.scss +30 -0
- package/src/sass/_registration.scss +123 -0
- package/src/sass/_reset_password.scss +3 -0
- package/src/sass/_style.scss +465 -0
- package/src/setupTest.js +68 -0
|
@@ -0,0 +1,839 @@
|
|
|
1
|
+
import { Provider } from 'react-redux';
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
getSiteConfig, injectIntl, CurrentAppProvider, IntlProvider, mergeAppConfig
|
|
5
|
+
} from '@openedx/frontend-base';
|
|
6
|
+
import {
|
|
7
|
+
fireEvent, render, screen, waitFor,
|
|
8
|
+
} from '@testing-library/react';
|
|
9
|
+
import { act } from 'react-dom/test-utils';
|
|
10
|
+
import { MemoryRouter } from 'react-router-dom';
|
|
11
|
+
import configureStore from 'redux-mock-store';
|
|
12
|
+
|
|
13
|
+
import { appId } from '../../constants';
|
|
14
|
+
import { initializeMockServices } from '../../setupTest';
|
|
15
|
+
import { COMPLETE_STATE, LOGIN_PAGE, PENDING_STATE } from '../../data/constants';
|
|
16
|
+
import { backupLoginFormBegin, dismissPasswordResetBanner, loginRequest } from '../data/actions';
|
|
17
|
+
import { INTERNAL_SERVER_ERROR } from '../data/constants';
|
|
18
|
+
import LoginPage from '../LoginPage';
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
const { analyticsService } = initializeMockServices();
|
|
22
|
+
const IntlLoginPage = injectIntl(LoginPage);
|
|
23
|
+
const mockStore = configureStore();
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
describe('LoginPage', () => {
|
|
27
|
+
let props = {};
|
|
28
|
+
let store = {};
|
|
29
|
+
|
|
30
|
+
const emptyFieldValidation = { emailOrUsername: 'Enter your username or email', password: 'Enter your password' };
|
|
31
|
+
const reduxWrapper = children => (
|
|
32
|
+
<IntlProvider locale="en">
|
|
33
|
+
<MemoryRouter>
|
|
34
|
+
<CurrentAppProvider appId={appId}>
|
|
35
|
+
<Provider store={store}>{children}</Provider>
|
|
36
|
+
</CurrentAppProvider>
|
|
37
|
+
</MemoryRouter>
|
|
38
|
+
</IntlProvider>
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
const initialState = {
|
|
42
|
+
login: {
|
|
43
|
+
loginResult: { success: false, redirectUrl: '' },
|
|
44
|
+
},
|
|
45
|
+
commonComponents: {
|
|
46
|
+
thirdPartyAuthApiStatus: null,
|
|
47
|
+
thirdPartyAuthContext: {
|
|
48
|
+
currentProvider: null,
|
|
49
|
+
finishAuthUrl: null,
|
|
50
|
+
providers: [],
|
|
51
|
+
secondaryProviders: [],
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
register: {
|
|
55
|
+
validationApiRateLimited: false,
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
const secondaryProviders = {
|
|
60
|
+
id: 'saml-test',
|
|
61
|
+
name: 'Test University',
|
|
62
|
+
loginUrl: '/dummy-auth',
|
|
63
|
+
registerUrl: '/dummy_auth',
|
|
64
|
+
skipHintedLogin: false,
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
const ssoProvider = {
|
|
68
|
+
id: 'oa2-apple-id',
|
|
69
|
+
name: 'Apple',
|
|
70
|
+
iconClass: 'apple',
|
|
71
|
+
iconImage: 'https://openedx.devstack.lms/logo.png',
|
|
72
|
+
loginUrl: '/auth/login/apple-id/?auth_entry=login&next=/dashboard',
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
beforeEach(() => {
|
|
76
|
+
store = mockStore(initialState);
|
|
77
|
+
props = {
|
|
78
|
+
loginRequest: jest.fn(),
|
|
79
|
+
handleInstitutionLogin: jest.fn(),
|
|
80
|
+
institutionLogin: false,
|
|
81
|
+
};
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
// ******** test login form submission ********
|
|
85
|
+
|
|
86
|
+
it('should submit form for valid input', () => {
|
|
87
|
+
store.dispatch = jest.fn(store.dispatch);
|
|
88
|
+
|
|
89
|
+
mergeAppConfig(appId, {
|
|
90
|
+
DISABLE_ENTERPRISE_LOGIN: '',
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
render(reduxWrapper(<IntlLoginPage {...props} />));
|
|
94
|
+
|
|
95
|
+
fireEvent.change(screen.getByText(
|
|
96
|
+
'',
|
|
97
|
+
{ selector: '#emailOrUsername' },
|
|
98
|
+
), { target: { value: 'test', name: 'emailOrUsername' } });
|
|
99
|
+
fireEvent.change(screen.getByText(
|
|
100
|
+
'',
|
|
101
|
+
{ selector: '#password' },
|
|
102
|
+
), { target: { value: 'test-password', name: 'password' } });
|
|
103
|
+
|
|
104
|
+
fireEvent.click(screen.getByText(
|
|
105
|
+
'',
|
|
106
|
+
{ selector: '.btn-brand' },
|
|
107
|
+
));
|
|
108
|
+
|
|
109
|
+
expect(store.dispatch).toHaveBeenCalledWith(loginRequest({ email_or_username: 'test', password: 'test-password' }));
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
it('should not dispatch loginRequest on empty form submission', () => {
|
|
113
|
+
store.dispatch = jest.fn(store.dispatch);
|
|
114
|
+
render(reduxWrapper(<IntlLoginPage {...props} />));
|
|
115
|
+
|
|
116
|
+
fireEvent.click(screen.getByText(
|
|
117
|
+
'',
|
|
118
|
+
{ selector: '.btn-brand' },
|
|
119
|
+
));
|
|
120
|
+
expect(store.dispatch).not.toHaveBeenCalledWith(loginRequest({}));
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
it('should dismiss reset password banner on form submission', () => {
|
|
124
|
+
store = mockStore({
|
|
125
|
+
...initialState,
|
|
126
|
+
login: {
|
|
127
|
+
...initialState.login,
|
|
128
|
+
showResetPasswordSuccessBanner: true,
|
|
129
|
+
},
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
store.dispatch = jest.fn(store.dispatch);
|
|
133
|
+
render(reduxWrapper(<IntlLoginPage {...props} />));
|
|
134
|
+
fireEvent.click(screen.getByText(
|
|
135
|
+
'',
|
|
136
|
+
{ selector: '.btn-brand' },
|
|
137
|
+
));
|
|
138
|
+
|
|
139
|
+
expect(store.dispatch).toHaveBeenCalledWith(dismissPasswordResetBanner());
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
// ******** test login form validations ********
|
|
143
|
+
|
|
144
|
+
it('should match state for invalid email (less than 2 characters), on form submission', () => {
|
|
145
|
+
store.dispatch = jest.fn(store.dispatch);
|
|
146
|
+
|
|
147
|
+
render(reduxWrapper(<IntlLoginPage {...props} />));
|
|
148
|
+
|
|
149
|
+
fireEvent.change(screen.getByText(
|
|
150
|
+
'',
|
|
151
|
+
{ selector: '#password' },
|
|
152
|
+
), { target: { value: 'test' } });
|
|
153
|
+
fireEvent.change(screen.getByText(
|
|
154
|
+
'',
|
|
155
|
+
{ selector: '#emailOrUsername' },
|
|
156
|
+
), { target: { value: 't' } });
|
|
157
|
+
|
|
158
|
+
fireEvent.click(screen.getByText(
|
|
159
|
+
'',
|
|
160
|
+
{ selector: '.btn-brand' },
|
|
161
|
+
));
|
|
162
|
+
|
|
163
|
+
expect(screen.getByText('Username or email must have at least 2 characters.')).toBeDefined();
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
it('should show error messages for required fields on empty form submission', () => {
|
|
167
|
+
const { container } = render(reduxWrapper(<IntlLoginPage {...props} />));
|
|
168
|
+
fireEvent.click(screen.getByText(
|
|
169
|
+
'',
|
|
170
|
+
{ selector: '.btn-brand' },
|
|
171
|
+
));
|
|
172
|
+
|
|
173
|
+
expect(container.querySelector('div[feedback-for="emailOrUsername"]').textContent).toEqual(emptyFieldValidation.emailOrUsername);
|
|
174
|
+
expect(container.querySelector('div[feedback-for="password"]').textContent).toEqual(emptyFieldValidation.password);
|
|
175
|
+
|
|
176
|
+
const alertBanner = 'We couldn\'t sign you in.Please fill in the fields below.';
|
|
177
|
+
expect(container.querySelector('#login-failure-alert').textContent).toEqual(alertBanner);
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
it('should run frontend validations for emailOrUsername field on form submission', () => {
|
|
181
|
+
const { container } = render(reduxWrapper(<IntlLoginPage {...props} />));
|
|
182
|
+
|
|
183
|
+
fireEvent.change(screen.getByText(
|
|
184
|
+
'',
|
|
185
|
+
{ selector: '#emailOrUsername' },
|
|
186
|
+
), { target: { value: 't', name: 'emailOrUsername' } });
|
|
187
|
+
|
|
188
|
+
fireEvent.click(screen.getByText(
|
|
189
|
+
'',
|
|
190
|
+
{ selector: '.btn-brand' },
|
|
191
|
+
));
|
|
192
|
+
|
|
193
|
+
expect(container.querySelector('div[feedback-for="emailOrUsername"]').textContent).toEqual('Username or email must have at least 2 characters.');
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
// ******** test field focus in functionality ********
|
|
197
|
+
it('should reset field related error messages on onFocus event', async () => {
|
|
198
|
+
store.dispatch = jest.fn(store.dispatch);
|
|
199
|
+
|
|
200
|
+
render(reduxWrapper(<IntlLoginPage {...props} />));
|
|
201
|
+
|
|
202
|
+
await act(async () => {
|
|
203
|
+
// clicking submit button with empty fields to make the errors appear
|
|
204
|
+
fireEvent.click(screen.getByText(
|
|
205
|
+
'',
|
|
206
|
+
{ selector: '.btn-brand' },
|
|
207
|
+
));
|
|
208
|
+
|
|
209
|
+
// focusing the fields to verify that the errors are cleared
|
|
210
|
+
fireEvent.focus(screen.getByText(
|
|
211
|
+
'',
|
|
212
|
+
{ selector: '#password' },
|
|
213
|
+
));
|
|
214
|
+
fireEvent.focus(screen.getByText(
|
|
215
|
+
'',
|
|
216
|
+
{ selector: '#emailOrUsername' },
|
|
217
|
+
));
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
// verifying that the errors are cleared
|
|
221
|
+
await waitFor(() => {
|
|
222
|
+
expect(screen.queryByText('Enter your username or email')).toBeNull();
|
|
223
|
+
});
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
// ******** test form buttons and links ********
|
|
227
|
+
|
|
228
|
+
it('should match default button state', () => {
|
|
229
|
+
render(reduxWrapper(<IntlLoginPage {...props} />));
|
|
230
|
+
expect(screen.getByText('Sign in')).toBeDefined();
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
it('should match pending button state', () => {
|
|
234
|
+
store = mockStore({
|
|
235
|
+
...initialState,
|
|
236
|
+
login: {
|
|
237
|
+
...initialState.login,
|
|
238
|
+
submitState: PENDING_STATE,
|
|
239
|
+
},
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
render(reduxWrapper(<IntlLoginPage {...props} />));
|
|
243
|
+
|
|
244
|
+
expect(screen.getByText(
|
|
245
|
+
'pending',
|
|
246
|
+
).textContent).toEqual('pending');
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
it('should show forgot password link', () => {
|
|
250
|
+
render(reduxWrapper(<IntlLoginPage {...props} />));
|
|
251
|
+
|
|
252
|
+
expect(screen.getByText(
|
|
253
|
+
'Forgot password',
|
|
254
|
+
{ selector: '#forgot-password' },
|
|
255
|
+
).textContent).toEqual('Forgot password');
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
it('should show single sign on provider button', () => {
|
|
259
|
+
store = mockStore({
|
|
260
|
+
...initialState,
|
|
261
|
+
commonComponents: {
|
|
262
|
+
...initialState.commonComponents,
|
|
263
|
+
thirdPartyAuthContext: {
|
|
264
|
+
...initialState.commonComponents.thirdPartyAuthContext,
|
|
265
|
+
providers: [ssoProvider],
|
|
266
|
+
},
|
|
267
|
+
},
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
render(reduxWrapper(<IntlLoginPage {...props} />));
|
|
271
|
+
expect(screen.getByText(
|
|
272
|
+
'',
|
|
273
|
+
{ selector: `#${ssoProvider.id}` },
|
|
274
|
+
)).toBeDefined();
|
|
275
|
+
|
|
276
|
+
mergeAppConfig(appId, {
|
|
277
|
+
DISABLE_ENTERPRISE_LOGIN: '',
|
|
278
|
+
});
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
it('should display sign-in header only when primary or secondary providers are available.', () => {
|
|
282
|
+
store = mockStore({
|
|
283
|
+
...initialState,
|
|
284
|
+
commonComponents: {
|
|
285
|
+
...initialState.commonComponents,
|
|
286
|
+
thirdPartyAuthContext: {
|
|
287
|
+
...initialState.commonComponents.thirdPartyAuthContext,
|
|
288
|
+
},
|
|
289
|
+
},
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
const { queryByText } = render(reduxWrapper(<IntlLoginPage {...props} />));
|
|
293
|
+
expect(queryByText('Company or school credentials')).toBeNull();
|
|
294
|
+
expect(queryByText('Or sign in with:')).toBeNull();
|
|
295
|
+
expect(queryByText('Institution/campus credentials')).toBeNull();
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
it('should hide sign-in header and enterprise login upon successful SSO authentication', () => {
|
|
299
|
+
store = mockStore({
|
|
300
|
+
...initialState,
|
|
301
|
+
commonComponents: {
|
|
302
|
+
...initialState.commonComponents,
|
|
303
|
+
thirdPartyAuthContext: {
|
|
304
|
+
...initialState.commonComponents.thirdPartyAuthContext,
|
|
305
|
+
providers: [ssoProvider],
|
|
306
|
+
secondaryProviders: [secondaryProviders],
|
|
307
|
+
currentProvider: 'Apple',
|
|
308
|
+
},
|
|
309
|
+
},
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
const { queryByText } = render(reduxWrapper(<IntlLoginPage {...props} />));
|
|
313
|
+
expect(queryByText('Company or school credentials')).toBeNull();
|
|
314
|
+
expect(queryByText('Or sign in with:')).toBeNull();
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
// ******** test enterprise login enabled scenarios ********
|
|
318
|
+
|
|
319
|
+
it('should show sign-in header for enterprise login', () => {
|
|
320
|
+
store = mockStore({
|
|
321
|
+
...initialState,
|
|
322
|
+
commonComponents: {
|
|
323
|
+
...initialState.commonComponents,
|
|
324
|
+
thirdPartyAuthContext: {
|
|
325
|
+
...initialState.commonComponents.thirdPartyAuthContext,
|
|
326
|
+
providers: [ssoProvider],
|
|
327
|
+
secondaryProviders: [secondaryProviders],
|
|
328
|
+
},
|
|
329
|
+
},
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
const { queryByText } = render(reduxWrapper(<IntlLoginPage {...props} />));
|
|
333
|
+
expect(queryByText('Or sign in with:')).toBeDefined();
|
|
334
|
+
expect(queryByText('Company or school credentials')).toBeDefined();
|
|
335
|
+
expect(queryByText('Institution/campus credentials')).toBeDefined();
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
// ******** test enterprise login disabled scenarios ********
|
|
339
|
+
|
|
340
|
+
it('should show sign-in header for institution login if enterprise login is disabled', () => {
|
|
341
|
+
mergeAppConfig(appId, {
|
|
342
|
+
DISABLE_ENTERPRISE_LOGIN: true,
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
store = mockStore({
|
|
346
|
+
...initialState,
|
|
347
|
+
commonComponents: {
|
|
348
|
+
...initialState.commonComponents,
|
|
349
|
+
thirdPartyAuthContext: {
|
|
350
|
+
...initialState.commonComponents.thirdPartyAuthContext,
|
|
351
|
+
providers: [ssoProvider],
|
|
352
|
+
secondaryProviders: [secondaryProviders],
|
|
353
|
+
},
|
|
354
|
+
},
|
|
355
|
+
});
|
|
356
|
+
|
|
357
|
+
const { queryByText } = render(reduxWrapper(<IntlLoginPage {...props} />));
|
|
358
|
+
expect(queryByText('Or sign in with:')).toBeDefined();
|
|
359
|
+
expect(queryByText('Company or school credentials')).toBeNull();
|
|
360
|
+
expect(queryByText('Institution/campus credentials')).toBeDefined();
|
|
361
|
+
|
|
362
|
+
mergeAppConfig(appId, {
|
|
363
|
+
DISABLE_ENTERPRISE_LOGIN: '',
|
|
364
|
+
});
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
it('should show sign-in header with secondary Providers and without Providers', () => {
|
|
368
|
+
mergeAppConfig(appId, {
|
|
369
|
+
DISABLE_ENTERPRISE_LOGIN: true,
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
store = mockStore({
|
|
373
|
+
...initialState,
|
|
374
|
+
commonComponents: {
|
|
375
|
+
...initialState.commonComponents,
|
|
376
|
+
thirdPartyAuthContext: {
|
|
377
|
+
...initialState.commonComponents.thirdPartyAuthContext,
|
|
378
|
+
secondaryProviders: [{
|
|
379
|
+
...secondaryProviders,
|
|
380
|
+
}],
|
|
381
|
+
},
|
|
382
|
+
},
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
const { queryByText } = render(reduxWrapper(<IntlLoginPage {...props} />));
|
|
386
|
+
expect(queryByText('Or sign in with:')).toBeDefined();
|
|
387
|
+
expect(queryByText('Institution/campus credentials')).toBeDefined();
|
|
388
|
+
|
|
389
|
+
mergeAppConfig(appId, {
|
|
390
|
+
DISABLE_ENTERPRISE_LOGIN: '',
|
|
391
|
+
});
|
|
392
|
+
});
|
|
393
|
+
|
|
394
|
+
it('should not show sign-in header without primary or secondary providers', () => {
|
|
395
|
+
store = mockStore({
|
|
396
|
+
...initialState,
|
|
397
|
+
commonComponents: {
|
|
398
|
+
...initialState.commonComponents,
|
|
399
|
+
thirdPartyAuthContext: {
|
|
400
|
+
...initialState.commonComponents.thirdPartyAuthContext,
|
|
401
|
+
},
|
|
402
|
+
},
|
|
403
|
+
});
|
|
404
|
+
|
|
405
|
+
const { queryByText } = render(reduxWrapper(<IntlLoginPage {...props} />));
|
|
406
|
+
expect(queryByText('Or sign in with:')).toBeNull();
|
|
407
|
+
expect(queryByText('Institution/campus credentials')).toBeNull();
|
|
408
|
+
expect(queryByText('Company or school credentials')).toBeNull();
|
|
409
|
+
});
|
|
410
|
+
|
|
411
|
+
it('should show enterprise login if even if only secondary providers are available', () => {
|
|
412
|
+
store = mockStore({
|
|
413
|
+
...initialState,
|
|
414
|
+
commonComponents: {
|
|
415
|
+
...initialState.commonComponents,
|
|
416
|
+
thirdPartyAuthContext: {
|
|
417
|
+
...initialState.commonComponents.thirdPartyAuthContext,
|
|
418
|
+
secondaryProviders: [secondaryProviders],
|
|
419
|
+
},
|
|
420
|
+
},
|
|
421
|
+
});
|
|
422
|
+
|
|
423
|
+
const { queryByText } = render(reduxWrapper(<IntlLoginPage {...props} />));
|
|
424
|
+
expect(queryByText('Or sign in with:')).toBeDefined();
|
|
425
|
+
expect(queryByText('Company or school credentials')).toBeNull();
|
|
426
|
+
expect(queryByText('Institution/campus credentials')).toBeDefined();
|
|
427
|
+
|
|
428
|
+
mergeAppConfig(appId, {
|
|
429
|
+
DISABLE_ENTERPRISE_LOGIN: '',
|
|
430
|
+
});
|
|
431
|
+
});
|
|
432
|
+
|
|
433
|
+
// ******** test alert messages ********
|
|
434
|
+
|
|
435
|
+
it('should match login internal server error message', () => {
|
|
436
|
+
const expectedMessage = 'We couldn\'t sign you in.'
|
|
437
|
+
+ 'An error has occurred. Try refreshing the page, or check your internet connection.';
|
|
438
|
+
store = mockStore({
|
|
439
|
+
...initialState,
|
|
440
|
+
login: {
|
|
441
|
+
...initialState.login,
|
|
442
|
+
loginErrorCode: INTERNAL_SERVER_ERROR,
|
|
443
|
+
},
|
|
444
|
+
});
|
|
445
|
+
|
|
446
|
+
render(reduxWrapper(<IntlLoginPage {...props} />));
|
|
447
|
+
expect(screen.getByText(
|
|
448
|
+
'',
|
|
449
|
+
{ selector: '#login-failure-alert' },
|
|
450
|
+
).textContent).toEqual(`${expectedMessage}`);
|
|
451
|
+
});
|
|
452
|
+
|
|
453
|
+
it('should match third party auth alert', () => {
|
|
454
|
+
store = mockStore({
|
|
455
|
+
...initialState,
|
|
456
|
+
commonComponents: {
|
|
457
|
+
...initialState.commonComponents,
|
|
458
|
+
thirdPartyAuthContext: {
|
|
459
|
+
...initialState.commonComponents.thirdPartyAuthContext,
|
|
460
|
+
currentProvider: 'Apple',
|
|
461
|
+
platformName: 'openedX',
|
|
462
|
+
},
|
|
463
|
+
},
|
|
464
|
+
});
|
|
465
|
+
|
|
466
|
+
const expectedMessage = `${'You have successfully signed into Apple, but your Apple account does not have a '
|
|
467
|
+
+ 'linked '}${getSiteConfig().siteName} account. To link your accounts, sign in now using your ${getSiteConfig().siteName} password.`;
|
|
468
|
+
|
|
469
|
+
render(reduxWrapper(<IntlLoginPage {...props} />));
|
|
470
|
+
expect(screen.getByText(
|
|
471
|
+
'',
|
|
472
|
+
{ selector: '#tpa-alert' },
|
|
473
|
+
).textContent).toEqual(expectedMessage);
|
|
474
|
+
});
|
|
475
|
+
|
|
476
|
+
it('should show third party authentication failure message', () => {
|
|
477
|
+
store = mockStore({
|
|
478
|
+
...initialState,
|
|
479
|
+
commonComponents: {
|
|
480
|
+
...initialState.commonComponents,
|
|
481
|
+
thirdPartyAuthContext: {
|
|
482
|
+
...initialState.commonComponents.thirdPartyAuthContext,
|
|
483
|
+
currentProvider: null,
|
|
484
|
+
errorMessage: 'An error occurred',
|
|
485
|
+
},
|
|
486
|
+
},
|
|
487
|
+
});
|
|
488
|
+
render(reduxWrapper(<IntlLoginPage {...props} />));
|
|
489
|
+
expect(screen.getByText(
|
|
490
|
+
'',
|
|
491
|
+
{ selector: '#login-failure-alert' },
|
|
492
|
+
).textContent).toContain('An error occurred');
|
|
493
|
+
});
|
|
494
|
+
|
|
495
|
+
it('should match invalid login form error message', () => {
|
|
496
|
+
const errorMessage = 'Please fill in the fields below.';
|
|
497
|
+
store = mockStore({
|
|
498
|
+
...initialState,
|
|
499
|
+
login: {
|
|
500
|
+
...initialState.login,
|
|
501
|
+
loginErrorCode: 'invalid-form',
|
|
502
|
+
},
|
|
503
|
+
});
|
|
504
|
+
|
|
505
|
+
render(reduxWrapper(<IntlLoginPage {...props} />));
|
|
506
|
+
expect(screen.getByText(
|
|
507
|
+
'',
|
|
508
|
+
{ selector: '#login-failure-alert' },
|
|
509
|
+
).textContent).toContain(errorMessage);
|
|
510
|
+
});
|
|
511
|
+
|
|
512
|
+
// ******** test redirection ********
|
|
513
|
+
|
|
514
|
+
it('should redirect to url returned by login endpoint after successful authentication', () => {
|
|
515
|
+
const dashboardURL = 'https://test.com/testing-dashboard/';
|
|
516
|
+
store = mockStore({
|
|
517
|
+
...initialState,
|
|
518
|
+
login: {
|
|
519
|
+
...initialState.login,
|
|
520
|
+
loginResult: {
|
|
521
|
+
success: true,
|
|
522
|
+
redirectUrl: dashboardURL,
|
|
523
|
+
},
|
|
524
|
+
},
|
|
525
|
+
});
|
|
526
|
+
|
|
527
|
+
delete window.location;
|
|
528
|
+
window.location = { href: getSiteConfig().baseUrl };
|
|
529
|
+
render(reduxWrapper(<IntlLoginPage {...props} />));
|
|
530
|
+
expect(window.location.href).toBe(dashboardURL);
|
|
531
|
+
});
|
|
532
|
+
|
|
533
|
+
it('should redirect to finishAuthUrl upon successful login via SSO', () => {
|
|
534
|
+
const authCompleteUrl = '/auth/complete/google-oauth2/';
|
|
535
|
+
store = mockStore({
|
|
536
|
+
...initialState,
|
|
537
|
+
login: {
|
|
538
|
+
...initialState.login,
|
|
539
|
+
loginResult: {
|
|
540
|
+
success: true,
|
|
541
|
+
redirectUrl: '',
|
|
542
|
+
},
|
|
543
|
+
},
|
|
544
|
+
commonComponents: {
|
|
545
|
+
...initialState.commonComponents,
|
|
546
|
+
thirdPartyAuthContext: {
|
|
547
|
+
...initialState.commonComponents.thirdPartyAuthContext,
|
|
548
|
+
finishAuthUrl: authCompleteUrl,
|
|
549
|
+
},
|
|
550
|
+
},
|
|
551
|
+
});
|
|
552
|
+
|
|
553
|
+
delete window.location;
|
|
554
|
+
window.location = { href: getSiteConfig().baseUrl };
|
|
555
|
+
|
|
556
|
+
render(reduxWrapper(<IntlLoginPage {...props} />));
|
|
557
|
+
expect(window.location.href).toBe(getSiteConfig().lmsBaseUrl + authCompleteUrl);
|
|
558
|
+
});
|
|
559
|
+
|
|
560
|
+
it('should redirect to social auth provider url on SSO button click', () => {
|
|
561
|
+
store = mockStore({
|
|
562
|
+
...initialState,
|
|
563
|
+
commonComponents: {
|
|
564
|
+
...initialState.commonComponents,
|
|
565
|
+
thirdPartyAuthContext: {
|
|
566
|
+
...initialState.commonComponents.thirdPartyAuthContext,
|
|
567
|
+
providers: [ssoProvider],
|
|
568
|
+
},
|
|
569
|
+
},
|
|
570
|
+
});
|
|
571
|
+
|
|
572
|
+
delete window.location;
|
|
573
|
+
window.location = { href: getSiteConfig().baseUrl };
|
|
574
|
+
|
|
575
|
+
render(reduxWrapper(<IntlLoginPage {...props} />));
|
|
576
|
+
|
|
577
|
+
fireEvent.click(screen.getByText(
|
|
578
|
+
'',
|
|
579
|
+
{ selector: '#oa2-apple-id' },
|
|
580
|
+
));
|
|
581
|
+
expect(window.location.href).toBe(getSiteConfig().lmsBaseUrl + ssoProvider.loginUrl);
|
|
582
|
+
});
|
|
583
|
+
|
|
584
|
+
it('should redirect to finishAuthUrl upon successful authentication via SSO', () => {
|
|
585
|
+
const finishAuthUrl = '/auth/complete/google-oauth2/';
|
|
586
|
+
store = mockStore({
|
|
587
|
+
...initialState,
|
|
588
|
+
login: {
|
|
589
|
+
...initialState.login,
|
|
590
|
+
loginResult: { success: true, redirectUrl: '' },
|
|
591
|
+
},
|
|
592
|
+
commonComponents: {
|
|
593
|
+
...initialState.commonComponents,
|
|
594
|
+
thirdPartyAuthContext: {
|
|
595
|
+
...initialState.commonComponents.thirdPartyAuthContext,
|
|
596
|
+
finishAuthUrl,
|
|
597
|
+
},
|
|
598
|
+
},
|
|
599
|
+
});
|
|
600
|
+
|
|
601
|
+
delete window.location;
|
|
602
|
+
window.location = { href: getSiteConfig().baseUrl };
|
|
603
|
+
|
|
604
|
+
render(reduxWrapper(<IntlLoginPage {...props} />));
|
|
605
|
+
expect(window.location.href).toBe(getSiteConfig().lmsBaseUrl + finishAuthUrl);
|
|
606
|
+
});
|
|
607
|
+
|
|
608
|
+
// ******** test hinted third party auth ********
|
|
609
|
+
|
|
610
|
+
it('should render tpa button for tpa_hint id matching one of the primary providers', () => {
|
|
611
|
+
store = mockStore({
|
|
612
|
+
...initialState,
|
|
613
|
+
commonComponents: {
|
|
614
|
+
...initialState.commonComponents,
|
|
615
|
+
thirdPartyAuthContext: {
|
|
616
|
+
...initialState.commonComponents.thirdPartyAuthContext,
|
|
617
|
+
providers: [ssoProvider],
|
|
618
|
+
},
|
|
619
|
+
thirdPartyAuthApiStatus: COMPLETE_STATE,
|
|
620
|
+
},
|
|
621
|
+
});
|
|
622
|
+
|
|
623
|
+
delete window.location;
|
|
624
|
+
window.location = { href: getSiteConfig().baseUrl.concat(LOGIN_PAGE), search: `?next=/dashboard&tpa_hint=${ssoProvider.id}` };
|
|
625
|
+
|
|
626
|
+
render(reduxWrapper(<IntlLoginPage {...props} />));
|
|
627
|
+
expect(screen.getByText(
|
|
628
|
+
'',
|
|
629
|
+
{ selector: `#${ssoProvider.id}` },
|
|
630
|
+
).textContent).toEqual(ssoProvider.name);
|
|
631
|
+
expect(screen.getByText(
|
|
632
|
+
'',
|
|
633
|
+
{ selector: `.btn-${ssoProvider.id}` },
|
|
634
|
+
)).toBeTruthy();
|
|
635
|
+
});
|
|
636
|
+
|
|
637
|
+
it('should render the skeleton when third party status is pending', () => {
|
|
638
|
+
store = mockStore({
|
|
639
|
+
...initialState,
|
|
640
|
+
commonComponents: {
|
|
641
|
+
...initialState.commonComponents,
|
|
642
|
+
thirdPartyAuthContext: {
|
|
643
|
+
...initialState.commonComponents.thirdPartyAuthContext,
|
|
644
|
+
providers: [ssoProvider],
|
|
645
|
+
},
|
|
646
|
+
thirdPartyAuthApiStatus: PENDING_STATE,
|
|
647
|
+
},
|
|
648
|
+
});
|
|
649
|
+
|
|
650
|
+
delete window.location;
|
|
651
|
+
window.location = { href: getSiteConfig().baseUrl.concat(LOGIN_PAGE), search: `?next=/dashboard&tpa_hint=${ssoProvider.id}` };
|
|
652
|
+
|
|
653
|
+
const { container } = render(reduxWrapper(<IntlLoginPage {...props} />));
|
|
654
|
+
expect(container.querySelector('.react-loading-skeleton')).toBeTruthy();
|
|
655
|
+
});
|
|
656
|
+
|
|
657
|
+
it('should render tpa button for tpa_hint id matching one of the secondary providers', () => {
|
|
658
|
+
secondaryProviders.skipHintedLogin = true;
|
|
659
|
+
store = mockStore({
|
|
660
|
+
...initialState,
|
|
661
|
+
commonComponents: {
|
|
662
|
+
...initialState.commonComponents,
|
|
663
|
+
thirdPartyAuthContext: {
|
|
664
|
+
...initialState.commonComponents.thirdPartyAuthContext,
|
|
665
|
+
secondaryProviders: [secondaryProviders],
|
|
666
|
+
},
|
|
667
|
+
thirdPartyAuthApiStatus: COMPLETE_STATE,
|
|
668
|
+
},
|
|
669
|
+
});
|
|
670
|
+
|
|
671
|
+
delete window.location;
|
|
672
|
+
window.location = { href: getSiteConfig().baseUrl.concat(LOGIN_PAGE), search: `?next=/dashboard&tpa_hint=${secondaryProviders.id}` };
|
|
673
|
+
secondaryProviders.iconImage = null;
|
|
674
|
+
|
|
675
|
+
render(reduxWrapper(<IntlLoginPage {...props} />));
|
|
676
|
+
expect(window.location.href).toEqual(getSiteConfig().lmsBaseUrl + secondaryProviders.loginUrl);
|
|
677
|
+
});
|
|
678
|
+
|
|
679
|
+
it('should render regular tpa button for invalid tpa_hint value', () => {
|
|
680
|
+
store = mockStore({
|
|
681
|
+
...initialState,
|
|
682
|
+
commonComponents: {
|
|
683
|
+
...initialState.commonComponents,
|
|
684
|
+
thirdPartyAuthContext: {
|
|
685
|
+
...initialState.commonComponents.thirdPartyAuthContext,
|
|
686
|
+
providers: [ssoProvider],
|
|
687
|
+
},
|
|
688
|
+
thirdPartyAuthApiStatus: COMPLETE_STATE,
|
|
689
|
+
},
|
|
690
|
+
});
|
|
691
|
+
|
|
692
|
+
delete window.location;
|
|
693
|
+
window.location = { href: getSiteConfig().baseUrl.concat(LOGIN_PAGE), search: '?next=/dashboard&tpa_hint=invalid' };
|
|
694
|
+
|
|
695
|
+
const { container } = render(reduxWrapper(<IntlLoginPage {...props} />));
|
|
696
|
+
expect(container.querySelector(`#${ssoProvider.id}`).querySelector('#provider-name').textContent).toEqual(`${ssoProvider.name}`);
|
|
697
|
+
|
|
698
|
+
mergeAppConfig(appId, {
|
|
699
|
+
DISABLE_ENTERPRISE_LOGIN: '',
|
|
700
|
+
});
|
|
701
|
+
});
|
|
702
|
+
|
|
703
|
+
it('should render "other ways to sign in" button on the tpa_hint page', () => {
|
|
704
|
+
store = mockStore({
|
|
705
|
+
...initialState,
|
|
706
|
+
commonComponents: {
|
|
707
|
+
...initialState.commonComponents,
|
|
708
|
+
thirdPartyAuthContext: {
|
|
709
|
+
...initialState.commonComponents.thirdPartyAuthContext,
|
|
710
|
+
providers: [ssoProvider],
|
|
711
|
+
},
|
|
712
|
+
thirdPartyAuthApiStatus: COMPLETE_STATE,
|
|
713
|
+
},
|
|
714
|
+
});
|
|
715
|
+
|
|
716
|
+
mergeAppConfig(appId, {
|
|
717
|
+
ALLOW_PUBLIC_ACCOUNT_CREATION: true,
|
|
718
|
+
SHOW_REGISTRATION_LINKS: true,
|
|
719
|
+
});
|
|
720
|
+
|
|
721
|
+
delete window.location;
|
|
722
|
+
window.location = { href: getSiteConfig().baseUrl.concat(LOGIN_PAGE), search: `?tpa_hint=${ssoProvider.id}` };
|
|
723
|
+
|
|
724
|
+
render(reduxWrapper(<IntlLoginPage {...props} />));
|
|
725
|
+
expect(screen.getByText(
|
|
726
|
+
'Show me other ways to sign in or register',
|
|
727
|
+
).textContent).toBeDefined();
|
|
728
|
+
});
|
|
729
|
+
|
|
730
|
+
it('should render other ways to sign in button when public account creation is disabled', () => {
|
|
731
|
+
mergeAppConfig(appId, {
|
|
732
|
+
ALLOW_PUBLIC_ACCOUNT_CREATION: false,
|
|
733
|
+
});
|
|
734
|
+
|
|
735
|
+
store = mockStore({
|
|
736
|
+
...initialState,
|
|
737
|
+
commonComponents: {
|
|
738
|
+
...initialState.commonComponents,
|
|
739
|
+
thirdPartyAuthContext: {
|
|
740
|
+
...initialState.commonComponents.thirdPartyAuthContext,
|
|
741
|
+
providers: [ssoProvider],
|
|
742
|
+
},
|
|
743
|
+
thirdPartyAuthApiStatus: COMPLETE_STATE,
|
|
744
|
+
},
|
|
745
|
+
});
|
|
746
|
+
|
|
747
|
+
delete window.location;
|
|
748
|
+
window.location = { href: getSiteConfig().baseUrl.concat(LOGIN_PAGE), search: `?tpa_hint=${ssoProvider.id}` };
|
|
749
|
+
|
|
750
|
+
render(reduxWrapper(<IntlLoginPage {...props} />));
|
|
751
|
+
expect(screen.getByText(
|
|
752
|
+
'Show me other ways to sign in',
|
|
753
|
+
).textContent).toBeDefined();
|
|
754
|
+
});
|
|
755
|
+
|
|
756
|
+
// ******** miscellaneous tests ********
|
|
757
|
+
|
|
758
|
+
it('should send page event when login page is rendered', () => {
|
|
759
|
+
render(reduxWrapper(<IntlLoginPage {...props} />));
|
|
760
|
+
expect(analyticsService.sendPageEvent).toHaveBeenCalledWith('login_and_registration', 'login', undefined);
|
|
761
|
+
});
|
|
762
|
+
|
|
763
|
+
it('tests that form is in invalid state when it is submitted', () => {
|
|
764
|
+
store = mockStore({
|
|
765
|
+
...initialState,
|
|
766
|
+
login: {
|
|
767
|
+
...initialState.login,
|
|
768
|
+
shouldBackupState: true,
|
|
769
|
+
},
|
|
770
|
+
});
|
|
771
|
+
|
|
772
|
+
store.dispatch = jest.fn(store.dispatch);
|
|
773
|
+
render(reduxWrapper(<IntlLoginPage {...props} />));
|
|
774
|
+
expect(store.dispatch).toHaveBeenCalledWith(backupLoginFormBegin(
|
|
775
|
+
{
|
|
776
|
+
formFields: {
|
|
777
|
+
emailOrUsername: '', password: '',
|
|
778
|
+
},
|
|
779
|
+
errors: {
|
|
780
|
+
emailOrUsername: '', password: '',
|
|
781
|
+
},
|
|
782
|
+
},
|
|
783
|
+
));
|
|
784
|
+
});
|
|
785
|
+
|
|
786
|
+
it('should send track event when forgot password link is clicked', () => {
|
|
787
|
+
render(reduxWrapper(<IntlLoginPage {...props} />));
|
|
788
|
+
fireEvent.click(screen.getByText(
|
|
789
|
+
'Forgot password',
|
|
790
|
+
{ selector: '#forgot-password' },
|
|
791
|
+
));
|
|
792
|
+
|
|
793
|
+
expect(analyticsService.sendTrackEvent).toHaveBeenCalledWith('edx.bi.password-reset_form.toggled', { category: 'user-engagement' });
|
|
794
|
+
});
|
|
795
|
+
|
|
796
|
+
it('should backup the login form state when shouldBackupState is true', () => {
|
|
797
|
+
store = mockStore({
|
|
798
|
+
...initialState,
|
|
799
|
+
login: {
|
|
800
|
+
...initialState.login,
|
|
801
|
+
shouldBackupState: true,
|
|
802
|
+
},
|
|
803
|
+
});
|
|
804
|
+
|
|
805
|
+
store.dispatch = jest.fn(store.dispatch);
|
|
806
|
+
render(reduxWrapper(<IntlLoginPage {...props} />));
|
|
807
|
+
expect(store.dispatch).toHaveBeenCalledWith(backupLoginFormBegin(
|
|
808
|
+
{
|
|
809
|
+
formFields: {
|
|
810
|
+
emailOrUsername: '', password: '',
|
|
811
|
+
},
|
|
812
|
+
errors: {
|
|
813
|
+
emailOrUsername: '', password: '',
|
|
814
|
+
},
|
|
815
|
+
},
|
|
816
|
+
));
|
|
817
|
+
});
|
|
818
|
+
|
|
819
|
+
it('should update form fields state if updated in redux store', () => {
|
|
820
|
+
store = mockStore({
|
|
821
|
+
...initialState,
|
|
822
|
+
login: {
|
|
823
|
+
...initialState.login,
|
|
824
|
+
loginFormData: {
|
|
825
|
+
formFields: {
|
|
826
|
+
emailOrUsername: 'john_doe', password: 'test-password',
|
|
827
|
+
},
|
|
828
|
+
errors: {
|
|
829
|
+
emailOrUsername: '', password: '',
|
|
830
|
+
},
|
|
831
|
+
},
|
|
832
|
+
},
|
|
833
|
+
});
|
|
834
|
+
|
|
835
|
+
const { container } = render(reduxWrapper(<IntlLoginPage {...props} />));
|
|
836
|
+
expect(container.querySelector('input#emailOrUsername').value).toEqual('john_doe');
|
|
837
|
+
expect(container.querySelector('input#password').value).toEqual('test-password');
|
|
838
|
+
});
|
|
839
|
+
});
|