1
0
mirror of https://github.com/SomboChea/ui synced 2026-01-17 16:45:49 +07:00

Refactor(#209): Converted App component from class to func

This commit is contained in:
Priscila Oliveira
2019-11-24 19:21:08 +01:00
committed by GitHub
parent 0a48906fc8
commit 0d9232a92c
16 changed files with 520 additions and 304 deletions

View File

@@ -1,16 +1,16 @@
import React from 'react';
import { BrowserRouter as Router } from 'react-router-dom';
import { render, fireEvent, waitForElementToBeRemoved, waitForElement } from '../../utils/test-react-testing-library';
import { render, fireEvent, waitForElement, waitForElementToBeRemoved } from '../../utils/test-react-testing-library';
import { AppContextProvider } from '../../App';
import Header from './Header';
const headerProps = {
username: 'verddacio-user',
scope: 'test scope',
withoutSearch: true,
handleToggleLoginModal: jest.fn(),
handleLogout: jest.fn(),
const props = {
user: {
username: 'verddacio-user',
},
packages: [],
};
/* eslint-disable react/jsx-no-bind*/
@@ -18,82 +18,71 @@ describe('<Header /> component with logged in state', () => {
test('should load the component in logged out state', () => {
const { container, queryByTestId, getByText } = render(
<Router>
<Header
onLogout={headerProps.handleLogout}
onToggleLoginModal={headerProps.handleToggleLoginModal}
scope={headerProps.scope}
/>
<AppContextProvider packages={props.packages}>
<Header />
</AppContextProvider>
</Router>
);
expect(container.firstChild).toMatchSnapshot();
expect(queryByTestId('header--menu-acountcircle')).toBeNull();
expect(queryByTestId('header--menu-accountcircle')).toBeNull();
expect(getByText('Login')).toBeTruthy();
});
test('should load the component in logged in state', () => {
const { container, getByTestId, queryByText } = render(
<Router>
<Header
onLogout={headerProps.handleLogout}
onToggleLoginModal={headerProps.handleToggleLoginModal}
scope={headerProps.scope}
username={headerProps.username}
/>
<AppContextProvider packages={props.packages} user={props.user}>
<Header />
</AppContextProvider>
</Router>
);
expect(container.firstChild).toMatchSnapshot();
expect(getByTestId('header--menu-acountcircle')).toBeTruthy();
expect(getByTestId('header--menu-accountcircle')).toBeTruthy();
expect(queryByText('Login')).toBeNull();
});
test('should open login dialog', async () => {
const { getByText } = render(
const { getByText, getByTestId } = render(
<Router>
<Header
onLogout={headerProps.handleLogout}
onToggleLoginModal={headerProps.handleToggleLoginModal}
scope={headerProps.scope}
/>
<AppContextProvider packages={props.packages}>
<Header />
</AppContextProvider>
</Router>
);
const loginBtn = getByText('Login');
fireEvent.click(loginBtn);
expect(headerProps.handleToggleLoginModal).toHaveBeenCalled();
// wait for login modal appearance and return the element
const registrationInfoModal = await waitForElement(() => getByTestId('login--form-container'));
expect(registrationInfoModal).toBeTruthy();
});
test('should logout the user', async () => {
const { getByText, getByTestId } = render(
<Router>
<Header
onLogout={headerProps.handleLogout}
onToggleLoginModal={headerProps.handleToggleLoginModal}
scope={headerProps.scope}
username={headerProps.username}
/>
<AppContextProvider packages={props.packages} user={props.user}>
<Header />
</AppContextProvider>
</Router>
);
const headerMenuAccountCircle = getByTestId('header--menu-acountcircle');
const headerMenuAccountCircle = getByTestId('header--menu-accountcircle');
fireEvent.click(headerMenuAccountCircle);
// wait for button Logout's appearance and return the element
const logoutBtn = await waitForElement(() => getByText('Logout'));
fireEvent.click(logoutBtn);
expect(headerProps.handleLogout).toHaveBeenCalled();
expect(getByText('Login')).toBeTruthy();
});
test("The question icon should open a new tab of verdaccio's website - installation doc", async () => {
test("The question icon should open a new tab of verdaccio's website - installation doc", () => {
const { getByTestId } = render(
<Router>
<Header
onLogout={headerProps.handleLogout}
onToggleLoginModal={headerProps.handleToggleLoginModal}
scope={headerProps.scope}
username={headerProps.username}
/>
<AppContextProvider packages={props.packages} user={props.user}>
<Header />
</AppContextProvider>
</Router>
);
@@ -104,12 +93,9 @@ describe('<Header /> component with logged in state', () => {
test('should open the registrationInfo modal when clicking on the info icon', async () => {
const { getByTestId } = render(
<Router>
<Header
onLogout={headerProps.handleLogout}
onToggleLoginModal={headerProps.handleToggleLoginModal}
scope={headerProps.scope}
username={headerProps.username}
/>
<AppContextProvider packages={props.packages} user={props.user}>
<Header />
</AppContextProvider>
</Router>
);
@@ -124,12 +110,9 @@ describe('<Header /> component with logged in state', () => {
test('should close the registrationInfo modal when clicking on the button close', async () => {
const { getByTestId, getByText, queryByTestId } = render(
<Router>
<Header
onLogout={headerProps.handleLogout}
onToggleLoginModal={headerProps.handleToggleLoginModal}
scope={headerProps.scope}
username={headerProps.username}
/>
<AppContextProvider packages={props.packages} user={props.user}>
<Header />
</AppContextProvider>
</Router>
);
@@ -144,6 +127,6 @@ describe('<Header /> component with logged in state', () => {
queryByTestId('registryInfo--dialog')
);
expect(hasRegistrationInfoModalBeenRemoved).toBeTruthy();
test.todo('autocompletion should display suggestions according to the type value');
});
test.todo('autocompletion should display suggestions according to the type value');
});

View File

@@ -1,8 +1,12 @@
import React, { useState } from 'react';
import React, { useState, useContext } from 'react';
import Search from '../Search';
import storage from '../../utils/storage';
import { getRegistryURL } from '../../utils/url';
import { makeLogin } from '../../utils/login';
import Button from '../../muiComponents/Button';
import AppContext from '../../App/AppContext';
import LoginModal from '../Login';
import Search from '../Search';
import { NavBar, InnerNavBar, MobileNavBar, InnerMobileNavBar } from './styles';
import HeaderLeft from './HeaderLeft';
@@ -10,31 +14,66 @@ import HeaderRight from './HeaderRight';
import HeaderInfoDialog from './HeaderInfoDialog';
interface Props {
logo?: string;
username?: string;
onLogout: () => void;
onToggleLoginModal: () => void;
scope: string;
withoutSearch?: boolean;
}
/* eslint-disable react/jsx-max-depth */
/* eslint-disable react/jsx-no-bind*/
const Header: React.FC<Props> = ({ logo, withoutSearch, username, onLogout, onToggleLoginModal, scope }) => {
/* eslint-disable @typescript-eslint/explicit-function-return-type */
const Header: React.FC<Props> = ({ withoutSearch }) => {
const appContext = useContext(AppContext);
const [isInfoDialogOpen, setOpenInfoDialog] = useState();
const [showMobileNavBar, setShowMobileNavBar] = useState();
const [showLoginModal, setShowLoginModal] = useState(false);
if (!appContext) {
throw Error('The app Context was not correct used');
}
const { user, scope, error, setUser, setError } = appContext;
const logo = window.VERDACCIO_LOGO;
/**
* handles login
* Required by: <Header />
*/
const handleDoLogin = async (usernameValue: string, passwordValue: string) => {
const { username, token, error } = await makeLogin(usernameValue, passwordValue);
if (username && token) {
storage.setItem('username', username);
storage.setItem('token', token);
setUser({ username });
setShowLoginModal(false);
}
if (error) {
setUser(undefined);
setError(error);
}
};
/**
* Logouts user
* Required by: <Header />
*/
const handleLogout = () => {
storage.removeItem('username');
storage.removeItem('token');
setUser(undefined);
};
return (
<>
<NavBar position="static">
<NavBar data-testid="header" position="static">
<InnerNavBar>
<HeaderLeft logo={logo} />
<HeaderRight
onLogout={onLogout}
onLogout={handleLogout}
onOpenRegistryInfoDialog={() => setOpenInfoDialog(true)}
onToggleLogin={onToggleLoginModal}
onToggleLogin={() => setShowLoginModal(!showLoginModal)}
onToggleMobileNav={() => setShowMobileNavBar(!showMobileNavBar)}
username={username}
username={user && user.username}
withoutSearch={withoutSearch}
/>
</InnerNavBar>
@@ -55,6 +94,12 @@ const Header: React.FC<Props> = ({ logo, withoutSearch, username, onLogout, onTo
</Button>
</MobileNavBar>
)}
<LoginModal
error={error}
onCancel={() => setShowLoginModal(false)}
onSubmit={handleDoLogin}
visibility={showLoginModal}
/>
</>
);
};

View File

@@ -11,7 +11,7 @@ interface Props {
const HeaderGreetings: React.FC<Props> = ({ username }) => (
<>
<Greetings>{'Hi,'}</Greetings>
<Label capitalize={true} text={username} weight="bold" />
<Label capitalize={true} data-testid="greetings-label" text={username} weight="bold" />
</>
);

View File

@@ -28,7 +28,7 @@ const HeaderMenu: React.FC<Props> = ({
<>
<IconButton
color="inherit"
data-testid="header--menu-acountcircle"
data-testid="header--menu-accountcircle"
id="header--button-account"
onClick={onLoggedInMenu}>
<AccountCircle />
@@ -48,7 +48,7 @@ const HeaderMenu: React.FC<Props> = ({
<MenuItem disabled={true}>
<HeaderGreetings username={username} />
</MenuItem>
<MenuItem button={true} id="header--button-logout" onClick={onLogout}>
<MenuItem button={true} data-testid="header--button-logout" id="header--button-logout" onClick={onLogout}>
{'Logout'}
</MenuItem>
</Menu>

View File

@@ -53,7 +53,7 @@ const HeaderRight: React.FC<Props> = ({
};
return (
<RightSide>
<RightSide data-testid="header-right">
{!withoutSearch && (
<HeaderToolTip onClick={onToggleMobileNav} title={'Search packages'} tooltipIconType={'search'} />
)}

View File

@@ -141,6 +141,7 @@ exports[`<Header /> component with logged in state should load the component in
<header
class="MuiPaper-root MuiPaper-elevation4 MuiAppBar-root MuiAppBar-positionStatic emotion-24 emotion-25 MuiAppBar-colorPrimary"
data-testid="header"
>
<div
class="MuiToolbar-root MuiToolbar-regular emotion-22 emotion-23 MuiToolbar-gutters"
@@ -213,6 +214,7 @@ exports[`<Header /> component with logged in state should load the component in
</div>
<div
class="MuiToolbar-root MuiToolbar-regular emotion-20 emotion-21 MuiToolbar-gutters"
data-testid="header-right"
>
<button
class="MuiButtonBase-root MuiIconButton-root emotion-16 emotion-17 MuiIconButton-colorInherit"
@@ -302,7 +304,7 @@ exports[`<Header /> component with logged in state should load the component in
</button>
<button
class="MuiButtonBase-root MuiIconButton-root MuiIconButton-colorInherit"
data-testid="header--menu-acountcircle"
data-testid="header--menu-accountcircle"
id="header--button-account"
tabindex="0"
type="button"
@@ -472,6 +474,7 @@ exports[`<Header /> component with logged in state should load the component in
<header
class="MuiPaper-root MuiPaper-elevation4 MuiAppBar-root MuiAppBar-positionStatic emotion-24 emotion-25 MuiAppBar-colorPrimary"
data-testid="header"
>
<div
class="MuiToolbar-root MuiToolbar-regular emotion-22 emotion-23 MuiToolbar-gutters"
@@ -544,6 +547,7 @@ exports[`<Header /> component with logged in state should load the component in
</div>
<div
class="MuiToolbar-root MuiToolbar-regular emotion-20 emotion-21 MuiToolbar-gutters"
data-testid="header-right"
>
<button
class="MuiButtonBase-root MuiIconButton-root emotion-16 emotion-17 MuiIconButton-colorInherit"