diff --git a/src/logistration/LoginPage.jsx b/src/logistration/LoginPage.jsx index c243452e..20737638 100644 --- a/src/logistration/LoginPage.jsx +++ b/src/logistration/LoginPage.jsx @@ -4,32 +4,13 @@ import PropTypes from 'prop-types'; import { Button, Input, ValidationFormGroup } from '@edx/paragon'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { faFacebookF, faGoogle, faMicrosoft } from '@fortawesome/free-brands-svg-icons'; -import { getQueryParameters } from '@edx/frontend-platform'; import { loginRequest } from './data/actions'; import { loginRequestSelector } from './data/selectors'; import { forgotPasswordResultSelector } from '../forgot-password'; import ConfirmationAlert from './ConfirmationAlert'; import LoginHelpLinks from './LoginHelpLinks'; - - -const LoginRedirect = (props) => { - const { success, redirectUrl } = props; - if (success) { - window.location.href = redirectUrl; - } - return <>; -}; - -LoginRedirect.defaultProps = { - redirectUrl: '', - success: false, -}; - -LoginRedirect.propTypes = { - redirectUrl: PropTypes.string, - success: PropTypes.bool, -}; +import RedirectLogistration from './RedirectLogistration'; class LoginPage extends React.Component { constructor(props, context) { @@ -50,13 +31,19 @@ class LoginPage extends React.Component { handleSubmit = (e) => { e.preventDefault(); - const params = getQueryParameters(); + const params = (new URL(document.location)).searchParams; const payload = { email: this.state.email, password: this.state.password, - next: params.next, - course_id: params.course_id, }; + const next = params.get('next'); + const courseId = params.get('course_id'); + if (next) { + payload.next = params.next; + } + if (courseId) { + payload.course_id = params.course_id; + } if (!this.state.formValid) { this.validateInput('email', payload.email); this.validateInput('password', payload.password); @@ -107,7 +94,10 @@ class LoginPage extends React.Component { render() { return ( <> - +
{this.props.forgotPassword.status === 'complete' ? : null} diff --git a/src/logistration/RedirectLogistration.jsx b/src/logistration/RedirectLogistration.jsx new file mode 100644 index 00000000..ca5c24c4 --- /dev/null +++ b/src/logistration/RedirectLogistration.jsx @@ -0,0 +1,23 @@ +import React from 'react'; +import PropTypes from 'prop-types'; + +function RedirectLogistration(props) { + const { success, redirectUrl } = props; + if (success) { + window.location.href = redirectUrl; + } + return <>; +} + +RedirectLogistration.defaultProps = { + redirectUrl: '', + success: false, +}; + +RedirectLogistration.propTypes = { + redirectUrl: PropTypes.string, + success: PropTypes.bool, +}; + + +export default RedirectLogistration; diff --git a/src/logistration/RegistrationPage.jsx b/src/logistration/RegistrationPage.jsx index 8784c035..42a6b180 100644 --- a/src/logistration/RegistrationPage.jsx +++ b/src/logistration/RegistrationPage.jsx @@ -10,6 +10,8 @@ import { faGraduationCap } from '@fortawesome/free-solid-svg-icons'; import { getLocale, getCountryList } from '@edx/frontend-platform/i18n'; import { registerNewUser } from './data/actions'; +import { registrationRequestSelector } from './data/selectors'; +import RedirectLogistration from './RedirectLogistration'; class RegistrationPage extends React.Component { constructor(props, context) { @@ -40,6 +42,7 @@ class RegistrationPage extends React.Component { handleSubmit = (e) => { e.preventDefault(); + const params = (new URL(document.location)).searchParams; const payload = { email: this.state.email, username: this.state.username, @@ -48,6 +51,14 @@ class RegistrationPage extends React.Component { honor_code: true, country: this.state.country, }; + const next = params.get('next'); + const courseId = params.get('course_id'); + if (next) { + payload.next = params.next; + } + if (courseId) { + payload.course_id = params.course_id; + } if (!this.state.formValid) { Object.entries(payload).forEach(([key, value]) => { @@ -140,6 +151,10 @@ class RegistrationPage extends React.Component { render() { return ( <> +
@@ -261,12 +276,25 @@ class RegistrationPage extends React.Component { } } +RegistrationPage.defaultProps = { + registrationResult: null, +}; + RegistrationPage.propTypes = { registerNewUser: PropTypes.func.isRequired, + registrationResult: PropTypes.shape({ + redirectUrl: PropTypes.string, + success: PropTypes.bool, + }), +}; + +const mapStateToProps = state => { + const registrationResult = registrationRequestSelector(state); + return { registrationResult }; }; export default connect( - () => ({}), + mapStateToProps, { registerNewUser, }, diff --git a/src/logistration/data/actions.js b/src/logistration/data/actions.js index 21c190b1..266e72dd 100644 --- a/src/logistration/data/actions.js +++ b/src/logistration/data/actions.js @@ -14,8 +14,9 @@ export const registerNewUserBegin = () => ({ type: REGISTER_NEW_USER.BEGIN, }); -export const registerNewUserSuccess = () => ({ +export const registerNewUserSuccess = (redirectUrl, success) => ({ type: REGISTER_NEW_USER.SUCCESS, + payload: { redirectUrl, success }, }); export const registerNewUserFailure = () => ({ diff --git a/src/logistration/data/reducers.js b/src/logistration/data/reducers.js index e5864dd8..c1c8ee12 100644 --- a/src/logistration/data/reducers.js +++ b/src/logistration/data/reducers.js @@ -17,6 +17,7 @@ const reducer = (state = defaultState, action) => { case REGISTER_NEW_USER.SUCCESS: return { ...state, + registrationResult: action.payload, }; case REGISTER_NEW_USER.FAILURE: return { diff --git a/src/logistration/data/sagas.js b/src/logistration/data/sagas.js index eea79c6d..a4e34ce4 100644 --- a/src/logistration/data/sagas.js +++ b/src/logistration/data/sagas.js @@ -20,9 +20,12 @@ export function* handleNewUserRegistration(action) { try { yield put(registerNewUserBegin()); - yield call(postNewUser, action.payload.registrationInfo); + const { redirectUrl, success } = yield call(postNewUser, action.payload.registrationInfo); - yield put(registerNewUserSuccess()); + yield put(registerNewUserSuccess( + redirectUrl, + success, + )); } catch (e) { yield put(registerNewUserFailure()); throw e; diff --git a/src/logistration/data/selectors.js b/src/logistration/data/selectors.js index 937087f9..b7e3e1fc 100644 --- a/src/logistration/data/selectors.js +++ b/src/logistration/data/selectors.js @@ -8,3 +8,8 @@ export const loginRequestSelector = createSelector( logistrationSelector, logistration => logistration.loginResult, ); + +export const registrationRequestSelector = createSelector( + logistrationSelector, + logistration => logistration.registrationResult, +); diff --git a/src/logistration/data/service.js b/src/logistration/data/service.js index b02008c7..afcc31b3 100644 --- a/src/logistration/data/service.js +++ b/src/logistration/data/service.js @@ -18,7 +18,10 @@ export async function postNewUser(registrationInformation) { throw (e); }); - return data; + return { + redirectUrl: data.redirect_url || `${getConfig().LMS_BASE_URL}/dashboard`, + success: data.success || false, + }; } export async function login(creds) { diff --git a/src/logistration/tests/LoginPage.test.jsx b/src/logistration/tests/LoginPage.test.jsx index eda4779f..54269da8 100644 --- a/src/logistration/tests/LoginPage.test.jsx +++ b/src/logistration/tests/LoginPage.test.jsx @@ -72,7 +72,7 @@ describe('LoginPage', () => { }); it('should match url after redirection', () => { - const dasboardUrl = 'http://test.com/dashboard/'; + const dasboardUrl = 'http://test.com/testing-dashboard/'; store = mockStore({ ...store, logistration: { diff --git a/src/logistration/tests/RegistrationPage.test.jsx b/src/logistration/tests/RegistrationPage.test.jsx new file mode 100644 index 00000000..80b92575 --- /dev/null +++ b/src/logistration/tests/RegistrationPage.test.jsx @@ -0,0 +1,69 @@ +import React from 'react'; +import { Provider } from 'react-redux'; +import renderer from 'react-test-renderer'; +import configureStore from 'redux-mock-store'; +import { IntlProvider, injectIntl, configure } from '@edx/frontend-platform/i18n'; + +import RegistrationPage from '../RegistrationPage'; + +const IntlRegistrationPage = injectIntl(RegistrationPage); +const mockStore = configureStore(); + +describe('./RegistrationPage.js', () => { + const initialState = { + logistration: { + registrationResult: { success: false, redirectUrl: '' }, + }, + }; + + let props = {}; + let store = {}; + + const reduxWrapper = children => ( + + {children} + + ); + + beforeEach(() => { + configure({ + loggingService: { logError: jest.fn() }, + config: { + ENVIRONMENT: 'production', + LANGUAGE_PREFERENCE_COOKIE_NAME: 'yum', + }, + messages: { + 'es-419': {}, + de: {}, + 'en-us': {}, + }, + }); + store = mockStore(initialState); + props = { + registrationResult: jest.fn(), + }; + }); + + it('should match default section snapshot', () => { + const tree = renderer.create(reduxWrapper()); + expect(tree.toJSON()).toMatchSnapshot(); + }); + + it('should match url after redirection', () => { + const dasboardUrl = 'http://test.com/testing-dashboard/'; + store = mockStore({ + ...store, + logistration: { + ...store.logistration, + registrationResult: { + success: true, + redirectUrl: dasboardUrl, + }, + }, + }); + delete window.location; + window.location = { href: '' }; + renderer.create(reduxWrapper()); + expect(window.location.href).toBe(dasboardUrl); + }); +}); diff --git a/src/logistration/tests/__snapshots__/RegistrationPage.test.jsx.snap b/src/logistration/tests/__snapshots__/RegistrationPage.test.jsx.snap new file mode 100644 index 00000000..9f159f13 --- /dev/null +++ b/src/logistration/tests/__snapshots__/RegistrationPage.test.jsx.snap @@ -0,0 +1,1576 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`./RegistrationPage.js should match default section snapshot 1`] = ` +
+
+ +

+ Start learning now! +

+
+
+ + Create an account using + + + + + + or create a new one here + +
+
+
+ + + + Enter a valid email address that contains at least 3 characters. + +
+
+ + + + Enter your full name. + +
+
+ + + + Username must be between 2 and 30 characters long. + +
+
+ + + + This password is too short. It must contain at least 8 characters. This password must contain at least 1 number. + +
+
+ + + + Select your country or region of residence. + +
+ + By creating an account, you agree to the + + Terms of Service and Honor Code + + and you acknowledge that edX and each Member process your personal data in accordance with the + + Privacy Policy + + . + + + +
+
+ + Already have an edX account? + + + Sign in. + +
+
+`;