From 94e823663e82ba264692abcb4a7da0ae779ff46b Mon Sep 17 00:00:00 2001 From: Attiya Ishaque Date: Tue, 30 Jan 2024 15:12:27 +0500 Subject: [PATCH 1/2] feat: split full name into first name and last name (#1131) --- .../NameField/NameField.jsx | 15 ++-- .../NameField/NameField.test.jsx | 78 +++++++++++++------ .../RegistrationFields/NameField/validator.js | 10 ++- src/register/RegistrationPage.jsx | 24 ++++-- src/register/RegistrationPage.test.jsx | 40 ++++++---- .../ConfigurableRegistrationForm.test.jsx | 10 ++- src/register/data/reducers.js | 4 +- src/register/data/tests/reducers.test.js | 4 +- src/register/messages.jsx | 45 +++++++---- 9 files changed, 155 insertions(+), 75 deletions(-) diff --git a/src/register/RegistrationFields/NameField/NameField.jsx b/src/register/RegistrationFields/NameField/NameField.jsx index ec2eb8552e..9a03b6cf1e 100644 --- a/src/register/RegistrationFields/NameField/NameField.jsx +++ b/src/register/RegistrationFields/NameField/NameField.jsx @@ -27,21 +27,23 @@ const NameField = (props) => { const { handleErrorChange, shouldFetchUsernameSuggestions, + name, + fullName, } = props; const handleOnBlur = (e) => { const { value } = e.target; - const fieldError = validateName(value, formatMessage); + const fieldError = validateName(value, name, formatMessage); if (fieldError) { - handleErrorChange('name', fieldError); + handleErrorChange(name, fieldError); } else if (shouldFetchUsernameSuggestions && !validationApiRateLimited) { - dispatch(fetchRealtimeValidations({ name: value })); + dispatch(fetchRealtimeValidations({ name: fullName.trim() })); } }; const handleOnFocus = () => { - handleErrorChange('name', ''); - dispatch(clearRegistrationBackendError('name')); + handleErrorChange(name, ''); + dispatch(clearRegistrationBackendError(name)); }; return ( @@ -56,6 +58,7 @@ const NameField = (props) => { NameField.defaultProps = { errorMessage: '', shouldFetchUsernameSuggestions: false, + fullName: '', }; NameField.propTypes = { @@ -64,6 +67,8 @@ NameField.propTypes = { value: PropTypes.string.isRequired, handleChange: PropTypes.func.isRequired, handleErrorChange: PropTypes.func.isRequired, + name: PropTypes.string.isRequired, + fullName: PropTypes.string, }; export default NameField; diff --git a/src/register/RegistrationFields/NameField/NameField.test.jsx b/src/register/RegistrationFields/NameField/NameField.test.jsx index 3d33467229..986e5f6bfe 100644 --- a/src/register/RegistrationFields/NameField/NameField.test.jsx +++ b/src/register/RegistrationFields/NameField/NameField.test.jsx @@ -51,7 +51,7 @@ describe('NameField', () => { beforeEach(() => { store = mockStore(initialState); props = { - name: 'name', + name: '', value: '', errorMessage: '', handleChange: jest.fn(), @@ -66,43 +66,44 @@ describe('NameField', () => { }); describe('Test Name Field', () => { - const fieldValidation = { name: 'Enter your full name' }; - - it('should run name field validation when onBlur is fired', () => { + it('should run first name field validation when onBlur is fired', () => { + props.name = 'firstname'; const { container } = render(routerWrapper(reduxWrapper())); - const nameInput = container.querySelector('input#name'); - fireEvent.blur(nameInput, { target: { value: '', name: 'name' } }); + const firstNameInput = container.querySelector('input#firstname'); + fireEvent.blur(firstNameInput, { target: { value: '', name: 'firstname' } }); expect(props.handleErrorChange).toHaveBeenCalledTimes(1); expect(props.handleErrorChange).toHaveBeenCalledWith( - 'name', - fieldValidation.name, + 'firstname', + 'Enter your first name', ); }); - it('should update errors for frontend validations', () => { + it('should update first name field error for frontend validations', () => { + props.name = 'firstname'; const { container } = render(routerWrapper(reduxWrapper())); - const nameInput = container.querySelector('input#name'); - fireEvent.blur(nameInput, { target: { value: 'https://invalid-name.com', name: 'name' } }); + const firstNameInput = container.querySelector('input#firstname'); + fireEvent.blur(firstNameInput, { target: { value: 'https://invalid-name.com', name: 'firstname' } }); expect(props.handleErrorChange).toHaveBeenCalledTimes(1); expect(props.handleErrorChange).toHaveBeenCalledWith( - 'name', - 'Enter a valid name', + 'firstname', + 'Enter a valid first name', ); }); - it('should clear error on focus', () => { + it('should clear first name error on focus', () => { + props.name = 'firstname'; const { container } = render(routerWrapper(reduxWrapper())); - const nameInput = container.querySelector('input#name'); - fireEvent.focus(nameInput, { target: { value: '', name: 'name' } }); + const firstNameInput = container.querySelector('input#firstname'); + fireEvent.focus(firstNameInput, { target: { value: '', name: 'firstname' } }); expect(props.handleErrorChange).toHaveBeenCalledTimes(1); expect(props.handleErrorChange).toHaveBeenCalledWith( - 'name', + 'firstname', '', ); }); @@ -112,14 +113,16 @@ describe('NameField', () => { props = { ...props, shouldFetchUsernameSuggestions: true, + fullName: 'test test', }; + props.name = 'lastname'; const { container } = render(routerWrapper(reduxWrapper())); - const nameInput = container.querySelector('input#name'); + const lastNameInput = container.querySelector('input#lastname'); // Enter a valid name so that frontend validations are passed - fireEvent.blur(nameInput, { target: { value: 'test', name: 'name' } }); + fireEvent.blur(lastNameInput, { target: { value: 'test', name: 'lastname' } }); - expect(store.dispatch).toHaveBeenCalledWith(fetchRealtimeValidations({ name: 'test' })); + expect(store.dispatch).toHaveBeenCalledWith(fetchRealtimeValidations({ name: props.fullName })); }); it('should clear the registration validation error on focus on field', () => { @@ -134,14 +137,43 @@ describe('NameField', () => { }, }); + props.name = 'lastname'; store.dispatch = jest.fn(store.dispatch); const { container } = render(routerWrapper(reduxWrapper())); - const nameInput = container.querySelector('input#name'); + const lastNameInput = container.querySelector('input#lastname'); + + fireEvent.focus(lastNameInput, { target: { value: 'test', name: 'lastname' } }); - fireEvent.focus(nameInput, { target: { value: 'test', name: 'name' } }); + expect(store.dispatch).toHaveBeenCalledWith(clearRegistrationBackendError('lastname')); + }); + + it('should run last name field validation when onBlur is fired', () => { + props.name = 'lastname'; + const { container } = render(routerWrapper(reduxWrapper())); - expect(store.dispatch).toHaveBeenCalledWith(clearRegistrationBackendError('name')); + const lastNameInput = container.querySelector('input#lastname'); + fireEvent.blur(lastNameInput, { target: { value: '', name: 'lastname' } }); + + expect(props.handleErrorChange).toHaveBeenCalledTimes(1); + expect(props.handleErrorChange).toHaveBeenCalledWith( + 'lastname', + 'Enter your last name', + ); + }); + + it('should update last name field error for frontend validation', () => { + props.name = 'lastname'; + const { container } = render(routerWrapper(reduxWrapper())); + + const lastNameInput = container.querySelector('input#lastname'); + fireEvent.blur(lastNameInput, { target: { value: 'https://invalid-name.com', name: 'lastname' } }); + + expect(props.handleErrorChange).toHaveBeenCalledTimes(1); + expect(props.handleErrorChange).toHaveBeenCalledWith( + 'lastname', + 'Enter a valid last name', + ); }); }); }); diff --git a/src/register/RegistrationFields/NameField/validator.js b/src/register/RegistrationFields/NameField/validator.js index e62c227d08..922ae1c9ab 100644 --- a/src/register/RegistrationFields/NameField/validator.js +++ b/src/register/RegistrationFields/NameField/validator.js @@ -9,12 +9,16 @@ export const HTML_REGEX = /<|>/u; // regex from backend export const INVALID_NAME_REGEX = /https?:\/\/(?:[-\w.]|(?:%[\da-fA-F]{2}))*/g; -const validateName = (value, formatMessage) => { +const validateName = (value, fieldName, formatMessage) => { let fieldError; if (!value.trim()) { - fieldError = formatMessage(messages['empty.name.field.error']); + fieldError = fieldName === 'lastname' + ? formatMessage(messages['empty.lastname.field.error']) + : formatMessage(messages['empty.firstname.field.error']); } else if (URL_REGEX.test(value) || HTML_REGEX.test(value) || INVALID_NAME_REGEX.test(value)) { - fieldError = formatMessage(messages['name.validation.message']); + fieldError = fieldName === 'lastname' + ? formatMessage(messages['lastname.validation.message']) + : formatMessage(messages['firstname.validation.message']); } return fieldError; }; diff --git a/src/register/RegistrationPage.jsx b/src/register/RegistrationPage.jsx index 43fc3df8b5..1584be989f 100644 --- a/src/register/RegistrationPage.jsx +++ b/src/register/RegistrationPage.jsx @@ -119,9 +119,11 @@ const RegistrationPage = (props) => { setErrorCode(prevState => ({ type: TPA_AUTHENTICATION_FAILURE, count: prevState.count + 1 })); } if (pipelineUserDetails && Object.keys(pipelineUserDetails).length !== 0) { - const { name = '', username = '', email = '' } = pipelineUserDetails; + const { + firstname = '', lastname = '', username = '', email = '', + } = pipelineUserDetails; setFormFields(prevState => ({ - ...prevState, name, username, email, + ...prevState, firstname, lastname, username, email, })); dispatch(setUserPipelineDataLoaded(true)); } @@ -310,14 +312,22 @@ const RegistrationPage = (props) => { />
+ { marketingEmailsOptIn: true, }, formFields: { - name: '', email: '', username: '', password: '', + firstname: '', lastname: '', email: '', username: '', password: '', }, emailSuggestion: { suggestion: '', type: '', }, errors: { - name: '', email: '', username: '', password: '', + firstname: '', lastname: '', email: '', username: '', password: '', }, }; @@ -134,7 +134,8 @@ describe('RegistrationPage', () => { }); const populateRequiredFields = (getByLabelText, payload, isThirdPartyAuth = false) => { - fireEvent.change(getByLabelText('Full name'), { target: { value: payload.name, name: 'name' } }); + fireEvent.change(getByLabelText('First name'), { target: { value: payload.firstname, name: 'firstname' } }); + fireEvent.change(getByLabelText('Last name'), { target: { value: payload.lastname, name: 'lastname' } }); fireEvent.change(getByLabelText('Public username'), { target: { value: payload.username, name: 'username' } }); fireEvent.change(getByLabelText('Email'), { target: { value: payload.email, name: 'email' } }); @@ -152,7 +153,8 @@ describe('RegistrationPage', () => { }); const emptyFieldValidation = { - name: 'Enter your full name', + firstname: 'Enter your first name', + lastname: 'Enter your last name', username: 'Username must be between 2 and 30 characters', email: 'Enter your email', password: 'Password criteria has not been met', @@ -169,7 +171,8 @@ describe('RegistrationPage', () => { window.location = { href: getConfig().BASE_URL, search: '?next=/course/demo-course-url' }; const payload = { - name: 'John Doe', + firstname: 'John', + lastname: 'Doe', username: 'john_doe', email: 'john.doe@gmail.com', password: 'password1', @@ -192,7 +195,8 @@ describe('RegistrationPage', () => { jest.spyOn(global.Date, 'now').mockImplementation(() => 0); const formPayload = { - name: 'John Doe', + firstname: 'John', + lastname: 'Doe', username: 'john_doe', email: 'john.doe@example.com', country: 'Pakistan', @@ -228,7 +232,8 @@ describe('RegistrationPage', () => { jest.spyOn(global.Date, 'now').mockImplementation(() => 0); const payload = { - name: 'John Doe', + firstname: 'John', + lastname: 'Doe', username: 'john_doe', email: 'john.doe@gmail.com', password: 'password1', @@ -579,7 +584,8 @@ describe('RegistrationPage', () => { registrationFormData: { ...registrationFormData, formFields: { - name: 'John Doe', + firstname: 'John', + lastname: 'Doe', username: 'john_doe', email: 'john.doe@yopmail.com', password: 'password1', @@ -593,13 +599,15 @@ describe('RegistrationPage', () => { const { container } = render(routerWrapper(reduxWrapper())); - const fullNameInput = container.querySelector('input#name'); + const firstNameInput = container.querySelector('input#firstname'); + const lastNameInput = container.querySelector('input#lastname'); const usernameInput = container.querySelector('input#username'); const emailInput = container.querySelector('input#email'); const passwordInput = container.querySelector('input#password'); const emailSuggestion = container.querySelector('.email-suggestion-alert-warning'); - expect(fullNameInput.value).toEqual('John Doe'); + expect(firstNameInput.value).toEqual('John'); + expect(lastNameInput.value).toEqual('Doe'); expect(usernameInput.value).toEqual('john_doe'); expect(emailInput.value).toEqual('john.doe@yopmail.com'); expect(passwordInput.value).toEqual('password1'); @@ -720,7 +728,8 @@ describe('RegistrationPage', () => { thirdPartyAuthContext: { ...initialState.commonComponents.thirdPartyAuthContext, pipelineUserDetails: { - name: 'John Doe', + firstname: 'John', + lastname: 'Doe', username: 'john_doe', email: 'john.doe@example.com', }, @@ -751,7 +760,8 @@ describe('RegistrationPage', () => { registrationFormData: { ...registrationFormData, formFields: { - name: 'John Doe', + firstname: 'John', + lastname: 'Doe', username: 'john_doe', email: 'john.doe@example.com', }, @@ -771,7 +781,8 @@ describe('RegistrationPage', () => { ...initialState.commonComponents.thirdPartyAuthContext, currentProvider: 'Apple', pipelineUserDetails: { - name: 'John Doe', + firstname: 'John', + lastname: 'Doe', username: 'john_doe', email: 'john.doe@example.com', }, @@ -783,7 +794,8 @@ describe('RegistrationPage', () => { render(routerWrapper(reduxWrapper())); expect(store.dispatch).toHaveBeenCalledWith(registerNewUser({ - name: 'John Doe', + firstname: 'John', + lastname: 'Doe', username: 'john_doe', email: 'john.doe@example.com', country: 'PK', diff --git a/src/register/components/tests/ConfigurableRegistrationForm.test.jsx b/src/register/components/tests/ConfigurableRegistrationForm.test.jsx index 88d3e39f97..3600130b9d 100644 --- a/src/register/components/tests/ConfigurableRegistrationForm.test.jsx +++ b/src/register/components/tests/ConfigurableRegistrationForm.test.jsx @@ -56,13 +56,13 @@ describe('ConfigurableRegistrationForm', () => { marketingEmailsOptIn: true, }, formFields: { - name: '', email: '', username: '', password: '', + firstname: '', lastname: '', email: '', username: '', password: '', }, emailSuggestion: { suggestion: '', type: '', }, errors: { - name: '', email: '', username: '', password: '', + firstname: '', lastname: '', email: '', username: '', password: '', }, }; @@ -128,7 +128,8 @@ describe('ConfigurableRegistrationForm', () => { }); const populateRequiredFields = (getByLabelText, payload, isThirdPartyAuth = false) => { - fireEvent.change(getByLabelText('Full name'), { target: { value: payload.name, name: 'name' } }); + fireEvent.change(getByLabelText('First name'), { target: { value: payload.firstname, name: 'firstname' } }); + fireEvent.change(getByLabelText('Last name'), { target: { value: payload.lastname, name: 'lastname' } }); fireEvent.change(getByLabelText('Public username'), { target: { value: payload.username, name: 'username' } }); fireEvent.change(getByLabelText('Email'), { target: { value: payload.email, name: 'email' } }); @@ -238,7 +239,8 @@ describe('ConfigurableRegistrationForm', () => { }); const payload = { - name: 'John Doe', + firstname: 'John', + lastname: 'Doe', username: 'john_doe', email: 'john.doe@example.com', password: 'password1', diff --git a/src/register/data/reducers.js b/src/register/data/reducers.js index e2cc00956a..0f3f1323a4 100644 --- a/src/register/data/reducers.js +++ b/src/register/data/reducers.js @@ -22,13 +22,13 @@ export const defaultState = { marketingEmailsOptIn: true, }, formFields: { - name: '', email: '', username: '', password: '', + firstname: '', lastname: '', email: '', username: '', password: '', }, emailSuggestion: { suggestion: '', type: '', }, errors: { - name: '', email: '', username: '', password: '', + firstname: '', lastname: '', email: '', username: '', password: '', }, }, validations: null, diff --git a/src/register/data/tests/reducers.test.js b/src/register/data/tests/reducers.test.js index 07badb9cda..7dc7d6ba95 100644 --- a/src/register/data/tests/reducers.test.js +++ b/src/register/data/tests/reducers.test.js @@ -22,13 +22,13 @@ describe('Registration Reducer Tests', () => { marketingEmailsOptIn: true, }, formFields: { - name: '', email: '', username: '', password: '', + firstname: '', lastname: '', email: '', username: '', password: '', }, emailSuggestion: { suggestion: '', type: '', }, errors: { - name: '', email: '', username: '', password: '', + firstname: '', lastname: '', email: '', username: '', password: '', }, }, validations: null, diff --git a/src/register/messages.jsx b/src/register/messages.jsx index 39d9e7f549..a715d576f0 100644 --- a/src/register/messages.jsx +++ b/src/register/messages.jsx @@ -7,10 +7,15 @@ const messages = defineMessages({ description: 'register page title', }, // Field labels - 'registration.fullname.label': { - id: 'registration.fullname.label', - defaultMessage: 'Full name', - description: 'Label that appears above fullname field', + 'registration.firstname.label': { + id: 'registration.firstname.label', + defaultMessage: 'First name', + description: 'Label that appears above first name field', + }, + 'registration.lastname.label': { + id: 'registration.lastname.label', + defaultMessage: 'Last name', + description: 'Label that appears above last name field', }, 'registration.email.label': { id: 'registration.email.label', @@ -38,10 +43,10 @@ const messages = defineMessages({ description: 'Text for opt in option on register page.', }, // Help text - 'help.text.name': { - id: 'help.text.name', + 'help.text.firstname': { + id: 'help.text.firstname', defaultMessage: 'This name will be used by any certificates that you earn.', - description: 'Help text for fullname field on registration page', + description: 'Help text for first name field on registration page', }, 'help.text.username.1': { id: 'help.text.username.1', @@ -76,10 +81,15 @@ const messages = defineMessages({ description: 'Heading of institution page', }, // Validation messages - 'empty.name.field.error': { - id: 'empty.name.field.error', - defaultMessage: 'Enter your full name', - description: 'Error message for empty fullname field', + 'empty.firstname.field.error': { + id: 'empty.firstname.field.error', + defaultMessage: 'Enter your first name', + description: 'Error message for empty first name field', + }, + 'empty.lastname.field.error': { + id: 'empty.lastname.field.error', + defaultMessage: 'Enter your last name', + description: 'Error message for empty last name field', }, 'empty.email.field.error': { id: 'empty.email.field.error', @@ -121,10 +131,15 @@ const messages = defineMessages({ defaultMessage: 'Username must be between 2 and 30 characters', description: 'Error message for empty username field', }, - 'name.validation.message': { - id: 'name.validation.message', - defaultMessage: 'Enter a valid name', - description: 'Validation message that appears when fullname contain URL', + 'firstname.validation.message': { + id: 'firstname.validation.message', + defaultMessage: 'Enter a valid first name', + description: 'Validation message that appears when first name contain URL', + }, + 'lastname.validation.message': { + id: 'lastname.validation.message', + defaultMessage: 'Enter a valid last name', + description: 'Validation message that appears when last name contain URL', }, 'password.validation.message': { id: 'password.validation.message', From 25909563a4f2d59e2a074c08cd69f6147af29dab Mon Sep 17 00:00:00 2001 From: Attiya Ishaque Date: Wed, 7 Feb 2024 18:49:32 +0500 Subject: [PATCH 2/2] fix: TPA data not auto-populated (#1156) --- .../NameField/NameField.test.jsx | 54 +++++++++---------- .../RegistrationFields/NameField/validator.js | 12 ++--- src/register/RegistrationPage.jsx | 22 ++++---- src/register/RegistrationPage.test.jsx | 48 ++++++++--------- .../ConfigurableRegistrationForm.test.jsx | 12 ++--- src/register/data/reducers.js | 4 +- src/register/data/tests/reducers.test.js | 4 +- src/register/messages.jsx | 28 +++++----- 8 files changed, 92 insertions(+), 92 deletions(-) diff --git a/src/register/RegistrationFields/NameField/NameField.test.jsx b/src/register/RegistrationFields/NameField/NameField.test.jsx index 986e5f6bfe..eb52754413 100644 --- a/src/register/RegistrationFields/NameField/NameField.test.jsx +++ b/src/register/RegistrationFields/NameField/NameField.test.jsx @@ -67,43 +67,43 @@ describe('NameField', () => { describe('Test Name Field', () => { it('should run first name field validation when onBlur is fired', () => { - props.name = 'firstname'; + props.name = 'firstName'; const { container } = render(routerWrapper(reduxWrapper())); - const firstNameInput = container.querySelector('input#firstname'); - fireEvent.blur(firstNameInput, { target: { value: '', name: 'firstname' } }); + const firstNameInput = container.querySelector('input#firstName'); + fireEvent.blur(firstNameInput, { target: { value: '', name: 'firstName' } }); expect(props.handleErrorChange).toHaveBeenCalledTimes(1); expect(props.handleErrorChange).toHaveBeenCalledWith( - 'firstname', + 'firstName', 'Enter your first name', ); }); it('should update first name field error for frontend validations', () => { - props.name = 'firstname'; + props.name = 'firstName'; const { container } = render(routerWrapper(reduxWrapper())); - const firstNameInput = container.querySelector('input#firstname'); - fireEvent.blur(firstNameInput, { target: { value: 'https://invalid-name.com', name: 'firstname' } }); + const firstNameInput = container.querySelector('input#firstName'); + fireEvent.blur(firstNameInput, { target: { value: 'https://invalid-name.com', name: 'firstName' } }); expect(props.handleErrorChange).toHaveBeenCalledTimes(1); expect(props.handleErrorChange).toHaveBeenCalledWith( - 'firstname', + 'firstName', 'Enter a valid first name', ); }); it('should clear first name error on focus', () => { - props.name = 'firstname'; + props.name = 'firstName'; const { container } = render(routerWrapper(reduxWrapper())); - const firstNameInput = container.querySelector('input#firstname'); - fireEvent.focus(firstNameInput, { target: { value: '', name: 'firstname' } }); + const firstNameInput = container.querySelector('input#firstName'); + fireEvent.focus(firstNameInput, { target: { value: '', name: 'firstName' } }); expect(props.handleErrorChange).toHaveBeenCalledTimes(1); expect(props.handleErrorChange).toHaveBeenCalledWith( - 'firstname', + 'firstName', '', ); }); @@ -115,12 +115,12 @@ describe('NameField', () => { shouldFetchUsernameSuggestions: true, fullName: 'test test', }; - props.name = 'lastname'; + props.name = 'lastName'; const { container } = render(routerWrapper(reduxWrapper())); - const lastNameInput = container.querySelector('input#lastname'); + const lastNameInput = container.querySelector('input#lastName'); // Enter a valid name so that frontend validations are passed - fireEvent.blur(lastNameInput, { target: { value: 'test', name: 'lastname' } }); + fireEvent.blur(lastNameInput, { target: { value: 'test', name: 'lastName' } }); expect(store.dispatch).toHaveBeenCalledWith(fetchRealtimeValidations({ name: props.fullName })); }); @@ -137,41 +137,41 @@ describe('NameField', () => { }, }); - props.name = 'lastname'; + props.name = 'lastName'; store.dispatch = jest.fn(store.dispatch); const { container } = render(routerWrapper(reduxWrapper())); - const lastNameInput = container.querySelector('input#lastname'); + const lastNameInput = container.querySelector('input#lastName'); - fireEvent.focus(lastNameInput, { target: { value: 'test', name: 'lastname' } }); + fireEvent.focus(lastNameInput, { target: { value: 'test', name: 'lastName' } }); - expect(store.dispatch).toHaveBeenCalledWith(clearRegistrationBackendError('lastname')); + expect(store.dispatch).toHaveBeenCalledWith(clearRegistrationBackendError('lastName')); }); it('should run last name field validation when onBlur is fired', () => { - props.name = 'lastname'; + props.name = 'lastName'; const { container } = render(routerWrapper(reduxWrapper())); - const lastNameInput = container.querySelector('input#lastname'); - fireEvent.blur(lastNameInput, { target: { value: '', name: 'lastname' } }); + const lastNameInput = container.querySelector('input#lastName'); + fireEvent.blur(lastNameInput, { target: { value: '', name: 'lastName' } }); expect(props.handleErrorChange).toHaveBeenCalledTimes(1); expect(props.handleErrorChange).toHaveBeenCalledWith( - 'lastname', + 'lastName', 'Enter your last name', ); }); it('should update last name field error for frontend validation', () => { - props.name = 'lastname'; + props.name = 'lastName'; const { container } = render(routerWrapper(reduxWrapper())); - const lastNameInput = container.querySelector('input#lastname'); - fireEvent.blur(lastNameInput, { target: { value: 'https://invalid-name.com', name: 'lastname' } }); + const lastNameInput = container.querySelector('input#lastName'); + fireEvent.blur(lastNameInput, { target: { value: 'https://invalid-name.com', name: 'lastName' } }); expect(props.handleErrorChange).toHaveBeenCalledTimes(1); expect(props.handleErrorChange).toHaveBeenCalledWith( - 'lastname', + 'lastName', 'Enter a valid last name', ); }); diff --git a/src/register/RegistrationFields/NameField/validator.js b/src/register/RegistrationFields/NameField/validator.js index 922ae1c9ab..36036bf924 100644 --- a/src/register/RegistrationFields/NameField/validator.js +++ b/src/register/RegistrationFields/NameField/validator.js @@ -12,13 +12,13 @@ export const INVALID_NAME_REGEX = /https?:\/\/(?:[-\w.]|(?:%[\da-fA-F]{2}))*/g; const validateName = (value, fieldName, formatMessage) => { let fieldError; if (!value.trim()) { - fieldError = fieldName === 'lastname' - ? formatMessage(messages['empty.lastname.field.error']) - : formatMessage(messages['empty.firstname.field.error']); + fieldError = fieldName === 'lastName' + ? formatMessage(messages['empty.lastName.field.error']) + : formatMessage(messages['empty.firstName.field.error']); } else if (URL_REGEX.test(value) || HTML_REGEX.test(value) || INVALID_NAME_REGEX.test(value)) { - fieldError = fieldName === 'lastname' - ? formatMessage(messages['lastname.validation.message']) - : formatMessage(messages['firstname.validation.message']); + fieldError = fieldName === 'lastName' + ? formatMessage(messages['lastName.validation.message']) + : formatMessage(messages['firstName.validation.message']); } return fieldError; }; diff --git a/src/register/RegistrationPage.jsx b/src/register/RegistrationPage.jsx index 1584be989f..c5e616cdbf 100644 --- a/src/register/RegistrationPage.jsx +++ b/src/register/RegistrationPage.jsx @@ -120,10 +120,10 @@ const RegistrationPage = (props) => { } if (pipelineUserDetails && Object.keys(pipelineUserDetails).length !== 0) { const { - firstname = '', lastname = '', username = '', email = '', + firstName = '', lastName = '', username = '', email = '', } = pipelineUserDetails; setFormFields(prevState => ({ - ...prevState, firstname, lastname, username, email, + ...prevState, firstName, lastName, username, email, })); dispatch(setUserPipelineDataLoaded(true)); } @@ -312,22 +312,22 @@ const RegistrationPage = (props) => { /> { marketingEmailsOptIn: true, }, formFields: { - firstname: '', lastname: '', email: '', username: '', password: '', + firstName: '', lastName: '', email: '', username: '', password: '', }, emailSuggestion: { suggestion: '', type: '', }, errors: { - firstname: '', lastname: '', email: '', username: '', password: '', + firstName: '', lastName: '', email: '', username: '', password: '', }, }; @@ -134,8 +134,8 @@ describe('RegistrationPage', () => { }); const populateRequiredFields = (getByLabelText, payload, isThirdPartyAuth = false) => { - fireEvent.change(getByLabelText('First name'), { target: { value: payload.firstname, name: 'firstname' } }); - fireEvent.change(getByLabelText('Last name'), { target: { value: payload.lastname, name: 'lastname' } }); + fireEvent.change(getByLabelText('First name'), { target: { value: payload.first_name, name: 'firstName' } }); + fireEvent.change(getByLabelText('Last name'), { target: { value: payload.last_name, name: 'lastName' } }); fireEvent.change(getByLabelText('Public username'), { target: { value: payload.username, name: 'username' } }); fireEvent.change(getByLabelText('Email'), { target: { value: payload.email, name: 'email' } }); @@ -153,8 +153,8 @@ describe('RegistrationPage', () => { }); const emptyFieldValidation = { - firstname: 'Enter your first name', - lastname: 'Enter your last name', + firstName: 'Enter your first name', + lastName: 'Enter your last name', username: 'Username must be between 2 and 30 characters', email: 'Enter your email', password: 'Password criteria has not been met', @@ -171,8 +171,8 @@ describe('RegistrationPage', () => { window.location = { href: getConfig().BASE_URL, search: '?next=/course/demo-course-url' }; const payload = { - firstname: 'John', - lastname: 'Doe', + first_name: 'John', + last_name: 'Doe', username: 'john_doe', email: 'john.doe@gmail.com', password: 'password1', @@ -195,8 +195,8 @@ describe('RegistrationPage', () => { jest.spyOn(global.Date, 'now').mockImplementation(() => 0); const formPayload = { - firstname: 'John', - lastname: 'Doe', + first_name: 'John', + last_name: 'Doe', username: 'john_doe', email: 'john.doe@example.com', country: 'Pakistan', @@ -232,8 +232,8 @@ describe('RegistrationPage', () => { jest.spyOn(global.Date, 'now').mockImplementation(() => 0); const payload = { - firstname: 'John', - lastname: 'Doe', + first_name: 'John', + last_name: 'Doe', username: 'john_doe', email: 'john.doe@gmail.com', password: 'password1', @@ -584,8 +584,8 @@ describe('RegistrationPage', () => { registrationFormData: { ...registrationFormData, formFields: { - firstname: 'John', - lastname: 'Doe', + firstName: 'John', + lastName: 'Doe', username: 'john_doe', email: 'john.doe@yopmail.com', password: 'password1', @@ -599,8 +599,8 @@ describe('RegistrationPage', () => { const { container } = render(routerWrapper(reduxWrapper())); - const firstNameInput = container.querySelector('input#firstname'); - const lastNameInput = container.querySelector('input#lastname'); + const firstNameInput = container.querySelector('input#firstName'); + const lastNameInput = container.querySelector('input#lastName'); const usernameInput = container.querySelector('input#username'); const emailInput = container.querySelector('input#email'); const passwordInput = container.querySelector('input#password'); @@ -728,8 +728,8 @@ describe('RegistrationPage', () => { thirdPartyAuthContext: { ...initialState.commonComponents.thirdPartyAuthContext, pipelineUserDetails: { - firstname: 'John', - lastname: 'Doe', + firstName: 'John', + lastName: 'Doe', username: 'john_doe', email: 'john.doe@example.com', }, @@ -760,8 +760,8 @@ describe('RegistrationPage', () => { registrationFormData: { ...registrationFormData, formFields: { - firstname: 'John', - lastname: 'Doe', + firstName: 'John', + lastName: 'Doe', username: 'john_doe', email: 'john.doe@example.com', }, @@ -781,8 +781,8 @@ describe('RegistrationPage', () => { ...initialState.commonComponents.thirdPartyAuthContext, currentProvider: 'Apple', pipelineUserDetails: { - firstname: 'John', - lastname: 'Doe', + firstName: 'John', + lastName: 'Doe', username: 'john_doe', email: 'john.doe@example.com', }, @@ -794,8 +794,8 @@ describe('RegistrationPage', () => { render(routerWrapper(reduxWrapper())); expect(store.dispatch).toHaveBeenCalledWith(registerNewUser({ - firstname: 'John', - lastname: 'Doe', + first_name: 'John', + last_name: 'Doe', username: 'john_doe', email: 'john.doe@example.com', country: 'PK', diff --git a/src/register/components/tests/ConfigurableRegistrationForm.test.jsx b/src/register/components/tests/ConfigurableRegistrationForm.test.jsx index 3600130b9d..1f6949eef2 100644 --- a/src/register/components/tests/ConfigurableRegistrationForm.test.jsx +++ b/src/register/components/tests/ConfigurableRegistrationForm.test.jsx @@ -56,13 +56,13 @@ describe('ConfigurableRegistrationForm', () => { marketingEmailsOptIn: true, }, formFields: { - firstname: '', lastname: '', email: '', username: '', password: '', + firstName: '', lastName: '', email: '', username: '', password: '', }, emailSuggestion: { suggestion: '', type: '', }, errors: { - firstname: '', lastname: '', email: '', username: '', password: '', + firstName: '', lastName: '', email: '', username: '', password: '', }, }; @@ -128,8 +128,8 @@ describe('ConfigurableRegistrationForm', () => { }); const populateRequiredFields = (getByLabelText, payload, isThirdPartyAuth = false) => { - fireEvent.change(getByLabelText('First name'), { target: { value: payload.firstname, name: 'firstname' } }); - fireEvent.change(getByLabelText('Last name'), { target: { value: payload.lastname, name: 'lastname' } }); + fireEvent.change(getByLabelText('First name'), { target: { value: payload.first_name, name: 'firstName' } }); + fireEvent.change(getByLabelText('Last name'), { target: { value: payload.last_name, name: 'lastName' } }); fireEvent.change(getByLabelText('Public username'), { target: { value: payload.username, name: 'username' } }); fireEvent.change(getByLabelText('Email'), { target: { value: payload.email, name: 'email' } }); @@ -239,8 +239,8 @@ describe('ConfigurableRegistrationForm', () => { }); const payload = { - firstname: 'John', - lastname: 'Doe', + first_name: 'John', + last_name: 'Doe', username: 'john_doe', email: 'john.doe@example.com', password: 'password1', diff --git a/src/register/data/reducers.js b/src/register/data/reducers.js index 0f3f1323a4..17e063f853 100644 --- a/src/register/data/reducers.js +++ b/src/register/data/reducers.js @@ -22,13 +22,13 @@ export const defaultState = { marketingEmailsOptIn: true, }, formFields: { - firstname: '', lastname: '', email: '', username: '', password: '', + firstName: '', lastName: '', email: '', username: '', password: '', }, emailSuggestion: { suggestion: '', type: '', }, errors: { - firstname: '', lastname: '', email: '', username: '', password: '', + firstName: '', lastName: '', email: '', username: '', password: '', }, }, validations: null, diff --git a/src/register/data/tests/reducers.test.js b/src/register/data/tests/reducers.test.js index 7dc7d6ba95..0844b00585 100644 --- a/src/register/data/tests/reducers.test.js +++ b/src/register/data/tests/reducers.test.js @@ -22,13 +22,13 @@ describe('Registration Reducer Tests', () => { marketingEmailsOptIn: true, }, formFields: { - firstname: '', lastname: '', email: '', username: '', password: '', + firstName: '', lastName: '', email: '', username: '', password: '', }, emailSuggestion: { suggestion: '', type: '', }, errors: { - firstname: '', lastname: '', email: '', username: '', password: '', + firstName: '', lastName: '', email: '', username: '', password: '', }, }, validations: null, diff --git a/src/register/messages.jsx b/src/register/messages.jsx index a715d576f0..600795d089 100644 --- a/src/register/messages.jsx +++ b/src/register/messages.jsx @@ -7,13 +7,13 @@ const messages = defineMessages({ description: 'register page title', }, // Field labels - 'registration.firstname.label': { - id: 'registration.firstname.label', + 'registration.firstName.label': { + id: 'registration.firstName.label', defaultMessage: 'First name', description: 'Label that appears above first name field', }, - 'registration.lastname.label': { - id: 'registration.lastname.label', + 'registration.lastName.label': { + id: 'registration.lastName.label', defaultMessage: 'Last name', description: 'Label that appears above last name field', }, @@ -43,8 +43,8 @@ const messages = defineMessages({ description: 'Text for opt in option on register page.', }, // Help text - 'help.text.firstname': { - id: 'help.text.firstname', + 'help.text.firstName': { + id: 'help.text.firstName', defaultMessage: 'This name will be used by any certificates that you earn.', description: 'Help text for first name field on registration page', }, @@ -81,13 +81,13 @@ const messages = defineMessages({ description: 'Heading of institution page', }, // Validation messages - 'empty.firstname.field.error': { - id: 'empty.firstname.field.error', + 'empty.firstName.field.error': { + id: 'empty.firstName.field.error', defaultMessage: 'Enter your first name', description: 'Error message for empty first name field', }, - 'empty.lastname.field.error': { - id: 'empty.lastname.field.error', + 'empty.lastName.field.error': { + id: 'empty.lastName.field.error', defaultMessage: 'Enter your last name', description: 'Error message for empty last name field', }, @@ -131,13 +131,13 @@ const messages = defineMessages({ defaultMessage: 'Username must be between 2 and 30 characters', description: 'Error message for empty username field', }, - 'firstname.validation.message': { - id: 'firstname.validation.message', + 'firstName.validation.message': { + id: 'firstName.validation.message', defaultMessage: 'Enter a valid first name', description: 'Validation message that appears when first name contain URL', }, - 'lastname.validation.message': { - id: 'lastname.validation.message', + 'lastName.validation.message': { + id: 'lastName.validation.message', defaultMessage: 'Enter a valid last name', description: 'Validation message that appears when last name contain URL', },