Ensured all data can be set when editing a short URL

This commit is contained in:
Alejandro Celaya 2021-03-27 09:49:47 +01:00
parent a019bd30df
commit eea76d88c3
15 changed files with 73 additions and 64 deletions

View file

@ -12,7 +12,7 @@ import {
ShlinkTagsResponse, ShlinkTagsResponse,
ShlinkVisits, ShlinkVisits,
ShlinkVisitsParams, ShlinkVisitsParams,
ShlinkShortUrlMeta, ShlinkShortUrlData,
ShlinkDomain, ShlinkDomain,
ShlinkDomainsResponse, ShlinkDomainsResponse,
ShlinkVisitsOverview, ShlinkVisitsOverview,
@ -67,7 +67,7 @@ export default class ShlinkApiClient {
this.performRequest(`/short-urls/${shortCode}`, 'DELETE', { domain }) this.performRequest(`/short-urls/${shortCode}`, 'DELETE', { domain })
.then(() => {}); .then(() => {});
/* @deprecated. If using Shlink 2.6.0 or greater, use updateShortUrlMeta instead */ /* @deprecated. If using Shlink 2.6.0 or greater, use updateShortUrl instead */
public readonly updateShortUrlTags = async ( public readonly updateShortUrlTags = async (
shortCode: string, shortCode: string,
domain: OptionalString, domain: OptionalString,
@ -76,12 +76,12 @@ export default class ShlinkApiClient {
this.performRequest<{ tags: string[] }>(`/short-urls/${shortCode}/tags`, 'PUT', { domain }, { tags }) this.performRequest<{ tags: string[] }>(`/short-urls/${shortCode}/tags`, 'PUT', { domain }, { tags })
.then(({ data }) => data.tags); .then(({ data }) => data.tags);
public readonly updateShortUrlMeta = async ( public readonly updateShortUrl = async (
shortCode: string, shortCode: string,
domain: OptionalString, domain: OptionalString,
meta: ShlinkShortUrlMeta, data: ShlinkShortUrlData,
): Promise<ShortUrl> => ): Promise<ShortUrl> =>
this.performRequest<ShortUrl>(`/short-urls/${shortCode}`, 'PATCH', { domain }, meta) this.performRequest<ShortUrl>(`/short-urls/${shortCode}`, 'PATCH', { domain }, data)
.then(({ data }) => data); .then(({ data }) => data);
public readonly listTags = async (): Promise<ShlinkTags> => public readonly listTags = async (): Promise<ShlinkTags> =>

View file

@ -57,8 +57,10 @@ export interface ShlinkVisitsParams {
endDate?: string; endDate?: string;
} }
export interface ShlinkShortUrlMeta extends ShortUrlMeta { export interface ShlinkShortUrlData extends ShortUrlMeta {
longUrl?: string; longUrl?: string;
title?: string;
validateUrl?: boolean;
tags?: string[]; tags?: string[];
} }

View file

@ -9,13 +9,14 @@ import { Result } from '../utils/Result';
import { ShlinkApiError } from '../api/ShlinkApiError'; import { ShlinkApiError } from '../api/ShlinkApiError';
import { ShortUrlFormProps } from './ShortUrlForm'; import { ShortUrlFormProps } from './ShortUrlForm';
import { ShortUrlDetail } from './reducers/shortUrlDetail'; import { ShortUrlDetail } from './reducers/shortUrlDetail';
import { ShortUrl, ShortUrlData } from './data'; import { EditShortUrlData, ShortUrl, ShortUrlData } from './data';
interface EditShortUrlConnectProps extends RouteComponentProps<{ shortCode: string }> { interface EditShortUrlConnectProps extends RouteComponentProps<{ shortCode: string }> {
settings: Settings; settings: Settings;
selectedServer: SelectedServer; selectedServer: SelectedServer;
shortUrlDetail: ShortUrlDetail; shortUrlDetail: ShortUrlDetail;
getShortUrlDetail: (shortCode: string, domain: OptionalString) => void; getShortUrlDetail: (shortCode: string, domain: OptionalString) => void;
editShortUrl: (shortUrl: string, domain: OptionalString, data: EditShortUrlData) => Promise<void>;
} }
const getInitialState = (shortUrl?: ShortUrl, settings?: ShortUrlCreationSettings): ShortUrlData => { const getInitialState = (shortUrl?: ShortUrl, settings?: ShortUrlCreationSettings): ShortUrlData => {
@ -44,6 +45,7 @@ export const EditShortUrl = (ShortUrlForm: FC<ShortUrlFormProps>) => ({
selectedServer, selectedServer,
shortUrlDetail, shortUrlDetail,
getShortUrlDetail, getShortUrlDetail,
editShortUrl,
}: EditShortUrlConnectProps) => { }: EditShortUrlConnectProps) => {
const { loading, error, errorData, shortUrl } = shortUrlDetail; const { loading, error, errorData, shortUrl } = shortUrlDetail;
const { domain } = parseQuery<{ domain?: string }>(search); const { domain } = parseQuery<{ domain?: string }>(search);
@ -70,7 +72,7 @@ export const EditShortUrl = (ShortUrlForm: FC<ShortUrlFormProps>) => ({
saving={false} saving={false}
selectedServer={selectedServer} selectedServer={selectedServer}
mode="edit" mode="edit"
onSave={async (shortUrlData) => Promise.resolve(console.log(shortUrlData))} onSave={async (shortUrlData) => shortUrl && editShortUrl(shortUrl.shortCode, shortUrl.domain, shortUrlData)}
/> />
); );
}; };

View file

@ -2,7 +2,7 @@ import { FC, useEffect, useState } from 'react';
import { InputType } from 'reactstrap/lib/Input'; import { InputType } from 'reactstrap/lib/Input';
import { Button, FormGroup, Input, Row } from 'reactstrap'; import { Button, FormGroup, Input, Row } from 'reactstrap';
import { isEmpty, pipe, replace, trim } from 'ramda'; import { isEmpty, pipe, replace, trim } from 'ramda';
import * as m from 'moment'; import m from 'moment';
import DateInput, { DateInputProps } from '../utils/DateInput'; import DateInput, { DateInputProps } from '../utils/DateInput';
import { import {
supportsListingDomains, supportsListingDomains,
@ -46,8 +46,9 @@ export const ShortUrlForm = (
const reset = () => setShortUrlData(initialState); const reset = () => setShortUrlData(initialState);
const submit = handleEventPreventingDefault(async () => onSave({ const submit = handleEventPreventingDefault(async () => onSave({
...shortUrlData, ...shortUrlData,
validSince: formatIsoDate(shortUrlData.validSince) ?? undefined, validSince: formatIsoDate(shortUrlData.validSince) ?? null,
validUntil: formatIsoDate(shortUrlData.validUntil) ?? undefined, validUntil: formatIsoDate(shortUrlData.validUntil) ?? null,
maxVisits: !hasValue(shortUrlData.maxVisits) ? null : Number(shortUrlData.maxVisits),
}).then(() => !isEdit && reset()).catch(() => {})); }).then(() => !isEdit && reset()).catch(() => {}));
useEffect(() => { useEffect(() => {
@ -69,7 +70,7 @@ export const ShortUrlForm = (
const renderDateInput = (id: DateFields, placeholder: string, props: Partial<DateInputProps> = {}) => ( const renderDateInput = (id: DateFields, placeholder: string, props: Partial<DateInputProps> = {}) => (
<div className="form-group"> <div className="form-group">
<DateInput <DateInput
selected={shortUrlData[id] as m.Moment | null} selected={shortUrlData[id] ? m(shortUrlData[id]) : null}
placeholderText={placeholder} placeholderText={placeholder}
isClearable isClearable
onChange={(date) => setShortUrlData({ ...shortUrlData, [id]: date })} onChange={(date) => setShortUrlData({ ...shortUrlData, [id]: date })}
@ -148,8 +149,8 @@ export const ShortUrlForm = (
<div className="col-sm-6 mb-3"> <div className="col-sm-6 mb-3">
<SimpleCard title="Limit access to the short URL"> <SimpleCard title="Limit access to the short URL">
{renderOptionalInput('maxVisits', 'Maximum number of visits allowed', 'number', { min: 1 })} {renderOptionalInput('maxVisits', 'Maximum number of visits allowed', 'number', { min: 1 })}
{renderDateInput('validSince', 'Enabled since...', { maxDate: shortUrlData.validUntil as m.Moment | undefined })} {renderDateInput('validSince', 'Enabled since...', { maxDate: shortUrlData.validUntil ? m(shortUrlData.validUntil) : undefined })}
{renderDateInput('validUntil', 'Enabled until...', { minDate: shortUrlData.validSince as m.Moment | undefined })} {renderDateInput('validUntil', 'Enabled until...', { minDate: shortUrlData.validSince ? m(shortUrlData.validSince) : undefined })}
</SimpleCard> </SimpleCard>
</div> </div>
</Row> </Row>

View file

@ -1,18 +1,22 @@
import * as m from 'moment'; import * as m from 'moment';
import { Nullable, OptionalString } from '../../utils/utils'; import { Nullable, OptionalString } from '../../utils/utils';
export interface ShortUrlData { export interface EditShortUrlData {
longUrl: string; longUrl?: string;
tags?: string[]; tags?: string[];
customSlug?: string;
title?: string; title?: string;
validSince?: m.Moment | string | null;
validUntil?: m.Moment | string | null;
maxVisits?: number | null;
validateUrl?: boolean;
}
export interface ShortUrlData extends EditShortUrlData {
longUrl: string;
customSlug?: string;
shortCodeLength?: number; shortCodeLength?: number;
domain?: string; domain?: string;
validSince?: m.Moment | string;
validUntil?: m.Moment | string;
maxVisits?: number;
findIfExists?: boolean; findIfExists?: boolean;
validateUrl?: boolean;
} }
export interface ShortUrl { export interface ShortUrl {

View file

@ -3,13 +3,13 @@ import { Modal, ModalBody, ModalFooter, ModalHeader, FormGroup, Input, Button }
import { ExternalLink } from 'react-external-link'; import { ExternalLink } from 'react-external-link';
import { ShortUrlEdition } from '../reducers/shortUrlEdition'; import { ShortUrlEdition } from '../reducers/shortUrlEdition';
import { handleEventPreventingDefault, hasValue, OptionalString } from '../../utils/utils'; import { handleEventPreventingDefault, hasValue, OptionalString } from '../../utils/utils';
import { ShortUrlModalProps } from '../data'; import { EditShortUrlData, ShortUrlModalProps } from '../data';
import { Result } from '../../utils/Result'; import { Result } from '../../utils/Result';
import { ShlinkApiError } from '../../api/ShlinkApiError'; import { ShlinkApiError } from '../../api/ShlinkApiError';
interface EditShortUrlModalProps extends ShortUrlModalProps { interface EditShortUrlModalProps extends ShortUrlModalProps {
shortUrlEdition: ShortUrlEdition; shortUrlEdition: ShortUrlEdition;
editShortUrl: (shortUrl: string, domain: OptionalString, longUrl: string) => Promise<void>; editShortUrl: (shortUrl: string, domain: OptionalString, data: EditShortUrlData) => Promise<void>;
} }
const EditShortUrlModal = ({ isOpen, toggle, shortUrl, shortUrlEdition, editShortUrl }: EditShortUrlModalProps) => { const EditShortUrlModal = ({ isOpen, toggle, shortUrl, shortUrlEdition, editShortUrl }: EditShortUrlModalProps) => {
@ -17,7 +17,7 @@ const EditShortUrlModal = ({ isOpen, toggle, shortUrl, shortUrlEdition, editShor
const url = shortUrl?.shortUrl ?? ''; const url = shortUrl?.shortUrl ?? '';
const [ longUrl, setLongUrl ] = useState(shortUrl.longUrl); const [ longUrl, setLongUrl ] = useState(shortUrl.longUrl);
const doEdit = async () => editShortUrl(shortUrl.shortCode, shortUrl.domain, longUrl).then(toggle); const doEdit = async () => editShortUrl(shortUrl.shortCode, shortUrl.domain, { longUrl }).then(toggle);
return ( return (
<Modal isOpen={isOpen} toggle={toggle} centered size="lg"> <Modal isOpen={isOpen} toggle={toggle} centered size="lg">

View file

@ -2,7 +2,7 @@ import { Action, Dispatch } from 'redux';
import { buildReducer } from '../../utils/helpers/redux'; import { buildReducer } from '../../utils/helpers/redux';
import { GetState } from '../../container/types'; import { GetState } from '../../container/types';
import { OptionalString } from '../../utils/utils'; import { OptionalString } from '../../utils/utils';
import { ShortUrlIdentifier } from '../data'; import { EditShortUrlData, ShortUrlIdentifier } from '../data';
import { ShlinkApiClientBuilder } from '../../api/services/ShlinkApiClientBuilder'; import { ShlinkApiClientBuilder } from '../../api/services/ShlinkApiClientBuilder';
import { ProblemDetailsError } from '../../api/types'; import { ProblemDetailsError } from '../../api/types';
import { parseApiError } from '../../api/utils'; import { parseApiError } from '../../api/utils';
@ -45,13 +45,16 @@ export default buildReducer<ShortUrlEdition, ShortUrlEditedAction & ShortUrlEdit
export const editShortUrl = (buildShlinkApiClient: ShlinkApiClientBuilder) => ( export const editShortUrl = (buildShlinkApiClient: ShlinkApiClientBuilder) => (
shortCode: string, shortCode: string,
domain: OptionalString, domain: OptionalString,
longUrl: string, data: EditShortUrlData,
) => async (dispatch: Dispatch, getState: GetState) => { ) => async (dispatch: Dispatch, getState: GetState) => {
dispatch({ type: EDIT_SHORT_URL_START }); dispatch({ type: EDIT_SHORT_URL_START });
const { updateShortUrlMeta } = buildShlinkApiClient(getState);
// TODO Pass tags to the updateTags function if server version is lower than 2.6
const { updateShortUrl } = buildShlinkApiClient(getState);
try { try {
await updateShortUrlMeta(shortCode, domain, { longUrl }); const { longUrl } = await updateShortUrl(shortCode, domain, data as any); // FIXME Parse dates
dispatch<ShortUrlEditedAction>({ shortCode, longUrl, domain, type: SHORT_URL_EDITED }); dispatch<ShortUrlEditedAction>({ shortCode, longUrl, domain, type: SHORT_URL_EDITED });
} catch (e) { } catch (e) {
dispatch<ShortUrlEditionFailedAction>({ type: EDIT_SHORT_URL_ERROR, errorData: parseApiError(e) }); dispatch<ShortUrlEditionFailedAction>({ type: EDIT_SHORT_URL_ERROR, errorData: parseApiError(e) });

View file

@ -50,10 +50,10 @@ export const editShortUrlMeta = (buildShlinkApiClient: ShlinkApiClientBuilder) =
meta: ShortUrlMeta, meta: ShortUrlMeta,
) => async (dispatch: Dispatch, getState: GetState) => { ) => async (dispatch: Dispatch, getState: GetState) => {
dispatch({ type: EDIT_SHORT_URL_META_START }); dispatch({ type: EDIT_SHORT_URL_META_START });
const { updateShortUrlMeta } = buildShlinkApiClient(getState); const { updateShortUrl } = buildShlinkApiClient(getState);
try { try {
await updateShortUrlMeta(shortCode, domain, meta); await updateShortUrl(shortCode, domain, meta);
dispatch<ShortUrlMetaEditedAction>({ shortCode, meta, domain, type: SHORT_URL_META_EDITED }); dispatch<ShortUrlMetaEditedAction>({ shortCode, meta, domain, type: SHORT_URL_META_EDITED });
} catch (e) { } catch (e) {
dispatch<ShortUrlMetaEditionFailedAction>({ type: EDIT_SHORT_URL_META_ERROR, errorData: parseApiError(e) }); dispatch<ShortUrlMetaEditionFailedAction>({ type: EDIT_SHORT_URL_META_ERROR, errorData: parseApiError(e) });

View file

@ -54,12 +54,12 @@ export const editShortUrlTags = (buildShlinkApiClient: ShlinkApiClientBuilder) =
dispatch({ type: EDIT_SHORT_URL_TAGS_START }); dispatch({ type: EDIT_SHORT_URL_TAGS_START });
const { selectedServer } = getState(); const { selectedServer } = getState();
const tagsInPatch = supportsTagsInPatch(selectedServer); const tagsInPatch = supportsTagsInPatch(selectedServer);
const { updateShortUrlTags, updateShortUrlMeta } = buildShlinkApiClient(getState); const { updateShortUrlTags, updateShortUrl } = buildShlinkApiClient(getState);
try { try {
const normalizedTags = await ( const normalizedTags = await (
tagsInPatch tagsInPatch
? updateShortUrlMeta(shortCode, domain, { tags }).then(prop('tags')) ? updateShortUrl(shortCode, domain, { tags }).then(prop('tags'))
: updateShortUrlTags(shortCode, domain, tags) : updateShortUrlTags(shortCode, domain, tags)
); );

View file

@ -59,7 +59,7 @@ const provideServices = (bottle: Bottle, connect: ConnectDecorator) => {
bottle.serviceFactory('EditShortUrl', EditShortUrl, 'ShortUrlForm'); bottle.serviceFactory('EditShortUrl', EditShortUrl, 'ShortUrlForm');
bottle.decorator( bottle.decorator(
'EditShortUrl', 'EditShortUrl',
connect([ 'shortUrlDetail', 'selectedServer', 'settings' ], [ 'getShortUrlDetail' ]), connect([ 'shortUrlDetail', 'selectedServer', 'settings' ], [ 'getShortUrlDetail', 'editShortUrl' ]),
); );
bottle.serviceFactory('DeleteShortUrlModal', () => DeleteShortUrlModal); bottle.serviceFactory('DeleteShortUrlModal', () => DeleteShortUrlModal);

View file

@ -48,10 +48,7 @@ describe('ShlinkApiClient', () => {
const axiosSpy = createAxiosMock({ data: shortUrl }); const axiosSpy = createAxiosMock({ data: shortUrl });
const { createShortUrl } = new ShlinkApiClient(axiosSpy, '', ''); const { createShortUrl } = new ShlinkApiClient(axiosSpy, '', '');
await createShortUrl( await createShortUrl({ longUrl: 'bar', customSlug: undefined, maxVisits: null });
// @ts-expect-error in case maxVisits is null, it needs to be ignored as if it was undefined
{ longUrl: 'bar', customSlug: undefined, maxVisits: null },
);
expect(axiosSpy).toHaveBeenCalledWith(expect.objectContaining({ data: { longUrl: 'bar' } })); expect(axiosSpy).toHaveBeenCalledWith(expect.objectContaining({ data: { longUrl: 'bar' } }));
}); });
@ -139,7 +136,7 @@ describe('ShlinkApiClient', () => {
}); });
}); });
describe('updateShortUrlMeta', () => { describe('updateShortUrl', () => {
it.each(shortCodesWithDomainCombinations)('properly updates short URL meta', async (shortCode, domain) => { it.each(shortCodesWithDomainCombinations)('properly updates short URL meta', async (shortCode, domain) => {
const meta = { const meta = {
maxVisits: 50, maxVisits: 50,
@ -147,9 +144,9 @@ describe('ShlinkApiClient', () => {
}; };
const expectedResp = Mock.of<ShortUrl>(); const expectedResp = Mock.of<ShortUrl>();
const axiosSpy = createAxiosMock({ data: expectedResp }); const axiosSpy = createAxiosMock({ data: expectedResp });
const { updateShortUrlMeta } = new ShlinkApiClient(axiosSpy, '', ''); const { updateShortUrl } = new ShlinkApiClient(axiosSpy, '', '');
const result = await updateShortUrlMeta(shortCode, domain, meta); const result = await updateShortUrl(shortCode, domain, meta);
expect(expectedResp).toEqual(result); expect(expectedResp).toEqual(result);
expect(axiosSpy).toHaveBeenCalledWith(expect.objectContaining({ expect(axiosSpy).toHaveBeenCalledWith(expect.objectContaining({

View file

@ -50,7 +50,7 @@ describe('<ShortUrlForm />', () => {
domain: 'example.com', domain: 'example.com',
validSince: validSince.format(), validSince: validSince.format(),
validUntil: validUntil.format(), validUntil: validUntil.format(),
maxVisits: '20', maxVisits: 20,
findIfExists: false, findIfExists: false,
shortCodeLength: 15, shortCodeLength: 15,
validateUrl: true, validateUrl: true,

View file

@ -42,19 +42,19 @@ describe('shortUrlEditionReducer', () => {
}); });
describe('editShortUrl', () => { describe('editShortUrl', () => {
const updateShortUrlMeta = jest.fn().mockResolvedValue({}); const updateShortUrl = jest.fn().mockResolvedValue({ longUrl });
const buildShlinkApiClient = jest.fn().mockReturnValue({ updateShortUrlMeta }); const buildShlinkApiClient = jest.fn().mockReturnValue({ updateShortUrl });
const dispatch = jest.fn(); const dispatch = jest.fn();
const getState = () => Mock.of<ShlinkState>(); const getState = () => Mock.of<ShlinkState>();
afterEach(jest.clearAllMocks); afterEach(jest.clearAllMocks);
it.each([[ undefined ], [ null ], [ 'example.com' ]])('dispatches long URL on success', async (domain) => { it.each([[ undefined ], [ null ], [ 'example.com' ]])('dispatches long URL on success', async (domain) => {
await editShortUrl(buildShlinkApiClient)(shortCode, domain, longUrl)(dispatch, getState); await editShortUrl(buildShlinkApiClient)(shortCode, domain, { longUrl })(dispatch, getState);
expect(buildShlinkApiClient).toHaveBeenCalledTimes(1); expect(buildShlinkApiClient).toHaveBeenCalledTimes(1);
expect(updateShortUrlMeta).toHaveBeenCalledTimes(1); expect(updateShortUrl).toHaveBeenCalledTimes(1);
expect(updateShortUrlMeta).toHaveBeenCalledWith(shortCode, domain, { longUrl }); expect(updateShortUrl).toHaveBeenCalledWith(shortCode, domain, { longUrl });
expect(dispatch).toHaveBeenCalledTimes(2); expect(dispatch).toHaveBeenCalledTimes(2);
expect(dispatch).toHaveBeenNthCalledWith(1, { type: EDIT_SHORT_URL_START }); expect(dispatch).toHaveBeenNthCalledWith(1, { type: EDIT_SHORT_URL_START });
expect(dispatch).toHaveBeenNthCalledWith(2, { type: SHORT_URL_EDITED, longUrl, shortCode, domain }); expect(dispatch).toHaveBeenNthCalledWith(2, { type: SHORT_URL_EDITED, longUrl, shortCode, domain });
@ -63,17 +63,17 @@ describe('shortUrlEditionReducer', () => {
it('dispatches error on failure', async () => { it('dispatches error on failure', async () => {
const error = new Error(); const error = new Error();
updateShortUrlMeta.mockRejectedValue(error); updateShortUrl.mockRejectedValue(error);
try { try {
await editShortUrl(buildShlinkApiClient)(shortCode, undefined, longUrl)(dispatch, getState); await editShortUrl(buildShlinkApiClient)(shortCode, undefined, { longUrl })(dispatch, getState);
} catch (e) { } catch (e) {
expect(e).toBe(error); expect(e).toBe(error);
} }
expect(buildShlinkApiClient).toHaveBeenCalledTimes(1); expect(buildShlinkApiClient).toHaveBeenCalledTimes(1);
expect(updateShortUrlMeta).toHaveBeenCalledTimes(1); expect(updateShortUrl).toHaveBeenCalledTimes(1);
expect(updateShortUrlMeta).toHaveBeenCalledWith(shortCode, undefined, { longUrl }); expect(updateShortUrl).toHaveBeenCalledWith(shortCode, undefined, { longUrl });
expect(dispatch).toHaveBeenCalledTimes(2); expect(dispatch).toHaveBeenCalledTimes(2);
expect(dispatch).toHaveBeenNthCalledWith(1, { type: EDIT_SHORT_URL_START }); expect(dispatch).toHaveBeenNthCalledWith(1, { type: EDIT_SHORT_URL_START });
expect(dispatch).toHaveBeenNthCalledWith(2, { type: EDIT_SHORT_URL_ERROR }); expect(dispatch).toHaveBeenNthCalledWith(2, { type: EDIT_SHORT_URL_ERROR });

View file

@ -56,8 +56,8 @@ describe('shortUrlMetaReducer', () => {
}); });
describe('editShortUrlMeta', () => { describe('editShortUrlMeta', () => {
const updateShortUrlMeta = jest.fn().mockResolvedValue({}); const updateShortUrl = jest.fn().mockResolvedValue({});
const buildShlinkApiClient = jest.fn().mockReturnValue({ updateShortUrlMeta }); const buildShlinkApiClient = jest.fn().mockReturnValue({ updateShortUrl });
const dispatch = jest.fn(); const dispatch = jest.fn();
const getState = () => Mock.all<ShlinkState>(); const getState = () => Mock.all<ShlinkState>();
@ -67,8 +67,8 @@ describe('shortUrlMetaReducer', () => {
await editShortUrlMeta(buildShlinkApiClient)(shortCode, domain, meta)(dispatch, getState); await editShortUrlMeta(buildShlinkApiClient)(shortCode, domain, meta)(dispatch, getState);
expect(buildShlinkApiClient).toHaveBeenCalledTimes(1); expect(buildShlinkApiClient).toHaveBeenCalledTimes(1);
expect(updateShortUrlMeta).toHaveBeenCalledTimes(1); expect(updateShortUrl).toHaveBeenCalledTimes(1);
expect(updateShortUrlMeta).toHaveBeenCalledWith(shortCode, domain, meta); expect(updateShortUrl).toHaveBeenCalledWith(shortCode, domain, meta);
expect(dispatch).toHaveBeenCalledTimes(2); expect(dispatch).toHaveBeenCalledTimes(2);
expect(dispatch).toHaveBeenNthCalledWith(1, { type: EDIT_SHORT_URL_META_START }); expect(dispatch).toHaveBeenNthCalledWith(1, { type: EDIT_SHORT_URL_META_START });
expect(dispatch).toHaveBeenNthCalledWith(2, { type: SHORT_URL_META_EDITED, meta, shortCode, domain }); expect(dispatch).toHaveBeenNthCalledWith(2, { type: SHORT_URL_META_EDITED, meta, shortCode, domain });
@ -77,7 +77,7 @@ describe('shortUrlMetaReducer', () => {
it('dispatches error on failure', async () => { it('dispatches error on failure', async () => {
const error = new Error(); const error = new Error();
updateShortUrlMeta.mockRejectedValue(error); updateShortUrl.mockRejectedValue(error);
try { try {
await editShortUrlMeta(buildShlinkApiClient)(shortCode, undefined, meta)(dispatch, getState); await editShortUrlMeta(buildShlinkApiClient)(shortCode, undefined, meta)(dispatch, getState);
@ -86,8 +86,8 @@ describe('shortUrlMetaReducer', () => {
} }
expect(buildShlinkApiClient).toHaveBeenCalledTimes(1); expect(buildShlinkApiClient).toHaveBeenCalledTimes(1);
expect(updateShortUrlMeta).toHaveBeenCalledTimes(1); expect(updateShortUrl).toHaveBeenCalledTimes(1);
expect(updateShortUrlMeta).toHaveBeenCalledWith(shortCode, undefined, meta); expect(updateShortUrl).toHaveBeenCalledWith(shortCode, undefined, meta);
expect(dispatch).toHaveBeenCalledTimes(2); expect(dispatch).toHaveBeenCalledTimes(2);
expect(dispatch).toHaveBeenNthCalledWith(1, { type: EDIT_SHORT_URL_META_START }); expect(dispatch).toHaveBeenNthCalledWith(1, { type: EDIT_SHORT_URL_META_START });
expect(dispatch).toHaveBeenNthCalledWith(2, { type: EDIT_SHORT_URL_META_ERROR }); expect(dispatch).toHaveBeenNthCalledWith(2, { type: EDIT_SHORT_URL_META_ERROR });

View file

@ -61,8 +61,8 @@ describe('shortUrlTagsReducer', () => {
describe('editShortUrlTags', () => { describe('editShortUrlTags', () => {
const updateShortUrlTags = jest.fn(); const updateShortUrlTags = jest.fn();
const updateShortUrlMeta = jest.fn(); const updateShortUrl = jest.fn();
const buildShlinkApiClient = jest.fn().mockReturnValue({ updateShortUrlTags, updateShortUrlMeta }); const buildShlinkApiClient = jest.fn().mockReturnValue({ updateShortUrlTags, updateShortUrl });
const dispatch = jest.fn(); const dispatch = jest.fn();
const buildGetState = (selectedServer?: SelectedServer) => () => Mock.of<ShlinkState>({ selectedServer }); const buildGetState = (selectedServer?: SelectedServer) => () => Mock.of<ShlinkState>({ selectedServer });
@ -78,7 +78,7 @@ describe('shortUrlTagsReducer', () => {
expect(buildShlinkApiClient).toHaveBeenCalledTimes(1); expect(buildShlinkApiClient).toHaveBeenCalledTimes(1);
expect(updateShortUrlTags).toHaveBeenCalledTimes(1); expect(updateShortUrlTags).toHaveBeenCalledTimes(1);
expect(updateShortUrlTags).toHaveBeenCalledWith(shortCode, domain, tags); expect(updateShortUrlTags).toHaveBeenCalledWith(shortCode, domain, tags);
expect(updateShortUrlMeta).not.toHaveBeenCalled(); expect(updateShortUrl).not.toHaveBeenCalled();
expect(dispatch).toHaveBeenCalledTimes(2); expect(dispatch).toHaveBeenCalledTimes(2);
expect(dispatch).toHaveBeenNthCalledWith(1, { type: EDIT_SHORT_URL_TAGS_START }); expect(dispatch).toHaveBeenNthCalledWith(1, { type: EDIT_SHORT_URL_TAGS_START });
expect(dispatch).toHaveBeenNthCalledWith( expect(dispatch).toHaveBeenNthCalledWith(
@ -87,10 +87,10 @@ describe('shortUrlTagsReducer', () => {
); );
}); });
it('calls updateShortUrlMeta when server is version 2.6.0 or above', async () => { it('calls updateShortUrl when server is version 2.6.0 or above', async () => {
const normalizedTags = [ 'bar', 'foo' ]; const normalizedTags = [ 'bar', 'foo' ];
updateShortUrlMeta.mockResolvedValue({ tags: normalizedTags }); updateShortUrl.mockResolvedValue({ tags: normalizedTags });
await editShortUrlTags(buildShlinkApiClient)(shortCode, undefined, tags)( await editShortUrlTags(buildShlinkApiClient)(shortCode, undefined, tags)(
dispatch, dispatch,
@ -98,8 +98,8 @@ describe('shortUrlTagsReducer', () => {
); );
expect(buildShlinkApiClient).toHaveBeenCalledTimes(1); expect(buildShlinkApiClient).toHaveBeenCalledTimes(1);
expect(updateShortUrlMeta).toHaveBeenCalledTimes(1); expect(updateShortUrl).toHaveBeenCalledTimes(1);
expect(updateShortUrlMeta).toHaveBeenCalledWith(shortCode, undefined, { tags }); expect(updateShortUrl).toHaveBeenCalledWith(shortCode, undefined, { tags });
expect(updateShortUrlTags).not.toHaveBeenCalled(); expect(updateShortUrlTags).not.toHaveBeenCalled();
expect(dispatch).toHaveBeenCalledTimes(2); expect(dispatch).toHaveBeenCalledTimes(2);
expect(dispatch).toHaveBeenNthCalledWith(1, { type: EDIT_SHORT_URL_TAGS_START }); expect(dispatch).toHaveBeenNthCalledWith(1, { type: EDIT_SHORT_URL_TAGS_START });