Add tests & default messages

Signed-off-by: HichamELBSI <elabbassih@gmail.com>
This commit is contained in:
HichamELBSI 2021-08-24 13:30:42 +02:00
parent ec85a4adf8
commit 9789deed73
11 changed files with 1644 additions and 75 deletions

View File

@ -33,28 +33,47 @@ const ForgotPassword = ({ onSubmit, schema }) => {
<Logo />
<Box paddingTop="6" paddingBottom="7">
<H1 id="password-forgotten">
{formatMessage({ id: 'Auth.form.button.password-forgotten' })}
{formatMessage({
id: 'Auth.form.button.password-forgotten',
defaultMessage: 'Password forgotten',
})}
</H1>
</Box>
{errors.errorMessage && (
<Text id="global-form-error" role="alert" tabIndex={-1} textColor="danger600">
{formatMessage({ id: errors.errorMessage })}
{formatMessage({
id: errors.errorMessage,
defaultMessage: 'An error occurred',
})}
</Text>
)}
</Column>
<Stack size={6}>
<TextInput
error={errors.email ? formatMessage({ id: errors.email }) : ''}
error={
errors.email
? formatMessage({
id: errors.email,
defaultMessage: 'This email is invalid.',
})
: ''
}
value={values.email}
onChange={handleChange}
label={formatMessage({ id: 'Auth.form.email.label' })}
placeholder={formatMessage({ id: 'Auth.form.email.placeholder' })}
label={formatMessage({ id: 'Auth.form.email.label', defaultMessage: 'Email' })}
placeholder={formatMessage({
id: 'Auth.form.email.placeholder',
defaultMessage: 'kai@doe.com',
})}
name="email"
required
/>
<AuthButton type="submit">
{formatMessage({ id: 'Auth.form.button.forgot-password' })}
{formatMessage({
id: 'Auth.form.button.forgot-password',
defaultMessage: 'Send Email',
})}
</AuthButton>
</Stack>
</Form>
@ -64,7 +83,9 @@ const ForgotPassword = ({ onSubmit, schema }) => {
<Row justifyContent="center">
<Box paddingTop={4}>
<Link to="/auth/login">
<Text small>{formatMessage({ id: 'Auth.link.ready' })}</Text>
<Text small>
{formatMessage({ id: 'Auth.link.ready', defaultMessage: 'Ready to sign in?' })}
</Text>
</Link>
</Box>
</Row>

View File

@ -18,18 +18,24 @@ const ForgotPasswordSuccess = () => {
<Logo />
<Box paddingTop="6" paddingBottom="7">
<H1 id="email-sent">
{formatMessage({ id: 'app.containers.AuthPage.ForgotPasswordSuccess.title' })}
{formatMessage({
id: 'app.containers.AuthPage.ForgotPasswordSuccess.title',
defaultMessage: 'Email sent',
})}
</H1>
</Box>
<Text>
{formatMessage({
id: 'app.containers.AuthPage.ForgotPasswordSuccess.text.email',
defaultMessage: 'It can take a few minutes to receive your password recovery link.',
})}
</Text>
<Box paddingTop={4}>
<Text>
{formatMessage({
id: 'app.containers.AuthPage.ForgotPasswordSuccess.text.contact-admin',
defaultMessage:
'If you do not receive this link, please contact your administrator.',
})}
</Text>
</Box>
@ -38,7 +44,7 @@ const ForgotPasswordSuccess = () => {
<Row justifyContent="center">
<Box paddingTop={4}>
<Link to="/auth/login">
<Text>{formatMessage({ id: 'Auth.link.signin' })}</Text>
<Text>{formatMessage({ id: 'Auth.link.signin', defaultMessage: 'Sign in' })}</Text>
</Link>
</Box>
</Row>

View File

@ -45,11 +45,19 @@ const Login = ({ onSubmit, schema, children }) => {
<Column>
<Logo />
<Box paddingTop="6" paddingBottom="1">
<H1 id="welcome">{formatMessage({ id: 'Auth.form.welcome.title' })}</H1>
<H1 id="welcome">
{formatMessage({
id: 'Auth.form.welcome.title',
defaultMessage: 'Welcome back!',
})}
</H1>
</Box>
<Box paddingBottom="7">
<Subtitle textColor="neutral600">
{formatMessage({ id: 'Auth.form.welcome.subtitle' })}
{formatMessage({
id: 'Auth.form.welcome.subtitle',
defaultMessage: 'Log in to your Strapi account',
})}
</Subtitle>
</Box>
{errors.errorMessage && (
@ -61,19 +69,39 @@ const Login = ({ onSubmit, schema, children }) => {
<Stack size={6}>
<TextInput
error={errors.email ? formatMessage({ id: errors.email }) : ''}
error={
errors.email
? formatMessage({
id: errors.email,
defaultMessage: 'This value is required.',
})
: ''
}
value={values.email}
onChange={handleChange}
label={formatMessage({ id: 'Auth.form.email.label' })}
placeholder={formatMessage({ id: 'Auth.form.email.placeholder' })}
label={formatMessage({ id: 'Auth.form.email.label', defaultMessage: 'Email' })}
placeholder={formatMessage({
id: 'Auth.form.email.placeholder',
defaultMessage: 'kai@doe.com',
})}
name="email"
required
/>
<TextInput
error={errors.password ? formatMessage({ id: errors.password }) : ''}
error={
errors.password
? formatMessage({
id: errors.password,
defaultMessage: 'This value is required.',
})
: ''
}
onChange={handleChange}
value={values.password}
label={formatMessage({ id: 'Auth.form.password.label' })}
label={formatMessage({
id: 'Auth.form.password.label',
defaultMessage: 'Password',
})}
name="password"
type={passwordShown ? 'text' : 'password'}
endAction={
@ -82,11 +110,17 @@ const Login = ({ onSubmit, schema, children }) => {
e.stopPropagation();
setPasswordShown(prev => !prev);
}}
label={formatMessage({
id: passwordShown
? 'Auth.form.password.show-password'
: 'Auth.form.password.hide-password',
})}
label={formatMessage(
passwordShown
? {
id: 'Auth.form.password.show-password',
defaultMessage: 'Show password',
}
: {
id: 'Auth.form.password.hide-password',
defaultMessage: 'Hide password',
}
)}
>
{passwordShown ? <Show /> : <Hide />}
</FieldActionWrapper>
@ -100,10 +134,13 @@ const Login = ({ onSubmit, schema, children }) => {
value={values.rememberMe}
name="rememberMe"
>
{formatMessage({ id: 'Auth.form.rememberMe.label' })}
{formatMessage({
id: 'Auth.form.rememberMe.label',
defaultMessage: 'Remember me',
})}
</Checkbox>
<AuthButton type="submit">
{formatMessage({ id: 'Auth.form.button.login' })}
{formatMessage({ id: 'Auth.form.button.login', defaultMessage: 'Login' })}
</AuthButton>
</Stack>
</Form>
@ -114,7 +151,12 @@ const Login = ({ onSubmit, schema, children }) => {
<Row justifyContent="center">
<Box paddingTop={4}>
<Link to="/auth/forgot-password">
<Text small>{formatMessage({ id: 'Auth.link.forgot-password' })}</Text>
<Text small>
{formatMessage({
id: 'Auth.link.forgot-password',
defaultMessage: 'Forgot your password?',
})}
</Text>
</Link>
</Box>
</Row>

View File

@ -0,0 +1,165 @@
import React from 'react';
import { render } from '@testing-library/react';
import { ThemeProvider } from '@strapi/parts/ThemeProvider';
import { lightTheme } from '@strapi/parts/themes';
import { Router } from 'react-router-dom';
import * as yup from 'yup';
import { createMemoryHistory } from 'history';
import BaseLogin from '../BaseLogin';
jest.mock('react-intl', () => {
const reactIntl = jest.requireActual('react-intl');
const intl = reactIntl.createIntl({
locale: 'en',
});
return {
...reactIntl,
useIntl: () => intl,
};
});
jest.mock('@strapi/helper-plugin', () => ({
useQuery: () => ({
get: () => '',
}),
Form: () => <form />,
}));
describe('ADMIN | PAGES | AUTH | BaseLogin', () => {
it('should render and match the snapshot', () => {
const history = createMemoryHistory();
const { container } = render(
<ThemeProvider theme={lightTheme}>
<Router history={history}>
<BaseLogin onSubmit={() => {}} schema={yup.object()} />
</Router>
</ThemeProvider>
);
expect(container.firstChild).toMatchInlineSnapshot(`
.c6 {
font-weight: 400;
font-size: 0.875rem;
line-height: 1.43;
color: #4945ff;
}
.c9 {
font-weight: 400;
font-size: 0.75rem;
line-height: 1.33;
color: #32324d;
}
.c7 {
font-weight: 600;
line-height: 1.14;
}
.c8 {
font-weight: 600;
font-size: 0.6875rem;
line-height: 1.45;
text-transform: uppercase;
}
.c1 {
background: #ffffff;
padding-top: 48px;
padding-right: 56px;
padding-bottom: 48px;
padding-left: 56px;
border-radius: 4px;
box-shadow: 0px 1px 4px rgba(33,33,52,0.1);
}
.c4 {
padding-top: 16px;
}
.c3 {
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
-webkit-flex-direction: row;
-ms-flex-direction: row;
flex-direction: row;
-webkit-box-pack: center;
-webkit-justify-content: center;
-ms-flex-pack: center;
justify-content: center;
-webkit-align-items: center;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
}
.c5 {
display: -webkit-inline-box;
display: -webkit-inline-flex;
display: -ms-inline-flexbox;
display: inline-flex;
-webkit-align-items: center;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
text-transform: uppercase;
-webkit-text-decoration: none;
text-decoration: none;
}
.c5 svg path {
fill: #4945ff;
}
.c5 svg {
font-size: 0.625rem;
}
.c0 {
outline: none;
}
.c2 {
margin: 0 auto;
width: 552px;
}
<main
aria-labelledby="welcome"
class="c0"
id="main-content"
tabindex="-1"
>
<div
class="c1 c2"
>
<form />
</div>
<div
class="c3"
>
<div
class="c4"
>
<a
class="c5"
href="/auth/forgot-password"
>
<span
class="c6 c7 c8"
>
<span
class="c9"
>
Forgot your password?
</span>
</span>
</a>
</div>
</div>
</main>
`);
});
});

View File

@ -3,7 +3,7 @@ import styled from 'styled-components';
import { useConfigurations } from '../../../../hooks';
const Img = styled.img`
height: 72px;
height: ${72 / 16}rem;
`;
const Logo = () => {

View File

@ -12,7 +12,12 @@ const Oops = () => {
const { formatMessage } = useIntl();
const query = useQuery();
const message = query.get('info') || formatMessage({ id: 'Auth.components.Oops.text' });
const message =
query.get('info') ||
formatMessage({
id: 'Auth.components.Oops.text',
defaultMessage: 'Your account has been suspended.',
});
return (
<UnauthenticatedLayout>
@ -21,13 +26,16 @@ const Oops = () => {
<Column>
<Logo />
<Box paddingTop="6" paddingBottom="7">
<H1 id="email-sent">{formatMessage({ id: 'Auth.components.Oops.title' })}</H1>
<H1 id="email-sent">
{formatMessage({ id: 'Auth.components.Oops.title', defaultMessage: 'Oops...' })}
</H1>
</Box>
<Text>{message}</Text>
<Box paddingTop={4}>
<Text>
{formatMessage({
id: 'Auth.components.Oops.text.admin',
defaultMessage: 'If this is a mistake, please contact your administrator.',
})}
</Text>
</Box>
@ -36,7 +44,7 @@ const Oops = () => {
<Row justifyContent="center">
<Box paddingTop={4}>
<Link to="/auth/login">
<Text>{formatMessage({ id: 'Auth.link.signin' })}</Text>
<Text>{formatMessage({ id: 'Auth.link.signin', defaultMessage: 'Sign in' })}</Text>
</Link>
</Box>
</Row>

View File

@ -0,0 +1,336 @@
import React from 'react';
import { render } from '@testing-library/react';
import { ThemeProvider } from '@strapi/parts/ThemeProvider';
import { lightTheme } from '@strapi/parts/themes';
import { Router } from 'react-router-dom';
import { createMemoryHistory } from 'history';
import Oops from '..';
jest.mock('react-intl', () => {
const reactIntl = jest.requireActual('react-intl');
const intl = reactIntl.createIntl({
locale: 'en',
});
return {
...reactIntl,
useIntl: () => intl,
};
});
jest.mock('../../../../../components/LocalesProvider/useLocalesProvider', () => () => ({
changeLocale: () => {},
localeNames: ['en'],
messages: ['test'],
}));
jest.mock('@strapi/helper-plugin', () => ({
useQuery: () => ({
get: () => '',
}),
}));
describe('ADMIN | PAGES | AUTH | Oops', () => {
it('should render and match the snapshot', () => {
const history = createMemoryHistory();
const { container } = render(
<ThemeProvider theme={lightTheme}>
<Router history={history}>
<Oops />
</Router>
</ThemeProvider>
);
expect(container.firstChild).toMatchInlineSnapshot(`
.c14 {
font-weight: 600;
font-size: 2rem;
line-height: 1.25;
color: #32324d;
}
.c4 {
font-weight: 400;
font-size: 0.875rem;
line-height: 1.43;
color: #32324d;
}
.c18 {
font-weight: 400;
font-size: 0.875rem;
line-height: 1.43;
color: #4945ff;
}
.c5 {
font-weight: 600;
line-height: 1.14;
}
.c19 {
font-weight: 600;
font-size: 0.6875rem;
line-height: 1.45;
text-transform: uppercase;
}
.c1 {
padding-top: 24px;
padding-right: 40px;
}
.c3 {
padding-right: 4px;
}
.c6 {
padding-top: 64px;
padding-bottom: 64px;
}
.c8 {
background: #ffffff;
padding-top: 48px;
padding-right: 56px;
padding-bottom: 48px;
padding-left: 56px;
border-radius: 4px;
box-shadow: 0px 1px 4px rgba(33,33,52,0.1);
}
.c13 {
padding-top: 24px;
padding-bottom: 32px;
}
.c15 {
padding-top: 16px;
}
.c0 {
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
-webkit-flex-direction: row;
-ms-flex-direction: row;
flex-direction: row;
-webkit-box-pack: end;
-webkit-justify-content: flex-end;
-ms-flex-pack: end;
justify-content: flex-end;
-webkit-align-items: center;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
}
.c10 {
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
-webkit-flex-direction: row;
-ms-flex-direction: row;
flex-direction: row;
-webkit-align-items: center;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
}
.c16 {
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
-webkit-flex-direction: row;
-ms-flex-direction: row;
flex-direction: row;
-webkit-box-pack: center;
-webkit-justify-content: center;
-ms-flex-pack: center;
justify-content: center;
-webkit-align-items: center;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
}
.c2 {
border: none;
background: transparent;
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
-webkit-align-items: center;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
font-size: 0.75rem;
}
.c2 svg {
height: 0.25rem;
}
.c2 svg path {
fill: #8e8ea9;
}
.c17 {
display: -webkit-inline-box;
display: -webkit-inline-flex;
display: -ms-inline-flexbox;
display: inline-flex;
-webkit-align-items: center;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
text-transform: uppercase;
-webkit-text-decoration: none;
text-decoration: none;
}
.c17 svg path {
fill: #4945ff;
}
.c17 svg {
font-size: 0.625rem;
}
.c7 {
outline: none;
}
.c9 {
margin: 0 auto;
width: 552px;
}
.c11 {
-webkit-flex-direction: column;
-ms-flex-direction: column;
flex-direction: column;
}
.c12 {
height: 4.5rem;
}
<div>
<header
class="c0"
>
<div
class="c1"
>
<div>
<button
aria-controls="simplemenu-1"
aria-expanded="false"
aria-haspopup="true"
class="c2"
type="button"
>
<div
class="c3"
>
<span
class="c4 c5"
/>
</div>
<svg
aria-hidden="true"
fill="none"
height="1em"
viewBox="0 0 14 8"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
clip-rule="evenodd"
d="M14 .889a.86.86 0 01-.26.625L7.615 7.736A.834.834 0 017 8a.834.834 0 01-.615-.264L.26 1.514A.861.861 0 010 .889c0-.24.087-.45.26-.625A.834.834 0 01.875 0h12.25c.237 0 .442.088.615.264a.86.86 0 01.26.625z"
fill="#32324D"
fill-rule="evenodd"
/>
</svg>
</button>
</div>
</div>
</header>
<div
class="c6"
>
<main
aria-labelledby="email-sent"
class="c7"
id="main-content"
tabindex="-1"
>
<div
class="c8 c9"
>
<div
class="c10 c11"
>
<img
alt=""
aria-hidden="true"
class="c12"
/>
<div
class="c13"
>
<h1
class="c14"
id="email-sent"
>
Oops...
</h1>
</div>
<span
class="c4"
>
Your account has been suspended.
</span>
<div
class="c15"
>
<span
class="c4"
>
If this is a mistake, please contact your administrator.
</span>
</div>
</div>
</div>
<div
class="c16"
>
<div
class="c15"
>
<a
class="c17"
href="/auth/login"
>
<span
class="c18 c5 c19"
>
<span
class="c4"
>
Sign in
</span>
</span>
</a>
</div>
</div>
</main>
</div>
</div>
`);
});
});

View File

@ -110,11 +110,20 @@ const Register = ({ fieldsToDisable, noSignin, onSubmit, schema }) => {
<Column>
<Logo />
<Box paddingTop="6" paddingBottom="1">
<H1 id="welcome">{formatMessage({ id: 'Auth.form.welcome.title' })}</H1>
<H1 id="welcome">
{formatMessage({
id: 'Auth.form.welcome.title',
defaultMessage: 'Welcome back!',
})}
</H1>
</Box>
<CenteredBox paddingBottom="7">
<Subtitle textColor="neutral600">
{formatMessage({ id: 'Auth.form.register.subtitle' })}
{formatMessage({
id: 'Auth.form.register.subtitle',
defaultMessage:
'Your credentials are only used to authenticate yourself on the admin panel. All saved data will be stored in your own database.',
})}
</Subtitle>
</CenteredBox>
</Column>
@ -126,20 +135,38 @@ const Register = ({ fieldsToDisable, noSignin, onSubmit, schema }) => {
required
value={values.firstname}
error={
errors.firstname ? formatMessage({ id: errors.firstname }) : undefined
errors.firstname
? formatMessage({
id: errors.firstname,
defaultMessage: 'This value is required.',
})
: undefined
}
onChange={handleChange}
label={formatMessage({ id: 'Auth.form.firstname.label' })}
label={formatMessage({
id: 'Auth.form.firstname.label',
defaultMessage: 'Firstname',
})}
/>
</GridItem>
<GridItem col={6}>
<TextInput
name="lastname"
error={errors.lastname ? formatMessage({ id: errors.lastname }) : undefined}
error={
errors.lastname
? formatMessage({
id: errors.lastname,
defaultMessage: 'This value is required.',
})
: undefined
}
required
value={values.lastname}
onChange={handleChange}
label={formatMessage({ id: 'Auth.form.lastname.label' })}
label={formatMessage({
id: 'Auth.form.lastname.label',
defaultMessage: 'Lastname',
})}
/>
</GridItem>
</Grid>
@ -148,16 +175,30 @@ const Register = ({ fieldsToDisable, noSignin, onSubmit, schema }) => {
disabled={fieldsToDisable.includes('email')}
value={values.email}
onChange={handleChange}
error={errors.email ? formatMessage({ id: errors.email }) : undefined}
error={
errors.email
? formatMessage({
id: errors.email,
defaultMessage: 'This value is required.',
})
: undefined
}
required
label={formatMessage({ id: 'Auth.form.email.label' })}
label={formatMessage({ id: 'Auth.form.email.label', defaultMessage: 'Email' })}
type="email"
/>
<TextInput
name="password"
onChange={handleChange}
value={values.password}
error={errors.password ? formatMessage({ id: errors.password }) : undefined}
error={
errors.password
? formatMessage({
id: errors.password,
defaultMessage: 'This value is required',
})
: undefined
}
endAction={
// eslint-disable-next-line react/jsx-wrap-multilines
<FieldActionWrapper
@ -165,18 +206,31 @@ const Register = ({ fieldsToDisable, noSignin, onSubmit, schema }) => {
e.preventDefault();
setPasswordShown(prev => !prev);
}}
label={formatMessage({
id: passwordShown
? 'Auth.form.password.show-password'
: 'Auth.form.password.hide-password',
})}
label={formatMessage(
passwordShown
? {
id: 'Auth.form.password.show-password',
defaultMessage: 'Show password',
}
: {
id: 'Auth.form.password.hide-password',
defaultMessage: 'Hide password',
}
)}
>
{passwordShown ? <Show /> : <Hide />}
</FieldActionWrapper>
}
hint={formatMessage({ id: 'Auth.form.password.hint' })}
hint={formatMessage({
id: 'Auth.form.password.hint',
defaultMessage:
'Password must contain at least 8 characters, 1 uppercase, 1 lowercase and 1 number',
})}
required
label={formatMessage({ id: 'Auth.form.password.label' })}
label={formatMessage({
id: 'Auth.form.password.label',
defaultMessage: 'Password',
})}
type={passwordShown ? 'text' : 'password'}
/>
<TextInput
@ -185,7 +239,10 @@ const Register = ({ fieldsToDisable, noSignin, onSubmit, schema }) => {
value={values.confirmPassword}
error={
errors.confirmPassword
? formatMessage({ id: errors.confirmPassword })
? formatMessage({
id: errors.confirmPassword,
defaultMessage: 'This value is required.',
})
: undefined
}
endAction={
@ -195,17 +252,26 @@ const Register = ({ fieldsToDisable, noSignin, onSubmit, schema }) => {
e.preventDefault();
setConfirmPasswordShown(prev => !prev);
}}
label={formatMessage({
id: confirmPasswordShown
? 'Auth.form.password.show-password'
: 'Auth.form.password.hide-password',
})}
label={formatMessage(
confirmPasswordShown
? {
id: 'Auth.form.password.show-password',
defaultMessage: 'Show password',
}
: {
id: 'Auth.form.password.hide-password',
defaultMessage: 'Hide password',
}
)}
>
{confirmPasswordShown ? <Show /> : <Hide />}
</FieldActionWrapper>
}
required
label={formatMessage({ id: 'Auth.form.confirmPassword.label' })}
label={formatMessage({
id: 'Auth.form.confirmPassword.label',
defaultMessage: 'Confirmation Password',
})}
type={confirmPasswordShown ? 'text' : 'password'}
/>
<Checkbox
@ -216,23 +282,36 @@ const Register = ({ fieldsToDisable, noSignin, onSubmit, schema }) => {
name="news"
>
{formatMessage(
{ id: 'Auth.form.register.news.label' },
{
id: 'Auth.form.register.news.label',
defaultMessage:
'Keep me updated about the new features and upcoming improvements (by doing this you accept the {terms} and the {policy}).',
},
{
terms: (
<A target="_blank" href="https://strapi.io/terms" rel="noreferrer">
{formatMessage({ id: 'Auth.privacy-policy-agreement.terms' })}
{formatMessage({
id: 'Auth.privacy-policy-agreement.terms',
defaultMessage: 'terms',
})}
</A>
),
policy: (
<A target="_blank" href="https://strapi.io/privacy" rel="noreferrer">
{formatMessage({ id: 'Auth.privacy-policy-agreement.policy' })}
{formatMessage({
id: 'Auth.privacy-policy-agreement.policy',
defaultMessage: 'policy',
})}
</A>
),
}
)}
</Checkbox>
<AuthButton size="L" type="submit">
{formatMessage({ id: 'Auth.form.button.register' })}
{formatMessage({
id: 'Auth.form.button.register',
defaultMessage: "Let's start",
})}
</AuthButton>
</Stack>
</Main>
@ -243,7 +322,10 @@ const Register = ({ fieldsToDisable, noSignin, onSubmit, schema }) => {
<Box paddingTop={4}>
<Row justifyContent="center">
<Link label="Auth.link.signin" to="/auth/login">
{formatMessage({ id: 'Auth.link.signin.account' })}
{formatMessage({
id: 'Auth.link.signin.account',
defaultMessage: 'Already have an account?',
})}
</Link>
</Row>
</Box>

View File

@ -0,0 +1,180 @@
import React from 'react';
import { render } from '@testing-library/react';
import { ThemeProvider } from '@strapi/parts/ThemeProvider';
import { lightTheme } from '@strapi/parts/themes';
import { Router } from 'react-router-dom';
import { createMemoryHistory } from 'history';
import * as yup from 'yup';
import Register from '..';
jest.mock('react-intl', () => {
const reactIntl = jest.requireActual('react-intl');
const intl = reactIntl.createIntl({
locale: 'en',
});
return {
...reactIntl,
useIntl: () => intl,
};
});
jest.mock('../../../../../components/LocalesProvider/useLocalesProvider', () => () => ({
changeLocale: () => {},
localeNames: ['en'],
messages: ['test'],
}));
jest.mock('@strapi/helper-plugin', () => ({
useNotification: () => jest.fn({}),
useQuery: () => ({
get: () => '',
}),
Form: () => <form />,
}));
describe('ADMIN | PAGES | AUTH | Register', () => {
it('should render and match the snapshot', () => {
const history = createMemoryHistory();
const { container } = render(
<ThemeProvider theme={lightTheme}>
<Router history={history}>
<Register fieldsToDisable={[]} noSignin onSubmit={() => {}} schema={yup.object()} />
</Router>
</ThemeProvider>
);
expect(container.firstChild).toMatchInlineSnapshot(`
.c4 {
font-weight: 400;
font-size: 0.875rem;
line-height: 1.43;
color: #32324d;
}
.c5 {
font-weight: 600;
line-height: 1.14;
}
.c1 {
padding-top: 24px;
padding-right: 40px;
}
.c3 {
padding-right: 4px;
}
.c6 {
padding-top: 64px;
padding-bottom: 64px;
}
.c7 {
background: #ffffff;
padding-top: 48px;
padding-right: 56px;
padding-bottom: 48px;
padding-left: 56px;
border-radius: 4px;
box-shadow: 0px 1px 4px rgba(33,33,52,0.1);
}
.c0 {
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
-webkit-flex-direction: row;
-ms-flex-direction: row;
flex-direction: row;
-webkit-box-pack: end;
-webkit-justify-content: flex-end;
-ms-flex-pack: end;
justify-content: flex-end;
-webkit-align-items: center;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
}
.c2 {
border: none;
background: transparent;
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
-webkit-align-items: center;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
font-size: 0.75rem;
}
.c2 svg {
height: 0.25rem;
}
.c2 svg path {
fill: #8e8ea9;
}
.c8 {
margin: 0 auto;
width: 552px;
}
<div>
<header
class="c0"
>
<div
class="c1"
>
<div>
<button
aria-controls="simplemenu-1"
aria-expanded="false"
aria-haspopup="true"
class="c2"
type="button"
>
<div
class="c3"
>
<span
class="c4 c5"
/>
</div>
<svg
aria-hidden="true"
fill="none"
height="1em"
viewBox="0 0 14 8"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
clip-rule="evenodd"
d="M14 .889a.86.86 0 01-.26.625L7.615 7.736A.834.834 0 017 8a.834.834 0 01-.615-.264L.26 1.514A.861.861 0 010 .889c0-.24.087-.45.26-.625A.834.834 0 01.875 0h12.25c.237 0 .442.088.615.264a.86.86 0 01.26.625z"
fill="#32324D"
fill-rule="evenodd"
/>
</svg>
</button>
</div>
</div>
</header>
<div
class="c6"
>
<div
class="c7 c8"
>
<form />
</div>
</div>
</div>
`);
});
});

View File

@ -38,12 +38,18 @@ const ForgotPassword = ({ onSubmit, schema }) => {
<Logo />
<Box paddingTop="6" paddingBottom="7">
<H1 id="password-forgotten">
{formatMessage({ id: 'Auth.reset-password.title' })}
{formatMessage({
id: 'Auth.reset-password.title',
defaultMessage: 'Reset password',
})}
</H1>
</Box>
{errors.errorMessage && (
<Text id="global-form-error" role="alert" tabIndex={-1} textColor="danger600">
{formatMessage({ id: errors.errorMessage })}
{formatMessage({
id: errors.errorMessage,
defaultMessage: 'An error occurred',
})}
</Text>
)}
</Column>
@ -53,25 +59,45 @@ const ForgotPassword = ({ onSubmit, schema }) => {
name="password"
onChange={handleChange}
value={values.password}
error={errors.password ? formatMessage({ id: errors.password }) : undefined}
error={
errors.password
? formatMessage({
id: errors.password,
defaultMessage: 'This field is required.',
})
: undefined
}
endAction={
<FieldActionWrapper
onClick={e => {
e.preventDefault();
setPasswordShown(prev => !prev);
}}
label={formatMessage({
id: passwordShown
? 'Auth.form.password.show-password'
: 'Auth.form.password.hide-password',
})}
label={formatMessage(
passwordShown
? {
id: 'Auth.form.password.show-password',
defaultMessage: 'Show password',
}
: {
id: 'Auth.form.password.hide-password',
defaultMessage: 'Hide password',
}
)}
>
{passwordShown ? <Show /> : <Hide />}
</FieldActionWrapper>
}
hint={formatMessage({ id: 'Auth.form.password.hint' })}
hint={formatMessage({
id: 'Auth.form.password.hint',
defaultMessage:
'Password must contain at least 8 characters, 1 uppercase, 1 lowercase and 1 number',
})}
required
label={formatMessage({ id: 'Auth.form.password.label' })}
label={formatMessage({
id: 'Auth.form.password.label',
defaultMessage: 'Password',
})}
type={passwordShown ? 'text' : 'password'}
/>
<TextInput
@ -80,7 +106,10 @@ const ForgotPassword = ({ onSubmit, schema }) => {
value={values.confirmPassword}
error={
errors.confirmPassword
? formatMessage({ id: errors.confirmPassword })
? formatMessage({
id: errors.confirmPassword,
defaultMessage: 'This value is required.',
})
: undefined
}
endAction={
@ -89,21 +118,33 @@ const ForgotPassword = ({ onSubmit, schema }) => {
e.preventDefault();
setConfirmPasswordShown(prev => !prev);
}}
label={formatMessage({
id: confirmPasswordShown
? 'Auth.form.password.show-password'
: 'Auth.form.password.hide-password',
})}
label={formatMessage(
passwordShown
? {
id: 'Auth.form.password.show-password',
defaultMessage: 'Show password',
}
: {
id: 'Auth.form.password.hide-password',
defaultMessage: 'Hide password',
}
)}
>
{confirmPasswordShown ? <Show /> : <Hide />}
</FieldActionWrapper>
}
required
label={formatMessage({ id: 'Auth.form.confirmPassword.label' })}
label={formatMessage({
id: 'Auth.form.confirmPassword.label',
defaultMessage: 'Confirmation Password',
})}
type={confirmPasswordShown ? 'text' : 'password'}
/>
<AuthButton type="submit">
{formatMessage({ id: 'Auth.form.button.reset-password' })}
{formatMessage({
id: 'Auth.form.button.reset-password',
defaultMessage: 'Change password',
})}
</AuthButton>
</Stack>
</Form>
@ -113,7 +154,9 @@ const ForgotPassword = ({ onSubmit, schema }) => {
<Row justifyContent="center">
<Box paddingTop={4}>
<Link to="/auth/login">
<Text small>{formatMessage({ id: 'Auth.link.ready' })}</Text>
<Text small>
{formatMessage({ id: 'Auth.link.ready', defaultMessage: 'Ready to sign in?' })}
</Text>
</Link>
</Box>
</Row>

View File

@ -0,0 +1,686 @@
import React from 'react';
import { render } from '@testing-library/react';
import { ThemeProvider } from '@strapi/parts/ThemeProvider';
import { lightTheme } from '@strapi/parts/themes';
import { Router } from 'react-router-dom';
import { createMemoryHistory } from 'history';
import * as yup from 'yup';
import ResetPassword from '..';
jest.mock('react-intl', () => {
const reactIntl = jest.requireActual('react-intl');
const intl = reactIntl.createIntl({
locale: 'en',
});
return {
...reactIntl,
useIntl: () => intl,
};
});
jest.mock('../../../../../components/LocalesProvider/useLocalesProvider', () => () => ({
changeLocale: () => {},
localeNames: ['en'],
messages: ['test'],
}));
describe('ADMIN | PAGES | AUTH | ResetPassword', () => {
it('should render and match the snapshot', () => {
const history = createMemoryHistory();
const { container } = render(
<ThemeProvider theme={lightTheme}>
<Router history={history}>
<ResetPassword onSubmit={() => {}} schema={yup.object()} />
</Router>
</ThemeProvider>
);
expect(container.firstChild).toMatchInlineSnapshot(`
.c14 {
font-weight: 600;
font-size: 2rem;
line-height: 1.25;
color: #32324d;
}
.c4 {
font-weight: 400;
font-size: 0.875rem;
line-height: 1.43;
color: #32324d;
}
.c18 {
font-weight: 500;
font-size: 0.75rem;
line-height: 1.33;
color: #32324d;
}
.c25 {
font-weight: 400;
font-size: 0.75rem;
line-height: 1.33;
color: #666687;
}
.c34 {
font-weight: 400;
font-size: 0.875rem;
line-height: 1.43;
color: #4945ff;
}
.c36 {
font-weight: 400;
font-size: 0.75rem;
line-height: 1.33;
color: #32324d;
}
.c5 {
font-weight: 600;
line-height: 1.14;
}
.c35 {
font-weight: 600;
font-size: 0.6875rem;
line-height: 1.45;
text-transform: uppercase;
}
.c1 {
padding-top: 24px;
padding-right: 40px;
}
.c3 {
padding-right: 4px;
}
.c6 {
padding-top: 64px;
padding-bottom: 64px;
}
.c8 {
background: #ffffff;
padding-top: 48px;
padding-right: 56px;
padding-bottom: 48px;
padding-left: 56px;
border-radius: 4px;
box-shadow: 0px 1px 4px rgba(33,33,52,0.1);
}
.c13 {
padding-top: 24px;
padding-bottom: 32px;
}
.c22 {
padding-right: 12px;
padding-left: 8px;
}
.c32 {
padding-top: 16px;
}
.c0 {
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
-webkit-flex-direction: row;
-ms-flex-direction: row;
flex-direction: row;
-webkit-box-pack: end;
-webkit-justify-content: flex-end;
-ms-flex-pack: end;
justify-content: flex-end;
-webkit-align-items: center;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
}
.c10 {
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
-webkit-flex-direction: row;
-ms-flex-direction: row;
flex-direction: row;
-webkit-align-items: center;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
}
.c19 {
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
-webkit-flex-direction: row;
-ms-flex-direction: row;
flex-direction: row;
-webkit-box-pack: justify;
-webkit-justify-content: space-between;
-ms-flex-pack: justify;
justify-content: space-between;
-webkit-align-items: center;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
}
.c31 {
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
-webkit-flex-direction: row;
-ms-flex-direction: row;
flex-direction: row;
-webkit-box-pack: center;
-webkit-justify-content: center;
-ms-flex-pack: center;
justify-content: center;
-webkit-align-items: center;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
}
.c15 > * {
margin-top: 0;
margin-bottom: 0;
}
.c15 > * + * {
margin-top: 24px;
}
.c17 > * {
margin-top: 0;
margin-bottom: 0;
}
.c17 > * + * {
margin-top: 4px;
}
.c21 {
border: none;
padding-left: 16px;
padding-right: 0;
color: #32324d;
font-weight: 400;
font-size: 0.875rem;
display: block;
width: 100%;
height: 2.5rem;
}
.c21::-webkit-input-placeholder {
color: #8e8ea9;
opacity: 1;
}
.c21::-moz-placeholder {
color: #8e8ea9;
opacity: 1;
}
.c21:-ms-input-placeholder {
color: #8e8ea9;
opacity: 1;
}
.c21::placeholder {
color: #8e8ea9;
opacity: 1;
}
.c21:disabled {
background: inherit;
color: inherit;
}
.c21:focus {
outline: none;
}
.c20 {
border: 1px solid #dcdce4;
border-radius: 4px;
background: #ffffff;
overflow: hidden;
}
.c20:focus-within {
border: 1px solid #4945ff;
}
.c16 textarea {
height: 5rem;
}
.c2 {
border: none;
background: transparent;
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
-webkit-align-items: center;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
font-size: 0.75rem;
}
.c2 svg {
height: 0.25rem;
}
.c2 svg path {
fill: #8e8ea9;
}
.c33 {
display: -webkit-inline-box;
display: -webkit-inline-flex;
display: -ms-inline-flexbox;
display: inline-flex;
-webkit-align-items: center;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
text-transform: uppercase;
-webkit-text-decoration: none;
text-decoration: none;
}
.c33 svg path {
fill: #4945ff;
}
.c33 svg {
font-size: 0.625rem;
}
.c7 {
outline: none;
}
.c9 {
margin: 0 auto;
width: 552px;
}
.c11 {
-webkit-flex-direction: column;
-ms-flex-direction: column;
flex-direction: column;
}
.c30 {
font-weight: 500;
font-size: 0.75rem;
line-height: 1.33;
color: #32324d;
}
.c26 {
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
cursor: pointer;
padding: 8px;
border-radius: 4px;
background: #ffffff;
border: 1px solid #dcdce4;
}
.c26 svg {
height: 12px;
width: 12px;
}
.c26 svg > g,
.c26 svg path {
fill: #ffffff;
}
.c26[aria-disabled='true'] {
pointer-events: none;
}
.c27 {
padding: 8px 16px;
background: #4945ff;
border: none;
border: 1px solid #4945ff;
background: #4945ff;
}
.c27 .c29 {
color: #ffffff;
}
.c27[aria-disabled='true'] {
border: 1px solid #dcdce4;
background: #eaeaef;
}
.c27[aria-disabled='true'] .c29 {
color: #666687;
}
.c27[aria-disabled='true'] svg > g,
.c27[aria-disabled='true'] svg path {
fill: #666687;
}
.c27[aria-disabled='true']:active {
border: 1px solid #dcdce4;
background: #eaeaef;
}
.c27[aria-disabled='true']:active .c29 {
color: #666687;
}
.c27[aria-disabled='true']:active svg > g,
.c27[aria-disabled='true']:active svg path {
fill: #666687;
}
.c27:hover {
border: 1px solid #7b79ff;
background: #7b79ff;
}
.c27:active {
border: 1px solid #4945ff;
background: #4945ff;
}
.c28 {
display: inline-block;
width: 100%;
}
.c12 {
height: 4.5rem;
}
.c23 {
border: none;
background: transparent;
font-size: 1.6rem;
width: auto;
padding: 0;
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
-webkit-align-items: center;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
}
.c24 svg {
height: 1rem;
width: 1rem;
}
.c24 svg path {
fill: #666687;
}
<div>
<header
class="c0"
>
<div
class="c1"
>
<div>
<button
aria-controls="simplemenu-1"
aria-expanded="false"
aria-haspopup="true"
class="c2"
type="button"
>
<div
class="c3"
>
<span
class="c4 c5"
/>
</div>
<svg
aria-hidden="true"
fill="none"
height="1em"
viewBox="0 0 14 8"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
clip-rule="evenodd"
d="M14 .889a.86.86 0 01-.26.625L7.615 7.736A.834.834 0 017 8a.834.834 0 01-.615-.264L.26 1.514A.861.861 0 010 .889c0-.24.087-.45.26-.625A.834.834 0 01.875 0h12.25c.237 0 .442.088.615.264a.86.86 0 01.26.625z"
fill="#32324D"
fill-rule="evenodd"
/>
</svg>
</button>
</div>
</div>
</header>
<div
class="c6"
>
<main
aria-labelledby="password-forgotten"
class="c7"
id="main-content"
tabindex="-1"
>
<div
class="c8 c9"
>
<form
action="#"
novalidate=""
>
<div
class="c10 c11"
>
<img
alt=""
aria-hidden="true"
class="c12"
/>
<div
class="c13"
>
<h1
class="c14"
id="password-forgotten"
>
Reset password
</h1>
</div>
</div>
<div
class="c15"
>
<div
class="c16"
>
<div>
<div
class="c17"
>
<div
class="c10"
>
<label
class="c18"
for="textinput-2"
>
Password
</label>
</div>
<div
class="c19 c20"
>
<input
aria-describedby="textinput-2-hint"
aria-invalid="false"
class="c21"
id="textinput-2"
name="password"
required=""
type="password"
value=""
/>
<div
class="c22"
>
<button
aria-label="Hide password"
class="c23 c24"
type="button"
>
<svg
fill="none"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M4.048 6.875L2.103 4.93a1 1 0 111.414-1.415l16.966 16.966a1 1 0 11-1.414 1.415l-2.686-2.686a12.247 12.247 0 01-4.383.788c-3.573 0-6.559-1.425-8.962-3.783a15.842 15.842 0 01-2.116-2.568 11.096 11.096 0 01-.711-1.211 1.145 1.145 0 010-.875c.124-.258.36-.68.711-1.211.58-.876 1.283-1.75 2.116-2.569.326-.32.663-.622 1.01-.906zm10.539 10.539l-1.551-1.551a4.005 4.005 0 01-4.9-4.9L6.584 9.411a6 6 0 008.002 8.002zM7.617 4.787A12.248 12.248 0 0112 3.998c3.572 0 6.559 1.426 8.961 3.783a15.845 15.845 0 012.117 2.569c.351.532.587.954.711 1.211.116.242.115.636 0 .875-.124.257-.36.68-.711 1.211-.58.876-1.283 1.75-2.117 2.568-.325.32-.662.623-1.01.907l-2.536-2.537a6 6 0 00-8.002-8.002L7.617 4.787zm3.347 3.347A4.005 4.005 0 0116 11.998c0 .359-.047.706-.136 1.037l-4.9-4.901z"
fill="#212134"
/>
</svg>
</button>
</div>
</div>
<p
class="c25"
id="textinput-2-hint"
>
Password must contain at least 8 characters, 1 uppercase, 1 lowercase and 1 number
</p>
</div>
</div>
</div>
<div
class="c16"
>
<div>
<div
class="c17"
>
<div
class="c10"
>
<label
class="c18"
for="textinput-3"
>
Confirmation Password
</label>
</div>
<div
class="c19 c20"
>
<input
aria-invalid="false"
class="c21"
id="textinput-3"
name="confirmPassword"
required=""
type="password"
value=""
/>
<div
class="c22"
>
<button
aria-label="Hide password"
class="c23 c24"
type="button"
>
<svg
fill="none"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M4.048 6.875L2.103 4.93a1 1 0 111.414-1.415l16.966 16.966a1 1 0 11-1.414 1.415l-2.686-2.686a12.247 12.247 0 01-4.383.788c-3.573 0-6.559-1.425-8.962-3.783a15.842 15.842 0 01-2.116-2.568 11.096 11.096 0 01-.711-1.211 1.145 1.145 0 010-.875c.124-.258.36-.68.711-1.211.58-.876 1.283-1.75 2.116-2.569.326-.32.663-.622 1.01-.906zm10.539 10.539l-1.551-1.551a4.005 4.005 0 01-4.9-4.9L6.584 9.411a6 6 0 008.002 8.002zM7.617 4.787A12.248 12.248 0 0112 3.998c3.572 0 6.559 1.426 8.961 3.783a15.845 15.845 0 012.117 2.569c.351.532.587.954.711 1.211.116.242.115.636 0 .875-.124.257-.36.68-.711 1.211-.58.876-1.283 1.75-2.117 2.568-.325.32-.662.623-1.01.907l-2.536-2.537a6 6 0 00-8.002-8.002L7.617 4.787zm3.347 3.347A4.005 4.005 0 0116 11.998c0 .359-.047.706-.136 1.037l-4.9-4.901z"
fill="#212134"
/>
</svg>
</button>
</div>
</div>
</div>
</div>
</div>
<button
aria-disabled="false"
class="c26 c27 c28"
type="submit"
>
<span
class="c29 c30"
>
Change password
</span>
</button>
</div>
</form>
</div>
<div
class="c31"
>
<div
class="c32"
>
<a
class="c33"
href="/auth/login"
>
<span
class="c34 c5 c35"
>
<span
class="c36"
>
Ready to sign in?
</span>
</span>
</a>
</div>
</div>
</main>
</div>
</div>
`);
});
});