fix: Persist Register/Sign in form states on tab switch [VAN-618]
This commit is contained in:
@@ -19,7 +19,7 @@ import {
|
|||||||
} from '@edx/paragon';
|
} from '@edx/paragon';
|
||||||
import { ChevronLeft } from '@edx/paragon/icons';
|
import { ChevronLeft } from '@edx/paragon/icons';
|
||||||
|
|
||||||
import { forgotPassword } from './data/actions';
|
import { forgotPassword, setForgotPasswordFormData } from './data/actions';
|
||||||
import { forgotPasswordResultSelector } from './data/selectors';
|
import { forgotPasswordResultSelector } from './data/selectors';
|
||||||
|
|
||||||
import messages from './messages';
|
import messages from './messages';
|
||||||
@@ -34,7 +34,7 @@ const ForgotPasswordPage = (props) => {
|
|||||||
|
|
||||||
const platformName = getConfig().SITE_NAME;
|
const platformName = getConfig().SITE_NAME;
|
||||||
const regex = new RegExp(VALID_EMAIL_REGEX, 'i');
|
const regex = new RegExp(VALID_EMAIL_REGEX, 'i');
|
||||||
const [validationError, setValidationError] = useState('');
|
const [validationError, setValidationError] = useState(props.emailValidationError || '');
|
||||||
const [key, setKey] = useState('');
|
const [key, setKey] = useState('');
|
||||||
const supportUrl = getConfig().LOGIN_ISSUE_SUPPORT_LINK;
|
const supportUrl = getConfig().LOGIN_ISSUE_SUPPORT_LINK;
|
||||||
|
|
||||||
@@ -56,6 +56,11 @@ const ForgotPasswordPage = (props) => {
|
|||||||
return error;
|
return error;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const onBlur = (email) => {
|
||||||
|
const emailValidationError = getValidationMessage(email);
|
||||||
|
props.setForgotPasswordFormData({ email, emailValidationError });
|
||||||
|
};
|
||||||
|
|
||||||
const tabTitle = (
|
const tabTitle = (
|
||||||
<div className="d-flex">
|
<div className="d-flex">
|
||||||
<Icon src={ChevronLeft} className="arrow-back-icon" />
|
<Icon src={ChevronLeft} className="arrow-back-icon" />
|
||||||
@@ -74,7 +79,7 @@ const ForgotPasswordPage = (props) => {
|
|||||||
)}
|
)}
|
||||||
<div id="main-content" className="main-content">
|
<div id="main-content" className="main-content">
|
||||||
<Formik
|
<Formik
|
||||||
initialValues={{ email: '' }}
|
initialValues={{ email: props.email || '' }}
|
||||||
validateOnChange={false}
|
validateOnChange={false}
|
||||||
validate={(values) => {
|
validate={(values) => {
|
||||||
const validationMessage = getValidationMessage(values.email);
|
const validationMessage = getValidationMessage(values.email);
|
||||||
@@ -110,7 +115,7 @@ const ForgotPasswordPage = (props) => {
|
|||||||
name="email"
|
name="email"
|
||||||
errorMessage={validationError}
|
errorMessage={validationError}
|
||||||
value={values.email}
|
value={values.email}
|
||||||
handleBlur={() => getValidationMessage(values.email)}
|
handleBlur={() => onBlur(values.email)}
|
||||||
handleChange={e => setFieldValue('email', e.target.value)}
|
handleChange={e => setFieldValue('email', e.target.value)}
|
||||||
handleFocus={() => setValidationError('')}
|
handleFocus={() => setValidationError('')}
|
||||||
helpText={[intl.formatMessage(messages['forgot.password.email.help.text'], { platformName })]}
|
helpText={[intl.formatMessage(messages['forgot.password.email.help.text'], { platformName })]}
|
||||||
@@ -156,13 +161,16 @@ const ForgotPasswordPage = (props) => {
|
|||||||
ForgotPasswordPage.propTypes = {
|
ForgotPasswordPage.propTypes = {
|
||||||
intl: intlShape.isRequired,
|
intl: intlShape.isRequired,
|
||||||
email: PropTypes.string,
|
email: PropTypes.string,
|
||||||
|
emailValidationError: PropTypes.string,
|
||||||
forgotPassword: PropTypes.func.isRequired,
|
forgotPassword: PropTypes.func.isRequired,
|
||||||
|
setForgotPasswordFormData: PropTypes.func.isRequired,
|
||||||
status: PropTypes.string,
|
status: PropTypes.string,
|
||||||
submitState: PropTypes.string,
|
submitState: PropTypes.string,
|
||||||
};
|
};
|
||||||
|
|
||||||
ForgotPasswordPage.defaultProps = {
|
ForgotPasswordPage.defaultProps = {
|
||||||
email: '',
|
email: '',
|
||||||
|
emailValidationError: '',
|
||||||
status: null,
|
status: null,
|
||||||
submitState: DEFAULT_STATE,
|
submitState: DEFAULT_STATE,
|
||||||
};
|
};
|
||||||
@@ -171,5 +179,6 @@ export default connect(
|
|||||||
forgotPasswordResultSelector,
|
forgotPasswordResultSelector,
|
||||||
{
|
{
|
||||||
forgotPassword,
|
forgotPassword,
|
||||||
|
setForgotPasswordFormData,
|
||||||
},
|
},
|
||||||
)(injectIntl(ForgotPasswordPage));
|
)(injectIntl(ForgotPasswordPage));
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { AsyncActionType } from '../../data/utils';
|
import { AsyncActionType } from '../../data/utils';
|
||||||
|
|
||||||
export const FORGOT_PASSWORD = new AsyncActionType('FORGOT', 'PASSWORD');
|
export const FORGOT_PASSWORD = new AsyncActionType('FORGOT', 'PASSWORD');
|
||||||
|
export const FORGOT_PASSWORD_PERSIST_FORM_DATA = 'FORGOT_PASSWORD_PERSIST_FORM_DATA';
|
||||||
|
|
||||||
// Forgot Password
|
// Forgot Password
|
||||||
export const forgotPassword = email => ({
|
export const forgotPassword = email => ({
|
||||||
@@ -24,3 +25,8 @@ export const forgotPasswordForbidden = () => ({
|
|||||||
export const forgotPasswordServerError = () => ({
|
export const forgotPasswordServerError = () => ({
|
||||||
type: FORGOT_PASSWORD.FAILURE,
|
type: FORGOT_PASSWORD.FAILURE,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const setForgotPasswordFormData = (forgotPasswordFormData) => ({
|
||||||
|
type: FORGOT_PASSWORD_PERSIST_FORM_DATA,
|
||||||
|
payload: { forgotPasswordFormData },
|
||||||
|
});
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { FORGOT_PASSWORD } from './actions';
|
import { FORGOT_PASSWORD, FORGOT_PASSWORD_PERSIST_FORM_DATA } from './actions';
|
||||||
import { INTERNAL_SERVER_ERROR, PENDING_STATE } from '../../data/constants';
|
import { INTERNAL_SERVER_ERROR, PENDING_STATE } from '../../data/constants';
|
||||||
import { PASSWORD_RESET_FAILURE } from '../../reset-password/data/actions';
|
import { PASSWORD_RESET_FAILURE } from '../../reset-password/data/actions';
|
||||||
|
|
||||||
@@ -6,6 +6,7 @@ export const defaultState = {
|
|||||||
status: '',
|
status: '',
|
||||||
submitState: '',
|
submitState: '',
|
||||||
email: '',
|
email: '',
|
||||||
|
emailValidationError: '',
|
||||||
};
|
};
|
||||||
|
|
||||||
const reducer = (state = defaultState, action = null) => {
|
const reducer = (state = defaultState, action = null) => {
|
||||||
@@ -33,8 +34,20 @@ const reducer = (state = defaultState, action = null) => {
|
|||||||
return {
|
return {
|
||||||
status: action.payload.errorCode,
|
status: action.payload.errorCode,
|
||||||
};
|
};
|
||||||
|
case FORGOT_PASSWORD_PERSIST_FORM_DATA: {
|
||||||
|
const { forgotPasswordFormData } = action.payload;
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
email: forgotPasswordFormData.email,
|
||||||
|
emailValidationError: forgotPasswordFormData.emailValidationError,
|
||||||
|
};
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
return defaultState;
|
return {
|
||||||
|
...defaultState,
|
||||||
|
email: state.email,
|
||||||
|
emailValidationError: state.emailValidationError,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return state;
|
return state;
|
||||||
|
|||||||
34
src/forgot-password/data/tests/reducers.test.js
Normal file
34
src/forgot-password/data/tests/reducers.test.js
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
import reducer from '../reducers';
|
||||||
|
import {
|
||||||
|
FORGOT_PASSWORD_PERSIST_FORM_DATA,
|
||||||
|
} from '../actions';
|
||||||
|
|
||||||
|
describe('forgot password reducer', () => {
|
||||||
|
it('should set email and emailValidationError', () => {
|
||||||
|
const state = {
|
||||||
|
status: '',
|
||||||
|
submitState: '',
|
||||||
|
email: '',
|
||||||
|
emailValidationError: '',
|
||||||
|
};
|
||||||
|
const forgotPasswordFormData = {
|
||||||
|
email: 'test@gmail',
|
||||||
|
emailValidationError: 'Enter a valid email address',
|
||||||
|
};
|
||||||
|
const action = {
|
||||||
|
type: FORGOT_PASSWORD_PERSIST_FORM_DATA,
|
||||||
|
payload: { forgotPasswordFormData },
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(
|
||||||
|
reducer(state, action),
|
||||||
|
).toEqual(
|
||||||
|
{
|
||||||
|
status: '',
|
||||||
|
submitState: '',
|
||||||
|
email: 'test@gmail',
|
||||||
|
emailValidationError: 'Enter a valid email address',
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -10,7 +10,7 @@ const { loggingService } = initializeMockLogging();
|
|||||||
describe('handleForgotPassword', () => {
|
describe('handleForgotPassword', () => {
|
||||||
const params = {
|
const params = {
|
||||||
payload: {
|
payload: {
|
||||||
formData: {
|
forgotPasswordFormData: {
|
||||||
email: 'test@test.com',
|
email: 'test@test.com',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import * as auth from '@edx/frontend-platform/auth';
|
|||||||
import ForgotPasswordPage from '../ForgotPasswordPage';
|
import ForgotPasswordPage from '../ForgotPasswordPage';
|
||||||
import { INTERNAL_SERVER_ERROR, LOGIN_PAGE } from '../../data/constants';
|
import { INTERNAL_SERVER_ERROR, LOGIN_PAGE } from '../../data/constants';
|
||||||
import { PASSWORD_RESET } from '../../reset-password/data/constants';
|
import { PASSWORD_RESET } from '../../reset-password/data/constants';
|
||||||
|
import { setForgotPasswordFormData } from '../data/actions';
|
||||||
|
|
||||||
jest.mock('@edx/frontend-platform/analytics');
|
jest.mock('@edx/frontend-platform/analytics');
|
||||||
jest.mock('@edx/frontend-platform/auth');
|
jest.mock('@edx/frontend-platform/auth');
|
||||||
@@ -197,4 +198,24 @@ describe('ForgotPasswordPage', () => {
|
|||||||
forgotPasswordPage.update();
|
forgotPasswordPage.update();
|
||||||
expect(history.location.pathname).toEqual(LOGIN_PAGE);
|
expect(history.location.pathname).toEqual(LOGIN_PAGE);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// *********** persists form data on going back to sign in page ***********
|
||||||
|
|
||||||
|
it('should set form data in redux store on onBlur', () => {
|
||||||
|
const forgotPasswordFormData = {
|
||||||
|
email: 'test@gmail',
|
||||||
|
emailValidationError: 'Enter a valid email address',
|
||||||
|
};
|
||||||
|
|
||||||
|
props = {
|
||||||
|
...props,
|
||||||
|
email: 'test@gmail',
|
||||||
|
emailValidationError: '',
|
||||||
|
};
|
||||||
|
|
||||||
|
store.dispatch = jest.fn(store.dispatch);
|
||||||
|
const wrapper = mount(reduxWrapper(<IntlForgotPasswordPage {...props} />));
|
||||||
|
wrapper.find('input#email').simulate('blur');
|
||||||
|
expect(store.dispatch).toHaveBeenCalledWith(setForgotPasswordFormData(forgotPasswordFormData));
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -15,9 +15,11 @@ import {
|
|||||||
import { Institution } from '@edx/paragon/icons';
|
import { Institution } from '@edx/paragon/icons';
|
||||||
|
|
||||||
import AccountActivationMessage from './AccountActivationMessage';
|
import AccountActivationMessage from './AccountActivationMessage';
|
||||||
import { loginRequest, loginRequestFailure, loginRequestReset } from './data/actions';
|
import {
|
||||||
|
loginRequest, loginRequestFailure, loginRequestReset, setLoginFormData,
|
||||||
|
} from './data/actions';
|
||||||
import { INVALID_FORM } from './data/constants';
|
import { INVALID_FORM } from './data/constants';
|
||||||
import { loginErrorSelector, loginRequestSelector } from './data/selectors';
|
import { loginErrorSelector, loginFormDataSelector, loginRequestSelector } from './data/selectors';
|
||||||
import LoginFailureMessage from './LoginFailure';
|
import LoginFailureMessage from './LoginFailure';
|
||||||
import messages from './messages';
|
import messages from './messages';
|
||||||
|
|
||||||
@@ -40,20 +42,18 @@ import {
|
|||||||
getAllPossibleQueryParam,
|
getAllPossibleQueryParam,
|
||||||
updatePathWithQueryParams,
|
updatePathWithQueryParams,
|
||||||
} from '../data/utils';
|
} from '../data/utils';
|
||||||
import { forgotPasswordResultSelector } from '../forgot-password';
|
|
||||||
import ResetPasswordSuccess from '../reset-password/ResetPasswordSuccess';
|
import ResetPasswordSuccess from '../reset-password/ResetPasswordSuccess';
|
||||||
|
|
||||||
class LoginPage extends React.Component {
|
class LoginPage extends React.Component {
|
||||||
constructor(props, context) {
|
constructor(props, context) {
|
||||||
super(props, context);
|
super(props, context);
|
||||||
|
|
||||||
sendPageEvent('login_and_registration', 'login');
|
sendPageEvent('login_and_registration', 'login');
|
||||||
this.state = {
|
this.state = {
|
||||||
password: '',
|
password: this.props.loginFormData.password || '',
|
||||||
emailOrUsername: '',
|
emailOrUsername: this.props.loginFormData.emailOrUsername || '',
|
||||||
errors: {
|
errors: {
|
||||||
emailOrUsername: '',
|
emailOrUsername: this.props.loginFormData.errors.emailOrUsername || '',
|
||||||
password: '',
|
password: this.props.loginFormData.errors.password || '',
|
||||||
},
|
},
|
||||||
isSubmitted: false,
|
isSubmitted: false,
|
||||||
};
|
};
|
||||||
@@ -78,12 +78,18 @@ class LoginPage extends React.Component {
|
|||||||
handleSubmit = (e) => {
|
handleSubmit = (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
this.setState({ isSubmitted: true });
|
this.setState({ isSubmitted: true });
|
||||||
|
|
||||||
const { emailOrUsername, password } = this.state;
|
const { emailOrUsername, password } = this.state;
|
||||||
const emailValidationError = this.validateEmail(emailOrUsername);
|
const emailValidationError = this.validateEmail(emailOrUsername);
|
||||||
const passwordValidationError = this.validatePassword(password);
|
const passwordValidationError = this.validatePassword(password);
|
||||||
|
|
||||||
if (emailValidationError !== '' || passwordValidationError !== '') {
|
if (emailValidationError !== '' || passwordValidationError !== '') {
|
||||||
|
this.props.setLoginFormData({
|
||||||
|
...this.props.loginFormData,
|
||||||
|
errors: {
|
||||||
|
emailOrUsername: emailValidationError,
|
||||||
|
password: passwordValidationError,
|
||||||
|
},
|
||||||
|
});
|
||||||
this.props.loginRequestFailure({
|
this.props.loginRequestFailure({
|
||||||
errorCode: INVALID_FORM,
|
errorCode: INVALID_FORM,
|
||||||
});
|
});
|
||||||
@@ -99,7 +105,18 @@ class LoginPage extends React.Component {
|
|||||||
handleOnFocus = (e) => {
|
handleOnFocus = (e) => {
|
||||||
const { errors } = this.state;
|
const { errors } = this.state;
|
||||||
errors[e.target.name] = '';
|
errors[e.target.name] = '';
|
||||||
this.setState({ errors });
|
this.props.setLoginFormData({
|
||||||
|
...this.props.loginFormData,
|
||||||
|
errors,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
handleOnBlur = (e) => {
|
||||||
|
const payload = {
|
||||||
|
...this.props.loginFormData,
|
||||||
|
[e.target.name]: e.target.value,
|
||||||
|
};
|
||||||
|
this.props.setLoginFormData(payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleForgotPasswordLinkClickEvent = () => {
|
handleForgotPasswordLinkClickEvent = () => {
|
||||||
@@ -116,7 +133,6 @@ class LoginPage extends React.Component {
|
|||||||
} else {
|
} else {
|
||||||
errors.emailOrUsername = '';
|
errors.emailOrUsername = '';
|
||||||
}
|
}
|
||||||
this.setState({ errors });
|
|
||||||
return errors.emailOrUsername;
|
return errors.emailOrUsername;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -124,7 +140,6 @@ class LoginPage extends React.Component {
|
|||||||
const { errors } = this.state;
|
const { errors } = this.state;
|
||||||
errors.password = password.length > 0 ? '' : this.props.intl.formatMessage(messages['password.validation.message']);
|
errors.password = password.length > 0 ? '' : this.props.intl.formatMessage(messages['password.validation.message']);
|
||||||
|
|
||||||
this.setState({ errors });
|
|
||||||
return errors.password;
|
return errors.password;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -230,6 +245,7 @@ class LoginPage extends React.Component {
|
|||||||
value={this.state.emailOrUsername}
|
value={this.state.emailOrUsername}
|
||||||
handleChange={(e) => this.setState({ emailOrUsername: e.target.value, isSubmitted: false })}
|
handleChange={(e) => this.setState({ emailOrUsername: e.target.value, isSubmitted: false })}
|
||||||
handleFocus={this.handleOnFocus}
|
handleFocus={this.handleOnFocus}
|
||||||
|
handleBlur={this.handleOnBlur}
|
||||||
errorMessage={this.state.errors.emailOrUsername}
|
errorMessage={this.state.errors.emailOrUsername}
|
||||||
floatingLabel={intl.formatMessage(messages['login.user.identity.label'])}
|
floatingLabel={intl.formatMessage(messages['login.user.identity.label'])}
|
||||||
/>
|
/>
|
||||||
@@ -239,6 +255,7 @@ class LoginPage extends React.Component {
|
|||||||
showRequirements={false}
|
showRequirements={false}
|
||||||
handleChange={(e) => this.setState({ password: e.target.value, isSubmitted: false })}
|
handleChange={(e) => this.setState({ password: e.target.value, isSubmitted: false })}
|
||||||
handleFocus={this.handleOnFocus}
|
handleFocus={this.handleOnFocus}
|
||||||
|
handleBlur={this.handleOnBlur}
|
||||||
errorMessage={this.state.errors.password}
|
errorMessage={this.state.errors.password}
|
||||||
floatingLabel={intl.formatMessage(messages['login.password.label'])}
|
floatingLabel={intl.formatMessage(messages['login.password.label'])}
|
||||||
/>
|
/>
|
||||||
@@ -310,9 +327,16 @@ class LoginPage extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
LoginPage.defaultProps = {
|
LoginPage.defaultProps = {
|
||||||
forgotPassword: null,
|
|
||||||
loginResult: null,
|
loginResult: null,
|
||||||
loginError: null,
|
loginError: null,
|
||||||
|
loginFormData: {
|
||||||
|
emailOrUsername: '',
|
||||||
|
password: '',
|
||||||
|
errors: {
|
||||||
|
emailOrUsername: '',
|
||||||
|
password: '',
|
||||||
|
},
|
||||||
|
},
|
||||||
resetPassword: false,
|
resetPassword: false,
|
||||||
submitState: DEFAULT_STATE,
|
submitState: DEFAULT_STATE,
|
||||||
thirdPartyAuthApiStatus: 'pending',
|
thirdPartyAuthApiStatus: 'pending',
|
||||||
@@ -325,20 +349,25 @@ LoginPage.defaultProps = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
LoginPage.propTypes = {
|
LoginPage.propTypes = {
|
||||||
forgotPassword: PropTypes.shape({
|
|
||||||
email: PropTypes.string,
|
|
||||||
status: PropTypes.string,
|
|
||||||
}),
|
|
||||||
getThirdPartyAuthContext: PropTypes.func.isRequired,
|
getThirdPartyAuthContext: PropTypes.func.isRequired,
|
||||||
intl: intlShape.isRequired,
|
intl: intlShape.isRequired,
|
||||||
loginError: PropTypes.objectOf(PropTypes.any),
|
loginError: PropTypes.objectOf(PropTypes.any),
|
||||||
loginRequest: PropTypes.func.isRequired,
|
loginRequest: PropTypes.func.isRequired,
|
||||||
loginRequestFailure: PropTypes.func.isRequired,
|
loginRequestFailure: PropTypes.func.isRequired,
|
||||||
loginRequestReset: PropTypes.func.isRequired,
|
loginRequestReset: PropTypes.func.isRequired,
|
||||||
|
setLoginFormData: PropTypes.func.isRequired,
|
||||||
loginResult: PropTypes.shape({
|
loginResult: PropTypes.shape({
|
||||||
redirectUrl: PropTypes.string,
|
redirectUrl: PropTypes.string,
|
||||||
success: PropTypes.bool,
|
success: PropTypes.bool,
|
||||||
}),
|
}),
|
||||||
|
loginFormData: PropTypes.shape({
|
||||||
|
emailOrUsername: PropTypes.string,
|
||||||
|
password: PropTypes.string,
|
||||||
|
errors: PropTypes.shape({
|
||||||
|
emailOrUsername: PropTypes.string,
|
||||||
|
password: PropTypes.string,
|
||||||
|
}),
|
||||||
|
}),
|
||||||
resetPassword: PropTypes.bool,
|
resetPassword: PropTypes.bool,
|
||||||
submitState: PropTypes.string,
|
submitState: PropTypes.string,
|
||||||
thirdPartyAuthApiStatus: PropTypes.string,
|
thirdPartyAuthApiStatus: PropTypes.string,
|
||||||
@@ -354,17 +383,17 @@ LoginPage.propTypes = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const mapStateToProps = state => {
|
const mapStateToProps = state => {
|
||||||
const forgotPassword = forgotPasswordResultSelector(state);
|
|
||||||
const loginResult = loginRequestSelector(state);
|
const loginResult = loginRequestSelector(state);
|
||||||
const thirdPartyAuthContext = thirdPartyAuthContextSelector(state);
|
const thirdPartyAuthContext = thirdPartyAuthContextSelector(state);
|
||||||
const loginError = loginErrorSelector(state);
|
const loginError = loginErrorSelector(state);
|
||||||
|
const loginFormData = loginFormDataSelector(state);
|
||||||
return {
|
return {
|
||||||
submitState: state.login.submitState,
|
submitState: state.login.submitState,
|
||||||
thirdPartyAuthApiStatus: state.commonComponents.thirdPartyAuthApiStatus,
|
thirdPartyAuthApiStatus: state.commonComponents.thirdPartyAuthApiStatus,
|
||||||
forgotPassword,
|
|
||||||
loginError,
|
loginError,
|
||||||
loginResult,
|
loginResult,
|
||||||
thirdPartyAuthContext,
|
thirdPartyAuthContext,
|
||||||
|
loginFormData,
|
||||||
resetPassword: state.login.resetPassword,
|
resetPassword: state.login.resetPassword,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@@ -376,5 +405,6 @@ export default connect(
|
|||||||
loginRequest,
|
loginRequest,
|
||||||
loginRequestFailure,
|
loginRequestFailure,
|
||||||
loginRequestReset,
|
loginRequestReset,
|
||||||
|
setLoginFormData,
|
||||||
},
|
},
|
||||||
)(injectIntl(LoginPage));
|
)(injectIntl(LoginPage));
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { AsyncActionType } from '../../data/utils';
|
import { AsyncActionType } from '../../data/utils';
|
||||||
|
|
||||||
export const LOGIN_REQUEST = new AsyncActionType('LOGIN', 'REQUEST');
|
export const LOGIN_REQUEST = new AsyncActionType('LOGIN', 'REQUEST');
|
||||||
|
export const LOGIN_PERSIST_FORM_DATA = 'LOGIN_PERSIST_FORM_DATA';
|
||||||
|
|
||||||
// Login
|
// Login
|
||||||
export const loginRequest = creds => ({
|
export const loginRequest = creds => ({
|
||||||
@@ -25,3 +26,8 @@ export const loginRequestFailure = (loginError) => ({
|
|||||||
export const loginRequestReset = () => ({
|
export const loginRequestReset = () => ({
|
||||||
type: LOGIN_REQUEST.RESET,
|
type: LOGIN_REQUEST.RESET,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const setLoginFormData = (loginFormData) => ({
|
||||||
|
type: LOGIN_PERSIST_FORM_DATA,
|
||||||
|
payload: { loginFormData },
|
||||||
|
});
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { LOGIN_REQUEST } from './actions';
|
import { LOGIN_REQUEST, LOGIN_PERSIST_FORM_DATA } from './actions';
|
||||||
|
|
||||||
import { DEFAULT_STATE, PENDING_STATE } from '../../data/constants';
|
import { DEFAULT_STATE, PENDING_STATE } from '../../data/constants';
|
||||||
import { RESET_PASSWORD } from '../../reset-password';
|
import { RESET_PASSWORD } from '../../reset-password';
|
||||||
@@ -7,6 +7,14 @@ export const defaultState = {
|
|||||||
loginError: null,
|
loginError: null,
|
||||||
loginResult: {},
|
loginResult: {},
|
||||||
resetPassword: false,
|
resetPassword: false,
|
||||||
|
loginFormData: {
|
||||||
|
password: '',
|
||||||
|
emailOrUsername: '',
|
||||||
|
errors: {
|
||||||
|
emailOrUsername: '',
|
||||||
|
password: '',
|
||||||
|
},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const reducer = (state = defaultState, action) => {
|
const reducer = (state = defaultState, action) => {
|
||||||
@@ -38,6 +46,13 @@ const reducer = (state = defaultState, action) => {
|
|||||||
...state,
|
...state,
|
||||||
resetPassword: true,
|
resetPassword: true,
|
||||||
};
|
};
|
||||||
|
case LOGIN_PERSIST_FORM_DATA: {
|
||||||
|
const { loginFormData } = action.payload;
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
loginFormData,
|
||||||
|
};
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
|
|||||||
@@ -13,3 +13,8 @@ export const loginErrorSelector = createSelector(
|
|||||||
loginSelector,
|
loginSelector,
|
||||||
login => login.loginError,
|
login => login.loginError,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export const loginFormDataSelector = createSelector(
|
||||||
|
loginSelector,
|
||||||
|
login => login.loginFormData,
|
||||||
|
);
|
||||||
|
|||||||
46
src/login/data/tests/reducers.test.js
Normal file
46
src/login/data/tests/reducers.test.js
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
import reducer from '../reducers';
|
||||||
|
import {
|
||||||
|
LOGIN_PERSIST_FORM_DATA,
|
||||||
|
} from '../actions';
|
||||||
|
|
||||||
|
describe('login reducer', () => {
|
||||||
|
it('should set registrationFormData', () => {
|
||||||
|
const state = {
|
||||||
|
loginFormData: {
|
||||||
|
password: '',
|
||||||
|
emailOrUsername: '',
|
||||||
|
errors: {
|
||||||
|
emailOrUsername: '',
|
||||||
|
password: '',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const loginFormData = {
|
||||||
|
password: 'johndoe',
|
||||||
|
emailOrUsername: 'john@gmail.com',
|
||||||
|
errors: {
|
||||||
|
emailOrUsername: '',
|
||||||
|
password: '',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const action = {
|
||||||
|
type: LOGIN_PERSIST_FORM_DATA,
|
||||||
|
payload: { loginFormData },
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(
|
||||||
|
reducer(state, action),
|
||||||
|
).toEqual(
|
||||||
|
{
|
||||||
|
loginFormData: {
|
||||||
|
password: 'johndoe',
|
||||||
|
emailOrUsername: 'john@gmail.com',
|
||||||
|
errors: {
|
||||||
|
emailOrUsername: '',
|
||||||
|
password: '',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -13,7 +13,7 @@ const { loggingService } = initializeMockLogging();
|
|||||||
describe('handleLoginRequest', () => {
|
describe('handleLoginRequest', () => {
|
||||||
const params = {
|
const params = {
|
||||||
payload: {
|
payload: {
|
||||||
formData: {
|
loginFormData: {
|
||||||
email: 'test@test.com',
|
email: 'test@test.com',
|
||||||
password: 'test-password',
|
password: 'test-password',
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -12,7 +12,9 @@ import * as analytics from '@edx/frontend-platform/analytics';
|
|||||||
import * as auth from '@edx/frontend-platform/auth';
|
import * as auth from '@edx/frontend-platform/auth';
|
||||||
import { IntlProvider, injectIntl } from '@edx/frontend-platform/i18n';
|
import { IntlProvider, injectIntl } from '@edx/frontend-platform/i18n';
|
||||||
|
|
||||||
import { loginRequest, loginRequestFailure, loginRequestReset } from '../data/actions';
|
import {
|
||||||
|
loginRequest, loginRequestFailure, loginRequestReset, setLoginFormData,
|
||||||
|
} from '../data/actions';
|
||||||
import LoginFailureMessage from '../LoginFailure';
|
import LoginFailureMessage from '../LoginFailure';
|
||||||
import LoginPage from '../LoginPage';
|
import LoginPage from '../LoginPage';
|
||||||
|
|
||||||
@@ -35,6 +37,7 @@ describe('LoginPage', () => {
|
|||||||
});
|
});
|
||||||
let props = {};
|
let props = {};
|
||||||
let store = {};
|
let store = {};
|
||||||
|
let loginFormData = {};
|
||||||
|
|
||||||
const reduxWrapper = children => (
|
const reduxWrapper = children => (
|
||||||
<IntlProvider locale="en">
|
<IntlProvider locale="en">
|
||||||
@@ -82,6 +85,14 @@ describe('LoginPage', () => {
|
|||||||
handleInstitutionLogin: jest.fn(),
|
handleInstitutionLogin: jest.fn(),
|
||||||
institutionLogin: false,
|
institutionLogin: false,
|
||||||
};
|
};
|
||||||
|
loginFormData = {
|
||||||
|
emailOrUsername: '',
|
||||||
|
password: '',
|
||||||
|
errors: {
|
||||||
|
emailOrUsername: '',
|
||||||
|
password: '',
|
||||||
|
},
|
||||||
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
// ******** test login form submission ********
|
// ******** test login form submission ********
|
||||||
@@ -465,4 +476,42 @@ describe('LoginPage', () => {
|
|||||||
expect(store.dispatch).toHaveBeenCalledWith(loginRequestReset());
|
expect(store.dispatch).toHaveBeenCalledWith(loginRequestReset());
|
||||||
expect(loginPage.state('errors')).toEqual(errorState);
|
expect(loginPage.state('errors')).toEqual(errorState);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// persists form data tests
|
||||||
|
|
||||||
|
it('should set errors in redux store on submit form for invalid input', () => {
|
||||||
|
loginFormData = {
|
||||||
|
...loginFormData,
|
||||||
|
errors: {
|
||||||
|
emailOrUsername: 'Enter your username or email',
|
||||||
|
password: 'Enter your password',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
store.dispatch = jest.fn(store.dispatch);
|
||||||
|
const loginPage = mount(reduxWrapper(<IntlLoginPage {...props} />));
|
||||||
|
|
||||||
|
loginPage.find('input#emailOrUsername').simulate('change', { target: { value: '' } });
|
||||||
|
loginPage.find('input#password').simulate('change', { target: { value: '' } });
|
||||||
|
loginPage.find('button.btn-brand').simulate('click');
|
||||||
|
|
||||||
|
expect(store.dispatch).toHaveBeenCalledWith(setLoginFormData(loginFormData));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should set form data in redux store on onBlur', () => {
|
||||||
|
store.dispatch = jest.fn(store.dispatch);
|
||||||
|
|
||||||
|
const loginPage = mount(reduxWrapper(<IntlLoginPage {...props} />));
|
||||||
|
loginPage.find('input#emailOrUsername').simulate('blur');
|
||||||
|
|
||||||
|
expect(store.dispatch).toHaveBeenCalledWith(setLoginFormData(loginFormData));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should clear form field errors in redux store on onFocus', () => {
|
||||||
|
store.dispatch = jest.fn(store.dispatch);
|
||||||
|
|
||||||
|
const loginPage = mount(reduxWrapper(<IntlLoginPage {...props} />));
|
||||||
|
loginPage.find('input#emailOrUsername').simulate('focus');
|
||||||
|
|
||||||
|
expect(store.dispatch).toHaveBeenCalledWith(setLoginFormData(loginFormData));
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ import {
|
|||||||
import { Error, Close } from '@edx/paragon/icons';
|
import { Error, Close } from '@edx/paragon/icons';
|
||||||
import FormFieldRenderer from '../field-renderer';
|
import FormFieldRenderer from '../field-renderer';
|
||||||
import {
|
import {
|
||||||
clearUsernameSuggestions, registerNewUser, resetRegistrationForm, fetchRealtimeValidations,
|
clearUsernameSuggestions, registerNewUser, resetRegistrationForm, fetchRealtimeValidations, setRegistrationFormData,
|
||||||
} from './data/actions';
|
} from './data/actions';
|
||||||
import {
|
import {
|
||||||
FIELDS, FORM_SUBMISSION_ERROR, DEFAULT_SERVICE_PROVIDER_DOMAINS, DEFAULT_TOP_LEVEL_DOMAINS, COMMON_EMAIL_PROVIDERS,
|
FIELDS, FORM_SUBMISSION_ERROR, DEFAULT_SERVICE_PROVIDER_DOMAINS, DEFAULT_TOP_LEVEL_DOMAINS, COMMON_EMAIL_PROVIDERS,
|
||||||
@@ -26,6 +26,7 @@ import {
|
|||||||
registrationRequestSelector,
|
registrationRequestSelector,
|
||||||
validationsSelector,
|
validationsSelector,
|
||||||
usernameSuggestionsSelector,
|
usernameSuggestionsSelector,
|
||||||
|
registrationFormDataSelector,
|
||||||
} from './data/selectors';
|
} from './data/selectors';
|
||||||
import messages from './messages';
|
import messages from './messages';
|
||||||
import RegistrationFailure from './RegistrationFailure';
|
import RegistrationFailure from './RegistrationFailure';
|
||||||
@@ -66,20 +67,21 @@ class RegistrationPage extends React.Component {
|
|||||||
this.tpaHint = getTpaHint();
|
this.tpaHint = getTpaHint();
|
||||||
this.state = {
|
this.state = {
|
||||||
country: '',
|
country: '',
|
||||||
email: '',
|
email: this.props.registrationFormData.email || '',
|
||||||
name: '',
|
name: this.props.registrationFormData.name || '',
|
||||||
password: '',
|
password: this.props.registrationFormData.password || '',
|
||||||
username: '',
|
username: this.props.registrationFormData.username || '',
|
||||||
marketingOptIn: true,
|
marketingOptIn: this.props.registrationFormData.marketingOptIn || true,
|
||||||
errors: {
|
errors: {
|
||||||
email: '',
|
email: this.props.registrationFormData.errors.email || '',
|
||||||
name: '',
|
name: this.props.registrationFormData.errors.name || '',
|
||||||
username: '',
|
username: this.props.registrationFormData.errors.username || '',
|
||||||
password: '',
|
password: this.props.registrationFormData.errors.password || '',
|
||||||
country: '',
|
country: '',
|
||||||
},
|
},
|
||||||
emailErrorSuggestion: null,
|
emailFieldBorderClass: this.props.registrationFormData.emailFieldBorderClass || '',
|
||||||
emailWarningSuggestion: null,
|
emailErrorSuggestion: this.props.registrationFormData.emailErrorSuggestion || null,
|
||||||
|
emailWarningSuggestion: this.props.registrationFormData.emailWarningSuggestion || null,
|
||||||
errorCode: null,
|
errorCode: null,
|
||||||
failureCount: 0,
|
failureCount: 0,
|
||||||
startTime: Date.now(),
|
startTime: Date.now(),
|
||||||
@@ -119,6 +121,12 @@ class RegistrationPage extends React.Component {
|
|||||||
});
|
});
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (this.props.registrationFormData !== nextProps.registrationFormData) {
|
||||||
|
const state = { ...this.state, ...nextProps.registrationFormData };
|
||||||
|
this.setState({
|
||||||
|
...state,
|
||||||
|
});
|
||||||
|
}
|
||||||
if (this.props.validationDecisions !== nextProps.validationDecisions) {
|
if (this.props.validationDecisions !== nextProps.validationDecisions) {
|
||||||
const state = { errors: { ...this.state.errors, ...nextProps.validationDecisions } };
|
const state = { errors: { ...this.state.errors, ...nextProps.validationDecisions } };
|
||||||
let validatePassword = false;
|
let validatePassword = false;
|
||||||
@@ -287,24 +295,27 @@ class RegistrationPage extends React.Component {
|
|||||||
handleOnFocus = (e) => {
|
handleOnFocus = (e) => {
|
||||||
const { errors } = this.state;
|
const { errors } = this.state;
|
||||||
errors[e.target.name] = '';
|
errors[e.target.name] = '';
|
||||||
const state = { errors };
|
|
||||||
if (e.target.name === 'username') {
|
if (e.target.name === 'username') {
|
||||||
this.props.clearUsernameSuggestions();
|
this.props.clearUsernameSuggestions();
|
||||||
}
|
}
|
||||||
if (e.target.name === 'country') {
|
if (e.target.name === 'country') {
|
||||||
state.readOnly = false;
|
this.setState({ readOnly: false });
|
||||||
}
|
}
|
||||||
if (e.target.name === 'passwordValidation') {
|
if (e.target.name === 'passwordValidation') {
|
||||||
state.errors.password = '';
|
errors.password = '';
|
||||||
}
|
}
|
||||||
this.setState({ ...state });
|
this.props.setRegistrationFormData({
|
||||||
|
...this.props.registrationFormData,
|
||||||
|
errors,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
handleSuggestionClick = (e, suggestion) => {
|
handleSuggestionClick = (e, suggestion) => {
|
||||||
const { errors } = this.state;
|
const { errors } = this.state;
|
||||||
if (e.target.name === 'username') {
|
if (e.target.name === 'username') {
|
||||||
errors.username = '';
|
errors.username = '';
|
||||||
this.setState({
|
this.props.setRegistrationFormData({
|
||||||
|
...this.props.registrationFormData,
|
||||||
username: suggestion,
|
username: suggestion,
|
||||||
errors,
|
errors,
|
||||||
});
|
});
|
||||||
@@ -312,18 +323,20 @@ class RegistrationPage extends React.Component {
|
|||||||
} else if (e.target.name === 'email') {
|
} else if (e.target.name === 'email') {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
errors.email = '';
|
errors.email = '';
|
||||||
this.setState({
|
this.props.setRegistrationFormData({
|
||||||
borderClass: '',
|
...this.props.registrationFormData,
|
||||||
email: suggestion,
|
email: suggestion,
|
||||||
emailErrorSuggestion: null,
|
emailErrorSuggestion: null,
|
||||||
emailWarningSuggestion: null,
|
emailWarningSuggestion: null,
|
||||||
|
emailFieldBorderClass: '',
|
||||||
errors,
|
errors,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleUsernameSuggestionClose = () => {
|
handleUsernameSuggestionClose = () => {
|
||||||
this.setState({
|
this.props.setRegistrationFormData({
|
||||||
|
...this.props.registrationFormData,
|
||||||
username: '',
|
username: '',
|
||||||
});
|
});
|
||||||
this.props.clearUsernameSuggestions();
|
this.props.clearUsernameSuggestions();
|
||||||
@@ -352,11 +365,15 @@ class RegistrationPage extends React.Component {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
this.setState({ errors });
|
this.props.setRegistrationFormData({
|
||||||
|
...this.props.registrationFormData,
|
||||||
|
errors,
|
||||||
|
});
|
||||||
return isValid;
|
return isValid;
|
||||||
}
|
}
|
||||||
|
|
||||||
validateInput(fieldName, value, payload) {
|
validateInput(fieldName, value, payload) {
|
||||||
|
let state = {};
|
||||||
const { errors } = this.state;
|
const { errors } = this.state;
|
||||||
const { intl, statusCode } = this.props;
|
const { intl, statusCode } = this.props;
|
||||||
const emailRegex = new RegExp(VALID_EMAIL_REGEX, 'i');
|
const emailRegex = new RegExp(VALID_EMAIL_REGEX, 'i');
|
||||||
@@ -409,11 +426,11 @@ class RegistrationPage extends React.Component {
|
|||||||
errors.email = '';
|
errors.email = '';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.setState({
|
state = {
|
||||||
emailWarningSuggestion,
|
emailWarningSuggestion,
|
||||||
emailErrorSuggestion,
|
emailErrorSuggestion,
|
||||||
borderClass: emailWarningSuggestion ? 'yellow-border' : null,
|
emailFieldBorderClass: emailWarningSuggestion ? 'yellow-border' : null,
|
||||||
});
|
};
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'name':
|
case 'name':
|
||||||
@@ -467,12 +484,21 @@ class RegistrationPage extends React.Component {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setState({ errors });
|
this.props.setRegistrationFormData({
|
||||||
|
...this.props.registrationFormData,
|
||||||
|
...state,
|
||||||
|
[fieldName]: value,
|
||||||
|
errors,
|
||||||
|
});
|
||||||
|
|
||||||
return errors;
|
return errors;
|
||||||
}
|
}
|
||||||
|
|
||||||
handleOnClose() {
|
handleOnClose() {
|
||||||
this.setState({ emailErrorSuggestion: null });
|
this.props.setRegistrationFormData({
|
||||||
|
...this.props.registrationFormData,
|
||||||
|
emailErrorSuggestion: null,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
renderEmailFeedback() {
|
renderEmailFeedback() {
|
||||||
@@ -695,7 +721,7 @@ class RegistrationPage extends React.Component {
|
|||||||
handleFocus={this.handleOnFocus}
|
handleFocus={this.handleOnFocus}
|
||||||
helpText={[intl.formatMessage(messages['help.text.email'])]}
|
helpText={[intl.formatMessage(messages['help.text.email'])]}
|
||||||
floatingLabel={intl.formatMessage(messages['registration.email.label'])}
|
floatingLabel={intl.formatMessage(messages['registration.email.label'])}
|
||||||
borderClass={this.state.borderClass}
|
borderClass={this.state.emailFieldBorderClass}
|
||||||
>
|
>
|
||||||
{this.renderEmailFeedback()}
|
{this.renderEmailFeedback()}
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
@@ -845,6 +871,24 @@ RegistrationPage.defaultProps = {
|
|||||||
secondaryProviders: [],
|
secondaryProviders: [],
|
||||||
pipelineUserDetails: null,
|
pipelineUserDetails: null,
|
||||||
},
|
},
|
||||||
|
registrationFormData: {
|
||||||
|
country: '',
|
||||||
|
email: '',
|
||||||
|
name: '',
|
||||||
|
password: '',
|
||||||
|
username: '',
|
||||||
|
marketingOptIn: true,
|
||||||
|
errors: {
|
||||||
|
email: '',
|
||||||
|
name: '',
|
||||||
|
username: '',
|
||||||
|
password: '',
|
||||||
|
country: '',
|
||||||
|
},
|
||||||
|
emailFieldBorderClass: '',
|
||||||
|
emailErrorSuggestion: null,
|
||||||
|
emailWarningSuggestion: null,
|
||||||
|
},
|
||||||
validationDecisions: null,
|
validationDecisions: null,
|
||||||
statusCode: null,
|
statusCode: null,
|
||||||
usernameSuggestions: [],
|
usernameSuggestions: [],
|
||||||
@@ -857,6 +901,7 @@ RegistrationPage.propTypes = {
|
|||||||
getThirdPartyAuthContext: PropTypes.func.isRequired,
|
getThirdPartyAuthContext: PropTypes.func.isRequired,
|
||||||
registerNewUser: PropTypes.func,
|
registerNewUser: PropTypes.func,
|
||||||
resetRegistrationForm: PropTypes.func.isRequired,
|
resetRegistrationForm: PropTypes.func.isRequired,
|
||||||
|
setRegistrationFormData: PropTypes.func.isRequired,
|
||||||
registrationResult: PropTypes.shape({
|
registrationResult: PropTypes.shape({
|
||||||
redirectUrl: PropTypes.string,
|
redirectUrl: PropTypes.string,
|
||||||
success: PropTypes.bool,
|
success: PropTypes.bool,
|
||||||
@@ -864,6 +909,24 @@ RegistrationPage.propTypes = {
|
|||||||
registrationErrorCode: PropTypes.string,
|
registrationErrorCode: PropTypes.string,
|
||||||
submitState: PropTypes.string,
|
submitState: PropTypes.string,
|
||||||
thirdPartyAuthApiStatus: PropTypes.string,
|
thirdPartyAuthApiStatus: PropTypes.string,
|
||||||
|
registrationFormData: PropTypes.shape({
|
||||||
|
country: PropTypes.string,
|
||||||
|
email: PropTypes.string,
|
||||||
|
name: PropTypes.string,
|
||||||
|
password: PropTypes.string,
|
||||||
|
username: PropTypes.string,
|
||||||
|
marketingOptIn: PropTypes.bool,
|
||||||
|
errors: PropTypes.shape({
|
||||||
|
email: PropTypes.string,
|
||||||
|
name: PropTypes.string,
|
||||||
|
username: PropTypes.string,
|
||||||
|
password: PropTypes.string,
|
||||||
|
country: PropTypes.string,
|
||||||
|
}),
|
||||||
|
emailFieldBorderClass: PropTypes.string,
|
||||||
|
emailErrorSuggestion: PropTypes.string,
|
||||||
|
emailWarningSuggestion: PropTypes.string,
|
||||||
|
}),
|
||||||
thirdPartyAuthContext: PropTypes.shape({
|
thirdPartyAuthContext: PropTypes.shape({
|
||||||
currentProvider: PropTypes.string,
|
currentProvider: PropTypes.string,
|
||||||
platformName: PropTypes.string,
|
platformName: PropTypes.string,
|
||||||
@@ -906,6 +969,7 @@ const mapStateToProps = state => {
|
|||||||
validationDecisions: validationsSelector(state),
|
validationDecisions: validationsSelector(state),
|
||||||
statusCode: state.register.statusCode,
|
statusCode: state.register.statusCode,
|
||||||
usernameSuggestions: usernameSuggestionsSelector(state),
|
usernameSuggestions: usernameSuggestionsSelector(state),
|
||||||
|
registrationFormData: registrationFormDataSelector(state),
|
||||||
fieldDescriptions: fieldDescriptionSelector(state),
|
fieldDescriptions: fieldDescriptionSelector(state),
|
||||||
extendedProfile: extendedProfileSelector(state),
|
extendedProfile: extendedProfileSelector(state),
|
||||||
};
|
};
|
||||||
@@ -919,5 +983,6 @@ export default connect(
|
|||||||
fetchRealtimeValidations,
|
fetchRealtimeValidations,
|
||||||
registerNewUser,
|
registerNewUser,
|
||||||
resetRegistrationForm,
|
resetRegistrationForm,
|
||||||
|
setRegistrationFormData,
|
||||||
},
|
},
|
||||||
)(injectIntl(RegistrationPage));
|
)(injectIntl(RegistrationPage));
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ export const REGISTER_NEW_USER = new AsyncActionType('REGISTRATION', 'REGISTER_N
|
|||||||
export const REGISTER_FORM_VALIDATIONS = new AsyncActionType('REGISTRATION', 'GET_FORM_VALIDATIONS');
|
export const REGISTER_FORM_VALIDATIONS = new AsyncActionType('REGISTRATION', 'GET_FORM_VALIDATIONS');
|
||||||
export const REGISTRATION_FORM = new AsyncActionType('REGISTRATION', 'REGISTRATION_FORM');
|
export const REGISTRATION_FORM = new AsyncActionType('REGISTRATION', 'REGISTRATION_FORM');
|
||||||
export const REGISTER_CLEAR_USERNAME_SUGGESTIONS = 'REGISTRATION_CLEAR_USERNAME_SUGGESTIONS';
|
export const REGISTER_CLEAR_USERNAME_SUGGESTIONS = 'REGISTRATION_CLEAR_USERNAME_SUGGESTIONS';
|
||||||
|
export const REGISTER_PERSIST_FORM_DATA = 'REGISTER_PERSIST_FORM_DATA';
|
||||||
|
|
||||||
// Reset Form
|
// Reset Form
|
||||||
export const resetRegistrationForm = () => ({
|
export const resetRegistrationForm = () => ({
|
||||||
@@ -53,3 +54,8 @@ export const fetchRealtimeValidationsFailure = () => ({
|
|||||||
export const clearUsernameSuggestions = () => ({
|
export const clearUsernameSuggestions = () => ({
|
||||||
type: REGISTER_CLEAR_USERNAME_SUGGESTIONS,
|
type: REGISTER_CLEAR_USERNAME_SUGGESTIONS,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const setRegistrationFormData = (registrationFormData) => ({
|
||||||
|
type: REGISTER_PERSIST_FORM_DATA,
|
||||||
|
payload: { registrationFormData },
|
||||||
|
});
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import {
|
|||||||
REGISTER_NEW_USER,
|
REGISTER_NEW_USER,
|
||||||
REGISTER_FORM_VALIDATIONS,
|
REGISTER_FORM_VALIDATIONS,
|
||||||
REGISTER_CLEAR_USERNAME_SUGGESTIONS,
|
REGISTER_CLEAR_USERNAME_SUGGESTIONS,
|
||||||
|
REGISTER_PERSIST_FORM_DATA,
|
||||||
} from './actions';
|
} from './actions';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@@ -13,7 +14,24 @@ import {
|
|||||||
export const defaultState = {
|
export const defaultState = {
|
||||||
registrationError: {},
|
registrationError: {},
|
||||||
registrationResult: {},
|
registrationResult: {},
|
||||||
formData: null,
|
registrationFormData: {
|
||||||
|
country: '',
|
||||||
|
email: '',
|
||||||
|
name: '',
|
||||||
|
password: '',
|
||||||
|
username: '',
|
||||||
|
marketingOptIn: true,
|
||||||
|
errors: {
|
||||||
|
email: '',
|
||||||
|
name: '',
|
||||||
|
username: '',
|
||||||
|
password: '',
|
||||||
|
country: '',
|
||||||
|
},
|
||||||
|
emailFieldBorderClass: '',
|
||||||
|
emailErrorSuggestion: null,
|
||||||
|
emailWarningSuggestion: null,
|
||||||
|
},
|
||||||
validations: null,
|
validations: null,
|
||||||
statusCode: null,
|
statusCode: null,
|
||||||
usernameSuggestions: [],
|
usernameSuggestions: [],
|
||||||
@@ -27,6 +45,8 @@ const reducer = (state = defaultState, action) => {
|
|||||||
case REGISTRATION_FORM.RESET:
|
case REGISTRATION_FORM.RESET:
|
||||||
return {
|
return {
|
||||||
...defaultState,
|
...defaultState,
|
||||||
|
registrationFormData: state.registrationFormData,
|
||||||
|
usernameSuggestions: state.usernameSuggestions,
|
||||||
};
|
};
|
||||||
case REGISTER_NEW_USER.BEGIN:
|
case REGISTER_NEW_USER.BEGIN:
|
||||||
return {
|
return {
|
||||||
@@ -72,6 +92,13 @@ const reducer = (state = defaultState, action) => {
|
|||||||
...state,
|
...state,
|
||||||
usernameSuggestions: [],
|
usernameSuggestions: [],
|
||||||
};
|
};
|
||||||
|
case REGISTER_PERSIST_FORM_DATA: {
|
||||||
|
const { registrationFormData } = action.payload;
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
registrationFormData,
|
||||||
|
};
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,3 +41,8 @@ export const usernameSuggestionsSelector = createSelector(
|
|||||||
registerSelector,
|
registerSelector,
|
||||||
register => register.usernameSuggestions,
|
register => register.usernameSuggestions,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export const registrationFormDataSelector = createSelector(
|
||||||
|
registerSelector,
|
||||||
|
register => register.registrationFormData,
|
||||||
|
);
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
import reducer from '../reducers';
|
import reducer from '../reducers';
|
||||||
import {
|
import {
|
||||||
REGISTER_CLEAR_USERNAME_SUGGESTIONS, REGISTER_FORM_VALIDATIONS, REGISTER_NEW_USER,
|
REGISTER_CLEAR_USERNAME_SUGGESTIONS,
|
||||||
|
REGISTER_FORM_VALIDATIONS,
|
||||||
|
REGISTER_NEW_USER,
|
||||||
|
REGISTER_PERSIST_FORM_DATA,
|
||||||
|
REGISTRATION_FORM,
|
||||||
} from '../actions';
|
} from '../actions';
|
||||||
import { DEFAULT_STATE } from '../../../data/constants';
|
import { DEFAULT_STATE } from '../../../data/constants';
|
||||||
|
|
||||||
@@ -10,7 +14,24 @@ describe('register reducer', () => {
|
|||||||
{
|
{
|
||||||
registrationError: {},
|
registrationError: {},
|
||||||
registrationResult: {},
|
registrationResult: {},
|
||||||
formData: null,
|
registrationFormData: {
|
||||||
|
country: '',
|
||||||
|
email: '',
|
||||||
|
name: '',
|
||||||
|
password: '',
|
||||||
|
username: '',
|
||||||
|
marketingOptIn: true,
|
||||||
|
errors: {
|
||||||
|
email: '',
|
||||||
|
name: '',
|
||||||
|
username: '',
|
||||||
|
password: '',
|
||||||
|
country: '',
|
||||||
|
},
|
||||||
|
emailFieldBorderClass: '',
|
||||||
|
emailErrorSuggestion: null,
|
||||||
|
emailWarningSuggestion: null,
|
||||||
|
},
|
||||||
validations: null,
|
validations: null,
|
||||||
statusCode: null,
|
statusCode: null,
|
||||||
usernameSuggestions: [],
|
usernameSuggestions: [],
|
||||||
@@ -75,4 +96,93 @@ describe('register reducer', () => {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
it('should not reset username suggestions and form data in form reset', () => {
|
||||||
|
const state = {
|
||||||
|
registrationError: {},
|
||||||
|
registrationResult: {},
|
||||||
|
registrationFormData: {
|
||||||
|
country: 'Pakistan',
|
||||||
|
email: 'test@email.com',
|
||||||
|
name: 'John Doe',
|
||||||
|
password: 'johndoe',
|
||||||
|
username: 'john',
|
||||||
|
marketingOptIn: true,
|
||||||
|
errors: {
|
||||||
|
email: '',
|
||||||
|
name: '',
|
||||||
|
username: '',
|
||||||
|
password: '',
|
||||||
|
country: '',
|
||||||
|
},
|
||||||
|
emailErrorSuggestion: 'test@email.com',
|
||||||
|
emailWarningSuggestion: 'test@email.com',
|
||||||
|
},
|
||||||
|
validations: null,
|
||||||
|
statusCode: null,
|
||||||
|
extendedProfile: [],
|
||||||
|
fieldDescriptions: {},
|
||||||
|
formRenderState: DEFAULT_STATE,
|
||||||
|
usernameSuggestions: ['test1', 'test2'],
|
||||||
|
};
|
||||||
|
const action = {
|
||||||
|
type: REGISTRATION_FORM.RESET,
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(
|
||||||
|
reducer(state, action),
|
||||||
|
).toEqual(
|
||||||
|
state,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should set registrationFormData', () => {
|
||||||
|
const state = {
|
||||||
|
registrationFormData: {
|
||||||
|
country: '',
|
||||||
|
email: '',
|
||||||
|
name: '',
|
||||||
|
password: '',
|
||||||
|
username: '',
|
||||||
|
marketingOptIn: true,
|
||||||
|
errors: {
|
||||||
|
email: '',
|
||||||
|
name: '',
|
||||||
|
username: '',
|
||||||
|
password: '',
|
||||||
|
country: '',
|
||||||
|
},
|
||||||
|
emailErrorSuggestion: null,
|
||||||
|
emailWarningSuggestion: null,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const registrationFormData = {
|
||||||
|
country: 'Pakistan',
|
||||||
|
email: 'test@email.com',
|
||||||
|
name: 'John Doe',
|
||||||
|
password: 'johndoe',
|
||||||
|
username: 'john',
|
||||||
|
marketingOptIn: true,
|
||||||
|
errors: {
|
||||||
|
email: '',
|
||||||
|
name: '',
|
||||||
|
username: '',
|
||||||
|
password: '',
|
||||||
|
country: '',
|
||||||
|
},
|
||||||
|
emailErrorSuggestion: 'test@email.com',
|
||||||
|
emailWarningSuggestion: 'test@email.com',
|
||||||
|
};
|
||||||
|
const action = {
|
||||||
|
type: REGISTER_PERSIST_FORM_DATA,
|
||||||
|
payload: { registrationFormData },
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(
|
||||||
|
reducer(state, action),
|
||||||
|
).toEqual(
|
||||||
|
{
|
||||||
|
registrationFormData,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ const { loggingService } = initializeMockLogging();
|
|||||||
describe('fetchRealtimeValidations', () => {
|
describe('fetchRealtimeValidations', () => {
|
||||||
const params = {
|
const params = {
|
||||||
payload: {
|
payload: {
|
||||||
formData: {
|
registrationFormData: {
|
||||||
email: 'test@test.com',
|
email: 'test@test.com',
|
||||||
username: '',
|
username: '',
|
||||||
password: 'test-password',
|
password: 'test-password',
|
||||||
@@ -112,7 +112,7 @@ describe('fetchRealtimeValidations', () => {
|
|||||||
describe('handleNewUserRegistration', () => {
|
describe('handleNewUserRegistration', () => {
|
||||||
const params = {
|
const params = {
|
||||||
payload: {
|
payload: {
|
||||||
formData: {
|
registrationFormData: {
|
||||||
email: 'test@test.com',
|
email: 'test@test.com',
|
||||||
username: 'test-username',
|
username: 'test-username',
|
||||||
password: 'test-password',
|
password: 'test-password',
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import { IntlProvider, injectIntl, configure } from '@edx/frontend-platform/i18n
|
|||||||
import {
|
import {
|
||||||
clearUsernameSuggestions,
|
clearUsernameSuggestions,
|
||||||
fetchRealtimeValidations,
|
fetchRealtimeValidations,
|
||||||
registerNewUser,
|
registerNewUser, setRegistrationFormData,
|
||||||
resetRegistrationForm,
|
resetRegistrationForm,
|
||||||
} from '../data/actions';
|
} from '../data/actions';
|
||||||
import {
|
import {
|
||||||
@@ -43,6 +43,7 @@ describe('RegistrationPage', () => {
|
|||||||
|
|
||||||
let props = {};
|
let props = {};
|
||||||
let store = {};
|
let store = {};
|
||||||
|
let registrationFormData = {};
|
||||||
|
|
||||||
const reduxWrapper = children => (
|
const reduxWrapper = children => (
|
||||||
<IntlProvider locale="en">
|
<IntlProvider locale="en">
|
||||||
@@ -86,6 +87,24 @@ describe('RegistrationPage', () => {
|
|||||||
handleInstitutionLogin: jest.fn(),
|
handleInstitutionLogin: jest.fn(),
|
||||||
institutionLogin: false,
|
institutionLogin: false,
|
||||||
};
|
};
|
||||||
|
registrationFormData = {
|
||||||
|
country: '',
|
||||||
|
email: '',
|
||||||
|
name: '',
|
||||||
|
password: '',
|
||||||
|
username: '',
|
||||||
|
marketingOptIn: true,
|
||||||
|
errors: {
|
||||||
|
email: '',
|
||||||
|
name: '',
|
||||||
|
username: '',
|
||||||
|
password: '',
|
||||||
|
country: '',
|
||||||
|
},
|
||||||
|
emailFieldBorderClass: '',
|
||||||
|
emailErrorSuggestion: null,
|
||||||
|
emailWarningSuggestion: null,
|
||||||
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
@@ -252,19 +271,6 @@ describe('RegistrationPage', () => {
|
|||||||
expect(store.dispatch).toHaveBeenCalledWith(fetchRealtimeValidations(formPayload));
|
expect(store.dispatch).toHaveBeenCalledWith(fetchRealtimeValidations(formPayload));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should validate the did you mean suggestions', () => {
|
|
||||||
const registrationPage = mount(reduxWrapper(<IntlRegistrationPage {...props} />));
|
|
||||||
|
|
||||||
registrationPage.find('input#email').simulate('blur', { target: { value: 'test@gmail.con', name: 'email' } });
|
|
||||||
expect(registrationPage.find('RegistrationPage').state('emailErrorSuggestion')).toEqual('test@gmail.com');
|
|
||||||
|
|
||||||
registrationPage.find('input#email').simulate('blur', { target: { value: 'test@fmail.com', name: 'email' } });
|
|
||||||
expect(registrationPage.find('RegistrationPage').state('emailWarningSuggestion')).toEqual('test@gmail.com');
|
|
||||||
|
|
||||||
registrationPage.find('input#email').simulate('blur', { target: { value: 'test@hotmail.com', name: 'email' } });
|
|
||||||
expect(registrationPage.find('RegistrationPage').state('emailWarningSuggestion')).toEqual(null);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should update props with validations returned by registration api', () => {
|
it('should update props with validations returned by registration api', () => {
|
||||||
store = mockStore({
|
store = mockStore({
|
||||||
...initialState,
|
...initialState,
|
||||||
@@ -511,8 +517,6 @@ describe('RegistrationPage', () => {
|
|||||||
registerPage.find('RegistrationPage').instance().shouldComponentUpdate(props);
|
registerPage.find('RegistrationPage').instance().shouldComponentUpdate(props);
|
||||||
registerPage.find('RegistrationPage').setState({ errors: { username: 'It looks like this username is already taken' } });
|
registerPage.find('RegistrationPage').setState({ errors: { username: 'It looks like this username is already taken' } });
|
||||||
expect(registerPage.find('button.username-suggestion').length).toEqual(3);
|
expect(registerPage.find('button.username-suggestion').length).toEqual(3);
|
||||||
registerPage.find('button.username-suggestion').at(0).simulate('click');
|
|
||||||
expect(registerPage.find('RegistrationPage').state('username')).toEqual('test_1');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should show username suggestions when full name is populated', () => {
|
it('should show username suggestions when full name is populated', () => {
|
||||||
@@ -528,8 +532,6 @@ describe('RegistrationPage', () => {
|
|||||||
registerPage.find('input#name').simulate('change', { target: { value: 'test name', name: 'name' } });
|
registerPage.find('input#name').simulate('change', { target: { value: 'test name', name: 'name' } });
|
||||||
|
|
||||||
expect(registerPage.find('button.username-suggestion').length).toEqual(3);
|
expect(registerPage.find('button.username-suggestion').length).toEqual(3);
|
||||||
registerPage.find('button.username-suggestion').at(0).simulate('click');
|
|
||||||
expect(registerPage.find('RegistrationPage').state('username')).toEqual('testname');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should clear username suggestions when close icon is clicked', () => {
|
it('should clear username suggestions when close icon is clicked', () => {
|
||||||
@@ -762,6 +764,30 @@ describe('RegistrationPage', () => {
|
|||||||
expect(registrationPage.find('RegistrationPage').state('errorCode')).toEqual(INTERNAL_SERVER_ERROR);
|
expect(registrationPage.find('RegistrationPage').state('errorCode')).toEqual(INTERNAL_SERVER_ERROR);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should update form fields state if updated in redux store', () => {
|
||||||
|
const nextProps = {
|
||||||
|
thirdPartyAuthContext,
|
||||||
|
registrationFormData: {
|
||||||
|
name: 'John Doe',
|
||||||
|
username: 'john_doe',
|
||||||
|
email: 'john.doe@example.com',
|
||||||
|
password: 'password1',
|
||||||
|
country: 'Pakistan',
|
||||||
|
emailErrorSuggestion: 'test@gmail.com',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const registrationPage = mount(reduxWrapper(<IntlRegistrationPage {...props} />));
|
||||||
|
registrationPage.find('RegistrationPage').instance().shouldComponentUpdate(nextProps);
|
||||||
|
|
||||||
|
expect(registrationPage.find('RegistrationPage').state('name')).toEqual('John Doe');
|
||||||
|
expect(registrationPage.find('RegistrationPage').state('username')).toEqual('john_doe');
|
||||||
|
expect(registrationPage.find('RegistrationPage').state('email')).toEqual('john.doe@example.com');
|
||||||
|
expect(registrationPage.find('RegistrationPage').state('emailErrorSuggestion')).toEqual('test@gmail.com');
|
||||||
|
expect(registrationPage.find('RegistrationPage').state('password')).toEqual('password1');
|
||||||
|
expect(registrationPage.find('RegistrationPage').state('country')).toEqual('Pakistan');
|
||||||
|
});
|
||||||
|
|
||||||
it('should display opt-in/opt-out checkbox', () => {
|
it('should display opt-in/opt-out checkbox', () => {
|
||||||
mergeConfig({
|
mergeConfig({
|
||||||
MARKETING_EMAILS_OPT_IN: 'true',
|
MARKETING_EMAILS_OPT_IN: 'true',
|
||||||
@@ -774,6 +800,115 @@ describe('RegistrationPage', () => {
|
|||||||
MARKETING_EMAILS_OPT_IN: '',
|
MARKETING_EMAILS_OPT_IN: '',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// ******** persist state tests ********
|
||||||
|
|
||||||
|
it('should clear form field errors in redux store on onFocus', () => {
|
||||||
|
store.dispatch = jest.fn(store.dispatch);
|
||||||
|
const registrationPage = mount(reduxWrapper(<IntlRegistrationPage {...props} />));
|
||||||
|
registrationPage.find('input#name').simulate('focus');
|
||||||
|
expect(store.dispatch).toHaveBeenCalledWith(setRegistrationFormData(registrationFormData));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should set username in redux store if usernameSuggestion is clicked', () => {
|
||||||
|
registrationFormData = {
|
||||||
|
...registrationFormData,
|
||||||
|
username: 'testname',
|
||||||
|
};
|
||||||
|
store = mockStore({
|
||||||
|
...initialState,
|
||||||
|
register: {
|
||||||
|
...initialState.register,
|
||||||
|
usernameSuggestions: ['testname', 't.name', 'test_0'],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
store.dispatch = jest.fn(store.dispatch);
|
||||||
|
const registerPage = mount(reduxWrapper(<IntlRegistrationPage {...props} />));
|
||||||
|
registerPage.find('input#name').simulate('change', { target: { value: 'test name', name: 'name' } });
|
||||||
|
registerPage.find('button.username-suggestion').at(0).simulate('click');
|
||||||
|
expect(store.dispatch).toHaveBeenCalledWith(setRegistrationFormData(registrationFormData));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should set email in redux store if emailSuggestion is clicked', () => {
|
||||||
|
registrationFormData = {
|
||||||
|
...registrationFormData,
|
||||||
|
email: 'test@gmail.com',
|
||||||
|
};
|
||||||
|
store = mockStore({
|
||||||
|
...initialState,
|
||||||
|
register: {
|
||||||
|
...initialState.register,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
store.dispatch = jest.fn(store.dispatch);
|
||||||
|
const registrationPage = mount(reduxWrapper(<IntlRegistrationPage {...props} />));
|
||||||
|
registrationPage.find('input#email').simulate('blur', { target: { value: 'test@gmail.con', name: 'email' } });
|
||||||
|
registrationPage.find('RegistrationPage').setState({ emailErrorSuggestion: 'test@gmail.com' });
|
||||||
|
registrationPage.find('.alert-link').simulate('click');
|
||||||
|
expect(store.dispatch).toHaveBeenCalledWith(setRegistrationFormData(registrationFormData));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should clear username in redux store if usernameSuggestion close button is clicked', () => {
|
||||||
|
store = mockStore({
|
||||||
|
...initialState,
|
||||||
|
register: {
|
||||||
|
...initialState.register,
|
||||||
|
usernameSuggestions: ['testname', 't.name', 'test_0'],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
store.dispatch = jest.fn(store.dispatch);
|
||||||
|
const registerPage = mount(reduxWrapper(<IntlRegistrationPage {...props} />));
|
||||||
|
registerPage.find('input#name').simulate('change', { target: { value: 'test name', name: 'name' } });
|
||||||
|
registerPage.find('button.suggested-username-close-button').simulate('click');
|
||||||
|
expect(store.dispatch).toHaveBeenCalledWith(setRegistrationFormData(registrationFormData));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should clear emailErrorSuggestion in redux store if close button is clicked', () => {
|
||||||
|
registrationFormData = {
|
||||||
|
...registrationFormData,
|
||||||
|
email: '',
|
||||||
|
};
|
||||||
|
store = mockStore({
|
||||||
|
...initialState,
|
||||||
|
register: {
|
||||||
|
...initialState.register,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
store.dispatch = jest.fn(store.dispatch);
|
||||||
|
const registrationPage = mount(reduxWrapper(<IntlRegistrationPage {...props} />));
|
||||||
|
registrationPage.find('input#email').simulate('blur', { target: { value: 'test@gmail.con', name: 'email' } });
|
||||||
|
registrationPage.find('RegistrationPage').setState({ emailErrorSuggestion: 'test@gmail.com' });
|
||||||
|
registrationPage.find('.alert-close').at(0).simulate('click');
|
||||||
|
expect(store.dispatch).toHaveBeenCalledWith(setRegistrationFormData(registrationFormData));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should set errors in redux store if form payload is invalid', () => {
|
||||||
|
registrationFormData = {
|
||||||
|
...registrationFormData,
|
||||||
|
errors: {
|
||||||
|
...registrationFormData.errors,
|
||||||
|
name: 'Enter your full name',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const payload = {
|
||||||
|
name: '',
|
||||||
|
username: 'john_doe',
|
||||||
|
email: 'john.doe@example.com',
|
||||||
|
password: 'password1',
|
||||||
|
country: 'Pakistan',
|
||||||
|
honor_code: true,
|
||||||
|
totalRegistrationTime: 0,
|
||||||
|
is_authn_mfe: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
store.dispatch = jest.fn(store.dispatch);
|
||||||
|
const registerPage = mount(reduxWrapper(<IntlRegistrationPage {...props} />));
|
||||||
|
|
||||||
|
populateRequiredFields(registerPage, payload);
|
||||||
|
registerPage.find('button.btn-brand').simulate('click');
|
||||||
|
expect(store.dispatch).toHaveBeenCalledWith(setRegistrationFormData(registrationFormData));
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('TestDynamicFields', () => {
|
describe('TestDynamicFields', () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user