@openedx/frontend-app-authn 1.0.0-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +661 -0
- package/README.rst +204 -0
- package/package.json +79 -0
- package/src/Main.tsx +23 -0
- package/src/__mocks__/file.js +1 -0
- package/src/__mocks__/svg.js +1 -0
- package/src/app.scss +6 -0
- package/src/app.ts +43 -0
- package/src/base-container/components/default-layout/DefaultLayout.test.jsx +48 -0
- package/src/base-container/components/default-layout/LargeLayout.jsx +42 -0
- package/src/base-container/components/default-layout/MediumLayout.jsx +49 -0
- package/src/base-container/components/default-layout/SmallLayout.jsx +37 -0
- package/src/base-container/components/default-layout/index.jsx +3 -0
- package/src/base-container/components/default-layout/messages.js +16 -0
- package/src/base-container/components/image-layout/ExtraSmallLayout.jsx +31 -0
- package/src/base-container/components/image-layout/LargeLayout.jsx +32 -0
- package/src/base-container/components/image-layout/MediumLayout.jsx +32 -0
- package/src/base-container/components/image-layout/SmallLayout.jsx +31 -0
- package/src/base-container/components/image-layout/index.jsx +4 -0
- package/src/base-container/components/image-layout/index.scss +37 -0
- package/src/base-container/components/image-layout/messages.js +16 -0
- package/src/base-container/components/welcome-page-layout/LargeLayout.jsx +46 -0
- package/src/base-container/components/welcome-page-layout/MediumLayout.jsx +49 -0
- package/src/base-container/components/welcome-page-layout/SmallLayout.jsx +38 -0
- package/src/base-container/components/welcome-page-layout/index.jsx +3 -0
- package/src/base-container/components/welcome-page-layout/messages.js +21 -0
- package/src/base-container/index.jsx +70 -0
- package/src/base-container/tests/BaseContainer.test.jsx +44 -0
- package/src/common-components/EmbeddedRegistrationRoute.jsx +26 -0
- package/src/common-components/EnterpriseSSO.jsx +110 -0
- package/src/common-components/FormGroup.jsx +106 -0
- package/src/common-components/InstitutionLogistration.jsx +102 -0
- package/src/common-components/NotFoundPage.jsx +15 -0
- package/src/common-components/PasswordField.jsx +175 -0
- package/src/common-components/RedirectLogistration.jsx +94 -0
- package/src/common-components/RegisterFaIcons.jsx +8 -0
- package/src/common-components/SocialAuthProviders.jsx +75 -0
- package/src/common-components/ThirdPartyAuth.jsx +123 -0
- package/src/common-components/ThirdPartyAuthAlert.jsx +49 -0
- package/src/common-components/UnAuthOnlyRoute.jsx +41 -0
- package/src/common-components/data/actions.js +27 -0
- package/src/common-components/data/reducers.js +63 -0
- package/src/common-components/data/sagas.js +32 -0
- package/src/common-components/data/selectors.js +28 -0
- package/src/common-components/data/service.js +23 -0
- package/src/common-components/data/tests/reducer.test.js +82 -0
- package/src/common-components/data/tests/sagas.test.js +71 -0
- package/src/common-components/index.jsx +14 -0
- package/src/common-components/messages.jsx +127 -0
- package/src/common-components/tests/EmbeddedRegistrationRoute.test.jsx +76 -0
- package/src/common-components/tests/FormField.test.jsx +289 -0
- package/src/common-components/tests/SocialAuthProviders.test.jsx +75 -0
- package/src/common-components/tests/ThirdPartyAuthAlert.test.jsx +39 -0
- package/src/common-components/tests/UnAuthOnlyRoute.test.jsx +73 -0
- package/src/common-components/tests/__snapshots__/SocialAuthProviders.test.jsx.snap +156 -0
- package/src/common-components/tests/__snapshots__/ThirdPartyAuthAlert.test.jsx.snap +53 -0
- package/src/constants.ts +1 -0
- package/src/data/configureStore.js +33 -0
- package/src/data/constants.js +38 -0
- package/src/data/countries.ts +59 -0
- package/src/data/oneTrust.js +3 -0
- package/src/data/reducers.js +36 -0
- package/src/data/sagas.js +19 -0
- package/src/data/tests/cookies.test.js +42 -0
- package/src/data/tests/dataUtils.test.js +19 -0
- package/src/data/tests/reduxUtils.test.js +14 -0
- package/src/data/utils/cookies.js +12 -0
- package/src/data/utils/dataUtils.js +81 -0
- package/src/data/utils/index.js +11 -0
- package/src/data/utils/reduxUtils.js +34 -0
- package/src/data/utils/useMobileResponsive.js +24 -0
- package/src/field-renderer/FieldRenderer.jsx +156 -0
- package/src/field-renderer/index.jsx +2 -0
- package/src/field-renderer/tests/FieldRenderer.test.jsx +201 -0
- package/src/forgot-password/ForgotPasswordAlert.jsx +97 -0
- package/src/forgot-password/ForgotPasswordPage.jsx +193 -0
- package/src/forgot-password/data/actions.js +32 -0
- package/src/forgot-password/data/reducers.js +58 -0
- package/src/forgot-password/data/sagas.js +35 -0
- package/src/forgot-password/data/selectors.js +10 -0
- package/src/forgot-password/data/service.js +21 -0
- package/src/forgot-password/data/tests/reducers.test.js +34 -0
- package/src/forgot-password/data/tests/sagas.test.js +67 -0
- package/src/forgot-password/index.js +5 -0
- package/src/forgot-password/messages.js +128 -0
- package/src/forgot-password/tests/ForgotPasswordPage.test.jsx +269 -0
- package/src/i18n/index.js +25 -0
- package/src/index.ts +3 -0
- package/src/login/AccountActivationMessage.jsx +78 -0
- package/src/login/ChangePasswordPrompt.jsx +91 -0
- package/src/login/LoginFailure.jsx +231 -0
- package/src/login/LoginPage.jsx +368 -0
- package/src/login/data/actions.js +39 -0
- package/src/login/data/constants.js +20 -0
- package/src/login/data/reducers.js +76 -0
- package/src/login/data/sagas.js +45 -0
- package/src/login/data/service.js +24 -0
- package/src/login/data/tests/reducers.test.js +155 -0
- package/src/login/data/tests/sagas.test.js +110 -0
- package/src/login/index.js +5 -0
- package/src/login/messages.jsx +211 -0
- package/src/login/tests/AccountActivationMessage.test.jsx +122 -0
- package/src/login/tests/ChangePasswordPrompt.test.jsx +75 -0
- package/src/login/tests/LoginFailure.test.jsx +323 -0
- package/src/login/tests/LoginPage.test.jsx +839 -0
- package/src/logistration/Logistration.jsx +189 -0
- package/src/logistration/Logistration.test.jsx +275 -0
- package/src/progressive-profiling/ProgressiveProfiling.jsx +313 -0
- package/src/progressive-profiling/ProgressiveProfilingPageModal.jsx +56 -0
- package/src/progressive-profiling/data/actions.js +22 -0
- package/src/progressive-profiling/data/reducers.js +38 -0
- package/src/progressive-profiling/data/sagas.js +24 -0
- package/src/progressive-profiling/data/selectors.js +14 -0
- package/src/progressive-profiling/data/service.js +17 -0
- package/src/progressive-profiling/index.js +5 -0
- package/src/progressive-profiling/messages.jsx +57 -0
- package/src/progressive-profiling/tests/ProgressiveProfiling.test.jsx +322 -0
- package/src/register/RegistrationFields/CountryField/CountryField.jsx +164 -0
- package/src/register/RegistrationFields/CountryField/CountryField.test.jsx +204 -0
- package/src/register/RegistrationFields/CountryField/validator.js +32 -0
- package/src/register/RegistrationFields/EmailField/EmailField.jsx +140 -0
- package/src/register/RegistrationFields/EmailField/EmailField.test.jsx +236 -0
- package/src/register/RegistrationFields/EmailField/constants.js +131 -0
- package/src/register/RegistrationFields/EmailField/validator.js +126 -0
- package/src/register/RegistrationFields/EmailField/validator.test.js +39 -0
- package/src/register/RegistrationFields/HonorCodeField/HonorCode.jsx +104 -0
- package/src/register/RegistrationFields/HonorCodeField/HonorCode.test.jsx +70 -0
- package/src/register/RegistrationFields/NameField/NameField.jsx +68 -0
- package/src/register/RegistrationFields/NameField/NameField.test.jsx +146 -0
- package/src/register/RegistrationFields/NameField/validator.js +22 -0
- package/src/register/RegistrationFields/TermsOfServiceField/TermsOfService.jsx +58 -0
- package/src/register/RegistrationFields/TermsOfServiceField/TermsOfService.test.jsx +55 -0
- package/src/register/RegistrationFields/UsernameField/UsernameField.jsx +162 -0
- package/src/register/RegistrationFields/UsernameField/UsernameField.test.jsx +319 -0
- package/src/register/RegistrationFields/UsernameField/validator.js +16 -0
- package/src/register/RegistrationFields/index.js +6 -0
- package/src/register/RegistrationPage.jsx +428 -0
- package/src/register/RegistrationPage.test.jsx +854 -0
- package/src/register/components/ConfigurableRegistrationForm.jsx +244 -0
- package/src/register/components/RegistrationFailure.jsx +80 -0
- package/src/register/components/tests/ConfigurableRegistrationForm.test.jsx +401 -0
- package/src/register/components/tests/RegistrationFailure.test.jsx +212 -0
- package/src/register/components/tests/ThirdPartyAuth.test.jsx +439 -0
- package/src/register/data/actions.js +85 -0
- package/src/register/data/constants.js +13 -0
- package/src/register/data/reducers.js +140 -0
- package/src/register/data/sagas.js +58 -0
- package/src/register/data/selectors.js +34 -0
- package/src/register/data/service.js +45 -0
- package/src/register/data/tests/reducers.test.js +277 -0
- package/src/register/data/tests/sagas.test.js +239 -0
- package/src/register/data/tests/utils.test.js +98 -0
- package/src/register/data/utils.js +153 -0
- package/src/register/index.js +4 -0
- package/src/register/messages.jsx +206 -0
- package/src/reset-password/ResetPasswordFailure.jsx +55 -0
- package/src/reset-password/ResetPasswordPage.jsx +224 -0
- package/src/reset-password/ResetPasswordSuccess.jsx +19 -0
- package/src/reset-password/data/actions.js +50 -0
- package/src/reset-password/data/constants.js +16 -0
- package/src/reset-password/data/reducers.js +44 -0
- package/src/reset-password/data/sagas.js +67 -0
- package/src/reset-password/data/selectors.js +10 -0
- package/src/reset-password/data/service.js +61 -0
- package/src/reset-password/data/tests/sagas.test.js +185 -0
- package/src/reset-password/index.js +5 -0
- package/src/reset-password/messages.js +88 -0
- package/src/reset-password/tests/ResetPasswordPage.test.jsx +241 -0
- package/src/routes.jsx +79 -0
- package/src/sass/_base_component.scss +202 -0
- package/src/sass/_forgot_password.scss +3 -0
- package/src/sass/_login_page.scss +3 -0
- package/src/sass/_progressive_profiling_page.scss +30 -0
- package/src/sass/_registration.scss +123 -0
- package/src/sass/_reset_password.scss +3 -0
- package/src/sass/_style.scss +465 -0
- package/src/setupTest.js +68 -0
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export const COUNTRY_CODE_KEY = 'code';
|
|
2
|
+
export const COUNTRY_DISPLAY_KEY = 'name';
|
|
3
|
+
|
|
4
|
+
const validateCountryField = (value, countryList, emptyErrorMessage, invalidCountryErrorMessage) => {
|
|
5
|
+
let countryCode = '';
|
|
6
|
+
let displayValue = value;
|
|
7
|
+
let error = '';
|
|
8
|
+
|
|
9
|
+
if (value) {
|
|
10
|
+
const normalizedValue = value.toLowerCase();
|
|
11
|
+
// Handling a case here where user enters a valid country code that needs to be
|
|
12
|
+
// evaluated and set its value as a valid value.
|
|
13
|
+
const selectedCountry = countryList.find(
|
|
14
|
+
(country) => (
|
|
15
|
+
// When translations are applied, extra space added in country value, so we should trim that.
|
|
16
|
+
country[COUNTRY_DISPLAY_KEY].toLowerCase().trim() === normalizedValue
|
|
17
|
+
|| country[COUNTRY_CODE_KEY].toLowerCase().trim() === normalizedValue
|
|
18
|
+
),
|
|
19
|
+
);
|
|
20
|
+
if (selectedCountry) {
|
|
21
|
+
countryCode = selectedCountry[COUNTRY_CODE_KEY];
|
|
22
|
+
displayValue = selectedCountry[COUNTRY_DISPLAY_KEY];
|
|
23
|
+
} else {
|
|
24
|
+
error = invalidCountryErrorMessage;
|
|
25
|
+
}
|
|
26
|
+
} else {
|
|
27
|
+
error = emptyErrorMessage;
|
|
28
|
+
}
|
|
29
|
+
return { error, countryCode, displayValue };
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export default validateCountryField;
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { useEffect, useState } from 'react';
|
|
2
|
+
import { useDispatch, useSelector } from 'react-redux';
|
|
3
|
+
|
|
4
|
+
import { useIntl } from '@openedx/frontend-base';
|
|
5
|
+
import { Alert, Icon } from '@openedx/paragon';
|
|
6
|
+
import { Close, Error } from '@openedx/paragon/icons';
|
|
7
|
+
import PropTypes from 'prop-types';
|
|
8
|
+
|
|
9
|
+
import { FormGroup } from '../../../common-components';
|
|
10
|
+
import {
|
|
11
|
+
clearRegistrationBackendError,
|
|
12
|
+
fetchRealtimeValidations,
|
|
13
|
+
setEmailSuggestionInStore,
|
|
14
|
+
} from '../../data/actions';
|
|
15
|
+
import messages from '../../messages';
|
|
16
|
+
import validateEmail from './validator';
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Email field wrapper. It accepts following handlers
|
|
20
|
+
* - handleChange for setting value change and
|
|
21
|
+
* - handleErrorChange for setting error
|
|
22
|
+
*
|
|
23
|
+
* It is responsible for
|
|
24
|
+
* - Generating email warning and error suggestions
|
|
25
|
+
* - Setting and clearing email suggestions
|
|
26
|
+
* - Performing email field validations
|
|
27
|
+
* - clearing error on focus
|
|
28
|
+
* - setting value on change
|
|
29
|
+
*/
|
|
30
|
+
const EmailField = (props) => {
|
|
31
|
+
const { formatMessage } = useIntl();
|
|
32
|
+
const dispatch = useDispatch();
|
|
33
|
+
|
|
34
|
+
const {
|
|
35
|
+
handleChange,
|
|
36
|
+
handleErrorChange,
|
|
37
|
+
confirmEmailValue,
|
|
38
|
+
} = props;
|
|
39
|
+
|
|
40
|
+
const backedUpFormData = useSelector(state => state.register.registrationFormData);
|
|
41
|
+
const validationApiRateLimited = useSelector(state => state.register.validationApiRateLimited);
|
|
42
|
+
|
|
43
|
+
const [emailSuggestion, setEmailSuggestion] = useState({ ...backedUpFormData?.emailSuggestion });
|
|
44
|
+
|
|
45
|
+
useEffect(() => {
|
|
46
|
+
setEmailSuggestion(backedUpFormData.emailSuggestion);
|
|
47
|
+
}, [backedUpFormData.emailSuggestion]);
|
|
48
|
+
|
|
49
|
+
const handleOnBlur = (e) => {
|
|
50
|
+
const { value } = e.target;
|
|
51
|
+
const { fieldError, confirmEmailError, suggestion } = validateEmail(value, confirmEmailValue, formatMessage);
|
|
52
|
+
|
|
53
|
+
if (confirmEmailError) {
|
|
54
|
+
handleErrorChange('confirm_email', confirmEmailError);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
dispatch(setEmailSuggestionInStore(suggestion));
|
|
58
|
+
setEmailSuggestion(suggestion);
|
|
59
|
+
|
|
60
|
+
if (fieldError) {
|
|
61
|
+
handleErrorChange('email', fieldError);
|
|
62
|
+
} else if (!validationApiRateLimited) {
|
|
63
|
+
dispatch(fetchRealtimeValidations({ email: value }));
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
const handleOnFocus = () => {
|
|
68
|
+
handleErrorChange('email', '');
|
|
69
|
+
dispatch(clearRegistrationBackendError('email'));
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
const handleSuggestionClick = (event) => {
|
|
73
|
+
event.preventDefault();
|
|
74
|
+
handleErrorChange('email', '');
|
|
75
|
+
handleChange({ target: { name: 'email', value: emailSuggestion.suggestion } });
|
|
76
|
+
setEmailSuggestion({ suggestion: '', type: '' });
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
const handleSuggestionClosed = () => setEmailSuggestion({ suggestion: '', type: '' });
|
|
80
|
+
|
|
81
|
+
const renderEmailFeedback = () => {
|
|
82
|
+
if (emailSuggestion.type === 'error') {
|
|
83
|
+
return (
|
|
84
|
+
<Alert variant="danger" className="email-suggestion-alert-error mt-1" icon={Error}>
|
|
85
|
+
<span className="email-suggestion__text">
|
|
86
|
+
{formatMessage(messages['did.you.mean.alert.text'])}{' '}
|
|
87
|
+
<Alert.Link
|
|
88
|
+
href="#"
|
|
89
|
+
name="email"
|
|
90
|
+
onClick={handleSuggestionClick}
|
|
91
|
+
>
|
|
92
|
+
{emailSuggestion.suggestion}
|
|
93
|
+
</Alert.Link>?
|
|
94
|
+
<Icon src={Close} className="email-suggestion__close" onClick={handleSuggestionClosed} tabIndex="0" />
|
|
95
|
+
</span>
|
|
96
|
+
</Alert>
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
return (
|
|
100
|
+
<span id="email-warning" className="small">
|
|
101
|
+
{formatMessage(messages['did.you.mean.alert.text'])}:{' '}
|
|
102
|
+
<Alert.Link
|
|
103
|
+
href="#"
|
|
104
|
+
name="email"
|
|
105
|
+
className="email-suggestion-alert-warning"
|
|
106
|
+
onClick={handleSuggestionClick}
|
|
107
|
+
>
|
|
108
|
+
{emailSuggestion.suggestion}
|
|
109
|
+
</Alert.Link>?
|
|
110
|
+
</span>
|
|
111
|
+
);
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
return (
|
|
115
|
+
<FormGroup
|
|
116
|
+
borderClass={emailSuggestion.type === 'warning' ? 'yellow-border' : ''}
|
|
117
|
+
maxLength={254} // Limit per RFCs is 254
|
|
118
|
+
{...props}
|
|
119
|
+
handleBlur={handleOnBlur}
|
|
120
|
+
handleFocus={handleOnFocus}
|
|
121
|
+
>
|
|
122
|
+
{emailSuggestion.suggestion ? renderEmailFeedback() : null}
|
|
123
|
+
</FormGroup>
|
|
124
|
+
);
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
EmailField.defaultProps = {
|
|
128
|
+
errorMessage: '',
|
|
129
|
+
confirmEmailValue: null,
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
EmailField.propTypes = {
|
|
133
|
+
errorMessage: PropTypes.string,
|
|
134
|
+
value: PropTypes.string.isRequired,
|
|
135
|
+
handleChange: PropTypes.func.isRequired,
|
|
136
|
+
handleErrorChange: PropTypes.func.isRequired,
|
|
137
|
+
confirmEmailValue: PropTypes.string,
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
export default EmailField;
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
import { Provider } from 'react-redux';
|
|
2
|
+
|
|
3
|
+
import { getSiteConfig, injectIntl, IntlProvider } from '@openedx/frontend-base';
|
|
4
|
+
import { fireEvent, render } from '@testing-library/react';
|
|
5
|
+
import { BrowserRouter as Router } from 'react-router-dom';
|
|
6
|
+
import configureStore from 'redux-mock-store';
|
|
7
|
+
|
|
8
|
+
import { clearRegistrationBackendError, fetchRealtimeValidations } from '../../data/actions';
|
|
9
|
+
import { EmailField } from '../index';
|
|
10
|
+
|
|
11
|
+
const IntlEmailField = injectIntl(EmailField);
|
|
12
|
+
const mockStore = configureStore();
|
|
13
|
+
|
|
14
|
+
jest.mock('react-router-dom', () => {
|
|
15
|
+
const mockNavigation = jest.fn();
|
|
16
|
+
|
|
17
|
+
// eslint-disable-next-line react/prop-types
|
|
18
|
+
const Navigate = ({ to }) => {
|
|
19
|
+
mockNavigation(to);
|
|
20
|
+
return <div />;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
return {
|
|
24
|
+
...jest.requireActual('react-router-dom'),
|
|
25
|
+
Navigate,
|
|
26
|
+
mockNavigate: mockNavigation,
|
|
27
|
+
};
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
describe('EmailField', () => {
|
|
31
|
+
let props = {};
|
|
32
|
+
let store = {};
|
|
33
|
+
|
|
34
|
+
const reduxWrapper = children => (
|
|
35
|
+
<IntlProvider locale="en">
|
|
36
|
+
<Provider store={store}>{children}</Provider>
|
|
37
|
+
</IntlProvider>
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
const routerWrapper = children => (
|
|
41
|
+
<Router>
|
|
42
|
+
{children}
|
|
43
|
+
</Router>
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
const initialState = {
|
|
47
|
+
register: {
|
|
48
|
+
registrationFormData: {
|
|
49
|
+
emailSuggestion: {
|
|
50
|
+
suggestion: 'example@gmail.com',
|
|
51
|
+
type: 'warning',
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
beforeEach(() => {
|
|
58
|
+
store = mockStore(initialState);
|
|
59
|
+
props = {
|
|
60
|
+
name: 'email',
|
|
61
|
+
value: '',
|
|
62
|
+
errorMessage: '',
|
|
63
|
+
handleChange: jest.fn(),
|
|
64
|
+
floatingLabel: '',
|
|
65
|
+
handleErrorChange: jest.fn(),
|
|
66
|
+
confirmEmailValue: '',
|
|
67
|
+
};
|
|
68
|
+
window.location = { search: '' };
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
afterEach(() => {
|
|
72
|
+
jest.clearAllMocks();
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
describe('Test Email Field', () => {
|
|
76
|
+
const emptyFieldValidation = {
|
|
77
|
+
email: 'Enter your email',
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
it('should run email field validation when onBlur is fired', () => {
|
|
81
|
+
const { container } = render(routerWrapper(reduxWrapper(<IntlEmailField {...props} />)));
|
|
82
|
+
|
|
83
|
+
const emailInput = container.querySelector('input#email');
|
|
84
|
+
fireEvent.blur(emailInput, { target: { value: '', name: 'email' } });
|
|
85
|
+
expect(props.handleErrorChange).toHaveBeenCalledTimes(1);
|
|
86
|
+
expect(props.handleErrorChange).toHaveBeenCalledWith(
|
|
87
|
+
'email',
|
|
88
|
+
emptyFieldValidation.email,
|
|
89
|
+
);
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it('should update errors for frontend validations', () => {
|
|
93
|
+
const { container } = render(routerWrapper(reduxWrapper(<IntlEmailField {...props} />)));
|
|
94
|
+
|
|
95
|
+
const emailInput = container.querySelector('input#email');
|
|
96
|
+
fireEvent.blur(emailInput, { target: { value: 'ab', name: 'email' } });
|
|
97
|
+
|
|
98
|
+
expect(props.handleErrorChange).toHaveBeenCalledTimes(1);
|
|
99
|
+
expect(props.handleErrorChange).toHaveBeenCalledWith(
|
|
100
|
+
'email',
|
|
101
|
+
'Enter a valid email address',
|
|
102
|
+
);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it('should clear error on focus', () => {
|
|
106
|
+
const { container } = render(routerWrapper(reduxWrapper(<IntlEmailField {...props} />)));
|
|
107
|
+
|
|
108
|
+
const emailInput = container.querySelector('input#email');
|
|
109
|
+
fireEvent.focus(emailInput, { target: { value: '', name: 'email' } });
|
|
110
|
+
|
|
111
|
+
expect(props.handleErrorChange).toHaveBeenCalledTimes(1);
|
|
112
|
+
expect(props.handleErrorChange).toHaveBeenCalledWith(
|
|
113
|
+
'email',
|
|
114
|
+
'',
|
|
115
|
+
);
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
it('should call backend validation api on blur event, if frontend validations have passed', () => {
|
|
119
|
+
store.dispatch = jest.fn(store.dispatch);
|
|
120
|
+
const { container } = render(routerWrapper(reduxWrapper(<IntlEmailField {...props} />)));
|
|
121
|
+
|
|
122
|
+
// Enter a valid email so that frontend validations are passed
|
|
123
|
+
const emailInput = container.querySelector('input#email');
|
|
124
|
+
fireEvent.blur(emailInput, { target: { value: 'test@gmail.com', name: 'email' } });
|
|
125
|
+
|
|
126
|
+
expect(store.dispatch).toHaveBeenCalledWith(fetchRealtimeValidations({ email: 'test@gmail.com' }));
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
it('should give email suggestions for common service provider domain typos', () => {
|
|
130
|
+
const { container } = render(routerWrapper(reduxWrapper(<IntlEmailField {...props} />)));
|
|
131
|
+
|
|
132
|
+
const emailInput = container.querySelector('input#email');
|
|
133
|
+
fireEvent.blur(emailInput, { target: { value: 'john@yopmail.com', name: 'email' } });
|
|
134
|
+
|
|
135
|
+
const emailWarning = container.querySelector('#email-warning');
|
|
136
|
+
expect(emailWarning.textContent).toEqual('Did you mean: john@hotmail.com?');
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
it('should be able to click on email suggestions and set it as value', () => {
|
|
140
|
+
const { container } = render(routerWrapper(reduxWrapper(<IntlEmailField {...props} />)));
|
|
141
|
+
|
|
142
|
+
const emailInput = container.querySelector('input#email');
|
|
143
|
+
fireEvent.blur(emailInput, { target: { value: 'john@yopmail.com', name: 'email' } });
|
|
144
|
+
|
|
145
|
+
const emailSuggestion = container.querySelector('.email-suggestion-alert-warning');
|
|
146
|
+
fireEvent.click(emailSuggestion);
|
|
147
|
+
|
|
148
|
+
expect(props.handleChange).toHaveBeenCalledTimes(1);
|
|
149
|
+
expect(props.handleChange).toHaveBeenCalledWith(
|
|
150
|
+
{ target: { name: 'email', value: 'john@hotmail.com' } },
|
|
151
|
+
);
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
it('should give error for common top level domain mistakes', () => {
|
|
155
|
+
const { container } = render(routerWrapper(reduxWrapper(<IntlEmailField {...props} />)));
|
|
156
|
+
|
|
157
|
+
const emailInput = container.querySelector('input#email');
|
|
158
|
+
fireEvent.blur(emailInput, { target: { value: 'john@gmail.mistake', name: 'email' } });
|
|
159
|
+
|
|
160
|
+
const errorElement = container.querySelector('.alert-danger');
|
|
161
|
+
expect(errorElement.textContent).toEqual('Did you mean john@gmail.com?');
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
it('should give error and suggestion for invalid email', () => {
|
|
165
|
+
const { container } = render(routerWrapper(reduxWrapper(<IntlEmailField {...props} />)));
|
|
166
|
+
|
|
167
|
+
const emailInput = container.querySelector('input#email');
|
|
168
|
+
fireEvent.blur(emailInput, { target: { value: 'john@gmail', name: 'email' } });
|
|
169
|
+
|
|
170
|
+
const errorElement = container.querySelector('.alert-danger');
|
|
171
|
+
expect(errorElement.textContent).toEqual('Did you mean john@gmail.com?');
|
|
172
|
+
|
|
173
|
+
expect(props.handleErrorChange).toHaveBeenCalledTimes(1);
|
|
174
|
+
expect(props.handleErrorChange).toHaveBeenCalledWith(
|
|
175
|
+
'email',
|
|
176
|
+
'Enter a valid email address',
|
|
177
|
+
);
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
it('should clear the registration validation error on focus on field', () => {
|
|
181
|
+
store.dispatch = jest.fn(store.dispatch);
|
|
182
|
+
store = mockStore({
|
|
183
|
+
...initialState,
|
|
184
|
+
register: {
|
|
185
|
+
...initialState.register,
|
|
186
|
+
registrationError: {
|
|
187
|
+
errorCode: 'duplicate-email',
|
|
188
|
+
email: [{ userMessage: `This email is already associated with an existing or previous ${getSiteConfig().siteName} account` }],
|
|
189
|
+
},
|
|
190
|
+
},
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
store.dispatch = jest.fn(store.dispatch);
|
|
194
|
+
|
|
195
|
+
const { container } = render(routerWrapper(reduxWrapper(<IntlEmailField {...props} />)));
|
|
196
|
+
|
|
197
|
+
const emailInput = container.querySelector('input#email');
|
|
198
|
+
fireEvent.focus(emailInput, { target: { value: 'a@gmail.com', name: 'email' } });
|
|
199
|
+
|
|
200
|
+
expect(store.dispatch).toHaveBeenCalledWith(clearRegistrationBackendError('email'));
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
it('should clear email suggestions when close icon is clicked', () => {
|
|
204
|
+
const { container } = render(routerWrapper(reduxWrapper(<IntlEmailField {...props} />)));
|
|
205
|
+
|
|
206
|
+
const emailInput = container.querySelector('input#email');
|
|
207
|
+
fireEvent.blur(emailInput, { target: { value: 'john@gmail.mistake', name: 'email' } });
|
|
208
|
+
|
|
209
|
+
const suggestionText = container.querySelector('.alert-danger');
|
|
210
|
+
expect(suggestionText.textContent).toEqual('Did you mean john@gmail.com?');
|
|
211
|
+
|
|
212
|
+
const closeButton = container.querySelector('.email-suggestion__close');
|
|
213
|
+
fireEvent.click(closeButton);
|
|
214
|
+
|
|
215
|
+
const closedSuggestionText = container.querySelector('.alert-danger');
|
|
216
|
+
expect(closedSuggestionText).toBeNull();
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
it('should set confirm email error if it exist', () => {
|
|
220
|
+
props = {
|
|
221
|
+
...props,
|
|
222
|
+
confirmEmailValue: 'confirmEmail@yopmail.com',
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
const { container } = render(routerWrapper(reduxWrapper(<IntlEmailField {...props} />)));
|
|
226
|
+
const emailInput = container.querySelector('input#email');
|
|
227
|
+
fireEvent.blur(emailInput, { target: { value: 'differentEmail@yopmail.com', name: 'email' } });
|
|
228
|
+
|
|
229
|
+
expect(props.handleErrorChange).toHaveBeenCalledTimes(1);
|
|
230
|
+
expect(props.handleErrorChange).toHaveBeenCalledWith(
|
|
231
|
+
'confirm_email',
|
|
232
|
+
'The email addresses do not match.',
|
|
233
|
+
);
|
|
234
|
+
});
|
|
235
|
+
});
|
|
236
|
+
});
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
export const COMMON_EMAIL_PROVIDERS = [
|
|
2
|
+
'hotmail.com', 'yahoo.com', 'outlook.com', 'live.com', 'gmail.com',
|
|
3
|
+
];
|
|
4
|
+
|
|
5
|
+
export const DEFAULT_SERVICE_PROVIDER_DOMAINS = ['yahoo', 'hotmail', 'live', 'outlook', 'gmail'];
|
|
6
|
+
|
|
7
|
+
export const DEFAULT_TOP_LEVEL_DOMAINS = [
|
|
8
|
+
'aaa', 'aarp', 'abarth', 'abb', 'abbott', 'abbvie', 'abc', 'able', 'abogado', 'abudhabi', 'ac', 'academy',
|
|
9
|
+
'accenture', 'accountant', 'accountants', 'aco', 'active', 'actor', 'ad', 'adac', 'ads', 'adult', 'ae', 'aeg', 'aero',
|
|
10
|
+
'aetna', 'af', 'afamilycompany', 'afl', 'africa', 'ag', 'agakhan', 'agency', 'ai', 'aig', 'aigo', 'airbus', 'airforce',
|
|
11
|
+
'airtel', 'akdn', 'al', 'alfaromeo', 'alibaba', 'alipay', 'allfinanz', 'allstate', 'ally', 'alsace', 'alstom', 'am',
|
|
12
|
+
'amazon', 'americanexpress', 'americanfamily', 'amex', 'amfam', 'amica', 'amsterdam', 'an', 'analytics', 'android',
|
|
13
|
+
'anquan', 'anz', 'ao', 'aol', 'apartments', 'app', 'apple', 'aq', 'aquarelle', 'ar', 'arab', 'aramco', 'archi', 'army',
|
|
14
|
+
'arpa', 'art', 'arte', 'as', 'asda', 'asia', 'associates', 'at', 'athleta', 'attorney', 'au', 'auction', 'audi',
|
|
15
|
+
'audible', 'audio', 'auspost', 'author', 'auto', 'autos', 'avianca', 'aw', 'aws', 'ax', 'axa', 'az', 'azure', 'ba',
|
|
16
|
+
'baby', 'baidu', 'banamex', 'bananarepublic', 'band', 'bank', 'bar', 'barcelona', 'barclaycard', 'barclays',
|
|
17
|
+
'barefoot', 'bargains', 'baseball', 'basketball', 'bauhaus', 'bayern', 'bb', 'bbc', 'bbt', 'bbva', 'bcg', 'bcn', 'bd',
|
|
18
|
+
'be', 'beats', 'beauty', 'beer', 'bentley', 'berlin', 'best', 'bestbuy', 'bet', 'bf', 'bg', 'bh', 'bharti', 'bi',
|
|
19
|
+
'bible', 'bid', 'bike', 'bing', 'bingo', 'bio', 'biz', 'bj', 'bl', 'black', 'blackfriday', 'blanco', 'blockbuster',
|
|
20
|
+
'blog', 'bloomberg', 'blue', 'bm', 'bms', 'bmw', 'bn', 'bnl', 'bnpparibas', 'bo', 'boats', 'boehringer', 'bofa', 'bom',
|
|
21
|
+
'bond', 'boo', 'book', 'booking', 'boots', 'bosch', 'bostik', 'boston', 'bot', 'boutique', 'box', 'bq', 'br',
|
|
22
|
+
'bradesco', 'bridgestone', 'broadway', 'broker', 'brother', 'brussels', 'bs', 'bt', 'budapest', 'bugatti', 'build',
|
|
23
|
+
'builders', 'business', 'buy', 'buzz', 'bv', 'bw', 'by', 'bz', 'bzh', 'ca', 'cab', 'cafe', 'cal', 'call',
|
|
24
|
+
'calvinklein', 'cam', 'camera', 'camp', 'cancerresearch', 'canon', 'capetown', 'capital', 'capitalone', 'car',
|
|
25
|
+
'caravan', 'cards', 'care', 'career', 'careers', 'cars', 'cartier', 'casa', 'case', 'caseih', 'cash', 'casino', 'cat',
|
|
26
|
+
'catering', 'catholic', 'cba', 'cbn', 'cbre', 'cbs', 'cc', 'cd', 'ceb', 'center', 'ceo', 'cern', 'cf', 'cfa', 'cfd',
|
|
27
|
+
'cg', 'ch', 'chanel', 'channel', 'charity', 'chase', 'chat', 'cheap', 'chintai', 'chloe', 'christmas', 'chrome',
|
|
28
|
+
'chrysler', 'church', 'ci', 'cipriani', 'circle', 'cisco', 'citadel', 'citi', 'citic', 'city', 'cityeats', 'ck', 'cl',
|
|
29
|
+
'claims', 'cleaning', 'click', 'clinic', 'clinique', 'clothing', 'cloud', 'club', 'clubmed', 'cm', 'cn', 'co', 'coach',
|
|
30
|
+
'codes', 'coffee', 'college', 'cologne', 'com', 'comcast', 'commbank', 'community', 'company', 'compare', 'computer',
|
|
31
|
+
'comsec', 'condos', 'construction', 'consulting', 'contact', 'contractors', 'cooking', 'cookingchannel', 'cool', 'coop',
|
|
32
|
+
'corsica', 'country', 'coupon', 'coupons', 'courses', 'cpa', 'cr', 'credit', 'creditcard', 'creditunion', 'cricket',
|
|
33
|
+
'crown', 'crs', 'cruise', 'cruises', 'csc', 'cu', 'cuisinella', 'cv', 'cw', 'cx', 'cy', 'cymru', 'cyou', 'cz', 'dabur',
|
|
34
|
+
'dad', 'dance', 'data', 'date', 'dating', 'datsun', 'day', 'dclk', 'dds', 'de', 'deal', 'dealer', 'deals', 'degree',
|
|
35
|
+
'delivery', 'dell', 'deloitte', 'delta', 'democrat', 'dental', 'dentist', 'desi', 'design', 'dev', 'dhl', 'diamonds',
|
|
36
|
+
'diet', 'digital', 'direct', 'directory', 'discount', 'discover', 'dish', 'diy', 'dj', 'dk', 'dm', 'dnp', 'do', 'docs',
|
|
37
|
+
'doctor', 'dodge', 'dog', 'doha', 'domains', 'doosan', 'dot', 'download', 'drive', 'dtv', 'dubai', 'duck', 'dunlop',
|
|
38
|
+
'duns', 'dupont', 'durban', 'dvag', 'dvr', 'dz', 'earth', 'eat', 'ec', 'eco', 'edeka', 'edu', 'education', 'ee', 'eg',
|
|
39
|
+
'eh', 'email', 'emerck', 'energy', 'engineer', 'engineering', 'enterprises', 'epost', 'epson', 'equipment', 'er',
|
|
40
|
+
'ericsson', 'erni', 'es', 'esq', 'estate', 'esurance', 'et', 'etisalat', 'eu', 'eurovision', 'eus', 'events', 'everbank',
|
|
41
|
+
'exchange', 'expert', 'exposed', 'express', 'extraspace', 'fage', 'fail', 'fairwinds', 'faith', 'family', 'fan', 'fans',
|
|
42
|
+
'farm', 'farmers', 'fashion', 'fast', 'fedex', 'feedback', 'ferrari', 'ferrero', 'fi', 'fiat', 'fidelity', 'fido', 'film',
|
|
43
|
+
'final', 'finance', 'financial', 'fire', 'firestone', 'firmdale', 'fish', 'fishing', 'fit', 'fitness', 'fj', 'fk',
|
|
44
|
+
'flickr', 'flights', 'flir', 'florist', 'flowers', 'flsmidth', 'fly', 'fm', 'fo', 'foo', 'food', 'foodnetwork', 'football',
|
|
45
|
+
'ford', 'forex', 'forsale', 'forum', 'foundation', 'fox', 'fr', 'free', 'fresenius', 'frl', 'frogans', 'frontdoor',
|
|
46
|
+
'frontier', 'ftr', 'fujitsu', 'fujixerox', 'fun', 'fund', 'furniture', 'futbol', 'fyi', 'ga', 'gal', 'gallery', 'gallo',
|
|
47
|
+
'gallup', 'game', 'games', 'gap', 'garden', 'gay', 'gb', 'gbiz', 'gd', 'gdn', 'ge', 'gea', 'gent', 'genting', 'george',
|
|
48
|
+
'gf', 'gg', 'ggee', 'gh', 'gi', 'gift', 'gifts', 'gives', 'giving', 'gl', 'glade', 'glass', 'gle', 'global', 'globo',
|
|
49
|
+
'gm', 'gmail', 'gmbh', 'gmo', 'gmx', 'gn', 'godaddy', 'gold', 'goldpoint', 'golf', 'goo', 'goodhands', 'goodyear', 'goog',
|
|
50
|
+
'google', 'gop', 'got', 'gov', 'gp', 'gq', 'gr', 'grainger', 'graphics', 'gratis', 'green', 'gripe', 'grocery', 'group',
|
|
51
|
+
'gs', 'gt', 'gu', 'guardian', 'gucci', 'guge', 'guide', 'guitars', 'guru', 'gw', 'gy', 'hair', 'hamburg', 'hangout',
|
|
52
|
+
'haus', 'hbo', 'hdfc', 'hdfcbank', 'health', 'healthcare', 'help', 'helsinki', 'here', 'hermes', 'hgtv', 'hiphop',
|
|
53
|
+
'hisamitsu', 'hitachi', 'hiv', 'hk', 'hkt', 'hm', 'hn', 'hockey', 'holdings', 'holiday', 'homedepot', 'homegoods',
|
|
54
|
+
'homes', 'homesense', 'honda', 'honeywell', 'horse', 'hospital', 'host', 'hosting', 'hot', 'hoteles', 'hotels', 'hotmail',
|
|
55
|
+
'house', 'how', 'hr', 'hsbc', 'ht', 'htc', 'hu', 'hughes', 'hyatt', 'hyundai', 'ibm', 'icbc', 'ice', 'icu', 'id', 'ie',
|
|
56
|
+
'ieee', 'ifm', 'iinet', 'ikano', 'il', 'im', 'imamat', 'imdb', 'immo', 'immobilien', 'in', 'inc', 'industries', 'infiniti',
|
|
57
|
+
'info', 'ing', 'ink', 'institute', 'insurance', 'insure', 'int', 'intel', 'international', 'intuit', 'investments',
|
|
58
|
+
'io', 'ipiranga', 'iq', 'ir', 'irish', 'is', 'iselect', 'ismaili', 'ist', 'istanbul', 'it', 'itau', 'itv', 'iveco', 'iwc',
|
|
59
|
+
'jaguar', 'java', 'jcb', 'jcp', 'je', 'jeep', 'jetzt', 'jewelry', 'jio', 'jlc', 'jll', 'jm', 'jmp', 'jnj', 'jo',
|
|
60
|
+
'jobs', 'joburg', 'jot', 'joy', 'jp', 'jpmorgan', 'jprs', 'juegos', 'juniper', 'kaufen', 'kddi', 'ke', 'kerryhotels',
|
|
61
|
+
'kerrylogistics', 'kerryproperties', 'kfh', 'kg', 'kh', 'ki', 'kia', 'kim', 'kinder', 'kindle', 'kitchen', 'kiwi', 'km',
|
|
62
|
+
'kn', 'koeln', 'komatsu', 'kosher', 'kp', 'kpmg', 'kpn', 'kr', 'krd', 'kred', 'kuokgroup', 'kw', 'ky', 'kyoto', 'kz',
|
|
63
|
+
'la', 'lacaixa', 'ladbrokes', 'lamborghini', 'lamer', 'lancaster', 'lancia', 'lancome', 'land', 'landrover', 'lanxess',
|
|
64
|
+
'lasalle', 'lat', 'latino', 'latrobe', 'law', 'lawyer', 'lb', 'lc', 'lds', 'lease', 'leclerc', 'lefrak', 'legal',
|
|
65
|
+
'lego', 'lexus', 'lgbt', 'li', 'liaison', 'lidl', 'life', 'lifeinsurance', 'lifestyle', 'lighting', 'like', 'lilly',
|
|
66
|
+
'limited', 'limo', 'lincoln', 'linde', 'link', 'lipsy', 'live', 'living', 'lixil', 'lk', 'llc', 'llp', 'loan', 'loans',
|
|
67
|
+
'locker', 'locus', 'loft', 'lol', 'london', 'lotte', 'lotto', 'love', 'lpl', 'lplfinancial', 'lr', 'ls', 'lt', 'ltd',
|
|
68
|
+
'ltda', 'lu', 'lundbeck', 'lupin', 'luxe', 'luxury', 'lv', 'ly', 'ma', 'macys', 'madrid', 'maif', 'maison', 'makeup',
|
|
69
|
+
'man', 'management', 'mango', 'map', 'market', 'marketing', 'markets', 'marriott', 'marshalls', 'maserati', 'mattel',
|
|
70
|
+
'mba', 'mc', 'mcd', 'mcdonalds', 'mckinsey', 'md', 'me', 'med', 'media', 'meet', 'melbourne', 'meme', 'memorial', 'men',
|
|
71
|
+
'menu', 'meo', 'merckmsd', 'metlife', 'mf', 'mg', 'mh', 'miami', 'microsoft', 'mil', 'mini', 'mint', 'mit', 'mitsubishi',
|
|
72
|
+
'mk', 'ml', 'mlb', 'mls', 'mm', 'mma', 'mn', 'mo', 'mobi', 'mobile', 'mobily', 'moda', 'moe', 'moi', 'mom', 'monash',
|
|
73
|
+
'money', 'monster', 'montblanc', 'mopar', 'mormon', 'mortgage', 'moscow', 'moto', 'motorcycles', 'mov', 'movie', 'movistar',
|
|
74
|
+
'mp', 'mq', 'mr', 'ms', 'msd', 'mt', 'mtn', 'mtpc', 'mtr', 'mu', 'museum', 'mutual', 'mutuelle', 'mv', 'mw', 'mx', 'my',
|
|
75
|
+
'mz', 'na', 'nab', 'nadex', 'nagoya', 'name', 'nationwide', 'natura', 'navy', 'nba', 'nc', 'ne', 'nec', 'net', 'netbank',
|
|
76
|
+
'netflix', 'network', 'neustar', 'new', 'newholland', 'news', 'next', 'nextdirect', 'nexus', 'nf', 'nfl', 'ng', 'ngo', 'nhk',
|
|
77
|
+
'ni', 'nico', 'nike', 'nikon', 'ninja', 'nissan', 'nissay', 'nl', 'no', 'nokia', 'northwesternmutual', 'norton', 'now',
|
|
78
|
+
'nowruz', 'nowtv', 'np', 'nr', 'nra', 'nrw', 'ntt', 'nu', 'nyc', 'nz', 'obi', 'observer', 'off', 'office', 'okinawa',
|
|
79
|
+
'olayan', 'olayangroup', 'oldnavy', 'ollo', 'om', 'omega', 'one', 'ong', 'onl', 'online', 'onyourside', 'ooo', 'open',
|
|
80
|
+
'oracle', 'orange', 'org', 'organic', 'orientexpress', 'origins', 'osaka', 'otsuka', 'ott', 'ovh', 'pa', 'page',
|
|
81
|
+
'pamperedchef', 'panasonic', 'panerai', 'paris', 'pars', 'partners', 'parts', 'party', 'passagens', 'pay', 'pccw', 'pe',
|
|
82
|
+
'pet', 'pf', 'pfizer', 'pg', 'ph', 'pharmacy', 'phd', 'philips', 'phone', 'photo', 'photography', 'photos', 'physio',
|
|
83
|
+
'piaget', 'pics', 'pictet', 'pictures', 'pid', 'pin', 'ping', 'pink', 'pioneer', 'pizza', 'pk', 'pl', 'place', 'play',
|
|
84
|
+
'playstation', 'plumbing', 'plus', 'pm', 'pn', 'pnc', 'pohl', 'poker', 'politie', 'porn', 'post', 'pr', 'pramerica',
|
|
85
|
+
'praxi', 'press', 'prime', 'pro', 'prod', 'productions', 'prof', 'progressive', 'promo', 'properties', 'property',
|
|
86
|
+
'protection', 'pru', 'prudential', 'ps', 'pt', 'pub', 'pw', 'pwc', 'py', 'qa', 'qpon', 'quebec', 'quest', 'qvc',
|
|
87
|
+
'racing', 'radio', 'raid', 're', 'read', 'realestate', 'realtor', 'realty', 'recipes', 'red', 'redstone', 'redumbrella',
|
|
88
|
+
'rehab', 'reise', 'reisen', 'reit', 'reliance', 'ren', 'rent', 'rentals', 'repair', 'report', 'republican', 'rest',
|
|
89
|
+
'restaurant', 'review', 'reviews', 'rexroth', 'rich', 'richardli', 'ricoh', 'rightathome', 'ril', 'rio', 'rip', 'rmit',
|
|
90
|
+
'ro', 'rocher', 'rocks', 'rodeo', 'rogers', 'room', 'rs', 'rsvp', 'ru', 'rugby', 'ruhr', 'run', 'rw', 'rwe', 'ryukyu',
|
|
91
|
+
'sa', 'saarland', 'safe', 'safety', 'sakura', 'sale', 'salon', 'samsclub', 'samsung', 'sandvik', 'sandvikcoromant',
|
|
92
|
+
'sanofi', 'sap', 'sapo', 'sarl', 'sas', 'save', 'saxo', 'sb', 'sbi', 'sbs', 'sc', 'sca', 'scb', 'schaeffler', 'schmidt',
|
|
93
|
+
'scholarships', 'school', 'schule', 'schwarz', 'science', 'scjohnson', 'scor', 'scot', 'sd', 'se', 'search', 'seat',
|
|
94
|
+
'secure', 'security', 'seek', 'select', 'sener', 'services', 'ses', 'seven', 'sew', 'sex', 'sexy', 'sfr', 'sg', 'sh',
|
|
95
|
+
'shangrila', 'sharp', 'shaw', 'shell', 'shia', 'shiksha', 'shoes', 'shop', 'shopping', 'shouji', 'show', 'showtime',
|
|
96
|
+
'shriram', 'si', 'silk', 'sina', 'singles', 'site', 'sj', 'sk', 'ski', 'skin', 'sky', 'skype', 'sl', 'sling', 'sm',
|
|
97
|
+
'smart', 'smile', 'sn', 'sncf', 'so', 'soccer', 'social', 'softbank', 'software', 'sohu', 'solar', 'solutions', 'song',
|
|
98
|
+
'sony', 'soy', 'spa', 'space', 'spiegel', 'sport', 'spot', 'spreadbetting', 'sr', 'srl', 'srt', 'ss', 'st', 'stada',
|
|
99
|
+
'staples', 'star', 'starhub', 'statebank', 'statefarm', 'statoil', 'stc', 'stcgroup', 'stockholm', 'storage', 'store',
|
|
100
|
+
'stream', 'studio', 'study', 'style', 'su', 'sucks', 'supplies', 'supply', 'support', 'surf', 'surgery', 'suzuki', 'sv',
|
|
101
|
+
'swatch', 'swiftcover', 'swiss', 'sx', 'sy', 'sydney', 'symantec', 'systems', 'sz', 'tab', 'taipei', 'talk', 'taobao',
|
|
102
|
+
'target', 'tatamotors', 'tatar', 'tattoo', 'tax', 'taxi', 'tc', 'tci', 'td', 'tdk', 'team', 'tech', 'technology', 'tel',
|
|
103
|
+
'telecity', 'telefonica', 'temasek', 'tennis', 'teva', 'tf', 'tg', 'th', 'thd', 'theater', 'theatre', 'tiaa', 'tickets',
|
|
104
|
+
'tienda', 'tiffany', 'tips', 'tires', 'tirol', 'tj', 'tjmaxx', 'tjx', 'tk', 'tkmaxx', 'tl', 'tm', 'tmall', 'tn', 'to',
|
|
105
|
+
'today', 'tokyo', 'tools', 'top', 'toray', 'toshiba', 'total', 'tours', 'town', 'toyota', 'toys', 'tp', 'tr', 'trade',
|
|
106
|
+
'trading', 'training', 'travel', 'travelchannel', 'travelers', 'travelersinsurance', 'trust', 'trv', 'tt', 'tube', 'tui',
|
|
107
|
+
'tunes', 'tushu', 'tv', 'tvs', 'tw', 'tz', 'ua', 'ubank', 'ubs', 'uconnect', 'ug', 'uk', 'um', 'unicom', 'university',
|
|
108
|
+
'uno', 'uol', 'ups', 'us', 'uy', 'uz', 'va', 'vacations', 'vana', 'vanguard', 'vc', 've', 'vegas', 'ventures', 'verisign',
|
|
109
|
+
'versicherung', 'vet', 'vg', 'vi', 'viajes', 'video', 'vig', 'viking', 'villas', 'vin', 'vip', 'virgin', 'visa', 'vision',
|
|
110
|
+
'vista', 'vistaprint', 'viva', 'vivo', 'vlaanderen', 'vn', 'vodka', 'volkswagen', 'volvo', 'vote', 'voting', 'voto',
|
|
111
|
+
'voyage', 'vu', 'vuelos', 'wales', 'walmart', 'walter', 'wang', 'wanggou', 'warman', 'watch', 'watches', 'weather',
|
|
112
|
+
'weatherchannel', 'webcam', 'weber', 'website', 'wed', 'wedding', 'weibo', 'weir', 'wf', 'whoswho', 'wien', 'wiki',
|
|
113
|
+
'williamhill', 'win', 'windows', 'wine', 'winners', 'wme', 'wolterskluwer', 'woodside', 'work', 'works', 'world', 'wow',
|
|
114
|
+
'ws', 'wtc', 'wtf', 'xbox', 'xerox', 'xfinity', 'xihuan', 'xin', '测试', 'कॉम', 'परीक्षा', 'セール', '佛山', 'ಭಾರತ', '慈善',
|
|
115
|
+
'集团', '在线', '한국', 'ଭାରତ', '大众汽车', '点看', 'คอม', 'ভাৰত', 'ভারত', '八卦', 'ישראל\u200e', 'موقع\u200e', 'বাংলা', '公益',
|
|
116
|
+
'公司', '香格里拉', '网站', '移动', '我爱你', 'москва', 'испытание', 'қаз', 'католик', 'онлайн', 'сайт', '联通', 'срб', 'бг',
|
|
117
|
+
'бел', 'קום\u200e', '时尚', '微博', '테스트', '淡马锡', 'ファッション', 'орг', 'नेट', 'ストア', 'アマゾン', '삼성', 'சிங்கப்பூர்', '商标',
|
|
118
|
+
'商店', '商城', 'дети', 'мкд', 'טעסט\u200e', 'ею', 'ポイント', '新闻', '工行', '家電', 'كوم\u200e', '中文网', '中信', '中国',
|
|
119
|
+
'中國', '娱乐', '谷歌', 'భారత్', 'ලංකා', '電訊盈科', '购物', '測試', 'クラウド', 'ભારત', '通販', 'भारतम्', 'भारत', 'भारोत', 'آزمایشی\u200e',
|
|
120
|
+
'பரிட்சை', '网店', 'संगठन', '餐厅', '网络', 'ком', 'укр', '香港', '亚马逊', '诺基亚', '食品', 'δοκιμή', '飞利浦', 'إختبار\u200e',
|
|
121
|
+
'台湾', '台灣', '手表', '手机', 'мон', 'الجزائر\u200e', 'عمان\u200e', 'ارامكو\u200e', 'ایران\u200e', 'العليان\u200e',
|
|
122
|
+
'اتصالات\u200e', 'امارات\u200e', 'بازار\u200e', 'موريتانيا\u200e', 'پاکستان\u200e', 'الاردن\u200e', 'موبايلي\u200e',
|
|
123
|
+
'بارت\u200e', 'بھارت\u200e', 'المغرب\u200e', 'ابوظبي\u200e', 'البحرين\u200e', 'السعودية\u200e', 'ڀارت\u200e',
|
|
124
|
+
'كاثوليك\u200e', 'سودان\u200e', 'همراه\u200e', 'عراق\u200e', 'مليسيا\u200e', '澳門', '닷컴', '政府', 'شبكة\u200e',
|
|
125
|
+
'بيتك\u200e', 'عرب\u200e', 'გე', '机构', '组织机构', '健康', 'ไทย', 'سورية\u200e', '招聘', 'рус', 'рф', '珠宝',
|
|
126
|
+
'تونس\u200e', '大拿', 'ລາວ', 'みんな', 'グーグル', 'ευ', 'ελ', '世界', '書籍', 'ഭാരതം', 'ਭਾਰਤ', '网址', '닷넷', 'コム',
|
|
127
|
+
'天主教', '游戏', 'vermögensberater', 'vermögensberatung', '企业', '信息', '嘉里大酒店', '嘉里', 'مصر\u200e',
|
|
128
|
+
'قطر\u200e', '广东', 'இலங்கை', 'இந்தியா', 'հայ', '新加坡', 'فلسطين\u200e', 'テスト', '政务', 'xperia', 'xxx',
|
|
129
|
+
'xyz', 'yachts', 'yahoo', 'yamaxun', 'yandex', 'ye', 'yodobashi', 'yoga', 'yokohama', 'you', 'youtube', 'yt',
|
|
130
|
+
'yun', 'za', 'zappos', 'zara', 'zero', 'zip', 'zippo', 'zm', 'zone', 'zuerich', 'zw',
|
|
131
|
+
];
|