Skip to content

Commit

Permalink
Merge branch 'arc-themes-release-version-2.3.0' into THEMES-1466
Browse files Browse the repository at this point in the history
  • Loading branch information
malavikakoppula authored Mar 26, 2024
2 parents 664317f + 092cfba commit bb46040
Show file tree
Hide file tree
Showing 12 changed files with 330 additions and 52 deletions.
12 changes: 12 additions & 0 deletions blocks/identity-block/_index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -62,11 +62,23 @@
.b-login-form {
@include scss.block-components("login-form");
@include scss.block-properties("login-form");
&__bot-protection-section {
@include scss.block-components("login-form-bot-protection-section");
@include scss.block-properties("login-form-bot-protection-section");
}
&__privacy-statement {
@include scss.block-components("login-form-privacy-statement");
@include scss.block-properties("login-form-privacy-statement");
}
}

.b-login-links {
@include scss.block-components("login-links");
@include scss.block-properties("login-links");
&__inner-link {
@include scss.block-components("login-links-inner-link");
@include scss.block-properties("login-links-inner-link");
}
}

.b-reset-password {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import React, { useState, useEffect } from "react";
import { useFusionContext } from "fusion:context";
import getProperties from "fusion:properties";
import getTranslatedPhrases from "fusion:intl";
import { Paragraph, useIdentity, useSales } from "@wpmedia/arc-themes-components";
import ReCAPTCHA from "react-google-recaptcha";

const BotChallengeProtection = ({ challengeIn, setCaptchaToken, className, captchaError, setCaptchaError }) => {
const { Identity, isInitialized } = useIdentity();
const { Sales } = useSales();
const [siteKey, setSiteKey] = useState();

const onChange = (value) => {
setCaptchaToken(value);
setCaptchaError(null);
localStorage.setItem('ArcXP_captchaToken', value);
};

useEffect(() => {
const checkCaptcha = async () => {
const config = await Identity.getConfig();
const {recaptchaSiteKey, recaptchaScore } = config;
const isIdentityCaptchaEnabled = config?.[`${challengeIn}Recaptcha`];

if(['signup', 'signin', 'magicLink'].includes(challengeIn)) {
if (isIdentityCaptchaEnabled && recaptchaScore === '-1' && recaptchaSiteKey) {
setSiteKey(recaptchaSiteKey);
}
}

if (challengeIn === 'checkout') {
const salesConfig = await Sales.getConfig();
const isSalesCaptchaEnabled = salesConfig?.checkoutRecaptchaEnabled;
if (isSalesCaptchaEnabled && recaptchaScore === '-1' && recaptchaSiteKey) {
setSiteKey(recaptchaSiteKey);
}
}

};
checkCaptcha();

}, [Identity, Sales, challengeIn]);

const { arcSite } = useFusionContext();
const { locale } = getProperties(arcSite);
const phrases = getTranslatedPhrases(locale);

if (!isInitialized) {
return null;
}

return (
<section className={`${className}__bot-protection-section`} data-testid="bot-challege-protection-container">
{!!siteKey && <ReCAPTCHA
sitekey={siteKey}
onChange={onChange}
onExpired={() => {}}
/>}
{captchaError && <Paragraph data-testid="bot-challege-captcha-error">{phrases.t("identity-block.bot-protection-error")}</Paragraph>}
</section>
);
};

export default BotChallengeProtection;
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import React from "react";
import { render, screen } from "@testing-library/react";
import { useIdentity } from "@wpmedia/arc-themes-components";
import BotChallengeProtection from ".";

const mockLogin = jest.fn(() => Promise.resolve());

const mockIdentity = {
isLoggedIn: jest.fn(() => false),
getConfig: jest.fn(() => ({})),
login: mockLogin,
};

const mockSales = {
getConfig: jest.fn(() => {})
}

jest.mock("@wpmedia/arc-themes-components", () => ({
...jest.requireActual("@wpmedia/arc-themes-components"),
useIdentity: jest.fn(() => ({
isInitialized: true,
Identity: {
...mockIdentity,
},
})),
useSales: jest.fn(() => ({
isInitialized: true,
Sales: {
...mockSales,
},
})),
}));

describe("Bot challenge protection", () => {
it("renders with required items", () => {
render(<BotChallengeProtection challengeIn="signin" />);

expect(screen.getByTestId("bot-challege-protection-container")).not.toBeNull();
});
it("it does not render if identity is not initialized", () => {
useIdentity.mockImplementation(() => ({
isInitialized: false,
Identity: {
...mockIdentity,
},
}))
render(<BotChallengeProtection challengeIn="test" />);
expect(screen.queryByTestId("bot-challege-protection-container")).toBeNull();
});
});
4 changes: 2 additions & 2 deletions blocks/identity-block/features/login-links/default.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React from "react";
import PropTypes from "@arc-fusion/prop-types";
import { useFusionContext } from "fusion:context";
import getTranslatedPhrases from "fusion:intl";
import { Link, Stack } from "@wpmedia/arc-themes-components";
import { Link, Paragraph, Stack } from "@wpmedia/arc-themes-components";

const BLOCK_CLASS_NAME = "b-login-links";
const defaultLoginURL = "/account/login/";
Expand Down Expand Up @@ -35,7 +35,7 @@ const LoginLinks = ({ customFields }) => {
<Link href={forgotURL}>{phrases.t("identity-block.login-links-forgot")}</Link>
) : null}
{showSignUp ? (
<Link href={signUpURL}>{phrases.t("identity-block.login-links-signup")}</Link>
<Paragraph>{phrases.t("identity-block.login-links-signup")}<Link href={signUpURL} className={`${BLOCK_CLASS_NAME}__inner-link`}>{phrases.t("identity-block.sign-up-natural")}</Link></Paragraph>
) : null}
</Stack>
);
Expand Down
40 changes: 31 additions & 9 deletions blocks/identity-block/features/login/default.jsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import React, { useState, useEffect } from "react";
/* global grecaptcha */
import React, { useState } from "react";
import PropTypes from "@arc-fusion/prop-types";
import { useFusionContext } from "fusion:context";
import getProperties from "fusion:properties";
import getTranslatedPhrases from "fusion:intl";
import { Input, useIdentity } from "@wpmedia/arc-themes-components";
import { Input, useIdentity, Paragraph } from "@wpmedia/arc-themes-components";
import HeadlinedSubmitForm from "../../components/headlined-submit-form";
import useLogin from "../../components/login";
import BotChallengeProtection from "../../components/bot-challenge-protection";
import useOIDCLogin from "../../utils/useOIDCLogin";
import validateURL from "../../utils/validate-redirect-url";

