Skip to content

Commit

Permalink
Fixes ReCaptcha Fail during login (PalisadoesFoundation#1613)
Browse files Browse the repository at this point in the history
* Fixed recaptcha for Admin/User

* Added Test Cases & Refactored Login & UserLoginPage

* Shift socialMediaLinks const to src/constants

* Revert changes in README.md
  • Loading branch information
GlenDsza authored Mar 4, 2024
1 parent af70ef3 commit b199b2f
Show file tree
Hide file tree
Showing 3 changed files with 109 additions and 99 deletions.
22 changes: 22 additions & 0 deletions src/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import {
FacebookLogo,
LinkedInLogo,
GithubLogo,
InstagramLogo,
SlackLogo,
TwitterLogo,
YoutubeLogo,
} from 'assets/svgs/social-icons';

export const socialMediaLinks = [
{ href: 'https://www.facebook.com/palisadoesproject', logo: FacebookLogo },
{ href: 'https://twitter.com/palisadoesorg?lang=en', logo: TwitterLogo },
{ href: 'https://www.linkedin.com/company/palisadoes/', logo: LinkedInLogo },
{ href: 'https://github.com/PalisadoesFoundation', logo: GithubLogo },
{
href: 'https://www.youtube.com/@PalisadoesOrganization',
logo: YoutubeLogo,
},
{ href: 'https://www.palisadoes.org/slack', logo: SlackLogo },
{ href: 'https://www.instagram.com/palisadoes/', logo: InstagramLogo },
];
67 changes: 66 additions & 1 deletion src/screens/LoginPage/LoginPage.test.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from 'react';
import { MockedProvider } from '@apollo/react-testing';
import { act, render, screen } from '@testing-library/react';
import { act, render, screen, fireEvent } from '@testing-library/react';
import { Provider } from 'react-redux';
import { BrowserRouter } from 'react-router-dom';
import userEvent from '@testing-library/user-event';
Expand Down Expand Up @@ -102,6 +102,41 @@ jest.mock('Constant/constant.ts', () => ({
RECAPTCHA_SITE_KEY: 'xxx',
}));

jest.mock('react-google-recaptcha', () => {
const react = jest.requireActual('react');
const recaptcha = react.forwardRef(
(
props: {
onChange: (value: string) => void;
} & React.InputHTMLAttributes<HTMLInputElement>,
ref: React.LegacyRef<HTMLInputElement> | undefined,
): JSX.Element => {
const { onChange, ...otherProps } = props;

const handleChange = (
event: React.ChangeEvent<HTMLInputElement>,
): void => {
if (onChange) {
onChange(event.target.value);
}
};

return (
<>
<input
type="text"
data-testid="mock-recaptcha"
{...otherProps}
onChange={handleChange}
ref={ref}
/>
</>
);
},
);
return recaptcha;
});

describe('Talawa-API server fetch check', () => {
beforeEach(() => {
jest.clearAllMocks();
Expand Down Expand Up @@ -697,4 +732,34 @@ describe('Testing Login Page Screen', () => {
expect(screen.getByText(/User Login/i)).toBeInTheDocument();
expect(window.location).toBeAt('/user/organizations');
});

test('on value change of ReCAPTCHA onChange event should be triggered in both the captcha', async () => {
render(
<MockedProvider addTypename={false} link={link}>
<BrowserRouter>
<Provider store={store}>
<I18nextProvider i18n={i18nForTest}>
<LoginPage />
</I18nextProvider>
</Provider>
</BrowserRouter>
</MockedProvider>,
);

const recaptchaElements = screen.getAllByTestId('mock-recaptcha');

for (const recaptchaElement of recaptchaElements) {
const inputElement = recaptchaElement as HTMLInputElement;

fireEvent.input(inputElement, {
target: { value: 'test-token' },
});

fireEvent.change(inputElement, {
target: { value: 'test-token2' },
});

expect(recaptchaElement).toHaveValue('test-token2');
}
});
});
119 changes: 21 additions & 98 deletions src/screens/LoginPage/LoginPage.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useMutation } from '@apollo/client';
import type { ChangeEvent } from 'react';
import React, { useEffect, useRef, useState } from 'react';
import React, { useEffect, useState } from 'react';
import { Form } from 'react-bootstrap';
import Button from 'react-bootstrap/Button';
import Col from 'react-bootstrap/Col';
Expand All @@ -11,16 +11,6 @@ import { Link, useHistory } from 'react-router-dom';
import { toast } from 'react-toastify';
import { Check, Clear } from '@mui/icons-material';

import {
FacebookLogo,
LinkedInLogo,
GithubLogo,
InstagramLogo,
SlackLogo,
TwitterLogo,
YoutubeLogo,
} from 'assets/svgs/social-icons';

import {
REACT_APP_USE_RECAPTCHA,
RECAPTCHA_SITE_KEY,
Expand All @@ -40,6 +30,7 @@ import { errorHandler } from 'utils/errorHandler';
import styles from './LoginPage.module.css';
import EmailOutlinedIcon from '@mui/icons-material/EmailOutlined';
import useLocalStorage from 'utils/useLocalstorage';
import { socialMediaLinks } from '../../constants';

const loginPage = (): JSX.Element => {
const { t } = useTranslation('translation', { keyPrefix: 'loginPage' });
Expand All @@ -55,6 +46,7 @@ const loginPage = (): JSX.Element => {
numericValue: boolean;
specialChar: boolean;
};
const [recaptchaToken, setRecaptchaToken] = useState<string | null>(null);
const [showTab, setShowTab] = useState<'LOGIN' | 'REGISTER'>('LOGIN');
const [role, setRole] = useState<'admin' | 'user'>('admin');
const [componentLoader, setComponentLoader] = useState(true);
Expand Down Expand Up @@ -86,38 +78,20 @@ const loginPage = (): JSX.Element => {
numericalValueRegExp: new RegExp('\\d'),
specialCharRegExp: new RegExp('[!@#$%^&*()_+{}\\[\\]:;<>,.?~\\\\/-]'),
};
const handleLowercasePassCheck = (pass: string): void => {
setShowAlert((prevAlert) => ({
...prevAlert,
lowercaseChar: !passwordValidationRegExp.lowercaseCharRegExp.test(pass),
}));
};

const handleUppercasePassCheck = (pass: string): void => {
setShowAlert((prevAlert) => ({
...prevAlert,
const handlePasswordCheck = (pass: string): void => {
setShowAlert({
lowercaseChar: !passwordValidationRegExp.lowercaseCharRegExp.test(pass),
uppercaseChar: !passwordValidationRegExp.uppercaseCharRegExp.test(pass),
}));
};
const handleNumericalValuePassCheck = (pass: string): void => {
setShowAlert((prevAlert) => ({
...prevAlert,
numericValue: !passwordValidationRegExp.numericalValueRegExp.test(pass),
}));
};
const handleSpecialCharPassCheck = (pass: string): void => {
setShowAlert((prevAlert) => ({
...prevAlert,
specialChar: !passwordValidationRegExp.specialCharRegExp.test(pass),
}));
});
};

const handleRoleToggle = (role: 'admin' | 'user'): void => {
setRole(role);
};

const recaptchaRef = useRef<ReCAPTCHA>(null);

useEffect(() => {
const isLoggedIn = getItem('IsLoggedIn');
if (isLoggedIn == 'TRUE') {
Expand Down Expand Up @@ -169,15 +143,16 @@ const loginPage = (): JSX.Element => {
}
};

const handleCaptcha = (token: string | null): void => {
setRecaptchaToken(token);
};

const signupLink = async (e: ChangeEvent<HTMLFormElement>): Promise<void> => {
e.preventDefault();

const { signfirstName, signlastName, signEmail, signPassword, cPassword } =
signformState;

const recaptchaToken = recaptchaRef.current?.getValue();
recaptchaRef.current?.reset();

const isVerified = await verifyRecaptcha(recaptchaToken);
/* istanbul ignore next */
if (!isVerified) {
Expand Down Expand Up @@ -259,10 +234,6 @@ const loginPage = (): JSX.Element => {

const loginLink = async (e: ChangeEvent<HTMLFormElement>): Promise<void> => {
e.preventDefault();

const recaptchaToken = recaptchaRef.current?.getValue();
recaptchaRef.current?.reset();

const isVerified = await verifyRecaptcha(recaptchaToken);
/* istanbul ignore next */
if (!isVerified) {
Expand Down Expand Up @@ -317,6 +288,12 @@ const loginPage = (): JSX.Element => {
return <Loader />;
}

const socialIconsList = socialMediaLinks.map(({ href, logo }, index) => (
<a key={index} href={href} target="_blank" rel="noopener noreferrer">
<img src={logo} />
</a>
));

return (
<>
<section className={styles.login_background}>
Expand All @@ -332,58 +309,7 @@ const loginPage = (): JSX.Element => {
<p className="text-center">{t('fromPalisadoes')}</p>
</a>
</div>

<div className={styles.socialIcons}>
<a
href="https://www.facebook.com/palisadoesproject"
target="_blank"
rel="noopener noreferrer"
>
<img src={FacebookLogo} />
</a>
<a
href="https://twitter.com/palisadoesorg?lang=en"
target="_blank"
rel="noopener noreferrer"
>
<img src={TwitterLogo} className={styles.socialIcon} />
</a>
<a
href="https://www.linkedin.com/company/palisadoes/"
target="_blank"
rel="noopener noreferrer"
>
<img src={LinkedInLogo} />
</a>
<a
href="https://github.com/PalisadoesFoundation"
target="_blank"
rel="noopener noreferrer"
>
<img src={GithubLogo} />
</a>
<a
href="https://www.youtube.com/@PalisadoesOrganization"
target="_blank"
rel="noopener noreferrer"
>
<img src={YoutubeLogo} />
</a>
<a
href="https://www.palisadoes.org/slack"
target="_blank"
rel="noopener noreferrer"
>
<img src={SlackLogo} />
</a>
<a
href="https://www.instagram.com/palisadoes/"
target="_blank"
rel="noopener noreferrer"
>
<img src={InstagramLogo} />
</a>
</div>
<div className={styles.socialIcons}>{socialIconsList}</div>
</Col>
<Col sm={12} md={6} lg={5}>
<div className={styles.right_portion}>
Expand Down Expand Up @@ -473,12 +399,12 @@ const loginPage = (): JSX.Element => {
{REACT_APP_USE_RECAPTCHA === 'yes' ? (
<div className="googleRecaptcha">
<ReCAPTCHA
ref={recaptchaRef}
className="mt-2"
sitekey={
/* istanbul ignore next */
RECAPTCHA_SITE_KEY ? RECAPTCHA_SITE_KEY : 'XXX'
}
onChange={handleCaptcha}
/>
</div>
) : (
Expand Down Expand Up @@ -605,10 +531,7 @@ const loginPage = (): JSX.Element => {
...signformState,
signPassword: e.target.value,
});
handleLowercasePassCheck(e.target.value);
handleUppercasePassCheck(e.target.value);
handleNumericalValuePassCheck(e.target.value);
handleSpecialCharPassCheck(e.target.value);
handlePasswordCheck(e.target.value);
}}
/>
<Button
Expand Down Expand Up @@ -788,11 +711,11 @@ const loginPage = (): JSX.Element => {
{REACT_APP_USE_RECAPTCHA === 'yes' ? (
<div className="mt-3">
<ReCAPTCHA
ref={recaptchaRef}
sitekey={
/* istanbul ignore next */
RECAPTCHA_SITE_KEY ? RECAPTCHA_SITE_KEY : 'XXX'
}
onChange={handleCaptcha}
/>
</div>
) : (
Expand Down

0 comments on commit b199b2f

Please sign in to comment.