diff --git a/src/register/RegistrationPage.test.jsx b/src/register/RegistrationPage.test.jsx
index 6bc2ce5a89..bcc13521fd 100644
--- a/src/register/RegistrationPage.test.jsx
+++ b/src/register/RegistrationPage.test.jsx
@@ -11,19 +11,16 @@ import { mockNavigate, BrowserRouter as Router } from 'react-router-dom';
import renderer from 'react-test-renderer';
import configureStore from 'redux-mock-store';
-import RegistrationFailureMessage from './components/RegistrationFailure';
import {
backupRegistrationFormBegin,
clearRegistrationBackendError,
registerNewUser,
setUserPipelineDataLoaded,
} from './data/actions';
-import {
- FIELDS, FORBIDDEN_REQUEST, INTERNAL_SERVER_ERROR, TPA_AUTHENTICATION_FAILURE, TPA_SESSION_EXPIRED,
-} from './data/constants';
+import { INTERNAL_SERVER_ERROR } from './data/constants';
import RegistrationPage from './RegistrationPage';
import {
- AUTHN_PROGRESSIVE_PROFILING, COMPLETE_STATE, LOGIN_PAGE, PENDING_STATE, REGISTER_PAGE,
+ AUTHN_PROGRESSIVE_PROFILING, COMPLETE_STATE, PENDING_STATE, REGISTER_PAGE,
} from '../data/constants';
jest.mock('@edx/frontend-platform/analytics', () => ({
@@ -36,7 +33,6 @@ jest.mock('@edx/frontend-platform/i18n', () => ({
}));
const IntlRegistrationPage = injectIntl(RegistrationPage);
-const IntlRegistrationFailure = injectIntl(RegistrationFailureMessage);
const mockStore = configureStore();
jest.mock('react-router-dom', () => {
@@ -95,7 +91,6 @@ describe('RegistrationPage', () => {
currentProvider: null,
finishAuthUrl: null,
providers: [],
- secondaryProviders: [],
pipelineUserDetails: null,
countryCode: null,
};
@@ -152,14 +147,6 @@ describe('RegistrationPage', () => {
}
};
- const ssoProvider = {
- id: 'oa2-apple-id',
- name: 'Apple',
- iconClass: 'apple',
- iconImage: 'https://openedx.devstack.lms/logo.png',
- loginUrl: '/auth/login/apple-id/?auth_entry=login&next=/dashboard',
- };
-
describe('Test Registration Page', () => {
mergeConfig({
SHOW_CONFIGURABLE_EDX_FIELDS: true,
@@ -173,10 +160,6 @@ describe('RegistrationPage', () => {
country: 'Select your country or region of residence',
};
- const secondaryProviders = {
- id: 'saml-test', name: 'Test University', loginUrl: '/dummy-auth', registerUrl: '/dummy_auth',
- };
-
// ******** test registration form submission ********
it('should submit form for valid input', () => {
@@ -213,7 +196,7 @@ describe('RegistrationPage', () => {
email: 'john.doe@example.com',
country: 'Pakistan',
honor_code: true,
- social_auth_provider: ssoProvider.name,
+ social_auth_provider: 'Apple',
totalRegistrationTime: 0,
};
@@ -223,7 +206,7 @@ describe('RegistrationPage', () => {
...initialState.commonComponents,
thirdPartyAuthContext: {
...initialState.commonComponents.thirdPartyAuthContext,
- currentProvider: ssoProvider.name,
+ currentProvider: 'Apple',
},
},
});
@@ -341,81 +324,6 @@ describe('RegistrationPage', () => {
expect(store.dispatch).toHaveBeenCalledWith(clearRegistrationBackendError('email'));
});
- // ******** test alert messages ********
-
- it('should match third party auth alert', () => {
- store = mockStore({
- ...initialState,
- commonComponents: {
- ...initialState.commonComponents,
- thirdPartyAuthContext: {
- ...initialState.commonComponents.thirdPartyAuthContext,
- currentProvider: 'Apple',
- },
- },
- });
-
- const expectedMessage = `${'You\'ve successfully signed into Apple! We just need a little more information before '
- + 'you start learning with '}${ getConfig().SITE_NAME }.`;
-
- const registrationPage = mount(routerWrapper(reduxWrapper()));
- expect(registrationPage.find('#tpa-alert').find('p').text()).toEqual(expectedMessage);
- });
-
- it('should match internal server error message', () => {
- const expectedMessage = 'We couldn\'t create your account.An error has occurred. Try refreshing the page, or check your internet connection.';
- props = {
- errorCode: INTERNAL_SERVER_ERROR,
- failureCount: 0,
- };
-
- const registrationPage = mount(reduxWrapper());
- expect(registrationPage.find('div.alert-heading').length).toEqual(1);
- expect(registrationPage.find('div.alert').first().text()).toEqual(expectedMessage);
- });
-
- it('should match registration api rate limit error message', () => {
- const expectedMessage = 'We couldn\'t create your account.Too many failed registration attempts. Try again later.';
- props = {
- errorCode: FORBIDDEN_REQUEST,
- failureCount: 0,
- };
-
- const registrationPage = mount(reduxWrapper());
- expect(registrationPage.find('div.alert-heading').length).toEqual(1);
- expect(registrationPage.find('div.alert').first().text()).toEqual(expectedMessage);
- });
-
- it('should match tpa session expired error message', () => {
- const expectedMessage = 'We couldn\'t create your account.Registration using Google has timed out.';
- props = {
- context: {
- provider: 'Google',
- },
- errorCode: TPA_SESSION_EXPIRED,
- failureCount: 0,
- };
-
- const registrationPage = mount(reduxWrapper());
- expect(registrationPage.find('div.alert-heading').length).toEqual(1);
- expect(registrationPage.find('div.alert').first().text()).toEqual(expectedMessage);
- });
-
- it('should match tpa authentication failed error message', () => {
- const expectedMessageSubstring = 'We are sorry, you are not authorized to access';
- props = {
- context: {
- provider: 'Google',
- },
- errorCode: TPA_AUTHENTICATION_FAILURE,
- failureCount: 0,
- };
-
- const registrationPage = mount(reduxWrapper());
- expect(registrationPage.find('div.alert-heading').length).toEqual(1);
- expect(registrationPage.find('div.alert').first().text()).toContain(expectedMessageSubstring);
- });
-
// ******** test form buttons and fields ********
it('should match default button state', () => {
@@ -451,58 +359,6 @@ describe('RegistrationPage', () => {
});
});
- it('should show single sign on provider button', () => {
- store = mockStore({
- ...initialState,
- commonComponents: {
- ...initialState.commonComponents,
- thirdPartyAuthContext: {
- ...initialState.commonComponents.thirdPartyAuthContext,
- providers: [ssoProvider],
- },
- },
- });
-
- const registrationPage = mount(routerWrapper(reduxWrapper()));
- expect(registrationPage.find(`button#${ssoProvider.id}`).length).toEqual(1);
- });
-
- it('should display institution register button', () => {
- mergeConfig({
- DISABLE_ENTERPRISE_LOGIN: 'true',
- });
-
- store = mockStore({
- ...initialState,
- commonComponents: {
- ...initialState.commonComponents,
- thirdPartyAuthContext: {
- ...initialState.commonComponents.thirdPartyAuthContext,
- secondaryProviders: [secondaryProviders],
- },
- },
- });
- delete window.location;
- window.location = { href: getConfig().BASE_URL };
-
- const root = mount(routerWrapper(reduxWrapper()));
- expect(root.text().includes('Institution/campus credentials')).toBe(true);
-
- mergeConfig({
- DISABLE_ENTERPRISE_LOGIN: '',
- });
- });
-
- it('should display InstitutionLogistration if insitutionLogin prop is true', () => {
- props = {
- ...props,
- institutionLogin: true,
- };
-
- const registrationPage = mount(routerWrapper(reduxWrapper()));
- expect(registrationPage.find('.institutions__heading').text()).toEqual('Register with institution/campus credentials');
- });
-
it('should show button label based on cta query params value', () => {
const buttonLabel = 'Register';
delete window.location;
@@ -511,22 +367,6 @@ describe('RegistrationPage', () => {
expect(registrationPage.find('button[type="submit"] span').first().text()).toEqual(buttonLabel);
});
- it('should not display password field when current provider is present', () => {
- store = mockStore({
- ...initialState,
- commonComponents: {
- ...initialState.commonComponents,
- thirdPartyAuthContext: {
- ...initialState.commonComponents.thirdPartyAuthContext,
- currentProvider: ssoProvider.name,
- },
- },
- });
-
- const registrationPage = mount(routerWrapper(reduxWrapper()));
- expect(registrationPage.find('input#password').length).toEqual(0);
- });
-
it('should check user retention cookie', () => {
store = mockStore({
...initialState,
@@ -560,57 +400,6 @@ describe('RegistrationPage', () => {
expect(window.location.href).toBe(dashboardURL);
});
- it('should redirect to social auth provider url on SSO button click', () => {
- const registerUrl = '/auth/login/apple-id/?auth_entry=register&next=/dashboard';
- store = mockStore({
- ...initialState,
- commonComponents: {
- ...initialState.commonComponents,
- thirdPartyAuthContext: {
- ...initialState.commonComponents.thirdPartyAuthContext,
- providers: [{
- ...ssoProvider,
- registerUrl,
- }],
- },
- },
- });
-
- delete window.location;
- window.location = { href: getConfig().BASE_URL };
-
- const loginPage = mount(routerWrapper(reduxWrapper()));
-
- loginPage.find('button#oa2-apple-id').simulate('click');
- expect(window.location.href).toBe(getConfig().LMS_BASE_URL + registerUrl);
- });
-
- it('should redirect to finishAuthUrl upon successful registration via SSO', () => {
- const authCompleteUrl = '/auth/complete/google-oauth2/';
- store = mockStore({
- ...initialState,
- register: {
- ...initialState.register,
- registrationResult: {
- success: true,
- },
- },
- commonComponents: {
- ...initialState.commonComponents,
- thirdPartyAuthContext: {
- ...initialState.commonComponents.thirdPartyAuthContext,
- finishAuthUrl: authCompleteUrl,
- },
- },
- });
-
- delete window.location;
- window.location = { href: getConfig().BASE_URL };
-
- renderer.create(routerWrapper(reduxWrapper()));
- expect(window.location.href).toBe(getConfig().LMS_BASE_URL + authCompleteUrl);
- });
-
it('should redirect to dashboard if features flags are configured but no optional fields are configured', () => {
mergeConfig({
ENABLE_PROGRESSIVE_PROFILING_ON_AUTHN: true,
@@ -672,112 +461,6 @@ describe('RegistrationPage', () => {
expect(mockNavigate).toHaveBeenCalledWith(AUTHN_PROGRESSIVE_PROFILING);
});
- // ******** test hinted third party auth ********
-
- it('should render tpa button for tpa_hint id matching one of the primary providers', () => {
- store = mockStore({
- ...initialState,
- commonComponents: {
- ...initialState.commonComponents,
- thirdPartyAuthContext: {
- ...initialState.commonComponents.thirdPartyAuthContext,
- providers: [ssoProvider],
- },
- thirdPartyAuthApiStatus: COMPLETE_STATE,
- },
- });
-
- delete window.location;
- window.location = { href: getConfig().BASE_URL.concat(LOGIN_PAGE), search: `?next=/dashboard&tpa_hint=${ssoProvider.id}` };
-
- const registrationPage = mount(routerWrapper(reduxWrapper()));
- expect(registrationPage.find(`button#${ssoProvider.id}`).find('span').text()).toEqual(ssoProvider.name);
- expect(registrationPage.find(`button#${ssoProvider.id}`).hasClass(`btn-tpa btn-${ssoProvider.id}`)).toEqual(true);
- });
-
- it('should display skeleton if tpa_hint is true and thirdPartyAuthContext is pending', () => {
- store = mockStore({
- ...initialState,
- commonComponents: {
- ...initialState.commonComponents,
- thirdPartyAuthApiStatus: PENDING_STATE,
- },
- });
-
- delete window.location;
- window.location = {
- href: getConfig().BASE_URL.concat(LOGIN_PAGE),
- search: `?next=/dashboard&tpa_hint=${ssoProvider.id}`,
- };
-
- const registrationPage = mount(routerWrapper(reduxWrapper()));
- expect(registrationPage.find('.react-loading-skeleton').exists()).toBeTruthy();
- });
-
- it('should render icon if icon classes are missing in providers', () => {
- ssoProvider.iconClass = null;
- store = mockStore({
- ...initialState,
- commonComponents: {
- ...initialState.commonComponents,
- thirdPartyAuthContext: {
- ...initialState.commonComponents.thirdPartyAuthContext,
- providers: [ssoProvider],
- },
- thirdPartyAuthApiStatus: COMPLETE_STATE,
- },
- });
-
- delete window.location;
- window.location = { href: getConfig().BASE_URL.concat(REGISTER_PAGE), search: `?next=/dashboard&tpa_hint=${ssoProvider.id}` };
- ssoProvider.iconImage = null;
-
- const registrationPage = mount(routerWrapper(reduxWrapper()));
- expect(registrationPage.find(`button#${ssoProvider.id}`).find('div').find('span').hasClass('pgn__icon')).toEqual(true);
- });
-
- it('should render tpa button for tpa_hint id matching one of the secondary providers', () => {
- secondaryProviders.skipHintedLogin = true;
- store = mockStore({
- ...initialState,
- commonComponents: {
- ...initialState.commonComponents,
- thirdPartyAuthContext: {
- ...initialState.commonComponents.thirdPartyAuthContext,
- secondaryProviders: [secondaryProviders],
- },
- thirdPartyAuthApiStatus: COMPLETE_STATE,
- },
- });
-
- delete window.location;
- window.location = { href: getConfig().BASE_URL.concat(REGISTER_PAGE), search: `?next=/dashboard&tpa_hint=${secondaryProviders.id}` };
-
- mount(routerWrapper(reduxWrapper()));
- expect(window.location.href).toEqual(getConfig().LMS_BASE_URL + secondaryProviders.registerUrl);
- });
-
- it('should render regular tpa button for invalid tpa_hint value', () => {
- const expectedMessage = `${ssoProvider.name}`;
- store = mockStore({
- ...initialState,
- commonComponents: {
- ...initialState.commonComponents,
- thirdPartyAuthContext: {
- ...initialState.commonComponents.thirdPartyAuthContext,
- providers: [ssoProvider],
- },
- thirdPartyAuthApiStatus: COMPLETE_STATE,
- },
- });
-
- delete window.location;
- window.location = { href: getConfig().BASE_URL.concat(LOGIN_PAGE), search: '?next=/dashboard&tpa_hint=invalid' };
-
- const registrationPage = mount(routerWrapper(reduxWrapper()));
- expect(registrationPage.find(`button#${ssoProvider.id}`).find('span#provider-name').text()).toEqual(expectedMessage);
- });
-
// ******** miscellaneous tests ********
it('should backup the registration form state when shouldBackupState is true', () => {
@@ -998,7 +681,6 @@ describe('RegistrationPage', () => {
thirdPartyAuthApiStatus: COMPLETE_STATE,
thirdPartyAuthContext: {
...initialState.commonComponents.thirdPartyAuthContext,
- currentProvider: ssoProvider.name,
pipelineUserDetails: {
name: 'John Doe',
username: 'john_doe',
@@ -1046,7 +728,7 @@ describe('RegistrationPage', () => {
thirdPartyAuthApiStatus: COMPLETE_STATE,
thirdPartyAuthContext: {
...initialState.commonComponents.thirdPartyAuthContext,
- currentProvider: ssoProvider.name,
+ currentProvider: 'Apple',
pipelineUserDetails: {
name: 'John Doe',
username: 'john_doe',
@@ -1068,163 +750,5 @@ describe('RegistrationPage', () => {
totalRegistrationTime: 0,
}));
});
-
- it('should display errorMessage if third party authentication fails', () => {
- jest.spyOn(global.Date, 'now').mockImplementation(() => 0);
- getLocale.mockImplementation(() => ('en-us'));
-
- store = mockStore({
- ...initialState,
- register: {
- ...initialState.register,
- backendCountryCode: 'PK',
- userPipelineDataLoaded: false,
- },
- commonComponents: {
- ...initialState.commonComponents,
- thirdPartyAuthApiStatus: COMPLETE_STATE,
- thirdPartyAuthContext: {
- ...initialState.commonComponents.thirdPartyAuthContext,
- currentProvider: null,
- pipelineUserDetails: {},
- errorMessage: 'An error occurred',
- },
- },
- });
-
- store.dispatch = jest.fn(store.dispatch);
-
- const registrationPage = mount(routerWrapper(reduxWrapper()));
- expect(registrationPage.find('div.alert-heading').length).toEqual(1);
- expect(registrationPage.find('div.alert').first().text()).toContain('An error occurred');
- });
- });
-
- describe('Test Configurable Fields', () => {
- mergeConfig({
- ENABLE_DYNAMIC_REGISTRATION_FIELDS: true,
- SHOW_CONFIGURABLE_EDX_FIELDS: true,
- });
-
- it('should render fields returned by backend', () => {
- store = mockStore({
- ...initialState,
- commonComponents: {
- ...initialState.commonComponents,
- fieldDescriptions: {
- profession: { name: 'profession', type: 'text', label: 'Profession' },
- terms_of_service: {
- name: FIELDS.TERMS_OF_SERVICE,
- error_message: 'You must agree to the Terms and Service agreement of our site',
- },
- },
- },
- });
- const registrationPage = mount(routerWrapper(reduxWrapper()));
- expect(registrationPage.find('#profession').exists()).toBeTruthy();
- expect(registrationPage.find('#tos').exists()).toBeTruthy();
- });
-
- it('should submit form with fields returned by backend in payload', () => {
- getLocale.mockImplementation(() => ('en-us'));
- jest.spyOn(global.Date, 'now').mockImplementation(() => 0);
- store = mockStore({
- ...initialState,
- commonComponents: {
- ...initialState.commonComponents,
- fieldDescriptions: {
- profession: { name: 'profession', type: 'text', label: 'Profession' },
- },
- extendedProfile: ['profession'],
- },
- });
-
- const payload = {
- name: 'John Doe',
- username: 'john_doe',
- email: 'john.doe@example.com',
- password: 'password1',
- country: 'Pakistan',
- honor_code: true,
- profession: 'Engineer',
- totalRegistrationTime: 0,
- };
-
- store.dispatch = jest.fn(store.dispatch);
- const registrationPage = mount(routerWrapper(reduxWrapper()));
-
- populateRequiredFields(registrationPage, payload);
- registrationPage.find('input#profession').simulate('change', { target: { value: 'Engineer', name: 'profession' } });
- registrationPage.find('button.btn-brand').simulate('click');
- expect(store.dispatch).toHaveBeenCalledWith(registerNewUser({ ...payload, country: 'PK' }));
- });
-
- it('should show error messages for required fields on empty form submission', () => {
- const professionError = 'Enter your profession';
- const countryError = 'Select your country or region of residence';
- const confirmEmailError = 'Enter your email';
-
- store = mockStore({
- ...initialState,
- commonComponents: {
- ...initialState.commonComponents,
- fieldDescriptions: {
- profession: {
- name: 'profession', type: 'text', label: 'Profession', error_message: professionError,
- },
- confirm_email: {
- name: 'confirm_email', type: 'text', label: 'Confirm Email', error_message: confirmEmailError,
- },
- country: { name: 'country' },
- },
- },
- });
-
- const registrationPage = mount(routerWrapper(reduxWrapper()));
- registrationPage.find('button.btn-brand').simulate('click');
-
- expect(registrationPage.find('#profession-error').last().text()).toEqual(professionError);
- expect(registrationPage.find('div[feedback-for="country"]').text()).toEqual(countryError);
- expect(registrationPage.find('#confirm_email-error').last().text()).toEqual(confirmEmailError);
- });
-
- it('should show error if email and confirm email fields do not match', () => {
- store = mockStore({
- ...initialState,
- commonComponents: {
- ...initialState.commonComponents,
- fieldDescriptions: {
- confirm_email: {
- name: 'confirm_email', type: 'text', label: 'Confirm Email',
- },
- },
- },
- });
- const registrationPage = mount(routerWrapper(reduxWrapper()));
- registrationPage.find('input#email').simulate('change', { target: { value: 'test1@gmail.com', name: 'email' } });
- registrationPage.find('input#confirm_email').simulate('blur', { target: { value: 'test2@gmail.com', name: 'confirm_email' } });
- expect(registrationPage.find('div#confirm_email-error').text()).toEqual('The email addresses do not match.');
- });
-
- it('should run validations for configurable focused field on form submission', () => {
- const professionError = 'Enter your profession';
- store = mockStore({
- ...initialState,
- commonComponents: {
- ...initialState.commonComponents,
- fieldDescriptions: {
- profession: {
- name: 'profession', type: 'text', label: 'Profession', error_message: professionError,
- },
- },
- },
- });
-
- const registrationPage = mount(routerWrapper(reduxWrapper()));
- registrationPage.find('input#profession').simulate('focus', { target: { value: '', name: 'profession' } });
- registrationPage.find('button.btn-brand').simulate('click');
-
- expect(registrationPage.find('#profession-error').last().text()).toEqual(professionError);
- });
});
});
diff --git a/src/register/components/ConfigurableRegistrationForm.test.jsx b/src/register/components/ConfigurableRegistrationForm.test.jsx
index 282a25eb5b..eb6028f12b 100644
--- a/src/register/components/ConfigurableRegistrationForm.test.jsx
+++ b/src/register/components/ConfigurableRegistrationForm.test.jsx
@@ -10,7 +10,9 @@ import { BrowserRouter as Router } from 'react-router-dom';
import configureStore from 'redux-mock-store';
import ConfigurableRegistrationForm from './ConfigurableRegistrationForm';
+import { registerNewUser } from '../data/actions';
import { FIELDS } from '../data/constants';
+import RegistrationPage from '../RegistrationPage';
jest.mock('@edx/frontend-platform/analytics', () => ({
sendPageEvent: jest.fn(),
@@ -22,6 +24,7 @@ jest.mock('@edx/frontend-platform/i18n', () => ({
}));
const IntlConfigurableRegistrationForm = injectIntl(ConfigurableRegistrationForm);
+const IntlRegistrationPage = injectIntl(RegistrationPage);
const mockStore = configureStore();
jest.mock('react-router-dom', () => {
@@ -113,6 +116,8 @@ describe('ConfigurableRegistrationForm', () => {
setFormFields: jest.fn(),
registrationEmbedded: false,
autoSubmitRegistrationForm: false,
+ handleInstitutionLogin: jest.fn(),
+ institutionLogin: false,
};
window.location = { search: '' };
getLocale.mockImplementationOnce(() => ('en-us'));
@@ -122,6 +127,19 @@ describe('ConfigurableRegistrationForm', () => {
jest.clearAllMocks();
});
+ const populateRequiredFields = (registrationPage, payload, isThirdPartyAuth = false) => {
+ registrationPage.find('input#name').simulate('change', { target: { value: payload.name, name: 'name' } });
+ registrationPage.find('input#username').simulate('change', { target: { value: payload.username, name: 'username' } });
+ registrationPage.find('input#email').simulate('change', { target: { value: payload.email, name: 'email' } });
+
+ registrationPage.find('input[name="country"]').simulate('change', { target: { value: payload.country, name: 'country' } });
+ registrationPage.find('input[name="country"]').simulate('blur', { target: { value: payload.country, name: 'country' } });
+
+ if (!isThirdPartyAuth) {
+ registrationPage.find('input#password').simulate('change', { target: { value: payload.password, name: 'password' } });
+ }
+ };
+
describe('Test Configurable Fields', () => {
mergeConfig({
ENABLE_DYNAMIC_REGISTRATION_FIELDS: true,
@@ -182,5 +200,128 @@ describe('ConfigurableRegistrationForm', () => {
[FIELDS.TERMS_OF_SERVICE]: true,
});
});
+
+ it('should render fields returned by backend', () => {
+ store = mockStore({
+ ...initialState,
+ commonComponents: {
+ ...initialState.commonComponents,
+ fieldDescriptions: {
+ profession: { name: 'profession', type: 'text', label: 'Profession' },
+ terms_of_service: {
+ name: FIELDS.TERMS_OF_SERVICE,
+ error_message: 'You must agree to the Terms and Service agreement of our site',
+ },
+ },
+ },
+ });
+ const registrationPage = mount(routerWrapper(reduxWrapper()));
+ expect(registrationPage.find('#profession').exists()).toBeTruthy();
+ expect(registrationPage.find('#tos').exists()).toBeTruthy();
+ });
+
+ it('should submit form with fields returned by backend in payload', () => {
+ mergeConfig({
+ SHOW_CONFIGURABLE_EDX_FIELDS: true,
+ });
+ getLocale.mockImplementation(() => ('en-us'));
+ jest.spyOn(global.Date, 'now').mockImplementation(() => 0);
+ store = mockStore({
+ ...initialState,
+ commonComponents: {
+ ...initialState.commonComponents,
+ fieldDescriptions: {
+ profession: { name: 'profession', type: 'text', label: 'Profession' },
+ },
+ extendedProfile: ['profession'],
+ },
+ });
+
+ const payload = {
+ name: 'John Doe',
+ username: 'john_doe',
+ email: 'john.doe@example.com',
+ password: 'password1',
+ country: 'Pakistan',
+ honor_code: true,
+ profession: 'Engineer',
+ totalRegistrationTime: 0,
+ };
+
+ store.dispatch = jest.fn(store.dispatch);
+ const registrationPage = mount(routerWrapper(reduxWrapper()));
+
+ populateRequiredFields(registrationPage, payload);
+ registrationPage.find('input#profession').simulate('change', { target: { value: 'Engineer', name: 'profession' } });
+ registrationPage.find('button.btn-brand').simulate('click');
+ expect(store.dispatch).toHaveBeenCalledWith(registerNewUser({ ...payload, country: 'PK' }));
+ });
+
+ it('should show error messages for required fields on empty form submission', () => {
+ const professionError = 'Enter your profession';
+ const countryError = 'Select your country or region of residence';
+ const confirmEmailError = 'Enter your email';
+
+ store = mockStore({
+ ...initialState,
+ commonComponents: {
+ ...initialState.commonComponents,
+ fieldDescriptions: {
+ profession: {
+ name: 'profession', type: 'text', label: 'Profession', error_message: professionError,
+ },
+ confirm_email: {
+ name: 'confirm_email', type: 'text', label: 'Confirm Email', error_message: confirmEmailError,
+ },
+ country: { name: 'country' },
+ },
+ },
+ });
+
+ const registrationPage = mount(routerWrapper(reduxWrapper()));
+ registrationPage.find('button.btn-brand').simulate('click');
+
+ expect(registrationPage.find('#profession-error').last().text()).toEqual(professionError);
+ expect(registrationPage.find('div[feedback-for="country"]').text()).toEqual(countryError);
+ expect(registrationPage.find('#confirm_email-error').last().text()).toEqual(confirmEmailError);
+ });
+ it('should show error if email and confirm email fields do not match', () => {
+ store = mockStore({
+ ...initialState,
+ commonComponents: {
+ ...initialState.commonComponents,
+ fieldDescriptions: {
+ confirm_email: {
+ name: 'confirm_email', type: 'text', label: 'Confirm Email',
+ },
+ },
+ },
+ });
+ const registrationPage = mount(routerWrapper(reduxWrapper()));
+ registrationPage.find('input#email').simulate('change', { target: { value: 'test1@gmail.com', name: 'email' } });
+ registrationPage.find('input#confirm_email').simulate('blur', { target: { value: 'test2@gmail.com', name: 'confirm_email' } });
+ expect(registrationPage.find('div#confirm_email-error').text()).toEqual('The email addresses do not match.');
+ });
+
+ it('should run validations for configurable focused field on form submission', () => {
+ const professionError = 'Enter your profession';
+ store = mockStore({
+ ...initialState,
+ commonComponents: {
+ ...initialState.commonComponents,
+ fieldDescriptions: {
+ profession: {
+ name: 'profession', type: 'text', label: 'Profession', error_message: professionError,
+ },
+ },
+ },
+ });
+
+ const registrationPage = mount(routerWrapper(reduxWrapper()));
+ registrationPage.find('input#profession').simulate('focus', { target: { value: '', name: 'profession' } });
+ registrationPage.find('button.btn-brand').simulate('click');
+
+ expect(registrationPage.find('#profession-error').last().text()).toEqual(professionError);
+ });
});
});
diff --git a/src/register/components/RegistrationFailure.test.jsx b/src/register/components/RegistrationFailure.test.jsx
new file mode 100644
index 0000000000..cd8a512c05
--- /dev/null
+++ b/src/register/components/RegistrationFailure.test.jsx
@@ -0,0 +1,204 @@
+import React from 'react';
+import { Provider } from 'react-redux';
+
+import { mergeConfig } from '@edx/frontend-platform';
+import {
+ configure, getLocale, injectIntl, IntlProvider,
+} from '@edx/frontend-platform/i18n';
+import { mount } from 'enzyme';
+import { BrowserRouter as Router } from 'react-router-dom';
+import configureStore from 'redux-mock-store';
+
+import RegistrationFailureMessage from './RegistrationFailure';
+import {
+ FORBIDDEN_REQUEST, INTERNAL_SERVER_ERROR, TPA_AUTHENTICATION_FAILURE, TPA_SESSION_EXPIRED,
+} from '../data/constants';
+import RegistrationPage from '../RegistrationPage';
+
+jest.mock('@edx/frontend-platform/analytics', () => ({
+ sendPageEvent: jest.fn(),
+ sendTrackEvent: jest.fn(),
+}));
+jest.mock('@edx/frontend-platform/i18n', () => ({
+ ...jest.requireActual('@edx/frontend-platform/i18n'),
+ getLocale: jest.fn(),
+}));
+
+const IntlRegistrationPage = injectIntl(RegistrationPage);
+const IntlRegistrationFailure = injectIntl(RegistrationFailureMessage);
+const mockStore = configureStore();
+
+jest.mock('react-router-dom', () => {
+ const mockNavigation = jest.fn();
+
+ // eslint-disable-next-line react/prop-types
+ const Navigate = ({ to }) => {
+ mockNavigation(to);
+ return
;
+ };
+
+ return {
+ ...jest.requireActual('react-router-dom'),
+ Navigate,
+ };
+});
+
+describe('RegistrationFailure', () => {
+ mergeConfig({
+ PRIVACY_POLICY: 'https://privacy-policy.com',
+ TOS_AND_HONOR_CODE: 'https://tos-and-honot-code.com',
+ USER_RETENTION_COOKIE_NAME: 'authn-returning-user',
+ });
+
+ let props = {};
+ let store = {};
+ const registrationFormData = {
+ configurableFormFields: {
+ marketingEmailsOptIn: true,
+ },
+ formFields: {
+ name: '', email: '', username: '', password: '',
+ },
+ emailSuggestion: {
+ suggestion: '', type: '',
+ },
+ errors: {
+ name: '', email: '', username: '', password: '',
+ },
+ };
+
+ const reduxWrapper = children => (
+
+ {children}
+
+ );
+
+ const routerWrapper = children => (
+
+ {children}
+
+ );
+
+ const thirdPartyAuthContext = {
+ currentProvider: null,
+ finishAuthUrl: null,
+ providers: [],
+ secondaryProviders: [],
+ pipelineUserDetails: null,
+ countryCode: null,
+ };
+
+ const initialState = {
+ register: {
+ registrationResult: { success: false, redirectUrl: '' },
+ registrationError: {},
+ registrationFormData,
+ usernameSuggestions: [],
+ },
+ commonComponents: {
+ thirdPartyAuthApiStatus: null,
+ thirdPartyAuthContext,
+ fieldDescriptions: {},
+ optionalFields: {
+ fields: {},
+ extended_profile: [],
+ },
+ },
+ };
+
+ beforeEach(() => {
+ store = mockStore(initialState);
+ configure({
+ loggingService: { logError: jest.fn() },
+ config: {
+ ENVIRONMENT: 'production',
+ LANGUAGE_PREFERENCE_COOKIE_NAME: 'yum',
+ },
+ messages: { 'es-419': {}, de: {}, 'en-us': {} },
+ });
+ props = {
+ handleInstitutionLogin: jest.fn(),
+ institutionLogin: false,
+ };
+ window.location = { search: '' };
+ });
+
+ afterEach(() => {
+ jest.clearAllMocks();
+ });
+
+ describe('Test Registration Failure', () => {
+ getLocale.mockImplementation(() => ('en-us'));
+
+ it('should match internal server error message', () => {
+ const expectedMessage = 'We couldn\'t create your account.An error has occurred. Try refreshing the page, or check your internet connection.';
+ props = {
+ errorCode: INTERNAL_SERVER_ERROR,
+ failureCount: 0,
+ };
+
+ const registrationPage = mount(reduxWrapper());
+ expect(registrationPage.find('div.alert-heading').length).toEqual(1);
+ expect(registrationPage.find('div.alert').first().text()).toEqual(expectedMessage);
+ });
+
+ it('should match registration api rate limit error message', () => {
+ const expectedMessage = 'We couldn\'t create your account.Too many failed registration attempts. Try again later.';
+ props = {
+ errorCode: FORBIDDEN_REQUEST,
+ failureCount: 0,
+ };
+
+ const registrationPage = mount(reduxWrapper());
+ expect(registrationPage.find('div.alert-heading').length).toEqual(1);
+ expect(registrationPage.find('div.alert').first().text()).toEqual(expectedMessage);
+ });
+
+ it('should match tpa session expired error message', () => {
+ const expectedMessage = 'We couldn\'t create your account.Registration using Google has timed out.';
+ props = {
+ context: {
+ provider: 'Google',
+ },
+ errorCode: TPA_SESSION_EXPIRED,
+ failureCount: 0,
+ };
+
+ const registrationPage = mount(reduxWrapper());
+ expect(registrationPage.find('div.alert-heading').length).toEqual(1);
+ expect(registrationPage.find('div.alert').first().text()).toEqual(expectedMessage);
+ });
+
+ it('should match tpa authentication failed error message', () => {
+ const expectedMessageSubstring = 'We are sorry, you are not authorized to access';
+ props = {
+ context: {
+ provider: 'Google',
+ },
+ errorCode: TPA_AUTHENTICATION_FAILURE,
+ failureCount: 0,
+ };
+
+ const registrationPage = mount(reduxWrapper());
+ expect(registrationPage.find('div.alert-heading').length).toEqual(1);
+ expect(registrationPage.find('div.alert').first().text()).toContain(expectedMessageSubstring);
+ });
+
+ it('should display error message based on the error code returned by API', () => {
+ store = mockStore({
+ ...initialState,
+ register: {
+ ...initialState.register,
+ registrationError: {
+ errorCode: INTERNAL_SERVER_ERROR,
+ },
+ },
+ });
+
+ const registrationPage = mount(routerWrapper(reduxWrapper())).find('RegistrationPage');
+ expect(registrationPage.find('div#validation-errors').first().text()).toContain(
+ 'An error has occurred. Try refreshing the page, or check your internet connection.',
+ );
+ });
+ });
+});
diff --git a/src/register/components/ThirdPartyAuth.test.jsx b/src/register/components/ThirdPartyAuth.test.jsx
new file mode 100644
index 0000000000..fb434c9372
--- /dev/null
+++ b/src/register/components/ThirdPartyAuth.test.jsx
@@ -0,0 +1,411 @@
+import React from 'react';
+import { Provider } from 'react-redux';
+
+import { getConfig, mergeConfig } from '@edx/frontend-platform';
+import {
+ configure, getLocale, injectIntl, IntlProvider,
+} from '@edx/frontend-platform/i18n';
+import { mount } from 'enzyme';
+import { BrowserRouter as Router } from 'react-router-dom';
+import renderer from 'react-test-renderer';
+import configureStore from 'redux-mock-store';
+
+import {
+ COMPLETE_STATE, LOGIN_PAGE, PENDING_STATE, REGISTER_PAGE,
+} from '../../data/constants';
+import RegistrationPage from '../RegistrationPage';
+
+jest.mock('@edx/frontend-platform/analytics', () => ({
+ sendPageEvent: jest.fn(),
+ sendTrackEvent: jest.fn(),
+}));
+jest.mock('@edx/frontend-platform/i18n', () => ({
+ ...jest.requireActual('@edx/frontend-platform/i18n'),
+ getLocale: jest.fn(),
+}));
+
+const IntlRegistrationPage = injectIntl(RegistrationPage);
+const mockStore = configureStore();
+
+jest.mock('react-router-dom', () => {
+ const mockNavigation = jest.fn();
+
+ // eslint-disable-next-line react/prop-types
+ const Navigate = ({ to }) => {
+ mockNavigation(to);
+ return ;
+ };
+
+ return {
+ ...jest.requireActual('react-router-dom'),
+ Navigate,
+ mockNavigate: mockNavigation,
+ };
+});
+
+describe('ThirdPartyAuth', () => {
+ mergeConfig({
+ PRIVACY_POLICY: 'https://privacy-policy.com',
+ TOS_AND_HONOR_CODE: 'https://tos-and-honot-code.com',
+ USER_RETENTION_COOKIE_NAME: 'authn-returning-user',
+ });
+
+ let props = {};
+ let store = {};
+ const registrationFormData = {
+ configurableFormFields: {
+ marketingEmailsOptIn: true,
+ },
+ formFields: {
+ name: '', email: '', username: '', password: '',
+ },
+ emailSuggestion: {
+ suggestion: '', type: '',
+ },
+ errors: {
+ name: '', email: '', username: '', password: '',
+ },
+ };
+
+ const reduxWrapper = children => (
+
+ {children}
+
+ );
+
+ const routerWrapper = children => (
+
+ {children}
+
+ );
+
+ const thirdPartyAuthContext = {
+ currentProvider: null,
+ finishAuthUrl: null,
+ providers: [],
+ secondaryProviders: [],
+ pipelineUserDetails: null,
+ countryCode: null,
+ };
+
+ const initialState = {
+ register: {
+ registrationResult: { success: false, redirectUrl: '' },
+ registrationError: {},
+ registrationFormData,
+ usernameSuggestions: [],
+ },
+ commonComponents: {
+ thirdPartyAuthApiStatus: null,
+ thirdPartyAuthContext,
+ fieldDescriptions: {},
+ optionalFields: {
+ fields: {},
+ extended_profile: [],
+ },
+ },
+ };
+
+ beforeEach(() => {
+ store = mockStore(initialState);
+ configure({
+ loggingService: { logError: jest.fn() },
+ config: {
+ ENVIRONMENT: 'production',
+ LANGUAGE_PREFERENCE_COOKIE_NAME: 'yum',
+ },
+ messages: { 'es-419': {}, de: {}, 'en-us': {} },
+ });
+ props = {
+ handleInstitutionLogin: jest.fn(),
+ institutionLogin: false,
+ };
+ window.location = { search: '' };
+ });
+
+ afterEach(() => {
+ jest.clearAllMocks();
+ });
+
+ const ssoProvider = {
+ id: 'oa2-apple-id',
+ name: 'Apple',
+ iconClass: 'apple',
+ iconImage: 'https://openedx.devstack.lms/logo.png',
+ loginUrl: '/auth/login/apple-id/?auth_entry=login&next=/dashboard',
+ };
+
+ describe('Test Third Party Auth', () => {
+ mergeConfig({
+ SHOW_CONFIGURABLE_EDX_FIELDS: true,
+ });
+ getLocale.mockImplementation(() => ('en-us'));
+
+ const secondaryProviders = {
+ id: 'saml-test', name: 'Test University', loginUrl: '/dummy-auth', registerUrl: '/dummy_auth',
+ };
+
+ it('should not display password field when current provider is present', () => {
+ store = mockStore({
+ ...initialState,
+ commonComponents: {
+ ...initialState.commonComponents,
+ thirdPartyAuthContext: {
+ ...initialState.commonComponents.thirdPartyAuthContext,
+ currentProvider: ssoProvider.name,
+ },
+ },
+ });
+
+ const registrationPage = mount(routerWrapper(reduxWrapper()));
+ expect(registrationPage.find('input#password').length).toEqual(0);
+ });
+
+ it('should render tpa button for tpa_hint id matching one of the primary providers', () => {
+ store = mockStore({
+ ...initialState,
+ commonComponents: {
+ ...initialState.commonComponents,
+ thirdPartyAuthContext: {
+ ...initialState.commonComponents.thirdPartyAuthContext,
+ providers: [ssoProvider],
+ },
+ thirdPartyAuthApiStatus: COMPLETE_STATE,
+ },
+ });
+
+ delete window.location;
+ window.location = { href: getConfig().BASE_URL.concat(LOGIN_PAGE), search: `?next=/dashboard&tpa_hint=${ssoProvider.id}` };
+
+ const registrationPage = mount(routerWrapper(reduxWrapper()));
+ expect(registrationPage.find(`button#${ssoProvider.id}`).find('span').text()).toEqual(ssoProvider.name);
+ expect(registrationPage.find(`button#${ssoProvider.id}`).hasClass(`btn-tpa btn-${ssoProvider.id}`)).toEqual(true);
+ });
+
+ it('should display skeleton if tpa_hint is true and thirdPartyAuthContext is pending', () => {
+ store = mockStore({
+ ...initialState,
+ commonComponents: {
+ ...initialState.commonComponents,
+ thirdPartyAuthApiStatus: PENDING_STATE,
+ },
+ });
+
+ delete window.location;
+ window.location = {
+ href: getConfig().BASE_URL.concat(LOGIN_PAGE),
+ search: `?next=/dashboard&tpa_hint=${ssoProvider.id}`,
+ };
+
+ const registrationPage = mount(routerWrapper(reduxWrapper()));
+ expect(registrationPage.find('.react-loading-skeleton').exists()).toBeTruthy();
+ });
+
+ it('should render icon if icon classes are missing in providers', () => {
+ ssoProvider.iconClass = null;
+ store = mockStore({
+ ...initialState,
+ commonComponents: {
+ ...initialState.commonComponents,
+ thirdPartyAuthContext: {
+ ...initialState.commonComponents.thirdPartyAuthContext,
+ providers: [ssoProvider],
+ },
+ thirdPartyAuthApiStatus: COMPLETE_STATE,
+ },
+ });
+
+ delete window.location;
+ window.location = { href: getConfig().BASE_URL.concat(REGISTER_PAGE), search: `?next=/dashboard&tpa_hint=${ssoProvider.id}` };
+ ssoProvider.iconImage = null;
+
+ const registrationPage = mount(routerWrapper(reduxWrapper()));
+ expect(registrationPage.find(`button#${ssoProvider.id}`).find('div').find('span').hasClass('pgn__icon')).toEqual(true);
+ });
+
+ it('should render tpa button for tpa_hint id matching one of the secondary providers', () => {
+ secondaryProviders.skipHintedLogin = true;
+ store = mockStore({
+ ...initialState,
+ commonComponents: {
+ ...initialState.commonComponents,
+ thirdPartyAuthContext: {
+ ...initialState.commonComponents.thirdPartyAuthContext,
+ secondaryProviders: [secondaryProviders],
+ },
+ thirdPartyAuthApiStatus: COMPLETE_STATE,
+ },
+ });
+
+ delete window.location;
+ window.location = { href: getConfig().BASE_URL.concat(REGISTER_PAGE), search: `?next=/dashboard&tpa_hint=${secondaryProviders.id}` };
+
+ mount(routerWrapper(reduxWrapper()));
+ expect(window.location.href).toEqual(getConfig().LMS_BASE_URL + secondaryProviders.registerUrl);
+ });
+
+ it('should render regular tpa button for invalid tpa_hint value', () => {
+ const expectedMessage = `${ssoProvider.name}`;
+ store = mockStore({
+ ...initialState,
+ commonComponents: {
+ ...initialState.commonComponents,
+ thirdPartyAuthContext: {
+ ...initialState.commonComponents.thirdPartyAuthContext,
+ providers: [ssoProvider],
+ },
+ thirdPartyAuthApiStatus: COMPLETE_STATE,
+ },
+ });
+
+ delete window.location;
+ window.location = { href: getConfig().BASE_URL.concat(LOGIN_PAGE), search: '?next=/dashboard&tpa_hint=invalid' };
+
+ const registrationPage = mount(routerWrapper(reduxWrapper()));
+ expect(registrationPage.find(`button#${ssoProvider.id}`).find('span#provider-name').text()).toEqual(expectedMessage);
+ });
+
+ it('should show single sign on provider button', () => {
+ store = mockStore({
+ ...initialState,
+ commonComponents: {
+ ...initialState.commonComponents,
+ thirdPartyAuthContext: {
+ ...initialState.commonComponents.thirdPartyAuthContext,
+ providers: [ssoProvider],
+ },
+ },
+ });
+
+ const registrationPage = mount(routerWrapper(reduxWrapper()));
+ expect(registrationPage.find(`button#${ssoProvider.id}`).length).toEqual(1);
+ });
+
+ it('should show single sign on provider button', () => {
+ store = mockStore({
+ ...initialState,
+ commonComponents: {
+ ...initialState.commonComponents,
+ thirdPartyAuthContext: {
+ ...initialState.commonComponents.thirdPartyAuthContext,
+ providers: [ssoProvider],
+ },
+ },
+ });
+
+ const registrationPage = mount(routerWrapper(reduxWrapper()));
+ expect(registrationPage.find(`button#${ssoProvider.id}`).length).toEqual(1);
+ });
+
+ it('should display InstitutionLogistration if insitutionLogin prop is true', () => {
+ props = {
+ ...props,
+ institutionLogin: true,
+ };
+
+ const registrationPage = mount(routerWrapper(reduxWrapper()));
+ expect(registrationPage.find('.institutions__heading').text()).toEqual('Register with institution/campus credentials');
+ });
+
+ it('should redirect to social auth provider url on SSO button click', () => {
+ const registerUrl = '/auth/login/apple-id/?auth_entry=register&next=/dashboard';
+ store = mockStore({
+ ...initialState,
+ commonComponents: {
+ ...initialState.commonComponents,
+ thirdPartyAuthContext: {
+ ...initialState.commonComponents.thirdPartyAuthContext,
+ providers: [{
+ ...ssoProvider,
+ registerUrl,
+ }],
+ },
+ },
+ });
+
+ delete window.location;
+ window.location = { href: getConfig().BASE_URL };
+
+ const loginPage = mount(routerWrapper(reduxWrapper()));
+
+ loginPage.find('button#oa2-apple-id').simulate('click');
+ expect(window.location.href).toBe(getConfig().LMS_BASE_URL + registerUrl);
+ });
+
+ it('should redirect to finishAuthUrl upon successful registration via SSO', () => {
+ const authCompleteUrl = '/auth/complete/google-oauth2/';
+ store = mockStore({
+ ...initialState,
+ register: {
+ ...initialState.register,
+ registrationResult: {
+ success: true,
+ },
+ },
+ commonComponents: {
+ ...initialState.commonComponents,
+ thirdPartyAuthContext: {
+ ...initialState.commonComponents.thirdPartyAuthContext,
+ finishAuthUrl: authCompleteUrl,
+ },
+ },
+ });
+
+ delete window.location;
+ window.location = { href: getConfig().BASE_URL };
+
+ renderer.create(routerWrapper(reduxWrapper()));
+ expect(window.location.href).toBe(getConfig().LMS_BASE_URL + authCompleteUrl);
+ });
+
+ // ******** test alert messages ********
+
+ it('should match third party auth alert', () => {
+ store = mockStore({
+ ...initialState,
+ commonComponents: {
+ ...initialState.commonComponents,
+ thirdPartyAuthContext: {
+ ...initialState.commonComponents.thirdPartyAuthContext,
+ currentProvider: 'Apple',
+ },
+ },
+ });
+
+ const expectedMessage = `${'You\'ve successfully signed into Apple! We just need a little more information before '
+ + 'you start learning with '}${ getConfig().SITE_NAME }.`;
+
+ const registrationPage = mount(routerWrapper(reduxWrapper()));
+ expect(registrationPage.find('#tpa-alert').find('p').text()).toEqual(expectedMessage);
+ });
+ it('should display errorMessage if third party authentication fails', () => {
+ jest.spyOn(global.Date, 'now').mockImplementation(() => 0);
+ getLocale.mockImplementation(() => ('en-us'));
+
+ store = mockStore({
+ ...initialState,
+ register: {
+ ...initialState.register,
+ backendCountryCode: 'PK',
+ userPipelineDataLoaded: false,
+ },
+ commonComponents: {
+ ...initialState.commonComponents,
+ thirdPartyAuthApiStatus: COMPLETE_STATE,
+ thirdPartyAuthContext: {
+ ...initialState.commonComponents.thirdPartyAuthContext,
+ currentProvider: null,
+ pipelineUserDetails: {},
+ errorMessage: 'An error occurred',
+ },
+ },
+ });
+
+ store.dispatch = jest.fn(store.dispatch);
+
+ const registrationPage = mount(routerWrapper(reduxWrapper()));
+ expect(registrationPage.find('div.alert-heading').length).toEqual(1);
+ expect(registrationPage.find('div.alert').first().text()).toContain('An error occurred');
+ });
+ });
+});