From a019bd30dfb527220993ffcfdbd9372205207839 Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Sat, 20 Mar 2021 16:32:12 +0100 Subject: [PATCH] Created view to edit short URLs --- src/common/MenuLayout.tsx | 2 + src/common/services/provideServices.ts | 1 + src/short-urls/CreateShortUrl.tsx | 3 - src/short-urls/EditShortUrl.tsx | 76 +++++++++++++++++++ src/short-urls/ShortUrlForm.tsx | 19 +++-- src/short-urls/helpers/ShortUrlDetailLink.tsx | 30 ++++++++ .../helpers/ShortUrlVisitsCount.tsx | 12 ++- src/short-urls/helpers/ShortUrlsRowMenu.tsx | 8 +- src/short-urls/helpers/VisitStatsLink.tsx | 27 ------- src/short-urls/reducers/shortUrlDetail.ts | 13 +++- src/short-urls/services/provideServices.ts | 10 +++ src/visits/services/provideServices.ts | 2 - test/common/MenuLayout.test.tsx | 12 +-- ...k.test.tsx => ShortUrlDetailLink.test.tsx} | 32 ++++++-- .../helpers/ShortUrlsRowMenu.test.tsx | 2 +- .../reducers/shortUrlDetail.test.ts | 2 +- 16 files changed, 190 insertions(+), 61 deletions(-) create mode 100644 src/short-urls/EditShortUrl.tsx create mode 100644 src/short-urls/helpers/ShortUrlDetailLink.tsx delete mode 100644 src/short-urls/helpers/VisitStatsLink.tsx rename test/short-urls/helpers/{VisitStatsLink.test.tsx => ShortUrlDetailLink.test.tsx} (59%) diff --git a/src/common/MenuLayout.tsx b/src/common/MenuLayout.tsx index 3743424a..669b2197 100644 --- a/src/common/MenuLayout.tsx +++ b/src/common/MenuLayout.tsx @@ -21,6 +21,7 @@ const MenuLayout = ( OrphanVisits: FC, ServerError: FC, Overview: FC, + EditShortUrl: FC, ) => withSelectedServer(({ location, selectedServer }) => { const [ sidebarVisible, toggleSidebar, showSidebar, hideSidebar ] = useToggle(); @@ -50,6 +51,7 @@ const MenuLayout = ( + {addTagsVisitsRoute && } {addOrphanVisitsRoute && } diff --git a/src/common/services/provideServices.ts b/src/common/services/provideServices.ts index c18689b8..eccd43f2 100644 --- a/src/common/services/provideServices.ts +++ b/src/common/services/provideServices.ts @@ -37,6 +37,7 @@ const provideServices = (bottle: Bottle, connect: ConnectDecorator, withRouter: 'OrphanVisits', 'ServerError', 'Overview', + 'EditShortUrl', ); bottle.decorator('MenuLayout', connect([ 'selectedServer', 'shortUrlsListParams' ], [ 'selectServer' ])); bottle.decorator('MenuLayout', withRouter); diff --git a/src/short-urls/CreateShortUrl.tsx b/src/short-urls/CreateShortUrl.tsx index 08aada4d..13a44654 100644 --- a/src/short-urls/CreateShortUrl.tsx +++ b/src/short-urls/CreateShortUrl.tsx @@ -1,4 +1,3 @@ -import { pipe, replace, trim } from 'ramda'; import { FC, useMemo } from 'react'; import { SelectedServer } from '../servers/data'; import { Settings, ShortUrlCreationSettings } from '../settings/reducers/settings'; @@ -19,8 +18,6 @@ interface CreateShortUrlConnectProps extends CreateShortUrlProps { resetCreateShortUrl: () => void; } -export const normalizeTag = pipe(trim, replace(/ /g, '-')); - const getInitialState = (settings?: ShortUrlCreationSettings): ShortUrlData => ({ longUrl: '', tags: [], diff --git a/src/short-urls/EditShortUrl.tsx b/src/short-urls/EditShortUrl.tsx new file mode 100644 index 00000000..f520ddce --- /dev/null +++ b/src/short-urls/EditShortUrl.tsx @@ -0,0 +1,76 @@ +import { FC, useEffect } from 'react'; +import { RouteComponentProps } from 'react-router'; +import { SelectedServer } from '../servers/data'; +import { Settings, ShortUrlCreationSettings } from '../settings/reducers/settings'; +import { OptionalString } from '../utils/utils'; +import { parseQuery } from '../utils/helpers/query'; +import Message from '../utils/Message'; +import { Result } from '../utils/Result'; +import { ShlinkApiError } from '../api/ShlinkApiError'; +import { ShortUrlFormProps } from './ShortUrlForm'; +import { ShortUrlDetail } from './reducers/shortUrlDetail'; +import { ShortUrl, ShortUrlData } from './data'; + +interface EditShortUrlConnectProps extends RouteComponentProps<{ shortCode: string }> { + settings: Settings; + selectedServer: SelectedServer; + shortUrlDetail: ShortUrlDetail; + getShortUrlDetail: (shortCode: string, domain: OptionalString) => void; +} + +const getInitialState = (shortUrl?: ShortUrl, settings?: ShortUrlCreationSettings): ShortUrlData => { + const validateUrl = settings?.validateUrls ?? false; + + if (!shortUrl) { + return { longUrl: '', validateUrl }; + } + + return { + longUrl: shortUrl.longUrl, + tags: shortUrl.tags, + title: shortUrl.title ?? undefined, + domain: shortUrl.domain ?? undefined, + validSince: shortUrl.meta.validSince ?? undefined, + validUntil: shortUrl.meta.validUntil ?? undefined, + maxVisits: shortUrl.meta.maxVisits ?? undefined, + validateUrl, + }; +}; + +export const EditShortUrl = (ShortUrlForm: FC) => ({ + match: { params }, + location: { search }, + settings: { shortUrlCreation: shortUrlCreationSettings }, + selectedServer, + shortUrlDetail, + getShortUrlDetail, +}: EditShortUrlConnectProps) => { + const { loading, error, errorData, shortUrl } = shortUrlDetail; + const { domain } = parseQuery<{ domain?: string }>(search); + + useEffect(() => { + getShortUrlDetail(params.shortCode, domain); + }, []); + + if (loading) { + return ; + } + + if (error) { + return ( + + + + ); + } + + return ( + Promise.resolve(console.log(shortUrlData))} + /> + ); +}; diff --git a/src/short-urls/ShortUrlForm.tsx b/src/short-urls/ShortUrlForm.tsx index abdb969e..8030a945 100644 --- a/src/short-urls/ShortUrlForm.tsx +++ b/src/short-urls/ShortUrlForm.tsx @@ -1,7 +1,7 @@ -import { FC, useState } from 'react'; +import { FC, useEffect, useState } from 'react'; import { InputType } from 'reactstrap/lib/Input'; import { Button, FormGroup, Input, Row } from 'reactstrap'; -import { isEmpty } from 'ramda'; +import { isEmpty, pipe, replace, trim } from 'ramda'; import * as m from 'moment'; import DateInput, { DateInputProps } from '../utils/DateInput'; import { @@ -18,7 +18,6 @@ import { Versions } from '../utils/helpers/version'; import { DomainSelectorProps } from '../domains/DomainSelector'; import { formatIsoDate } from '../utils/helpers/date'; import UseExistingIfFoundInfoIcon from './UseExistingIfFoundInfoIcon'; -import { normalizeTag } from './CreateShortUrl'; import { ShortUrlData } from './data'; import './ShortUrlForm.scss'; @@ -34,19 +33,26 @@ export interface ShortUrlFormProps { selectedServer: SelectedServer; } +const normalizeTag = pipe(trim, replace(/ /g, '-')); + export const ShortUrlForm = ( TagsSelector: FC, ForServerVersion: FC, DomainSelector: FC, ): FC => ({ mode, saving, onSave, initialState, selectedServer, children }) => { // eslint-disable-line complexity const [ shortUrlData, setShortUrlData ] = useState(initialState); + const isEdit = mode === 'edit'; const changeTags = (tags: string[]) => setShortUrlData({ ...shortUrlData, tags: tags.map(normalizeTag) }); const reset = () => setShortUrlData(initialState); const submit = handleEventPreventingDefault(async () => onSave({ ...shortUrlData, validSince: formatIsoDate(shortUrlData.validSince) ?? undefined, validUntil: formatIsoDate(shortUrlData.validUntil) ?? undefined, - }).then(reset).catch(() => {})); + }).then(() => !isEdit && reset()).catch(() => {})); + + useEffect(() => { + setShortUrlData(initialState); + }, [ initialState ]); const renderOptionalInput = (id: NonDateFields, placeholder: string, type: InputType = 'text', props = {}) => ( @@ -54,7 +60,7 @@ export const ShortUrlForm = ( id={id} type={type} placeholder={placeholder} - value={shortUrlData[id]} + value={shortUrlData[id] ?? ''} onChange={(e) => setShortUrlData({ ...shortUrlData, [id]: e.target.value })} {...props} /> @@ -93,7 +99,6 @@ export const ShortUrlForm = ( const showDomainSelector = supportsListingDomains(selectedServer); const disableShortCodeLength = !supportsSettingShortCodeLength(selectedServer); const supportsTitle = supportsShortUrlTitle(selectedServer); - const isEdit = mode === 'edit'; return (
@@ -191,7 +196,7 @@ export const ShortUrlForm = ( disabled={saving || isEmpty(shortUrlData.longUrl)} className="btn-xs-block" > - {saving ? 'Creating...' : 'Create'} + {saving ? 'Saving...' : 'Save'} diff --git a/src/short-urls/helpers/ShortUrlDetailLink.tsx b/src/short-urls/helpers/ShortUrlDetailLink.tsx new file mode 100644 index 00000000..2ba82055 --- /dev/null +++ b/src/short-urls/helpers/ShortUrlDetailLink.tsx @@ -0,0 +1,30 @@ +import { FC } from 'react'; +import { Link } from 'react-router-dom'; +import { isServerWithId, SelectedServer, ServerWithId } from '../../servers/data'; +import { ShortUrl } from '../data'; + +export type LinkSuffix = 'visits' | 'edit'; + +export interface ShortUrlDetailLinkProps { + shortUrl?: ShortUrl | null; + selectedServer?: SelectedServer; + suffix: LinkSuffix; +} + +const buildUrl = ({ id }: ServerWithId, { shortCode, domain }: ShortUrl, suffix: LinkSuffix) => { + const query = domain ? `?domain=${domain}` : ''; + + return `/server/${id}/short-code/${shortCode}/${suffix}${query}`; +}; + +const ShortUrlDetailLink: FC> = ( + { selectedServer, shortUrl, suffix, children, ...rest }, +) => { + if (!selectedServer || !isServerWithId(selectedServer) || !shortUrl) { + return {children}; + } + + return {children}; +}; + +export default ShortUrlDetailLink; diff --git a/src/short-urls/helpers/ShortUrlVisitsCount.tsx b/src/short-urls/helpers/ShortUrlVisitsCount.tsx index 6d4e4afa..7cb2f9a5 100644 --- a/src/short-urls/helpers/ShortUrlVisitsCount.tsx +++ b/src/short-urls/helpers/ShortUrlVisitsCount.tsx @@ -4,10 +4,14 @@ import { faInfoCircle as infoIcon } from '@fortawesome/free-solid-svg-icons'; import { UncontrolledTooltip } from 'reactstrap'; import classNames from 'classnames'; import { prettify } from '../../utils/helpers/numbers'; -import VisitStatsLink, { VisitStatsLinkProps } from './VisitStatsLink'; +import { ShortUrl } from '../data'; +import { SelectedServer } from '../../servers/data'; +import ShortUrlDetailLink from './ShortUrlDetailLink'; import './ShortUrlVisitsCount.scss'; -interface ShortUrlVisitsCountProps extends VisitStatsLinkProps { +interface ShortUrlVisitsCountProps { + shortUrl?: ShortUrl | null; + selectedServer?: SelectedServer; visitsCount: number; active?: boolean; } @@ -15,13 +19,13 @@ interface ShortUrlVisitsCountProps extends VisitStatsLinkProps { const ShortUrlVisitsCount = ({ visitsCount, shortUrl, selectedServer, active = false }: ShortUrlVisitsCountProps) => { const maxVisits = shortUrl?.meta?.maxVisits; const visitsLink = ( - + {prettify(visitsCount)} - + ); if (!maxVisits) { diff --git a/src/short-urls/helpers/ShortUrlsRowMenu.tsx b/src/short-urls/helpers/ShortUrlsRowMenu.tsx index e867473d..684d56bb 100644 --- a/src/short-urls/helpers/ShortUrlsRowMenu.tsx +++ b/src/short-urls/helpers/ShortUrlsRowMenu.tsx @@ -14,7 +14,7 @@ import { useToggle } from '../../utils/helpers/hooks'; import { ShortUrl, ShortUrlModalProps } from '../data'; import { Versions } from '../../utils/helpers/version'; import { SelectedServer } from '../../servers/data'; -import VisitStatsLink from './VisitStatsLink'; +import ShortUrlDetailLink from './ShortUrlDetailLink'; import './ShortUrlsRowMenu.scss'; export interface ShortUrlsRowMenuProps { @@ -44,10 +44,14 @@ const ShortUrlsRowMenu = (    - + Visit stats + + Edit short URL + + Edit tags diff --git a/src/short-urls/helpers/VisitStatsLink.tsx b/src/short-urls/helpers/VisitStatsLink.tsx deleted file mode 100644 index f80d9964..00000000 --- a/src/short-urls/helpers/VisitStatsLink.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import { FC } from 'react'; -import { Link } from 'react-router-dom'; -import { isServerWithId, SelectedServer, ServerWithId } from '../../servers/data'; -import { ShortUrl } from '../data'; - -export interface VisitStatsLinkProps { - shortUrl?: ShortUrl | null; - selectedServer?: SelectedServer; -} - -const buildVisitsUrl = ({ id }: ServerWithId, { shortCode, domain }: ShortUrl) => { - const query = domain ? `?domain=${domain}` : ''; - - return `/server/${id}/short-code/${shortCode}/visits${query}`; -}; - -const VisitStatsLink: FC> = ( - { selectedServer, shortUrl, children, ...rest }, -) => { - if (!selectedServer || !isServerWithId(selectedServer) || !shortUrl) { - return {children}; - } - - return {children}; -}; - -export default VisitStatsLink; diff --git a/src/short-urls/reducers/shortUrlDetail.ts b/src/short-urls/reducers/shortUrlDetail.ts index 42771a92..1b174f1d 100644 --- a/src/short-urls/reducers/shortUrlDetail.ts +++ b/src/short-urls/reducers/shortUrlDetail.ts @@ -5,6 +5,8 @@ import { ShlinkApiClientBuilder } from '../../api/services/ShlinkApiClientBuilde import { OptionalString } from '../../utils/utils'; import { GetState } from '../../container/types'; import { shortUrlMatches } from '../helpers'; +import { ProblemDetailsError } from '../../api/types'; +import { parseApiError } from '../../api/utils'; /* eslint-disable padding-line-between-statements */ export const GET_SHORT_URL_DETAIL_START = 'shlink/shortUrlDetail/GET_SHORT_URL_DETAIL_START'; @@ -16,20 +18,25 @@ export interface ShortUrlDetail { shortUrl?: ShortUrl; loading: boolean; error: boolean; + errorData?: ProblemDetailsError; } export interface ShortUrlDetailAction extends Action { shortUrl: ShortUrl; } +export interface ShortUrlDetailFailedAction extends Action { + errorData?: ProblemDetailsError; +} + const initialState: ShortUrlDetail = { loading: false, error: false, }; -export default buildReducer({ +export default buildReducer({ [GET_SHORT_URL_DETAIL_START]: () => ({ loading: true, error: false }), - [GET_SHORT_URL_DETAIL_ERROR]: () => ({ loading: false, error: true }), + [GET_SHORT_URL_DETAIL_ERROR]: (_, { errorData }) => ({ loading: false, error: true, errorData }), [GET_SHORT_URL_DETAIL]: (_, { shortUrl }) => ({ shortUrl, ...initialState }), }, initialState); @@ -47,6 +54,6 @@ export const getShortUrlDetail = (buildShlinkApiClient: ShlinkApiClientBuilder) dispatch({ shortUrl, type: GET_SHORT_URL_DETAIL }); } catch (e) { - dispatch({ type: GET_SHORT_URL_DETAIL_ERROR }); + dispatch({ type: GET_SHORT_URL_DETAIL_ERROR, errorData: parseApiError(e) }); } }; diff --git a/src/short-urls/services/provideServices.ts b/src/short-urls/services/provideServices.ts index ba4c8615..39820d0c 100644 --- a/src/short-urls/services/provideServices.ts +++ b/src/short-urls/services/provideServices.ts @@ -21,6 +21,8 @@ import { ConnectDecorator } from '../../container/types'; import { ShortUrlsTable } from '../ShortUrlsTable'; import QrCodeModal from '../helpers/QrCodeModal'; import { ShortUrlForm } from '../ShortUrlForm'; +import { EditShortUrl } from '../EditShortUrl'; +import { getShortUrlDetail } from '../reducers/shortUrlDetail'; const provideServices = (bottle: Bottle, connect: ConnectDecorator) => { // Components @@ -54,6 +56,12 @@ const provideServices = (bottle: Bottle, connect: ConnectDecorator) => { connect([ 'shortUrlCreationResult', 'selectedServer', 'settings' ], [ 'createShortUrl', 'resetCreateShortUrl' ]), ); + bottle.serviceFactory('EditShortUrl', EditShortUrl, 'ShortUrlForm'); + bottle.decorator( + 'EditShortUrl', + connect([ 'shortUrlDetail', 'selectedServer', 'settings' ], [ 'getShortUrlDetail' ]), + ); + bottle.serviceFactory('DeleteShortUrlModal', () => DeleteShortUrlModal); bottle.decorator('DeleteShortUrlModal', connect([ 'shortUrlDeletion' ], [ 'deleteShortUrl', 'resetDeleteShortUrl' ])); @@ -89,6 +97,8 @@ const provideServices = (bottle: Bottle, connect: ConnectDecorator) => { bottle.serviceFactory('editShortUrlMeta', editShortUrlMeta, 'buildShlinkApiClient'); bottle.serviceFactory('resetShortUrlMeta', () => resetShortUrlMeta); + bottle.serviceFactory('getShortUrlDetail', getShortUrlDetail, 'buildShlinkApiClient'); + bottle.serviceFactory('editShortUrl', editShortUrl, 'buildShlinkApiClient'); }; diff --git a/src/visits/services/provideServices.ts b/src/visits/services/provideServices.ts index 2bf91ab9..733cac95 100644 --- a/src/visits/services/provideServices.ts +++ b/src/visits/services/provideServices.ts @@ -1,7 +1,6 @@ import Bottle from 'bottlejs'; import ShortUrlVisits from '../ShortUrlVisits'; import { cancelGetShortUrlVisits, getShortUrlVisits } from '../reducers/shortUrlVisits'; -import { getShortUrlDetail } from '../../short-urls/reducers/shortUrlDetail'; import MapModal from '../helpers/MapModal'; import { createNewVisits } from '../reducers/visitCreation'; import TagVisits from '../TagVisits'; @@ -41,7 +40,6 @@ const provideServices = (bottle: Bottle, connect: ConnectDecorator) => { // Actions bottle.serviceFactory('getShortUrlVisits', getShortUrlVisits, 'buildShlinkApiClient'); - bottle.serviceFactory('getShortUrlDetail', getShortUrlDetail, 'buildShlinkApiClient'); bottle.serviceFactory('cancelGetShortUrlVisits', () => cancelGetShortUrlVisits); bottle.serviceFactory('getTagVisits', getTagVisits, 'buildShlinkApiClient'); diff --git a/test/common/MenuLayout.test.tsx b/test/common/MenuLayout.test.tsx index 1e7219b5..1177b0e1 100644 --- a/test/common/MenuLayout.test.tsx +++ b/test/common/MenuLayout.test.tsx @@ -11,7 +11,7 @@ import { SemVer } from '../../src/utils/helpers/version'; describe('', () => { const ServerError = jest.fn(); const C = jest.fn(); - const MenuLayout = createMenuLayout(C, C, C, C, C, C, C, ServerError, C); + const MenuLayout = createMenuLayout(C, C, C, C, C, C, C, ServerError, C, C); let wrapper: ShallowWrapper; const createWrapper = (selectedServer: SelectedServer) => { wrapper = shallow( @@ -49,11 +49,11 @@ describe('', () => { }); it.each([ - [ '2.1.0' as SemVer, 6 ], - [ '2.2.0' as SemVer, 7 ], - [ '2.5.0' as SemVer, 7 ], - [ '2.6.0' as SemVer, 8 ], - [ '2.7.0' as SemVer, 8 ], + [ '2.1.0' as SemVer, 7 ], + [ '2.2.0' as SemVer, 8 ], + [ '2.5.0' as SemVer, 8 ], + [ '2.6.0' as SemVer, 9 ], + [ '2.7.0' as SemVer, 9 ], ])('has expected amount of routes based on selected server\'s version', (version, expectedAmountOfRoutes) => { const selectedServer = Mock.of({ version }); const wrapper = createWrapper(selectedServer).dive(); diff --git a/test/short-urls/helpers/VisitStatsLink.test.tsx b/test/short-urls/helpers/ShortUrlDetailLink.test.tsx similarity index 59% rename from test/short-urls/helpers/VisitStatsLink.test.tsx rename to test/short-urls/helpers/ShortUrlDetailLink.test.tsx index 457d3024..9de7902d 100644 --- a/test/short-urls/helpers/VisitStatsLink.test.tsx +++ b/test/short-urls/helpers/ShortUrlDetailLink.test.tsx @@ -1,11 +1,11 @@ import { shallow, ShallowWrapper } from 'enzyme'; import { Link } from 'react-router-dom'; import { Mock } from 'ts-mockery'; -import VisitStatsLink from '../../../src/short-urls/helpers/VisitStatsLink'; +import ShortUrlDetailLink, { LinkSuffix } from '../../../src/short-urls/helpers/ShortUrlDetailLink'; import { NotFoundServer, ReachableServer } from '../../../src/servers/data'; import { ShortUrl } from '../../../src/short-urls/data'; -describe('', () => { +describe('', () => { let wrapper: ShallowWrapper; afterEach(() => wrapper?.unmount()); @@ -19,7 +19,11 @@ describe('', () => { [ null, Mock.all() ], [ undefined, Mock.all() ], ])('only renders a plain span when either server or short URL are not set', (selectedServer, shortUrl) => { - wrapper = shallow(Something); + wrapper = shallow( + + Something + , + ); const link = wrapper.find(Link); expect(link).toHaveLength(0); @@ -30,15 +34,33 @@ describe('', () => { [ Mock.of({ id: '1' }), Mock.of({ shortCode: 'abc123' }), + 'visits' as LinkSuffix, '/server/1/short-code/abc123/visits', ], [ Mock.of({ id: '3' }), Mock.of({ shortCode: 'def456', domain: 'example.com' }), + 'visits' as LinkSuffix, '/server/3/short-code/def456/visits?domain=example.com', ], - ])('renders link with expected query when', (selectedServer, shortUrl, expectedLink) => { - wrapper = shallow(Something); + [ + Mock.of({ id: '1' }), + Mock.of({ shortCode: 'abc123' }), + 'edit' as LinkSuffix, + '/server/1/short-code/abc123/edit', + ], + [ + Mock.of({ id: '3' }), + Mock.of({ shortCode: 'def456', domain: 'example.com' }), + 'edit' as LinkSuffix, + '/server/3/short-code/def456/edit?domain=example.com', + ], + ])('renders link with expected query when', (selectedServer, shortUrl, suffix, expectedLink) => { + wrapper = shallow( + + Something + , + ); const link = wrapper.find(Link); const to = link.prop('to'); diff --git a/test/short-urls/helpers/ShortUrlsRowMenu.test.tsx b/test/short-urls/helpers/ShortUrlsRowMenu.test.tsx index 1c1aac93..c25c7382 100644 --- a/test/short-urls/helpers/ShortUrlsRowMenu.test.tsx +++ b/test/short-urls/helpers/ShortUrlsRowMenu.test.tsx @@ -51,7 +51,7 @@ describe('', () => { const wrapper = createWrapper(); const items = wrapper.find(DropdownItem); - expect(items).toHaveLength(7); + expect(items).toHaveLength(8); expect(items.find('[divider]')).toHaveLength(1); }); diff --git a/test/short-urls/reducers/shortUrlDetail.test.ts b/test/short-urls/reducers/shortUrlDetail.test.ts index a83d1a4d..748d71f0 100644 --- a/test/short-urls/reducers/shortUrlDetail.test.ts +++ b/test/short-urls/reducers/shortUrlDetail.test.ts @@ -51,7 +51,7 @@ describe('shortUrlDetailReducer', () => { const buildGetState = (shortUrlsList?: ShortUrlsList) => () => Mock.of({ shortUrlsList }); it('dispatches start and error when promise is rejected', async () => { - const ShlinkApiClient = buildApiClientMock(Promise.reject()); + const ShlinkApiClient = buildApiClientMock(Promise.reject({})); await getShortUrlDetail(() => ShlinkApiClient)('abc123', '')(dispatchMock, buildGetState());