fix: apply frontend validations (#1041)
Description: Applied frontend validations on all the fields VAN-1614
This commit is contained in:
@@ -32,6 +32,7 @@ export const VALID_EMAIL_REGEX = '(^[-!#$%&\'*+/=?^_`{}|~0-9A-Z]+(\\.[-!#$%&\'*+
|
||||
+ ')@((?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\\.)+)(?:[A-Z0-9-]{2,63})'
|
||||
+ '|\\[(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)(\\.(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)){3}\\]$';
|
||||
export const LETTER_REGEX = /[a-zA-Z]/;
|
||||
export const USERNAME_REGEX = /^[a-zA-Z0-9_-]*$/i;
|
||||
export const NUMBER_REGEX = /\d/;
|
||||
export const INVALID_NAME_REGEX = /[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)?/gi; // eslint-disable-line no-useless-escape
|
||||
|
||||
|
||||
@@ -25,6 +25,9 @@ import {
|
||||
FORM_SUBMISSION_ERROR,
|
||||
} from './data/constants';
|
||||
import { registrationErrorSelector, validationsSelector } from './data/selectors';
|
||||
import {
|
||||
emailRegex, getSuggestionForInvalidEmail, urlRegex, validateCountryField, validateEmailAddress,
|
||||
} from './data/utils';
|
||||
import messages from './messages';
|
||||
import RegistrationFailure from './RegistrationFailure';
|
||||
import { EmailField, UsernameField } from './registrationFields';
|
||||
@@ -36,7 +39,7 @@ import {
|
||||
fieldDescriptionSelector,
|
||||
} from '../common-components/data/selectors';
|
||||
import {
|
||||
DEFAULT_STATE, REDIRECT,
|
||||
DEFAULT_STATE, LETTER_REGEX, NUMBER_REGEX, REDIRECT, USERNAME_REGEX,
|
||||
} from '../data/constants';
|
||||
import {
|
||||
getAllPossibleQueryParams, setCookie,
|
||||
@@ -173,16 +176,76 @@ const EmbeddableRegistrationPage = (props) => {
|
||||
}
|
||||
}, [registrationResult, host]);
|
||||
|
||||
const validateInput = (fieldName, value, payload, shouldValidateFromBackend) => {
|
||||
const validateInput = (fieldName, value, payload, shouldValidateFromBackend, shouldSetErrors = true) => {
|
||||
let fieldError = '';
|
||||
|
||||
switch (fieldName) {
|
||||
case 'name':
|
||||
if (value && !payload.username.trim() && shouldValidateFromBackend) {
|
||||
validateFromBackend(payload);
|
||||
case 'name':
|
||||
if (value && value.match(urlRegex)) {
|
||||
fieldError = formatMessage(messages['name.validation.message']);
|
||||
} else if (value && !payload.username.trim() && shouldValidateFromBackend) {
|
||||
validateFromBackend(payload);
|
||||
}
|
||||
break;
|
||||
case 'email':
|
||||
if (value.length <= 2) {
|
||||
fieldError = formatMessage(messages['email.invalid.format.error']);
|
||||
} else {
|
||||
const [username, domainName] = value.split('@');
|
||||
// Check if email address is invalid. If we have a suggestion for invalid email
|
||||
// provide that along with the error message.
|
||||
if (!emailRegex.test(value)) {
|
||||
fieldError = formatMessage(messages['email.invalid.format.error']);
|
||||
setEmailSuggestion({
|
||||
suggestion: getSuggestionForInvalidEmail(domainName, username),
|
||||
type: 'error',
|
||||
});
|
||||
} else {
|
||||
const response = validateEmailAddress(value, username, domainName);
|
||||
if (response.hasError) {
|
||||
fieldError = formatMessage(messages['email.invalid.format.error']);
|
||||
delete response.hasError;
|
||||
}
|
||||
setEmailSuggestion({ ...response });
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 'username':
|
||||
if (!value.match(USERNAME_REGEX)) {
|
||||
fieldError = formatMessage(messages['username.format.validation.message']);
|
||||
}
|
||||
break;
|
||||
case 'password':
|
||||
if (!value || !LETTER_REGEX.test(value) || !NUMBER_REGEX.test(value) || value.length < 8) {
|
||||
fieldError = formatMessage(messages['password.validation.message']);
|
||||
}
|
||||
break;
|
||||
case 'country':
|
||||
if (flags.showConfigurableEdxFields || flags.showConfigurableRegistrationFields) {
|
||||
const {
|
||||
countryCode, displayValue, error,
|
||||
} = validateCountryField(value.trim(), countryList, formatMessage(messages['empty.country.field.error']));
|
||||
fieldError = error;
|
||||
setConfigurableFormFields(prevState => ({ ...prevState, country: { countryCode, displayValue } }));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (flags.showConfigurableRegistrationFields) {
|
||||
if (!value && fieldDescriptions[fieldName]?.error_message) {
|
||||
fieldError = fieldDescriptions[fieldName].error_message;
|
||||
} else if (fieldName === 'confirm_email' && formFields.email && value !== formFields.email) {
|
||||
fieldError = formatMessage(messages['email.do.not.match']);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (shouldSetErrors && fieldError) {
|
||||
setErrors(prevErrors => ({
|
||||
...prevErrors,
|
||||
[fieldName]: fieldError,
|
||||
}));
|
||||
}
|
||||
return fieldError;
|
||||
};
|
||||
|
||||
const isFormValid = (payload) => {
|
||||
@@ -226,6 +289,10 @@ const EmbeddableRegistrationPage = (props) => {
|
||||
event.preventDefault();
|
||||
setErrors(prevErrors => ({ ...prevErrors, [fieldName]: '' }));
|
||||
switch (fieldName) {
|
||||
case 'email':
|
||||
setFormFields(prevState => ({ ...prevState, email: emailSuggestion.suggestion }));
|
||||
setEmailSuggestion({ suggestion: '', type: '' });
|
||||
break;
|
||||
case 'username':
|
||||
setFormFields(prevState => ({ ...prevState, username: suggestion }));
|
||||
props.resetUsernameSuggestions();
|
||||
@@ -267,8 +334,12 @@ const EmbeddableRegistrationPage = (props) => {
|
||||
value,
|
||||
{ name: formFields.name, username: formFields.username, form_field_key: name },
|
||||
!validationApiRateLimited,
|
||||
false,
|
||||
);
|
||||
}
|
||||
if (name === 'email') {
|
||||
validateInput(name, value, null, !validationApiRateLimited, false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleOnFocus = (event) => {
|
||||
@@ -294,7 +365,6 @@ const EmbeddableRegistrationPage = (props) => {
|
||||
e.preventDefault();
|
||||
const totalRegistrationTime = (Date.now() - formStartTime) / 1000;
|
||||
let payload = { ...formFields };
|
||||
|
||||
if (!isFormValid(payload)) {
|
||||
setErrorCode(prevState => ({ type: FORM_SUBMISSION_ERROR, count: prevState.count + 1 }));
|
||||
return;
|
||||
@@ -307,12 +377,20 @@ const EmbeddableRegistrationPage = (props) => {
|
||||
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;
|
||||
}
|
||||
|
||||
let isValid = true;
|
||||
Object.entries(payload).forEach(([key, value]) => {
|
||||
if (validateInput(key, value, payload, false, true) !== '') {
|
||||
isValid = false;
|
||||
}
|
||||
});
|
||||
if (!isValid) {
|
||||
setErrorCode(prevState => ({ type: FORM_SUBMISSION_ERROR, count: prevState.count + 1 }));
|
||||
return;
|
||||
}
|
||||
payload = snakeCaseObject(payload);
|
||||
payload.totalRegistrationTime = totalRegistrationTime;
|
||||
|
||||
@@ -350,6 +428,7 @@ const EmbeddableRegistrationPage = (props) => {
|
||||
handleChange={handleOnChange}
|
||||
handleBlur={handleOnBlur}
|
||||
handleFocus={handleOnFocus}
|
||||
handleSuggestionClick={(e) => handleSuggestionClick(e, 'email')}
|
||||
handleOnClose={handleEmailSuggestionClosed}
|
||||
emailSuggestion={emailSuggestion}
|
||||
errorMessage={errors.email}
|
||||
|
||||
@@ -234,6 +234,97 @@ describe('RegistrationPage', () => {
|
||||
|
||||
expect(registrationPage.find('input#username').prop('value')).toEqual('test-user');
|
||||
});
|
||||
it('should run username and email frontend validations', () => {
|
||||
const payload = {
|
||||
name: 'John Doe',
|
||||
username: 'test@2u.com',
|
||||
email: 'test@yopmail.test',
|
||||
password: 'password1',
|
||||
country: 'Pakistan',
|
||||
honor_code: true,
|
||||
totalRegistrationTime: 0,
|
||||
marketing_emails_opt_in: true,
|
||||
};
|
||||
|
||||
store.dispatch = jest.fn(store.dispatch);
|
||||
const registrationPage = mount(reduxWrapper(<IntlEmbedableRegistrationForm {...props} />));
|
||||
populateRequiredFields(registrationPage, payload);
|
||||
registrationPage.find('input[name="email"]').simulate('focus');
|
||||
registrationPage.find('input[name="email"]').simulate('blur', { target: { value: 'test@yopmail.test', name: 'email' } });
|
||||
expect(registrationPage.find('.email-suggestion__text').exists()).toBeTruthy();
|
||||
|
||||
registrationPage.find('input[name="email"]').simulate('focus');
|
||||
registrationPage.find('input[name="email"]').simulate('blur', { target: { value: 'asasasasas', name: 'email' } });
|
||||
|
||||
registrationPage.find('button.btn-brand').simulate('click');
|
||||
expect(registrationPage.find('div[feedback-for="email"]').exists()).toBeTruthy();
|
||||
expect(registrationPage.find('div[feedback-for="username"]').exists()).toBeTruthy();
|
||||
});
|
||||
it('should run email frontend validations when random string is input', () => {
|
||||
const payload = {
|
||||
name: 'John Doe',
|
||||
username: 'testh@2u.com',
|
||||
email: 'as',
|
||||
password: 'password1',
|
||||
country: 'Pakistan',
|
||||
honor_code: true,
|
||||
totalRegistrationTime: 0,
|
||||
marketing_emails_opt_in: true,
|
||||
};
|
||||
|
||||
const registrationPage = mount(reduxWrapper(<IntlEmbedableRegistrationForm {...props} />));
|
||||
populateRequiredFields(registrationPage, payload);
|
||||
|
||||
registrationPage.find('button.btn-brand').simulate('click');
|
||||
expect(registrationPage.find('div[feedback-for="email"]').exists()).toBeTruthy();
|
||||
});
|
||||
it('should run frontend validations for name field', () => {
|
||||
const payload = {
|
||||
name: 'https://localhost.com',
|
||||
username: 'test@2u.com',
|
||||
email: 'as',
|
||||
password: 'password1',
|
||||
country: 'Pakistan',
|
||||
honor_code: true,
|
||||
totalRegistrationTime: 0,
|
||||
marketing_emails_opt_in: true,
|
||||
};
|
||||
|
||||
const registrationPage = mount(reduxWrapper(<IntlEmbedableRegistrationForm {...props} />));
|
||||
populateRequiredFields(registrationPage, payload);
|
||||
|
||||
registrationPage.find('button.btn-brand').simulate('click');
|
||||
expect(registrationPage.find('div[feedback-for="name"]').exists()).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should run frontend validations for password field', () => {
|
||||
const payload = {
|
||||
name: 'https://localhost.com',
|
||||
username: 'test@2u.com',
|
||||
email: 'as',
|
||||
password: 'as',
|
||||
country: 'Pakistan',
|
||||
honor_code: true,
|
||||
totalRegistrationTime: 0,
|
||||
marketing_emails_opt_in: true,
|
||||
};
|
||||
|
||||
const registrationPage = mount(reduxWrapper(<IntlEmbedableRegistrationForm {...props} />));
|
||||
populateRequiredFields(registrationPage, payload);
|
||||
|
||||
registrationPage.find('button.btn-brand').simulate('click');
|
||||
expect(registrationPage.find('div[feedback-for="password"]').exists()).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should click on email suggestion in case suggestion is avialable', () => {
|
||||
const registrationPage = mount(reduxWrapper(<IntlEmbedableRegistrationForm {...props} />));
|
||||
registrationPage.find('input[name="email"]').simulate('focus');
|
||||
registrationPage.find('input[name="email"]').simulate('blur', { target: { value: 'test@gmail.co', name: 'email' } });
|
||||
|
||||
registrationPage.find('a.email-suggestion-alert-warning').simulate('click');
|
||||
expect(registrationPage.find('input#email').prop('value')).toEqual('test@gmail.com');
|
||||
});
|
||||
|
||||
it('should remove extra character if username is more than 30 character long', () => {
|
||||
const registrationPage = mount(reduxWrapper(<IntlEmbedableRegistrationForm {...props} />));
|
||||
registrationPage.find('input#username').simulate('change', { target: { value: 'why_this_is_not_valid_username_', name: 'username' } });
|
||||
|
||||
Reference in New Issue
Block a user