mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-10-27 08:44:49 +00:00
chore(ui): fix signup page styling (#13086)
* chore(ui): fix signup page styling * fix the file naming convention * optimize signup form
This commit is contained in:
parent
de7e06d024
commit
6ef003798b
@ -64,7 +64,9 @@ jest.mock('components/AppBar/Appbar', () =>
|
||||
jest.fn().mockReturnValue(<p>Appbar</p>)
|
||||
);
|
||||
|
||||
jest.mock('pages/signup', () => jest.fn().mockReturnValue(<p>SignUpPage</p>));
|
||||
jest.mock('pages/SignUp/SignUpPage', () =>
|
||||
jest.fn().mockReturnValue(<p>SignUpPage</p>)
|
||||
);
|
||||
|
||||
jest.mock('components/router/AuthenticatedAppRouter', () =>
|
||||
jest.fn().mockReturnValue(<p>AuthenticatedAppRouter</p>)
|
||||
|
||||
@ -19,7 +19,7 @@ import LeftSidebar from 'components/MyData/LeftSidebar/LeftSidebar.component';
|
||||
import AuthenticatedAppRouter from 'components/router/AuthenticatedAppRouter';
|
||||
import { ROUTES } from 'constants/constants';
|
||||
import { isEmpty } from 'lodash';
|
||||
import SignupPage from 'pages/signup';
|
||||
import SignUpPage from 'pages/SignUp/SignUpPage';
|
||||
import React from 'react';
|
||||
import { Redirect, Route, Switch } from 'react-router-dom';
|
||||
import './app-container.less';
|
||||
@ -27,7 +27,7 @@ import './app-container.less';
|
||||
const AppContainer = () => {
|
||||
return (
|
||||
<Switch>
|
||||
<Route exact component={SignupPage} path={ROUTES.SIGNUP}>
|
||||
<Route exact component={SignUpPage} path={ROUTES.SIGNUP}>
|
||||
{!isEmpty(AppState.userDetails) && <Redirect to={ROUTES.HOME} />}
|
||||
</Route>
|
||||
|
||||
|
||||
@ -12,7 +12,7 @@
|
||||
*/
|
||||
export interface TeamsSelectableProps {
|
||||
showTeamsAlert?: boolean;
|
||||
onSelectionChange: (teams: string[]) => void;
|
||||
onSelectionChange?: (teams: string[]) => void;
|
||||
filterJoinable?: boolean;
|
||||
placeholder?: string;
|
||||
selectedTeams?: string[];
|
||||
|
||||
@ -36,7 +36,7 @@ const TeamsSelectable = ({
|
||||
const [teams, setTeams] = useState<Array<TeamHierarchy>>([]);
|
||||
|
||||
const onChange = (newValue: string[]) => {
|
||||
onSelectionChange(newValue);
|
||||
onSelectionChange && onSelectionChange(newValue);
|
||||
setValue(newValue);
|
||||
};
|
||||
|
||||
|
||||
@ -14,7 +14,7 @@
|
||||
import AppContainer from 'components/AppContainer/AppContainer';
|
||||
import { CustomEventTypes } from 'generated/analytics/webAnalyticEventData';
|
||||
import { AuthProvider } from 'generated/settings/settings';
|
||||
import AccountActivationConfirmation from 'pages/signup/account-activation-confirmation.component';
|
||||
import AccountActivationConfirmation from 'pages/SignUp/account-activation-confirmation.component';
|
||||
import React, { useCallback, useEffect } from 'react';
|
||||
import { Redirect, Route, Switch, useLocation } from 'react-router-dom';
|
||||
import { useAnalytics } from 'use-analytics';
|
||||
@ -41,7 +41,7 @@ const ResetPassword = withSuspenseFallback(
|
||||
);
|
||||
|
||||
const BasicSignupPage = withSuspenseFallback(
|
||||
React.lazy(() => import('pages/signup/BasicSignup.component'))
|
||||
React.lazy(() => import('pages/SignUp/BasicSignup.component'))
|
||||
);
|
||||
|
||||
const AppRouter = () => {
|
||||
|
||||
@ -121,7 +121,7 @@ export const PAGE_HEADERS = {
|
||||
STORED_PROCEDURE_CUSTOM_ATTRIBUTES: {
|
||||
header: i18n.t('label.stored-procedure'),
|
||||
subHeader: i18n.t('message.define-custom-property-for-entity', {
|
||||
entity: i18n.t('label.label.stored-procedure'),
|
||||
entity: i18n.t('label.stored-procedure'),
|
||||
}),
|
||||
},
|
||||
BOTS: {
|
||||
|
||||
@ -0,0 +1,227 @@
|
||||
/*
|
||||
* Copyright 2023 Collate.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { act, fireEvent, render, screen } from '@testing-library/react';
|
||||
import AppState from 'AppState';
|
||||
import React from 'react';
|
||||
import { createUser } from 'rest/userAPI';
|
||||
import { getImages } from 'utils/CommonUtils';
|
||||
import {
|
||||
mockChangedFormData,
|
||||
mockCreateUser,
|
||||
mockFormData,
|
||||
} from './mocks/signup.mock';
|
||||
import SignUp from './SignUpPage';
|
||||
|
||||
let letExpectedUserName = {
|
||||
name: 'sample123',
|
||||
email: 'sample123@sample.com',
|
||||
};
|
||||
|
||||
const mockShowErrorToast = jest.fn();
|
||||
|
||||
jest.mock('react-router-dom', () => ({
|
||||
useHistory: jest.fn().mockReturnValue({
|
||||
push: jest.fn(),
|
||||
}),
|
||||
}));
|
||||
|
||||
jest.mock('components/authentication/auth-provider/AuthProvider', () => ({
|
||||
useAuthContext: jest.fn(() => ({
|
||||
setIsSigningIn: jest.fn(),
|
||||
})),
|
||||
}));
|
||||
|
||||
jest.mock('components/TeamsSelectable/TeamsSelectable', () =>
|
||||
jest.fn().mockImplementation(() => <div>TeamSelectable</div>)
|
||||
);
|
||||
|
||||
jest.mock('rest/userAPI', () => ({
|
||||
createUser: jest
|
||||
.fn()
|
||||
.mockImplementation(() => Promise.resolve(mockCreateUser)),
|
||||
}));
|
||||
|
||||
jest.mock('../../utils/ToastUtils', () => ({
|
||||
showErrorToast: jest.fn().mockImplementation(() => mockShowErrorToast),
|
||||
}));
|
||||
|
||||
jest.mock('../../AppState', () => ({
|
||||
...jest.requireActual('../../AppState'),
|
||||
newUser: {
|
||||
name: 'Sample Name',
|
||||
email: 'sample123@sample.com',
|
||||
picture: 'Profile Picture',
|
||||
},
|
||||
updateUserDetails: jest.fn(),
|
||||
updateUserPermissions: jest.fn(),
|
||||
}));
|
||||
|
||||
jest.mock('../../utils/CommonUtils', () => ({
|
||||
getImages: jest
|
||||
.fn()
|
||||
.mockResolvedValue(
|
||||
'https://lh3.googleusercontent.com/a/ALm5wu0HwEPhAbyRha16cUHrEum-zxTDzj6KZiqYsT5Y=s96-c'
|
||||
),
|
||||
Transi18next: jest.fn().mockReturnValue('text'),
|
||||
}));
|
||||
|
||||
jest.mock('utils/AuthProvider.util', () => ({
|
||||
getNameFromUserData: jest.fn().mockImplementation(() => letExpectedUserName),
|
||||
}));
|
||||
|
||||
describe('SignUp page', () => {
|
||||
it('Component should render properly', async () => {
|
||||
(createUser as jest.Mock).mockImplementationOnce(() =>
|
||||
Promise.resolve({ data: {} })
|
||||
);
|
||||
render(<SignUp />);
|
||||
const logo = screen.getByTestId('om-logo');
|
||||
const heading = screen.getByTestId('om-heading');
|
||||
const form = screen.getByTestId('create-user-form');
|
||||
const fullNameLabel = screen.getByTestId('full-name-label');
|
||||
const fullNameInput = screen.getByTestId('full-name-input');
|
||||
const usernameLabel = screen.getByTestId('username-label');
|
||||
const usernameInput = screen.getByTestId('username-input');
|
||||
const emailLabel = screen.getByTestId('email-label');
|
||||
const emailInput = screen.getByTestId('email-input');
|
||||
const selectTeamLabel = screen.getByTestId('select-team-label');
|
||||
const createButton = screen.getByTestId('create-button');
|
||||
const loadingContent = await screen.queryByTestId('loading-content');
|
||||
const submitButton = screen.getByTestId('create-button');
|
||||
|
||||
expect(logo).toBeInTheDocument();
|
||||
expect(heading).toBeInTheDocument();
|
||||
expect(form).toBeInTheDocument();
|
||||
expect(fullNameLabel).toBeInTheDocument();
|
||||
expect(fullNameInput).toBeInTheDocument();
|
||||
expect(usernameLabel).toBeInTheDocument();
|
||||
expect(usernameInput).toBeInTheDocument();
|
||||
expect(emailLabel).toBeInTheDocument();
|
||||
expect(emailInput).toBeInTheDocument();
|
||||
expect(selectTeamLabel).toBeInTheDocument();
|
||||
expect(createButton).toBeInTheDocument();
|
||||
expect(loadingContent).toBeNull();
|
||||
expect(submitButton).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Handlers in forms for change and submit should work properly', async () => {
|
||||
render(<SignUp />);
|
||||
const form = screen.getByTestId('create-user-form');
|
||||
const fullNameInput = screen.getByTestId(
|
||||
'full-name-input'
|
||||
) as HTMLInputElement;
|
||||
const userNameInput = screen.getByTestId(
|
||||
'username-input'
|
||||
) as HTMLInputElement;
|
||||
const emailInput = screen.getByTestId('email-input') as HTMLInputElement;
|
||||
const submitButton = screen.getByTestId('create-button');
|
||||
|
||||
expect(form).toBeInTheDocument();
|
||||
expect(fullNameInput).toHaveValue(mockFormData.name);
|
||||
expect(userNameInput).toHaveValue(mockFormData.userName);
|
||||
expect(emailInput).toHaveValue(mockFormData.email);
|
||||
|
||||
await act(async () => {
|
||||
fireEvent.change(fullNameInput, {
|
||||
target: { name: 'displayName', value: mockChangedFormData.fullName },
|
||||
});
|
||||
fireEvent.change(userNameInput, {
|
||||
target: { name: 'name', value: mockChangedFormData.userName },
|
||||
});
|
||||
fireEvent.change(emailInput, {
|
||||
target: { name: 'email', value: mockChangedFormData.email },
|
||||
});
|
||||
});
|
||||
|
||||
expect(fullNameInput).toHaveValue(mockChangedFormData.fullName);
|
||||
expect(userNameInput).toHaveValue(mockChangedFormData.userName);
|
||||
expect(emailInput).toHaveValue(mockChangedFormData.email);
|
||||
|
||||
fireEvent.click(submitButton);
|
||||
|
||||
await act(async () => {
|
||||
(createUser as jest.Mock).mockImplementationOnce(() =>
|
||||
Promise.resolve(undefined)
|
||||
);
|
||||
});
|
||||
|
||||
expect(createUser as jest.Mock).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('Error should be thrown if createUser API fails', async () => {
|
||||
render(<SignUp />);
|
||||
const form = screen.getByTestId('create-user-form');
|
||||
const fullNameInput = screen.getByTestId('full-name-input');
|
||||
const userNameInput = screen.getByTestId('username-input');
|
||||
const emailInput = screen.getByTestId('email-input');
|
||||
const submitButton = screen.getByTestId('create-button');
|
||||
|
||||
expect(form).toBeInTheDocument();
|
||||
expect(fullNameInput).toHaveValue(mockFormData.name);
|
||||
expect(userNameInput).toHaveValue(mockFormData.userName);
|
||||
expect(emailInput).toHaveValue(mockFormData.email);
|
||||
|
||||
await act(async () => {
|
||||
fireEvent.change(fullNameInput, {
|
||||
target: { name: 'displayName', value: mockChangedFormData.fullName },
|
||||
});
|
||||
fireEvent.change(userNameInput, {
|
||||
target: { name: 'name', value: mockChangedFormData.userName },
|
||||
});
|
||||
fireEvent.change(emailInput, {
|
||||
target: { name: 'email', value: mockChangedFormData.email },
|
||||
});
|
||||
});
|
||||
|
||||
expect(fullNameInput).toHaveValue(mockChangedFormData.fullName);
|
||||
expect(userNameInput).toHaveValue(mockChangedFormData.userName);
|
||||
expect(emailInput).toHaveValue(mockChangedFormData.email);
|
||||
|
||||
fireEvent.click(submitButton);
|
||||
await act(async () => {
|
||||
(createUser as jest.Mock).mockImplementationOnce(() =>
|
||||
Promise.reject({
|
||||
response: { data: { message: 'error' } },
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
expect(createUser as jest.Mock).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('Handlers in form should work if data is empty', async () => {
|
||||
(getImages as jest.Mock).mockImplementationOnce(() => Promise.reject(''));
|
||||
letExpectedUserName = { name: '', email: '' };
|
||||
AppState.newUser = {
|
||||
name: '',
|
||||
email: '',
|
||||
picture: '',
|
||||
};
|
||||
render(<SignUp />);
|
||||
const form = screen.getByTestId('create-user-form');
|
||||
const fullNameInput = screen.getByTestId('full-name-input');
|
||||
const usernameInput = screen.getByTestId('username-input');
|
||||
const emailInput = screen.getByTestId('email-input');
|
||||
|
||||
expect(form).toBeInTheDocument();
|
||||
expect(fullNameInput).toHaveValue('');
|
||||
expect(usernameInput).toHaveValue('');
|
||||
expect(emailInput).toHaveValue('');
|
||||
|
||||
const submitButton = screen.getByTestId('create-button');
|
||||
fireEvent.click(submitButton);
|
||||
|
||||
expect(createUser as jest.Mock).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,193 @@
|
||||
/*
|
||||
* Copyright 2023 Collate.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Button, Card, Form, FormProps, Input, Space, Typography } from 'antd';
|
||||
import { AxiosError } from 'axios';
|
||||
import { useAuthContext } from 'components/authentication/auth-provider/AuthProvider';
|
||||
import { UserProfile } from 'components/authentication/auth-provider/AuthProvider.interface';
|
||||
import TeamsSelectable from 'components/TeamsSelectable/TeamsSelectable';
|
||||
import { CookieStorage } from 'cookie-storage';
|
||||
import React, { useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import { createUser } from 'rest/userAPI';
|
||||
import { getNameFromUserData } from 'utils/AuthProvider.util';
|
||||
import appState from '../../AppState';
|
||||
import { ReactComponent as OMDLogo } from '../../assets/svg/logo-monogram.svg';
|
||||
import {
|
||||
REDIRECT_PATHNAME,
|
||||
ROUTES,
|
||||
VALIDATION_MESSAGES,
|
||||
} from '../../constants/constants';
|
||||
import { getImages, Transi18next } from '../../utils/CommonUtils';
|
||||
import { showErrorToast } from '../../utils/ToastUtils';
|
||||
|
||||
const cookieStorage = new CookieStorage();
|
||||
|
||||
const SignUp = () => {
|
||||
const { t } = useTranslation();
|
||||
const history = useHistory();
|
||||
const {
|
||||
setIsSigningIn,
|
||||
jwtPrincipalClaims = [],
|
||||
authorizerConfig,
|
||||
} = useAuthContext();
|
||||
|
||||
const [loading, setLoading] = useState<boolean>(false);
|
||||
|
||||
const handleCreateNewUser: FormProps['onFinish'] = async (data) => {
|
||||
setLoading(true);
|
||||
|
||||
try {
|
||||
const res = await createUser({
|
||||
...data,
|
||||
profile: {
|
||||
images: getImages(appState.newUser.picture ?? ''),
|
||||
},
|
||||
});
|
||||
|
||||
appState.updateUserDetails(res);
|
||||
cookieStorage.removeItem(REDIRECT_PATHNAME);
|
||||
setIsSigningIn(false);
|
||||
history.push(ROUTES.HOME);
|
||||
} catch (error) {
|
||||
showErrorToast(
|
||||
error as AxiosError,
|
||||
t('server.create-entity-error', {
|
||||
entity: t('label.user'),
|
||||
})
|
||||
);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex-center w-full h-full">
|
||||
<Card className="p-x-md p-y-md w-500">
|
||||
<Space
|
||||
align="center"
|
||||
className="w-full m-b-lg"
|
||||
direction="vertical"
|
||||
size="middle">
|
||||
<OMDLogo
|
||||
data-testid="om-logo"
|
||||
height={50}
|
||||
name={t('label.open-metadata-logo')}
|
||||
width={50}
|
||||
/>
|
||||
<Typography.Title
|
||||
className="text-center"
|
||||
data-testid="om-heading"
|
||||
level={3}>
|
||||
<Transi18next
|
||||
i18nKey="label.join-entity"
|
||||
renderElement={<span className="text-primary" />}
|
||||
values={{
|
||||
entity: t('label.open-metadata'),
|
||||
}}
|
||||
/>
|
||||
</Typography.Title>
|
||||
</Space>
|
||||
|
||||
<Form
|
||||
data-testid="create-user-form"
|
||||
initialValues={{
|
||||
displayName: appState.newUser.name || '',
|
||||
...getNameFromUserData(
|
||||
appState.newUser as UserProfile,
|
||||
jwtPrincipalClaims,
|
||||
authorizerConfig?.principalDomain
|
||||
),
|
||||
}}
|
||||
layout="vertical"
|
||||
validateMessages={VALIDATION_MESSAGES}
|
||||
onFinish={handleCreateNewUser}>
|
||||
<Form.Item
|
||||
data-testid="full-name-label"
|
||||
label={t('label.full-name')}
|
||||
name="displayName"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
},
|
||||
]}>
|
||||
<Input
|
||||
data-testid="full-name-input"
|
||||
placeholder={t('label.your-entity', {
|
||||
entity: t('label.full-name'),
|
||||
})}
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
data-testid="username-label"
|
||||
label={t('label.username')}
|
||||
name="name"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
},
|
||||
]}>
|
||||
<Input
|
||||
disabled
|
||||
data-testid="username-input"
|
||||
placeholder={t('label.username')}
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
data-testid="email-label"
|
||||
label={t('label.email')}
|
||||
name="email"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
},
|
||||
]}>
|
||||
<Input
|
||||
disabled
|
||||
data-testid="email-input"
|
||||
placeholder={t('label.your-entity', {
|
||||
entity: `${t('label.email')} ${t('label.address')}`,
|
||||
})}
|
||||
type="email"
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
data-testid="select-team-label"
|
||||
label={t('label.select-field', {
|
||||
field: t('label.team-plural-lowercase'),
|
||||
})}
|
||||
name="teams"
|
||||
trigger="onSelectionChange">
|
||||
<TeamsSelectable filterJoinable showTeamsAlert />
|
||||
</Form.Item>
|
||||
|
||||
<Space align="center" className="w-full justify-end d-flex">
|
||||
<Button
|
||||
data-testid="create-button"
|
||||
htmlType="submit"
|
||||
loading={loading}
|
||||
type="primary">
|
||||
{t('label.create')}
|
||||
</Button>
|
||||
</Space>
|
||||
</Form>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default SignUp;
|
||||
@ -11,6 +11,19 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export const mockFormData = {
|
||||
name: 'Sample Name',
|
||||
email: 'sample123@sample.com',
|
||||
picture: 'Profile Picture',
|
||||
userName: 'sample123',
|
||||
};
|
||||
|
||||
export const mockChangedFormData = {
|
||||
fullName: 'f_name m_name l_name',
|
||||
userName: 'mockUserName',
|
||||
email: 'test@gmail.com',
|
||||
};
|
||||
|
||||
export const mockCreateUser = {
|
||||
data: {
|
||||
id: '911d4be4-6ebf-48a0-9016-43a2cf716428',
|
||||
@ -1,289 +0,0 @@
|
||||
/*
|
||||
* Copyright 2022 Collate.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { act, fireEvent, render } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import React from 'react';
|
||||
import { createUser } from 'rest/userAPI';
|
||||
import SignUp from '.';
|
||||
import AppState from '../../AppState';
|
||||
import { getImages } from '../../utils/CommonUtils';
|
||||
import { mockCreateUser } from './mocks/signup.mock';
|
||||
|
||||
let letExpectedUserName = { name: 'sample123', email: 'sample123@sample.com' };
|
||||
|
||||
const mockChangeHandler = jest.fn();
|
||||
const mockSubmitHandler = jest.fn();
|
||||
const mockShowErrorToast = jest.fn();
|
||||
|
||||
jest.mock('react-router-dom', () => ({
|
||||
useHistory: jest.fn().mockReturnValue({
|
||||
push: jest.fn(),
|
||||
}),
|
||||
}));
|
||||
|
||||
jest.mock('components/authentication/auth-provider/AuthProvider', () => ({
|
||||
useAuthContext: jest.fn(() => ({
|
||||
setIsSigningIn: jest.fn(),
|
||||
})),
|
||||
}));
|
||||
|
||||
jest.mock('components/TeamsSelectable/TeamsSelectable', () => {
|
||||
return jest.fn().mockImplementation(() => <div>TeamSelectable</div>);
|
||||
});
|
||||
|
||||
jest.mock('rest/userAPI', () => ({
|
||||
createUser: jest
|
||||
.fn()
|
||||
.mockImplementation(() => Promise.resolve(mockCreateUser)),
|
||||
}));
|
||||
|
||||
jest.mock('../../utils/ToastUtils', () => ({
|
||||
showErrorToast: jest.fn().mockImplementation(() => mockShowErrorToast),
|
||||
}));
|
||||
|
||||
jest.mock('../../AppState', () => ({
|
||||
...jest.requireActual('../../AppState'),
|
||||
newUser: {
|
||||
name: 'Sample Name',
|
||||
email: 'sample123@sample.com',
|
||||
picture: 'Profile Picture',
|
||||
},
|
||||
updateUserDetails: jest.fn(),
|
||||
updateUserPermissions: jest.fn(),
|
||||
}));
|
||||
|
||||
jest.mock('../../utils/CommonUtils', () => ({
|
||||
getImages: jest
|
||||
.fn()
|
||||
.mockResolvedValue(
|
||||
'https://lh3.googleusercontent.com/a/ALm5wu0HwEPhAbyRha16cUHrEum-zxTDzj6KZiqYsT5Y=s96-c'
|
||||
),
|
||||
Transi18next: jest.fn().mockReturnValue('text'),
|
||||
}));
|
||||
|
||||
jest.mock('utils/AuthProvider.util', () => ({
|
||||
getNameFromUserData: jest.fn().mockImplementation(() => letExpectedUserName),
|
||||
}));
|
||||
|
||||
describe('SignUp page', () => {
|
||||
it('Component should render properly', async () => {
|
||||
(createUser as jest.Mock).mockImplementationOnce(() =>
|
||||
Promise.resolve({ data: {} })
|
||||
);
|
||||
|
||||
const { getByTestId, queryByTestId } = render(<SignUp />);
|
||||
|
||||
const logo = getByTestId('om-logo');
|
||||
const heading = getByTestId('om-heading');
|
||||
const form = getByTestId('create-user-form');
|
||||
const fullNameLabel = getByTestId('full-name-label');
|
||||
const fullNameInput = getByTestId('full-name-input');
|
||||
const usernameLabel = getByTestId('username-label');
|
||||
const usernameInput = getByTestId('username-input');
|
||||
const emailLabel = getByTestId('email-label');
|
||||
const emailInput = getByTestId('email-input');
|
||||
const selectTeamLabel = getByTestId('select-team-label');
|
||||
const createButton = getByTestId('create-button');
|
||||
const loadingContent = await queryByTestId('loading-content');
|
||||
|
||||
expect(logo).toBeInTheDocument();
|
||||
expect(heading).toBeInTheDocument();
|
||||
expect(form).toBeInTheDocument();
|
||||
expect(fullNameLabel).toBeInTheDocument();
|
||||
expect(fullNameInput).toBeInTheDocument();
|
||||
expect(usernameLabel).toBeInTheDocument();
|
||||
expect(usernameInput).toBeInTheDocument();
|
||||
expect(emailLabel).toBeInTheDocument();
|
||||
expect(emailInput).toBeInTheDocument();
|
||||
expect(selectTeamLabel).toBeInTheDocument();
|
||||
expect(createButton).toBeInTheDocument();
|
||||
expect(loadingContent).toBeNull();
|
||||
|
||||
await act(async () => {
|
||||
form.onsubmit = mockSubmitHandler;
|
||||
|
||||
fireEvent.submit(form);
|
||||
|
||||
expect(mockSubmitHandler).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
it('Handlers in forms for change and submit should work properly', async () => {
|
||||
(createUser as jest.Mock).mockImplementationOnce(() =>
|
||||
Promise.resolve(undefined)
|
||||
);
|
||||
|
||||
const { getByTestId } = render(<SignUp />);
|
||||
|
||||
const form = getByTestId('create-user-form');
|
||||
const fullNameInput = getByTestId('full-name-input');
|
||||
const usernameInput = getByTestId('username-input');
|
||||
const emailInput = getByTestId('email-input');
|
||||
|
||||
expect(form).toBeInTheDocument();
|
||||
expect(fullNameInput).toHaveValue('Sample Name');
|
||||
expect(usernameInput).toHaveValue('sample123');
|
||||
expect(emailInput).toHaveValue('sample123@sample.com');
|
||||
|
||||
fullNameInput.onchange = mockChangeHandler;
|
||||
usernameInput.onchange = mockChangeHandler;
|
||||
emailInput.onchange = mockChangeHandler;
|
||||
|
||||
await act(async () => {
|
||||
fireEvent.change(fullNameInput, {
|
||||
target: { name: 'displayName', value: 'Fname Mname Lname' },
|
||||
});
|
||||
|
||||
fireEvent.change(usernameInput, {
|
||||
target: { name: 'displayName', value: 'mockUserName' },
|
||||
});
|
||||
fireEvent.change(emailInput, {
|
||||
target: { name: 'displayName', value: 'sample@sample.com' },
|
||||
});
|
||||
|
||||
expect(mockChangeHandler).toHaveBeenCalledTimes(3);
|
||||
|
||||
form.onsubmit = mockSubmitHandler;
|
||||
|
||||
fireEvent.submit(form);
|
||||
|
||||
expect(mockSubmitHandler).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
it('Error should be thrown if createUser API fails', async () => {
|
||||
const { getByTestId } = render(<SignUp />);
|
||||
|
||||
const form = getByTestId('create-user-form');
|
||||
const fullNameInput = getByTestId('full-name-input');
|
||||
const usernameInput = getByTestId('username-input');
|
||||
const emailInput = getByTestId('email-input');
|
||||
|
||||
expect(form).toBeInTheDocument();
|
||||
expect(fullNameInput).toHaveValue('Sample Name');
|
||||
expect(usernameInput).toHaveValue('sample123');
|
||||
expect(emailInput).toHaveValue('sample123@sample.com');
|
||||
|
||||
fullNameInput.onchange = mockChangeHandler;
|
||||
usernameInput.onchange = mockChangeHandler;
|
||||
emailInput.onchange = mockChangeHandler;
|
||||
|
||||
await act(async () => {
|
||||
fireEvent.change(fullNameInput, {
|
||||
target: { name: 'displayName', value: 'Fname Mname Lname' },
|
||||
});
|
||||
|
||||
fireEvent.change(usernameInput, {
|
||||
target: { name: 'displayName', value: 'mockUserName' },
|
||||
});
|
||||
fireEvent.change(emailInput, {
|
||||
target: { name: 'displayName', value: '' },
|
||||
});
|
||||
});
|
||||
|
||||
expect(mockChangeHandler).toHaveBeenCalledTimes(3);
|
||||
|
||||
form.onsubmit = mockSubmitHandler;
|
||||
|
||||
await act(async () => {
|
||||
(createUser as jest.Mock).mockImplementationOnce(() =>
|
||||
Promise.reject({
|
||||
response: { data: { message: 'error' } },
|
||||
})
|
||||
);
|
||||
fireEvent.submit(form);
|
||||
});
|
||||
|
||||
expect(createUser as jest.Mock).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('Handlers in form should work if data is empty', async () => {
|
||||
(getImages as jest.Mock).mockImplementationOnce(() => Promise.reject(''));
|
||||
letExpectedUserName = { name: '', email: '' };
|
||||
|
||||
AppState.newUser = {
|
||||
name: '',
|
||||
email: '',
|
||||
picture: '',
|
||||
};
|
||||
|
||||
const { getByTestId } = render(<SignUp />);
|
||||
|
||||
const form = getByTestId('create-user-form');
|
||||
const fullNameInput = getByTestId('full-name-input');
|
||||
const usernameInput = getByTestId('username-input');
|
||||
const emailInput = getByTestId('email-input');
|
||||
|
||||
expect(form).toBeInTheDocument();
|
||||
expect(fullNameInput).toHaveValue('');
|
||||
expect(usernameInput).toHaveValue('');
|
||||
expect(emailInput).toHaveValue('');
|
||||
|
||||
fullNameInput.onchange = mockChangeHandler;
|
||||
usernameInput.onchange = mockChangeHandler;
|
||||
emailInput.onchange = mockChangeHandler;
|
||||
|
||||
expect(mockChangeHandler).not.toHaveBeenCalled();
|
||||
|
||||
form.onsubmit = mockSubmitHandler;
|
||||
|
||||
await act(async () => {
|
||||
fireEvent.submit(form);
|
||||
});
|
||||
|
||||
expect(createUser as jest.Mock).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
|
||||
it('Create Button Should Work Properly and call the form handler', async () => {
|
||||
(createUser as jest.Mock).mockImplementationOnce(() =>
|
||||
Promise.resolve(undefined)
|
||||
);
|
||||
|
||||
const { getByTestId } = render(<SignUp />);
|
||||
|
||||
const form = getByTestId('create-user-form');
|
||||
const fullNameInput = getByTestId('full-name-input');
|
||||
const usernameInput = getByTestId('username-input');
|
||||
const emailInput = getByTestId('email-input');
|
||||
|
||||
expect(form).toBeInTheDocument();
|
||||
|
||||
fullNameInput.onchange = mockChangeHandler;
|
||||
usernameInput.onchange = mockChangeHandler;
|
||||
emailInput.onchange = mockChangeHandler;
|
||||
|
||||
await act(async () => {
|
||||
fireEvent.change(fullNameInput, {
|
||||
target: { name: 'displayName', value: 'Fname Mname Lname' },
|
||||
});
|
||||
|
||||
fireEvent.change(usernameInput, {
|
||||
target: { name: 'displayName', value: 'mockUserName' },
|
||||
});
|
||||
fireEvent.change(emailInput, {
|
||||
target: { name: 'displayName', value: 'sample@sample.com' },
|
||||
});
|
||||
|
||||
expect(mockChangeHandler).toHaveBeenCalledTimes(3);
|
||||
|
||||
form.onsubmit = mockSubmitHandler;
|
||||
|
||||
const createButton = getByTestId('create-button');
|
||||
|
||||
userEvent.click(createButton);
|
||||
|
||||
expect(mockSubmitHandler).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -1,224 +0,0 @@
|
||||
/*
|
||||
* Copyright 2022 Collate.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Button } from 'antd';
|
||||
import { AxiosError } from 'axios';
|
||||
import { useAuthContext } from 'components/authentication/auth-provider/AuthProvider';
|
||||
import { UserProfile } from 'components/authentication/auth-provider/AuthProvider.interface';
|
||||
import TeamsSelectable from 'components/TeamsSelectable/TeamsSelectable';
|
||||
import { CookieStorage } from 'cookie-storage';
|
||||
import React, { useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import { createUser } from 'rest/userAPI';
|
||||
import { getNameFromUserData } from 'utils/AuthProvider.util';
|
||||
import appState from '../../AppState';
|
||||
import { ReactComponent as OMDLogo } from '../../assets/svg/logo-monogram.svg';
|
||||
import { ELLIPSES, REDIRECT_PATHNAME, ROUTES } from '../../constants/constants';
|
||||
import { CreateUser } from '../../generated/api/teams/createUser';
|
||||
import { User } from '../../generated/entity/teams/user';
|
||||
import { getImages, Transi18next } from '../../utils/CommonUtils';
|
||||
import { showErrorToast } from '../../utils/ToastUtils';
|
||||
|
||||
const cookieStorage = new CookieStorage();
|
||||
|
||||
const SignUp = () => {
|
||||
const { t } = useTranslation();
|
||||
const history = useHistory();
|
||||
const {
|
||||
setIsSigningIn,
|
||||
jwtPrincipalClaims = [],
|
||||
authorizerConfig,
|
||||
} = useAuthContext();
|
||||
|
||||
const [selectedTeams, setSelectedTeams] = useState<Array<string>>([]);
|
||||
const [loading, setLoading] = useState<boolean>(false);
|
||||
const [details, setDetails] = useState({
|
||||
displayName: appState.newUser.name || '',
|
||||
...getNameFromUserData(
|
||||
appState.newUser as UserProfile,
|
||||
jwtPrincipalClaims,
|
||||
authorizerConfig?.principalDomain
|
||||
),
|
||||
});
|
||||
|
||||
const createNewUser = (details: User | CreateUser) => {
|
||||
setLoading(true);
|
||||
createUser(details as CreateUser)
|
||||
.then((res) => {
|
||||
if (res) {
|
||||
appState.updateUserDetails(res);
|
||||
cookieStorage.removeItem(REDIRECT_PATHNAME);
|
||||
setIsSigningIn(false);
|
||||
history.push(ROUTES.HOME);
|
||||
} else {
|
||||
setLoading(false);
|
||||
}
|
||||
})
|
||||
.catch((err: AxiosError) => {
|
||||
showErrorToast(
|
||||
err,
|
||||
t('server.create-entity-error', {
|
||||
entity: t('label.user'),
|
||||
})
|
||||
);
|
||||
})
|
||||
.finally(() => {
|
||||
setLoading(false);
|
||||
});
|
||||
};
|
||||
|
||||
const onChangeHandler = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
e.persist();
|
||||
setDetails((prevState) => {
|
||||
return {
|
||||
...prevState,
|
||||
[e.target.name]: e.target.value,
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
const onSubmitHandler = (e: React.FormEvent<HTMLFormElement>) => {
|
||||
e.preventDefault();
|
||||
if (details.name && details.displayName) {
|
||||
createNewUser({
|
||||
...details,
|
||||
teams: selectedTeams as Array<string>,
|
||||
profile: {
|
||||
images: getImages(appState.newUser.picture ?? ''),
|
||||
},
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return loading ? (
|
||||
<p
|
||||
className="text-center text-grey-body d-flex justify-center items-center"
|
||||
data-testid="loading-content">
|
||||
{t('label.creating-account')}
|
||||
{ELLIPSES}
|
||||
</p>
|
||||
) : (
|
||||
// TODO: replace this with form
|
||||
<div className="d-flex justify-center">
|
||||
<div className="d-flex flex-col items-center signup-box">
|
||||
<div className="d-flex justify-center items-center">
|
||||
<OMDLogo
|
||||
data-testid="om-logo"
|
||||
height={50}
|
||||
name={t('label.open-metadata-logo')}
|
||||
width={50}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<h4 className="font-semibold" data-testid="om-heading">
|
||||
<Transi18next
|
||||
i18nKey="label.join-entity"
|
||||
renderElement={<span className="text-primary" />}
|
||||
values={{
|
||||
entity: t('label.open-metadata'),
|
||||
}}
|
||||
/>
|
||||
</h4>
|
||||
</div>
|
||||
<div className="w-full">
|
||||
<form
|
||||
action="."
|
||||
data-testid="create-user-form"
|
||||
method="POST"
|
||||
onSubmit={onSubmitHandler}>
|
||||
<div>
|
||||
<label
|
||||
className="d-block text-body text-grey-body required-field"
|
||||
data-testid="full-name-label"
|
||||
htmlFor="displayName">
|
||||
{t('label.full-name')}
|
||||
</label>
|
||||
<input
|
||||
required
|
||||
autoComplete="off"
|
||||
data-testid="full-name-input"
|
||||
id="displayName"
|
||||
name="displayName"
|
||||
placeholder={t('label.your-entity', {
|
||||
entity: t('label.full-name'),
|
||||
})}
|
||||
type="text"
|
||||
value={details.displayName}
|
||||
onChange={onChangeHandler}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label data-testid="username-label" htmlFor="name">
|
||||
{t('label.username')}
|
||||
</label>
|
||||
<input
|
||||
readOnly
|
||||
required
|
||||
autoComplete="off"
|
||||
data-testid="username-input"
|
||||
id="name"
|
||||
name="name"
|
||||
placeholder={t('label.username')}
|
||||
type="text"
|
||||
value={details.name}
|
||||
onChange={onChangeHandler}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label data-testid="email-label" htmlFor="email">
|
||||
{t('label.email')}
|
||||
</label>
|
||||
<input
|
||||
readOnly
|
||||
required
|
||||
autoComplete="off"
|
||||
data-testid="email-input"
|
||||
id="email"
|
||||
name="email"
|
||||
placeholder={t('label.your-entity', {
|
||||
entity: `${t('label.email')} ${t('label.address')}`,
|
||||
})}
|
||||
type="email"
|
||||
value={details.email}
|
||||
onChange={onChangeHandler}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label data-testid="select-team-label">
|
||||
{t('label.select-field', {
|
||||
field: t('label.team-plural-lowercase'),
|
||||
})}
|
||||
</label>
|
||||
<TeamsSelectable
|
||||
filterJoinable
|
||||
showTeamsAlert
|
||||
onSelectionChange={setSelectedTeams}
|
||||
/>
|
||||
</div>
|
||||
<div className="d-flex justify-end">
|
||||
<Button
|
||||
data-testid="create-button"
|
||||
htmlType="submit"
|
||||
type="primary">
|
||||
{t('label.create')}
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default SignUp;
|
||||
Loading…
x
Reference in New Issue
Block a user