Expand All @@ -14,16 +16,18 @@ const BLOCK_CLASS_NAME = "b-login-form";
const Login = ({ customFields }) => {
const { redirectURL, redirectToPreviousPage, loggedInPageLocation, OIDC } = customFields;

const url_string = window.location.href;
const url = new URL(url_string);
const urlString = window.location.href;
const url = new URL(urlString);

const { isAdmin, arcSite } = useFusionContext();
const { locale } = getProperties(arcSite);
const phrases = getTranslatedPhrases(locale);

const isOIDC = OIDC && url.searchParams.get("client_id") && url.searchParams.get("response_type") === "code";
const { Identity, isInitialized } = useIdentity();
const [captchaToken, setCaptchaToken] = useState();
const [error, setError] = useState();
const [captchaError, setCaptchaError] = useState();
const { loginRedirect } = useLogin({
isAdmin,
redirectURL,
Expand All @@ -42,9 +46,14 @@ const Login = ({ customFields }) => {
buttonLabel={phrases.t("identity-block.log-in")}
className={BLOCK_CLASS_NAME}
formErrorText={error}
headline={phrases.t("identity-block.log-in")}
onSubmit={({ email, password }) =>
Identity.login(email, password, {rememberMe: true})
headline={phrases.t("identity-block.log-in-headline")}
onSubmit={({ email, password }) => {
setError(null);
setCaptchaError(null);
return Identity.login(email, password, {
rememberMe: true,
recaptchaToken: captchaToken
})
.then(() => {
if (isOIDC) {
loginByOIDC();
Expand All @@ -53,12 +62,23 @@ const Login = ({ customFields }) => {
window.location = validatedURL;
}
})
.catch(() => setError(phrases.t("identity-block.login-form-error")))
.catch((e) => {
if (e?.code === "130001") {
setCaptchaError(true);
}
else {
setError(phrases.t("identity-block.login-form-error"));
}
if (grecaptcha) {
grecaptcha.reset();
}
})
}
}
>
<Input
autoComplete="email"
label={phrases.t("identity-block.email")}
label={phrases.t("identity-block.email-label")}
name="email"
required
showDefaultError={false}
Expand All @@ -73,6 +93,8 @@ const Login = ({ customFields }) => {
showDefaultError={false}
type="password"
/>
<BotChallengeProtection className={BLOCK_CLASS_NAME} challengeIn="signin" setCaptchaToken={setCaptchaToken} captchaError={captchaError} setCaptchaError={setCaptchaError} />
<Paragraph className={`${BLOCK_CLASS_NAME}__privacy-statement`}>{phrases.t("identity-block.privacy-statement")}</Paragraph>
</HeadlinedSubmitForm>
);
};
Expand Down
32 changes: 23 additions & 9 deletions blocks/identity-block/features/login/default.test.jsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import React from "react";
import { fireEvent, render, screen, waitFor } from "@testing-library/react";
import Login from "./default";
import { useIdentity } from "@wpmedia/arc-themes-components";
import Login from "./default";

const defaultCustomFields = {
redirectURL: "",
redirectToPreviousPage: true,
signUpURL: ""
};

const mockLogin = jest.fn(() => Promise.resolve());
Expand All @@ -15,6 +16,10 @@ const mockIdentity = {
login: mockLogin,
};

const mockSales = {
getConfig: jest.fn(() => {})
}

jest.mock("@wpmedia/arc-themes-components", () => ({
...jest.requireActual("@wpmedia/arc-themes-components"),
useIdentity: jest.fn(() => ({
Expand All @@ -23,27 +28,33 @@ jest.mock("@wpmedia/arc-themes-components", () => ({
...mockIdentity,
},
})),
useSales: jest.fn(() => ({
isInitialized: true,
Sales: {
...mockSales,
},
})),
}));
jest.mock("fusion:properties", () => jest.fn(() => ({})));

describe("Identity Login Feature", () => {
it("renders", () => {
render(<Login customFields={defaultCustomFields} />);
expect(screen.queryByRole("form")).not.toBeNull();
expect(screen.getByRole("form")).not.toBeNull();
});

it("shows login form", () => {
render(<Login customFields={defaultCustomFields} />);
expect(screen.queryByRole("form")).not.toBeNull();
expect(screen.getByRole("form")).not.toBeNull();
expect(screen.getByLabelText("identity-block.password")).not.toBeNull();
expect(screen.getByLabelText("identity-block.email")).not.toBeNull();
expect(screen.getByLabelText("identity-block.email-label")).not.toBeNull();
});

it("submits the login form", async () => {
render(<Login customFields={defaultCustomFields} />);

await waitFor(() => expect(screen.getByLabelText("identity-block.email")));
fireEvent.change(screen.getByLabelText("identity-block.email"), {
await waitFor(() => expect(screen.getByLabelText("identity-block.email-label")));
fireEvent.change(screen.getByLabelText("identity-block.email-label"), {
target: { value: "[email protected]" },
});
await waitFor(() => expect(screen.getByLabelText("identity-block.password")));
Expand All @@ -60,6 +71,9 @@ describe("Identity Login Feature", () => {
describe("Identity Login Feature - rejected Login", () => {
beforeEach(() => {
mockLogin.mockImplementation(() => Promise.reject());
global.grecaptcha = {
reset: jest.fn()
}
});

afterEach(() => {
Expand All @@ -69,8 +83,8 @@ describe("Identity Login Feature - rejected Login", () => {
it("rejects the login", async () => {
render(<Login customFields={defaultCustomFields} />);

await waitFor(() => expect(screen.getByLabelText("identity-block.email")));
fireEvent.change(screen.getByLabelText("identity-block.email"), {
await waitFor(() => expect(screen.getByLabelText("identity-block.email-label")));
fireEvent.change(screen.getByLabelText("identity-block.email-label"), {
target: { value: "[email protected]" },
});

Expand All @@ -83,7 +97,7 @@ describe("Identity Login Feature - rejected Login", () => {
fireEvent.click(screen.getByRole("button"));

await waitFor(() => expect(mockLogin).toHaveBeenCalled());
await waitFor(() => screen.getByText("identity-block.login-form-error"));
await screen.findByText("identity-block.login-form-error");
});
});

Expand Down
17 changes: 16 additions & 1 deletion blocks/identity-block/intl.json
Original file line number Diff line number Diff line change
Expand Up @@ -629,7 +629,7 @@
"bn": "কোনো অ্যাকাউন্ট নেই?\nসাইন আপ করুন",
"bo": "ཁྱེད་ལ་དྲ་གྲངས་མེད་དམ། ཐོ་འགོད།",
"de": "Sign up for an account.",
"en": "Don't have an account? Sign up",
"en": "Need to create an account? ",
"es": "Sign up for an account.",
"fr": "Sign up for an account.",
"id": "Belum memiliki akun? Daftar",
Expand Down Expand Up @@ -1273,5 +1273,20 @@
"vi": "Tên người dùng",
"zh-CN": "用户名",
"zh-TW": "使用者名稱"
},
"identity-block.bot-protection-error": {
"en": "Please verify that you are not a robot."
},
"identity-block.email-label": {
"en": "Email"
},
"identity-block.log-in-headline": {
"en": "Log in to your account"
},
"identity-block.privacy-statement": {
"en": "By creating an account, you agree to the Terms of Service and acknowledge our Privacy Policy."
},
"identity-block.sign-up-natural": {
"en": "Sign up"
}
}
Loading

0 comments on commit bb46040

Please sign in to comment.