mirror of
https://github.com/shlinkio/shlink-web-client.git
synced 2025-01-09 01:37:24 +03:00
Merge pull request #639 from acelaya-forks/feature/more-rtl-tests
Feature/more rtl tests
This commit is contained in:
commit
116c36febc
20 changed files with 832 additions and 884 deletions
1157
package-lock.json
generated
1157
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -20,7 +20,7 @@
|
||||||
"test:coverage": "npm run test -- --coverage --coverageReporters=text --coverageReporters=text-summary",
|
"test:coverage": "npm run test -- --coverage --coverageReporters=text --coverageReporters=text-summary",
|
||||||
"test:ci": "npm run test:coverage -- --coverageReporters=clover",
|
"test:ci": "npm run test:coverage -- --coverageReporters=clover",
|
||||||
"test:pretty": "npm run test:coverage -- --coverageReporters=html",
|
"test:pretty": "npm run test:coverage -- --coverageReporters=html",
|
||||||
"mutate": "./node_modules/.bin/stryker run --concurrency 4"
|
"mutate": "./node_modules/.bin/stryker run --concurrency 4 --ignoreStatic"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fortawesome/fontawesome-free": "^6.0.0",
|
"@fortawesome/fontawesome-free": "^6.0.0",
|
||||||
|
@ -68,9 +68,9 @@
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@shlinkio/eslint-config-js-coding-standard": "~2.0.0",
|
"@shlinkio/eslint-config-js-coding-standard": "~2.0.0",
|
||||||
"@stryker-mutator/core": "^5.6.1",
|
"@stryker-mutator/core": "^6.0.2",
|
||||||
"@stryker-mutator/jest-runner": "^5.6.1",
|
"@stryker-mutator/jest-runner": "^6.0.2",
|
||||||
"@stryker-mutator/typescript-checker": "^5.6.1",
|
"@stryker-mutator/typescript-checker": "^6.0.2",
|
||||||
"@testing-library/jest-dom": "^5.16.4",
|
"@testing-library/jest-dom": "^5.16.4",
|
||||||
"@testing-library/react": "^13.1.1",
|
"@testing-library/react": "^13.1.1",
|
||||||
"@types/classnames": "^2.3.1",
|
"@types/classnames": "^2.3.1",
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { useEffect, FC } from 'react';
|
import { useEffect, FC } from 'react';
|
||||||
import { Route, Routes, useLocation } from 'react-router-dom';
|
import { Route, Routes, useLocation } from 'react-router-dom';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import NotFound from '../common/NotFound';
|
import { NotFound } from '../common/NotFound';
|
||||||
import { ServersMap } from '../servers/data';
|
import { ServersMap } from '../servers/data';
|
||||||
import { Settings } from '../settings/reducers/settings';
|
import { Settings } from '../settings/reducers/settings';
|
||||||
import { changeThemeInMarkup } from '../utils/theme';
|
import { changeThemeInMarkup } from '../utils/theme';
|
||||||
|
@ -17,7 +17,7 @@ interface AppProps {
|
||||||
appUpdated: boolean;
|
appUpdated: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const App = (
|
export const App = (
|
||||||
MainHeader: FC,
|
MainHeader: FC,
|
||||||
Home: FC,
|
Home: FC,
|
||||||
MenuLayout: FC,
|
MenuLayout: FC,
|
||||||
|
@ -65,5 +65,3 @@ const App = (
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default App;
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import Bottle from 'bottlejs';
|
import Bottle from 'bottlejs';
|
||||||
import { appUpdateAvailable, resetAppUpdate } from '../reducers/appUpdates';
|
import { appUpdateAvailable, resetAppUpdate } from '../reducers/appUpdates';
|
||||||
import App from '../App';
|
import { App } from '../App';
|
||||||
import { ConnectDecorator } from '../../container/types';
|
import { ConnectDecorator } from '../../container/types';
|
||||||
|
|
||||||
const provideServices = (bottle: Bottle, connect: ConnectDecorator) => {
|
const provideServices = (bottle: Bottle, connect: ConnectDecorator) => {
|
||||||
|
|
|
@ -24,7 +24,7 @@ export const AppUpdateBanner: FC<AppUpdateBannerProps> = ({ isOpen, toggle, forc
|
||||||
<h4 className="mb-4">This app has just been updated!</h4>
|
<h4 className="mb-4">This app has just been updated!</h4>
|
||||||
<p className="mb-0">
|
<p className="mb-0">
|
||||||
Restart it to enjoy the new features.
|
Restart it to enjoy the new features.
|
||||||
<Button disabled={isUpdating} className="ms-2" color="secondary" size="sm" onClick={update}>
|
<Button role="button" disabled={isUpdating} className="ms-2" color="secondary" size="sm" onClick={update}>
|
||||||
{!isUpdating && <>Restart now <FontAwesomeIcon icon={reloadIcon} className="ms-1" /></>}
|
{!isUpdating && <>Restart now <FontAwesomeIcon icon={reloadIcon} className="ms-1" /></>}
|
||||||
{isUpdating && <>Restarting...</>}
|
{isUpdating && <>Restarting...</>}
|
||||||
</Button>
|
</Button>
|
||||||
|
|
|
@ -6,10 +6,10 @@ interface ErrorHandlerState {
|
||||||
hasError: boolean;
|
hasError: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ErrorHandlerCreator = (
|
export const ErrorHandler = (
|
||||||
{ location }: Window,
|
{ location }: Window,
|
||||||
{ error }: Console,
|
{ error }: Console,
|
||||||
) => class ErrorHandler extends Component<any, ErrorHandlerState> {
|
) => class extends Component<any, ErrorHandlerState> {
|
||||||
public constructor(props: object) {
|
public constructor(props: object) {
|
||||||
super(props);
|
super(props);
|
||||||
this.state = { hasError: false };
|
this.state = { hasError: false };
|
||||||
|
@ -44,5 +44,3 @@ const ErrorHandlerCreator = (
|
||||||
return children;
|
return children;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export default ErrorHandlerCreator;
|
|
||||||
|
|
|
@ -10,11 +10,11 @@ import { ServersMap } from '../servers/data';
|
||||||
import { ShlinkLogo } from './img/ShlinkLogo';
|
import { ShlinkLogo } from './img/ShlinkLogo';
|
||||||
import './Home.scss';
|
import './Home.scss';
|
||||||
|
|
||||||
export interface HomeProps {
|
interface HomeProps {
|
||||||
servers: ServersMap;
|
servers: ServersMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Home = ({ servers }: HomeProps) => {
|
export const Home = ({ servers }: HomeProps) => {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const serversList = values(servers);
|
const serversList = values(servers);
|
||||||
const hasServers = !isEmpty(serversList);
|
const hasServers = !isEmpty(serversList);
|
||||||
|
@ -65,5 +65,3 @@ const Home = ({ servers }: HomeProps) => {
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Home;
|
|
||||||
|
|
|
@ -8,7 +8,7 @@ import { useToggle } from '../utils/helpers/hooks';
|
||||||
import { ShlinkLogo } from './img/ShlinkLogo';
|
import { ShlinkLogo } from './img/ShlinkLogo';
|
||||||
import './MainHeader.scss';
|
import './MainHeader.scss';
|
||||||
|
|
||||||
const MainHeader = (ServersDropdown: FC) => () => {
|
export const MainHeader = (ServersDropdown: FC) => () => {
|
||||||
const [isOpen, toggleOpen, , close] = useToggle();
|
const [isOpen, toggleOpen, , close] = useToggle();
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
const { pathname } = location;
|
const { pathname } = location;
|
||||||
|
@ -41,5 +41,3 @@ const MainHeader = (ServersDropdown: FC) => () => {
|
||||||
</Navbar>
|
</Navbar>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default MainHeader;
|
|
||||||
|
|
|
@ -7,7 +7,7 @@ import { withSelectedServer } from '../servers/helpers/withSelectedServer';
|
||||||
import { useSwipeable, useToggle } from '../utils/helpers/hooks';
|
import { useSwipeable, useToggle } from '../utils/helpers/hooks';
|
||||||
import { supportsDomainRedirects, supportsDomainVisits, supportsNonOrphanVisits } from '../utils/helpers/features';
|
import { supportsDomainRedirects, supportsDomainVisits, supportsNonOrphanVisits } from '../utils/helpers/features';
|
||||||
import { isReachableServer } from '../servers/data';
|
import { isReachableServer } from '../servers/data';
|
||||||
import NotFound from './NotFound';
|
import { NotFound } from './NotFound';
|
||||||
import { AsideMenuProps } from './AsideMenu';
|
import { AsideMenuProps } from './AsideMenu';
|
||||||
import './MenuLayout.scss';
|
import './MenuLayout.scss';
|
||||||
|
|
||||||
|
@ -16,7 +16,7 @@ interface MenuLayoutProps {
|
||||||
sidebarNotPresent: Function;
|
sidebarNotPresent: Function;
|
||||||
}
|
}
|
||||||
|
|
||||||
const MenuLayout = (
|
export const MenuLayout = (
|
||||||
TagsList: FC,
|
TagsList: FC,
|
||||||
ShortUrlsList: FC,
|
ShortUrlsList: FC,
|
||||||
AsideMenu: FC<AsideMenuProps>,
|
AsideMenu: FC<AsideMenuProps>,
|
||||||
|
@ -86,5 +86,3 @@ const MenuLayout = (
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}, ServerError);
|
}, ServerError);
|
||||||
|
|
||||||
export default MenuLayout;
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ import { SimpleCard } from '../utils/SimpleCard';
|
||||||
|
|
||||||
type NotFoundProps = PropsWithChildren<{ to?: string }>;
|
type NotFoundProps = PropsWithChildren<{ to?: string }>;
|
||||||
|
|
||||||
const NotFound: FC<NotFoundProps> = ({ to = '/', children = 'Home' }) => (
|
export const NotFound: FC<NotFoundProps> = ({ to = '/', children = 'Home' }) => (
|
||||||
<div className="home">
|
<div className="home">
|
||||||
<SimpleCard className="p-4">
|
<SimpleCard className="p-4">
|
||||||
<h2>Oops! We could not find requested route.</h2>
|
<h2>Oops! We could not find requested route.</h2>
|
||||||
|
@ -17,5 +17,3 @@ const NotFound: FC<NotFoundProps> = ({ to = '/', children = 'Home' }) => (
|
||||||
</SimpleCard>
|
</SimpleCard>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
export default NotFound;
|
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import Bottle from 'bottlejs';
|
import Bottle from 'bottlejs';
|
||||||
import ScrollToTop from '../ScrollToTop';
|
import ScrollToTop from '../ScrollToTop';
|
||||||
import MainHeader from '../MainHeader';
|
import { MainHeader } from '../MainHeader';
|
||||||
import Home from '../Home';
|
import { Home } from '../Home';
|
||||||
import MenuLayout from '../MenuLayout';
|
import { MenuLayout } from '../MenuLayout';
|
||||||
import AsideMenu from '../AsideMenu';
|
import AsideMenu from '../AsideMenu';
|
||||||
import ErrorHandler from '../ErrorHandler';
|
import { ErrorHandler } from '../ErrorHandler';
|
||||||
import ShlinkVersionsContainer from '../ShlinkVersionsContainer';
|
import ShlinkVersionsContainer from '../ShlinkVersionsContainer';
|
||||||
import { ConnectDecorator } from '../../container/types';
|
import { ConnectDecorator } from '../../container/types';
|
||||||
import { withoutSelectedServer } from '../../servers/helpers/withoutSelectedServer';
|
import { withoutSelectedServer } from '../../servers/helpers/withoutSelectedServer';
|
||||||
|
|
|
@ -1,71 +1,61 @@
|
||||||
import { shallow, ShallowWrapper } from 'enzyme';
|
import { render, screen } from '@testing-library/react';
|
||||||
import { Route, useLocation } from 'react-router-dom';
|
import { Router } from 'react-router-dom';
|
||||||
|
import { createMemoryHistory } from 'history';
|
||||||
import { Mock } from 'ts-mockery';
|
import { Mock } from 'ts-mockery';
|
||||||
import { Settings } from '../../src/settings/reducers/settings';
|
import { Settings } from '../../src/settings/reducers/settings';
|
||||||
import appFactory from '../../src/app/App';
|
import { App as createApp } from '../../src/app/App';
|
||||||
import { AppUpdateBanner } from '../../src/common/AppUpdateBanner';
|
|
||||||
|
|
||||||
jest.mock('react-router-dom', () => ({
|
|
||||||
...jest.requireActual('react-router-dom'),
|
|
||||||
useLocation: jest.fn().mockReturnValue({}),
|
|
||||||
}));
|
|
||||||
|
|
||||||
describe('<App />', () => {
|
describe('<App />', () => {
|
||||||
let wrapper: ShallowWrapper;
|
const App = createApp(
|
||||||
const MainHeader = () => null;
|
() => <>MainHeader</>,
|
||||||
const ShlinkVersions = () => null;
|
() => <>Home</>,
|
||||||
const App = appFactory(
|
() => <>MenuLayout</>,
|
||||||
MainHeader,
|
() => <>CreateServer</>,
|
||||||
() => null,
|
() => <>EditServer</>,
|
||||||
() => null,
|
() => <>SettingsComp</>,
|
||||||
() => null,
|
() => <>ManageServers</>,
|
||||||
() => null,
|
() => <>ShlinkVersions</>,
|
||||||
() => null,
|
|
||||||
() => null,
|
|
||||||
ShlinkVersions,
|
|
||||||
);
|
);
|
||||||
const createWrapper = () => {
|
const setUp = (activeRoute = '/') => {
|
||||||
wrapper = shallow(
|
const history = createMemoryHistory();
|
||||||
<App
|
history.push(activeRoute);
|
||||||
fetchServers={() => {}}
|
|
||||||
servers={{}}
|
|
||||||
settings={Mock.all<Settings>()}
|
|
||||||
appUpdated={false}
|
|
||||||
resetAppUpdate={() => {}}
|
|
||||||
/>,
|
|
||||||
);
|
|
||||||
|
|
||||||
return wrapper;
|
return render(
|
||||||
|
<Router location={history.location} navigator={history}>
|
||||||
|
<App
|
||||||
|
fetchServers={() => {}}
|
||||||
|
servers={{}}
|
||||||
|
settings={Mock.all<Settings>()}
|
||||||
|
appUpdated
|
||||||
|
resetAppUpdate={() => {}}
|
||||||
|
/>
|
||||||
|
</Router>,
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
afterEach(jest.clearAllMocks);
|
afterEach(jest.clearAllMocks);
|
||||||
afterEach(() => wrapper?.unmount());
|
|
||||||
|
|
||||||
it('renders children components', () => {
|
it('renders children components', () => {
|
||||||
const wrapper = createWrapper();
|
setUp();
|
||||||
|
|
||||||
expect(wrapper.find(MainHeader)).toHaveLength(1);
|
expect(screen.getByText('MainHeader')).toBeInTheDocument();
|
||||||
expect(wrapper.find(ShlinkVersions)).toHaveLength(1);
|
expect(screen.getByText('ShlinkVersions')).toBeInTheDocument();
|
||||||
expect(wrapper.find(AppUpdateBanner)).toHaveLength(1);
|
expect(screen.getByText('This app has just been updated!')).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('renders app main routes', () => {
|
it.each([
|
||||||
const wrapper = createWrapper();
|
['/settings/foo', 'SettingsComp'],
|
||||||
const routes = wrapper.find(Route);
|
['/settings/bar', 'SettingsComp'],
|
||||||
const expectedPaths = [
|
['/manage-servers', 'ManageServers'],
|
||||||
undefined,
|
['/server/create', 'CreateServer'],
|
||||||
'/settings/*',
|
['/server/abc123/edit', 'EditServer'],
|
||||||
'/manage-servers',
|
['/server/def456/edit', 'EditServer'],
|
||||||
'/server/create',
|
['/server/abc123/foo', 'MenuLayout'],
|
||||||
'/server/:serverId/edit',
|
['/server/def456/bar', 'MenuLayout'],
|
||||||
'/server/:serverId/*',
|
['/other', 'Oops! We could not find requested route.'],
|
||||||
];
|
])('renders expected route', async (activeRoute, expectedComponent) => {
|
||||||
|
setUp(activeRoute);
|
||||||
expect.assertions(expectedPaths.length + 1);
|
expect(await screen.findByText(expectedComponent)).toBeInTheDocument();
|
||||||
expect(routes).toHaveLength(expectedPaths.length + 1);
|
|
||||||
expectedPaths.forEach((path, index) => {
|
|
||||||
expect(routes.at(index).prop('path')).toEqual(path);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it.each([
|
it.each([
|
||||||
|
@ -73,11 +63,9 @@ describe('<App />', () => {
|
||||||
['/bar', 'shlink-wrapper'],
|
['/bar', 'shlink-wrapper'],
|
||||||
['/', 'shlink-wrapper d-flex d-md-block align-items-center'],
|
['/', 'shlink-wrapper d-flex d-md-block align-items-center'],
|
||||||
])('renders expected classes on shlink-wrapper based on current pathname', (pathname, expectedClasses) => {
|
])('renders expected classes on shlink-wrapper based on current pathname', (pathname, expectedClasses) => {
|
||||||
(useLocation as any).mockReturnValue({ pathname });
|
const { container } = setUp(pathname);
|
||||||
|
const shlinkWrapper = container.querySelector('.shlink-wrapper');
|
||||||
|
|
||||||
const wrapper = createWrapper();
|
expect(shlinkWrapper).toHaveAttribute('class', expectedClasses);
|
||||||
const shlinkWrapper = wrapper.find('.shlink-wrapper');
|
|
||||||
|
|
||||||
expect(shlinkWrapper.prop('className')).toEqual(expectedClasses);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,43 +1,31 @@
|
||||||
import { shallow, ShallowWrapper } from 'enzyme';
|
import { fireEvent, render, screen } from '@testing-library/react';
|
||||||
import { Button } from 'reactstrap';
|
|
||||||
import { AppUpdateBanner } from '../../src/common/AppUpdateBanner';
|
import { AppUpdateBanner } from '../../src/common/AppUpdateBanner';
|
||||||
import { SimpleCard } from '../../src/utils/SimpleCard';
|
|
||||||
|
|
||||||
describe('<AppUpdateBanner />', () => {
|
describe('<AppUpdateBanner />', () => {
|
||||||
const toggle = jest.fn();
|
const toggle = jest.fn();
|
||||||
const forceUpdate = jest.fn();
|
const forceUpdate = jest.fn();
|
||||||
let wrapper: ShallowWrapper;
|
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => render(<AppUpdateBanner isOpen toggle={toggle} forceUpdate={forceUpdate} />));
|
||||||
wrapper = shallow(<AppUpdateBanner isOpen toggle={toggle} forceUpdate={forceUpdate} />);
|
|
||||||
});
|
|
||||||
|
|
||||||
afterEach(jest.clearAllMocks);
|
afterEach(jest.clearAllMocks);
|
||||||
afterEach(() => wrapper?.unmount());
|
|
||||||
|
|
||||||
it('renders an alert with expected props', () => {
|
it('renders initial state', () => {
|
||||||
expect(wrapper.prop('className')).toEqual('app-update-banner');
|
expect(screen.getByRole('heading')).toHaveTextContent('This app has just been updated!');
|
||||||
expect(wrapper.prop('isOpen')).toEqual(true);
|
expect(screen.queryByText('Restarting...')).not.toBeInTheDocument();
|
||||||
expect(wrapper.prop('toggle')).toEqual(toggle);
|
expect(screen.getByText('Restart now')).not.toHaveAttribute('disabled');
|
||||||
expect(wrapper.prop('tag')).toEqual(SimpleCard);
|
|
||||||
expect(wrapper.prop('color')).toEqual('secondary');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('invokes toggle when alert is toggled', () => {
|
it('invokes toggle when alert is closed', () => {
|
||||||
(wrapper.prop('toggle') as Function)();
|
expect(toggle).not.toHaveBeenCalled();
|
||||||
|
fireEvent.click(screen.getByLabelText('Close'));
|
||||||
expect(toggle).toHaveBeenCalled();
|
expect(toggle).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('triggers the update when clicking the button', () => {
|
it('triggers the update when clicking the button', async () => {
|
||||||
expect(wrapper.find(Button).html()).toContain('Restart now');
|
|
||||||
expect(wrapper.find(Button).prop('disabled')).toEqual(false);
|
|
||||||
expect(forceUpdate).not.toHaveBeenCalled();
|
expect(forceUpdate).not.toHaveBeenCalled();
|
||||||
|
fireEvent.click(screen.getByText(/^Restart now/));
|
||||||
wrapper.find(Button).simulate('click');
|
|
||||||
|
|
||||||
expect(wrapper.find(Button).html()).toContain('Restarting...');
|
|
||||||
expect(wrapper.find(Button).prop('disabled')).toEqual(true);
|
|
||||||
expect(forceUpdate).toHaveBeenCalled();
|
expect(forceUpdate).toHaveBeenCalled();
|
||||||
|
expect(await screen.findByText('Restarting...')).toBeInTheDocument();
|
||||||
|
expect(screen.queryByText(/^Restart now/)).not.toBeInTheDocument();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,32 +1,41 @@
|
||||||
import { shallow, ShallowWrapper } from 'enzyme';
|
import { render, screen } from '@testing-library/react';
|
||||||
import { Mock } from 'ts-mockery';
|
import { Mock } from 'ts-mockery';
|
||||||
|
import { MemoryRouter } from 'react-router-dom';
|
||||||
import asideMenuCreator from '../../src/common/AsideMenu';
|
import asideMenuCreator from '../../src/common/AsideMenu';
|
||||||
import { ReachableServer } from '../../src/servers/data';
|
import { ReachableServer } from '../../src/servers/data';
|
||||||
|
import { SemVer } from '../../src/utils/helpers/version';
|
||||||
jest.mock('react-router-dom', () => ({
|
|
||||||
...jest.requireActual('react-router-dom'),
|
|
||||||
useLocation: jest.fn().mockReturnValue({ pathname: '' }),
|
|
||||||
}));
|
|
||||||
|
|
||||||
describe('<AsideMenu />', () => {
|
describe('<AsideMenu />', () => {
|
||||||
let wrapped: ShallowWrapper;
|
const AsideMenu = asideMenuCreator(() => <>DeleteServerButton</>);
|
||||||
const DeleteServerButton = () => null;
|
const setUp = (version: SemVer, id: string | false = 'abc123') => render(
|
||||||
|
<MemoryRouter>
|
||||||
|
<AsideMenu selectedServer={Mock.of<ReachableServer>({ id: id || undefined, version })} />
|
||||||
|
</MemoryRouter>,
|
||||||
|
);
|
||||||
|
|
||||||
beforeEach(() => {
|
it.each([
|
||||||
const AsideMenu = asideMenuCreator(DeleteServerButton);
|
['2.7.0' as SemVer, 5],
|
||||||
|
['2.8.0' as SemVer, 6],
|
||||||
|
])('contains links to different sections', (version, expectedAmountOfLinks) => {
|
||||||
|
setUp(version);
|
||||||
|
|
||||||
wrapped = shallow(<AsideMenu selectedServer={Mock.of<ReachableServer>({ id: 'abc123' })} />);
|
const links = screen.getAllByRole('link');
|
||||||
});
|
|
||||||
afterEach(() => wrapped.unmount());
|
|
||||||
|
|
||||||
it('contains links to different sections', () => {
|
expect.assertions(links.length + 1);
|
||||||
const links = wrapped.find('[to]');
|
expect(links).toHaveLength(expectedAmountOfLinks);
|
||||||
|
links.forEach((link) => expect(link.getAttribute('href')).toContain('abc123'));
|
||||||
expect(links).toHaveLength(5);
|
|
||||||
links.forEach((link) => expect(link.prop('to')).toContain('abc123'));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('contains a button to delete server', () => {
|
it.each([
|
||||||
expect(wrapped.find(DeleteServerButton)).toHaveLength(1);
|
['abc', true],
|
||||||
|
[false, false],
|
||||||
|
])('contains a button to delete server if appropriate', (id, shouldHaveBtn) => {
|
||||||
|
setUp('2.8.0', id as string | false);
|
||||||
|
|
||||||
|
if (shouldHaveBtn) {
|
||||||
|
expect(screen.getByText('DeleteServerButton')).toBeInTheDocument();
|
||||||
|
} else {
|
||||||
|
expect(screen.queryByText('DeleteServerButton')).not.toBeInTheDocument();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,38 +1,44 @@
|
||||||
import { shallow, ShallowWrapper } from 'enzyme';
|
import { fireEvent, render, screen } from '@testing-library/react';
|
||||||
import { Button } from 'reactstrap';
|
|
||||||
import { Mock } from 'ts-mockery';
|
import { Mock } from 'ts-mockery';
|
||||||
import createErrorHandler from '../../src/common/ErrorHandler';
|
import { ErrorHandler as createErrorHandler } from '../../src/common/ErrorHandler';
|
||||||
import { SimpleCard } from '../../src/utils/SimpleCard';
|
|
||||||
|
const ComponentWithError = () => {
|
||||||
|
throw new Error('Error!!');
|
||||||
|
};
|
||||||
|
|
||||||
describe('<ErrorHandler />', () => {
|
describe('<ErrorHandler />', () => {
|
||||||
|
const reload = jest.fn();
|
||||||
const window = Mock.of<Window>({
|
const window = Mock.of<Window>({
|
||||||
location: {
|
location: { reload },
|
||||||
reload: jest.fn(),
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
const console = Mock.of<Console>({ error: jest.fn() });
|
const cons = Mock.of<Console>({ error: jest.fn() });
|
||||||
let wrapper: ShallowWrapper;
|
const ErrorHandler = createErrorHandler(window, cons);
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
const ErrorHandler = createErrorHandler(window, console);
|
jest.spyOn(console, 'error').mockImplementation(() => {}); // Silence react errors
|
||||||
|
|
||||||
wrapper = shallow(<ErrorHandler children={<span>Foo</span>} />);
|
|
||||||
});
|
});
|
||||||
|
afterEach(jest.resetAllMocks);
|
||||||
afterEach(() => wrapper.unmount());
|
|
||||||
|
|
||||||
it('renders children when no error has occurred', () => {
|
it('renders children when no error has occurred', () => {
|
||||||
expect(wrapper.text()).toEqual('Foo');
|
render(<ErrorHandler children={<span>Foo</span>} />);
|
||||||
expect(wrapper.find(Button)).toHaveLength(0);
|
|
||||||
|
expect(screen.getByText('Foo')).toBeInTheDocument();
|
||||||
|
expect(screen.queryByText('Oops! This is awkward :S')).not.toBeInTheDocument();
|
||||||
|
expect(screen.queryByRole('button')).not.toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('renders error page when error has occurred', () => {
|
it('renders error page when error has occurred', () => {
|
||||||
wrapper.setState({ hasError: true });
|
render(<ErrorHandler children={<ComponentWithError />} />);
|
||||||
|
|
||||||
expect(wrapper.find(SimpleCard).contains('Oops! This is awkward :S')).toEqual(true);
|
expect(screen.getByText('Oops! This is awkward :S')).toBeInTheDocument();
|
||||||
expect(wrapper.find(SimpleCard).contains(
|
expect(screen.getByRole('button')).toBeInTheDocument();
|
||||||
'It seems that something went wrong. Try refreshing the page or just click this button.',
|
});
|
||||||
)).toEqual(true);
|
|
||||||
expect(wrapper.find(Button)).toHaveLength(1);
|
it('reloads page on button click', () => {
|
||||||
|
render(<ErrorHandler children={<ComponentWithError />} />);
|
||||||
|
|
||||||
|
expect(reload).not.toHaveBeenCalled();
|
||||||
|
fireEvent.click(screen.getByRole('button'));
|
||||||
|
expect(reload).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,31 +1,19 @@
|
||||||
import { shallow, ShallowWrapper } from 'enzyme';
|
import { render, screen } from '@testing-library/react';
|
||||||
import { Mock } from 'ts-mockery';
|
import { Mock } from 'ts-mockery';
|
||||||
import Home, { HomeProps } from '../../src/common/Home';
|
import { MemoryRouter } from 'react-router-dom';
|
||||||
import { ServerWithId } from '../../src/servers/data';
|
import { Home } from '../../src/common/Home';
|
||||||
import { ShlinkLogo } from '../../src/common/img/ShlinkLogo';
|
import { ServersMap, ServerWithId } from '../../src/servers/data';
|
||||||
|
|
||||||
jest.mock('react-router-dom', () => ({
|
|
||||||
...jest.requireActual('react-router-dom'),
|
|
||||||
useNavigate: jest.fn().mockReturnValue(jest.fn()),
|
|
||||||
}));
|
|
||||||
|
|
||||||
describe('<Home />', () => {
|
describe('<Home />', () => {
|
||||||
let wrapped: ShallowWrapper;
|
const setUp = (servers: ServersMap = {}) => render(
|
||||||
const createComponent = (props: Partial<HomeProps> = {}) => {
|
<MemoryRouter>
|
||||||
const actualProps = { resetSelectedServer: jest.fn(), servers: {}, ...props };
|
<Home servers={servers} />
|
||||||
|
</MemoryRouter>,
|
||||||
|
);
|
||||||
|
|
||||||
wrapped = shallow(<Home {...actualProps} />);
|
it('renders title', () => {
|
||||||
|
setUp();
|
||||||
return wrapped;
|
expect(screen.getByRole('heading', { name: 'Welcome!' })).toBeInTheDocument();
|
||||||
};
|
|
||||||
|
|
||||||
afterEach(() => wrapped?.unmount());
|
|
||||||
|
|
||||||
it('renders logo and title', () => {
|
|
||||||
const wrapped = createComponent();
|
|
||||||
|
|
||||||
expect(wrapped.find(ShlinkLogo)).toHaveLength(1);
|
|
||||||
expect(wrapped.find('.home__title')).toHaveLength(1);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it.each([
|
it.each([
|
||||||
|
@ -33,14 +21,20 @@ describe('<Home />', () => {
|
||||||
{
|
{
|
||||||
'1a': Mock.of<ServerWithId>({ name: 'foo', id: '1' }),
|
'1a': Mock.of<ServerWithId>({ name: 'foo', id: '1' }),
|
||||||
'2b': Mock.of<ServerWithId>({ name: 'bar', id: '2' }),
|
'2b': Mock.of<ServerWithId>({ name: 'bar', id: '2' }),
|
||||||
|
'3c': Mock.of<ServerWithId>({ name: 'baz', id: '3' }),
|
||||||
},
|
},
|
||||||
0,
|
3,
|
||||||
],
|
],
|
||||||
[{}, 3],
|
[{}, 2],
|
||||||
])('shows link to create or set-up server only when no servers exist', (servers, expectedParagraphs) => {
|
])('shows link to create or set-up server only when no servers exist', (servers, expectedServers) => {
|
||||||
const wrapped = createComponent({ servers });
|
setUp(servers);
|
||||||
const p = wrapped.find('p');
|
const links = screen.getAllByRole('link');
|
||||||
|
|
||||||
expect(p).toHaveLength(expectedParagraphs);
|
expect(links).toHaveLength(expectedServers);
|
||||||
|
|
||||||
|
if (Object.keys(servers).length === 0) {
|
||||||
|
expect(screen.getByText('This application will help you manage your Shlink servers.')).toBeInTheDocument();
|
||||||
|
expect(screen.getByText('Learn more about Shlink')).toBeInTheDocument();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,34 +1,24 @@
|
||||||
import { shallow, ShallowWrapper } from 'enzyme';
|
import { fireEvent, render, screen, waitFor } from '@testing-library/react';
|
||||||
import { useLocation } from 'react-router-dom';
|
import { Router } from 'react-router-dom';
|
||||||
import { Collapse, NavbarToggler, NavLink } from 'reactstrap';
|
import { createMemoryHistory } from 'history';
|
||||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
import { MainHeader as createMainHeader } from '../../src/common/MainHeader';
|
||||||
import createMainHeader from '../../src/common/MainHeader';
|
|
||||||
|
|
||||||
jest.mock('react-router-dom', () => ({
|
|
||||||
...jest.requireActual('react-router-dom'),
|
|
||||||
useLocation: jest.fn().mockReturnValue({}),
|
|
||||||
}));
|
|
||||||
|
|
||||||
describe('<MainHeader />', () => {
|
describe('<MainHeader />', () => {
|
||||||
const ServersDropdown = () => null;
|
const MainHeader = createMainHeader(() => <>ServersDropdown</>);
|
||||||
const MainHeader = createMainHeader(ServersDropdown);
|
const setUp = (pathname = '') => {
|
||||||
let wrapper: ShallowWrapper;
|
const history = createMemoryHistory();
|
||||||
|
history.push(pathname);
|
||||||
|
|
||||||
const createWrapper = (pathname = '') => {
|
return render(
|
||||||
(useLocation as any).mockReturnValue({ pathname });
|
<Router location={history.location} navigator={history}>
|
||||||
|
<MainHeader />
|
||||||
wrapper = shallow(<MainHeader />);
|
</Router>,
|
||||||
|
);
|
||||||
return wrapper;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
afterEach(jest.clearAllMocks);
|
|
||||||
afterEach(() => wrapper?.unmount());
|
|
||||||
|
|
||||||
it('renders ServersDropdown', () => {
|
it('renders ServersDropdown', () => {
|
||||||
const wrapper = createWrapper();
|
setUp();
|
||||||
|
expect(screen.getByText('ServersDropdown')).toBeInTheDocument();
|
||||||
expect(wrapper.find(ServersDropdown)).toHaveLength(1);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it.each([
|
it.each([
|
||||||
|
@ -38,31 +28,38 @@ describe('<MainHeader />', () => {
|
||||||
['/settings/foo', true],
|
['/settings/foo', true],
|
||||||
['/settings/bar', true],
|
['/settings/bar', true],
|
||||||
])('sets link to settings as active only when current path is settings', (currentPath, isActive) => {
|
])('sets link to settings as active only when current path is settings', (currentPath, isActive) => {
|
||||||
const wrapper = createWrapper(currentPath);
|
setUp(currentPath);
|
||||||
const settingsLink = wrapper.find(NavLink);
|
|
||||||
|
|
||||||
expect(settingsLink.prop('active')).toEqual(isActive);
|
if (isActive) {
|
||||||
|
expect(screen.getByText(/Settings$/).getAttribute('class')).toContain('active');
|
||||||
|
} else {
|
||||||
|
expect(screen.getByText(/Settings$/).getAttribute('class')).not.toContain('active');
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it('renders expected class based on the nav bar state', () => {
|
it('renders expected class based on the nav bar state', () => {
|
||||||
const wrapper = createWrapper();
|
setUp();
|
||||||
|
|
||||||
expect(wrapper.find(NavbarToggler).find(FontAwesomeIcon).prop('className')).toEqual('main-header__toggle-icon');
|
const toggle = screen.getByLabelText('Toggle navigation');
|
||||||
wrapper.find(NavbarToggler).simulate('click');
|
const icon = toggle.firstChild;
|
||||||
expect(wrapper.find(NavbarToggler).find(FontAwesomeIcon).prop('className')).toEqual(
|
|
||||||
'main-header__toggle-icon main-header__toggle-icon--opened',
|
expect(icon).toHaveAttribute('class', expect.stringMatching(/main-header__toggle-icon$/));
|
||||||
|
fireEvent.click(toggle);
|
||||||
|
expect(icon).toHaveAttribute(
|
||||||
|
'class',
|
||||||
|
expect.stringMatching(/main-header__toggle-icon main-header__toggle-icon--opened$/),
|
||||||
);
|
);
|
||||||
wrapper.find(NavbarToggler).simulate('click');
|
fireEvent.click(toggle);
|
||||||
expect(wrapper.find(NavbarToggler).find(FontAwesomeIcon).prop('className')).toEqual('main-header__toggle-icon');
|
expect(icon).toHaveAttribute('class', expect.stringMatching(/main-header__toggle-icon$/));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('opens Collapse when clicking toggle', () => {
|
it('opens Collapse when clicking toggle', async () => {
|
||||||
const wrapper = createWrapper();
|
const { container } = setUp();
|
||||||
|
const collapse = container.querySelector('.collapse');
|
||||||
|
const toggle = screen.getByLabelText('Toggle navigation');
|
||||||
|
|
||||||
expect(wrapper.find(Collapse).prop('isOpen')).toEqual(false);
|
expect(collapse).not.toHaveAttribute('class', expect.stringContaining('show'));
|
||||||
wrapper.find(NavbarToggler).simulate('click');
|
fireEvent.click(toggle);
|
||||||
expect(wrapper.find(Collapse).prop('isOpen')).toEqual(true);
|
await waitFor(() => expect(collapse).toHaveAttribute('class', expect.stringContaining('show')));
|
||||||
wrapper.find(NavbarToggler).simulate('click');
|
|
||||||
expect(wrapper.find(Collapse).prop('isOpen')).toEqual(false);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,70 +1,89 @@
|
||||||
import { shallow, ShallowWrapper } from 'enzyme';
|
import { render, screen } from '@testing-library/react';
|
||||||
import { Route, useParams } from 'react-router-dom';
|
import { Router, useParams } from 'react-router-dom';
|
||||||
|
import { createMemoryHistory } from 'history';
|
||||||
import { Mock } from 'ts-mockery';
|
import { Mock } from 'ts-mockery';
|
||||||
import createMenuLayout from '../../src/common/MenuLayout';
|
import { MenuLayout as createMenuLayout } from '../../src/common/MenuLayout';
|
||||||
import { NonReachableServer, NotFoundServer, ReachableServer, SelectedServer } from '../../src/servers/data';
|
import { NonReachableServer, NotFoundServer, ReachableServer, SelectedServer } from '../../src/servers/data';
|
||||||
import { NoMenuLayout } from '../../src/common/NoMenuLayout';
|
|
||||||
import { SemVer } from '../../src/utils/helpers/version';
|
import { SemVer } from '../../src/utils/helpers/version';
|
||||||
|
|
||||||
jest.mock('react-router-dom', () => ({
|
jest.mock('react-router-dom', () => ({ ...jest.requireActual('react-router-dom'), useParams: jest.fn() }));
|
||||||
...jest.requireActual('react-router-dom'),
|
|
||||||
useParams: jest.fn().mockReturnValue({}),
|
|
||||||
useLocation: jest.fn().mockReturnValue({}),
|
|
||||||
}));
|
|
||||||
|
|
||||||
describe('<MenuLayout />', () => {
|
describe('<MenuLayout />', () => {
|
||||||
const ServerError = jest.fn();
|
const MenuLayout = createMenuLayout(
|
||||||
const C = jest.fn();
|
() => <>TagsList</>,
|
||||||
const MenuLayout = createMenuLayout(C, C, C, C, C, C, C, C, C, ServerError, C, C, C);
|
() => <>ShortUrlsList</>,
|
||||||
let wrapper: ShallowWrapper;
|
() => <>AsideMenu</>,
|
||||||
const createWrapper = (selectedServer: SelectedServer) => {
|
() => <>CreateShortUrl</>,
|
||||||
(useParams as any).mockReturnValue({ serverId: 'abc123' });
|
() => <>ShortUrlVisits</>,
|
||||||
|
() => <>TagVisits</>,
|
||||||
|
() => <>DomainVisits</>,
|
||||||
|
() => <>OrphanVisits</>,
|
||||||
|
() => <>NonOrphanVisits</>,
|
||||||
|
() => <>ServerError</>,
|
||||||
|
() => <>Overview</>,
|
||||||
|
() => <>EditShortUrl</>,
|
||||||
|
() => <>ManageDomains</>,
|
||||||
|
);
|
||||||
|
const setUp = (selectedServer: SelectedServer, currentPath = '/') => {
|
||||||
|
const history = createMemoryHistory();
|
||||||
|
history.push(currentPath);
|
||||||
|
|
||||||
wrapper = shallow(
|
return render(
|
||||||
<MenuLayout
|
<Router location={history.location} navigator={history}>
|
||||||
sidebarNotPresent={jest.fn()}
|
<MenuLayout
|
||||||
sidebarPresent={jest.fn()}
|
sidebarNotPresent={jest.fn()}
|
||||||
selectServer={jest.fn()}
|
sidebarPresent={jest.fn()}
|
||||||
selectedServer={selectedServer}
|
selectServer={jest.fn()}
|
||||||
/>,
|
selectedServer={selectedServer}
|
||||||
|
/>
|
||||||
|
</Router>,
|
||||||
);
|
);
|
||||||
|
|
||||||
return wrapper;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
(useParams as any).mockReturnValue({ serverId: 'abc123' });
|
||||||
|
});
|
||||||
|
|
||||||
afterEach(jest.clearAllMocks);
|
afterEach(jest.clearAllMocks);
|
||||||
afterEach(() => wrapper?.unmount());
|
|
||||||
|
|
||||||
it.each([
|
it('shows loading indicator while loading server', () => {
|
||||||
[null, NoMenuLayout],
|
setUp(null);
|
||||||
[Mock.of<NotFoundServer>({ serverNotFound: true }), ServerError],
|
|
||||||
])('returns error when server is not found', (selectedServer, ExpectedComp) => {
|
|
||||||
const wrapper = createWrapper(selectedServer);
|
|
||||||
const comp = wrapper.find(ExpectedComp);
|
|
||||||
|
|
||||||
expect(comp).toHaveLength(1);
|
expect(screen.getByText('Loading...')).toBeInTheDocument();
|
||||||
});
|
expect(screen.queryByText('ServerError')).not.toBeInTheDocument();
|
||||||
|
|
||||||
it('returns error if server is not reachable', () => {
|
|
||||||
const wrapper = createWrapper(Mock.of<NonReachableServer>()).dive();
|
|
||||||
const serverError = wrapper.find(ServerError);
|
|
||||||
|
|
||||||
expect(serverError).toHaveLength(1);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it.each([
|
it.each([
|
||||||
['2.6.0' as SemVer, 10],
|
[Mock.of<NotFoundServer>({ serverNotFound: true })],
|
||||||
['2.7.0' as SemVer, 10],
|
[Mock.of<NonReachableServer>({ serverNotReachable: true })],
|
||||||
['2.8.0' as SemVer, 11],
|
])('shows error for non reachable servers', (selectedServer) => {
|
||||||
['2.10.0' as SemVer, 11],
|
setUp(selectedServer);
|
||||||
['3.0.0' as SemVer, 12],
|
|
||||||
['3.1.0' as SemVer, 13],
|
|
||||||
])('has expected amount of routes based on selected server\'s version', (version, expectedAmountOfRoutes) => {
|
|
||||||
const selectedServer = Mock.of<ReachableServer>({ version });
|
|
||||||
const wrapper = createWrapper(selectedServer).dive();
|
|
||||||
const routes = wrapper.find(Route);
|
|
||||||
|
|
||||||
expect(routes).toHaveLength(expectedAmountOfRoutes);
|
expect(screen.queryByText('Loading...')).not.toBeInTheDocument();
|
||||||
expect(routes.findWhere((element) => element.prop('index'))).toHaveLength(1);
|
expect(screen.getByText('ServerError')).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it.each([
|
||||||
|
['3.0.0' as SemVer, '/overview', 'Overview'],
|
||||||
|
['3.0.0' as SemVer, '/list-short-urls/1', 'ShortUrlsList'],
|
||||||
|
['3.0.0' as SemVer, '/create-short-url', 'CreateShortUrl'],
|
||||||
|
['3.0.0' as SemVer, '/short-code/abc123/visits/foo', 'ShortUrlVisits'],
|
||||||
|
['3.0.0' as SemVer, '/short-code/abc123/edit', 'EditShortUrl'],
|
||||||
|
['3.0.0' as SemVer, '/tag/foo/visits/foo', 'TagVisits'],
|
||||||
|
['3.0.0' as SemVer, '/orphan-visits/foo', 'OrphanVisits'],
|
||||||
|
['3.0.0' as SemVer, '/manage-tags', 'TagsList'],
|
||||||
|
['3.0.0' as SemVer, '/not-found', 'Oops! We could not find requested route.'],
|
||||||
|
['3.0.0' as SemVer, '/domain/domain.com/visits/foo', 'Oops! We could not find requested route.'],
|
||||||
|
['3.1.0' as SemVer, '/domain/domain.com/visits/foo', 'DomainVisits'],
|
||||||
|
['2.10.0' as SemVer, '/non-orphan-visits/foo', 'Oops! We could not find requested route.'],
|
||||||
|
['3.0.0' as SemVer, '/non-orphan-visits/foo', 'NonOrphanVisits'],
|
||||||
|
['2.7.0' as SemVer, '/manage-domains', 'Oops! We could not find requested route.'],
|
||||||
|
['2.8.0' as SemVer, '/manage-domains', 'ManageDomains'],
|
||||||
|
])(
|
||||||
|
'renders expected component based on location and server version',
|
||||||
|
(version, currentPath, expectedContent) => {
|
||||||
|
setUp(Mock.of<ReachableServer>({ version }), currentPath);
|
||||||
|
expect(screen.getByText(expectedContent)).toBeInTheDocument();
|
||||||
|
},
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { shallow, ShallowWrapper } from 'enzyme';
|
import { shallow, ShallowWrapper } from 'enzyme';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
import NotFound from '../../src/common/NotFound';
|
import { NotFound } from '../../src/common/NotFound';
|
||||||
import { SimpleCard } from '../../src/utils/SimpleCard';
|
import { SimpleCard } from '../../src/utils/SimpleCard';
|
||||||
|
|
||||||
describe('<NotFound />', () => {
|
describe('<NotFound />', () => {
|
||||||
|
|
|
@ -9,12 +9,6 @@ import { Settings } from '../../src/settings/reducers/settings';
|
||||||
import { ReportExporter } from '../../src/common/services/ReportExporter';
|
import { ReportExporter } from '../../src/common/services/ReportExporter';
|
||||||
import { SelectedServer } from '../../src/servers/data';
|
import { SelectedServer } from '../../src/servers/data';
|
||||||
|
|
||||||
jest.mock('react-router-dom', () => ({
|
|
||||||
...jest.requireActual('react-router-dom'),
|
|
||||||
useNavigate: jest.fn().mockReturnValue(jest.fn()),
|
|
||||||
useParams: jest.fn().mockReturnValue({}),
|
|
||||||
}));
|
|
||||||
|
|
||||||
describe('<NonOrphanVisits />', () => {
|
describe('<NonOrphanVisits />', () => {
|
||||||
const exportVisits = jest.fn();
|
const exportVisits = jest.fn();
|
||||||
const getNonOrphanVisits = jest.fn();
|
const getNonOrphanVisits = jest.fn();
|
||||||
|
|
Loading…
Reference in a new issue