logistration folder refactored to login and register
This commit is contained in:
49
src/common-components/ConfirmationAlert.jsx
Normal file
49
src/common-components/ConfirmationAlert.jsx
Normal file
@@ -0,0 +1,49 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import { getConfig } from '@edx/frontend-platform';
|
||||
import { FormattedMessage, injectIntl, intlShape } from '@edx/frontend-platform/i18n';
|
||||
import { Alert } from '@edx/paragon';
|
||||
|
||||
import messages from './messages';
|
||||
|
||||
const ConfirmationAlert = (props) => {
|
||||
const { email, intl } = props;
|
||||
|
||||
return (
|
||||
<Alert id="confirmation-alert" variant="success">
|
||||
<Alert.Heading>{intl.formatMessage(messages['forgot.password.confirmation.title'])}</Alert.Heading>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id="logistration.forgot.password.confirmation.message"
|
||||
defaultMessage="You entered {strongEmail}. If this email address is associated with your
|
||||
edX account, we will send a message with password recovery instructions to this email address."
|
||||
description="Forgot password confirmation message"
|
||||
values={{ strongEmail: <strong>{email}</strong> }}
|
||||
/>
|
||||
</p>
|
||||
<p>{intl.formatMessage(messages['forgot.password.confirmation.info'])}</p>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id="forgot.password.technical.support.help.message"
|
||||
defaultMessage="If you need further assistance, {technicalSupportLink}."
|
||||
description="Message to help user contact technical support"
|
||||
values={{
|
||||
technicalSupportLink: (
|
||||
<Alert.Link href={getConfig().PASSWORD_RESET_SUPPORT_LINK}>
|
||||
{intl.formatMessage(messages['forgot.password.confirmation.support.link'])}
|
||||
</Alert.Link>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</p>
|
||||
</Alert>
|
||||
);
|
||||
};
|
||||
|
||||
ConfirmationAlert.propTypes = {
|
||||
email: PropTypes.string.isRequired,
|
||||
intl: intlShape.isRequired,
|
||||
};
|
||||
|
||||
export default injectIntl(ConfirmationAlert);
|
||||
99
src/common-components/InstitutionLogistration.jsx
Normal file
99
src/common-components/InstitutionLogistration.jsx
Normal file
@@ -0,0 +1,99 @@
|
||||
import React from 'react';
|
||||
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
|
||||
import { getConfig } from '@edx/frontend-platform';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Button, Hyperlink } from '@edx/paragon';
|
||||
import messages from './messages';
|
||||
|
||||
export const RenderInstitutionButton = props => {
|
||||
const { onSubmitHandler, secondaryProviders, buttonTitle } = props;
|
||||
if (secondaryProviders !== undefined && secondaryProviders.length > 0) {
|
||||
return (
|
||||
<Button
|
||||
className="mt-2"
|
||||
block
|
||||
variant="outline-primary"
|
||||
onClick={onSubmitHandler}
|
||||
>
|
||||
{buttonTitle}
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
return <></>;
|
||||
};
|
||||
|
||||
const InstitutionLogistration = props => {
|
||||
const lmsBaseUrl = getConfig().LMS_BASE_URL;
|
||||
const {
|
||||
intl,
|
||||
onSubmitHandler,
|
||||
secondaryProviders,
|
||||
headingTitle,
|
||||
buttonTitle,
|
||||
} = props;
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="d-flex justify-content-center m-4">
|
||||
<div className="flex-column">
|
||||
<h1 className="mt-5 mb-4 font-weight-normal">
|
||||
{headingTitle}
|
||||
</h1>
|
||||
<p className="mb-2">
|
||||
{intl.formatMessage(messages['institution.login.page.sub.heading'])}
|
||||
</p>
|
||||
<div className="mb-2 ml-2">
|
||||
<ul>
|
||||
{secondaryProviders.map(provider => (
|
||||
<li key={provider}>
|
||||
<Hyperlink destination={lmsBaseUrl + provider.loginUrl}>{provider.name}</Hyperlink>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
<div className="mb-4">
|
||||
<h4>or</h4>
|
||||
</div>
|
||||
<Button
|
||||
variant="primary"
|
||||
onClick={onSubmitHandler}
|
||||
>
|
||||
{buttonTitle}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const LogistrationDefaultProps = {
|
||||
secondaryProviders: [],
|
||||
buttonTitle: '',
|
||||
};
|
||||
const LogistrationProps = {
|
||||
onSubmitHandler: PropTypes.func.isRequired,
|
||||
secondaryProviders: PropTypes.arrayOf(PropTypes.shape({
|
||||
name: PropTypes.string.isRequried,
|
||||
loginUrl: PropTypes.string.isRequired,
|
||||
})),
|
||||
buttonTitle: PropTypes.string,
|
||||
};
|
||||
|
||||
RenderInstitutionButton.propTypes = {
|
||||
...LogistrationProps,
|
||||
};
|
||||
RenderInstitutionButton.defaultProps = {
|
||||
...LogistrationDefaultProps,
|
||||
};
|
||||
|
||||
InstitutionLogistration.propTypes = {
|
||||
...LogistrationProps,
|
||||
intl: intlShape.isRequired,
|
||||
headingTitle: PropTypes.string,
|
||||
};
|
||||
InstitutionLogistration.defaultProps = {
|
||||
...LogistrationDefaultProps,
|
||||
headingTitle: '',
|
||||
};
|
||||
|
||||
export default injectIntl(InstitutionLogistration);
|
||||
71
src/common-components/SocialAuthProviders.jsx
Normal file
71
src/common-components/SocialAuthProviders.jsx
Normal file
@@ -0,0 +1,71 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import { getConfig } from '@edx/frontend-platform';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import { faSignInAlt } from '@fortawesome/free-solid-svg-icons';
|
||||
|
||||
import { LOGIN_PAGE, SUPPORTED_ICON_CLASSES } from '../data/constants';
|
||||
|
||||
function SocialAuthProviders(props) {
|
||||
const { referrer, socialAuthProviders } = props;
|
||||
|
||||
function handleSubmit(e) {
|
||||
e.preventDefault();
|
||||
|
||||
const url = e.currentTarget.dataset.providerUrl;
|
||||
window.location.href = getConfig().LMS_BASE_URL + url;
|
||||
}
|
||||
|
||||
const socialAuth = socialAuthProviders.map((provider) => (
|
||||
<button
|
||||
id={provider.id}
|
||||
key={provider.id}
|
||||
type="button"
|
||||
className={`btn-social btn-${provider.id}`}
|
||||
data-provider-url={referrer === LOGIN_PAGE ? provider.loginUrl : provider.registerUrl}
|
||||
onClick={handleSubmit}
|
||||
>
|
||||
{provider.iconImage ? (
|
||||
<>
|
||||
<div aria-hidden="true">
|
||||
<img className="icon-image" src={provider.iconImage} alt={`icon ${provider.name}`} />
|
||||
</div>
|
||||
<span className="pl-2" aria-hidden="true">{provider.name}</span>
|
||||
</>
|
||||
)
|
||||
: (
|
||||
<>
|
||||
<div className="font-container" aria-hidden="true">
|
||||
<FontAwesomeIcon
|
||||
icon={SUPPORTED_ICON_CLASSES.includes(provider.iconClass) ? ['fab', provider.iconClass] : faSignInAlt}
|
||||
/>
|
||||
</div>
|
||||
<span className="pl-2 provider-name" aria-hidden="true">{provider.name}</span>
|
||||
</>
|
||||
)}
|
||||
|
||||
</button>
|
||||
));
|
||||
|
||||
return <>{socialAuth}</>;
|
||||
}
|
||||
|
||||
SocialAuthProviders.defaultProps = {
|
||||
referrer: LOGIN_PAGE,
|
||||
socialAuthProviders: [],
|
||||
};
|
||||
|
||||
SocialAuthProviders.propTypes = {
|
||||
referrer: PropTypes.string,
|
||||
socialAuthProviders: PropTypes.arrayOf(PropTypes.shape({
|
||||
id: PropTypes.string,
|
||||
name: PropTypes.string,
|
||||
iconClass: PropTypes.string,
|
||||
iconImage: PropTypes.string,
|
||||
loginUrl: PropTypes.string,
|
||||
registerUrl: PropTypes.string,
|
||||
})),
|
||||
};
|
||||
|
||||
export default SocialAuthProviders;
|
||||
61
src/common-components/SwitchContent.jsx
Normal file
61
src/common-components/SwitchContent.jsx
Normal file
@@ -0,0 +1,61 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { TransitionReplace } from '@edx/paragon';
|
||||
|
||||
const onChildExit = (htmlNode) => {
|
||||
// If the leaving child has focus, take control and redirect it
|
||||
if (htmlNode.contains(document.activeElement)) {
|
||||
// Get the newly entering sibling.
|
||||
// It's the previousSibling, but not for any explicit reason. So checking for both.
|
||||
const enteringChild = htmlNode.previousSibling || htmlNode.nextSibling;
|
||||
|
||||
// There's no replacement, do nothing.
|
||||
if (!enteringChild) return; // eslint-disable-line curly
|
||||
|
||||
// Get all the focusable elements in the entering child and focus the first one
|
||||
const focusableElements = enteringChild.querySelectorAll('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])');
|
||||
if (focusableElements.length) {
|
||||
focusableElements[0].focus();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function SwitchContent({ expression, cases, className }) {
|
||||
const getContent = (caseKey) => {
|
||||
if (cases[caseKey]) {
|
||||
if (typeof cases[caseKey] === 'string') {
|
||||
return getContent(cases[caseKey]);
|
||||
}
|
||||
return React.cloneElement(cases[caseKey], { key: caseKey });
|
||||
} else if (cases.default) { // eslint-disable-line no-else-return
|
||||
if (typeof cases.default === 'string') {
|
||||
return getContent(cases.default);
|
||||
}
|
||||
React.cloneElement(cases.default, { key: 'default' });
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
return (
|
||||
<TransitionReplace
|
||||
className={className}
|
||||
onChildExit={onChildExit}
|
||||
>
|
||||
{getContent(expression)}
|
||||
</TransitionReplace>
|
||||
);
|
||||
}
|
||||
|
||||
SwitchContent.propTypes = {
|
||||
expression: PropTypes.string,
|
||||
cases: PropTypes.objectOf(PropTypes.node).isRequired,
|
||||
className: PropTypes.string,
|
||||
};
|
||||
|
||||
SwitchContent.defaultProps = {
|
||||
expression: null,
|
||||
className: null,
|
||||
};
|
||||
|
||||
export default SwitchContent;
|
||||
45
src/common-components/ThirdPartyAuthAlert.jsx
Normal file
45
src/common-components/ThirdPartyAuthAlert.jsx
Normal file
@@ -0,0 +1,45 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import { FormattedMessage } from '@edx/frontend-platform/i18n';
|
||||
import { Alert } from '@edx/paragon';
|
||||
import { LOGIN_PAGE } from '../data/constants';
|
||||
|
||||
const ThirdPartyAuthAlert = (props) => {
|
||||
const { currentProvider, referrer, platformName } = props;
|
||||
let message;
|
||||
|
||||
if (referrer === LOGIN_PAGE) {
|
||||
message = (
|
||||
<FormattedMessage
|
||||
id="login.third.party.auth.account.not.linked.message"
|
||||
defaultMessage="You have successfully signed into {currentProvider}, but your {currentProvider} account does not have a linked {platformName} account. To link your accounts, sign in now using your {platformName} password."
|
||||
description="Message that appears on login page if user has successfully authenticated with TPA but no associated platform account exists"
|
||||
values={{ currentProvider, platformName }}
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
message = (
|
||||
<FormattedMessage
|
||||
id="register.third.party.auth.account.not.linked.message"
|
||||
defaultMessage="You've successfully signed into {currentProvider}. We just need a little more information before you start learning with {platformName}."
|
||||
description="Message that appears on register page if user has successfully authenticated with TPA but no associated platform account exists"
|
||||
values={{ currentProvider, platformName }}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return <Alert id="tpa-alert" className="alert-warning mt-n2">{ message }</Alert>;
|
||||
};
|
||||
|
||||
ThirdPartyAuthAlert.defaultProps = {
|
||||
referrer: LOGIN_PAGE,
|
||||
};
|
||||
|
||||
ThirdPartyAuthAlert.propTypes = {
|
||||
currentProvider: PropTypes.string.isRequired,
|
||||
platformName: PropTypes.string.isRequired,
|
||||
referrer: PropTypes.string,
|
||||
};
|
||||
|
||||
export default ThirdPartyAuthAlert;
|
||||
22
src/common-components/data/actions.js
Normal file
22
src/common-components/data/actions.js
Normal file
@@ -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,
|
||||
});
|
||||
32
src/common-components/data/reducers.js
Normal file
32
src/common-components/data/reducers.js
Normal file
@@ -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;
|
||||
34
src/common-components/data/sagas.js
Normal file
34
src/common-components/data/sagas.js
Normal file
@@ -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);
|
||||
}
|
||||
10
src/common-components/data/selectors.js
Normal file
10
src/common-components/data/selectors.js
Normal file
@@ -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,
|
||||
);
|
||||
23
src/common-components/data/service.js
Normal file
23
src/common-components/data/service.js
Normal file
@@ -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' })),
|
||||
};
|
||||
}
|
||||
65
src/common-components/data/tests/sagas.test.js
Normal file
65
src/common-components/data/tests/sagas.test.js
Normal file
@@ -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();
|
||||
});
|
||||
});
|
||||
@@ -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';
|
||||
|
||||
28
src/common-components/messages.jsx
Normal file
28
src/common-components/messages.jsx
Normal file
@@ -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;
|
||||
34
src/common-components/tests/ConfirmationAlert.test.jsx
Normal file
34
src/common-components/tests/ConfirmationAlert.test.jsx
Normal file
@@ -0,0 +1,34 @@
|
||||
import React from 'react';
|
||||
import { mount } from 'enzyme';
|
||||
|
||||
import { mergeConfig } from '@edx/frontend-platform';
|
||||
import { injectIntl, IntlProvider } from '@edx/frontend-platform/i18n';
|
||||
|
||||
import ConfirmationAlert from '../ConfirmationAlert';
|
||||
|
||||
const IntlConfirmationAlertMessage = injectIntl(ConfirmationAlert);
|
||||
|
||||
describe('ConfirmationAlert', () => {
|
||||
const supportLink = 'https://support.test.com/What-if-I-did-not-receive-a-password-reset-message';
|
||||
mergeConfig({
|
||||
PASSWORD_RESET_SUPPORT_LINK: supportLink,
|
||||
});
|
||||
|
||||
it('should match default confirmation message', () => {
|
||||
const confirmationAlertMessage = mount(
|
||||
<IntlProvider locale="en">
|
||||
<IntlConfirmationAlertMessage email="test@example.com" />
|
||||
</IntlProvider>,
|
||||
);
|
||||
|
||||
const expectedMessage = 'Check Your Email'
|
||||
+ 'You entered test@example.com. If this email address is associated with your edX account, '
|
||||
+ 'we will send a message with password recovery instructions to this email address.'
|
||||
+ '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.'
|
||||
+ 'If you need further assistance, contact technical support.';
|
||||
|
||||
expect(confirmationAlertMessage.find('#confirmation-alert').first().text()).toEqual(expectedMessage);
|
||||
expect(confirmationAlertMessage.find('#confirmation-alert').find('a').props().href).toEqual(supportLink);
|
||||
});
|
||||
});
|
||||
68
src/common-components/tests/SocialAuthProviders.test.jsx
Normal file
68
src/common-components/tests/SocialAuthProviders.test.jsx
Normal file
@@ -0,0 +1,68 @@
|
||||
import React from 'react';
|
||||
import renderer from 'react-test-renderer';
|
||||
import { IntlProvider } from '@edx/frontend-platform/i18n';
|
||||
|
||||
import SocialAuthProviders from '../SocialAuthProviders';
|
||||
import registerIcons from '../RegisterFaIcons';
|
||||
|
||||
registerIcons();
|
||||
|
||||
describe('SocialAuthProviders', () => {
|
||||
let props = {};
|
||||
|
||||
const appleProvider = {
|
||||
id: 'oa2-apple-id',
|
||||
name: 'Apple',
|
||||
iconClass: null,
|
||||
iconImage: 'https://edx.devstack.lms/logo.png',
|
||||
loginUrl: '/auth/login/apple-id/?auth_entry=login&next=/dashboard',
|
||||
};
|
||||
|
||||
it('should match social auth provider with iconImage snapshot', () => {
|
||||
props = { socialAuthProviders: [appleProvider] };
|
||||
|
||||
const tree = renderer.create(
|
||||
<IntlProvider locale="en">
|
||||
<SocialAuthProviders {...props} />
|
||||
</IntlProvider>,
|
||||
).toJSON();
|
||||
|
||||
expect(tree).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should match social auth provider with iconClass snapshot', () => {
|
||||
props = {
|
||||
socialAuthProviders: [{
|
||||
...appleProvider,
|
||||
iconClass: 'google',
|
||||
iconImage: null,
|
||||
}],
|
||||
};
|
||||
|
||||
const tree = renderer.create(
|
||||
<IntlProvider locale="en">
|
||||
<SocialAuthProviders {...props} />
|
||||
</IntlProvider>,
|
||||
).toJSON();
|
||||
|
||||
expect(tree).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should match social auth provider with default icon snapshot', () => {
|
||||
props = {
|
||||
socialAuthProviders: [{
|
||||
...appleProvider,
|
||||
iconClass: 'default',
|
||||
iconImage: null,
|
||||
}],
|
||||
};
|
||||
|
||||
const tree = renderer.create(
|
||||
<IntlProvider locale="en">
|
||||
<SocialAuthProviders {...props} />
|
||||
</IntlProvider>,
|
||||
).toJSON();
|
||||
|
||||
expect(tree).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
39
src/common-components/tests/ThirdPartyAuthAlert.test.jsx
Normal file
39
src/common-components/tests/ThirdPartyAuthAlert.test.jsx
Normal file
@@ -0,0 +1,39 @@
|
||||
import React from 'react';
|
||||
import renderer from 'react-test-renderer';
|
||||
import { IntlProvider } from '@edx/frontend-platform/i18n';
|
||||
|
||||
import ThirdPartyAuthAlert from '../ThirdPartyAuthAlert';
|
||||
|
||||
describe('ThirdPartyAuthAlert', () => {
|
||||
let props = {};
|
||||
|
||||
beforeEach(() => {
|
||||
props = {
|
||||
currentProvider: 'Google',
|
||||
platformName: 'edX',
|
||||
};
|
||||
});
|
||||
|
||||
it('should match login page third party auth alert message snapshot', () => {
|
||||
const tree = renderer.create(
|
||||
<IntlProvider locale="en">
|
||||
<ThirdPartyAuthAlert {...props} />
|
||||
</IntlProvider>,
|
||||
).toJSON();
|
||||
expect(tree).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should match register page third party auth alert message snapshot', () => {
|
||||
props = {
|
||||
...props,
|
||||
referrer: 'register',
|
||||
};
|
||||
|
||||
const tree = renderer.create(
|
||||
<IntlProvider locale="en">
|
||||
<ThirdPartyAuthAlert {...props} />
|
||||
</IntlProvider>,
|
||||
).toJSON();
|
||||
expect(tree).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,105 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`SocialAuthProviders should match social auth provider with default icon snapshot 1`] = `
|
||||
<button
|
||||
className="btn-social btn-oa2-apple-id"
|
||||
data-provider-url="/auth/login/apple-id/?auth_entry=login&next=/dashboard"
|
||||
id="oa2-apple-id"
|
||||
onClick={[Function]}
|
||||
type="button"
|
||||
>
|
||||
<div
|
||||
aria-hidden="true"
|
||||
className="font-container"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
className="svg-inline--fa fa-sign-in-alt fa-w-16 "
|
||||
data-icon="sign-in-alt"
|
||||
data-prefix="fas"
|
||||
focusable="false"
|
||||
role="img"
|
||||
style={Object {}}
|
||||
viewBox="0 0 512 512"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M416 448h-84c-6.6 0-12-5.4-12-12v-40c0-6.6 5.4-12 12-12h84c17.7 0 32-14.3 32-32V160c0-17.7-14.3-32-32-32h-84c-6.6 0-12-5.4-12-12V76c0-6.6 5.4-12 12-12h84c53 0 96 43 96 96v192c0 53-43 96-96 96zm-47-201L201 79c-15-15-41-4.5-41 17v96H24c-13.3 0-24 10.7-24 24v96c0 13.3 10.7 24 24 24h136v96c0 21.5 26 32 41 17l168-168c9.3-9.4 9.3-24.6 0-34z"
|
||||
fill="currentColor"
|
||||
style={Object {}}
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
className="pl-2 provider-name"
|
||||
>
|
||||
Apple
|
||||
</span>
|
||||
</button>
|
||||
`;
|
||||
|
||||
exports[`SocialAuthProviders should match social auth provider with iconClass snapshot 1`] = `
|
||||
<button
|
||||
className="btn-social btn-oa2-apple-id"
|
||||
data-provider-url="/auth/login/apple-id/?auth_entry=login&next=/dashboard"
|
||||
id="oa2-apple-id"
|
||||
onClick={[Function]}
|
||||
type="button"
|
||||
>
|
||||
<div
|
||||
aria-hidden="true"
|
||||
className="font-container"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
className="svg-inline--fa fa-google fa-w-16 "
|
||||
data-icon="google"
|
||||
data-prefix="fab"
|
||||
focusable="false"
|
||||
role="img"
|
||||
style={Object {}}
|
||||
viewBox="0 0 488 512"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M488 261.8C488 403.3 391.1 504 248 504 110.8 504 0 393.2 0 256S110.8 8 248 8c66.8 0 123 24.5 166.3 64.9l-67.5 64.9C258.5 52.6 94.3 116.6 94.3 256c0 86.5 69.1 156.6 153.7 156.6 98.2 0 135-70.4 140.8-106.9H248v-85.3h236.1c2.3 12.7 3.9 24.9 3.9 41.4z"
|
||||
fill="currentColor"
|
||||
style={Object {}}
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
className="pl-2 provider-name"
|
||||
>
|
||||
Apple
|
||||
</span>
|
||||
</button>
|
||||
`;
|
||||
|
||||
exports[`SocialAuthProviders should match social auth provider with iconImage snapshot 1`] = `
|
||||
<button
|
||||
className="btn-social btn-oa2-apple-id"
|
||||
data-provider-url="/auth/login/apple-id/?auth_entry=login&next=/dashboard"
|
||||
id="oa2-apple-id"
|
||||
onClick={[Function]}
|
||||
type="button"
|
||||
>
|
||||
<div
|
||||
aria-hidden="true"
|
||||
>
|
||||
<img
|
||||
alt="icon Apple"
|
||||
className="icon-image"
|
||||
src="https://edx.devstack.lms/logo.png"
|
||||
/>
|
||||
</div>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
className="pl-2"
|
||||
>
|
||||
Apple
|
||||
</span>
|
||||
</button>
|
||||
`;
|
||||
@@ -0,0 +1,25 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`ThirdPartyAuthAlert should match login page third party auth alert message snapshot 1`] = `
|
||||
<div
|
||||
className="fade alert-warning mt-n2 alert show"
|
||||
id="tpa-alert"
|
||||
role="alert"
|
||||
>
|
||||
<span>
|
||||
You have successfully signed into Google, but your Google account does not have a linked edX account. To link your accounts, sign in now using your edX password.
|
||||
</span>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`ThirdPartyAuthAlert should match register page third party auth alert message snapshot 1`] = `
|
||||
<div
|
||||
className="fade alert-warning mt-n2 alert show"
|
||||
id="tpa-alert"
|
||||
role="alert"
|
||||
>
|
||||
<span>
|
||||
You've successfully signed into Google. We just need a little more information before you start learning with edX.
|
||||
</span>
|
||||
</div>
|
||||
`;
|
||||
Reference in New Issue
Block a user