mirror of
https://github.com/shlinkio/shlink-web-client.git
synced 2024-12-23 17:40:23 +03:00
Refactored ServerError to infer error message based on provided server type guards
This commit is contained in:
parent
f40ad91ea9
commit
8cc0695ee9
14 changed files with 177 additions and 186 deletions
|
@ -13,18 +13,18 @@ import { DeleteServerButtonProps } from '../servers/DeleteServerButton';
|
||||||
import { ServerWithId } from '../servers/data';
|
import { ServerWithId } from '../servers/data';
|
||||||
import './AsideMenu.scss';
|
import './AsideMenu.scss';
|
||||||
|
|
||||||
interface AsideMenuProps {
|
export interface AsideMenuProps {
|
||||||
selectedServer: ServerWithId;
|
selectedServer: ServerWithId;
|
||||||
className?: string;
|
className?: string;
|
||||||
showOnMobile?: boolean;
|
showOnMobile?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface AsideMenuItemItemProps extends NavLinkProps {
|
interface AsideMenuItemProps extends NavLinkProps {
|
||||||
to: string;
|
to: string;
|
||||||
className?: string;
|
className?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const AsideMenuItem: FC<AsideMenuItemItemProps> = ({ children, to, className, ...rest }) => (
|
const AsideMenuItem: FC<AsideMenuItemProps> = ({ children, to, className, ...rest }) => (
|
||||||
<NavLink
|
<NavLink
|
||||||
className={classNames('aside-menu__item', className)}
|
className={classNames('aside-menu__item', className)}
|
||||||
activeClassName="aside-menu__item--selected"
|
activeClassName="aside-menu__item--selected"
|
||||||
|
|
|
@ -1,30 +1,31 @@
|
||||||
import React from 'react';
|
import React, { ReactNode } from 'react';
|
||||||
import * as PropTypes from 'prop-types';
|
|
||||||
import './ErrorHandler.scss';
|
|
||||||
import { Button } from 'reactstrap';
|
import { Button } from 'reactstrap';
|
||||||
|
import './ErrorHandler.scss';
|
||||||
|
|
||||||
// FIXME Replace with typescript: (window, console)
|
interface ErrorHandlerState {
|
||||||
const ErrorHandler = ({ location }, { error }) => class ErrorHandler extends React.Component {
|
hasError: boolean;
|
||||||
static propTypes = {
|
}
|
||||||
children: PropTypes.node.isRequired,
|
|
||||||
};
|
|
||||||
|
|
||||||
constructor(props) {
|
const ErrorHandler = (
|
||||||
|
{ location }: Window,
|
||||||
|
{ error }: Console,
|
||||||
|
) => class ErrorHandler extends React.Component<any, ErrorHandlerState> {
|
||||||
|
public constructor(props: object) {
|
||||||
super(props);
|
super(props);
|
||||||
this.state = { hasError: false };
|
this.state = { hasError: false };
|
||||||
}
|
}
|
||||||
|
|
||||||
static getDerivedStateFromError() {
|
public static getDerivedStateFromError(): ErrorHandlerState {
|
||||||
return { hasError: true };
|
return { hasError: true };
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidCatch(e) {
|
public componentDidCatch(e: Error): void {
|
||||||
if (process.env.NODE_ENV !== 'development') {
|
if (process.env.NODE_ENV !== 'development') {
|
||||||
error(e);
|
error(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
public render(): ReactNode | undefined {
|
||||||
if (this.state.hasError) {
|
if (this.state.hasError) {
|
||||||
return (
|
return (
|
||||||
<div className="error-handler">
|
<div className="error-handler">
|
|
@ -2,8 +2,8 @@ import React, { useEffect } from 'react';
|
||||||
import { isEmpty, values } from 'ramda';
|
import { isEmpty, values } from 'ramda';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
import ServersListGroup from '../servers/ServersListGroup';
|
import ServersListGroup from '../servers/ServersListGroup';
|
||||||
import { ServersMap } from '../servers/reducers/servers';
|
|
||||||
import './Home.scss';
|
import './Home.scss';
|
||||||
|
import { ServersMap } from '../servers/data';
|
||||||
|
|
||||||
export interface HomeProps {
|
export interface HomeProps {
|
||||||
resetSelectedServer: Function;
|
resetSelectedServer: Function;
|
||||||
|
|
|
@ -1,98 +0,0 @@
|
||||||
import React, { useEffect } from 'react';
|
|
||||||
import { Route, Switch } from 'react-router-dom';
|
|
||||||
import { Swipeable } from 'react-swipeable';
|
|
||||||
import { faBars as burgerIcon } from '@fortawesome/free-solid-svg-icons';
|
|
||||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
|
||||||
import classNames from 'classnames';
|
|
||||||
import * as PropTypes from 'prop-types';
|
|
||||||
import { serverType } from '../servers/prop-types';
|
|
||||||
import { withSelectedServer } from '../servers/helpers/withSelectedServer';
|
|
||||||
import { useToggle } from '../utils/helpers/hooks';
|
|
||||||
import { versionMatch } from '../utils/helpers/version';
|
|
||||||
import NotFound from './NotFound';
|
|
||||||
import './MenuLayout.scss';
|
|
||||||
|
|
||||||
const propTypes = {
|
|
||||||
match: PropTypes.object,
|
|
||||||
location: PropTypes.object,
|
|
||||||
selectedServer: serverType,
|
|
||||||
};
|
|
||||||
|
|
||||||
const MenuLayout = (
|
|
||||||
TagsList,
|
|
||||||
ShortUrls,
|
|
||||||
AsideMenu,
|
|
||||||
CreateShortUrl,
|
|
||||||
ShortUrlVisits,
|
|
||||||
TagVisits,
|
|
||||||
ShlinkVersions,
|
|
||||||
ServerError,
|
|
||||||
) => {
|
|
||||||
const MenuLayoutComp = ({ match, location, selectedServer }) => {
|
|
||||||
const [ sidebarVisible, toggleSidebar, showSidebar, hideSidebar ] = useToggle();
|
|
||||||
const { params: { serverId } } = match;
|
|
||||||
|
|
||||||
useEffect(() => hideSidebar(), [ location ]);
|
|
||||||
|
|
||||||
if (selectedServer.serverNotReachable) {
|
|
||||||
return <ServerError type="not-reachable" />;
|
|
||||||
}
|
|
||||||
|
|
||||||
const addTagsVisitsRoute = versionMatch(selectedServer.version, { minVersion: '2.2.0' });
|
|
||||||
const burgerClasses = classNames('menu-layout__burger-icon', {
|
|
||||||
'menu-layout__burger-icon--active': sidebarVisible,
|
|
||||||
});
|
|
||||||
const swipeMenuIfNoModalExists = (callback) => (e) => {
|
|
||||||
const swippedOnVisitsTable = e.event.path.some(
|
|
||||||
({ classList }) => classList && classList.contains('visits-table'),
|
|
||||||
);
|
|
||||||
|
|
||||||
if (swippedOnVisitsTable || document.querySelector('.modal')) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
callback();
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<React.Fragment>
|
|
||||||
<FontAwesomeIcon icon={burgerIcon} className={burgerClasses} onClick={toggleSidebar} />
|
|
||||||
|
|
||||||
<Swipeable
|
|
||||||
delta={40}
|
|
||||||
className="menu-layout__swipeable"
|
|
||||||
onSwipedLeft={swipeMenuIfNoModalExists(hideSidebar)}
|
|
||||||
onSwipedRight={swipeMenuIfNoModalExists(showSidebar)}
|
|
||||||
>
|
|
||||||
<div className="row menu-layout__swipeable-inner">
|
|
||||||
<AsideMenu className="col-lg-2 col-md-3" selectedServer={selectedServer} showOnMobile={sidebarVisible} />
|
|
||||||
<div className="col-lg-10 offset-lg-2 col-md-9 offset-md-3" onClick={() => hideSidebar()}>
|
|
||||||
<div className="menu-layout__container">
|
|
||||||
<Switch>
|
|
||||||
<Route exact path="/server/:serverId/list-short-urls/:page" component={ShortUrls} />
|
|
||||||
<Route exact path="/server/:serverId/create-short-url" component={CreateShortUrl} />
|
|
||||||
<Route exact path="/server/:serverId/short-code/:shortCode/visits" component={ShortUrlVisits} />
|
|
||||||
{addTagsVisitsRoute && <Route exact path="/server/:serverId/tag/:tag/visits" component={TagVisits} />}
|
|
||||||
<Route exact path="/server/:serverId/manage-tags" component={TagsList} />
|
|
||||||
<Route
|
|
||||||
render={() => <NotFound to={`/server/${serverId}/list-short-urls/1`}>List short URLs</NotFound>}
|
|
||||||
/>
|
|
||||||
</Switch>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="menu-layout__footer text-center text-md-right">
|
|
||||||
<ShlinkVersions />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Swipeable>
|
|
||||||
</React.Fragment>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
MenuLayoutComp.propTypes = propTypes;
|
|
||||||
|
|
||||||
return withSelectedServer(MenuLayoutComp, ServerError);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default MenuLayout;
|
|
89
src/common/MenuLayout.tsx
Normal file
89
src/common/MenuLayout.tsx
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
import React, { FC, useEffect } from 'react';
|
||||||
|
import { Route, RouteChildrenProps, Switch } from 'react-router-dom';
|
||||||
|
import { EventData, Swipeable } from 'react-swipeable';
|
||||||
|
import { faBars as burgerIcon } from '@fortawesome/free-solid-svg-icons';
|
||||||
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
import { withSelectedServer } from '../servers/helpers/withSelectedServer';
|
||||||
|
import { useToggle } from '../utils/helpers/hooks';
|
||||||
|
import { versionMatch } from '../utils/helpers/version';
|
||||||
|
import { isReachableServer, SelectedServer } from '../servers/data';
|
||||||
|
import NotFound from './NotFound';
|
||||||
|
import { AsideMenuProps } from './AsideMenu';
|
||||||
|
import './MenuLayout.scss';
|
||||||
|
|
||||||
|
interface MenuLayoutProps extends RouteChildrenProps {
|
||||||
|
selectedServer: SelectedServer;
|
||||||
|
}
|
||||||
|
|
||||||
|
const MenuLayout = (
|
||||||
|
TagsList: FC,
|
||||||
|
ShortUrls: FC,
|
||||||
|
AsideMenu: FC<AsideMenuProps>,
|
||||||
|
CreateShortUrl: FC,
|
||||||
|
ShortUrlVisits: FC,
|
||||||
|
TagVisits: FC,
|
||||||
|
ShlinkVersions: FC,
|
||||||
|
ServerError: FC,
|
||||||
|
) => withSelectedServer(({ location, selectedServer }: MenuLayoutProps) => {
|
||||||
|
const [ sidebarVisible, toggleSidebar, showSidebar, hideSidebar ] = useToggle();
|
||||||
|
|
||||||
|
useEffect(() => hideSidebar(), [ location ]);
|
||||||
|
|
||||||
|
if (!isReachableServer(selectedServer)) {
|
||||||
|
return <ServerError />;
|
||||||
|
}
|
||||||
|
|
||||||
|
const addTagsVisitsRoute = versionMatch(selectedServer.version, { minVersion: '2.2.0' });
|
||||||
|
const burgerClasses = classNames('menu-layout__burger-icon', {
|
||||||
|
'menu-layout__burger-icon--active': sidebarVisible,
|
||||||
|
});
|
||||||
|
const swipeMenuIfNoModalExists = (callback: () => void) => (e: EventData) => {
|
||||||
|
const swippedOnVisitsTable = (e.event.composedPath() as HTMLElement[]).some(
|
||||||
|
({ classList }) => classList?.contains('visits-table'),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (swippedOnVisitsTable || document.querySelector('.modal')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
callback();
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<React.Fragment>
|
||||||
|
<FontAwesomeIcon icon={burgerIcon} className={burgerClasses} onClick={toggleSidebar} />
|
||||||
|
|
||||||
|
<Swipeable
|
||||||
|
delta={40}
|
||||||
|
className="menu-layout__swipeable"
|
||||||
|
onSwipedLeft={swipeMenuIfNoModalExists(hideSidebar)}
|
||||||
|
onSwipedRight={swipeMenuIfNoModalExists(showSidebar)}
|
||||||
|
>
|
||||||
|
<div className="row menu-layout__swipeable-inner">
|
||||||
|
<AsideMenu className="col-lg-2 col-md-3" selectedServer={selectedServer} showOnMobile={sidebarVisible} />
|
||||||
|
<div className="col-lg-10 offset-lg-2 col-md-9 offset-md-3" onClick={() => hideSidebar()}>
|
||||||
|
<div className="menu-layout__container">
|
||||||
|
<Switch>
|
||||||
|
<Route exact path="/server/:serverId/list-short-urls/:page" component={ShortUrls} />
|
||||||
|
<Route exact path="/server/:serverId/create-short-url" component={CreateShortUrl} />
|
||||||
|
<Route exact path="/server/:serverId/short-code/:shortCode/visits" component={ShortUrlVisits} />
|
||||||
|
{addTagsVisitsRoute && <Route exact path="/server/:serverId/tag/:tag/visits" component={TagVisits} />}
|
||||||
|
<Route exact path="/server/:serverId/manage-tags" component={TagsList} />
|
||||||
|
<Route
|
||||||
|
render={() => <NotFound to={`/server/${selectedServer.id}/list-short-urls/1`}>List short URLs</NotFound>}
|
||||||
|
/>
|
||||||
|
</Switch>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="menu-layout__footer text-center text-md-right">
|
||||||
|
<ShlinkVersions />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Swipeable>
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
}, ServerError);
|
||||||
|
|
||||||
|
export default MenuLayout;
|
|
@ -1,6 +1,5 @@
|
||||||
import { MercureInfo } from '../mercure/reducers/mercureInfo';
|
import { MercureInfo } from '../mercure/reducers/mercureInfo';
|
||||||
import { ServersMap } from '../servers/reducers/servers';
|
import { SelectedServer, ServersMap } from '../servers/data';
|
||||||
import { SelectedServer } from '../servers/data';
|
|
||||||
import { Settings } from '../settings/reducers/settings';
|
import { Settings } from '../settings/reducers/settings';
|
||||||
import { ShortUrlMetaEdition } from '../short-urls/reducers/shortUrlMeta';
|
import { ShortUrlMetaEdition } from '../short-urls/reducers/shortUrlMeta';
|
||||||
import { ShortUrlCreation } from '../short-urls/reducers/shortUrlCreation';
|
import { ShortUrlCreation } from '../short-urls/reducers/shortUrlCreation';
|
||||||
|
|
|
@ -25,8 +25,13 @@ export type RegularServer = ReachableServer | NonReachableServer;
|
||||||
|
|
||||||
export type SelectedServer = RegularServer | NotFoundServer | null;
|
export type SelectedServer = RegularServer | NotFoundServer | null;
|
||||||
|
|
||||||
|
export type ServersMap = Record<string, ServerWithId>;
|
||||||
|
|
||||||
export const hasServerData = (server: ServerData | NotFoundServer | null): server is ServerData =>
|
export const hasServerData = (server: ServerData | NotFoundServer | null): server is ServerData =>
|
||||||
!!(server as ServerData)?.url && !!(server as ServerData)?.apiKey;
|
!!(server as ServerData)?.url && !!(server as ServerData)?.apiKey;
|
||||||
|
|
||||||
export const isReachableServer = (server: SelectedServer): server is ReachableServer =>
|
export const isReachableServer = (server: SelectedServer): server is ReachableServer =>
|
||||||
!!server?.hasOwnProperty('printableVersion');
|
!!server?.hasOwnProperty('printableVersion');
|
||||||
|
|
||||||
|
export const isServerWithId = (server: SelectedServer | ServerWithId): server is ServerWithId =>
|
||||||
|
!!server?.hasOwnProperty('id');
|
||||||
|
|
|
@ -1,50 +0,0 @@
|
||||||
import React from 'react';
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import { Link } from 'react-router-dom';
|
|
||||||
import Message from '../../utils/Message';
|
|
||||||
import ServersListGroup from '../ServersListGroup';
|
|
||||||
import { serverType } from '../prop-types';
|
|
||||||
import './ServerError.scss';
|
|
||||||
|
|
||||||
const propTypes = {
|
|
||||||
servers: PropTypes.object,
|
|
||||||
selectedServer: serverType,
|
|
||||||
type: PropTypes.oneOf([ 'not-found', 'not-reachable' ]).isRequired,
|
|
||||||
};
|
|
||||||
|
|
||||||
export const ServerError = (DeleteServerButton) => {
|
|
||||||
const ServerErrorComp = ({ type, servers, selectedServer }) => (
|
|
||||||
<div className="server-error__container flex-column">
|
|
||||||
<div className="row w-100 mb-3 mb-md-5">
|
|
||||||
<Message type="error">
|
|
||||||
{type === 'not-found' && 'Could not find this Shlink server.'}
|
|
||||||
{type === 'not-reachable' && (
|
|
||||||
<React.Fragment>
|
|
||||||
<p>Oops! Could not connect to this Shlink server.</p>
|
|
||||||
Make sure you have internet connection, and the server is properly configured and on-line.
|
|
||||||
</React.Fragment>
|
|
||||||
)}
|
|
||||||
</Message>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<ServersListGroup servers={Object.values(servers)}>
|
|
||||||
These are the Shlink servers currently configured. Choose one of
|
|
||||||
them or <Link to="/server/create">add a new one</Link>.
|
|
||||||
</ServersListGroup>
|
|
||||||
|
|
||||||
{type === 'not-reachable' && (
|
|
||||||
<div className="container mt-3 mt-md-5">
|
|
||||||
<h5>
|
|
||||||
Alternatively, if you think you may have miss-configured this server, you
|
|
||||||
can <DeleteServerButton server={selectedServer} className="server-error__delete-btn">remove it</DeleteServerButton> or
|
|
||||||
<Link to={`/server/${selectedServer.id}/edit`}>edit it</Link>.
|
|
||||||
</h5>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
|
|
||||||
ServerErrorComp.propTypes = propTypes;
|
|
||||||
|
|
||||||
return ServerErrorComp;
|
|
||||||
};
|
|
45
src/servers/helpers/ServerError.tsx
Normal file
45
src/servers/helpers/ServerError.tsx
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
import React, { FC } from 'react';
|
||||||
|
import { Link } from 'react-router-dom';
|
||||||
|
import Message from '../../utils/Message';
|
||||||
|
import ServersListGroup from '../ServersListGroup';
|
||||||
|
import { DeleteServerButtonProps } from '../DeleteServerButton';
|
||||||
|
import { isServerWithId, SelectedServer, ServersMap } from '../data';
|
||||||
|
import './ServerError.scss';
|
||||||
|
|
||||||
|
interface ServerErrorProps {
|
||||||
|
servers: ServersMap;
|
||||||
|
selectedServer: SelectedServer;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ServerError = (DeleteServerButton: FC<DeleteServerButtonProps>): FC<ServerErrorProps> => (
|
||||||
|
{ servers, selectedServer },
|
||||||
|
) => (
|
||||||
|
<div className="server-error__container flex-column">
|
||||||
|
<div className="row w-100 mb-3 mb-md-5">
|
||||||
|
<Message type="error">
|
||||||
|
{!isServerWithId(selectedServer) && 'Could not find this Shlink server.'}
|
||||||
|
{isServerWithId(selectedServer) && (
|
||||||
|
<React.Fragment>
|
||||||
|
<p>Oops! Could not connect to this Shlink server.</p>
|
||||||
|
Make sure you have internet connection, and the server is properly configured and on-line.
|
||||||
|
</React.Fragment>
|
||||||
|
)}
|
||||||
|
</Message>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ServersListGroup servers={Object.values(servers)}>
|
||||||
|
These are the Shlink servers currently configured. Choose one of
|
||||||
|
them or <Link to="/server/create">add a new one</Link>.
|
||||||
|
</ServersListGroup>
|
||||||
|
|
||||||
|
{isServerWithId(selectedServer) && (
|
||||||
|
<div className="container mt-3 mt-md-5">
|
||||||
|
<h5>
|
||||||
|
Alternatively, if you think you may have miss-configured this server, you
|
||||||
|
can <DeleteServerButton server={selectedServer} className="server-error__delete-btn">remove it</DeleteServerButton> or
|
||||||
|
<Link to={`/server/${selectedServer.id}/edit`}>edit it</Link>.
|
||||||
|
</h5>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
|
@ -23,7 +23,7 @@ export const withSelectedServer = (WrappedComponent, ServerError) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (selectedServer.serverNotFound) {
|
if (selectedServer.serverNotFound) {
|
||||||
return <ServerError type="not-found" />;
|
return <ServerError />;
|
||||||
}
|
}
|
||||||
|
|
||||||
return <WrappedComponent {...props} />;
|
return <WrappedComponent {...props} />;
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { pipe, assoc, map, reduce, dissoc } from 'ramda';
|
import { assoc, dissoc, map, pipe, reduce } from 'ramda';
|
||||||
import { v4 as uuid } from 'uuid';
|
import { v4 as uuid } from 'uuid';
|
||||||
import { Action } from 'redux';
|
import { Action } from 'redux';
|
||||||
import { ServerData, ServerWithId } from '../data';
|
import { ServerData, ServersMap, ServerWithId } from '../data';
|
||||||
import { buildReducer } from '../../utils/helpers/redux';
|
import { buildReducer } from '../../utils/helpers/redux';
|
||||||
|
|
||||||
/* eslint-disable padding-line-between-statements */
|
/* eslint-disable padding-line-between-statements */
|
||||||
|
@ -10,8 +10,6 @@ export const DELETE_SERVER = 'shlink/servers/DELETE_SERVER';
|
||||||
export const CREATE_SERVERS = 'shlink/servers/CREATE_SERVERS';
|
export const CREATE_SERVERS = 'shlink/servers/CREATE_SERVERS';
|
||||||
/* eslint-enable padding-line-between-statements */
|
/* eslint-enable padding-line-between-statements */
|
||||||
|
|
||||||
export type ServersMap = Record<string, ServerWithId>;
|
|
||||||
|
|
||||||
export interface CreateServersAction extends Action<string> {
|
export interface CreateServersAction extends Action<string> {
|
||||||
newServers: ServersMap;
|
newServers: ServersMap;
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,7 @@ const ShortUrlVisitsHeader = ({ shortUrlDetail, shortUrlVisits, goBack }) => {
|
||||||
const shortLink = shortUrl && shortUrl.shortUrl ? shortUrl.shortUrl : '';
|
const shortLink = shortUrl && shortUrl.shortUrl ? shortUrl.shortUrl : '';
|
||||||
const longLink = shortUrl && shortUrl.longUrl ? shortUrl.longUrl : '';
|
const longLink = shortUrl && shortUrl.longUrl ? shortUrl.longUrl : '';
|
||||||
|
|
||||||
const renderDate = () => (
|
const renderDate = () => !shortUrl ? <small>Loading...</small> : (
|
||||||
<span>
|
<span>
|
||||||
<b id="created" className="short-url-visits-header__created-at">
|
<b id="created" className="short-url-visits-header__created-at">
|
||||||
<Moment fromNow>{shortUrl.dateCreated}</Moment>
|
<Moment fromNow>{shortUrl.dateCreated}</Moment>
|
||||||
|
|
|
@ -1,16 +1,17 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { shallow } from 'enzyme';
|
import { shallow, ShallowWrapper } from 'enzyme';
|
||||||
import { Button } from 'reactstrap';
|
import { Button } from 'reactstrap';
|
||||||
|
import { Mock } from 'ts-mockery';
|
||||||
import createErrorHandler from '../../src/common/ErrorHandler';
|
import createErrorHandler from '../../src/common/ErrorHandler';
|
||||||
|
|
||||||
describe('<ErrorHandler />', () => {
|
describe('<ErrorHandler />', () => {
|
||||||
const window = {
|
const window = Mock.of<Window>({
|
||||||
location: {
|
location: {
|
||||||
reload: jest.fn(),
|
reload: jest.fn(),
|
||||||
},
|
},
|
||||||
};
|
});
|
||||||
const console = { error: jest.fn() };
|
const console = Mock.of<Console>({ error: jest.fn() });
|
||||||
let wrapper;
|
let wrapper: ShallowWrapper;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
const ErrorHandler = createErrorHandler(window, console);
|
const ErrorHandler = createErrorHandler(window, console);
|
|
@ -1,18 +1,19 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { shallow } from 'enzyme';
|
import { shallow, ShallowWrapper } from 'enzyme';
|
||||||
import { BrowserRouter } from 'react-router-dom';
|
import { BrowserRouter } from 'react-router-dom';
|
||||||
|
import { Mock } from 'ts-mockery';
|
||||||
import { ServerError as createServerError } from '../../../src/servers/helpers/ServerError';
|
import { ServerError as createServerError } from '../../../src/servers/helpers/ServerError';
|
||||||
|
import { NonReachableServer, NotFoundServer } from '../../../src/servers/data';
|
||||||
|
|
||||||
describe('<ServerError />', () => {
|
describe('<ServerError />', () => {
|
||||||
let wrapper;
|
let wrapper: ShallowWrapper;
|
||||||
const selectedServer = { id: '' };
|
const ServerError = createServerError(() => null);
|
||||||
const ServerError = createServerError(() => '');
|
|
||||||
|
|
||||||
afterEach(() => wrapper && wrapper.unmount());
|
afterEach(() => wrapper?.unmount());
|
||||||
|
|
||||||
it.each([
|
it.each([
|
||||||
[
|
[
|
||||||
'not-found',
|
Mock.all<NotFoundServer>(),
|
||||||
{
|
{
|
||||||
'Could not find this Shlink server.': true,
|
'Could not find this Shlink server.': true,
|
||||||
'Oops! Could not connect to this Shlink server.': false,
|
'Oops! Could not connect to this Shlink server.': false,
|
||||||
|
@ -21,7 +22,7 @@ describe('<ServerError />', () => {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'not-reachable',
|
Mock.of<NonReachableServer>({ id: 'abc123' }),
|
||||||
{
|
{
|
||||||
'Could not find this Shlink server.': false,
|
'Could not find this Shlink server.': false,
|
||||||
'Oops! Could not connect to this Shlink server.': true,
|
'Oops! Could not connect to this Shlink server.': true,
|
||||||
|
@ -29,10 +30,10 @@ describe('<ServerError />', () => {
|
||||||
'Alternatively, if you think you may have miss-configured this server': true,
|
'Alternatively, if you think you may have miss-configured this server': true,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
])('renders expected information for type "%s"', (type, textsToFind) => {
|
])('renders expected information based on provided server type', (selectedServer, textsToFind) => {
|
||||||
wrapper = shallow(
|
wrapper = shallow(
|
||||||
<BrowserRouter>
|
<BrowserRouter>
|
||||||
<ServerError type={type} servers={{}} selectedServer={selectedServer} />
|
<ServerError servers={{}} selectedServer={selectedServer} />
|
||||||
</BrowserRouter>,
|
</BrowserRouter>,
|
||||||
);
|
);
|
||||||
const wrapperText = wrapper.html();
|
const wrapperText = wrapper.html();
|
Loading…
Reference in a new issue