diff --git a/src/logistration/RegistrationFailure.jsx b/src/logistration/RegistrationFailure.jsx index c346bcd2..6778309b 100644 --- a/src/logistration/RegistrationFailure.jsx +++ b/src/logistration/RegistrationFailure.jsx @@ -14,14 +14,13 @@ const RegistrationFailureMessage = (props) => { Object.keys(errorMessage).forEach((key) => { const errors = errorMessage[key]; - const errorList = errors.map((error) => { - const errorMsg = error.user_message; - return ( + const errorList = errors.map((error) => ( + (error.user_message) ? (
  • - {errorMsg} + {error.user_message}
  • - ); - }); + ) : null + )); userErrors.push(errorList); }); diff --git a/src/logistration/RegistrationPage.jsx b/src/logistration/RegistrationPage.jsx index 6b651d92..3cc472e7 100644 --- a/src/logistration/RegistrationPage.jsx +++ b/src/logistration/RegistrationPage.jsx @@ -61,6 +61,7 @@ class RegistrationPage extends React.Component { confirmEmail: '', enableOptionalField: false, validationFieldName: '', + emptyFields: {}, errors: { email: '', name: '', @@ -96,8 +97,31 @@ class RegistrationPage extends React.Component { const { errors } = this.state; const errorMsg = nextProps.validations.validation_decisions[this.state.validationFieldName]; errors[this.state.validationFieldName] = errorMsg; - this.setState({ - errors, + const stateValidKey = `${camelCase(this.state.validationFieldName)}Valid`; + const stateValidValue = !errorMsg; + + this.setState(({ [stateValidKey]: stateValidValue }), () => { + const { + emailValid, + nameValid, + usernameValid, + passwordValid, + } = this.state; + + const validityMap = REGISTRATION_VALIDITY_MAP; + let extraFieldsValid = true; + Object.entries(validityMap).forEach(([key, value]) => { + if (value) { + const stateValid = `${camelCase(key)}Valid`; + extraFieldsValid = extraFieldsValid && this.state[stateValid]; + } + }); + + const formValid = emailValid && nameValid && usernameValid && passwordValid && extraFieldsValid; + this.setState({ + errors, + formValid, + }); }); return false; } @@ -112,10 +136,10 @@ class RegistrationPage extends React.Component { e.preventDefault(); const params = (new URL(document.location)).searchParams; const payload = { - email: this.state.email, - username: this.state.username, - password: this.state.password, name: this.state.name, + username: this.state.username, + email: this.state.email, + password: this.state.password, }; const fieldMap = { ...REGISTRATION_VALIDITY_MAP, ...REGISTRATION_OPTIONAL_MAP }; @@ -133,11 +157,10 @@ class RegistrationPage extends React.Component { if (courseId) { payload.course_id = params.course_id; } - if (!this.state.formValid) { // Special case where honor code and tos is a single field, true by default. We don't need // to validate this field - Object.entries(payload).filter(([key]) => (key !== 'honor_code' || !('terms_of_service' in REGISTRATION_EXTRA_FIELDS))) + Object.entries(payload).filter(([key]) => (key !== 'honor_code' || 'terms_of_service' in REGISTRATION_VALIDITY_MAP)) .forEach(([key, value]) => { this.validateInput(key, value); }); @@ -167,7 +190,6 @@ class RegistrationPage extends React.Component { this.setState({ [camelCase(e.target.name)]: targetValue, }); - this.validateInput(e.target.name, targetValue); } handleOnOptional(e) { @@ -180,6 +202,7 @@ class RegistrationPage extends React.Component { validateInput(inputName, value) { const { errors } = this.state; + const { emptyFields } = this.state; let { emailValid, nameValid, @@ -192,24 +215,24 @@ class RegistrationPage extends React.Component { switch (inputName) { case 'email': - emailValid = value.match(/^([\w.%+-]+)@([\w-]+\.)+([\w]{2,})$/i); - errors.email = emailValid ? '' : this.intl.formatMessage(messages['logistration.email.validation.message']); + emailValid = value.length >= 1; + emptyFields.email = this.generateUserMessage(emailValid, 'logistration.email.validation.message'); break; case 'name': nameValid = value.length >= 1; - errors.name = nameValid ? '' : this.intl.formatMessage(messages['logistration.fullname.validation.message']); + emptyFields.name = this.generateUserMessage(nameValid, 'logistration.fullname.validation.message'); break; case 'username': - usernameValid = value.length >= 2 && value.length <= 30; - errors.username = usernameValid ? '' : this.intl.formatMessage(messages['logistration.username.validation.message']); + usernameValid = value.length >= 1; + emptyFields.username = this.generateUserMessage(usernameValid, 'logistration.username.validation.message'); break; case 'password': - passwordValid = !!(value.length >= 8 && value.match(/\d+/g)); - errors.password = passwordValid ? '' : this.intl.formatMessage(messages['logistration.register.page.password.validation.message']); + passwordValid = value.length >= 1; + emptyFields.password = this.generateUserMessage(passwordValid, 'logistration.register.page.password.validation.message'); break; case 'country': countryValid = value !== ''; - errors.country = countryValid ? '' : this.intl.formatMessage(messages['logistration.country.validation.message']); + emptyFields.country = this.generateUserMessage(countryValid, 'logistration.country.validation.message'); break; case 'honor_code': honorCodeValid = value !== false; @@ -224,7 +247,7 @@ class RegistrationPage extends React.Component { } this.setState({ - errors, + emptyFields, emailValid, nameValid, usernameValid, @@ -232,32 +255,7 @@ class RegistrationPage extends React.Component { countryValid, honorCodeValid, termsOfServiceValid, - }, this.validateForm); - } - - validateForm() { - const { - emailValid, - nameValid, - usernameValid, - passwordValid, - } = this.state; - - const validityMap = REGISTRATION_VALIDITY_MAP; - let extraFieldsValid = true; - Object.entries(validityMap).forEach(([key, value]) => { - if (value) { - const stateValid = `${camelCase(key)}Valid`; - extraFieldsValid = extraFieldsValid && this.state[stateValid]; - } }); - - let formValid = emailValid && nameValid && usernameValid && extraFieldsValid; - if (!this.props.thirdPartyAuthContext.currentProvider) { - formValid = formValid && passwordValid; - } - - this.setState({ formValid }); } addExtraRequiredFields() { @@ -330,6 +328,7 @@ class RegistrationPage extends React.Component { label: item.name, })); props.options = options; + props.onBlur = e => this.handleOnBlur(e); } return ( 0) { + errorsObject = this.state.emptyFields; + } else if (this.props.registrationError) { + errorsObject = this.props.registrationError; + } else { + return null; + } + return ; + } + render() { const { intl, submitState } = this.props; const { @@ -426,7 +441,7 @@ class RegistrationPage extends React.Component { finishAuthUrl={finishAuthUrl} />
    - {this.props.registrationError ? : null} + {this.renderErrors()} {currentProvider && ( { jest.clearAllMocks(); }); - it('should show error message on invalid email', () => { - const validationMessage = 'Enter a valid email address that contains at least 3 characters.'; - - const registrationPage = mount(reduxWrapper()); - registrationPage.find('input#email').simulate('change', { target: { value: '', name: 'email' } }); - registrationPage.update(); - expect(registrationPage.find('#email-invalid-feedback').text()).toEqual(validationMessage); - }); - - it('should show error message on invalid username', () => { - const validationMessage = 'Username must be between 2 and 30 characters long.'; - const registrationPage = mount(reduxWrapper()); - registrationPage.find('input#username').simulate('change', { target: { value: '', name: 'username' } }); - registrationPage.update(); - expect(registrationPage.find('#username-invalid-feedback').text()).toEqual(validationMessage); - }); - - it('should show error message on invalid name', () => { - const validationMessage = 'Enter your full name.'; - const registrationPage = mount(reduxWrapper()); - registrationPage.find('input#name').simulate('change', { target: { value: '', name: 'name' } }); - registrationPage.update(); - expect(registrationPage.find('#name-invalid-feedback').text()).toEqual(validationMessage); - }); - - it('should show error message on invalid password', () => { - const validationMessage = 'This password is too short. It must contain at least 8 characters. This password must contain at least 1 number.'; - const registrationPage = mount(reduxWrapper()); - registrationPage.find('input#password').simulate('change', { target: { value: '', name: 'password' } }); - registrationPage.update(); - expect(registrationPage.find('#password-invalid-feedback').text()).toEqual(validationMessage); - }); - it('should show error messages on invalid extra fields', () => { const validationMessage = { honorCode: 'You must agree to the Your Platform Name Here Honor Code', diff --git a/src/setupTest.js b/src/setupTest.js index 156b68d6..dd28bfd5 100755 --- a/src/setupTest.js +++ b/src/setupTest.js @@ -20,3 +20,7 @@ export default function initializeMockLogging() { return { loggingService }; } + +window.scrollTo = (x, y) => { + document.documentElement.scrollTop = y; +};