From c4e928ff09ed9c400350d729cc70e7bd0f9217ac Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Mon, 7 Feb 2022 22:17:57 +0100 Subject: [PATCH] Fixed most tests using react-router-dom hooks --- .eslintrc | 4 +- src/mercure/helpers/boundToMercureHub.tsx | 2 +- src/short-urls/ShortUrlsList.tsx | 3 +- src/utils/helpers/hooks.ts | 2 +- test/app/App.test.tsx | 25 +++++++------ test/common/AsideMenu.test.tsx | 5 +++ test/common/Home.test.tsx | 13 +++---- test/common/MainHeader.test.tsx | 14 ++++--- test/common/MenuLayout.test.tsx | 38 +++++++++---------- test/common/ScrollToTop.test.tsx | 9 +++-- test/servers/CreateServer.test.tsx | 16 ++++---- test/servers/DeleteServerModal.test.tsx | 13 ++++--- test/servers/EditServer.test.tsx | 27 +++++--------- test/short-urls/EditShortUrl.test.tsx | 22 ++++++----- test/short-urls/SearchBar.test.tsx | 45 ++++++++++++----------- test/short-urls/ShortUrlsList.test.tsx | 28 ++++++++------ test/tags/TagsTable.test.tsx | 15 +++----- test/visits/NonOrphanVisits.test.tsx | 15 ++++---- test/visits/OrphanVisits.test.tsx | 15 ++++---- test/visits/ShortUrlVisits.test.tsx | 21 ++++------- test/visits/TagVisits.test.tsx | 17 ++++----- test/visits/VisitsStats.test.tsx | 3 +- 22 files changed, 177 insertions(+), 175 deletions(-) diff --git a/.eslintrc b/.eslintrc index 48aab946..20632049 100644 --- a/.eslintrc +++ b/.eslintrc @@ -17,6 +17,8 @@ "ignorePatterns": ["src/service*.ts"], "rules": { "complexity": "off", - "@typescript-eslint/no-unnecessary-type-assertion": "off" + "@typescript-eslint/no-unnecessary-type-assertion": "off", + "@typescript-eslint/no-unsafe-return": "off", + "@typescript-eslint/no-unsafe-call": "off" } } diff --git a/src/mercure/helpers/boundToMercureHub.tsx b/src/mercure/helpers/boundToMercureHub.tsx index 7a7a6ef3..bc7394b7 100644 --- a/src/mercure/helpers/boundToMercureHub.tsx +++ b/src/mercure/helpers/boundToMercureHub.tsx @@ -1,9 +1,9 @@ import { FC, useEffect } from 'react'; import { pipe } from 'ramda'; +import { useParams } from 'react-router-dom'; import { CreateVisit } from '../../visits/types'; import { MercureInfo } from '../reducers/mercureInfo'; import { bindToMercureTopic } from './index'; -import { useParams } from 'react-router-dom'; export interface MercureBoundProps { createNewVisits: (createdVisits: CreateVisit[]) => void; diff --git a/src/short-urls/ShortUrlsList.tsx b/src/short-urls/ShortUrlsList.tsx index 510f8fc4..f2bc1952 100644 --- a/src/short-urls/ShortUrlsList.tsx +++ b/src/short-urls/ShortUrlsList.tsx @@ -1,7 +1,7 @@ import { pipe } from 'ramda'; import { FC, useEffect, useMemo, useState } from 'react'; import { Card } from 'reactstrap'; -import { useParams } from 'react-router-dom'; +import { useLocation, useParams } from 'react-router-dom'; import { OrderingDropdown } from '../utils/OrderingDropdown'; import { determineOrderDir, OrderDir } from '../utils/helpers/ordering'; import { getServerId, SelectedServer } from '../servers/data'; @@ -31,6 +31,7 @@ const ShortUrlsList = (ShortUrlsTable: FC, ShortUrlsFilteri }: ShortUrlsListProps) => { const serverId = getServerId(selectedServer); const { page } = useParams(); + const location = useLocation(); const [{ tags, search, startDate, endDate, orderBy }, toFirstPage ] = useShortUrlsQuery(); const [ actualOrderBy, setActualOrderBy ] = useState( // This separated state handling is needed to be able to fall back to settings value, but only once when loaded diff --git a/src/utils/helpers/hooks.ts b/src/utils/helpers/hooks.ts index 6689667a..63b566bb 100644 --- a/src/utils/helpers/hooks.ts +++ b/src/utils/helpers/hooks.ts @@ -1,7 +1,7 @@ import { useState, useRef, EffectCallback, DependencyList, useEffect } from 'react'; import { useSwipeable as useReactSwipeable } from 'react-swipeable'; -import { parseQuery, stringifyQuery } from './query'; import { useNavigate } from 'react-router-dom'; +import { parseQuery, stringifyQuery } from './query'; const DEFAULT_DELAY = 2000; diff --git a/test/app/App.test.tsx b/test/app/App.test.tsx index a161a44e..4802a983 100644 --- a/test/app/App.test.tsx +++ b/test/app/App.test.tsx @@ -1,12 +1,15 @@ import { shallow, ShallowWrapper } from 'enzyme'; -import { Route } from 'react-router-dom'; +import { Route, useLocation } from 'react-router-dom'; import { Mock } from 'ts-mockery'; -import { match } from 'react-router'; -import { History, Location } from 'history'; import { Settings } from '../../src/settings/reducers/settings'; import appFactory 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('', () => { let wrapper: ShallowWrapper; const MainHeader = () => null; @@ -21,7 +24,7 @@ describe('', () => { () => null, ShlinkVersions, ); - const createWrapper = (pathname = '') => { + const createWrapper = () => { wrapper = shallow( {}} @@ -29,16 +32,14 @@ describe('', () => { settings={Mock.all()} appUpdated={false} resetAppUpdate={() => {}} - match={Mock.all()} - history={Mock.all()} - location={Mock.of({ pathname })} />, ); return wrapper; }; - afterEach(() => wrapper.unmount()); + afterEach(jest.clearAllMocks); + afterEach(() => wrapper?.unmount()); it('renders children components', () => { const wrapper = createWrapper(); @@ -52,12 +53,12 @@ describe('', () => { const wrapper = createWrapper(); const routes = wrapper.find(Route); const expectedPaths = [ - '/', + undefined, '/settings', '/manage-servers', '/server/create', '/server/:serverId/edit', - '/server/:serverId', + '/server/:serverId/*', ]; expect.assertions(expectedPaths.length + 1); @@ -72,7 +73,9 @@ describe('', () => { [ '/bar', 'shlink-wrapper' ], [ '/', 'shlink-wrapper d-flex d-md-block align-items-center' ], ])('renders expected classes on shlink-wrapper based on current pathname', (pathname, expectedClasses) => { - const wrapper = createWrapper(pathname); + (useLocation as any).mockReturnValue({ pathname }); // eslint-disable-line @typescript-eslint/no-unsafe-call + + const wrapper = createWrapper(); const shlinkWrapper = wrapper.find('.shlink-wrapper'); expect(shlinkWrapper.prop('className')).toEqual(expectedClasses); diff --git a/test/common/AsideMenu.test.tsx b/test/common/AsideMenu.test.tsx index 22ef6a26..01ad26b1 100644 --- a/test/common/AsideMenu.test.tsx +++ b/test/common/AsideMenu.test.tsx @@ -3,6 +3,11 @@ import { Mock } from 'ts-mockery'; import asideMenuCreator from '../../src/common/AsideMenu'; import { ReachableServer } from '../../src/servers/data'; +jest.mock('react-router-dom', () => ({ + ...jest.requireActual('react-router-dom'), + useLocation: jest.fn().mockReturnValue({ pathname: '' }), +})); + describe('', () => { let wrapped: ShallowWrapper; const DeleteServerButton = () => null; diff --git a/test/common/Home.test.tsx b/test/common/Home.test.tsx index b24db7d1..96f17a3e 100644 --- a/test/common/Home.test.tsx +++ b/test/common/Home.test.tsx @@ -1,19 +1,18 @@ import { shallow, ShallowWrapper } from 'enzyme'; import { Mock } from 'ts-mockery'; -import { RouteChildrenProps } from 'react-router-dom'; import Home, { HomeProps } from '../../src/common/Home'; import { ServerWithId } from '../../src/servers/data'; import { ShlinkLogo } from '../../src/common/img/ShlinkLogo'; +jest.mock('react-router-dom', () => ({ + ...jest.requireActual('react-router-dom'), + useNavigate: jest.fn().mockReturnValue(jest.fn()), +})); + describe('', () => { let wrapped: ShallowWrapper; - const defaultProps = { - ...Mock.all(), - resetSelectedServer: jest.fn(), - servers: {}, - }; const createComponent = (props: Partial = {}) => { - const actualProps = { ...defaultProps, ...props }; + const actualProps = { resetSelectedServer: jest.fn(), servers: {}, ...props }; wrapped = shallow(); diff --git a/test/common/MainHeader.test.tsx b/test/common/MainHeader.test.tsx index 81ff9d2e..b1fa282e 100644 --- a/test/common/MainHeader.test.tsx +++ b/test/common/MainHeader.test.tsx @@ -1,24 +1,28 @@ import { shallow, ShallowWrapper } from 'enzyme'; -import { Mock } from 'ts-mockery'; -import { match } from 'react-router'; -import { History, Location } from 'history'; +import { useLocation } from 'react-router-dom'; import { Collapse, NavbarToggler, NavLink } from 'reactstrap'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import createMainHeader from '../../src/common/MainHeader'; +jest.mock('react-router-dom', () => ({ + ...jest.requireActual('react-router-dom'), + useLocation: jest.fn().mockReturnValue({}), +})); + describe('', () => { const ServersDropdown = () => null; const MainHeader = createMainHeader(ServersDropdown); let wrapper: ShallowWrapper; const createWrapper = (pathname = '') => { - const location = Mock.of({ pathname }); + (useLocation as any).mockReturnValue({ pathname }); - wrapper = shallow(()} location={location} match={Mock.all()} />); + wrapper = shallow(); return wrapper; }; + afterEach(jest.clearAllMocks); afterEach(() => wrapper?.unmount()); it('renders ServersDropdown', () => { diff --git a/test/common/MenuLayout.test.tsx b/test/common/MenuLayout.test.tsx index ba5ad007..98306335 100644 --- a/test/common/MenuLayout.test.tsx +++ b/test/common/MenuLayout.test.tsx @@ -1,34 +1,31 @@ import { shallow, ShallowWrapper } from 'enzyme'; -import { History, Location } from 'history'; -import { match } from 'react-router'; // eslint-disable-line @typescript-eslint/no-unused-vars -import { Route } from 'react-router-dom'; +import { Route, useParams } from 'react-router-dom'; import { Mock } from 'ts-mockery'; import createMenuLayout from '../../src/common/MenuLayout'; import { NonReachableServer, NotFoundServer, ReachableServer, SelectedServer } from '../../src/servers/data'; import { NoMenuLayout } from '../../src/common/NoMenuLayout'; import { SemVer } from '../../src/utils/helpers/version'; +jest.mock('react-router-dom', () => ({ + ...jest.requireActual('react-router-dom'), + useParams: jest.fn().mockReturnValue({}), + useLocation: jest.fn().mockReturnValue({}), +})); + describe('', () => { const ServerError = jest.fn(); const C = jest.fn(); const MenuLayout = createMenuLayout(C, C, C, C, C, C, C, C, ServerError, C, C, C); let wrapper: ShallowWrapper; const createWrapper = (selectedServer: SelectedServer) => { - wrapper = shallow( - ()} - location={Mock.all()} - match={Mock.of>({ - params: { serverId: 'abc123' }, - })} - />, - ); + (useParams as any).mockReturnValue({ serverId: 'abc123' }); + + wrapper = shallow(); return wrapper; }; + afterEach(jest.clearAllMocks); afterEach(() => wrapper?.unmount()); it.each([ @@ -49,17 +46,18 @@ describe('', () => { }); it.each([ - [ '2.5.0' as SemVer, 8 ], - [ '2.6.0' as SemVer, 9 ], - [ '2.7.0' as SemVer, 9 ], - [ '2.8.0' as SemVer, 10 ], - [ '2.10.0' as SemVer, 10 ], - [ '3.0.0' as SemVer, 11 ], + [ '2.5.0' as SemVer, 9 ], + [ '2.6.0' as SemVer, 10 ], + [ '2.7.0' as SemVer, 10 ], + [ '2.8.0' as SemVer, 11 ], + [ '2.10.0' as SemVer, 11 ], + [ '3.0.0' as SemVer, 12 ], ])('has expected amount of routes based on selected server\'s version', (version, expectedAmountOfRoutes) => { const selectedServer = Mock.of({ version }); const wrapper = createWrapper(selectedServer).dive(); const routes = wrapper.find(Route); expect(routes).toHaveLength(expectedAmountOfRoutes); + expect(routes.findWhere((element) => element.prop('index'))).toHaveLength(1); }); }); diff --git a/test/common/ScrollToTop.test.tsx b/test/common/ScrollToTop.test.tsx index cd3ce860..2720f0d3 100644 --- a/test/common/ScrollToTop.test.tsx +++ b/test/common/ScrollToTop.test.tsx @@ -1,15 +1,18 @@ import { shallow, ShallowWrapper } from 'enzyme'; -import { Mock } from 'ts-mockery'; -import { RouteComponentProps } from 'react-router'; import createScrollToTop from '../../src/common/ScrollToTop'; +jest.mock('react-router-dom', () => ({ + ...jest.requireActual('react-router-dom'), + useLocation: jest.fn().mockReturnValue({}), +})); + describe('', () => { let wrapper: ShallowWrapper; beforeEach(() => { const ScrollToTop = createScrollToTop(); - wrapper = shallow(()}>Foobar); + wrapper = shallow(Foobar); }); afterEach(() => wrapper.unmount()); diff --git a/test/servers/CreateServer.test.tsx b/test/servers/CreateServer.test.tsx index b55944ed..06fde51c 100644 --- a/test/servers/CreateServer.test.tsx +++ b/test/servers/CreateServer.test.tsx @@ -1,27 +1,29 @@ import { shallow, ShallowWrapper } from 'enzyme'; import { Mock } from 'ts-mockery'; -import { History } from 'history'; +import { useNavigate } from 'react-router-dom'; import createServerConstruct from '../../src/servers/CreateServer'; import { ServerForm } from '../../src/servers/helpers/ServerForm'; import { ServerWithId } from '../../src/servers/data'; import { DuplicatedServersModal } from '../../src/servers/helpers/DuplicatedServersModal'; +jest.mock('react-router-dom', () => ({ ...jest.requireActual('react-router-dom'), useNavigate: jest.fn() })); + describe('', () => { let wrapper: ShallowWrapper; const ImportServersBtn = () => null; const createServerMock = jest.fn(); - const push = jest.fn(); - const goBack = jest.fn(); - const historyMock = Mock.of({ push, goBack }); + const navigate = jest.fn(); const servers = { foo: Mock.all() }; const createWrapper = (serversImported = false, importFailed = false) => { + (useNavigate as any).mockReturnValue(navigate); + const useStateFlagTimeout = jest.fn() .mockReturnValueOnce([ serversImported, () => '' ]) .mockReturnValueOnce([ importFailed, () => '' ]) .mockReturnValue([]); const CreateServer = createServerConstruct(ImportServersBtn, useStateFlagTimeout); - wrapper = shallow(); + wrapper = shallow(); return wrapper; }; @@ -68,7 +70,7 @@ describe('', () => { wrapper.find(DuplicatedServersModal).simulate('save'); expect(createServerMock).toHaveBeenCalledTimes(1); - expect(push).toHaveBeenCalledTimes(1); + expect(navigate).toHaveBeenCalledTimes(1); }); it('goes back on modal discard', () => { @@ -76,6 +78,6 @@ describe('', () => { wrapper.find(DuplicatedServersModal).simulate('discard'); - expect(goBack).toHaveBeenCalledTimes(1); + expect(navigate).toHaveBeenCalledWith(-1); }); }); diff --git a/test/servers/DeleteServerModal.test.tsx b/test/servers/DeleteServerModal.test.tsx index 14eed177..151832b2 100644 --- a/test/servers/DeleteServerModal.test.tsx +++ b/test/servers/DeleteServerModal.test.tsx @@ -1,25 +1,28 @@ import { shallow, ShallowWrapper } from 'enzyme'; import { Modal, ModalBody, ModalFooter, ModalHeader } from 'reactstrap'; -import { History } from 'history'; import { Mock } from 'ts-mockery'; +import { useNavigate } from 'react-router-dom'; import DeleteServerModal from '../../src/servers/DeleteServerModal'; import { ServerWithId } from '../../src/servers/data'; +jest.mock('react-router-dom', () => ({ ...jest.requireActual('react-router-dom'), useNavigate: jest.fn() })); + describe('', () => { let wrapper: ShallowWrapper; const deleteServerMock = jest.fn(); - const push = jest.fn(); + const navigate = jest.fn(); const toggleMock = jest.fn(); const serverName = 'the_server_name'; beforeEach(() => { + (useNavigate as any).mockReturnValue(navigate); + wrapper = shallow( ({ name: serverName })} toggle={toggleMock} isOpen={true} deleteServer={deleteServerMock} - history={Mock.of({ push })} />, ); }); @@ -48,7 +51,7 @@ describe('', () => { expect(toggleMock).toHaveBeenCalledTimes(1); expect(deleteServerMock).not.toHaveBeenCalled(); - expect(push).not.toHaveBeenCalled(); + expect(navigate).not.toHaveBeenCalled(); }); it('deletes server when clicking accept button', () => { @@ -58,6 +61,6 @@ describe('', () => { expect(toggleMock).toHaveBeenCalledTimes(1); expect(deleteServerMock).toHaveBeenCalledTimes(1); - expect(push).toHaveBeenCalledTimes(1); + expect(navigate).toHaveBeenCalledTimes(1); }); }); diff --git a/test/servers/EditServer.test.tsx b/test/servers/EditServer.test.tsx index 4f6a65da..abd60589 100644 --- a/test/servers/EditServer.test.tsx +++ b/test/servers/EditServer.test.tsx @@ -1,43 +1,34 @@ import { mount, ReactWrapper } from 'enzyme'; import { Mock } from 'ts-mockery'; -import { History, Location } from 'history'; -import { match } from 'react-router'; // eslint-disable-line @typescript-eslint/no-unused-vars +import { useNavigate } from 'react-router-dom'; import { EditServer as editServerConstruct } from '../../src/servers/EditServer'; import { ServerForm } from '../../src/servers/helpers/ServerForm'; import { ReachableServer } from '../../src/servers/data'; +jest.mock('react-router-dom', () => ({ ...jest.requireActual('react-router-dom'), useNavigate: jest.fn() })); + describe('', () => { let wrapper: ReactWrapper; const ServerError = jest.fn(); const editServerMock = jest.fn(); - const goBack = jest.fn(); - const historyMock = Mock.of({ goBack }); - const match = Mock.of>({ - params: { serverId: 'abc123' }, - }); + const navigate = jest.fn(); const selectedServer = Mock.of({ id: 'abc123', name: 'name', url: 'url', apiKey: 'apiKey', }); + const EditServer = editServerConstruct(ServerError); beforeEach(() => { - const EditServer = editServerConstruct(ServerError); + (useNavigate as any).mockReturnValue(navigate); wrapper = mount( - ()} - selectedServer={selectedServer} - selectServer={jest.fn()} - />, + , ); }); - afterEach(jest.resetAllMocks); + afterEach(jest.clearAllMocks); afterEach(() => wrapper?.unmount()); it('renders components', () => { @@ -50,6 +41,6 @@ describe('', () => { form.simulate('submit', {}); expect(editServerMock).toHaveBeenCalledTimes(1); - expect(goBack).toHaveBeenCalledTimes(1); + expect(navigate).toHaveBeenCalledWith(-1); }); }); diff --git a/test/short-urls/EditShortUrl.test.tsx b/test/short-urls/EditShortUrl.test.tsx index 51243bf7..5aa2eaa8 100644 --- a/test/short-urls/EditShortUrl.test.tsx +++ b/test/short-urls/EditShortUrl.test.tsx @@ -1,7 +1,6 @@ import { shallow, ShallowWrapper } from 'enzyme'; import { Mock } from 'ts-mockery'; -import { History, Location } from 'history'; -import { match } from 'react-router'; // eslint-disable-line @typescript-eslint/no-unused-vars +import { useLocation, useParams } from 'react-router-dom'; import { EditShortUrl as createEditShortUrl } from '../../src/short-urls/EditShortUrl'; import { Settings } from '../../src/settings/reducers/settings'; import { ShortUrlDetail } from '../../src/short-urls/reducers/shortUrlDetail'; @@ -9,29 +8,32 @@ import { ShortUrlEdition } from '../../src/short-urls/reducers/shortUrlEdition'; import { ShlinkApiError } from '../../src/api/ShlinkApiError'; import { ShortUrl } from '../../src/short-urls/data'; +jest.mock('react-router-dom', () => ({ + ...jest.requireActual('react-router-dom'), + useNavigate: jest.fn().mockReturnValue(jest.fn()), + useParams: jest.fn().mockReturnValue({}), + useLocation: jest.fn().mockReturnValue({}), +})); + describe('', () => { let wrapper: ShallowWrapper; const ShortUrlForm = () => null; - const goBack = jest.fn(); const getShortUrlDetail = jest.fn(); const editShortUrl = jest.fn(async () => Promise.resolve()); const shortUrlCreation = { validateUrls: true }; + const EditShortUrl = createEditShortUrl(ShortUrlForm); const createWrapper = (detail: Partial = {}, edition: Partial = {}) => { - const EditSHortUrl = createEditShortUrl(ShortUrlForm); + (useParams as any).mockReturnValue({ shortCode: 'the_base_url' }); + (useLocation as any).mockReturnValue({ search: '' }); wrapper = shallow( - ({ shortUrlCreation })} selectedServer={null} shortUrlDetail={Mock.of(detail)} shortUrlEdition={Mock.of(edition)} getShortUrlDetail={getShortUrlDetail} editShortUrl={editShortUrl} - history={Mock.of({ goBack })} - location={Mock.all()} - match={Mock.of>({ - params: { shortCode: 'the_base_url' }, - })} />, ); diff --git a/test/short-urls/SearchBar.test.tsx b/test/short-urls/SearchBar.test.tsx index b8ccdc4c..2567b8e1 100644 --- a/test/short-urls/SearchBar.test.tsx +++ b/test/short-urls/SearchBar.test.tsx @@ -1,29 +1,30 @@ import { shallow, ShallowWrapper } from 'enzyme'; import { Mock } from 'ts-mockery'; -import { History, Location } from 'history'; -import { match } from 'react-router'; import { formatISO } from 'date-fns'; -import filteringBarCreator, { ShortUrlsFilteringProps } from '../../src/short-urls/ShortUrlsFilteringBar'; +import { useLocation, useNavigate } from 'react-router-dom'; +import filteringBarCreator from '../../src/short-urls/ShortUrlsFilteringBar'; import SearchField from '../../src/utils/SearchField'; import Tag from '../../src/tags/helpers/Tag'; import { DateRangeSelector } from '../../src/utils/dates/DateRangeSelector'; import ColorGenerator from '../../src/utils/services/ColorGenerator'; -import { ShortUrlListRouteParams } from '../../src/short-urls/helpers/hooks'; + +jest.mock('react-router-dom', () => ({ + ...jest.requireActual('react-router-dom'), + useNavigate: jest.fn(), + useParams: jest.fn().mockReturnValue({ serverId: '1' }), + useLocation: jest.fn().mockReturnValue({}), +})); describe('', () => { let wrapper: ShallowWrapper; const ShortUrlsFilteringBar = filteringBarCreator(Mock.all()); - const push = jest.fn(); + const navigate = jest.fn(); const now = new Date(); - const createWrapper = (props: Partial = {}) => { - wrapper = shallow( - ({ push })} - location={Mock.of({ search: '' })} - match={Mock.of>({ params: { serverId: '1' } })} - {...props} - />, - ); + const createWrapper = (search = '') => { + (useLocation as any).mockReturnValue({ search }); + (useNavigate as any).mockReturnValue(navigate); + + wrapper = shallow(); return wrapper; }; @@ -44,7 +45,7 @@ describe('', () => { [ '', 0 ], [ 'foo=bar', 0 ], ])('renders the proper amount of tags', (search, expectedTagComps) => { - const wrapper = createWrapper({ location: Mock.of({ search }) }); + const wrapper = createWrapper(search); expect(wrapper.find(Tag)).toHaveLength(expectedTagComps); }); @@ -53,18 +54,18 @@ describe('', () => { const wrapper = createWrapper(); const searchField = wrapper.find(SearchField); - expect(push).not.toHaveBeenCalled(); + expect(navigate).not.toHaveBeenCalled(); searchField.simulate('change', 'search-term'); - expect(push).toHaveBeenCalledWith('/server/1/list-short-urls/1?search=search-term'); + expect(navigate).toHaveBeenCalledWith('/server/1/list-short-urls/1?search=search-term'); }); it('redirects to first page when a tag is removed', () => { - const wrapper = createWrapper({ location: Mock.of({ search: 'tags=foo,bar' }) }); + const wrapper = createWrapper('tags=foo,bar'); const tag = wrapper.find(Tag).first(); - expect(push).not.toHaveBeenCalled(); + expect(navigate).not.toHaveBeenCalled(); tag.simulate('close'); - expect(push).toHaveBeenCalledWith('/server/1/list-short-urls/1?tags=bar'); + expect(navigate).toHaveBeenCalledWith('/server/1/list-short-urls/1?tags=bar'); }); it.each([ @@ -78,8 +79,8 @@ describe('', () => { const wrapper = createWrapper(); const dateRange = wrapper.find(DateRangeSelector); - expect(push).not.toHaveBeenCalled(); + expect(navigate).not.toHaveBeenCalled(); dateRange.simulate('datesChange', dates); - expect(push).toHaveBeenCalledWith(`/server/1/list-short-urls/1?${expectedQuery}`); + expect(navigate).toHaveBeenCalledWith(`/server/1/list-short-urls/1?${expectedQuery}`); }); }); diff --git a/test/short-urls/ShortUrlsList.test.tsx b/test/short-urls/ShortUrlsList.test.tsx index 9f9cb65f..8ce2ffbc 100644 --- a/test/short-urls/ShortUrlsList.test.tsx +++ b/test/short-urls/ShortUrlsList.test.tsx @@ -1,8 +1,7 @@ import { shallow, ShallowWrapper } from 'enzyme'; import { ReactElement } from 'react'; import { Mock } from 'ts-mockery'; -import { History, Location } from 'history'; -import { match } from 'react-router'; +import { useNavigate } from 'react-router-dom'; import shortUrlsListCreator from '../../src/short-urls/ShortUrlsList'; import { ShortUrlsOrderableFields, ShortUrl, ShortUrlsOrder } from '../../src/short-urls/data'; import { MercureBoundProps } from '../../src/mercure/helpers/boundToMercureHub'; @@ -10,15 +9,21 @@ import { ShortUrlsList as ShortUrlsListModel } from '../../src/short-urls/reduce import { OrderingDropdown } from '../../src/utils/OrderingDropdown'; import Paginator from '../../src/short-urls/Paginator'; import { ReachableServer } from '../../src/servers/data'; -import { ShortUrlListRouteParams } from '../../src/short-urls/helpers/hooks'; import { Settings } from '../../src/settings/reducers/settings'; +jest.mock('react-router-dom', () => ({ + ...jest.requireActual('react-router-dom'), + useNavigate: jest.fn().mockReturnValue(jest.fn()), + useParams: jest.fn().mockReturnValue({}), + useLocation: jest.fn().mockReturnValue({ search: '?tags=test%20tag&search=example.com' }), +})); + describe('', () => { let wrapper: ShallowWrapper; const ShortUrlsTable = () => null; const ShortUrlsFilteringBar = () => null; const listShortUrlsMock = jest.fn(); - const push = jest.fn(); + const navigate = jest.fn(); const shortUrlsList = Mock.of({ shortUrls: { data: [ @@ -36,20 +41,19 @@ describe('', () => { ({ mercureInfo: { loading: true } })} listShortUrls={listShortUrlsMock} - match={Mock.of>({ params: {} })} - location={Mock.of({ search: '?tags=test%20tag&search=example.com' })} shortUrlsList={shortUrlsList} - history={Mock.of({ push })} selectedServer={Mock.of({ id: '1' })} settings={Mock.of({ shortUrlsList: { defaultOrdering } })} />, ).dive(); // Dive is needed as this component is wrapped in a HOC beforeEach(() => { + (useNavigate as any).mockReturnValue(navigate); + wrapper = createWrapper(); }); - afterEach(jest.resetAllMocks); + afterEach(jest.clearAllMocks); afterEach(() => wrapper?.unmount()); it('wraps expected components', () => { @@ -68,10 +72,10 @@ describe('', () => { wrapper.find(ShortUrlsTable).simulate('tagClick', 'bar'); wrapper.find(ShortUrlsTable).simulate('tagClick', 'baz'); - expect(push).toHaveBeenCalledTimes(3); - expect(push).toHaveBeenNthCalledWith(1, expect.stringContaining(`tags=${encodeURIComponent('test tag,foo')}`)); - expect(push).toHaveBeenNthCalledWith(2, expect.stringContaining(`tags=${encodeURIComponent('test tag,bar')}`)); - expect(push).toHaveBeenNthCalledWith(3, expect.stringContaining(`tags=${encodeURIComponent('test tag,baz')}`)); + expect(navigate).toHaveBeenCalledTimes(3); + expect(navigate).toHaveBeenNthCalledWith(1, expect.stringContaining(`tags=${encodeURIComponent('test tag,foo')}`)); + expect(navigate).toHaveBeenNthCalledWith(2, expect.stringContaining(`tags=${encodeURIComponent('test tag,bar')}`)); + expect(navigate).toHaveBeenNthCalledWith(3, expect.stringContaining(`tags=${encodeURIComponent('test tag,baz')}`)); }); it('invokes order icon rendering', () => { diff --git a/test/tags/TagsTable.test.tsx b/test/tags/TagsTable.test.tsx index 4d5dc344..ef8315af 100644 --- a/test/tags/TagsTable.test.tsx +++ b/test/tags/TagsTable.test.tsx @@ -1,13 +1,14 @@ import { Mock } from 'ts-mockery'; import { shallow, ShallowWrapper } from 'enzyme'; -import { match } from 'react-router'; -import { Location, History } from 'history'; +import { useLocation } from 'react-router-dom'; import { TagsTable as createTagsTable } from '../../src/tags/TagsTable'; import { SelectedServer } from '../../src/servers/data'; import { rangeOf } from '../../src/utils/utils'; import SimplePaginator from '../../src/common/SimplePaginator'; import { NormalizedTag } from '../../src/tags/data'; +jest.mock('react-router-dom', () => ({ ...jest.requireActual('react-router-dom'), useLocation: jest.fn() })); + describe('', () => { const TagsTableRow = () => null; const orderByColumn = jest.fn(); @@ -15,14 +16,13 @@ describe('', () => { const tags = (amount: number) => rangeOf(amount, (i) => `tag_${i}`); let wrapper: ShallowWrapper; const createWrapper = (sortedTags: string[] = [], search = '') => { + (useLocation as any).mockReturnValue({ search }); + wrapper = shallow( Mock.of({ tag }))} selectedServer={Mock.all()} currentOrder={{}} - history={Mock.all()} - location={Mock.of({ search })} - match={Mock.all()} orderByColumn={() => orderByColumn} />, ); @@ -30,11 +30,6 @@ describe('', () => { return wrapper; }; - beforeEach(() => { - (global as any).location = { search: '', pathname: '' }; - (global as any).history = { pushState: jest.fn() }; - }); - afterEach(jest.clearAllMocks); afterEach(() => wrapper?.unmount()); diff --git a/test/visits/NonOrphanVisits.test.tsx b/test/visits/NonOrphanVisits.test.tsx index 813d4e89..c1af476b 100644 --- a/test/visits/NonOrphanVisits.test.tsx +++ b/test/visits/NonOrphanVisits.test.tsx @@ -1,7 +1,5 @@ import { shallow } from 'enzyme'; import { Mock } from 'ts-mockery'; -import { History, Location } from 'history'; -import { match } from 'react-router'; import { NonOrphanVisits as createNonOrphanVisits } from '../../src/visits/NonOrphanVisits'; import { MercureBoundProps } from '../../src/mercure/helpers/boundToMercureHub'; import { VisitsInfo } from '../../src/visits/types'; @@ -11,9 +9,14 @@ import { Settings } from '../../src/settings/reducers/settings'; import { VisitsExporter } from '../../src/visits/services/VisitsExporter'; 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('', () => { it('wraps visits stats and header', () => { - const goBack = jest.fn(); const getNonOrphanVisits = jest.fn(); const cancelGetNonOrphanVisits = jest.fn(); const nonOrphanVisits = Mock.all(); @@ -25,9 +28,6 @@ describe('', () => { getNonOrphanVisits={getNonOrphanVisits} nonOrphanVisits={nonOrphanVisits} cancelGetNonOrphanVisits={cancelGetNonOrphanVisits} - history={Mock.of({ goBack })} - location={Mock.all()} - match={Mock.of({ url: 'the_base_url' })} settings={Mock.all()} selectedServer={Mock.all()} />, @@ -39,9 +39,8 @@ describe('', () => { expect(header).toHaveLength(1); expect(stats.prop('cancelGetVisits')).toEqual(cancelGetNonOrphanVisits); expect(stats.prop('visitsInfo')).toEqual(nonOrphanVisits); - expect(stats.prop('baseUrl')).toEqual('the_base_url'); expect(stats.prop('isOrphanVisits')).not.toBeDefined(); expect(header.prop('nonOrphanVisits')).toEqual(nonOrphanVisits); - expect(header.prop('goBack')).toEqual(goBack); + expect(header.prop('goBack')).toEqual(expect.any(Function)); }); }); diff --git a/test/visits/OrphanVisits.test.tsx b/test/visits/OrphanVisits.test.tsx index e1f6dc78..cae65bda 100644 --- a/test/visits/OrphanVisits.test.tsx +++ b/test/visits/OrphanVisits.test.tsx @@ -1,7 +1,5 @@ import { shallow } from 'enzyme'; import { Mock } from 'ts-mockery'; -import { History, Location } from 'history'; -import { match } from 'react-router'; import { OrphanVisits as createOrphanVisits } from '../../src/visits/OrphanVisits'; import { MercureBoundProps } from '../../src/mercure/helpers/boundToMercureHub'; import { VisitsInfo } from '../../src/visits/types'; @@ -11,9 +9,14 @@ import { Settings } from '../../src/settings/reducers/settings'; import { VisitsExporter } from '../../src/visits/services/VisitsExporter'; 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('', () => { it('wraps visits stats and header', () => { - const goBack = jest.fn(); const getOrphanVisits = jest.fn(); const cancelGetOrphanVisits = jest.fn(); const orphanVisits = Mock.all(); @@ -25,9 +28,6 @@ describe('', () => { getOrphanVisits={getOrphanVisits} orphanVisits={orphanVisits} cancelGetOrphanVisits={cancelGetOrphanVisits} - history={Mock.of({ goBack })} - location={Mock.all()} - match={Mock.of({ url: 'the_base_url' })} settings={Mock.all()} selectedServer={Mock.all()} />, @@ -39,9 +39,8 @@ describe('', () => { expect(header).toHaveLength(1); expect(stats.prop('cancelGetVisits')).toEqual(cancelGetOrphanVisits); expect(stats.prop('visitsInfo')).toEqual(orphanVisits); - expect(stats.prop('baseUrl')).toEqual('the_base_url'); expect(stats.prop('isOrphanVisits')).toEqual(true); expect(header.prop('orphanVisits')).toEqual(orphanVisits); - expect(header.prop('goBack')).toEqual(goBack); + expect(header.prop('goBack')).toEqual(expect.any(Function)); }); }); diff --git a/test/visits/ShortUrlVisits.test.tsx b/test/visits/ShortUrlVisits.test.tsx index 6a85a838..c70ac4b2 100644 --- a/test/visits/ShortUrlVisits.test.tsx +++ b/test/visits/ShortUrlVisits.test.tsx @@ -1,8 +1,6 @@ import { shallow, ShallowWrapper } from 'enzyme'; import { identity } from 'ramda'; import { Mock } from 'ts-mockery'; -import { History, Location } from 'history'; -import { match } from 'react-router'; // eslint-disable-line @typescript-eslint/no-unused-vars import createShortUrlVisits, { ShortUrlVisitsProps } from '../../src/visits/ShortUrlVisits'; import ShortUrlVisitsHeader from '../../src/visits/ShortUrlVisitsHeader'; import { ShortUrlVisits as ShortUrlVisitsState } from '../../src/visits/reducers/shortUrlVisits'; @@ -11,16 +9,16 @@ import VisitsStats from '../../src/visits/VisitsStats'; import { MercureBoundProps } from '../../src/mercure/helpers/boundToMercureHub'; import { VisitsExporter } from '../../src/visits/services/VisitsExporter'; +jest.mock('react-router-dom', () => ({ + ...jest.requireActual('react-router-dom'), + useNavigate: jest.fn().mockReturnValue(jest.fn()), + useLocation: jest.fn().mockReturnValue({ search: '' }), + useParams: jest.fn().mockReturnValue({ shortCode: 'abc123' }), +})); + describe('', () => { let wrapper: ShallowWrapper; const getShortUrlVisitsMock = jest.fn(); - const match = Mock.of>({ - params: { shortCode: 'abc123' }, - }); - const location = Mock.of({ search: '' }); - const history = Mock.of({ - goBack: jest.fn(), - }); const ShortUrlVisits = createShortUrlVisits(Mock.all()); beforeEach(() => { @@ -30,9 +28,6 @@ describe('', () => { {...Mock.of({ mercureInfo: {} })} getShortUrlDetail={identity} getShortUrlVisits={getShortUrlVisitsMock} - match={match} - location={location} - history={history} shortUrlVisits={Mock.of({ loading: true, visits: [] })} shortUrlDetail={Mock.all()} cancelGetShortUrlVisits={() => {}} @@ -40,8 +35,8 @@ describe('', () => { ).dive(); // Dive is needed as this component is wrapped in a HOC }); + afterEach(jest.clearAllMocks); afterEach(() => wrapper.unmount()); - afterEach(jest.resetAllMocks); it('renders visit stats and visits header', () => { const visitStats = wrapper.find(VisitsStats); diff --git a/test/visits/TagVisits.test.tsx b/test/visits/TagVisits.test.tsx index ca00f9e4..10d60d6a 100644 --- a/test/visits/TagVisits.test.tsx +++ b/test/visits/TagVisits.test.tsx @@ -1,7 +1,5 @@ import { shallow, ShallowWrapper } from 'enzyme'; import { Mock } from 'ts-mockery'; -import { History } from 'history'; -import { match } from 'react-router'; // eslint-disable-line @typescript-eslint/no-unused-vars import createTagVisits, { TagVisitsProps } from '../../src/visits/TagVisits'; import TagVisitsHeader from '../../src/visits/TagVisitsHeader'; import ColorGenerator from '../../src/utils/services/ColorGenerator'; @@ -10,15 +8,16 @@ import VisitsStats from '../../src/visits/VisitsStats'; import { MercureBoundProps } from '../../src/mercure/helpers/boundToMercureHub'; import { VisitsExporter } from '../../src/visits/services/VisitsExporter'; +jest.mock('react-router-dom', () => ({ + ...jest.requireActual('react-router-dom'), + useNavigate: jest.fn().mockReturnValue(jest.fn()), + useLocation: jest.fn().mockReturnValue({}), + useParams: jest.fn().mockReturnValue({ tag: 'foo' }), +})); + describe('', () => { let wrapper: ShallowWrapper; const getTagVisitsMock = jest.fn(); - const match = Mock.of>({ - params: { tag: 'foo' }, - }); - const history = Mock.of({ - goBack: jest.fn(), - }); beforeEach(() => { const TagVisits = createTagVisits(Mock.all(), Mock.all()); @@ -28,8 +27,6 @@ describe('', () => { {...Mock.all()} {...Mock.of({ mercureInfo: {} })} getTagVisits={getTagVisitsMock} - match={match} - history={history} tagVisits={Mock.of({ loading: true, visits: [] })} cancelGetTagVisits={() => {}} />, diff --git a/test/visits/VisitsStats.test.tsx b/test/visits/VisitsStats.test.tsx index 32f1e1f3..24529b7a 100644 --- a/test/visits/VisitsStats.test.tsx +++ b/test/visits/VisitsStats.test.tsx @@ -12,7 +12,7 @@ import { SelectedServer } from '../../src/servers/data'; import { SortableBarChartCard } from '../../src/visits/charts/SortableBarChartCard'; import { DoughnutChartCard } from '../../src/visits/charts/DoughnutChartCard'; -describe('', () => { +describe('', () => { const visits = [ Mock.all(), Mock.all(), Mock.all() ]; let wrapper: ShallowWrapper; @@ -25,7 +25,6 @@ describe('', () => { getVisits={getVisitsMock} visitsInfo={Mock.of(visitsInfo)} cancelGetVisits={() => {}} - baseUrl={''} settings={Mock.all()} exportCsv={exportCsv} selectedServer={Mock.all()}