From dea1f7d335d5a9d96beaae63d6e686bdd7e1f14d Mon Sep 17 00:00:00 2001 From: Zainab Amir Date: Thu, 18 Feb 2021 15:36:05 +0500 Subject: [PATCH] make registration form static (#147) - removed fetch registration call from register page - removed unused state variables - moved few hardcoded links to env variables VAN-369 --- .env | 1 + .env.development | 3 + src/data/constants.js | 16 +- src/index.jsx | 3 + src/register/OptionalFields.jsx | 89 + src/register/RegistrationFailure.jsx | 2 +- src/register/RegistrationPage.jsx | 420 +- src/register/data/actions.js | 18 - src/register/data/constants.js | 26 + src/register/data/reducers.js | 19 +- src/register/data/sagas.js | 25 +- src/register/data/service.js | 20 - src/register/data/tests/sagas.test.js | 65 - src/register/messages.jsx | 97 + src/register/tests/RegistrationPage.test.jsx | 266 +- .../RegistrationPage.test.jsx.snap | 4187 ++++++++++++++++- 16 files changed, 4385 insertions(+), 872 deletions(-) create mode 100644 src/register/OptionalFields.jsx create mode 100644 src/register/data/constants.js diff --git a/.env b/.env index e8a92df7..041109ce 100644 --- a/.env +++ b/.env @@ -16,3 +16,4 @@ SITE_NAME=null USER_INFO_COOKIE_NAME=null AUTHN_MINIMAL_HEADER=true LOGIN_ISSUE_SUPPORT_LINK=null +REGISTRATION_OPTIONAL_FIELDS='' diff --git a/.env.development b/.env.development index 6763ca58..160cec0f 100644 --- a/.env.development +++ b/.env.development @@ -21,3 +21,6 @@ SITE_NAME='edX' USER_INFO_COOKIE_NAME='edx-user-info' AUTHN_MINIMAL_HEADER=true LOGIN_ISSUE_SUPPORT_LINK='' +TOS_AND_HONOR_CODE='http://localhost:18000/honor' +PRIVACY_POLICY='http://localhost:18000/privacy' +REGISTRATION_OPTIONAL_FIELDS='gender goals level_of_education year_of_birth' diff --git a/src/data/constants.js b/src/data/constants.js index 41ad35bc..2ab5bf8c 100644 --- a/src/data/constants.js +++ b/src/data/constants.js @@ -18,21 +18,7 @@ export const DEFAULT_STATE = 'default'; export const PENDING_STATE = 'pending'; export const COMPLETE_STATE = 'complete'; -export const REGISTRATION_VALIDITY_MAP = {}; -export const REGISTRATION_OPTIONAL_MAP = {}; -export const REGISTRATION_EXTRA_FIELDS = [ - 'confirm_email', - 'level_of_education', - 'gender', - 'year_of_birth', - 'mailing_address', - 'goals', - 'honor_code', - 'terms_of_service', - 'city', - 'country', -]; - +// Regex export const VALID_EMAIL_REGEX = '(^[-!#$%&\'*+/=?^_`{}|~0-9A-Z]+(\\.[-!#$%&\'*+/=?^_`{}|~0-9A-Z]+)*' + '|^"([\\001-\\010\\013\\014\\016-\\037!#-\\[\\]-\\177]|\\\\[\\001-\\011\\013\\014\\016-\\177])*"' + ')@((?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\\.)+)(?:[A-Z0-9-]{2,63})' diff --git a/src/index.jsx b/src/index.jsx index e8f59bcb..c3b345f5 100755 --- a/src/index.jsx +++ b/src/index.jsx @@ -61,6 +61,9 @@ initialize({ LOGIN_ISSUE_SUPPORT_LINK: process.env.LOGIN_ISSUE_SUPPORT_LINK || null, ACTIVATION_EMAIL_SUPPORT_LINK: process.env.ACTIVATION_EMAIL_SUPPORT_LINK || null, PASSWORD_RESET_SUPPORT_LINK: process.env.PASSWORD_RESET_SUPPORT_LINK || null, + TOS_AND_HONOR_CODE: process.env.TOS_AND_HONOR_CODE || null, + PRIVACY_POLICY: process.env.PRIVACY_POLICY || null, + REGISTRATION_OPTIONAL_FIELDS: process.env.REGISTRATION_OPTIONAL_FIELDS || '', }); }, }, diff --git a/src/register/OptionalFields.jsx b/src/register/OptionalFields.jsx new file mode 100644 index 00000000..c2b43843 --- /dev/null +++ b/src/register/OptionalFields.jsx @@ -0,0 +1,89 @@ +import React from 'react'; +import PropTypes from 'prop-types'; + +import { injectIntl, intlShape } from '@edx/frontend-platform/i18n'; + +import { EDUCATION_LEVELS, GENDER_OPTIONS, YEAR_OF_BIRTH_OPTIONS } from './data/constants'; +import messages from './messages'; + +import { AuthnValidationFormGroup } from '../common-components'; + +const OptionalFields = (props) => { + const { intl, onChangeHandler, values } = props; + + const getOptions = () => ({ + yearOfBirthOptions: [{ + value: '', + label: intl.formatMessage(messages['registration.year.of.birth.label']), + }].concat(YEAR_OF_BIRTH_OPTIONS), + educationLevelOptions: EDUCATION_LEVELS.map(key => ({ + value: key, + label: intl.formatMessage(messages[`registration.field.education.levels.${key || 'label'}`]), + })), + genderOptions: GENDER_OPTIONS.map(key => ({ + value: key, + label: intl.formatMessage(messages[`registration.field.gender.options.${key || 'label'}`]), + })), + }); + + return ( + <> + onChangeHandler('gender', e.target.value)} + selectOptions={getOptions().genderOptions} + /> + onChangeHandler('yearOfBirth', e.target.value)} + selectOptions={getOptions().yearOfBirthOptions} + /> + onChangeHandler('levelOfEducation', e.target.value)} + selectOptions={getOptions().educationLevelOptions} + /> + onChangeHandler('goals', e.target.value)} + /> + + ); +}; + +OptionalFields.propTypes = { + intl: intlShape.isRequired, + onChangeHandler: PropTypes.func.isRequired, + values: PropTypes.shape({ + gender: PropTypes.string, + goals: PropTypes.string, + levelOfEducation: PropTypes.string, + yearOfBirth: PropTypes.string, + }).isRequired, +}; + +export default injectIntl(OptionalFields); diff --git a/src/register/RegistrationFailure.jsx b/src/register/RegistrationFailure.jsx index 71889d22..63c0e7ec 100644 --- a/src/register/RegistrationFailure.jsx +++ b/src/register/RegistrationFailure.jsx @@ -44,7 +44,7 @@ const RegistrationFailureMessage = (props) => { return ( !userErrors.length ? null : ( - + {props.intl.formatMessage(messages['registration.request.failure.header'])} diff --git a/src/register/RegistrationPage.jsx b/src/register/RegistrationPage.jsx index 628a8fe5..9446506c 100644 --- a/src/register/RegistrationPage.jsx +++ b/src/register/RegistrationPage.jsx @@ -1,49 +1,36 @@ import React from 'react'; + +import camelCase from 'lodash.camelcase'; import { connect } from 'react-redux'; import Skeleton from 'react-loading-skeleton'; import PropTypes from 'prop-types'; + +import { getConfig } from '@edx/frontend-platform'; import { sendPageEvent, sendTrackEvent } from '@edx/frontend-platform/analytics'; -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { faSpinner } from '@fortawesome/free-solid-svg-icons'; import { - Input, - StatefulButton, - Hyperlink, - ValidationFormGroup, - Form, -} from '@edx/paragon'; - -import { - injectIntl, intlShape, + injectIntl, intlShape, getCountryList, getLocale, FormattedMessage, } from '@edx/frontend-platform/i18n'; +import { faSpinner } from '@fortawesome/free-solid-svg-icons'; +import { Form, Hyperlink, StatefulButton } from '@edx/paragon'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import camelCase from 'lodash.camelcase'; -import { getThirdPartyAuthContext } from '../common-components/data/actions'; -import { - registerNewUser, - fetchRegistrationForm, - fetchRealtimeValidations, -} from './data/actions'; +import { registerNewUser, fetchRealtimeValidations } from './data/actions'; import { registrationRequestSelector } from './data/selectors'; -import { thirdPartyAuthContextSelector } from '../common-components/data/selectors'; +import messages from './messages'; +import OptionalFields from './OptionalFields'; +import RegistrationFailure from './RegistrationFailure'; + import { RedirectLogistration, SocialAuthProviders, ThirdPartyAuthAlert, RenderInstitutionButton, InstitutionLogistration, AuthnValidationFormGroup, } from '../common-components'; -import RegistrationFailure from './RegistrationFailure'; +import { getThirdPartyAuthContext } from '../common-components/data/actions'; +import { thirdPartyAuthContextSelector } from '../common-components/data/selectors'; import EnterpriseSSO from '../common-components/EnterpriseSSO'; import { - DEFAULT_REDIRECT_URL, - DEFAULT_STATE, - PENDING_STATE, - LOGIN_PAGE, - REGISTER_PAGE, - REGISTRATION_VALIDITY_MAP, - REGISTRATION_OPTIONAL_MAP, - REGISTRATION_EXTRA_FIELDS, + DEFAULT_REDIRECT_URL, DEFAULT_STATE, LOGIN_PAGE, PENDING_STATE, REGISTER_PAGE, } from '../data/constants'; -import messages from './messages'; -import processLink, { getTpaProvider } from '../data/utils'; +import { getTpaProvider } from '../data/utils'; class RegistrationPage extends React.Component { constructor(props, context) { @@ -58,17 +45,11 @@ class RegistrationPage extends React.Component { username: '', password: '', country: '', - city: '', gender: '', yearOfBirth: '', - mailingAddress: '', goals: '', - honorCode: true, - termsOfService: true, levelOfEducation: '', - confirmEmail: '', enableOptionalField: false, - validationFieldName: '', validationErrorsAlertMessages: { name: [{ user_message: '' }], username: [{ user_message: '' }], @@ -84,16 +65,7 @@ class RegistrationPage extends React.Component { username: '', password: '', country: '', - honorCode: '', - termsOfService: '', }, - emailValid: false, - nameValid: false, - usernameValid: false, - passwordValid: false, - countryValid: false, - honorCodeValid: true, - termsOfServiceValid: false, institutionLogin: false, formValid: false, assignRegistrationErrorsToField: true, @@ -111,7 +83,6 @@ class RegistrationPage extends React.Component { payload.tpa_hint = tpaHint; } this.props.getThirdPartyAuthContext(payload); - this.props.fetchRegistrationForm(); } shouldComponentUpdate(nextProps) { @@ -120,11 +91,9 @@ class RegistrationPage extends React.Component { const { fieldName } = nextProps.validations.validation_decisions; const errorMsg = nextProps.validations.validation_decisions[fieldName]; errors[fieldName] = errorMsg; - const stateValidKey = `${camelCase(fieldName)}Valid`; const currentValidations = nextProps.validations.validation_decisions; this.setState({ - [stateValidKey]: !errorMsg, errors, currentValidations, }); @@ -147,6 +116,29 @@ class RegistrationPage extends React.Component { return true; } + getCountryOptions = () => { + const { intl } = this.props; + return [{ + value: '', + label: intl.formatMessage(messages['registration.country.label']), + }].concat(getCountryList(getLocale()).map(({ code, name }) => ({ value: code, label: name }))); + } + + getOptionalFields() { + const values = {}; + const optionalFields = getConfig().REGISTRATION_OPTIONAL_FIELDS.split(' '); + optionalFields.forEach((key) => { + values[camelCase(key)] = this.state[camelCase(key)]; + }); + + return ( + { this.setState({ [fieldName]: value }); }} + /> + ); + } + handleInstitutionLogin = () => { this.setState(prevState => ({ institutionLogin: !prevState.institutionLogin })); } @@ -154,24 +146,18 @@ class RegistrationPage extends React.Component { handleSubmit = (e) => { e.preventDefault(); const params = (new URL(document.location)).searchParams; - const payload = {}; - const payloadMap = new Map(); - payloadMap.set('name', this.state.name); - payloadMap.set('username', this.state.username); - payloadMap.set('email', this.state.email); + const payload = { + name: this.state.name, + username: this.state.username, + email: this.state.email, + country: this.state.country, + honor_code: true, + }; if (!this.props.thirdPartyAuthContext.currentProvider) { - payloadMap.set('password', this.state.password); + payload.password = this.state.password; } - const fieldMap = { ...REGISTRATION_VALIDITY_MAP, ...REGISTRATION_OPTIONAL_MAP }; - Object.entries(fieldMap).forEach(([key, value]) => { - if (value) { - payloadMap.set(key, this.state[camelCase(key)]); - } - }); - payloadMap.forEach((value, key) => { payload[key] = value; }); - const next = params.get('next'); const courseId = params.get('course_id'); if (next) { @@ -181,16 +167,21 @@ class RegistrationPage extends React.Component { payload.course_id = courseId; } - let finalValidation = this.isFormValid(); - if (!this.isFormValid()) { - // Special case where honor code and tos is a single field, true by default. We don't need - // to validate this field - payloadMap.forEach((value, key) => { - if (key !== 'honor_code' || 'terms_of_service' in REGISTRATION_VALIDITY_MAP) { - finalValidation = this.validateInput(key, value); - } + let finalValidation = this.state.formValid; + if (!this.state.formValid) { + Object.keys(payload).forEach(key => { + finalValidation = this.validateInput(key, payload[key]); }); } + // Since optional fields are not validated we can add it to payload after required fields + // have been validated. This will save us unwanted calls to validateInput() + const optionalFields = getConfig().REGISTRATION_OPTIONAL_FIELDS.split(' '); + optionalFields.forEach((key) => { + const stateKey = camelCase(key); + if (this.state[stateKey]) { + payload[key] = this.state[stateKey]; + } + }); if (finalValidation) { this.props.registerNewUser(payload); } else { @@ -199,18 +190,13 @@ class RegistrationPage extends React.Component { } checkNoValidationsErrors(validations) { - let keyValidList = null; - keyValidList = Object.entries(validations).map(([key]) => { + const keyValidList = Object.entries(validations).map(([key]) => { const validation = validations[key][0]; return !validation.user_message; }); return keyValidList.every((current) => current === true); } - isFormValid() { - return this.state.formValid; - } - handleOnBlur(e) { const { name, value } = e.target; if (this.props.statusCode === 403) { @@ -227,24 +213,16 @@ class RegistrationPage extends React.Component { username: this.state.username, password: this.state.password, name: this.state.name, - honor_code: this.state.honorCode, + honor_code: true, country: this.state.country, }; this.setState({ - validationFieldName: e.target.name, assignRegistrationErrorsToField: false, }); this.props.fetchRealtimeValidations(payload); } - handleOnChange(e) { - const targetValue = e.target.type === 'checkbox' ? e.target.checked : e.target.value; - this.setState({ - [camelCase(e.target.name)]: targetValue, - }); - } - handleOnOptional(e) { const optionalEnable = this.state.enableOptionalField; const targetValue = e.target.id === 'additionalFields' ? !optionalEnable : e.target.checked; @@ -254,19 +232,6 @@ class RegistrationPage extends React.Component { sendTrackEvent('edx.bi.user.register.optional_fields_selected', {}); } - handleOnClick(e) { - if (this.state.currentValidations && this.props.statusCode !== 403) { - const { errors } = this.state; - const fieldName = e.target.name; - errors[fieldName] = this.state.currentValidations[fieldName]; - const stateValidKey = `${camelCase(fieldName)}Valid`; - this.setState(prevState => ({ - [stateValidKey]: !prevState.currentValidations[fieldName], - errors, - })); - } - } - handleLoginLinkClickEvent() { sendTrackEvent('edx.bi.login_form.toggled', { category: 'user-engagement' }); } @@ -277,12 +242,7 @@ class RegistrationPage extends React.Component { validationErrorsAlertMessages, } = this.state; - let { - honorCodeValid, - termsOfServiceValid, - formValid, - assignRegistrationErrorsToField, - } = this.state; + let { formValid, assignRegistrationErrorsToField } = this.state; const validations = this.state.currentValidations; switch (inputName) { case 'email': @@ -385,14 +345,6 @@ class RegistrationPage extends React.Component { errors.country = emptyError[0].user_message; } break; - case 'honor_code': - honorCodeValid = value !== false; - errors.honorCode = honorCodeValid ? '' : null; - break; - case 'terms_of_service': - termsOfServiceValid = value !== false; - errors.termsOfService = termsOfServiceValid ? '' : null; - break; default: break; } @@ -404,179 +356,12 @@ class RegistrationPage extends React.Component { this.setState({ formValid, validationErrorsAlertMessages, - honorCodeValid, - termsOfServiceValid, assignRegistrationErrorsToField, errors, }); return formValid; } - addExtraRequiredFields() { - const fields = this.props.formData.fields.map((field) => { - let options = null; - if (REGISTRATION_EXTRA_FIELDS.includes(field.name)) { - if (field.required) { - const stateVar = camelCase(field.name); - - let beforeLink; - let link; - let linkText; - let afterLink; - - const props = { - id: field.name, - name: field.name, - type: field.type, - value: this.state[stateVar], - required: true, - onChange: e => this.handleOnChange(e), - }; - - REGISTRATION_VALIDITY_MAP[field.name] = true; - if (field.type === 'plaintext' && field.name === 'honor_code') { // special case where honor code and tos are combined - afterLink = field.label; - props.type = 'hidden'; - const nodes = []; - do { - const matches = processLink(afterLink); - [beforeLink, link, linkText, afterLink] = matches; - nodes.push( - - {beforeLink} - {linkText} - , - ); - } while (afterLink.includes('a href')); - nodes.push({afterLink}); - - return ( - - - - { nodes } - - - ); - } - if (field.type === 'checkbox') { - const matches = processLink(field.label); - [beforeLink, link, linkText, afterLink] = matches; - props.checked = this.state[stateVar]; - return ( - - - {beforeLink} - {linkText} - {afterLink} - - ); - } - if (field.type === 'select') { - options = field.options.map((item) => { - const option = {}; - option.value = item.value; - option.label = item.name; - if (item.name === '--') { - option.label = `${field.label} (required)`; - option.disabled = true; - } - return option; - }); - props.options = options; - } - return ( - this.handleOnClick(e)} - onBlur={(e) => this.handleOnBlur(e)} - onChange={(e) => this.handleOnChange(e)} - selectOptions={props.options} - /> - ); - } - } - return null; - }); - return fields; - } - - addExtraOptionalFields() { - const fields = this.props.formData.fields.map((field) => { - let options = null; - let cssClass = 'mb-20'; - if (REGISTRATION_EXTRA_FIELDS.includes(field.name)) { - if (!field.required && field.name !== 'honor_code' && field.name !== 'country') { - REGISTRATION_OPTIONAL_MAP[field.name] = true; - const stateVar = camelCase(field.name); - const props = { - id: field.name, - name: field.name, - type: field.type, - onChange: e => this.handleOnChange(e), - value: this.state[stateVar], - }; - - if (field.type === 'select') { - options = field.options.map((item) => { - const option = {}; - option.value = item.value; - option.label = item.name; - if (item.name === '--') { - option.label = `${field.label} (optional)`; - option.disabled = true; - } - return option; - }); - props.options = options; - } - if (field.name === 'gender') { - cssClass += ' opt-inline-field'; - } - - if (field.name === 'year_of_birth') { - cssClass += ' opt-inline-field opt-year-field'; - } - - return ( - this.handleOnClick(e)} - onChange={(e) => this.handleOnChange(e)} - selectOptions={props.options} - /> - ); - } - } - return null; - }); - return fields; - } - generateUserMessage(isFieldInValid, messageID) { return [{ user_message: isFieldInValid ? this.intl.formatMessage(messages[messageID]) : '' }]; } @@ -701,9 +486,8 @@ class RegistrationPage extends React.Component { invalid={this.state.errors.name !== ''} invalidMessage={this.state.errors.name} value={this.state.name} - onClick={(e) => this.handleOnClick(e)} onBlur={(e) => this.handleOnBlur(e)} - onChange={(e) => this.handleOnChange(e)} + onChange={(e) => this.setState({ name: e.target.value })} helpText={intl.formatMessage(messages['helptext.name'])} /> this.handleOnClick(e)} onBlur={(e) => this.handleOnBlur(e)} - onChange={(e) => this.handleOnChange(e)} + onChange={(e) => this.setState({ username: e.target.value })} helpText={intl.formatMessage(messages['helptext.username'])} /> this.handleOnClick(e)} onBlur={(e) => this.handleOnBlur(e)} - onChange={(e) => this.handleOnChange(e)} + onChange={(e) => this.setState({ email: e.target.value })} helpText={intl.formatMessage(messages['helptext.email'])} /> {!currentProvider && ( @@ -741,19 +523,51 @@ class RegistrationPage extends React.Component { invalid={this.state.errors.password !== ''} invalidMessage={this.state.errors.password} value={this.state.password} - onClick={(e) => this.handleOnClick(e)} onBlur={(e) => this.handleOnBlur(e)} - onChange={(e) => this.handleOnChange(e)} + onChange={(e) => this.setState({ password: e.target.value })} helpText={intl.formatMessage(messages['helptext.password'])} /> )} - { this.addExtraRequiredFields() } + this.handleOnBlur(e)} + onChange={(e) => this.setState({ country: e.target.value })} + selectOptions={this.getCountryOptions()} + /> +
+ + {intl.formatMessage(messages['terms.of.service.and.honor.code'])} + + ), + privacyPolicy: ( + + {intl.formatMessage(messages['privacy.policy'])} + + ), + }} + /> +
this.handleOnOptional(e)} onBlur={null} @@ -762,7 +576,7 @@ class RegistrationPage extends React.Component { isChecked={this.state.enableOptionalField} checkboxMessage={intl.formatMessage(messages['support.education.research'])} /> - { this.state.enableOptionalField ? this.addExtraOptionalFields() : null} + { this.state.enableOptionalField ? this.getOptionalFields() : null}
- + {intl.formatMessage(messages['create.an.account.using'])} @@ -803,10 +617,6 @@ class RegistrationPage extends React.Component { currentProvider, finishAuthUrl, providers, secondaryProviders, } = this.props.thirdPartyAuthContext; - if (!this.props.formData) { - return
; - } - const params = (new URL(window.location.href)).searchParams; const tpaHint = params.get('tpa_hint'); @@ -816,21 +626,25 @@ class RegistrationPage extends React.Component { } const provider = getTpaProvider(tpaHint, providers, secondaryProviders); return provider ? () - : this.renderForm(currentProvider, + : this.renderForm( + currentProvider, providers, secondaryProviders, thirdPartyAuthApiStatus, finishAuthUrl, submitState, - intl); + intl, + ); } - return this.renderForm(currentProvider, + return this.renderForm( + currentProvider, providers, secondaryProviders, thirdPartyAuthApiStatus, finishAuthUrl, submitState, - intl); + intl, + ); } } @@ -847,7 +661,6 @@ RegistrationPage.defaultProps = { secondaryProviders: [], pipelineUserDetails: null, }, - formData: null, validations: null, statusCode: null, }; @@ -883,11 +696,6 @@ RegistrationPage.propTypes = { username: PropTypes.string, }), }), - - fetchRegistrationForm: PropTypes.func.isRequired, - formData: PropTypes.shape({ - fields: PropTypes.array, - }), fetchRealtimeValidations: PropTypes.func.isRequired, validations: PropTypes.shape({ validation_decisions: PropTypes.shape({ @@ -911,7 +719,6 @@ const mapStateToProps = state => { thirdPartyAuthApiStatus: state.commonComponents.thirdPartyAuthApiStatus, registrationResult, thirdPartyAuthContext, - formData: state.register.formData, validations: state.register.validations, statusCode: state.register.statusCode, }; @@ -921,7 +728,6 @@ export default connect( mapStateToProps, { getThirdPartyAuthContext, - fetchRegistrationForm, fetchRealtimeValidations, registerNewUser, }, diff --git a/src/register/data/actions.js b/src/register/data/actions.js index aee161da..aef1dc52 100644 --- a/src/register/data/actions.js +++ b/src/register/data/actions.js @@ -26,24 +26,6 @@ export const registerNewUserFailure = (error) => ({ payload: { error }, }); -// Registration Form Fields -export const fetchRegistrationForm = () => ({ - type: REGISTER_FORM.BASE, -}); - -export const fetchRegistrationFormBegin = () => ({ - type: REGISTER_FORM.BEGIN, -}); - -export const fetchRegistrationFormSuccess = (formData) => ({ - type: REGISTER_FORM.SUCCESS, - payload: { formData }, -}); - -export const fetchRegistrationFormFailure = () => ({ - type: REGISTER_FORM.FAILURE, -}); - // Realtime Field validations export const fetchRealtimeValidations = (formPayload) => ({ type: REGISTER_FORM_VALIDATIONS.BASE, diff --git a/src/register/data/constants.js b/src/register/data/constants.js new file mode 100644 index 00000000..872387ca --- /dev/null +++ b/src/register/data/constants.js @@ -0,0 +1,26 @@ +export const YEAR_OF_BIRTH_OPTIONS = (() => { + const currentYear = new Date().getFullYear(); + const years = []; + let startYear = currentYear - 120; + while (startYear < currentYear) { + startYear += 1; + + years.push({ value: startYear.toString(), label: startYear }); + } + return years.reverse(); +})(); + +export const EDUCATION_LEVELS = [ + '', + 'p', + 'm', + 'b', + 'a', + 'hs', + 'jhs', + 'el', + 'none', + 'o', +]; + +export const GENDER_OPTIONS = ['', 'f', 'm', 'o']; diff --git a/src/register/data/reducers.js b/src/register/data/reducers.js index 02978434..42d926be 100644 --- a/src/register/data/reducers.js +++ b/src/register/data/reducers.js @@ -1,8 +1,4 @@ -import { - REGISTER_NEW_USER, - REGISTER_FORM, - REGISTER_FORM_VALIDATIONS, -} from './actions'; +import { REGISTER_NEW_USER, REGISTER_FORM_VALIDATIONS } from './actions'; import { DEFAULT_STATE, PENDING_STATE } from '../../data/constants'; @@ -32,19 +28,6 @@ const reducer = (state = defaultState, action) => { registrationError: action.payload.error, submitState: DEFAULT_STATE, }; - case REGISTER_FORM.BEGIN: - return { - ...state, - }; - case REGISTER_FORM.SUCCESS: - return { - ...state, - formData: action.payload.formData, - }; - case REGISTER_FORM.FAILURE: - return { - ...state, - }; case REGISTER_FORM_VALIDATIONS.BEGIN: return { ...state, diff --git a/src/register/data/sagas.js b/src/register/data/sagas.js index 00165df4..26e82093 100644 --- a/src/register/data/sagas.js +++ b/src/register/data/sagas.js @@ -12,18 +12,10 @@ import { fetchRealtimeValidationsBegin, fetchRealtimeValidationsSuccess, fetchRealtimeValidationsFailure, - REGISTER_FORM, - fetchRegistrationFormBegin, - fetchRegistrationFormSuccess, - fetchRegistrationFormFailure, } from './actions'; // Services -import { - getFieldsValidations, - getRegistrationForm, - registerRequest, -} from './service'; +import { getFieldsValidations, registerRequest } from './service'; import { INTERNAL_SERVER_ERROR } from '../../login/data/constants'; export function* handleNewUserRegistration(action) { @@ -47,20 +39,6 @@ export function* handleNewUserRegistration(action) { } } -export function* fetchRegistrationForm() { - try { - yield put(fetchRegistrationFormBegin()); - const { registrationForm } = yield call(getRegistrationForm); - - yield put(fetchRegistrationFormSuccess( - registrationForm, - )); - } catch (e) { - yield put(fetchRegistrationFormFailure()); - logError(e); - } -} - export function* fetchRealtimeValidations(action) { try { yield put(fetchRealtimeValidationsBegin()); @@ -80,6 +58,5 @@ export function* fetchRealtimeValidations(action) { export default function* saga() { yield takeEvery(REGISTER_NEW_USER.BASE, handleNewUserRegistration); - yield takeEvery(REGISTER_FORM.BASE, fetchRegistrationForm); yield takeEvery(REGISTER_FORM_VALIDATIONS.BASE, fetchRealtimeValidations); } diff --git a/src/register/data/service.js b/src/register/data/service.js index 87f19917..7acad273 100644 --- a/src/register/data/service.js +++ b/src/register/data/service.js @@ -24,26 +24,6 @@ export async function registerRequest(registrationInformation) { }; } -export async function getRegistrationForm() { - const requestConfig = { - headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, - isPublic: true, - }; - - const { data } = await getAuthenticatedHttpClient() - .get( - `${getConfig().LMS_BASE_URL}/user_api/v2/account/registration/`, - requestConfig, - ) - .catch((e) => { - throw (e); - }); - - return { - registrationForm: data, - }; -} - export async function getFieldsValidations(formPayload) { const requestConfig = { headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, diff --git a/src/register/data/tests/sagas.test.js b/src/register/data/tests/sagas.test.js index 94cb83ee..5454e20f 100644 --- a/src/register/data/tests/sagas.test.js +++ b/src/register/data/tests/sagas.test.js @@ -4,7 +4,6 @@ import { camelCaseObject } from '@edx/frontend-platform'; import * as actions from '../actions'; import { fetchRealtimeValidations, - fetchRegistrationForm, handleNewUserRegistration, } from '../sagas'; import * as api from '../service'; @@ -12,70 +11,6 @@ import initializeMockLogging from '../../../setupTest'; const { loggingService } = initializeMockLogging(); -describe('fetchRegistrationForm', () => { - const data = { - fields: [{ - label: 'City', - name: 'city', - type: 'text', - errorMessages: { - required: 'invalid city', - }, - required: true, - }, - { - label: 'I agree to the Your Platform Name Here Honor Code', - name: 'honor_code', - type: 'checkbox', - errorMessages: { - required: 'invalid honor code', - }, - required: true, - }], - }; - - beforeEach(() => { - loggingService.logError.mockReset(); - }); - - it('should call service and dispatch success action', async () => { - const getRegistrationForm = jest.spyOn(api, 'getRegistrationForm') - .mockImplementation(() => Promise.resolve({ registrationForm: data })); - - const dispatched = []; - await runSaga( - { dispatch: (action) => dispatched.push(action) }, - fetchRegistrationForm, - ); - - expect(getRegistrationForm).toHaveBeenCalledTimes(1); - expect(dispatched).toEqual([ - actions.fetchRegistrationFormBegin(), - actions.fetchRegistrationFormSuccess(data), - ]); - getRegistrationForm.mockClear(); - }); - - it('should call service and dispatch error action', async () => { - const getRegistrationForm = jest.spyOn(api, 'getRegistrationForm') - .mockImplementation(() => Promise.reject(new Error('something went wrong'))); - - const dispatched = []; - await runSaga( - { dispatch: (action) => dispatched.push(action) }, - fetchRegistrationForm, - ); - - expect(getRegistrationForm).toHaveBeenCalledTimes(1); - expect(loggingService.logError).toHaveBeenCalled(); - expect(dispatched).toEqual([ - actions.fetchRegistrationFormBegin(), - actions.fetchRegistrationFormFailure(), - ]); - getRegistrationForm.mockClear(); - }); -}); - describe('fetchRealtimeValidations', () => { const params = { payload: { diff --git a/src/register/messages.jsx b/src/register/messages.jsx index 2be08025..da53a3f6 100644 --- a/src/register/messages.jsx +++ b/src/register/messages.jsx @@ -161,6 +161,103 @@ const messages = defineMessages({ defaultMessage: 'This is what you will use to login.', description: '', }, + // Terms of Service and Honor Code + 'terms.of.service.and.honor.code': { + id: 'terms.of.service.and.honor.code', + defaultMessage: 'Terms of Service and Honor Code', + description: 'Text for the hyperlink that redirects user to terms of service and honor code', + }, + 'privacy.policy': { + id: 'privacy.policy', + defaultMessage: 'Privacy Policy', + description: 'Text for the hyperlink that redirects user to privacy policy', + }, + // Registration Fields + 'registration.year.of.birth.label': { + id: 'registration.year.of.birth.label', + defaultMessage: 'Year of birth (optional)', + description: 'Placeholder for the year of birth options dropdown', + }, + 'registration.country.label': { + id: 'registration.country.label', + defaultMessage: 'Country or Region of Residence (required)', + description: 'Placeholder for the country options dropdown.', + }, + 'registration.field.gender.options.label': { + id: 'registration.field.gender.options.label', + defaultMessage: 'Gender (optional)', + description: 'Placeholder for the gender options dropdown', + }, + 'registration.goals.label': { + id: 'registration.goals.label', + defaultMessage: 'Tell us why you\'re interested in edX (optional)', + description: 'Placeholder for the goals options dropdown', + }, + 'registration.field.gender.options.f': { + id: 'registration.field.gender.options.f', + defaultMessage: 'Female', + description: 'The label for the female gender option.', + }, + 'registration.field.gender.options.m': { + id: 'registration.field.gender.options.m', + defaultMessage: 'Male', + description: 'The label for the male gender option.', + }, + 'registration.field.gender.options.o': { + id: 'registration.field.gender.options.o', + defaultMessage: 'Other/Prefer Not to Say', + description: 'The label for catch-all gender option.', + }, + 'registration.field.education.levels.label': { + id: 'registration.field.education.levels.label', + defaultMessage: 'Highest level of education completed (optional)', + description: 'Placeholder for the education levels dropdown.', + }, + 'registration.field.education.levels.p': { + id: 'registration.field.education.levels.p', + defaultMessage: 'Doctorate', + description: 'Selected by the user if their highest level of education is a doctorate degree.', + }, + 'registration.field.education.levels.m': { + id: 'registration.field.education.levels.m', + defaultMessage: "Master's or professional degree", + description: "Selected by the user if their highest level of education is a master's or professional degree from a college or university.", + }, + 'registration.field.education.levels.b': { + id: 'registration.field.education.levels.b', + defaultMessage: "Bachelor's Degree", + description: "Selected by the user if their highest level of education is a four year college or university bachelor's degree.", + }, + 'registration.field.education.levels.a': { + id: 'registration.field.education.levels.a', + defaultMessage: "Associate's degree", + description: "Selected by the user if their highest level of education is an associate's degree. 1-2 years of college or university.", + }, + 'registration.field.education.levels.hs': { + id: 'registration.field.education.levels.hs', + defaultMessage: 'Secondary/high school', + description: 'Selected by the user if their highest level of education is secondary or high school. 9-12 years of education.', + }, + 'registration.field.education.levels.jhs': { + id: 'registration.field.education.levels.jhs', + defaultMessage: 'Junior secondary/junior high/middle school', + description: 'Selected by the user if their highest level of education is junior or middle school. 6-8 years of education.', + }, + 'registration.field.education.levels.el': { + id: 'registration.field.education.levels.el', + defaultMessage: 'Elementary/primary school', + description: 'Selected by the user if their highest level of education is elementary or primary school. 1-5 years of education.', + }, + 'registration.field.education.levels.none': { + id: 'registration.field.education.levels.none', + defaultMessage: 'No formal education', + description: 'Selected by the user to describe their education.', + }, + 'registration.field.education.levels.o': { + id: 'registration.field.education.levels.o', + defaultMessage: 'Other education', + description: 'Selected by the user if they have a type of education not described by the other choices.', + }, }); export default messages; diff --git a/src/register/tests/RegistrationPage.test.jsx b/src/register/tests/RegistrationPage.test.jsx index d5d26e46..43614c9b 100644 --- a/src/register/tests/RegistrationPage.test.jsx +++ b/src/register/tests/RegistrationPage.test.jsx @@ -3,7 +3,7 @@ import { Provider } from 'react-redux'; import renderer from 'react-test-renderer'; import { mount } from 'enzyme'; import configureStore from 'redux-mock-store'; -import { getConfig } from '@edx/frontend-platform'; +import { getConfig, mergeConfig } from '@edx/frontend-platform'; import { IntlProvider, injectIntl, configure } from '@edx/frontend-platform/i18n'; import * as analytics from '@edx/frontend-platform/analytics'; import CookiePolicyBanner from '@edx/frontend-component-cookie-policy-banner'; @@ -13,7 +13,7 @@ import { RenderInstitutionButton } from '../../common-components'; import RegistrationFailureMessage from '../RegistrationFailure'; import { COMPLETE_STATE, PENDING_STATE } from '../../data/constants'; import { INTERNAL_SERVER_ERROR } from '../../login/data/constants'; -import { fetchRegistrationForm, fetchRealtimeValidations, registerNewUser } from '../data/actions'; +import { fetchRealtimeValidations, registerNewUser } from '../data/actions'; jest.mock('@edx/frontend-platform/analytics'); @@ -24,22 +24,17 @@ const IntlRegistrationPage = injectIntl(RegistrationPage); const IntlRegistrationFailure = injectIntl(RegistrationFailureMessage); const mockStore = configureStore(); -describe('./RegistrationPage.js', () => { +describe('RegistrationPageTests', () => { + mergeConfig({ + PRIVACY_POLICY: 'http://privacy-policy.com', + REGISTRATION_OPTIONAL_FIELDS: 'gender goals level_of_education year_of_birth', + TOS_AND_HONOR_CODE: 'http://tos-and-honot-code.com', + }); + const initialState = { register: { registrationResult: { success: false, redirectUrl: '' }, registrationError: null, - formData: { - fields: [{ - label: 'I agree to the Your Platform Name Here Honor Code', - name: 'honor_code', - type: 'checkbox', - errorMessages: { - required: 'You must agree to the Your Platform Name Here Honor Code', - }, - required: true, - }], - }, }, commonComponents: { thirdPartyAuthApiStatus: null, @@ -71,6 +66,14 @@ describe('./RegistrationPage.js', () => { registerUrl: '/dummy_auth', }; + const emptyFieldValidation = { + name: 'Please enter your Full Name.', + username: 'Please enter your Public Username.', + email: 'Please enter your Email.', + password: 'Please enter your Password.', + country: 'Select your country or region of residence.', + }; + const reduxWrapper = children => ( {children} @@ -85,11 +88,7 @@ describe('./RegistrationPage.js', () => { ENVIRONMENT: 'production', LANGUAGE_PREFERENCE_COOKIE_NAME: 'yum', }, - messages: { - 'es-419': {}, - de: {}, - 'en-us': {}, - }, + messages: { 'es-419': {}, de: {}, 'en-us': {} }, }); props = { registrationResult: jest.fn(), @@ -100,43 +99,6 @@ describe('./RegistrationPage.js', () => { jest.clearAllMocks(); }); - it('should show error messages on invalid extra fields', () => { - const validationMessage = { - honorCode: 'You must agree to the Your Platform Name Here Honor Code', - country: 'Select your country or region of residence.', - }; - store = mockStore({ - ...initialState, - register: { - ...initialState.register, - formData: { - fields: [ - ...initialState.register.formData.fields, - { - label: 'The country or region where you live.', - name: 'country', - type: 'select', - options: [{ value: '', name: '--' }, { value: 'AF', name: 'Afghanistan' }], - errorMessages: { - required: validationMessage.country, - }, - required: true, - }, - ], - }, - }, - }); - const registrationPage = mount(reduxWrapper()); - - registrationPage.find('input#honor_code').simulate('change', { target: { checked: false, name: 'honor_code', type: 'checkbox' } }); - registrationPage.update(); - expect(registrationPage.find('#honor_code-invalid-feedback').text()).toEqual(validationMessage.honorCode); - - registrationPage.find('select#country').simulate('change', { target: { checked: false, name: 'country', type: 'checkbox' } }); - registrationPage.update(); - expect(registrationPage.find('#country-invalid-feedback').text()).toEqual(validationMessage.country); - }); - it('should toggle optional fields state on checkbox click', () => { const registrationPage = mount(reduxWrapper()); @@ -173,58 +135,14 @@ describe('./RegistrationPage.js', () => { }); it('should show optional fields section on optional check enabled', () => { - store = mockStore({ - ...initialState, - register: { - ...initialState.register, - formData: { - fields: [ - { - label: 'Tell us why you\'re interested in edX', - name: 'goals', - type: 'textarea', - required: false, - }, - { - label: 'Highest level of Education completed.', - name: 'level_of_education', - type: 'select', - options: [{ value: '', name: '--' }, { value: 'p', name: 'Doctorate' }], - required: false, - }, - { - label: 'Year of birth.', - name: 'year_of_birth', - type: 'select', - options: [{ value: '', name: '--' }, { value: '2021', name: '2021' }], - required: false, - }, - { - label: 'Gender.', - name: 'gender', - type: 'select', - options: [{ value: '', name: '--' }, { value: 'f', name: 'Female' }], - required: false, - }, - ], - }, - }, - }); - const registrationPage = mount(reduxWrapper()); - registrationPage.find('input#optional').simulate('change', { target: { checked: true } }); registrationPage.update(); - expect(registrationPage.find('textarea#goals').length).toEqual(1); - expect(registrationPage.find('select#level_of_education').length).toEqual(1); - expect(registrationPage.find('select#year_of_birth').length).toEqual(1); - expect(registrationPage.find('select#gender').length).toEqual(1); - }); - it('should dispatch fetchRegistrationForm on ComponentDidMount', () => { - store.dispatch = jest.fn(store.dispatch); - mount(reduxWrapper()); - expect(store.dispatch).toHaveBeenCalledWith(fetchRegistrationForm()); + expect(registrationPage.find('textarea#goals').length).toEqual(1); + expect(registrationPage.find('select#levelOfEducation').length).toEqual(1); + expect(registrationPage.find('select#yearOfBirth').length).toEqual(1); + expect(registrationPage.find('select#gender').length).toEqual(1); }); it('should dispatch fetchRealtimeValidations on Blur', () => { @@ -255,9 +173,13 @@ describe('./RegistrationPage.js', () => { registrationPage.find('input#password').simulate('blur', { target: { value: '', name: 'password' } }); formPayload.fieldName = 'password'; expect(store.dispatch).toHaveBeenCalledWith(fetchRealtimeValidations(formPayload)); + + registrationPage.find('select#country').simulate('blur', { target: { value: '', name: 'country' } }); + formPayload.fieldName = 'country'; + expect(store.dispatch).toHaveBeenCalledWith(fetchRealtimeValidations(formPayload)); }); - it('should call Validations function on Blur in case of 403', () => { + it('should call validations function on Blur in case of 403', () => { store = mockStore({ ...initialState, register: { @@ -265,25 +187,17 @@ describe('./RegistrationPage.js', () => { statusCode: 403, }, }); - const errors = { - email: 'Please enter your Email.', - name: 'Please enter your Full Name.', - username: 'Please enter your Public Username.', - password: 'Please enter your Password.', - country: '', - honorCode: '', - termsOfService: '', - }; const registrationPage = mount(reduxWrapper()); registrationPage.find('input#username').simulate('blur', { target: { value: '', name: 'username' } }); registrationPage.find('input#name').simulate('blur', { target: { value: '', name: 'name' } }); registrationPage.find('input#email').simulate('blur', { target: { value: '', name: 'email' } }); registrationPage.find('input#password').simulate('blur', { target: { value: '', name: 'password' } }); - expect(registrationPage.find('RegistrationPage').state('errors')).toEqual(errors); + registrationPage.find('select#country').simulate('blur', { target: { value: '', name: 'country' } }); + expect(registrationPage.find('RegistrationPage').state('errors')).toEqual(emptyFieldValidation); }); - it('validate passwrod validations incsae of 403', () => { + it('validate password validations in case of 403', () => { store = mockStore({ ...initialState, register: { @@ -297,8 +211,6 @@ describe('./RegistrationPage.js', () => { username: '', password: 'Your password must contain at least 8 characters', country: '', - honorCode: '', - termsOfService: '', }; const registrationPage = mount(reduxWrapper()); @@ -341,32 +253,7 @@ describe('./RegistrationPage.js', () => { expect(shouldUpdate).toBe(false); }); - it('tests onClick should change errors state via realtime validation', () => { - const nextProps = { - validations: { - validation_decisions: { - username: 'Username must be between 2 and 30 characters long.', - }, - }, - }; - - const errors = { - email: '', - name: '', - username: 'Username must be between 2 and 30 characters long.', - password: '', - country: '', - honorCode: '', - termsOfService: '', - }; - - const root = mount(reduxWrapper()); - root.find('RegistrationPage').instance().shouldComponentUpdate(nextProps); - root.find('input#username').simulate('click', { target: { value: '', name: 'username' } }); - expect(root.find('RegistrationPage').state('errors')).toEqual(errors); - }); - - it('should not dispatch registerNewUser on Submit', () => { + it('should not dispatch registerNewUser on empty form Submission', () => { const formPayload = { email: '', username: '', @@ -382,6 +269,76 @@ describe('./RegistrationPage.js', () => { expect(store.dispatch).not.toHaveBeenCalledWith(registerNewUser(formPayload)); }); + it('should show error messages for required fields on empty form submission', () => { + const registrationPage = mount(reduxWrapper()); + registrationPage.find('button.btn-brand').simulate('click'); + + expect(registrationPage.find('#name-invalid-feedback').text()).toEqual(emptyFieldValidation.name); + expect(registrationPage.find('#username-invalid-feedback').text()).toEqual(emptyFieldValidation.username); + expect(registrationPage.find('#email-invalid-feedback').text()).toEqual(emptyFieldValidation.email); + expect(registrationPage.find('#password-invalid-feedback').text()).toEqual(emptyFieldValidation.password); + expect(registrationPage.find('#country-invalid-feedback').text()).toEqual(emptyFieldValidation.country); + + let alertBanner = 'We couldn\'t create your account.'; + Object.keys(emptyFieldValidation).forEach(key => { + alertBanner += emptyFieldValidation[key]; + }); + + expect(registrationPage.find('#validation-errors').first().text()).toEqual(alertBanner); + }); + + it('should show error message on 409 on alert and below the fields', () => { + const errors = { + email: 'It looks like test@gmail.com belongs to an existing account. Try again with a different email address.', + username: 'It looks like test belongs to an existing account. Try again with a different username.', + }; + store = mockStore({ + ...initialState, + register: { + ...initialState.register, + isSubmitted: true, + registrationError: { + email: [{ user_message: errors.email }], + username: [{ user_message: errors.username }], + }, + }, + }); + + const registrationPage = mount(reduxWrapper()); + expect(registrationPage.find('#email-invalid-feedback').text()).toEqual(errors.email); + expect(registrationPage.find('#username-invalid-feedback').text()).toEqual(errors.username); + expect(registrationPage.find('#validation-errors').first().text()).toEqual( + 'We couldn\'t create your account.'.concat(errors.email + errors.username), + ); + }); + + it('should submit form for valid input', () => { + const formPayload = { + name: 'John Doe', + username: 'john_doe', + email: 'john.doe@example.com', + password: 'password', + country: 'Pakistan', + gender: 'm', + }; + + store.dispatch = jest.fn(store.dispatch); + const registerPage = mount(reduxWrapper()); + + registerPage.find('input#name').simulate('change', { target: { value: formPayload.name, name: 'name' } }); + registerPage.find('input#username').simulate('change', { target: { value: formPayload.username, name: 'username' } }); + registerPage.find('input#email').simulate('change', { target: { value: formPayload.email, name: 'email' } }); + registerPage.find('input#password').simulate('change', { target: { value: formPayload.password, name: 'password' } }); + registerPage.find('select#country').simulate('change', { target: { value: formPayload.country, name: 'country' } }); + + // Send optional field + registerPage.find('input#optional').simulate('change', { target: { checked: true } }); + registerPage.find('select#gender').simulate('change', { target: { value: formPayload.gender, name: 'gender' } }); + + registerPage.find('button.btn-brand').simulate('click'); + expect(store.dispatch).not.toHaveBeenCalledWith(registerNewUser(formPayload)); + }); + it('should match default section snapshot', () => { const tree = renderer.create(reduxWrapper()); expect(tree.toJSON()).toMatchSnapshot(); @@ -441,8 +398,8 @@ describe('./RegistrationPage.js', () => { }, }); - const tree = renderer.create(reduxWrapper()).toJSON(); - expect(tree).toMatchSnapshot(); + const registrationPage = mount(reduxWrapper()); + expect(registrationPage.find('input#password').length).toEqual(0); }); it('tests shouldComponentUpdate with pipeline user data', () => { @@ -557,19 +514,6 @@ describe('./RegistrationPage.js', () => { }], }, }, - register: { - ...initialState.register, - formData: { - fields: [{ - label: 'I agree to the Your Platform Name Here Honor Code', - name: 'honor_code', - type: 'checkbox', - errorMessages: { - required: 'You must agree to the Your Platform Name Here Honor Code', - }, - }], - }, - }, }); delete window.location; @@ -593,8 +537,6 @@ describe('./RegistrationPage.js', () => { }, }); - delete window.location; - window.location = { href: getConfig().BASE_URL }; const expectedMessage = 'You\'ve successfully signed into Apple. We just need a little more information before ' + 'you start learning with openedX.'; @@ -603,8 +545,6 @@ describe('./RegistrationPage.js', () => { }); it('check cookie rendered', () => { - delete window.location; - window.location = { href: getConfig().BASE_URL }; const registerPage = mount(reduxWrapper()); expect(registerPage.find()).toBeTruthy(); }); diff --git a/src/register/tests/__snapshots__/RegistrationPage.test.jsx.snap b/src/register/tests/__snapshots__/RegistrationPage.test.jsx.snap index ddb495c2..329c1e96 100644 --- a/src/register/tests/__snapshots__/RegistrationPage.test.jsx.snap +++ b/src/register/tests/__snapshots__/RegistrationPage.test.jsx.snap @@ -1,186 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`./RegistrationPage.js should display no password field when current provider is present 1`] = ` -
-
-
- -

- Already have an edX account? - - Sign in. - -

-
-

- Create a new account -

-
-
- - - -
-
- - - -
-
- - - -
-
- - I agree to the Your Platform Name Here - - Honor Code - - - - You must agree to the Your Platform Name Here Honor Code - -
-
- - - -
- -
-
-
-
-`; - -exports[`./RegistrationPage.js should match TPA provider snapshot 1`] = ` +exports[`RegistrationPageTests should match TPA provider snapshot 1`] = `
@@ -293,35 +113,1330 @@ exports[`./RegistrationPage.js should match TPA provider snapshot 1`] = `
- + + - You must agree to the Your Platform Name Here Honor Code + Select your country or region of residence.
+
+ + By creating an account, you agree to the + + Terms of Service and Honor Code + + + + + + and you acknowledge that and each Member process your personal data in accordance with the + + Privacy Policy + + + + + + . + +
@@ -372,7 +1487,7 @@ exports[`./RegistrationPage.js should match TPA provider snapshot 1`] = ` className="mt-0 border-gray-200" /> or create an account using @@ -410,7 +1525,7 @@ exports[`./RegistrationPage.js should match TPA provider snapshot 1`] = `
`; -exports[`./RegistrationPage.js should match default section snapshot 1`] = ` +exports[`RegistrationPageTests should match default section snapshot 1`] = `
@@ -523,35 +1638,1330 @@ exports[`./RegistrationPage.js should match default section snapshot 1`] = `
- + + - You must agree to the Your Platform Name Here Honor Code + Select your country or region of residence.
+
+ + By creating an account, you agree to the + + Terms of Service and Honor Code + + + + + + and you acknowledge that and each Member process your personal data in accordance with the + + Privacy Policy + + + + + + . + +
@@ -601,7 +3011,7 @@ exports[`./RegistrationPage.js should match default section snapshot 1`] = `
`; -exports[`./RegistrationPage.js should match pending button state snapshot 1`] = ` +exports[`RegistrationPageTests should match pending button state snapshot 1`] = `
@@ -714,35 +3124,1330 @@ exports[`./RegistrationPage.js should match pending button state snapshot 1`] =
- + + - You must agree to the Your Platform Name Here Honor Code + Select your country or region of residence.
+
+ + By creating an account, you agree to the + + Terms of Service and Honor Code + + + + + + and you acknowledge that and each Member process your personal data in accordance with the + + Privacy Policy + + + + + + . + +