From ed0da9607637fdf4517d2f19a502b0d3f88bbf75 Mon Sep 17 00:00:00 2001 From: Zainab Amir Date: Tue, 6 Dec 2022 14:26:05 +0500 Subject: [PATCH] feat: add tests and fix bugs --- .env.development | 1 + .../RedirectLogistration.jsx | 3 +- src/common-components/data/reducers.js | 2 +- .../tests/Logistration.test.jsx | 24 ++ src/field-renderer/FieldRenderer.jsx | 13 +- src/register/ConfigurableRegistrationForm.jsx | 21 ++ src/register/RegistrationPage.jsx | 32 +- src/register/data/reducers.js | 6 +- src/register/data/tests/reducers.test.js | 6 +- .../registrationFields/CountryField.jsx | 6 +- .../registrationFields/EmailField.jsx | 2 +- src/register/tests/RegistrationPage.test.jsx | 278 +++++++++++++----- 12 files changed, 281 insertions(+), 113 deletions(-) diff --git a/.env.development b/.env.development index 7b22f46a..bf47dde9 100644 --- a/.env.development +++ b/.env.development @@ -36,3 +36,4 @@ ZENDESK_KEY='' ZENDESK_LOGO_URL='' APP_ID='' MFE_CONFIG_API_URL='' +SHOW_CONFIGURABLE_EDX_FIELDS='true' diff --git a/src/common-components/RedirectLogistration.jsx b/src/common-components/RedirectLogistration.jsx index 419c3b0c..ece5f778 100644 --- a/src/common-components/RedirectLogistration.jsx +++ b/src/common-components/RedirectLogistration.jsx @@ -24,10 +24,9 @@ function RedirectLogistration(props) { finalRedirectUrl = redirectUrl; } + // Redirect to Progressive Profiling after successful registration if (redirectToWelcomePage) { setCookie('van-504-returning-user', true); - // use this component to redirect WelcomePage after successful registration - // return ; const registrationResult = { redirectUrl: finalRedirectUrl, success }; return ( { case THIRD_PARTY_AUTH_CONTEXT.SUCCESS: return { ...state, - extendedProfile: action.payload.fieldDescriptions.extendedProfile, + extendedProfile: action.payload.fieldDescriptions.extended_profile, fieldDescriptions: action.payload.fieldDescriptions.fields, optionalFields: action.payload.optionalFields, thirdPartyAuthContext: action.payload.thirdPartyAuthContext, diff --git a/src/common-components/tests/Logistration.test.jsx b/src/common-components/tests/Logistration.test.jsx index 786d7f4d..fb2a6bff 100644 --- a/src/common-components/tests/Logistration.test.jsx +++ b/src/common-components/tests/Logistration.test.jsx @@ -10,6 +10,7 @@ import { MemoryRouter } from 'react-router-dom'; import configureStore from 'redux-mock-store'; import { COMPLETE_STATE, LOGIN_PAGE } from '../../data/constants'; +import { backupRegistrationForm } from '../../register/data/actions'; import { RenderInstitutionButton } from '../InstitutionLogistration'; import Logistration from '../Logistration'; @@ -184,4 +185,27 @@ describe('Logistration', () => { DISABLE_ENTERPRISE_LOGIN: '', }); }); + + it('should fire action to backup registration form on tab click', () => { + store = mockStore({ + login: { + loginResult: { success: false, redirectUrl: '' }, + }, + register: { + registrationResult: { success: false, redirectUrl: '' }, + registrationError: {}, + }, + commonComponents: { + thirdPartyAuthContext: { + providers: [], + secondaryProviders: [], + }, + }, + }); + + store.dispatch = jest.fn(store.dispatch); + const logistration = mount(reduxWrapper()); + logistration.find('a[data-rb-event-key="/login"]').simulate('click'); + expect(store.dispatch).toHaveBeenCalledWith(backupRegistrationForm()); + }); }); diff --git a/src/field-renderer/FieldRenderer.jsx b/src/field-renderer/FieldRenderer.jsx index 34d3b18f..561b35d0 100644 --- a/src/field-renderer/FieldRenderer.jsx +++ b/src/field-renderer/FieldRenderer.jsx @@ -7,7 +7,7 @@ import PropTypes from 'prop-types'; const FormFieldRenderer = (props) => { let formField = null; const { - errorMessage, fieldData, onChangeHandler, isRequired, value, + className, errorMessage, fieldData, onChangeHandler, isRequired, value, } = props; const handleFocus = (e) => { @@ -26,6 +26,7 @@ const FormFieldRenderer = (props) => { formField = ( { formField = ( { formField = ( { formField = ( { return formField; }; FormFieldRenderer.defaultProps = { + className: '', value: '', handleBlur: null, handleFocus: null, @@ -132,6 +137,7 @@ FormFieldRenderer.defaultProps = { }; FormFieldRenderer.propTypes = { + className: PropTypes.string, fieldData: PropTypes.shape({ type: PropTypes.string, label: PropTypes.string, @@ -142,7 +148,10 @@ FormFieldRenderer.propTypes = { handleFocus: PropTypes.func, errorMessage: PropTypes.string, isRequired: PropTypes.bool, - value: PropTypes.string, + value: PropTypes.oneOfType([ + PropTypes.string, + PropTypes.bool, + ]), }; export default FormFieldRenderer; diff --git a/src/register/ConfigurableRegistrationForm.jsx b/src/register/ConfigurableRegistrationForm.jsx index fa7c7427..788d817e 100644 --- a/src/register/ConfigurableRegistrationForm.jsx +++ b/src/register/ConfigurableRegistrationForm.jsx @@ -44,6 +44,7 @@ const ConfigurableRegistrationForm = (props) => { const flags = { showConfigurableRegistrationFields: getConfig().ENABLE_DYNAMIC_REGISTRATION_FIELDS, showConfigurableEdxFields: getConfig().SHOW_CONFIGURABLE_EDX_FIELDS, + showMarketingEmailOptInCheckbox: getConfig().MARKETING_EMAILS_OPT_IN, }; useEffect(() => { @@ -160,6 +161,25 @@ const ConfigurableRegistrationForm = (props) => { ); } + if (flags.showMarketingEmailOptInCheckbox) { + formFieldDescriptions.push( + + + , + ); + } + if (flags.showConfigurableEdxFields || showTermsOfServiceAndHonorCode) { formFieldDescriptions.push( @@ -191,6 +211,7 @@ ConfigurableRegistrationForm.propTypes = { countryCode: PropTypes.string, }), honor_code: PropTypes.bool, + marketingEmailsOptIn: PropTypes.bool, }).isRequired, intl: intlShape.isRequired, setFieldErrors: PropTypes.func.isRequired, diff --git a/src/register/RegistrationPage.jsx b/src/register/RegistrationPage.jsx index c6ec4ea4..efa8f64e 100644 --- a/src/register/RegistrationPage.jsx +++ b/src/register/RegistrationPage.jsx @@ -80,6 +80,7 @@ const RegistrationPage = (props) => { const flags = { showConfigurableEdxFields: getConfig().SHOW_CONFIGURABLE_EDX_FIELDS, showConfigurableRegistrationFields: getConfig().ENABLE_DYNAMIC_REGISTRATION_FIELDS, + showMarketingEmailOptInCheckbox: getConfig().MARKETING_EMAILS_OPT_IN, }; const [formFields, setFormFields] = useState({ ...backedUpFormData.formFields }); @@ -240,7 +241,7 @@ const RegistrationPage = (props) => { case 'username': if (!value || value.length <= 1 || value.length > 30) { fieldError = intl.formatMessage(messages['username.validation.message']); - } else if (!value.match(/^[a-zA-Z\d_-]*$/i)) { + } else if (!value.match(/^[a-zA-Z0-9_-]*$/i)) { fieldError = intl.formatMessage(messages['username.format.validation.message']); } else if (shouldValidateFromBackend) { validateFromBackend(payload); @@ -402,7 +403,7 @@ const RegistrationPage = (props) => { payload.social_auth_provider = currentProvider; } - const { focusedFieldError, countryFieldCode } = focusedField ? ( + const { fieldError: focusedFieldError, countryFieldCode } = focusedField ? ( validateInput( focusedField, (focusedField in fieldDescriptions || focusedField === 'country') ? ( @@ -419,17 +420,19 @@ const RegistrationPage = (props) => { return; } - payload.extendedProfile = []; Object.keys(configurableFormFields).forEach((fieldName) => { - if (props.extendedProfile.includes(fieldName)) { - payload.extendedProfile.push({ fieldName, fieldValue: configurableFormFields[fieldName] }); - } else if (fieldName === 'country') { + if (fieldName === 'country') { payload[fieldName] = focusedField === 'country' ? countryFieldCode : configurableFormFields[fieldName].countryCode; } else { payload[fieldName] = configurableFormFields[fieldName]; } }); + // Don't send the marketing email opt-in value if the flag is turned off + if (!flags.showMarketingEmailOptInCheckbox) { + delete payload.marketingEmailsOptIn; + } + payload = snakeCaseObject(payload); payload.totalRegistrationTime = totalRegistrationTime; @@ -521,17 +524,6 @@ const RegistrationPage = (props) => { floatingLabel={intl.formatMessage(messages['registration.password.label'])} /> )} - {getConfig().MARKETING_EMAILS_OPT_IN - && ( - - {intl.formatMessage(messages['registration.opt.in.label'], { siteName: getConfig().SITE_NAME })} - - )} { registrationError: {}, registrationResult: {}, registrationFormData: { - configurableFormFields: {}, + configurableFormFields: { + marketingEmailsOptIn: true, + }, formFields: { - name: '', email: '', username: '', password: '', marketingEmailsOptIn: true, + name: '', email: '', username: '', password: '', }, emailSuggestion: { suggestion: '', type: '', diff --git a/src/register/registrationFields/CountryField.jsx b/src/register/registrationFields/CountryField.jsx index ce8a7a29..e310d2f7 100644 --- a/src/register/registrationFields/CountryField.jsx +++ b/src/register/registrationFields/CountryField.jsx @@ -20,7 +20,7 @@ const CountryField = (props) => { const [displayValue, setDisplayValue] = useState(''); const [trailingIcon, setTrailingIcon] = useState(null); - const onBlurHandler = (event, itemClicked = false) => { + const onBlurHandler = (event, itemClicked = false, countryName = '') => { const { name } = event.target; const relatedName = event.relatedTarget ? event.relatedTarget.name : ''; // For a better user experience, do not validate when focus out from 'country' field @@ -28,7 +28,7 @@ const CountryField = (props) => { if ((relatedName === 'countryItem' || relatedName === 'countryExpand') && name === 'country') { return; } - const countryValue = itemClicked ? event.target.value : displayValue; + const countryValue = itemClicked ? countryName : displayValue; if (props.onBlurHandler) { props.onBlurHandler({ target: { name: 'country', value: countryValue } }); } @@ -53,7 +53,7 @@ const CountryField = (props) => { className="dropdown-item data-hj-suppress" value={countryName} key={country[COUNTRY_CODE_KEY]} - onClick={(event) => onBlurHandler(event, true)} + onClick={(event) => onBlurHandler(event, true, countryName)} > {countryName.length > 30 ? countryName.substring(0, 30).concat('...') : countryName} diff --git a/src/register/registrationFields/EmailField.jsx b/src/register/registrationFields/EmailField.jsx index 243d5234..6ca744eb 100644 --- a/src/register/registrationFields/EmailField.jsx +++ b/src/register/registrationFields/EmailField.jsx @@ -34,7 +34,7 @@ const EmailField = (props) => { ); } return ( - + {intl.formatMessage(messages['did.you.mean.alert.text'])}:{' '} { mergeConfig({ @@ -37,15 +41,16 @@ describe('RegistrationPage', () => { TOS_AND_HONOR_CODE: 'https://tos-and-honot-code.com', USER_SURVEY_COOKIE_NAME: process.env.USER_SURVEY_COOKIE_NAME, REGISTER_CONVERSION_COOKIE_NAME: process.env.REGISTER_CONVERSION_COOKIE_NAME, - SHOW_CONFIGURABLE_EDX_FIELDS: true, }); let props = {}; let store = {}; const registrationFormData = { - configurableFormFields: {}, + configurableFormFields: { + marketingEmailsOptIn: true, + }, formFields: { - name: '', email: '', username: '', password: '', marketingEmailsOptIn: true, + name: '', email: '', username: '', password: '', }, emailSuggestion: { suggestion: '', type: '', @@ -74,6 +79,7 @@ describe('RegistrationPage', () => { register: { registrationResult: { success: false, redirectUrl: '' }, registrationError: {}, + registrationFormData, }, commonComponents: { thirdPartyAuthApiStatus: null, @@ -103,20 +109,24 @@ describe('RegistrationPage', () => { jest.clearAllMocks(); }); - const populateRequiredFields = (registerPage, payload, isThirdPartyAuth = false) => { - registerPage.find('input#name').simulate('change', { target: { value: payload.name, name: 'name' } }); - registerPage.find('input#username').simulate('change', { target: { value: payload.username, name: 'username' } }); - registerPage.find('input#email').simulate('change', { target: { value: payload.email, name: 'email' } }); + 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' } }); - registerPage.find('input#country').simulate('change', { target: { value: payload.country, name: 'country' } }); - registerPage.find('input#country').simulate('blur', { target: { value: payload.country, name: 'country' } }); + registrationPage.find('input#country').simulate('change', { target: { value: payload.country, name: 'country' } }); + registrationPage.find('input#country').simulate('blur', { target: { value: payload.country, name: 'country' } }); if (!isThirdPartyAuth) { - registerPage.find('input#password').simulate('change', { target: { value: payload.password, name: 'password' } }); + registrationPage.find('input#password').simulate('change', { target: { value: payload.password, name: 'password' } }); } }; describe('Test Registration Page', () => { + mergeConfig({ + SHOW_CONFIGURABLE_EDX_FIELDS: true, + }); + const emptyFieldValidation = { name: 'Enter your full name', username: 'Username must be between 2 and 30 characters', @@ -153,15 +163,13 @@ describe('RegistrationPage', () => { country: 'Pakistan', honor_code: true, totalRegistrationTime: 0, - marketing_emails_opt_in: true, next: '/course/demo-course-url', - extended_profile: [], }; store.dispatch = jest.fn(store.dispatch); - const registerPage = mount(reduxWrapper()); - populateRequiredFields(registerPage, payload); - registerPage.find('button.btn-brand').simulate('click'); + const registrationPage = mount(reduxWrapper()); + populateRequiredFields(registrationPage, payload); + registrationPage.find('button.btn-brand').simulate('click'); expect(store.dispatch).toHaveBeenCalledWith(registerNewUser({ ...payload, country: 'PK' })); }); @@ -176,8 +184,6 @@ describe('RegistrationPage', () => { honor_code: true, social_auth_provider: ssoProvider.name, totalRegistrationTime: 0, - marketing_emails_opt_in: true, - extended_profile: [], }; store = mockStore({ @@ -191,13 +197,42 @@ describe('RegistrationPage', () => { }, }); store.dispatch = jest.fn(store.dispatch); - const registerPage = mount(reduxWrapper()); + const registrationPage = mount(reduxWrapper()); - populateRequiredFields(registerPage, formPayload, true); - registerPage.find('button.btn-brand').simulate('click'); + populateRequiredFields(registrationPage, formPayload, true); + registrationPage.find('button.btn-brand').simulate('click'); expect(store.dispatch).toHaveBeenCalledWith(registerNewUser({ ...formPayload, country: 'PK' })); }); + it('should submit form with marketing email opt in value', () => { + mergeConfig({ + MARKETING_EMAILS_OPT_IN: 'true', + }); + + jest.spyOn(global.Date, 'now').mockImplementation(() => 0); + + const payload = { + name: 'John Doe', + username: 'john_doe', + email: 'john.doe@gmail.com', + password: 'password1', + country: 'Pakistan', + honor_code: true, + totalRegistrationTime: 0, + marketing_emails_opt_in: true, + }; + + store.dispatch = jest.fn(store.dispatch); + const registrationPage = mount(reduxWrapper()); + populateRequiredFields(registrationPage, payload); + registrationPage.find('button.btn-brand').simulate('click'); + expect(store.dispatch).toHaveBeenCalledWith(registerNewUser({ ...payload, country: 'PK' })); + + mergeConfig({ + MARKETING_EMAILS_OPT_IN: '', + }); + }); + it('should not dispatch registerNewUser on empty form Submission', () => { store.dispatch = jest.fn(store.dispatch); @@ -235,7 +270,15 @@ describe('RegistrationPage', () => { registrationPage.find('div[feedback-for="password"]').text(), ).toContain('Password criteria has not been met'); - registrationPage.find('input#password').simulate('blur', { target: { value: 'invalid-email', name: 'email' } }); + registrationPage.find('input#username').simulate('blur', { target: { value: 'u$ername', name: 'username' } }); + expect( + registrationPage.find('div[feedback-for="username"]').text(), + ).toContain( + 'Usernames can only contain letters (A-Z, a-z), numerals (0-9),' + + ' underscores (_), and hyphens (-). Usernames cannot contain spaces', + ); + + registrationPage.find('input#email').simulate('blur', { target: { value: 'ab', name: 'email' } }); expect( registrationPage.find('div[feedback-for="email"]').text(), ).toEqual('Enter a valid email address'); @@ -264,18 +307,49 @@ describe('RegistrationPage', () => { store.dispatch = jest.fn(store.dispatch); const registrationPage = mount(reduxWrapper()); - // Enter a valid username so that frontend validations are passed - registrationPage.find('input#username').simulate('change', { target: { value: 'test', name: 'username' } }); - registrationPage.find('input#username').simulate('blur'); + // Enter a valid name so that frontend validations are passed + registrationPage.find('input#name').simulate('change', { target: { value: 'John Doe', name: 'name' } }); + registrationPage.find('input#name').simulate('blur'); + expect(store.dispatch).toHaveBeenCalledWith(fetchRealtimeValidations({ + form_field_key: 'name', email: '', name: 'John Doe', username: '', password: '', + })); - const formPayload = { - form_field_key: 'username', - email: '', - name: '', - username: 'test', - password: '', - }; - expect(store.dispatch).toHaveBeenCalledWith(fetchRealtimeValidations(formPayload)); + // Enter a valid username so that frontend validations are passed + registrationPage.find('input#username').simulate('change', { target: { value: 'john', name: 'username' } }); + registrationPage.find('input#username').simulate('blur'); + expect(store.dispatch).toHaveBeenCalledWith(fetchRealtimeValidations({ + form_field_key: 'username', email: '', name: 'John Doe', username: 'john', password: '', + })); + }); + + it('should run validations for focused field on form submission', () => { + const registrationPage = mount(reduxWrapper()); + registrationPage.find('input#country').simulate('focus'); + registrationPage.find('button.btn-brand').simulate('click'); + + expect(registrationPage.find('div[feedback-for="country"]').text()).toEqual(emptyFieldValidation.country); + }); + + it('should give email suggestions for common service provider domain typos', () => { + store.dispatch = jest.fn(store.dispatch); + const registrationPage = mount(reduxWrapper()); + + registrationPage.find('input#email').simulate('change', { target: { value: 'john@yopmail.com', name: 'email' } }); + registrationPage.find('input#email').simulate('blur'); + + expect(registrationPage.find('#email-warning').text()).toEqual('Did you mean: john@hotmail.com?'); + }); + + it('should give error for common top level domain mistakes', () => { + store.dispatch = jest.fn(store.dispatch); + const registrationPage = mount(reduxWrapper()); + + registrationPage.find('input#email').simulate( + 'change', { target: { value: 'john@gmail.mistake', name: 'email' } }, + ); + registrationPage.find('input#email').simulate('blur'); + + expect(registrationPage.find('.alert-danger').text()).toEqual('Did you mean john@gmail.com?'); }); it('should update props with validations returned by registration api', () => { @@ -356,8 +430,8 @@ describe('RegistrationPage', () => { const expectedMessage = `${'You\'ve successfully signed into Apple! We just need a little more information before ' + 'you start learning with '}${ getConfig().SITE_NAME }.`; - const registerPage = mount(reduxWrapper()); - expect(registerPage.find('#tpa-alert').find('p').text()).toEqual(expectedMessage); + const registrationPage = mount(reduxWrapper()); + expect(registrationPage.find('#tpa-alert').find('p').text()).toEqual(expectedMessage); }); it('should match internal server error message', () => { @@ -426,8 +500,8 @@ describe('RegistrationPage', () => { MARKETING_EMAILS_OPT_IN: 'true', }); - const registerPage = mount(reduxWrapper()); - expect(registerPage.find('div.opt-checkbox').length).toEqual(1); + const registrationPage = mount(reduxWrapper()); + expect(registrationPage.find('div.opt-checkbox').length).toEqual(1); mergeConfig({ MARKETING_EMAILS_OPT_IN: '', @@ -524,8 +598,8 @@ describe('RegistrationPage', () => { }, }); - const registerPage = mount(reduxWrapper()); - expect(registerPage.find('button.username-suggestion').length).toEqual(3); + const registrationPage = mount(reduxWrapper()); + expect(registrationPage.find('button.username-suggestion').length).toEqual(3); }); it('should show username suggestions when full name is populated', () => { @@ -541,10 +615,10 @@ describe('RegistrationPage', () => { }, }); - const registerPage = mount(reduxWrapper()); - registerPage.find('input#name').simulate('change', { target: { value: 'test name', name: 'name' } }); + const registrationPage = mount(reduxWrapper()); + registrationPage.find('input#name').simulate('change', { target: { value: 'test name', name: 'name' } }); - expect(registerPage.find('button.username-suggestion').length).toEqual(3); + expect(registrationPage.find('button.username-suggestion').length).toEqual(3); }); it('should clear username suggestions when close icon is clicked', () => { @@ -561,9 +635,9 @@ describe('RegistrationPage', () => { }); store.dispatch = jest.fn(store.dispatch); - const registerPage = mount(reduxWrapper()); - registerPage.find('input#name').simulate('change', { target: { value: 'test name', name: 'name' } }); - registerPage.find('button.suggested-username-close-button').at(0).simulate('click'); + const registrationPage = mount(reduxWrapper()); + registrationPage.find('input#name').simulate('change', { target: { value: 'test name', name: 'name' } }); + registrationPage.find('button.suggested-username-close-button').at(0).simulate('click'); expect(store.dispatch).toHaveBeenCalledWith(clearUsernameSuggestions()); }); @@ -657,6 +731,35 @@ describe('RegistrationPage', () => { expect(window.location.href).toBe(dashboardUrl); }); + it('should redirect to progressive profiling page if optional fields are configured', () => { + mergeConfig({ + ENABLE_PROGRESSIVE_PROFILING: true, + }); + + store = mockStore({ + ...initialState, + register: { + ...initialState.register, + registrationResult: { + success: true, + }, + }, + commonComponents: { + optionalFields: { + country: { name: 'country', error_message: false }, + }, + }, + }); + + const progressiveProfilingPage = mount(reduxWrapper( + + + , + )); + progressiveProfilingPage.update(); + expect(history.location.pathname).toEqual(WELCOME_PAGE); + }); + // ******** test hinted third party auth ******** it('should render tpa button for tpa_hint id matching one of the primary providers', () => { @@ -676,9 +779,9 @@ describe('RegistrationPage', () => { window.location = { href: getConfig().BASE_URL.concat('/login'), search: `?next=/dashboard&tpa_hint=${ssoProvider.id}` }; ssoProvider.iconImage = null; - const registerPage = mount(reduxWrapper()); - expect(registerPage.find(`button#${ssoProvider.id}`).find('span').text()).toEqual(ssoProvider.name); - expect(registerPage.find(`button#${ssoProvider.id}`).hasClass(`btn-tpa btn-${ssoProvider.id}`)).toEqual(true); + const registrationPage = mount(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 render tpa button for tpa_hint id matching one of the secondary providers', () => { @@ -721,8 +824,8 @@ describe('RegistrationPage', () => { window.location = { href: getConfig().BASE_URL.concat('/login'), search: '?next=/dashboard&tpa_hint=invalid' }; ssoProvider.iconImage = null; - const registerPage = mount(reduxWrapper()); - expect(registerPage.find(`button#${ssoProvider.id}`).find('span#provider-name').text()).toEqual(expectedMessage); + const registrationPage = mount(reduxWrapper()); + expect(registrationPage.find(`button#${ssoProvider.id}`).find('span#provider-name').text()).toEqual(expectedMessage); }); // ******** miscellaneous tests ******** @@ -742,8 +845,8 @@ describe('RegistrationPage', () => { }); it('should render cookie banner', () => { - const registerPage = mount(reduxWrapper()); - expect(registerPage.find()).toBeTruthy(); + const registrationPage = mount(reduxWrapper()); + expect(registrationPage.find()).toBeTruthy(); }); it('should send page event when register page is rendered', () => { @@ -769,10 +872,12 @@ describe('RegistrationPage', () => { }, }, }); + store.dispatch = jest.fn(store.dispatch); const registrationPage = mount(reduxWrapper()).find('RegistrationPage'); expect(registrationPage.find('input#email').props().value).toEqual('test@example.com'); expect(registrationPage.find('input#username').props().value).toEqual('test'); + expect(store.dispatch).toHaveBeenCalledWith(setUserPipelineDataLoaded(true)); }); it('should update state from country code present in redux store', () => { @@ -854,9 +959,9 @@ describe('RegistrationPage', () => { }, }, }); - const registerPage = mount(reduxWrapper()); - expect(registerPage.find('#profession').exists()).toBeTruthy(); - expect(registerPage.find('#tos').exists()).toBeTruthy(); + const registrationPage = mount(reduxWrapper()); + expect(registrationPage.find('#profession').exists()).toBeTruthy(); + expect(registrationPage.find('#tos').exists()).toBeTruthy(); }); it('should submit form with fields returned by backend in payload', () => { @@ -879,48 +984,36 @@ describe('RegistrationPage', () => { password: 'password1', country: 'Pakistan', honor_code: true, + profession: 'Engineer', totalRegistrationTime: 0, - marketing_emails_opt_in: true, - extended_profile: [{ field_name: 'profession', field_value: 'Engineer' }], }; store.dispatch = jest.fn(store.dispatch); - const registerPage = mount(reduxWrapper()); + const registrationPage = mount(reduxWrapper()); - populateRequiredFields(registerPage, payload); - registerPage.find('input#profession').simulate('change', { target: { value: 'Engineer', name: 'profession' } }); - registerPage.find('button.btn-brand').simulate('click'); + 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 message for fields returned by backend', () => { + 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: 'Enter profession', + name: 'profession', type: 'text', label: 'Profession', error_message: professionError, }, - }, - }, - }); - - const registrationPage = mount(reduxWrapper()); - registrationPage.find('button.btn-brand').simulate('click'); - - expect(registrationPage.find('#profession-error').last().text()).toEqual('Enter profession'); - }); - - it('should show error message for confirm email field returned by backend', () => { - store = mockStore({ - ...initialState, - commonComponents: { - ...initialState.commonComponents, - fieldDescriptions: { confirm_email: { - name: 'confirm_email', type: 'text', label: 'Confirm Email', error_message: 'Enter your confirm email', + name: 'confirm_email', type: 'text', label: 'Confirm Email', error_message: confirmEmailError, }, + country: { name: 'country' }, }, }, }); @@ -928,7 +1021,9 @@ describe('RegistrationPage', () => { const registrationPage = mount(reduxWrapper()); registrationPage.find('button.btn-brand').simulate('click'); - expect(registrationPage.find('#confirm_email-error').last().text()).toEqual('Enter your confirm email'); + 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', () => { @@ -949,5 +1044,26 @@ describe('RegistrationPage', () => { 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(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); + }); }); });