From ad2ee9e013781618d9df01bf4e28217441562992 Mon Sep 17 00:00:00 2001 From: adeelehsan Date: Thu, 21 Jan 2021 02:28:53 +0500 Subject: [PATCH] logistration folder refactored to login and register --- src/{logistration => }/_style.scss | 0 .../ConfirmationAlert.jsx | 8 +- .../InstitutionLogistration.jsx | 2 +- .../SocialAuthProviders.jsx | 0 .../SwitchContent.jsx | 0 .../ThirdPartyAuthAlert.jsx | 0 src/common-components/data/actions.js | 22 ++ src/common-components/data/reducers.js | 32 ++ src/common-components/data/sagas.js | 34 +++ src/common-components/data/selectors.js | 10 + src/common-components/data/service.js | 23 ++ .../data/tests/sagas.test.js | 65 ++++ src/common-components/index.jsx | 7 + src/common-components/messages.jsx | 28 ++ .../tests/ConfirmationAlert.test.jsx | 0 .../tests/SocialAuthProviders.test.jsx | 2 +- .../tests/ThirdPartyAuthAlert.test.jsx | 0 .../SocialAuthProviders.test.jsx.snap | 0 .../ThirdPartyAuthAlert.test.jsx.snap | 0 src/data/reducers.js | 18 +- src/data/sagas.js | 6 +- src/forgot-password/ForgotPasswordPage.jsx | 16 +- .../RequestInProgressAlert.jsx | 2 +- src/forgot-password/messages.js | 28 +- src/index.jsx | 3 +- src/index.scss | 2 +- .../AccountActivationMessage.jsx | 12 +- src/{logistration => login}/LoginFailure.jsx | 10 +- .../LoginHelpLinks.jsx | 12 +- src/{logistration => login}/LoginPage.jsx | 49 +-- src/login/data/actions.js | 23 ++ src/{logistration => login}/data/constants.js | 0 src/login/data/reducers.js | 33 ++ src/login/data/sagas.js | 45 +++ src/login/data/selectors.js | 15 + src/login/data/service.js | 26 ++ src/login/data/tests/sagas.test.js | 99 ++++++ src/login/index.js | 4 + src/login/messages.jsx | 162 ++++++++++ .../tests/AccountActivationMessage.test.jsx | 0 .../tests/LoginFailure.test.jsx | 0 .../tests/LoginHelpLinks.test.jsx | 0 .../tests/LoginPage.test.jsx | 59 ++-- .../__snapshots__/LoginPage.test.jsx.snap | 76 ----- src/logistration/data/selectors.js | 25 -- src/logistration/messages.jsx | 285 ------------------ .../RegistrationFailure.jsx | 2 +- .../RegistrationPage.jsx | 73 ++--- .../data/actions.js | 41 --- .../data/reducers.js | 38 +-- src/{logistration => register}/data/sagas.js | 54 ---- src/register/data/selectors.js | 10 + .../data/service.js | 44 +-- .../data/tests/sagas.test.js | 161 ---------- src/{logistration => register}/index.js | 1 - src/register/messages.jsx | 121 ++++++++ .../tests/RegistrationPage.test.jsx | 89 +++--- .../RegistrationPage.test.jsx.snap | 124 +------- src/reset-password/InvalidToken.jsx | 10 +- src/reset-password/ResetFailure.jsx | 2 +- src/reset-password/ResetPasswordPage.jsx | 14 +- src/reset-password/ResetSuccess.jsx | 8 +- src/reset-password/messages.js | 24 +- .../ResetPasswordPage.test.jsx.snap | 12 +- 64 files changed, 1025 insertions(+), 1046 deletions(-) rename src/{logistration => }/_style.scss (100%) rename src/{logistration => common-components}/ConfirmationAlert.jsx (79%) rename src/{logistration => common-components}/InstitutionLogistration.jsx (96%) rename src/{logistration => common-components}/SocialAuthProviders.jsx (100%) rename src/{logistration => common-components}/SwitchContent.jsx (100%) rename src/{logistration => common-components}/ThirdPartyAuthAlert.jsx (100%) create mode 100644 src/common-components/data/actions.js create mode 100644 src/common-components/data/reducers.js create mode 100644 src/common-components/data/sagas.js create mode 100644 src/common-components/data/selectors.js create mode 100644 src/common-components/data/service.js create mode 100644 src/common-components/data/tests/sagas.test.js create mode 100644 src/common-components/messages.jsx rename src/{logistration => common-components}/tests/ConfirmationAlert.test.jsx (100%) rename src/{logistration => common-components}/tests/SocialAuthProviders.test.jsx (96%) rename src/{logistration => common-components}/tests/ThirdPartyAuthAlert.test.jsx (100%) rename src/{logistration => common-components}/tests/__snapshots__/SocialAuthProviders.test.jsx.snap (100%) rename src/{logistration => common-components}/tests/__snapshots__/ThirdPartyAuthAlert.test.jsx.snap (100%) rename src/{logistration => login}/AccountActivationMessage.jsx (75%) rename src/{logistration => login}/LoginFailure.jsx (88%) rename src/{logistration => login}/LoginHelpLinks.jsx (83%) rename src/{logistration => login}/LoginPage.jsx (83%) create mode 100644 src/login/data/actions.js rename src/{logistration => login}/data/constants.js (100%) create mode 100644 src/login/data/reducers.js create mode 100644 src/login/data/sagas.js create mode 100644 src/login/data/selectors.js create mode 100644 src/login/data/service.js create mode 100644 src/login/data/tests/sagas.test.js create mode 100644 src/login/index.js create mode 100644 src/login/messages.jsx rename src/{logistration => login}/tests/AccountActivationMessage.test.jsx (100%) rename src/{logistration => login}/tests/LoginFailure.test.jsx (100%) rename src/{logistration => login}/tests/LoginHelpLinks.test.jsx (100%) rename src/{logistration => login}/tests/LoginPage.test.jsx (86%) rename src/{logistration => login}/tests/__snapshots__/LoginPage.test.jsx.snap (93%) delete mode 100644 src/logistration/data/selectors.js delete mode 100644 src/logistration/messages.jsx rename src/{logistration => register}/RegistrationFailure.jsx (94%) rename src/{logistration => register}/RegistrationPage.jsx (90%) rename src/{logistration => register}/data/actions.js (60%) rename src/{logistration => register}/data/reducers.js (58%) rename src/{logistration => register}/data/sagas.js (57%) create mode 100644 src/register/data/selectors.js rename src/{logistration => register}/data/service.js (58%) rename src/{logistration => register}/data/tests/sagas.test.js (57%) rename src/{logistration => register}/index.js (80%) create mode 100644 src/register/messages.jsx rename src/{logistration => register}/tests/RegistrationPage.test.jsx (91%) rename src/{logistration => register}/tests/__snapshots__/RegistrationPage.test.jsx.snap (92%) diff --git a/src/logistration/_style.scss b/src/_style.scss similarity index 100% rename from src/logistration/_style.scss rename to src/_style.scss diff --git a/src/logistration/ConfirmationAlert.jsx b/src/common-components/ConfirmationAlert.jsx similarity index 79% rename from src/logistration/ConfirmationAlert.jsx rename to src/common-components/ConfirmationAlert.jsx index ddf84b09..be4b05d6 100644 --- a/src/logistration/ConfirmationAlert.jsx +++ b/src/common-components/ConfirmationAlert.jsx @@ -12,7 +12,7 @@ const ConfirmationAlert = (props) => { return ( - {intl.formatMessage(messages['authn.forgot.password.confirmation.title'])} + {intl.formatMessage(messages['forgot.password.confirmation.title'])}

{ values={{ strongEmail: {email} }} />

-

{intl.formatMessage(messages['authn.forgot.password.confirmation.info'])}

+

{intl.formatMessage(messages['forgot.password.confirmation.info'])}

- {intl.formatMessage(messages['authn.forgot.password.confirmation.support.link'])} + {intl.formatMessage(messages['forgot.password.confirmation.support.link'])} ), }} diff --git a/src/logistration/InstitutionLogistration.jsx b/src/common-components/InstitutionLogistration.jsx similarity index 96% rename from src/logistration/InstitutionLogistration.jsx rename to src/common-components/InstitutionLogistration.jsx index bd19f40c..64fe87ed 100644 --- a/src/logistration/InstitutionLogistration.jsx +++ b/src/common-components/InstitutionLogistration.jsx @@ -40,7 +40,7 @@ const InstitutionLogistration = props => { {headingTitle}

- {intl.formatMessage(messages['logistration.institution.login.page.sub.heading'])} + {intl.formatMessage(messages['institution.login.page.sub.heading'])}

    diff --git a/src/logistration/SocialAuthProviders.jsx b/src/common-components/SocialAuthProviders.jsx similarity index 100% rename from src/logistration/SocialAuthProviders.jsx rename to src/common-components/SocialAuthProviders.jsx diff --git a/src/logistration/SwitchContent.jsx b/src/common-components/SwitchContent.jsx similarity index 100% rename from src/logistration/SwitchContent.jsx rename to src/common-components/SwitchContent.jsx diff --git a/src/logistration/ThirdPartyAuthAlert.jsx b/src/common-components/ThirdPartyAuthAlert.jsx similarity index 100% rename from src/logistration/ThirdPartyAuthAlert.jsx rename to src/common-components/ThirdPartyAuthAlert.jsx diff --git a/src/common-components/data/actions.js b/src/common-components/data/actions.js new file mode 100644 index 00000000..28a34982 --- /dev/null +++ b/src/common-components/data/actions.js @@ -0,0 +1,22 @@ +import { AsyncActionType } from '../../data/utils'; + +export const THIRD_PARTY_AUTH_CONTEXT = new AsyncActionType('THIRD_PARTY_AUTH', 'GET_THIRD_PARTY_AUTH_CONTEXT'); + +// Third party auth context +export const getThirdPartyAuthContext = (urlParams) => ({ + type: THIRD_PARTY_AUTH_CONTEXT.BASE, + payload: { urlParams }, +}); + +export const getThirdPartyAuthContextBegin = () => ({ + type: THIRD_PARTY_AUTH_CONTEXT.BEGIN, +}); + +export const getThirdPartyAuthContextSuccess = (thirdPartyAuthContext) => ({ + type: THIRD_PARTY_AUTH_CONTEXT.SUCCESS, + payload: { thirdPartyAuthContext }, +}); + +export const getThirdPartyAuthContextFailure = () => ({ + type: THIRD_PARTY_AUTH_CONTEXT.FAILURE, +}); diff --git a/src/common-components/data/reducers.js b/src/common-components/data/reducers.js new file mode 100644 index 00000000..6a944bd2 --- /dev/null +++ b/src/common-components/data/reducers.js @@ -0,0 +1,32 @@ +import { THIRD_PARTY_AUTH_CONTEXT } from './actions'; + +import { PENDING_STATE, COMPLETE_STATE } from '../../data/constants'; + +export const defaultState = { + thirdPartyAuthApiStatus: null, +}; + +const reducer = (state = defaultState, action) => { + switch (action.type) { + case THIRD_PARTY_AUTH_CONTEXT.BEGIN: + return { + ...state, + thirdPartyAuthApiStatus: PENDING_STATE, + }; + case THIRD_PARTY_AUTH_CONTEXT.SUCCESS: + return { + ...state, + thirdPartyAuthContext: action.payload.thirdPartyAuthContext, + thirdPartyAuthApiStatus: COMPLETE_STATE, + }; + case THIRD_PARTY_AUTH_CONTEXT.FAILURE: + return { + ...state, + thirdPartyAuthApiStatus: COMPLETE_STATE, + }; + default: + return state; + } +}; + +export default reducer; diff --git a/src/common-components/data/sagas.js b/src/common-components/data/sagas.js new file mode 100644 index 00000000..d3ac7f03 --- /dev/null +++ b/src/common-components/data/sagas.js @@ -0,0 +1,34 @@ +import { call, put, takeEvery } from 'redux-saga/effects'; + +import { logError } from '@edx/frontend-platform/logging'; + +// Actions +import { + THIRD_PARTY_AUTH_CONTEXT, + getThirdPartyAuthContextBegin, + getThirdPartyAuthContextSuccess, + getThirdPartyAuthContextFailure, +} from './actions'; + +// Services +import { + getThirdPartyAuthContext, +} from './service'; + +export function* fetchThirdPartyAuthContext(action) { + try { + yield put(getThirdPartyAuthContextBegin()); + const { thirdPartyAuthContext } = yield call(getThirdPartyAuthContext, action.payload.urlParams); + + yield put(getThirdPartyAuthContextSuccess( + thirdPartyAuthContext, + )); + } catch (e) { + yield put(getThirdPartyAuthContextFailure()); + logError(e); + } +} + +export default function* saga() { + yield takeEvery(THIRD_PARTY_AUTH_CONTEXT.BASE, fetchThirdPartyAuthContext); +} diff --git a/src/common-components/data/selectors.js b/src/common-components/data/selectors.js new file mode 100644 index 00000000..f5bf2173 --- /dev/null +++ b/src/common-components/data/selectors.js @@ -0,0 +1,10 @@ +import { createSelector } from 'reselect'; + +export const storeName = 'commonComponents'; + +export const commonComponentsSelector = state => ({ ...state[storeName] }); + +export const thirdPartyAuthContextSelector = createSelector( + commonComponentsSelector, + commonComponents => commonComponents.thirdPartyAuthContext, +); diff --git a/src/common-components/data/service.js b/src/common-components/data/service.js new file mode 100644 index 00000000..566a6fb8 --- /dev/null +++ b/src/common-components/data/service.js @@ -0,0 +1,23 @@ +import { camelCaseObject, convertKeyNames, getConfig } from '@edx/frontend-platform'; +import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth'; + +// eslint-disable-next-line import/prefer-default-export +export async function getThirdPartyAuthContext(urlParams) { + const requestConfig = { + headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, + params: urlParams, + isPublic: true, + }; + + const { data } = await getAuthenticatedHttpClient() + .get( + `${getConfig().LMS_BASE_URL}/api/third_party_auth_context`, + requestConfig, + ) + .catch((e) => { + throw (e); + }); + return { + thirdPartyAuthContext: camelCaseObject(convertKeyNames(data, { fullname: 'name' })), + }; +} diff --git a/src/common-components/data/tests/sagas.test.js b/src/common-components/data/tests/sagas.test.js new file mode 100644 index 00000000..fe0feb8a --- /dev/null +++ b/src/common-components/data/tests/sagas.test.js @@ -0,0 +1,65 @@ +import { runSaga } from 'redux-saga'; + +import * as actions from '../actions'; +import { fetchThirdPartyAuthContext } from '../sagas'; +import * as api from '../service'; +import initializeMockLogging from '../../../setupTest'; + +const { loggingService } = initializeMockLogging(); + +describe('fetchThirdPartyAuthContext', () => { + const params = { + payload: { urlParams: {} }, + }; + + const data = { + currentProvider: null, + providers: [], + secondaryProviders: [], + finishAuthUrl: null, + pipelineUserDetails: {}, + }; + + beforeEach(() => { + loggingService.logError.mockReset(); + }); + + it('should call service and dispatch success action', async () => { + const getThirdPartyAuthContext = jest.spyOn(api, 'getThirdPartyAuthContext') + .mockImplementation(() => Promise.resolve({ thirdPartyAuthContext: data })); + + const dispatched = []; + await runSaga( + { dispatch: (action) => dispatched.push(action) }, + fetchThirdPartyAuthContext, + params, + ); + + expect(getThirdPartyAuthContext).toHaveBeenCalledTimes(1); + expect(dispatched).toEqual([ + actions.getThirdPartyAuthContextBegin(), + actions.getThirdPartyAuthContextSuccess(data), + ]); + getThirdPartyAuthContext.mockClear(); + }); + + it('should call service and dispatch error action', async () => { + const getThirdPartyAuthContext = jest.spyOn(api, 'getThirdPartyAuthContext') + .mockImplementation(() => Promise.reject(new Error('something went wrong'))); + + const dispatched = []; + await runSaga( + { dispatch: (action) => dispatched.push(action) }, + fetchThirdPartyAuthContext, + params, + ); + + expect(getThirdPartyAuthContext).toHaveBeenCalledTimes(1); + expect(loggingService.logError).toHaveBeenCalled(); + expect(dispatched).toEqual([ + actions.getThirdPartyAuthContextBegin(), + actions.getThirdPartyAuthContextFailure(), + ]); + getThirdPartyAuthContext.mockClear(); + }); +}); diff --git a/src/common-components/index.jsx b/src/common-components/index.jsx index 2a818f9e..43e9348a 100644 --- a/src/common-components/index.jsx +++ b/src/common-components/index.jsx @@ -3,3 +3,10 @@ export { default as RedirectLogistration } from './RedirectLogistration'; export { default as registerIcons } from './RegisterFaIcons'; export { default as UnAuthOnlyRoute } from './UnAuthOnlyRoute'; export { default as NotFoundPage } from './NotFoundPage'; +export { default as SocialAuthProviders } from './SocialAuthProviders'; +export { default as ThirdPartyAuthAlert } from './ThirdPartyAuthAlert'; +export { default as InstitutionLogistration } from './InstitutionLogistration'; +export { RenderInstitutionButton } from './InstitutionLogistration'; +export { default as reducer } from './data/reducers'; +export { default as saga } from './data/sagas'; +export { storeName } from './data/selectors'; diff --git a/src/common-components/messages.jsx b/src/common-components/messages.jsx new file mode 100644 index 00000000..3bdf0013 --- /dev/null +++ b/src/common-components/messages.jsx @@ -0,0 +1,28 @@ +import { defineMessages } from '@edx/frontend-platform/i18n'; + +const messages = defineMessages({ + 'institution.login.page.sub.heading': { + id: 'institution.login.page.sub.heading', + defaultMessage: 'Choose your institution from the list below:', + description: 'Heading of the institutions list', + }, + // Confirmation Alert Message + 'forgot.password.confirmation.title': { + id: 'forgot.password.confirmation.title', + defaultMessage: 'Check Your Email', + description: 'Forgot password confirmation message title', + }, + 'forgot.password.confirmation.support.link': { + id: 'forgot.password.confirmation.support.link', + defaultMessage: 'contact technical support', + description: 'Technical support link text', + }, + 'forgot.password.confirmation.info': { + id: 'forgot.password.confirmation.info', + defaultMessage: 'If you do not receive a password reset message after 1 minute, verify that you entered the correct ' + + 'email address, or check your spam folder.', + description: 'Part of message that appears after user requests password change', + }, +}); + +export default messages; diff --git a/src/logistration/tests/ConfirmationAlert.test.jsx b/src/common-components/tests/ConfirmationAlert.test.jsx similarity index 100% rename from src/logistration/tests/ConfirmationAlert.test.jsx rename to src/common-components/tests/ConfirmationAlert.test.jsx diff --git a/src/logistration/tests/SocialAuthProviders.test.jsx b/src/common-components/tests/SocialAuthProviders.test.jsx similarity index 96% rename from src/logistration/tests/SocialAuthProviders.test.jsx rename to src/common-components/tests/SocialAuthProviders.test.jsx index b8b8b8bd..4c2ac8f6 100644 --- a/src/logistration/tests/SocialAuthProviders.test.jsx +++ b/src/common-components/tests/SocialAuthProviders.test.jsx @@ -3,7 +3,7 @@ import renderer from 'react-test-renderer'; import { IntlProvider } from '@edx/frontend-platform/i18n'; import SocialAuthProviders from '../SocialAuthProviders'; -import { registerIcons } from '../../common-components'; +import registerIcons from '../RegisterFaIcons'; registerIcons(); diff --git a/src/logistration/tests/ThirdPartyAuthAlert.test.jsx b/src/common-components/tests/ThirdPartyAuthAlert.test.jsx similarity index 100% rename from src/logistration/tests/ThirdPartyAuthAlert.test.jsx rename to src/common-components/tests/ThirdPartyAuthAlert.test.jsx diff --git a/src/logistration/tests/__snapshots__/SocialAuthProviders.test.jsx.snap b/src/common-components/tests/__snapshots__/SocialAuthProviders.test.jsx.snap similarity index 100% rename from src/logistration/tests/__snapshots__/SocialAuthProviders.test.jsx.snap rename to src/common-components/tests/__snapshots__/SocialAuthProviders.test.jsx.snap diff --git a/src/logistration/tests/__snapshots__/ThirdPartyAuthAlert.test.jsx.snap b/src/common-components/tests/__snapshots__/ThirdPartyAuthAlert.test.jsx.snap similarity index 100% rename from src/logistration/tests/__snapshots__/ThirdPartyAuthAlert.test.jsx.snap rename to src/common-components/tests/__snapshots__/ThirdPartyAuthAlert.test.jsx.snap diff --git a/src/data/reducers.js b/src/data/reducers.js index 8a5c92f9..4442848e 100755 --- a/src/data/reducers.js +++ b/src/data/reducers.js @@ -1,9 +1,17 @@ import { combineReducers } from 'redux'; import { - reducer as logistrationReducer, - storeName as logistrationStoreName, -} from '../logistration'; + reducer as loginReducer, + storeName as loginStoreName, +} from '../login'; +import { + reducer as registerReducer, + storeName as registerStoreName, +} from '../register'; +import { + reducer as commonComponentsReducer, + storeName as commonComponentsStoreName, +} from '../common-components'; import { reducer as forgotPasswordReducer, storeName as forgotPasswordStoreName, @@ -14,7 +22,9 @@ import { } from '../reset-password'; const createRootReducer = () => combineReducers({ - [logistrationStoreName]: logistrationReducer, + [loginStoreName]: loginReducer, + [registerStoreName]: registerReducer, + [commonComponentsStoreName]: commonComponentsReducer, [forgotPasswordStoreName]: forgotPasswordReducer, [resetPasswordStoreName]: resetPasswordReducer, }); diff --git a/src/data/sagas.js b/src/data/sagas.js index eb836238..30ad4799 100644 --- a/src/data/sagas.js +++ b/src/data/sagas.js @@ -1,12 +1,16 @@ import { all } from 'redux-saga/effects'; -import { saga as registrationSaga } from '../logistration'; +import { saga as registrationSaga } from '../register'; +import { saga as loginSaga } from '../login'; +import { saga as commonComponentsSaga } from '../common-components'; import { saga as forgotPasswordSaga } from '../forgot-password'; import { saga as resetPasswordSaga } from '../reset-password'; export default function* rootSaga() { yield all([ + loginSaga(), registrationSaga(), + commonComponentsSaga(), forgotPasswordSaga(), resetPasswordSaga(), ]); diff --git a/src/forgot-password/ForgotPasswordPage.jsx b/src/forgot-password/ForgotPasswordPage.jsx index 8ce50610..60fd3eeb 100644 --- a/src/forgot-password/ForgotPasswordPage.jsx +++ b/src/forgot-password/ForgotPasswordPage.jsx @@ -16,14 +16,14 @@ import { forgotPassword } from './data/actions'; import { forgotPasswordResultSelector } from './data/selectors'; import RequestInProgressAlert from './RequestInProgressAlert'; import { LOGIN_PAGE, VALID_EMAIL_REGEX } from '../data/constants'; -import LoginHelpLinks from '../logistration/LoginHelpLinks'; +import LoginHelpLinks from '../login/LoginHelpLinks'; const ForgotPasswordPage = (props) => { const { intl, status } = props; let invalidEmailMessage; const validateEmail = (e, setFieldValue) => { - invalidEmailMessage = intl.formatMessage(messages['logisration.forgot.password.page.invalid.email.message']); + invalidEmailMessage = intl.formatMessage(messages['forgot.password.page.invalid.email.message']); const regex = new RegExp(VALID_EMAIL_REGEX, 'i'); const inputEmail = e.target.value; @@ -31,7 +31,7 @@ const ForgotPasswordPage = (props) => { setFieldValue('email', inputEmail); setFieldValue('isEmailValid', isEmailValid); if (inputEmail.length < 3) { - invalidEmailMessage = `${intl.formatMessage(messages['logisration.forgot.password.page.email.invalid.length.message'])} ${invalidEmailMessage}`; + invalidEmailMessage = `${intl.formatMessage(messages['forgot.password.page.email.invalid.length.message'])} ${invalidEmailMessage}`; } }; @@ -59,10 +59,10 @@ const ForgotPasswordPage = (props) => {
    {status === 'forbidden' ? : null}

    - {intl.formatMessage(messages['logisration.forgot.password.page.heading'])} + {intl.formatMessage(messages['forgot.password.page.heading'])}

    - {intl.formatMessage(messages['logisration.forgot.password.page.instructions'])} + {intl.formatMessage(messages['forgot.password.page.instructions'])}

    { invalidMessage={invalidEmailMessage} > - {intl.formatMessage(messages['logisration.forgot.password.page.email.field.label'])} + {intl.formatMessage(messages['forgot.password.page.email.field.label'])} { onChange={e => validateEmail(e, setFieldValue)} />

    - {intl.formatMessage(messages['logisration.forgot.password.page.email.field.help.text'])} + {intl.formatMessage(messages['forgot.password.page.email.field.help.text'])}

    @@ -91,7 +91,7 @@ const ForgotPasswordPage = (props) => { className="btn-primary mt-3" state={status} labels={{ - default: intl.formatMessage(messages['logisration.forgot.password.page.submit.button']), + default: intl.formatMessage(messages['forgot.password.page.submit.button']), }} onClick={handleSubmit} /> diff --git a/src/forgot-password/RequestInProgressAlert.jsx b/src/forgot-password/RequestInProgressAlert.jsx index bc68c986..ae2ae173 100644 --- a/src/forgot-password/RequestInProgressAlert.jsx +++ b/src/forgot-password/RequestInProgressAlert.jsx @@ -8,7 +8,7 @@ const RequestInProgressAlert = () => ( diff --git a/src/forgot-password/messages.js b/src/forgot-password/messages.js index 5826d5bb..809f7ead 100644 --- a/src/forgot-password/messages.js +++ b/src/forgot-password/messages.js @@ -1,38 +1,38 @@ import { defineMessages } from '@edx/frontend-platform/i18n'; const messages = defineMessages({ - 'logisration.forgot.password.page.heading': { - id: 'logisration.forgot.password.page.heading', + 'forgot.password.page.heading': { + id: 'forgot.password.page.heading', defaultMessage: 'Password assistance', description: 'The page heading for the forgot password page.', }, - 'logisration.forgot.password.page.instructions': { - id: 'logisration.forgot.password.page.instructions', + 'forgot.password.page.instructions': { + id: 'forgot.password.page.instructions', defaultMessage: 'Please enter your log-in or recovery email address below and we will send you an email with instructions.', description: 'Instructions message for forgot password page.', }, - 'logisration.forgot.password.page.invalid.email.message': { - id: 'logisration.forgot.password.page.invalid.email.message', + 'forgot.password.page.invalid.email.message': { + id: 'forgot.password.page.invalid.email.message', defaultMessage: "The email address you've provided isn't formatted correctly.", description: 'Invalid email address message for the forgot password page.', }, - 'logisration.forgot.password.page.email.field.label': { - id: 'logisration.forgot.password.page.email.field.label', + 'forgot.password.page.email.field.label': { + id: 'forgot.password.page.email.field.label', defaultMessage: 'Email', description: 'Email field label for the forgot password page.', }, - 'logisration.forgot.password.page.email.field.help.text': { - id: 'logisration.forgot.password.page.email.field.help.text', + 'forgot.password.page.email.field.help.text': { + id: 'forgot.password.page.email.field.help.text', defaultMessage: 'The email address you used to register with edX.', description: 'Email field help text for the forgot password page.', }, - 'logisration.forgot.password.page.submit.button': { - id: 'logisration.forgot.password.page.submit.button', + 'forgot.password.page.submit.button': { + id: 'forgot.password.page.submit.button', defaultMessage: 'Recover my password', description: 'Submit button text for the forgot password page.', }, - 'logisration.forgot.password.page.email.invalid.length.message': { - id: 'logisration.forgot.password.page.email.invalid.length.message', + 'forgot.password.page.email.invalid.length.message': { + id: 'forgot.password.page.email.invalid.length.message', defaultMessage: 'Email must have at least 3 characters.', description: 'Invalid email address length message for the forgot password page.', }, diff --git a/src/index.jsx b/src/index.jsx index 7503bcf4..e8f59bcb 100755 --- a/src/index.jsx +++ b/src/index.jsx @@ -11,7 +11,8 @@ import { Redirect, Route, Switch } from 'react-router-dom'; import { messages as headerMessages } from '@edx/frontend-component-header'; import configureStore from './data/configureStore'; -import { LoginPage, RegistrationPage } from './logistration'; +import { RegistrationPage } from './register'; +import { LoginPage } from './login'; import { LOGIN_PAGE, PAGE_NOT_FOUND, REGISTER_PAGE, RESET_PAGE, PASSWORD_RESET_CONFIRM, } from './data/constants'; diff --git a/src/index.scss b/src/index.scss index 17a909af..5ba962d9 100755 --- a/src/index.scss +++ b/src/index.scss @@ -5,4 +5,4 @@ @import "~@edx/frontend-component-header/dist/index"; -@import "./logistration/style"; +@import "./style"; diff --git a/src/logistration/AccountActivationMessage.jsx b/src/login/AccountActivationMessage.jsx similarity index 75% rename from src/logistration/AccountActivationMessage.jsx rename to src/login/AccountActivationMessage.jsx index 91a53e1f..c86cd5db 100644 --- a/src/logistration/AccountActivationMessage.jsx +++ b/src/login/AccountActivationMessage.jsx @@ -17,25 +17,25 @@ const AccountActivationMessage = (props) => { switch (messageType) { case ACCOUNT_ACTIVATION_MESSAGE.SUCCESS: { - heading = intl.formatMessage(messages['authn.account.activation.success.message.title']); - activationMessage = intl.formatMessage(messages['authn.account.activation.success.message']); + heading = intl.formatMessage(messages['account.activation.success.message.title']); + activationMessage = intl.formatMessage(messages['account.activation.success.message']); break; } case ACCOUNT_ACTIVATION_MESSAGE.INFO: { - activationMessage = intl.formatMessage(messages['authn.account.already.activated.message']); + activationMessage = intl.formatMessage(messages['account.already.activated.message']); break; } case ACCOUNT_ACTIVATION_MESSAGE.ERROR: { const supportLink = ( - {intl.formatMessage(messages['authn.account.activation.support.link'])} + {intl.formatMessage(messages['account.activation.support.link'])} ); - heading = intl.formatMessage(messages['authn.account.activation.error.message.title']); + heading = intl.formatMessage(messages['account.activation.error.message.title']); activationMessage = ( { errorList = (
  • {intl.formatMessage(messages['logistration.non.compliant.password.title'])}, + passwordComplaintRequirements: {intl.formatMessage(messages['non.compliant.password.title'])}, lineBreak:
    , }} /> @@ -40,7 +40,7 @@ const LoginFailureMessage = (props) => { case INACTIVE_USER: { const supportLink = ( - {intl.formatMessage(messages['logistration.contact.support.link'], { platformName: context.platformName })} + {intl.formatMessage(messages['contact.support.link'], { platformName: context.platformName })} ); errorList = ( @@ -70,7 +70,7 @@ const LoginFailureMessage = (props) => { case INTERNAL_SERVER_ERROR: errorList = (
  • - {intl.formatMessage(messages['authn.internal.server.error.message'])} + {intl.formatMessage(messages['internal.server.error.message'])}
  • ); break; @@ -96,7 +96,7 @@ const LoginFailureMessage = (props) => { return ( - {intl.formatMessage(messages['logistration.login.failure.header.title'])} + {intl.formatMessage(messages['login.failure.header.title'])}
      {errorList}
    ); diff --git a/src/logistration/LoginHelpLinks.jsx b/src/login/LoginHelpLinks.jsx similarity index 83% rename from src/logistration/LoginHelpLinks.jsx rename to src/login/LoginHelpLinks.jsx index 5c72b257..b170074e 100644 --- a/src/logistration/LoginHelpLinks.jsx +++ b/src/login/LoginHelpLinks.jsx @@ -5,7 +5,7 @@ import { faCaretDown, faCaretRight } from '@fortawesome/free-solid-svg-icons'; import { injectIntl, intlShape } from '@edx/frontend-platform/i18n'; import { getConfig } from '@edx/frontend-platform'; -import SwitchContent from './SwitchContent'; +import SwitchContent from '../common-components/SwitchContent'; import { LOGIN_PAGE, REGISTER_PAGE, @@ -24,28 +24,28 @@ const LoginHelpLinks = (props) => { const forgotPasswordLink = () => ( - {intl.formatMessage(messages['logistration.forgot.password.link'])} + {intl.formatMessage(messages['forgot.password.link'])} ); const signUpLink = () => ( - {intl.formatMessage(messages['logistration.register.link'])} + {intl.formatMessage(messages['register.link'])} ); const loginIssueSupportURL = (config) => (config.LOGIN_ISSUE_SUPPORT_LINK ? ( - {intl.formatMessage(messages['logistration.other.sign.in.issues'])} + {intl.formatMessage(messages['other.sign.in.issues'])} ) : null); const getHelpButtonMessage = () => { - let mid = 'logistration.need.other.help.signing.in.collapsible.menu'; + let mid = 'need.other.help.signing.in.collapsible.menu'; if (page === LOGIN_PAGE) { - mid = 'logistration.need.help.signing.in.collapsible.menu'; + mid = 'need.help.signing.in.collapsible.menu'; } return intl.formatMessage(messages[mid]); diff --git a/src/logistration/LoginPage.jsx b/src/login/LoginPage.jsx similarity index 83% rename from src/logistration/LoginPage.jsx rename to src/login/LoginPage.jsx index 1b8b0f53..7acda232 100644 --- a/src/logistration/LoginPage.jsx +++ b/src/login/LoginPage.jsx @@ -10,17 +10,18 @@ import PropTypes from 'prop-types'; import { connect } from 'react-redux'; import AccountActivationMessage from './AccountActivationMessage'; -import ConfirmationAlert from './ConfirmationAlert'; -import { getThirdPartyAuthContext, loginRequest } from './data/actions'; -import { loginErrorSelector, loginRequestSelector, thirdPartyAuthContextSelector } from './data/selectors'; -import InstitutionLogistration, { RenderInstitutionButton } from './InstitutionLogistration'; +import ConfirmationAlert from '../common-components/ConfirmationAlert'; +import { loginRequest } from './data/actions'; +import { getThirdPartyAuthContext } from '../common-components/data/actions'; +import { loginErrorSelector, loginRequestSelector } from './data/selectors'; +import { thirdPartyAuthContextSelector } from '../common-components/data/selectors'; import LoginHelpLinks from './LoginHelpLinks'; import LoginFailureMessage from './LoginFailure'; import messages from './messages'; -import SocialAuthProviders from './SocialAuthProviders'; -import ThirdPartyAuthAlert from './ThirdPartyAuthAlert'; - -import { RedirectLogistration } from '../common-components'; +import { + RedirectLogistration, SocialAuthProviders, ThirdPartyAuthAlert, RenderInstitutionButton, + InstitutionLogistration, +} from '../common-components'; import { DEFAULT_REDIRECT_URL, DEFAULT_STATE, LOGIN_PAGE, REGISTER_PAGE, ENTERPRISE_LOGIN_URL, PENDING_STATE, } from '../data/constants'; @@ -130,7 +131,7 @@ class LoginPage extends React.Component {
    @@ -156,8 +157,8 @@ class LoginPage extends React.Component { ); } @@ -183,24 +184,24 @@ class LoginPage extends React.Component { {this.props.forgotPassword.status === 'complete' ? : null}

    - {intl.formatMessage(messages['logistration.first.time.here'])} + {intl.formatMessage(messages['first.time.here'])} - {intl.formatMessage(messages['logistration.create.an.account'])}. + {intl.formatMessage(messages['create.an.account'])}.

    - {intl.formatMessage(messages['logistration.sign.in.heading'])} + {intl.formatMessage(messages['sign.in.heading'])}

    - {intl.formatMessage(messages['logistration.login.page.email.label'])} + {intl.formatMessage(messages['email.label'])} this.handleOnChange(e)} /> -

    {intl.formatMessage(messages['logistration.email.help.message'])}

    +

    {intl.formatMessage(messages['email.help.message'])}

    - {intl.formatMessage(messages['logistration.password'])} + {intl.formatMessage(messages['password.label'])} - {intl.formatMessage(messages['logistration.enterprise.login.link.text'])} + {intl.formatMessage(messages['enterprise.login.link.text'])} @@ -246,7 +247,7 @@ class LoginPage extends React.Component { {(providers.length || secondaryProviders.length || thirdPartyAuthApiStatus === PENDING_STATE) && !currentProvider ? (
    -

    {intl.formatMessage(messages['logistration.or.sign.in.with'])}

    +

    {intl.formatMessage(messages['or.sign.in.with'])}

    ) : null} {this.renderThirdPartyAuth(providers, secondaryProviders, currentProvider, thirdPartyAuthApiStatus, intl)} @@ -302,8 +303,8 @@ const mapStateToProps = state => { const thirdPartyAuthContext = thirdPartyAuthContextSelector(state); const loginError = loginErrorSelector(state); return { - submitState: state.logistration.submitState, - thirdPartyAuthApiStatus: state.logistration.thirdPartyAuthApiStatus, + submitState: state.login.submitState, + thirdPartyAuthApiStatus: state.commonComponents.thirdPartyAuthApiStatus, forgotPassword, loginError, loginResult, diff --git a/src/login/data/actions.js b/src/login/data/actions.js new file mode 100644 index 00000000..7d70a324 --- /dev/null +++ b/src/login/data/actions.js @@ -0,0 +1,23 @@ +import { AsyncActionType } from '../../data/utils'; + +export const LOGIN_REQUEST = new AsyncActionType('LOGIN', 'REQUEST'); + +// Login +export const loginRequest = creds => ({ + type: LOGIN_REQUEST.BASE, + payload: { creds }, +}); + +export const loginRequestBegin = () => ({ + type: LOGIN_REQUEST.BEGIN, +}); + +export const loginRequestSuccess = (redirectUrl, success) => ({ + type: LOGIN_REQUEST.SUCCESS, + payload: { redirectUrl, success }, +}); + +export const loginRequestFailure = (loginError) => ({ + type: LOGIN_REQUEST.FAILURE, + payload: { loginError }, +}); diff --git a/src/logistration/data/constants.js b/src/login/data/constants.js similarity index 100% rename from src/logistration/data/constants.js rename to src/login/data/constants.js diff --git a/src/login/data/reducers.js b/src/login/data/reducers.js new file mode 100644 index 00000000..04d38b96 --- /dev/null +++ b/src/login/data/reducers.js @@ -0,0 +1,33 @@ +import { LOGIN_REQUEST } from './actions'; + +import { DEFAULT_STATE, PENDING_STATE } from '../../data/constants'; + +export const defaultState = { + loginError: null, + loginResult: {}, +}; + +const reducer = (state = defaultState, action) => { + switch (action.type) { + case LOGIN_REQUEST.BEGIN: + return { + ...state, + submitState: PENDING_STATE, + }; + case LOGIN_REQUEST.SUCCESS: + return { + ...state, + loginResult: action.payload, + }; + case LOGIN_REQUEST.FAILURE: + return { + ...state, + loginError: action.payload.loginError, + submitState: DEFAULT_STATE, + }; + default: + return state; + } +}; + +export default reducer; diff --git a/src/login/data/sagas.js b/src/login/data/sagas.js new file mode 100644 index 00000000..35e943a3 --- /dev/null +++ b/src/login/data/sagas.js @@ -0,0 +1,45 @@ +import { call, put, takeEvery } from 'redux-saga/effects'; + +import { camelCaseObject } from '@edx/frontend-platform'; +import { logError } from '@edx/frontend-platform/logging'; +import { INTERNAL_SERVER_ERROR } from './constants'; + +// Actions +import { + LOGIN_REQUEST, + loginRequestBegin, + loginRequestFailure, + loginRequestSuccess, +} from './actions'; + +// Services +import { + loginRequest, +} from './service'; + +export function* handleLoginRequest(action) { + try { + yield put(loginRequestBegin()); + + const { redirectUrl, success } = yield call(loginRequest, action.payload.creds); + + yield put(loginRequestSuccess( + redirectUrl, + success, + )); + } catch (e) { + const statusCodes = [400]; + if (e.response) { + if (statusCodes.includes(e.response.status)) { + yield put(loginRequestFailure(camelCaseObject(e.response.data))); + } else { + yield put(loginRequestFailure(camelCaseObject({ errorCode: INTERNAL_SERVER_ERROR }))); + } + } + logError(e); + } +} + +export default function* saga() { + yield takeEvery(LOGIN_REQUEST.BASE, handleLoginRequest); +} diff --git a/src/login/data/selectors.js b/src/login/data/selectors.js new file mode 100644 index 00000000..a409a5ee --- /dev/null +++ b/src/login/data/selectors.js @@ -0,0 +1,15 @@ +import { createSelector } from 'reselect'; + +export const storeName = 'login'; + +export const loginSelector = state => ({ ...state[storeName] }); + +export const loginRequestSelector = createSelector( + loginSelector, + login => login.loginResult, +); + +export const loginErrorSelector = createSelector( + loginSelector, + login => login.loginError, +); diff --git a/src/login/data/service.js b/src/login/data/service.js new file mode 100644 index 00000000..24923d3c --- /dev/null +++ b/src/login/data/service.js @@ -0,0 +1,26 @@ +import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth'; +import { getConfig } from '@edx/frontend-platform'; +import querystring from 'querystring'; + +// eslint-disable-next-line import/prefer-default-export +export async function loginRequest(creds) { + const requestConfig = { + headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, + isPublic: true, + }; + + const { data } = await getAuthenticatedHttpClient() + .post( + `${getConfig().LMS_BASE_URL}/user_api/v1/account/login_session/`, + querystring.stringify(creds), + requestConfig, + ) + .catch((e) => { + throw (e); + }); + + return { + redirectUrl: data.redirect_url || `${getConfig().LMS_BASE_URL}/dashboard`, + success: data.success || false, + }; +} diff --git a/src/login/data/tests/sagas.test.js b/src/login/data/tests/sagas.test.js new file mode 100644 index 00000000..5b4a1726 --- /dev/null +++ b/src/login/data/tests/sagas.test.js @@ -0,0 +1,99 @@ +import { runSaga } from 'redux-saga'; + +import { camelCaseObject } from '@edx/frontend-platform'; + +import * as actions from '../actions'; +import { handleLoginRequest } from '../sagas'; +import * as api from '../service'; +import initializeMockLogging from '../../../setupTest'; + +const { loggingService } = initializeMockLogging(); + +describe('handleLoginRequest', () => { + const params = { + payload: { + formData: { + email: 'test@test.com', + password: 'test-password', + }, + }, + }; + + beforeEach(() => { + loggingService.logError.mockReset(); + }); + + it('should call service and dispatch success action', async () => { + const data = { redirectUrl: '/dashboard', success: true }; + const loginRequest = jest.spyOn(api, 'loginRequest') + .mockImplementation(() => Promise.resolve(data)); + + const dispatched = []; + await runSaga( + { dispatch: (action) => dispatched.push(action) }, + handleLoginRequest, + params, + ); + + expect(loginRequest).toHaveBeenCalledTimes(1); + expect(dispatched).toEqual([ + actions.loginRequestBegin(), + actions.loginRequestSuccess(data.redirectUrl, data.success), + ]); + loginRequest.mockClear(); + }); + + it('should call service and dispatch error action', async () => { + const loginErrorResponse = { + response: { + status: 400, + data: { + login_error: 'something went wrong', + }, + }, + }; + const loginRequest = jest.spyOn(api, 'loginRequest') + .mockImplementation(() => Promise.reject(loginErrorResponse)); + + const dispatched = []; + await runSaga( + { dispatch: (action) => dispatched.push(action) }, + handleLoginRequest, + params, + ); + + expect(loginRequest).toHaveBeenCalledTimes(1); + expect(loggingService.logError).toHaveBeenCalled(); + expect(dispatched).toEqual([ + actions.loginRequestBegin(), + actions.loginRequestFailure(camelCaseObject(loginErrorResponse.response.data)), + ]); + loginRequest.mockClear(); + }); + + it('should handle 500 error code', async () => { + const loginErrorResponse = { + response: { + status: 500, + data: { + errorCode: 'internal-server-error', + }, + }, + }; + + const loginRequest = jest.spyOn(api, 'loginRequest').mockImplementation(() => Promise.reject(loginErrorResponse)); + + const dispatched = []; + await runSaga( + { dispatch: (action) => dispatched.push(action) }, + handleLoginRequest, + params, + ); + + expect(dispatched).toEqual([ + actions.loginRequestBegin(), + actions.loginRequestFailure(camelCaseObject(loginErrorResponse.response.data)), + ]); + loginRequest.mockClear(); + }); +}); diff --git a/src/login/index.js b/src/login/index.js new file mode 100644 index 00000000..769161d2 --- /dev/null +++ b/src/login/index.js @@ -0,0 +1,4 @@ +export { default as LoginPage } from './LoginPage'; +export { default as reducer } from './data/reducers'; +export { default as saga } from './data/sagas'; +export { storeName } from './data/selectors'; diff --git a/src/login/messages.jsx b/src/login/messages.jsx new file mode 100644 index 00000000..909c9c6a --- /dev/null +++ b/src/login/messages.jsx @@ -0,0 +1,162 @@ +import { defineMessages } from '@edx/frontend-platform/i18n'; + +const messages = defineMessages({ + 'sign.in.button': { + id: 'sign.in.button', + defaultMessage: 'Sign in', + description: 'Button label that appears on login page', + }, + 'need.help.signing.in.collapsible.menu': { + id: 'need.help.signing.in.collapsible.menu', + defaultMessage: 'Need help signing in?', + description: 'A button for collapsible need help signing in menu on login page', + }, + 'forgot.password.link': { + id: 'forgot.password.link', + defaultMessage: 'Forgot my password', + description: 'Forgot password link', + }, + 'other.sign.in.issues': { + id: 'other.sign.in.issues', + defaultMessage: 'Other sign-in issues', + description: 'A link that redirects to sign-in issues help', + }, + 'need.other.help.signing.in.collapsible.menu': { + id: 'need.other.help.signing.in.collapsible.menu', + defaultMessage: 'Need other help signing in?', + description: 'A button for collapsible need other help signing in menu on forgot password page', + }, + 'institution.login.button': { + id: 'institution.login.button', + defaultMessage: 'Use my university info', + description: 'shows institutions list', + }, + 'institution.login.page.title': { + id: 'institution.login.page.title', + defaultMessage: 'Sign in with Institution/Campus Credentials', + description: 'Heading of institution page', + }, + 'institution.login.page.sub.heading': { + id: 'institution.login.page.sub.heading', + defaultMessage: 'Choose your institution from the list below:', + description: 'Heading of the institutions list', + }, + 'institution.login.page.back.button': { + id: 'institution.login.page.back.button', + defaultMessage: 'Back to sign in', + description: 'return to login page', + }, + 'create.an.account': { + id: 'create.an.account', + defaultMessage: 'Create an Account', + description: 'Message on button to return to register page', + }, + 'institution.login.sign.in': { + id: 'institution.login.sign.in', + defaultMessage: 'Sign In', + description: 'Sign In text', + }, + 'or.sign.in.with': { + id: 'or.sign.in.with', + defaultMessage: 'or sign in with', + description: 'gives hint about other sign in options', + }, + 'non.compliant.password.title': { + id: 'non.compliant.password.title', + defaultMessage: 'We recently changed our password requirements', + description: 'A title that appears in bold before error message for non-compliant password', + }, + 'first.time.here': { + id: 'first.time.here', + defaultMessage: 'First time here?', + description: 'A question that appears before sign up link', + }, + 'email.label': { + id: 'email.label', + defaultMessage: 'Email', + description: 'Label that appears above email field', + }, + 'email.help.message': { + id: 'email.help.message', + defaultMessage: 'The email address you used to register with edX.', + description: 'Message that appears below email field on login page', + }, + 'enterprise.login.link.text': { + id: 'enterprise.login.link.text', + defaultMessage: 'Sign in with your company or school', + description: 'Company or school login link text.', + }, + 'email.format.validation.message': { + id: 'email.format.validation.message', + defaultMessage: 'The email address you\'ve provided isn\'t formatted correctly.', + description: 'Validation message that appears when email address format is incorrect', + }, + 'password.validation.message': { + id: 'password.validation.message', + defaultMessage: 'Please enter your password.', + description: 'Validation message that appears when password is empty', + }, + 'password.label': { + id: 'password.label', + defaultMessage: 'Password', + description: 'Text that appears above password field or as a placeholder', + }, + 'register.link': { + id: 'register.link', + defaultMessage: 'Create an account', + description: 'Register page link', + }, + 'sign.in.heading': { + id: 'sign.in.heading', + defaultMessage: 'Sign In', + description: 'Sign In text', + }, + // Account Activation Strings + 'account.activation.success.message.title': { + id: 'account.activation.success.message.title', + defaultMessage: 'Success! You have activated your account.', + description: 'Account Activation success message title', + }, + 'account.activation.success.message': { + id: 'account.activation.success.message', + defaultMessage: 'You will now receive email updates and alerts from us related to the courses you are enrolled in. Sign In to continue.', + description: 'Message show to learners when their account has been activated successfully', + }, + 'account.already.activated.message': { + id: 'account.already.activated.message', + defaultMessage: 'This account has already been activated.', + description: 'Message shown when learner account has already been activated', + }, + 'account.activation.error.message.title': { + id: 'account.activation.error.message.title', + defaultMessage: 'Your account could not be activated', + description: 'Account Activation error message title', + }, + 'account.activation.support.link': { + id: 'account.activation.support.link', + defaultMessage: 'contact support', + description: 'Link text used in account activation error message to go to learner help center', + }, + 'internal.server.error.message': { + id: 'internal.server.error.message', + defaultMessage: 'An error has occurred. Try refreshing the page, or check your Internet connection.', + description: 'Error message that appears when server responds with 500 error code', + }, + 'login.rate.limit.reached.message': { + id: 'login.rate.limit.reached.message', + defaultMessage: 'Too many failed login attempts. Try again later.', + description: 'Error message that appears when an anonymous user has made too many failed login attempts', + }, + 'login.failure.header.title': { + id: 'login.failure.header.title', + defaultMessage: 'We couldn\'t sign you in.', + description: 'Login failure header message.', + }, + 'contact.support.link': { + id: 'contact.support.link', + defaultMessage: 'contact {platformName} Support', + description: 'Link text used in inactive user error message to go to learner help center', + }, +}); + +export default messages; diff --git a/src/logistration/tests/AccountActivationMessage.test.jsx b/src/login/tests/AccountActivationMessage.test.jsx similarity index 100% rename from src/logistration/tests/AccountActivationMessage.test.jsx rename to src/login/tests/AccountActivationMessage.test.jsx diff --git a/src/logistration/tests/LoginFailure.test.jsx b/src/login/tests/LoginFailure.test.jsx similarity index 100% rename from src/logistration/tests/LoginFailure.test.jsx rename to src/login/tests/LoginFailure.test.jsx diff --git a/src/logistration/tests/LoginHelpLinks.test.jsx b/src/login/tests/LoginHelpLinks.test.jsx similarity index 100% rename from src/logistration/tests/LoginHelpLinks.test.jsx rename to src/login/tests/LoginHelpLinks.test.jsx diff --git a/src/logistration/tests/LoginPage.test.jsx b/src/login/tests/LoginPage.test.jsx similarity index 86% rename from src/logistration/tests/LoginPage.test.jsx rename to src/login/tests/LoginPage.test.jsx index 22de7529..e8e36192 100644 --- a/src/logistration/tests/LoginPage.test.jsx +++ b/src/login/tests/LoginPage.test.jsx @@ -7,7 +7,7 @@ import configureStore from 'redux-mock-store'; import { getConfig } from '@edx/frontend-platform'; import { IntlProvider, injectIntl } from '@edx/frontend-platform/i18n'; import LoginPage from '../LoginPage'; -import { RenderInstitutionButton } from '../InstitutionLogistration'; +import { RenderInstitutionButton } from '../../common-components'; import { PENDING_STATE } from '../../data/constants'; const IntlLoginPage = injectIntl(LoginPage); @@ -16,9 +16,13 @@ const mockStore = configureStore(); describe('LoginPage', () => { const initialState = { forgotPassword: { status: null }, - logistration: { + login: { + forgotPassword: { status: null }, loginResult: { success: false, redirectUrl: '' }, response_error: null, + }, + commonComponents: { + thirdPartyAuthApiStatus: null, thirdPartyAuthContext: { currentProvider: null, finishAuthUrl: null, @@ -68,8 +72,8 @@ describe('LoginPage', () => { it('should match pending button state snapshot', () => { store = mockStore({ ...initialState, - logistration: { - ...initialState.logistration, + login: { + ...initialState.login, submitState: PENDING_STATE, }, }); @@ -92,10 +96,10 @@ describe('LoginPage', () => { it('should match TPA provider snapshot', () => { store = mockStore({ ...initialState, - logistration: { - ...initialState.logistration, + commonComponents: { + ...initialState.commonComponents, thirdPartyAuthContext: { - ...initialState.logistration.thirdPartyAuthContext, + ...initialState.commonComponents.thirdPartyAuthContext, providers: [appleProvider], }, }, @@ -108,8 +112,8 @@ describe('LoginPage', () => { it('should show error message', () => { store = mockStore({ ...initialState, - logistration: { - ...initialState.logistration, + login: { + ...initialState.login, loginError: { value: 'Email or password is incorrect.' }, }, }); @@ -156,8 +160,8 @@ describe('LoginPage', () => { const dasboardUrl = 'http://test.com/testing-dashboard/'; store = mockStore({ ...initialState, - logistration: { - ...initialState.logistration, + login: { + ...initialState.login, loginResult: { success: true, redirectUrl: dasboardUrl, @@ -174,14 +178,17 @@ describe('LoginPage', () => { const authCompleteUrl = '/auth/complete/google-oauth2/'; store = mockStore({ ...initialState, - logistration: { - ...initialState.logistration, + login: { + ...initialState.login, loginResult: { success: true, redirectUrl: '', }, + }, + commonComponents: { + ...initialState.commonComponents, thirdPartyAuthContext: { - ...initialState.logistration.thirdPartyAuthContext, + ...initialState.commonComponents.thirdPartyAuthContext, finishAuthUrl: authCompleteUrl, }, }, @@ -197,10 +204,10 @@ describe('LoginPage', () => { const loginUrl = '/auth/login/apple-id/?auth_entry=login&next=/dashboard'; store = mockStore({ ...initialState, - logistration: { - ...initialState.logistration, + commonComponents: { + ...initialState.commonComponents, thirdPartyAuthContext: { - ...initialState.logistration.thirdPartyAuthContext, + ...initialState.commonComponents.thirdPartyAuthContext, providers: [{ ...appleProvider, loginUrl, @@ -221,10 +228,10 @@ describe('LoginPage', () => { it('should match third party auth alert', () => { store = mockStore({ ...initialState, - logistration: { - ...initialState.logistration, + commonComponents: { + ...initialState.commonComponents, thirdPartyAuthContext: { - ...initialState.logistration.thirdPartyAuthContext, + ...initialState.commonComponents.thirdPartyAuthContext, currentProvider: 'Apple', platformName: 'edX', }, @@ -241,10 +248,10 @@ describe('LoginPage', () => { it('should display institution login button', () => { store = mockStore({ ...initialState, - logistration: { - ...initialState.logistration, + commonComponents: { + ...initialState.commonComponents, thirdPartyAuthContext: { - ...initialState.logistration.thirdPartyAuthContext, + ...initialState.commonComponents.thirdPartyAuthContext, secondaryProviders: [secondaryProviders], }, }, @@ -261,10 +268,10 @@ describe('LoginPage', () => { it('should display institution login page', () => { store = mockStore({ ...initialState, - logistration: { - ...initialState.logistration, + commonComponents: { + ...initialState.commonComponents, thirdPartyAuthContext: { - ...initialState.logistration.thirdPartyAuthContext, + ...initialState.commonComponents.thirdPartyAuthContext, secondaryProviders: [secondaryProviders], }, }, diff --git a/src/logistration/tests/__snapshots__/LoginPage.test.jsx.snap b/src/login/tests/__snapshots__/LoginPage.test.jsx.snap similarity index 93% rename from src/logistration/tests/__snapshots__/LoginPage.test.jsx.snap rename to src/login/tests/__snapshots__/LoginPage.test.jsx.snap index 08ff7981..0fac4b49 100644 --- a/src/logistration/tests/__snapshots__/LoginPage.test.jsx.snap +++ b/src/login/tests/__snapshots__/LoginPage.test.jsx.snap @@ -345,25 +345,6 @@ exports[`LoginPage should match default section snapshot 1`] = ` -
    -

    - or sign in with -

    -
    - - - ‌ - -
@@ -560,25 +541,6 @@ exports[`LoginPage should match forget password alert message snapshot 1`] = ` -
-

- or sign in with -

-
- - - ‌ - - @@ -746,25 +708,6 @@ exports[`LoginPage should match pending button state snapshot 1`] = ` -
-

- or sign in with -

-
- - - ‌ - - @@ -939,25 +882,6 @@ exports[`LoginPage should show error message 1`] = ` -
-

- or sign in with -

-
- - - ‌ - - diff --git a/src/logistration/data/selectors.js b/src/logistration/data/selectors.js deleted file mode 100644 index 13763bc1..00000000 --- a/src/logistration/data/selectors.js +++ /dev/null @@ -1,25 +0,0 @@ -import { createSelector } from 'reselect'; - -export const storeName = 'logistration'; - -export const logistrationSelector = state => ({ ...state[storeName] }); - -export const loginRequestSelector = createSelector( - logistrationSelector, - logistration => logistration.loginResult, -); - -export const loginErrorSelector = createSelector( - logistrationSelector, - logistration => logistration.loginError, -); - -export const registrationRequestSelector = createSelector( - logistrationSelector, - logistration => logistration.registrationResult, -); - -export const thirdPartyAuthContextSelector = createSelector( - logistrationSelector, - logistration => logistration.thirdPartyAuthContext, -); diff --git a/src/logistration/messages.jsx b/src/logistration/messages.jsx deleted file mode 100644 index 07b8b3eb..00000000 --- a/src/logistration/messages.jsx +++ /dev/null @@ -1,285 +0,0 @@ -import { defineMessages } from '@edx/frontend-platform/i18n'; - -const messages = defineMessages({ - 'logistration.sign.in.button': { - id: 'logistration.sign.in.button', - defaultMessage: 'Sign in', - description: 'Button label that appears on login page', - }, - 'logistration.create.account.button': { - id: 'logistration.create.account.button', - defaultMessage: 'Create Account', - description: 'Button label that appears on register page', - }, - 'logistration.need.help.signing.in.collapsible.menu': { - id: 'logistration.need.help.signing.in.collapsible.menu', - defaultMessage: 'Need help signing in?', - description: 'A button for collapsible need help signing in menu on login page', - }, - 'logistration.need.other.help.signing.in.collapsible.menu': { - id: 'logistration.need.other.help.signing.in.collapsible.menu', - defaultMessage: 'Need other help signing in?', - description: 'A button for collapsible need other help signing in menu on forgot password page', - }, - 'logistration.register.link': { - id: 'logistration.register.link', - defaultMessage: 'Create an account', - description: 'Register page link', - }, - 'logistration.forgot.password.link': { - id: 'logistration.forgot.password.link', - defaultMessage: 'Forgot my password', - description: 'Forgot password link', - }, - 'logistration.already.have.an.edx.account': { - id: 'logistration.already.have.an.edx.account', - defaultMessage: 'Already have an edX account?', - description: 'A message on registration page asking the user if he already has an edX account', - }, - 'logistration.sign.in.hyperlink': { - id: 'logistration.sign.in.hyperlink', - defaultMessage: ' Sign in.', - description: 'Text for the hyperlink that takes user to login page', - }, - 'logistration.create.an.account.using': { - id: 'logistration.create.an.account.using', - defaultMessage: 'Create an account using', - description: 'A message that appears before social auth buttons', - }, - 'logistration.create.a.new.one.here': { - id: 'logistration.create.a.new.one.here', - defaultMessage: 'or create a new one here', - description: 'Text that appears after social auth buttons and before the registration form', - }, - 'logistration.other.sign.in.issues': { - id: 'logistration.other.sign.in.issues', - defaultMessage: 'Other sign-in issues', - description: 'A link that redirects to sign-in issues help', - }, - 'logistration.login.institution.login.button': { - id: 'logistration.login.institution.login.button', - defaultMessage: 'Use my university info', - description: 'shows institutions list', - }, - 'logistration.login.institution.login.page.title': { - id: 'logistration.login.institution.login.page.title', - defaultMessage: 'Sign in with Institution/Campus Credentials', - description: 'Heading of institution page', - }, - 'logistration.institution.login.page.sub.heading': { - id: 'logistration.institution.login.page.sub.heading', - defaultMessage: 'Choose your institution from the list below:', - description: 'Heading of the institutions list', - }, - 'logistration.login.institution.login.page.back.button': { - id: 'logistration.login.institution.login.page.back.button', - defaultMessage: 'Back to sign in', - description: 'return to login page', - }, - 'logistration.register.institution.login.button': { - id: 'logistration.register.institution.login.button', - defaultMessage: 'Use my institution/campus credentials', - description: 'shows institutions list', - }, - 'logistration.register.institution.login.page.title': { - id: 'logistration.register.institution.login.page.title', - defaultMessage: 'Register with Institution/Campus Credentials', - description: 'Heading of institution page', - }, - 'logistration.create.an.account': { - id: 'logistration.create.an.account', - defaultMessage: 'Create an Account', - description: 'Message on button to return to register page', - }, - 'logistration.sign.in.heading': { - id: 'logistration.sign.in.heading', - defaultMessage: 'Sign In', - description: 'Sign In text', - }, - 'logistration.or.sign.in.with': { - id: 'logistration.or.sign.in.with', - defaultMessage: 'or sign in with', - description: 'gives hint about other sign in options', - }, - 'logistration.non.compliant.password.title': { - id: 'logistration.non.compliant.password.title', - defaultMessage: 'We recently changed our password requirements', - description: 'A title that appears in bold before error message for non-compliant password', - }, - 'logistration.first.time.here': { - id: 'logistration.first.time.here', - defaultMessage: 'First time here?', - description: 'A question that appears before sign up link', - }, - 'logistration.login.page.email.label': { - id: 'logistration.login.page.email.label', - defaultMessage: 'Email', - description: 'Label that appears above email field', - }, - 'logistration.register.page.email.label': { - id: 'logistration.register.page.email.label', - defaultMessage: 'Email (required)', - description: 'Label that appears above email field on register page', - }, - 'logistration.email.format.validation.message': { - id: 'logistration.email.format.validation.message', - defaultMessage: 'The email address you\'ve provided isn\'t formatted correctly.', - description: 'Validation message that appears when email address format is incorrect', - }, - 'logistration.email.validation.message': { - id: 'logistration.email.validation.message', - defaultMessage: 'Please enter your Email.', - description: 'Validation message that appears when email address is empty', - }, - 'logistration.email.ratelimit.less.chars.validation.message': { - id: 'logistration.email.ratelimit.less.chars.validation.message', - defaultMessage: 'Email must have 3 characters.', - description: 'Validation message that appears when email address is less than 3 characters', - }, - 'logistration.email.ratelimit.incorrect.format.validation.message': { - id: 'logistration.email.ratelimit.incorrect.format.validation.message', - defaultMessage: 'The email address you provided isn\'t formatted correctly.', - description: 'Validation message that appears when email address is not formatted correctly with no backend validations available.', - }, - 'logistration.email.ratelimit.password.validation.message': { - id: 'logistration.email.ratelimit.password.validation.message', - defaultMessage: 'Your password must contain at least 8 characters', - description: 'Validation message that appears when password is not formatted correctly with no backend validations available.', - }, - 'logistration.email.help.message': { - id: 'logistration.email.help.message', - defaultMessage: 'The email address you used to register with edX.', - description: 'Message that appears below email field on login page', - }, - 'logistration.password': { - id: 'logistration.password', - defaultMessage: 'Password', - description: 'Text that appears above password field or as a placeholder', - }, - 'logistration.password.label': { - id: 'logistration.password.label', - defaultMessage: 'Password (required)', - description: 'Label that appears above password field', - }, - 'logistration.login.page.password.validation.message': { - id: 'logistration.login.page.password.validation.message', - defaultMessage: 'Please enter your password.', - description: 'Validation message that appears when password is empty', - }, - 'logistration.register.page.password.validation.message': { - id: 'logistration.register.page.password.validation.message', - defaultMessage: 'Please enter your Password.', - description: 'Validation message that appears when password is non compliant with edX requirement', - }, - 'logistration.fullname.label': { - id: 'logistration.fullname.label', - defaultMessage: 'Full Name (required)', - description: 'Label that appears above fullname field', - }, - 'logistration.fullname.validation.message': { - id: 'logistration.fullname.validation.message', - defaultMessage: 'Please enter your Full Name.', - description: 'Validation message that appears when fullname is empty', - }, - 'logistration.username.label': { - id: 'logistration.username.label', - defaultMessage: 'Public Username (required)', - description: 'Label that appears above username field', - }, - 'logistration.username.validation.message': { - id: 'logistration.username.validation.message', - defaultMessage: 'Please enter your Public Username.', - description: 'Validation message that appears when username is invalid', - }, - 'logistration.username.ratelimit.less.chars.message': { - id: 'logistration.username.ratelimit.less.chars.message', - defaultMessage: 'Public Username must have atleast 2 characters.', - description: 'Validation message that appears when username is less than 2 characters and with no backend validations available.', - }, - 'logistration.country.validation.message': { - id: 'logistration.country.validation.message', - defaultMessage: 'Select your country or region of residence.', - description: 'Validation message that appears when country is not selected', - }, - 'logistration.support.education.research': { - id: 'logistration.support.education.research', - defaultMessage: 'Support education research by providing additional information', - description: 'Text for a checkbox to ask user for if they are willing to provide extra information for education research', - }, - 'logistration.register.optional.label': { - id: 'logistration.register.optional.label', - defaultMessage: '(optional)', - description: 'Text that appears with optional field labels', - }, - 'logistration.enterprise.login.link.text': { - id: 'logistration.enterprise.login.link.text', - defaultMessage: 'Sign in with your company or school', - description: 'Company or school login link text.', - }, - // Account Activation Strings - 'authn.account.activation.success.message.title': { - id: 'authn.account.activation.success.message.title', - defaultMessage: 'Success! You have activated your account.', - description: 'Account Activation success message title', - }, - 'authn.account.activation.success.message': { - id: 'authn.account.activation.success.message', - defaultMessage: 'You will now receive email updates and alerts from us related to the courses you are enrolled in. Sign In to continue.', - description: 'Message show to learners when their account has been activated successfully', - }, - 'authn.account.already.activated.message': { - id: 'authn.account.already.activated.message', - defaultMessage: 'This account has already been activated.', - description: 'Message shown when learner account has already been activated', - }, - 'authn.account.activation.error.message.title': { - id: 'authn.account.activation.error.message.title', - defaultMessage: 'Your account could not be activated', - description: 'Account Activation error message title', - }, - 'authn.account.activation.support.link': { - id: 'authn.account.activation.support.link', - defaultMessage: 'contact support', - description: 'Link text used in account activation error message to go to learner help center', - }, - // Confirmation Alert Message - 'authn.forgot.password.confirmation.title': { - id: 'authn.forgot.password.confirmation.title', - defaultMessage: 'Check Your Email', - description: 'Forgot password confirmation message title', - }, - 'authn.forgot.password.confirmation.support.link': { - id: 'authn.forgot.password.confirmation.support.link', - defaultMessage: 'contact technical support', - description: 'Technical support link text', - }, - 'authn.forgot.password.confirmation.info': { - id: 'authn.forgot.password.confirmation.info', - defaultMessage: 'If you do not receive a password reset message after 1 minute, verify that you entered the correct ' - + 'email address, or check your spam folder.', - description: 'Part of message that appears after user requests password change', - }, - // Login Failure Messages - 'logistration.login.failure.header.title': { - id: 'logistration.login.failure.header.title', - defaultMessage: 'We couldn\'t sign you in.', - description: 'Login failure header message.', - }, - 'logistration.contact.support.link': { - id: 'logistration.contact.support.link', - defaultMessage: 'contact {platformName} Support', - description: 'Link text used in inactive user error message to go to learner help center', - }, - 'login.rate.limit.reached.message': { - id: 'login.rate.limit.reached.message', - defaultMessage: 'Too many failed login attempts. Try again later.', - description: 'Error message that appears when an anonymous user has made too many failed login attempts', - }, - 'authn.internal.server.error.message': { - id: 'authn.internal.server.error.message', - defaultMessage: 'An error has occurred. Try refreshing the page, or check your Internet connection.', - description: 'Error message that appears when server responds with 500 error code', - }, -}); - -export default messages; diff --git a/src/logistration/RegistrationFailure.jsx b/src/register/RegistrationFailure.jsx similarity index 94% rename from src/logistration/RegistrationFailure.jsx rename to src/register/RegistrationFailure.jsx index 780a3454..82d8528d 100644 --- a/src/logistration/RegistrationFailure.jsx +++ b/src/register/RegistrationFailure.jsx @@ -32,7 +32,7 @@ const RegistrationFailureMessage = (props) => { diff --git a/src/logistration/RegistrationPage.jsx b/src/register/RegistrationPage.jsx similarity index 90% rename from src/logistration/RegistrationPage.jsx rename to src/register/RegistrationPage.jsx index 8606072d..3b6d5201 100644 --- a/src/logistration/RegistrationPage.jsx +++ b/src/register/RegistrationPage.jsx @@ -15,14 +15,18 @@ import { } from '@edx/frontend-platform/i18n'; import camelCase from 'lodash.camelcase'; +import { getThirdPartyAuthContext } from '../common-components/data/actions'; import { - getThirdPartyAuthContext, registerNewUser, fetchRegistrationForm, fetchRealtimeValidations, } from './data/actions'; -import { registrationRequestSelector, thirdPartyAuthContextSelector } from './data/selectors'; -import { RedirectLogistration } from '../common-components'; +import { registrationRequestSelector } from './data/selectors'; +import { thirdPartyAuthContextSelector } from '../common-components/data/selectors'; +import { + RedirectLogistration, SocialAuthProviders, ThirdPartyAuthAlert, RenderInstitutionButton, + InstitutionLogistration, +} from '../common-components'; import RegistrationFailure from './RegistrationFailure'; import { DEFAULT_REDIRECT_URL, @@ -34,9 +38,6 @@ import { REGISTRATION_OPTIONAL_MAP, REGISTRATION_EXTRA_FIELDS, } from '../data/constants'; -import SocialAuthProviders from './SocialAuthProviders'; -import ThirdPartyAuthAlert from './ThirdPartyAuthAlert'; -import InstitutionLogistration, { RenderInstitutionButton } from './InstitutionLogistration'; import messages from './messages'; import { processLink } from '../data/utils/dataUtils'; @@ -262,11 +263,11 @@ class RegistrationPage extends React.Component { if (this.props.statusCode !== 403 && validations && validations.email) { validationErrorsAlertMessages.email = [{ user_message: validations.email }]; } else if (value.length < 1) { - const errorEmpty = this.generateUserMessage(value.length < 1, 'logistration.email.validation.message'); + const errorEmpty = this.generateUserMessage(value.length < 1, 'email.validation.message'); validationErrorsAlertMessages.email = errorEmpty; } else { - const errorCharlength = this.generateUserMessage(value.length <= 2, 'logistration.email.ratelimit.less.chars.validation.message'); - const formatError = this.generateUserMessage(!value.match(/^([\w.%+-]+)@([\w-]+\.)+([\w]{2,})$/i), 'logistration.email.ratelimit.incorrect.format.validation.message'); + const errorCharlength = this.generateUserMessage(value.length <= 2, 'email.ratelimit.less.chars.validation.message'); + const formatError = this.generateUserMessage(!value.match(/^([\w.%+-]+)@([\w-]+\.)+([\w]{2,})$/i), 'email.ratelimit.incorrect.format.validation.message'); validationErrorsAlertMessages.email = errorCharlength; validationErrorsAlertMessages.emailFormat = formatError; } @@ -275,7 +276,7 @@ class RegistrationPage extends React.Component { if (this.props.statusCode !== 403 && validations && validations.name) { validationErrorsAlertMessages.name = [{ user_message: validations.name }]; } else if (value.length < 1) { - const errorEmpty = this.generateUserMessage(value.length < 1, 'logistration.fullname.validation.message'); + const errorEmpty = this.generateUserMessage(value.length < 1, 'fullname.validation.message'); validationErrorsAlertMessages.name = errorEmpty; } else { validationErrorsAlertMessages.name = [{ user_message: '' }]; @@ -285,10 +286,10 @@ class RegistrationPage extends React.Component { if (this.props.statusCode !== 403 && validations && validations.username) { validationErrorsAlertMessages.username = [{ user_message: validations.username }]; } else if (value.length < 1) { - const errorEmpty = this.generateUserMessage(value.length < 1, 'logistration.username.validation.message'); + const errorEmpty = this.generateUserMessage(value.length < 1, 'username.validation.message'); validationErrorsAlertMessages.username = errorEmpty; } else { - const errorCharLength = this.generateUserMessage(value.length <= 1, 'logistration.username.ratelimit.less.chars.message'); + const errorCharLength = this.generateUserMessage(value.length <= 1, 'username.ratelimit.less.chars.message'); validationErrorsAlertMessages.username = errorCharLength; } break; @@ -296,10 +297,10 @@ class RegistrationPage extends React.Component { if (this.props.statusCode !== 403 && validations && validations.password) { validationErrorsAlertMessages.password = [{ user_message: validations.password }]; } else if (value.length < 1) { - const errorEmpty = this.generateUserMessage(value.length < 1, 'logistration.register.page.password.validation.message'); + const errorEmpty = this.generateUserMessage(value.length < 1, 'register.page.password.validation.message'); validationErrorsAlertMessages.password = errorEmpty; } else { - const errorCharLength = this.generateUserMessage(value.length < 8, 'logistration.email.ratelimit.password.validation.message'); + const errorCharLength = this.generateUserMessage(value.length < 8, 'email.ratelimit.password.validation.message'); validationErrorsAlertMessages.password = errorCharLength; } break; @@ -307,7 +308,7 @@ class RegistrationPage extends React.Component { if (this.props.statusCode !== 403 && validations && validations.country) { validationErrorsAlertMessages.country = [{ user_message: validations.country }]; } else { - const emptyError = this.generateUserMessage(value === '', 'logistration.country.validation.message'); + const emptyError = this.generateUserMessage(value === '', 'country.validation.message'); validationErrorsAlertMessages.country = emptyError; } break; @@ -469,7 +470,7 @@ class RegistrationPage extends React.Component { className={cssClass} > @@ -493,13 +494,13 @@ class RegistrationPage extends React.Component {

- {intl.formatMessage(messages['logistration.create.a.new.one.here'])} + {intl.formatMessage(messages['create.a.new.one.here'])}

); @@ -536,8 +537,8 @@ class RegistrationPage extends React.Component { ); } @@ -561,17 +562,17 @@ class RegistrationPage extends React.Component { /> )}
- {intl.formatMessage(messages['logistration.already.have.an.edx.account'])} - {intl.formatMessage(messages['logistration.sign.in.hyperlink'])} + {intl.formatMessage(messages['already.have.an.edx.account'])} + {intl.formatMessage(messages['sign.in.hyperlink'])}
{(providers.length || secondaryProviders.length || thirdPartyAuthApiStatus === PENDING_STATE) && !currentProvider ? (

- {intl.formatMessage(messages['logistration.create.an.account.using'])} + {intl.formatMessage(messages['create.an.account.using'])}

- ) :

{intl.formatMessage(messages['logistration.create.an.account'])}

} + ) :

{intl.formatMessage(messages['create.an.account'])}

} {this.renderThirdPartyAuth(providers, secondaryProviders, currentProvider, thirdPartyAuthApiStatus, intl)}
{ this.state.enableOptionalField ? this.addExtraOptionalFields() : null} @@ -689,7 +690,7 @@ class RegistrationPage extends React.Component { className="btn-primary mt-10" state={submitState} labels={{ - default: intl.formatMessage(messages['logistration.create.account.button']), + default: intl.formatMessage(messages['create.account.button']), }} onClick={this.handleSubmit} /> @@ -770,14 +771,14 @@ const mapStateToProps = state => { const registrationResult = registrationRequestSelector(state); const thirdPartyAuthContext = thirdPartyAuthContextSelector(state); return { - registrationError: state.logistration.registrationError, - submitState: state.logistration.submitState, - thirdPartyAuthApiStatus: state.logistration.thirdPartyAuthApiStatus, + registrationError: state.register.registrationError, + submitState: state.register.submitState, + thirdPartyAuthApiStatus: state.commonComponents.thirdPartyAuthApiStatus, registrationResult, thirdPartyAuthContext, - formData: state.logistration.formData, - validations: state.logistration.validations, - statusCode: state.logistration.statusCode, + formData: state.register.formData, + validations: state.register.validations, + statusCode: state.register.statusCode, }; }; diff --git a/src/logistration/data/actions.js b/src/register/data/actions.js similarity index 60% rename from src/logistration/data/actions.js rename to src/register/data/actions.js index b15479de..aee161da 100644 --- a/src/logistration/data/actions.js +++ b/src/register/data/actions.js @@ -1,8 +1,6 @@ import { AsyncActionType } from '../../data/utils'; export const REGISTER_NEW_USER = new AsyncActionType('REGISTRATION', 'REGISTER_NEW_USER'); -export const LOGIN_REQUEST = new AsyncActionType('LOGIN', 'REQUEST'); -export const THIRD_PARTY_AUTH_CONTEXT = new AsyncActionType('THIRD_PARTY_AUTH', 'GET_THIRD_PARTY_AUTH_CONTEXT'); export const REGISTER_FORM = new AsyncActionType('REGISTRATION', 'GET_FORM_FIELDS'); export const REGISTER_FORM_VALIDATIONS = new AsyncActionType('REGISTRATION', 'GET_FORM_VALIDATIONS'); @@ -28,45 +26,6 @@ export const registerNewUserFailure = (error) => ({ payload: { error }, }); -// Login -export const loginRequest = creds => ({ - type: LOGIN_REQUEST.BASE, - payload: { creds }, -}); - -export const loginRequestBegin = () => ({ - type: LOGIN_REQUEST.BEGIN, -}); - -export const loginRequestSuccess = (redirectUrl, success) => ({ - type: LOGIN_REQUEST.SUCCESS, - payload: { redirectUrl, success }, -}); - -export const loginRequestFailure = (loginError) => ({ - type: LOGIN_REQUEST.FAILURE, - payload: { loginError }, -}); - -// Third party auth context -export const getThirdPartyAuthContext = (urlParams) => ({ - type: THIRD_PARTY_AUTH_CONTEXT.BASE, - payload: { urlParams }, -}); - -export const getThirdPartyAuthContextBegin = () => ({ - type: THIRD_PARTY_AUTH_CONTEXT.BEGIN, -}); - -export const getThirdPartyAuthContextSuccess = (thirdPartyAuthContext) => ({ - type: THIRD_PARTY_AUTH_CONTEXT.SUCCESS, - payload: { thirdPartyAuthContext }, -}); - -export const getThirdPartyAuthContextFailure = () => ({ - type: THIRD_PARTY_AUTH_CONTEXT.FAILURE, -}); - // Registration Form Fields export const fetchRegistrationForm = () => ({ type: REGISTER_FORM.BASE, diff --git a/src/logistration/data/reducers.js b/src/register/data/reducers.js similarity index 58% rename from src/logistration/data/reducers.js rename to src/register/data/reducers.js index 86f9a933..02978434 100644 --- a/src/logistration/data/reducers.js +++ b/src/register/data/reducers.js @@ -1,16 +1,12 @@ import { REGISTER_NEW_USER, - LOGIN_REQUEST, - THIRD_PARTY_AUTH_CONTEXT, REGISTER_FORM, REGISTER_FORM_VALIDATIONS, } from './actions'; -import { DEFAULT_STATE, PENDING_STATE, COMPLETE_STATE } from '../../data/constants'; +import { DEFAULT_STATE, PENDING_STATE } from '../../data/constants'; export const defaultState = { - loginError: null, - loginResult: {}, registrationError: null, registrationResult: {}, formData: null, @@ -36,38 +32,6 @@ const reducer = (state = defaultState, action) => { registrationError: action.payload.error, submitState: DEFAULT_STATE, }; - case LOGIN_REQUEST.BEGIN: - return { - ...state, - submitState: PENDING_STATE, - }; - case LOGIN_REQUEST.SUCCESS: - return { - ...state, - loginResult: action.payload, - }; - case LOGIN_REQUEST.FAILURE: - return { - ...state, - loginError: action.payload.loginError, - submitState: DEFAULT_STATE, - }; - case THIRD_PARTY_AUTH_CONTEXT.BEGIN: - return { - ...state, - thirdPartyAuthApiStatus: PENDING_STATE, - }; - case THIRD_PARTY_AUTH_CONTEXT.SUCCESS: - return { - ...state, - thirdPartyAuthContext: action.payload.thirdPartyAuthContext, - thirdPartyAuthApiStatus: COMPLETE_STATE, - }; - case THIRD_PARTY_AUTH_CONTEXT.FAILURE: - return { - ...state, - thirdPartyAuthApiStatus: COMPLETE_STATE, - }; case REGISTER_FORM.BEGIN: return { ...state, diff --git a/src/logistration/data/sagas.js b/src/register/data/sagas.js similarity index 57% rename from src/logistration/data/sagas.js rename to src/register/data/sagas.js index bc73bf09..f1c55427 100644 --- a/src/logistration/data/sagas.js +++ b/src/register/data/sagas.js @@ -1,8 +1,6 @@ import { call, put, takeEvery } from 'redux-saga/effects'; -import { camelCaseObject } from '@edx/frontend-platform'; import { logError } from '@edx/frontend-platform/logging'; -import { FORBIDDEN_REQUEST, INTERNAL_SERVER_ERROR } from './constants'; // Actions import { @@ -10,18 +8,10 @@ import { registerNewUserBegin, registerNewUserFailure, registerNewUserSuccess, - LOGIN_REQUEST, - loginRequestBegin, - loginRequestFailure, - loginRequestSuccess, REGISTER_FORM_VALIDATIONS, fetchRealtimeValidationsBegin, fetchRealtimeValidationsSuccess, fetchRealtimeValidationsFailure, - THIRD_PARTY_AUTH_CONTEXT, - getThirdPartyAuthContextBegin, - getThirdPartyAuthContextSuccess, - getThirdPartyAuthContextFailure, REGISTER_FORM, fetchRegistrationFormBegin, fetchRegistrationFormSuccess, @@ -32,9 +22,7 @@ import { import { getFieldsValidations, getRegistrationForm, - getThirdPartyAuthContext, registerRequest, - loginRequest, } from './service'; export function* handleNewUserRegistration(action) { @@ -56,46 +44,6 @@ export function* handleNewUserRegistration(action) { } } -export function* handleLoginRequest(action) { - try { - yield put(loginRequestBegin()); - - const { redirectUrl, success } = yield call(loginRequest, action.payload.creds); - - yield put(loginRequestSuccess( - redirectUrl, - success, - )); - } catch (e) { - const statusCodes = [400]; - if (e.response) { - const { status } = e.response; - if (statusCodes.includes(status)) { - yield put(loginRequestFailure(camelCaseObject(e.response.data))); - } else if (status === 403) { - yield put(loginRequestFailure({ errorCode: FORBIDDEN_REQUEST })); - } else { - yield put(loginRequestFailure({ errorCode: INTERNAL_SERVER_ERROR })); - } - } - logError(e); - } -} - -export function* fetchThirdPartyAuthContext(action) { - try { - yield put(getThirdPartyAuthContextBegin()); - const { thirdPartyAuthContext } = yield call(getThirdPartyAuthContext, action.payload.urlParams); - - yield put(getThirdPartyAuthContextSuccess( - thirdPartyAuthContext, - )); - } catch (e) { - yield put(getThirdPartyAuthContextFailure()); - logError(e); - } -} - export function* fetchRegistrationForm() { try { yield put(fetchRegistrationFormBegin()); @@ -129,8 +77,6 @@ export function* fetchRealtimeValidations(action) { export default function* saga() { yield takeEvery(REGISTER_NEW_USER.BASE, handleNewUserRegistration); - yield takeEvery(LOGIN_REQUEST.BASE, handleLoginRequest); - yield takeEvery(THIRD_PARTY_AUTH_CONTEXT.BASE, fetchThirdPartyAuthContext); yield takeEvery(REGISTER_FORM.BASE, fetchRegistrationForm); yield takeEvery(REGISTER_FORM_VALIDATIONS.BASE, fetchRealtimeValidations); } diff --git a/src/register/data/selectors.js b/src/register/data/selectors.js new file mode 100644 index 00000000..1935831b --- /dev/null +++ b/src/register/data/selectors.js @@ -0,0 +1,10 @@ +import { createSelector } from 'reselect'; + +export const storeName = 'register'; + +export const registerSelector = state => ({ ...state[storeName] }); + +export const registrationRequestSelector = createSelector( + registerSelector, + register => register.registrationResult, +); diff --git a/src/logistration/data/service.js b/src/register/data/service.js similarity index 58% rename from src/logistration/data/service.js rename to src/register/data/service.js index c95be123..87f19917 100644 --- a/src/logistration/data/service.js +++ b/src/register/data/service.js @@ -1,4 +1,4 @@ -import { camelCaseObject, convertKeyNames, getConfig } from '@edx/frontend-platform'; +import { getConfig } from '@edx/frontend-platform'; import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth'; import querystring from 'querystring'; @@ -24,48 +24,6 @@ export async function registerRequest(registrationInformation) { }; } -export async function loginRequest(creds) { - const requestConfig = { - headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, - isPublic: true, - }; - - const { data } = await getAuthenticatedHttpClient() - .post( - `${getConfig().LMS_BASE_URL}/user_api/v1/account/login_session/`, - querystring.stringify(creds), - requestConfig, - ) - .catch((e) => { - throw (e); - }); - - return { - redirectUrl: data.redirect_url || `${getConfig().LMS_BASE_URL}/dashboard`, - success: data.success || false, - }; -} - -export async function getThirdPartyAuthContext(urlParams) { - const requestConfig = { - headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, - params: urlParams, - isPublic: true, - }; - - const { data } = await getAuthenticatedHttpClient() - .get( - `${getConfig().LMS_BASE_URL}/api/third_party_auth_context`, - requestConfig, - ) - .catch((e) => { - throw (e); - }); - return { - thirdPartyAuthContext: camelCaseObject(convertKeyNames(data, { fullname: 'name' })), - }; -} - export async function getRegistrationForm() { const requestConfig = { headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, diff --git a/src/logistration/data/tests/sagas.test.js b/src/register/data/tests/sagas.test.js similarity index 57% rename from src/logistration/data/tests/sagas.test.js rename to src/register/data/tests/sagas.test.js index 502821e1..a2d5457b 100644 --- a/src/logistration/data/tests/sagas.test.js +++ b/src/register/data/tests/sagas.test.js @@ -1,78 +1,16 @@ import { runSaga } from 'redux-saga'; -import { camelCaseObject } from '@edx/frontend-platform'; - import * as actions from '../actions'; import { fetchRealtimeValidations, - fetchThirdPartyAuthContext, fetchRegistrationForm, - handleLoginRequest, handleNewUserRegistration, } from '../sagas'; import * as api from '../service'; import initializeMockLogging from '../../../setupTest'; -import { FORBIDDEN_REQUEST, INTERNAL_SERVER_ERROR } from '../constants'; const { loggingService } = initializeMockLogging(); -describe('fetchThirdPartyAuthContext', () => { - const params = { - payload: { urlParams: {} }, - }; - - const data = { - currentProvider: null, - providers: [], - secondaryProviders: [], - finishAuthUrl: null, - pipelineUserDetails: {}, - }; - - beforeEach(() => { - loggingService.logError.mockReset(); - }); - - it('should call service and dispatch success action', async () => { - const getThirdPartyAuthContext = jest.spyOn(api, 'getThirdPartyAuthContext') - .mockImplementation(() => Promise.resolve({ thirdPartyAuthContext: data })); - - const dispatched = []; - await runSaga( - { dispatch: (action) => dispatched.push(action) }, - fetchThirdPartyAuthContext, - params, - ); - - expect(getThirdPartyAuthContext).toHaveBeenCalledTimes(1); - expect(dispatched).toEqual([ - actions.getThirdPartyAuthContextBegin(), - actions.getThirdPartyAuthContextSuccess(data), - ]); - getThirdPartyAuthContext.mockClear(); - }); - - it('should call service and dispatch error action', async () => { - const getThirdPartyAuthContext = jest.spyOn(api, 'getThirdPartyAuthContext') - .mockImplementation(() => Promise.reject(new Error('something went wrong'))); - - const dispatched = []; - await runSaga( - { dispatch: (action) => dispatched.push(action) }, - fetchThirdPartyAuthContext, - params, - ); - - expect(getThirdPartyAuthContext).toHaveBeenCalledTimes(1); - expect(loggingService.logError).toHaveBeenCalled(); - expect(dispatched).toEqual([ - actions.getThirdPartyAuthContextBegin(), - actions.getThirdPartyAuthContextFailure(), - ]); - getThirdPartyAuthContext.mockClear(); - }); -}); - describe('fetchRegistrationForm', () => { const data = { fields: [{ @@ -212,105 +150,6 @@ describe('fetchRealtimeValidations', () => { }); }); -describe('handleLoginRequest', () => { - const params = { - payload: { - formData: { - email: 'test@test.com', - password: 'test-password', - }, - }, - }; - - const testErrorResponse = async (loginErrorResponse, expectedDispatchers) => { - const loginRequest = jest.spyOn(api, 'loginRequest').mockImplementation(() => Promise.reject(loginErrorResponse)); - - const dispatched = []; - await runSaga( - { dispatch: (action) => dispatched.push(action) }, - handleLoginRequest, - params, - ); - - expect(loginRequest).toHaveBeenCalledTimes(1); - expect(loggingService.logError).toHaveBeenCalled(); - expect(dispatched).toEqual(expectedDispatchers); - loginRequest.mockClear(); - }; - - beforeEach(() => { - loggingService.logError.mockReset(); - }); - - it('should call service and dispatch success action', async () => { - const data = { redirectUrl: '/dashboard', success: true }; - const loginRequest = jest.spyOn(api, 'loginRequest') - .mockImplementation(() => Promise.resolve(data)); - - const dispatched = []; - await runSaga( - { dispatch: (action) => dispatched.push(action) }, - handleLoginRequest, - params, - ); - - expect(loginRequest).toHaveBeenCalledTimes(1); - expect(dispatched).toEqual([ - actions.loginRequestBegin(), - actions.loginRequestSuccess(data.redirectUrl, data.success), - ]); - loginRequest.mockClear(); - }); - - it('should call service and dispatch error action', async () => { - const loginErrorResponse = { - response: { - status: 400, - data: { - login_error: 'something went wrong', - }, - }, - }; - - await testErrorResponse(loginErrorResponse, [ - actions.loginRequestBegin(), - actions.loginRequestFailure(camelCaseObject(loginErrorResponse.response.data)), - ]); - }); - - it('should handle rate limit error code', async () => { - const loginErrorResponse = { - response: { - status: 403, - data: { - errorCode: FORBIDDEN_REQUEST, - }, - }, - }; - - await testErrorResponse(loginErrorResponse, [ - actions.loginRequestBegin(), - actions.loginRequestFailure(loginErrorResponse.response.data), - ]); - }); - - it('should handle 500 error code', async () => { - const loginErrorResponse = { - response: { - status: 500, - data: { - errorCode: INTERNAL_SERVER_ERROR, - }, - }, - }; - - await testErrorResponse(loginErrorResponse, [ - actions.loginRequestBegin(), - actions.loginRequestFailure(loginErrorResponse.response.data), - ]); - }); -}); - describe('handleNewUserRegistration', () => { const params = { payload: { diff --git a/src/logistration/index.js b/src/register/index.js similarity index 80% rename from src/logistration/index.js rename to src/register/index.js index a9e89934..496c0f7b 100644 --- a/src/logistration/index.js +++ b/src/register/index.js @@ -1,4 +1,3 @@ -export { default as LoginPage } from './LoginPage'; export { default as RegistrationPage } from './RegistrationPage'; export { default as reducer } from './data/reducers'; export { default as saga } from './data/sagas'; diff --git a/src/register/messages.jsx b/src/register/messages.jsx new file mode 100644 index 00000000..807df097 --- /dev/null +++ b/src/register/messages.jsx @@ -0,0 +1,121 @@ +import { defineMessages } from '@edx/frontend-platform/i18n'; + +const messages = defineMessages({ + 'create.account.button': { + id: 'create.account.button', + defaultMessage: 'Create Account', + description: 'Button label that appears on register page', + }, + 'already.have.an.edx.account': { + id: 'already.have.an.edx.account', + defaultMessage: 'Already have an edX account?', + description: 'A message on registration page asking the user if he already has an edX account', + }, + 'sign.in.hyperlink': { + id: 'sign.in.hyperlink', + defaultMessage: ' Sign in.', + description: 'Text for the hyperlink that takes user to login page', + }, + 'create.an.account.using': { + id: 'create.an.account.using', + defaultMessage: 'Create an account using', + description: 'A message that appears before social auth buttons', + }, + 'create.a.new.one.here': { + id: 'create.a.new.one.here', + defaultMessage: 'or create a new one here', + description: 'Text that appears after social auth buttons and before the registration form', + }, + 'register.institution.login.button': { + id: 'register.institution.login.button', + defaultMessage: 'Use my institution/campus credentials', + description: 'shows institutions list', + }, + 'register.institution.login.page.title': { + id: 'register.institution.login.page.title', + defaultMessage: 'Register with Institution/Campus Credentials', + description: 'Heading of institution page', + }, + 'create.an.account': { + id: 'create.an.account', + defaultMessage: 'Create an Account', + description: 'Message on button to return to register page', + }, + 'register.page.email.label': { + id: 'register.page.email.label', + defaultMessage: 'Email (required)', + description: 'Label that appears above email field on register page', + }, + 'email.validation.message': { + id: 'email.validation.message', + defaultMessage: 'Please enter your Email.', + description: 'Validation message that appears when email address is empty', + }, + 'email.ratelimit.less.chars.validation.message': { + id: 'email.ratelimit.less.chars.validation.message', + defaultMessage: 'Email must have 3 characters.', + description: 'Validation message that appears when email address is less than 3 characters', + }, + 'email.ratelimit.incorrect.format.validation.message': { + id: 'email.ratelimit.incorrect.format.validation.message', + defaultMessage: 'The email address you provided isn\'t formatted correctly.', + description: 'Validation message that appears when email address is not formatted correctly with no backend validations available.', + }, + 'email.ratelimit.password.validation.message': { + id: 'email.ratelimit.password.validation.message', + defaultMessage: 'Your password must contain at least 8 characters', + description: 'Validation message that appears when password is not formatted correctly with no backend validations available.', + }, + 'password.label': { + id: 'password.label', + defaultMessage: 'Password (required)', + description: 'Label that appears above password field', + }, + 'register.page.password.validation.message': { + id: 'register.page.password.validation.message', + defaultMessage: 'Please enter your Password.', + description: 'Validation message that appears when password is non compliant with edX requirement', + }, + 'fullname.label': { + id: 'fullname.label', + defaultMessage: 'Full Name (required)', + description: 'Label that appears above fullname field', + }, + 'fullname.validation.message': { + id: 'fullname.validation.message', + defaultMessage: 'Please enter your Full Name.', + description: 'Validation message that appears when fullname is empty', + }, + 'username.label': { + id: 'username.label', + defaultMessage: 'Public Username (required)', + description: 'Label that appears above username field', + }, + 'username.validation.message': { + id: 'username.validation.message', + defaultMessage: 'Please enter your Public Username.', + description: 'Validation message that appears when username is invalid', + }, + 'username.ratelimit.less.chars.message': { + id: 'username.ratelimit.less.chars.message', + defaultMessage: 'Public Username must have atleast 2 characters.', + description: 'Validation message that appears when username is less than 2 characters and with no backend validations available.', + }, + 'country.validation.message': { + id: 'country.validation.message', + defaultMessage: 'Select your country or region of residence.', + description: 'Validation message that appears when country is not selected', + }, + 'support.education.research': { + id: 'support.education.research', + defaultMessage: 'Support education research by providing additional information', + description: 'Text for a checkbox to ask user for if they are willing to provide extra information for education research', + }, + 'register.optional.label': { + id: 'register.optional.label', + defaultMessage: '(optional)', + description: 'Text that appears with optional field labels', + }, +}); + +export default messages; diff --git a/src/logistration/tests/RegistrationPage.test.jsx b/src/register/tests/RegistrationPage.test.jsx similarity index 91% rename from src/logistration/tests/RegistrationPage.test.jsx rename to src/register/tests/RegistrationPage.test.jsx index 0e58d909..3f6b1a00 100644 --- a/src/logistration/tests/RegistrationPage.test.jsx +++ b/src/register/tests/RegistrationPage.test.jsx @@ -7,7 +7,7 @@ import { getConfig } from '@edx/frontend-platform'; import { IntlProvider, injectIntl, configure } from '@edx/frontend-platform/i18n'; import RegistrationPage from '../RegistrationPage'; -import { RenderInstitutionButton } from '../InstitutionLogistration'; +import { RenderInstitutionButton } from '../../common-components'; import { PENDING_STATE } from '../../data/constants'; import { fetchRegistrationForm, fetchRealtimeValidations, registerNewUser } from '../data/actions'; @@ -16,15 +16,8 @@ const mockStore = configureStore(); describe('./RegistrationPage.js', () => { const initialState = { - logistration: { + register: { registrationResult: { success: false, redirectUrl: '' }, - thirdPartyAuthContext: { - platformName: 'openedX', - currentProvider: null, - finishAuthUrl: null, - providers: [], - secondaryProviders: [], - }, registrationError: null, formData: { fields: [{ @@ -38,6 +31,16 @@ describe('./RegistrationPage.js', () => { }], }, }, + commonComponents: { + thirdPartyAuthApiStatus: null, + thirdPartyAuthContext: { + platformName: 'openedX', + currentProvider: null, + finishAuthUrl: null, + providers: [], + secondaryProviders: [], + }, + }, }; let props = {}; @@ -94,11 +97,11 @@ describe('./RegistrationPage.js', () => { }; store = mockStore({ ...initialState, - logistration: { - ...initialState.logistration, + register: { + ...initialState.register, formData: { fields: [ - ...initialState.logistration.formData.fields, + ...initialState.register.formData.fields, { label: 'The country or region where you live.', name: 'country', @@ -141,8 +144,8 @@ describe('./RegistrationPage.js', () => { it('should show optional fields section on optional check enabled', () => { store = mockStore({ ...initialState, - logistration: { - ...initialState.logistration, + register: { + ...initialState.register, formData: { fields: [ { @@ -299,8 +302,8 @@ describe('./RegistrationPage.js', () => { it('should match pending button state snapshot', () => { store = mockStore({ ...initialState, - logistration: { - ...initialState.logistration, + register: { + ...initialState.register, submitState: PENDING_STATE, }, }); @@ -312,9 +315,10 @@ describe('./RegistrationPage.js', () => { it('should match TPA provider snapshot', () => { store = mockStore({ ...initialState, - logistration: { - ...initialState.logistration, + commonComponents: { + ...initialState.commonComponents, thirdPartyAuthContext: { + ...initialState.commonComponents.thirdPartyAuthContext, providers: [appleProvider], }, }, @@ -327,10 +331,10 @@ describe('./RegistrationPage.js', () => { it('should display no password field when current provider is present', () => { store = mockStore({ ...initialState, - logistration: { - ...initialState.logistration, + commonComponents: { + ...initialState.commonComponents, thirdPartyAuthContext: { - ...initialState.logistration.thirdPartyAuthContext, + ...initialState.commonComponents.thirdPartyAuthContext, currentProvider: 'Google', }, }, @@ -362,8 +366,8 @@ describe('./RegistrationPage.js', () => { const dasboardUrl = 'http://test.com/testing-dashboard/'; store = mockStore({ ...initialState, - logistration: { - ...initialState.logistration, + register: { + ...initialState.register, registrationResult: { success: true, redirectUrl: dasboardUrl, @@ -379,10 +383,10 @@ describe('./RegistrationPage.js', () => { it('should display institution register button', () => { store = mockStore({ ...initialState, - logistration: { - ...initialState.logistration, + commonComponents: { + ...initialState.commonComponents, thirdPartyAuthContext: { - ...initialState.logistration.thirdPartyAuthContext, + ...initialState.commonComponents.thirdPartyAuthContext, secondaryProviders: [secondaryProviders], }, }, @@ -394,10 +398,10 @@ describe('./RegistrationPage.js', () => { it('should not display institution register button', () => { store = mockStore({ ...initialState, - logistration: { - ...initialState.logistration, + commonComponents: { + ...initialState.commonComponents, thirdPartyAuthContext: { - ...initialState.logistration.thirdPartyAuthContext, + ...initialState.commonComponents.thirdPartyAuthContext, secondaryProviders: [secondaryProviders], }, }, @@ -411,14 +415,17 @@ describe('./RegistrationPage.js', () => { const authCompleteUrl = '/auth/complete/google-oauth2/'; store = mockStore({ ...initialState, - logistration: { - ...initialState.logistration, + register: { + ...initialState.register, registrationResult: { success: true, redirectUrl: '', }, + }, + commonComponents: { + ...initialState.commonComponents, thirdPartyAuthContext: { - ...initialState.logistration.thirdPartyAuthContext, + ...initialState.commonComponents.thirdPartyAuthContext, finishAuthUrl: authCompleteUrl, }, }, @@ -435,14 +442,18 @@ describe('./RegistrationPage.js', () => { const registerUrl = '/auth/login/apple-id/?auth_entry=register&next=/dashboard'; store = mockStore({ ...initialState, - logistration: { - ...initialState.logistration, + commonComponents: { + ...initialState.commonComponents, thirdPartyAuthContext: { + ...initialState.commonComponents.thirdPartyAuthContext, providers: [{ ...appleProvider, registerUrl, }], }, + }, + register: { + ...initialState.register, formData: { fields: [{ label: 'I agree to the Your Platform Name Here Honor Code', @@ -468,10 +479,10 @@ describe('./RegistrationPage.js', () => { it('should match third party auth alert', () => { store = mockStore({ ...initialState, - logistration: { - ...initialState.logistration, + commonComponents: { + ...initialState.commonComponents, thirdPartyAuthContext: { - ...initialState.logistration.thirdPartyAuthContext, + ...initialState.commonComponents.thirdPartyAuthContext, currentProvider: 'Apple', }, }, @@ -494,8 +505,8 @@ describe('./RegistrationPage.js', () => { store = mockStore({ ...initialState, - logistration: { - ...initialState.logistration, + register: { + ...initialState.register, registrationError: { email: [ { diff --git a/src/logistration/tests/__snapshots__/RegistrationPage.test.jsx.snap b/src/register/tests/__snapshots__/RegistrationPage.test.jsx.snap similarity index 92% rename from src/logistration/tests/__snapshots__/RegistrationPage.test.jsx.snap rename to src/register/tests/__snapshots__/RegistrationPage.test.jsx.snap index 48ee496e..0ec0da52 100644 --- a/src/logistration/tests/__snapshots__/RegistrationPage.test.jsx.snap +++ b/src/register/tests/__snapshots__/RegistrationPage.test.jsx.snap @@ -36,28 +36,6 @@ exports[`./RegistrationPage.js should display no password field when current pro > Create an Account - - - ‌ - - - ‌ - - @@ -499,37 +477,11 @@ exports[`./RegistrationPage.js should match default section snapshot 1`] = ` Sign in. -
-

- Create an account using -

-
- - - ‌ - - - ‌ - - + Create an Account + @@ -745,37 +697,11 @@ exports[`./RegistrationPage.js should match pending button state snapshot 1`] = Sign in. -
-

- Create an account using -

-
- - - ‌ - - - ‌ - - + Create an Account + @@ -1026,37 +952,11 @@ exports[`./RegistrationPage.js should show error message on 409 1`] = ` Sign in. -
-

- Create an account using -

-
- - - ‌ - - - ‌ - - + Create an Account + diff --git a/src/reset-password/InvalidToken.jsx b/src/reset-password/InvalidToken.jsx index 11bb1eb3..c6f7e34c 100644 --- a/src/reset-password/InvalidToken.jsx +++ b/src/reset-password/InvalidToken.jsx @@ -6,9 +6,9 @@ const InvalidTokenMessage = () => { const loginPasswordLink = ( ); @@ -16,7 +16,7 @@ const InvalidTokenMessage = () => { const forgotPassword = ( @@ -29,13 +29,13 @@ const InvalidTokenMessage = () => { {
{
-
+

- {intl.formatMessage(messages['logistration.reset.password.page.heading'])} + {intl.formatMessage(messages['reset.password.page.heading'])}

- {intl.formatMessage(messages['logistration.reset.password.page.instructions'])} + {intl.formatMessage(messages['reset.password.page.instructions'])}

{ className="w-100" > - {intl.formatMessage(messages['logistration.reset.password.page.new.field.label'])} + {intl.formatMessage(messages['reset.password.page.new.field.label'])} { - {intl.formatMessage(messages['logistration.reset.password.page.confirm.field.label'])} + {intl.formatMessage(messages['reset.password.page.confirm.field.label'])} { className="btn-primary" state={props.status} labels={{ - default: intl.formatMessage(messages['logistration.reset.password.page.submit.button']), + default: intl.formatMessage(messages['reset.password.page.submit.button']), }} onClick={e => handleSubmit(e)} /> diff --git a/src/reset-password/ResetSuccess.jsx b/src/reset-password/ResetSuccess.jsx index 64e126b6..0ec818e4 100644 --- a/src/reset-password/ResetSuccess.jsx +++ b/src/reset-password/ResetSuccess.jsx @@ -6,9 +6,9 @@ const ResetSuccessMessage = () => { const loginPasswordLink = ( ); @@ -20,13 +20,13 @@ const ResetSuccessMessage = () => { -
+

@@ -154,7 +156,9 @@ exports[`ResetPasswordPage should match reset password default section snapshot -
+

@@ -305,7 +309,9 @@ Array [ -
+