diff --git a/src/container/types.ts b/src/container/types.ts index 3b4fff27..95b234ac 100644 --- a/src/container/types.ts +++ b/src/container/types.ts @@ -1,12 +1,10 @@ import { MercureInfo } from '../mercure/reducers/mercureInfo'; import { SelectedServer, ServersMap } from '../servers/data'; import { Settings } from '../settings/reducers/settings'; -import { ShortUrlMetaEdition } from '../short-urls/reducers/shortUrlMeta'; import { ShortUrlCreation } from '../short-urls/reducers/shortUrlCreation'; import { ShortUrlDeletion } from '../short-urls/reducers/shortUrlDeletion'; import { ShortUrlEdition } from '../short-urls/reducers/shortUrlEdition'; import { ShortUrlsListParams } from '../short-urls/reducers/shortUrlsListParams'; -import { ShortUrlTags } from '../short-urls/reducers/shortUrlTags'; import { ShortUrlsList } from '../short-urls/reducers/shortUrlsList'; import { TagDeletion } from '../tags/reducers/tagDelete'; import { TagEdition } from '../tags/reducers/tagEdit'; @@ -25,8 +23,6 @@ export interface ShlinkState { shortUrlsListParams: ShortUrlsListParams; shortUrlCreationResult: ShortUrlCreation; shortUrlDeletion: ShortUrlDeletion; - shortUrlTags: ShortUrlTags; - shortUrlMeta: ShortUrlMetaEdition; shortUrlEdition: ShortUrlEdition; shortUrlVisits: ShortUrlVisits; tagVisits: TagVisits; diff --git a/src/reducers/index.ts b/src/reducers/index.ts index 29311fa7..d764f54d 100644 --- a/src/reducers/index.ts +++ b/src/reducers/index.ts @@ -5,8 +5,6 @@ import shortUrlsListReducer from '../short-urls/reducers/shortUrlsList'; import shortUrlsListParamsReducer from '../short-urls/reducers/shortUrlsListParams'; import shortUrlCreationReducer from '../short-urls/reducers/shortUrlCreation'; import shortUrlDeletionReducer from '../short-urls/reducers/shortUrlDeletion'; -import shortUrlTagsReducer from '../short-urls/reducers/shortUrlTags'; -import shortUrlMetaReducer from '../short-urls/reducers/shortUrlMeta'; import shortUrlEditionReducer from '../short-urls/reducers/shortUrlEdition'; import shortUrlVisitsReducer from '../visits/reducers/shortUrlVisits'; import tagVisitsReducer from '../visits/reducers/tagVisits'; @@ -28,8 +26,6 @@ export default combineReducers({ shortUrlsListParams: shortUrlsListParamsReducer, shortUrlCreationResult: shortUrlCreationReducer, shortUrlDeletion: shortUrlDeletionReducer, - shortUrlTags: shortUrlTagsReducer, - shortUrlMeta: shortUrlMetaReducer, shortUrlEdition: shortUrlEditionReducer, shortUrlVisits: shortUrlVisitsReducer, tagVisits: tagVisitsReducer, diff --git a/src/short-urls/reducers/shortUrlEdition.ts b/src/short-urls/reducers/shortUrlEdition.ts index 1663f403..50537fb1 100644 --- a/src/short-urls/reducers/shortUrlEdition.ts +++ b/src/short-urls/reducers/shortUrlEdition.ts @@ -2,10 +2,11 @@ import { Action, Dispatch } from 'redux'; import { buildReducer } from '../../utils/helpers/redux'; import { GetState } from '../../container/types'; import { OptionalString } from '../../utils/utils'; -import { EditShortUrlData, ShortUrlIdentifier } from '../data'; +import { EditShortUrlData, ShortUrl } from '../data'; import { ShlinkApiClientBuilder } from '../../api/services/ShlinkApiClientBuilder'; import { ProblemDetailsError } from '../../api/types'; import { parseApiError } from '../../api/utils'; +import { supportsTagsInPatch } from '../../utils/helpers/features'; /* eslint-disable padding-line-between-statements */ export const EDIT_SHORT_URL_START = 'shlink/shortUrlEdition/EDIT_SHORT_URL_START'; @@ -14,15 +15,14 @@ export const SHORT_URL_EDITED = 'shlink/shortUrlEdition/SHORT_URL_EDITED'; /* eslint-enable padding-line-between-statements */ export interface ShortUrlEdition { - shortCode: string | null; - longUrl: string | null; + shortUrl?: ShortUrl; saving: boolean; error: boolean; errorData?: ProblemDetailsError; } -export interface ShortUrlEditedAction extends Action, ShortUrlIdentifier { - longUrl: string; +export interface ShortUrlEditedAction extends Action { + shortUrl: ShortUrl; } export interface ShortUrlEditionFailedAction extends Action { @@ -30,8 +30,6 @@ export interface ShortUrlEditionFailedAction extends Action { } const initialState: ShortUrlEdition = { - shortCode: null, - longUrl: null, saving: false, error: false, }; @@ -39,7 +37,7 @@ const initialState: ShortUrlEdition = { export default buildReducer({ [EDIT_SHORT_URL_START]: (state) => ({ ...state, saving: true, error: false }), [EDIT_SHORT_URL_ERROR]: (state, { errorData }) => ({ ...state, saving: false, error: true, errorData }), - [SHORT_URL_EDITED]: (_, { shortCode, longUrl }) => ({ shortCode, longUrl, saving: false, error: false }), + [SHORT_URL_EDITED]: (_, { shortUrl }) => ({ shortUrl, saving: false, error: false }), }, initialState); export const editShortUrl = (buildShlinkApiClient: ShlinkApiClientBuilder) => ( @@ -49,13 +47,17 @@ export const editShortUrl = (buildShlinkApiClient: ShlinkApiClientBuilder) => ( ) => async (dispatch: Dispatch, getState: GetState) => { dispatch({ type: EDIT_SHORT_URL_START }); - // TODO Pass tags to the updateTags function if server version is lower than 2.6 - const { updateShortUrl } = buildShlinkApiClient(getState); + const { selectedServer } = getState(); + const sendTagsSeparately = !supportsTagsInPatch(selectedServer); + const { updateShortUrl, updateShortUrlTags } = buildShlinkApiClient(getState); try { - const { longUrl } = await updateShortUrl(shortCode, domain, data as any); // FIXME Parse dates + const [ shortUrl ] = await Promise.all([ + updateShortUrl(shortCode, domain, data as any), // FIXME Parse dates + sendTagsSeparately && data.tags ? updateShortUrlTags(shortCode, domain, data.tags) : undefined, + ]); - dispatch({ shortCode, longUrl, domain, type: SHORT_URL_EDITED }); + dispatch({ shortUrl, type: SHORT_URL_EDITED }); } catch (e) { dispatch({ type: EDIT_SHORT_URL_ERROR, errorData: parseApiError(e) }); diff --git a/src/short-urls/reducers/shortUrlMeta.ts b/src/short-urls/reducers/shortUrlMeta.ts deleted file mode 100644 index 7305570e..00000000 --- a/src/short-urls/reducers/shortUrlMeta.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { Dispatch, Action } from 'redux'; -import { ShortUrlIdentifier, ShortUrlMeta } from '../data'; -import { GetState } from '../../container/types'; -import { buildActionCreator, buildReducer } from '../../utils/helpers/redux'; -import { OptionalString } from '../../utils/utils'; -import { ShlinkApiClientBuilder } from '../../api/services/ShlinkApiClientBuilder'; -import { ProblemDetailsError } from '../../api/types'; -import { parseApiError } from '../../api/utils'; - -/* eslint-disable padding-line-between-statements */ -export const EDIT_SHORT_URL_META_START = 'shlink/shortUrlMeta/EDIT_SHORT_URL_META_START'; -export const EDIT_SHORT_URL_META_ERROR = 'shlink/shortUrlMeta/EDIT_SHORT_URL_META_ERROR'; -export const SHORT_URL_META_EDITED = 'shlink/shortUrlMeta/SHORT_URL_META_EDITED'; -export const RESET_EDIT_SHORT_URL_META = 'shlink/shortUrlMeta/RESET_EDIT_SHORT_URL_META'; -/* eslint-enable padding-line-between-statements */ - -export interface ShortUrlMetaEdition { - shortCode: string | null; - meta: ShortUrlMeta; - saving: boolean; - error: boolean; - errorData?: ProblemDetailsError; -} - -export interface ShortUrlMetaEditedAction extends Action, ShortUrlIdentifier { - meta: ShortUrlMeta; -} - -export interface ShortUrlMetaEditionFailedAction extends Action { - errorData?: ProblemDetailsError; -} - -const initialState: ShortUrlMetaEdition = { - shortCode: null, - meta: {}, - saving: false, - error: false, -}; - -export default buildReducer({ - [EDIT_SHORT_URL_META_START]: (state) => ({ ...state, saving: true, error: false }), - [EDIT_SHORT_URL_META_ERROR]: (state, { errorData }) => ({ ...state, saving: false, error: true, errorData }), - [SHORT_URL_META_EDITED]: (_, { shortCode, meta }) => ({ shortCode, meta, saving: false, error: false }), - [RESET_EDIT_SHORT_URL_META]: () => initialState, -}, initialState); - -export const editShortUrlMeta = (buildShlinkApiClient: ShlinkApiClientBuilder) => ( - shortCode: string, - domain: OptionalString, - meta: ShortUrlMeta, -) => async (dispatch: Dispatch, getState: GetState) => { - dispatch({ type: EDIT_SHORT_URL_META_START }); - const { updateShortUrl } = buildShlinkApiClient(getState); - - try { - await updateShortUrl(shortCode, domain, meta); - dispatch({ shortCode, meta, domain, type: SHORT_URL_META_EDITED }); - } catch (e) { - dispatch({ type: EDIT_SHORT_URL_META_ERROR, errorData: parseApiError(e) }); - - throw e; - } -}; - -export const resetShortUrlMeta = buildActionCreator(RESET_EDIT_SHORT_URL_META); diff --git a/src/short-urls/reducers/shortUrlTags.ts b/src/short-urls/reducers/shortUrlTags.ts deleted file mode 100644 index 2340b571..00000000 --- a/src/short-urls/reducers/shortUrlTags.ts +++ /dev/null @@ -1,74 +0,0 @@ -import { Action, Dispatch } from 'redux'; -import { prop } from 'ramda'; -import { buildActionCreator, buildReducer } from '../../utils/helpers/redux'; -import { GetState } from '../../container/types'; -import { OptionalString } from '../../utils/utils'; -import { ShortUrlIdentifier } from '../data'; -import { ShlinkApiClientBuilder } from '../../api/services/ShlinkApiClientBuilder'; -import { ProblemDetailsError } from '../../api/types'; -import { parseApiError } from '../../api/utils'; -import { supportsTagsInPatch } from '../../utils/helpers/features'; - -/* eslint-disable padding-line-between-statements */ -export const EDIT_SHORT_URL_TAGS_START = 'shlink/shortUrlTags/EDIT_SHORT_URL_TAGS_START'; -export const EDIT_SHORT_URL_TAGS_ERROR = 'shlink/shortUrlTags/EDIT_SHORT_URL_TAGS_ERROR'; -export const SHORT_URL_TAGS_EDITED = 'shlink/shortUrlTags/SHORT_URL_TAGS_EDITED'; -export const RESET_EDIT_SHORT_URL_TAGS = 'shlink/shortUrlTags/RESET_EDIT_SHORT_URL_TAGS'; -/* eslint-enable padding-line-between-statements */ - -export interface ShortUrlTags { - shortCode: string | null; - tags: string[]; - saving: boolean; - error: boolean; - errorData?: ProblemDetailsError; -} - -export interface EditShortUrlTagsAction extends Action, ShortUrlIdentifier { - tags: string[]; -} - -export interface EditShortUrlTagsFailedAction extends Action { - errorData?: ProblemDetailsError; -} - -const initialState: ShortUrlTags = { - shortCode: null, - tags: [], - saving: false, - error: false, -}; - -export default buildReducer({ - [EDIT_SHORT_URL_TAGS_START]: (state) => ({ ...state, saving: true, error: false }), - [EDIT_SHORT_URL_TAGS_ERROR]: (state, { errorData }) => ({ ...state, saving: false, error: true, errorData }), - [SHORT_URL_TAGS_EDITED]: (_, { shortCode, tags }) => ({ shortCode, tags, saving: false, error: false }), - [RESET_EDIT_SHORT_URL_TAGS]: () => initialState, -}, initialState); - -export const editShortUrlTags = (buildShlinkApiClient: ShlinkApiClientBuilder) => ( - shortCode: string, - domain: OptionalString, - tags: string[], -) => async (dispatch: Dispatch, getState: GetState) => { - dispatch({ type: EDIT_SHORT_URL_TAGS_START }); - const { selectedServer } = getState(); - const tagsInPatch = supportsTagsInPatch(selectedServer); - const { updateShortUrlTags, updateShortUrl } = buildShlinkApiClient(getState); - - try { - const normalizedTags = await ( - tagsInPatch - ? updateShortUrl(shortCode, domain, { tags }).then(prop('tags')) - : updateShortUrlTags(shortCode, domain, tags) - ); - - dispatch({ tags: normalizedTags, shortCode, domain, type: SHORT_URL_TAGS_EDITED }); - } catch (e) { - dispatch({ type: EDIT_SHORT_URL_TAGS_ERROR, errorData: parseApiError(e) }); - - throw e; - } -}; - -export const resetShortUrlsTags = buildActionCreator(RESET_EDIT_SHORT_URL_TAGS); diff --git a/src/short-urls/reducers/shortUrlsList.ts b/src/short-urls/reducers/shortUrlsList.ts index 1ff640f0..6ae5765f 100644 --- a/src/short-urls/reducers/shortUrlsList.ts +++ b/src/short-urls/reducers/shortUrlsList.ts @@ -2,15 +2,11 @@ import { assoc, assocPath, init, last, pipe, reject } from 'ramda'; import { Action, Dispatch } from 'redux'; import { shortUrlMatches } from '../helpers'; import { CREATE_VISITS, CreateVisitsAction } from '../../visits/reducers/visitCreation'; -import { ShortUrl, ShortUrlIdentifier } from '../data'; import { buildReducer } from '../../utils/helpers/redux'; import { GetState } from '../../container/types'; import { ShlinkApiClientBuilder } from '../../api/services/ShlinkApiClientBuilder'; import { ShlinkShortUrlsResponse } from '../../api/types'; -import { EditShortUrlTagsAction, SHORT_URL_TAGS_EDITED } from './shortUrlTags'; import { DeleteShortUrlAction, SHORT_URL_DELETED } from './shortUrlDeletion'; -import { SHORT_URL_META_EDITED, ShortUrlMetaEditedAction } from './shortUrlMeta'; -import { SHORT_URL_EDITED, ShortUrlEditedAction } from './shortUrlEdition'; import { ShortUrlsListParams } from './shortUrlsListParams'; import { CREATE_SHORT_URL, CreateShortUrlAction } from './shortUrlCreation'; @@ -33,9 +29,6 @@ export interface ListShortUrlsAction extends Action { export type ListShortUrlsCombinedAction = ( ListShortUrlsAction - & EditShortUrlTagsAction - & ShortUrlEditedAction - & ShortUrlMetaEditedAction & CreateVisitsAction & CreateShortUrlAction & DeleteShortUrlAction @@ -46,18 +39,6 @@ const initialState: ShortUrlsList = { error: false, }; -const setPropFromActionOnMatchingShortUrl = (prop: keyof T) => ( - state: ShortUrlsList, - { shortCode, domain, [prop]: propValue }: T, -): ShortUrlsList => !state.shortUrls ? state : assocPath( - [ 'shortUrls', 'data' ], - state.shortUrls.data.map( - (shortUrl: ShortUrl) => - shortUrlMatches(shortUrl, shortCode, domain) ? { ...shortUrl, [prop]: propValue } : shortUrl, - ), - state, -); - export default buildReducer({ [LIST_SHORT_URLS_START]: (state) => ({ ...state, loading: true, error: false }), [LIST_SHORT_URLS_ERROR]: () => ({ loading: false, error: true }), @@ -74,9 +55,6 @@ export default buildReducer({ state, ), ), - [SHORT_URL_TAGS_EDITED]: setPropFromActionOnMatchingShortUrl('tags'), - [SHORT_URL_META_EDITED]: setPropFromActionOnMatchingShortUrl('meta'), - [SHORT_URL_EDITED]: setPropFromActionOnMatchingShortUrl('longUrl'), [CREATE_VISITS]: (state, { createdVisits }) => assocPath( [ 'shortUrls', 'data' ], state.shortUrls?.data?.map( diff --git a/src/short-urls/services/provideServices.ts b/src/short-urls/services/provideServices.ts index 927ae87b..9caa6d36 100644 --- a/src/short-urls/services/provideServices.ts +++ b/src/short-urls/services/provideServices.ts @@ -10,8 +10,6 @@ import CreateShortUrlResult from '../helpers/CreateShortUrlResult'; import { listShortUrls } from '../reducers/shortUrlsList'; import { createShortUrl, resetCreateShortUrl } from '../reducers/shortUrlCreation'; import { deleteShortUrl, resetDeleteShortUrl } from '../reducers/shortUrlDeletion'; -import { editShortUrlTags, resetShortUrlsTags } from '../reducers/shortUrlTags'; -import { editShortUrlMeta, resetShortUrlMeta } from '../reducers/shortUrlMeta'; import { resetShortUrlParams } from '../reducers/shortUrlsListParams'; import { editShortUrl } from '../reducers/shortUrlEdition'; import { ConnectDecorator } from '../../container/types'; @@ -61,9 +59,6 @@ const provideServices = (bottle: Bottle, connect: ConnectDecorator) => { bottle.decorator('SearchBar', connect([ 'shortUrlsListParams' ], [ 'listShortUrls' ])); // Actions - bottle.serviceFactory('editShortUrlTags', editShortUrlTags, 'buildShlinkApiClient'); - bottle.serviceFactory('resetShortUrlsTags', () => resetShortUrlsTags); - bottle.serviceFactory('listShortUrls', listShortUrls, 'buildShlinkApiClient'); bottle.serviceFactory('resetShortUrlParams', () => resetShortUrlParams); @@ -73,9 +68,6 @@ const provideServices = (bottle: Bottle, connect: ConnectDecorator) => { bottle.serviceFactory('deleteShortUrl', deleteShortUrl, 'buildShlinkApiClient'); bottle.serviceFactory('resetDeleteShortUrl', () => resetDeleteShortUrl); - bottle.serviceFactory('editShortUrlMeta', editShortUrlMeta, 'buildShlinkApiClient'); - bottle.serviceFactory('resetShortUrlMeta', () => resetShortUrlMeta); - bottle.serviceFactory('getShortUrlDetail', getShortUrlDetail, 'buildShlinkApiClient'); bottle.serviceFactory('editShortUrl', editShortUrl, 'buildShlinkApiClient'); diff --git a/test/short-urls/reducers/shortUrlEdition.test.ts b/test/short-urls/reducers/shortUrlEdition.test.ts index 8e109cf5..5775f5b8 100644 --- a/test/short-urls/reducers/shortUrlEdition.test.ts +++ b/test/short-urls/reducers/shortUrlEdition.test.ts @@ -7,16 +7,17 @@ import reducer, { ShortUrlEditedAction, } from '../../../src/short-urls/reducers/shortUrlEdition'; import { ShlinkState } from '../../../src/container/types'; +import { ShortUrl } from '../../../src/short-urls/data'; +import { ReachableServer, SelectedServer } from '../../../src/servers/data'; describe('shortUrlEditionReducer', () => { const longUrl = 'https://shlink.io'; const shortCode = 'abc123'; + const shortUrl = Mock.of({ longUrl, shortCode }); describe('reducer', () => { it('returns loading on EDIT_SHORT_URL_START', () => { expect(reducer(undefined, Mock.of({ type: EDIT_SHORT_URL_START }))).toEqual({ - longUrl: null, - shortCode: null, saving: true, error: false, }); @@ -24,17 +25,14 @@ describe('shortUrlEditionReducer', () => { it('returns error on EDIT_SHORT_URL_ERROR', () => { expect(reducer(undefined, Mock.of({ type: EDIT_SHORT_URL_ERROR }))).toEqual({ - longUrl: null, - shortCode: null, saving: false, error: true, }); }); it('returns provided tags and shortCode on SHORT_URL_EDITED', () => { - expect(reducer(undefined, { type: SHORT_URL_EDITED, longUrl, shortCode, domain: null })).toEqual({ - longUrl, - shortCode, + expect(reducer(undefined, { type: SHORT_URL_EDITED, shortUrl })).toEqual({ + shortUrl, saving: false, error: false, }); @@ -42,31 +40,51 @@ describe('shortUrlEditionReducer', () => { }); describe('editShortUrl', () => { - const updateShortUrl = jest.fn().mockResolvedValue({ longUrl }); - const buildShlinkApiClient = jest.fn().mockReturnValue({ updateShortUrl }); + const updateShortUrl = jest.fn().mockResolvedValue(shortUrl); + const updateShortUrlTags = jest.fn().mockResolvedValue([]); + const buildShlinkApiClient = jest.fn().mockReturnValue({ updateShortUrl, updateShortUrlTags }); const dispatch = jest.fn(); - const getState = () => Mock.of(); + const createGetState = (selectedServer: SelectedServer = null) => () => Mock.of({ selectedServer }); afterEach(jest.clearAllMocks); - it.each([[ undefined ], [ null ], [ 'example.com' ]])('dispatches long URL on success', async (domain) => { - await editShortUrl(buildShlinkApiClient)(shortCode, domain, { longUrl })(dispatch, getState); + it.each([[ undefined ], [ null ], [ 'example.com' ]])('dispatches short URL on success', async (domain) => { + await editShortUrl(buildShlinkApiClient)(shortCode, domain, { longUrl })(dispatch, createGetState()); expect(buildShlinkApiClient).toHaveBeenCalledTimes(1); expect(updateShortUrl).toHaveBeenCalledTimes(1); expect(updateShortUrl).toHaveBeenCalledWith(shortCode, domain, { longUrl }); expect(dispatch).toHaveBeenCalledTimes(2); 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, shortUrl }); }); + it.each([ + [ null, { tags: [ 'foo', 'bar' ] }, 1 ], + [ null, {}, 0 ], + [ Mock.of({ version: '2.6.0' }), {}, 0 ], + [ Mock.of({ version: '2.6.0' }), { tags: [ 'foo', 'bar' ] }, 0 ], + [ Mock.of({ version: '2.5.0' }), {}, 0 ], + [ Mock.of({ version: '2.5.0' }), { tags: [ 'foo', 'bar' ] }, 1 ], + ])( + 'sends tags separately when appropriate, based on selected server and the payload', + async (server, payload, expectedTagsCalls) => { + const getState = createGetState(server); + + await editShortUrl(buildShlinkApiClient)(shortCode, null, payload)(dispatch, getState); + + expect(updateShortUrl).toHaveBeenCalled(); + expect(updateShortUrlTags).toHaveBeenCalledTimes(expectedTagsCalls); + }, + ); + it('dispatches error on failure', async () => { const error = new Error(); updateShortUrl.mockRejectedValue(error); try { - await editShortUrl(buildShlinkApiClient)(shortCode, undefined, { longUrl })(dispatch, getState); + await editShortUrl(buildShlinkApiClient)(shortCode, undefined, { longUrl })(dispatch, createGetState()); } catch (e) { expect(e).toBe(error); } diff --git a/test/short-urls/reducers/shortUrlMeta.test.ts b/test/short-urls/reducers/shortUrlMeta.test.ts deleted file mode 100644 index cb20b85c..00000000 --- a/test/short-urls/reducers/shortUrlMeta.test.ts +++ /dev/null @@ -1,100 +0,0 @@ -import moment from 'moment'; -import { Mock } from 'ts-mockery'; -import reducer, { - EDIT_SHORT_URL_META_START, - EDIT_SHORT_URL_META_ERROR, - SHORT_URL_META_EDITED, - RESET_EDIT_SHORT_URL_META, - editShortUrlMeta, - resetShortUrlMeta, -} from '../../../src/short-urls/reducers/shortUrlMeta'; -import { ShlinkState } from '../../../src/container/types'; - -describe('shortUrlMetaReducer', () => { - const meta = { - maxVisits: 50, - startDate: moment('2020-01-01').format(), - }; - const shortCode = 'abc123'; - - describe('reducer', () => { - it('returns loading on EDIT_SHORT_URL_META_START', () => { - expect(reducer(undefined, { type: EDIT_SHORT_URL_META_START } as any)).toEqual({ - meta: {}, - shortCode: null, - saving: true, - error: false, - }); - }); - - it('returns error on EDIT_SHORT_URL_META_ERROR', () => { - expect(reducer(undefined, { type: EDIT_SHORT_URL_META_ERROR } as any)).toEqual({ - meta: {}, - shortCode: null, - saving: false, - error: true, - }); - }); - - it('returns provided tags and shortCode on SHORT_URL_META_EDITED', () => { - expect(reducer(undefined, { type: SHORT_URL_META_EDITED, meta, shortCode } as any)).toEqual({ - meta, - shortCode, - saving: false, - error: false, - }); - }); - - it('goes back to initial state on RESET_EDIT_SHORT_URL_META', () => { - expect(reducer(undefined, { type: RESET_EDIT_SHORT_URL_META } as any)).toEqual({ - meta: {}, - shortCode: null, - saving: false, - error: false, - }); - }); - }); - - describe('editShortUrlMeta', () => { - const updateShortUrl = jest.fn().mockResolvedValue({}); - const buildShlinkApiClient = jest.fn().mockReturnValue({ updateShortUrl }); - const dispatch = jest.fn(); - const getState = () => Mock.all(); - - afterEach(jest.clearAllMocks); - - it.each([[ undefined ], [ null ], [ 'example.com' ]])('dispatches metadata on success', async (domain) => { - await editShortUrlMeta(buildShlinkApiClient)(shortCode, domain, meta)(dispatch, getState); - - expect(buildShlinkApiClient).toHaveBeenCalledTimes(1); - expect(updateShortUrl).toHaveBeenCalledTimes(1); - expect(updateShortUrl).toHaveBeenCalledWith(shortCode, domain, meta); - expect(dispatch).toHaveBeenCalledTimes(2); - expect(dispatch).toHaveBeenNthCalledWith(1, { type: EDIT_SHORT_URL_META_START }); - expect(dispatch).toHaveBeenNthCalledWith(2, { type: SHORT_URL_META_EDITED, meta, shortCode, domain }); - }); - - it('dispatches error on failure', async () => { - const error = new Error(); - - updateShortUrl.mockRejectedValue(error); - - try { - await editShortUrlMeta(buildShlinkApiClient)(shortCode, undefined, meta)(dispatch, getState); - } catch (e) { - expect(e).toBe(error); - } - - expect(buildShlinkApiClient).toHaveBeenCalledTimes(1); - expect(updateShortUrl).toHaveBeenCalledTimes(1); - expect(updateShortUrl).toHaveBeenCalledWith(shortCode, undefined, meta); - expect(dispatch).toHaveBeenCalledTimes(2); - expect(dispatch).toHaveBeenNthCalledWith(1, { type: EDIT_SHORT_URL_META_START }); - expect(dispatch).toHaveBeenNthCalledWith(2, { type: EDIT_SHORT_URL_META_ERROR }); - }); - }); - - describe('resetShortUrlMeta', () => { - it('creates expected action', () => expect(resetShortUrlMeta()).toEqual({ type: RESET_EDIT_SHORT_URL_META })); - }); -}); diff --git a/test/short-urls/reducers/shortUrlTags.test.ts b/test/short-urls/reducers/shortUrlTags.test.ts deleted file mode 100644 index 82381da0..00000000 --- a/test/short-urls/reducers/shortUrlTags.test.ts +++ /dev/null @@ -1,131 +0,0 @@ -import { Mock } from 'ts-mockery'; -import reducer, { - EDIT_SHORT_URL_TAGS_ERROR, - EDIT_SHORT_URL_TAGS_START, - RESET_EDIT_SHORT_URL_TAGS, - resetShortUrlsTags, - SHORT_URL_TAGS_EDITED, - editShortUrlTags, - EditShortUrlTagsAction, -} from '../../../src/short-urls/reducers/shortUrlTags'; -import { ShlinkState } from '../../../src/container/types'; -import { ReachableServer, SelectedServer } from '../../../src/servers/data'; - -describe('shortUrlTagsReducer', () => { - const tags = [ 'foo', 'bar', 'baz' ]; - const shortCode = 'abc123'; - - describe('reducer', () => { - const action = (type: string) => Mock.of({ type }); - - it('returns loading on EDIT_SHORT_URL_TAGS_START', () => { - expect(reducer(undefined, action(EDIT_SHORT_URL_TAGS_START))).toEqual({ - tags: [], - shortCode: null, - saving: true, - error: false, - }); - }); - - it('returns error on EDIT_SHORT_URL_TAGS_ERROR', () => { - expect(reducer(undefined, action(EDIT_SHORT_URL_TAGS_ERROR))).toEqual({ - tags: [], - shortCode: null, - saving: false, - error: true, - }); - }); - - it('returns provided tags and shortCode on SHORT_URL_TAGS_EDITED', () => { - expect(reducer(undefined, { type: SHORT_URL_TAGS_EDITED, tags, shortCode, domain: null })).toEqual({ - tags, - shortCode, - saving: false, - error: false, - }); - }); - - it('goes back to initial state on RESET_EDIT_SHORT_URL_TAGS', () => { - expect(reducer(undefined, action(RESET_EDIT_SHORT_URL_TAGS))).toEqual({ - tags: [], - shortCode: null, - saving: false, - error: false, - }); - }); - }); - - describe('resetShortUrlsTags', () => { - it('creates expected action', () => expect(resetShortUrlsTags()).toEqual({ type: RESET_EDIT_SHORT_URL_TAGS })); - }); - - describe('editShortUrlTags', () => { - const updateShortUrlTags = jest.fn(); - const updateShortUrl = jest.fn(); - const buildShlinkApiClient = jest.fn().mockReturnValue({ updateShortUrlTags, updateShortUrl }); - const dispatch = jest.fn(); - const buildGetState = (selectedServer?: SelectedServer) => () => Mock.of({ selectedServer }); - - afterEach(jest.clearAllMocks); - - it.each([[ undefined ], [ null ], [ 'example.com' ]])('dispatches normalized tags on success', async (domain) => { - const normalizedTags = [ 'bar', 'foo' ]; - - updateShortUrlTags.mockResolvedValue(normalizedTags); - - await editShortUrlTags(buildShlinkApiClient)(shortCode, domain, tags)(dispatch, buildGetState()); - - expect(buildShlinkApiClient).toHaveBeenCalledTimes(1); - expect(updateShortUrlTags).toHaveBeenCalledTimes(1); - expect(updateShortUrlTags).toHaveBeenCalledWith(shortCode, domain, tags); - expect(updateShortUrl).not.toHaveBeenCalled(); - expect(dispatch).toHaveBeenCalledTimes(2); - expect(dispatch).toHaveBeenNthCalledWith(1, { type: EDIT_SHORT_URL_TAGS_START }); - expect(dispatch).toHaveBeenNthCalledWith( - 2, - { type: SHORT_URL_TAGS_EDITED, tags: normalizedTags, shortCode, domain }, - ); - }); - - it('calls updateShortUrl when server is version 2.6.0 or above', async () => { - const normalizedTags = [ 'bar', 'foo' ]; - - updateShortUrl.mockResolvedValue({ tags: normalizedTags }); - - await editShortUrlTags(buildShlinkApiClient)(shortCode, undefined, tags)( - dispatch, - buildGetState(Mock.of({ printableVersion: '', version: '2.6.0' })), - ); - - expect(buildShlinkApiClient).toHaveBeenCalledTimes(1); - expect(updateShortUrl).toHaveBeenCalledTimes(1); - expect(updateShortUrl).toHaveBeenCalledWith(shortCode, undefined, { tags }); - expect(updateShortUrlTags).not.toHaveBeenCalled(); - expect(dispatch).toHaveBeenCalledTimes(2); - expect(dispatch).toHaveBeenNthCalledWith(1, { type: EDIT_SHORT_URL_TAGS_START }); - expect(dispatch).toHaveBeenNthCalledWith( - 2, - { type: SHORT_URL_TAGS_EDITED, tags: normalizedTags, shortCode }, - ); - }); - - it('dispatches error on failure', async () => { - const error = new Error(); - - updateShortUrlTags.mockRejectedValue(error); - - try { - await editShortUrlTags(buildShlinkApiClient)(shortCode, undefined, tags)(dispatch, buildGetState()); - } catch (e) { - expect(e).toBe(error); - } - - expect(buildShlinkApiClient).toHaveBeenCalledTimes(1); - expect(updateShortUrlTags).toHaveBeenCalledTimes(1); - expect(updateShortUrlTags).toHaveBeenCalledWith(shortCode, undefined, tags); - expect(dispatch).toHaveBeenCalledTimes(2); - expect(dispatch).toHaveBeenNthCalledWith(1, { type: EDIT_SHORT_URL_TAGS_START }); - expect(dispatch).toHaveBeenNthCalledWith(2, { type: EDIT_SHORT_URL_TAGS_ERROR }); - }); - }); -}); diff --git a/test/short-urls/reducers/shortUrlsList.test.ts b/test/short-urls/reducers/shortUrlsList.test.ts index 2266129c..761cf0b1 100644 --- a/test/short-urls/reducers/shortUrlsList.test.ts +++ b/test/short-urls/reducers/shortUrlsList.test.ts @@ -5,15 +5,12 @@ import reducer, { LIST_SHORT_URLS_START, listShortUrls, } from '../../../src/short-urls/reducers/shortUrlsList'; -import { SHORT_URL_TAGS_EDITED } from '../../../src/short-urls/reducers/shortUrlTags'; import { SHORT_URL_DELETED } from '../../../src/short-urls/reducers/shortUrlDeletion'; -import { SHORT_URL_META_EDITED } from '../../../src/short-urls/reducers/shortUrlMeta'; import { CREATE_VISITS } from '../../../src/visits/reducers/visitCreation'; import { ShortUrl } from '../../../src/short-urls/data'; import ShlinkApiClient from '../../../src/api/services/ShlinkApiClient'; import { ShlinkPaginator, ShlinkShortUrlsResponse } from '../../../src/api/types'; import { CREATE_SHORT_URL } from '../../../src/short-urls/reducers/shortUrlCreation'; -import { SHORT_URL_EDITED } from '../../../src/short-urls/reducers/shortUrlEdition'; describe('shortUrlsListReducer', () => { describe('reducer', () => { @@ -36,66 +33,6 @@ describe('shortUrlsListReducer', () => { error: true, })); - it('updates tags on matching URL on SHORT_URL_TAGS_EDITED', () => { - const shortCode = 'abc123'; - const tags = [ 'foo', 'bar', 'baz' ]; - const state = { - shortUrls: Mock.of({ - data: [ - Mock.of({ shortCode, tags: [] }), - Mock.of({ shortCode, tags: [], domain: 'example.com' }), - Mock.of({ shortCode: 'foo', tags: [] }), - ], - }), - loading: false, - error: false, - }; - - expect(reducer(state, { type: SHORT_URL_TAGS_EDITED, shortCode, tags, domain: null } as any)).toEqual({ - shortUrls: { - data: [ - { shortCode, tags }, - { shortCode, tags: [], domain: 'example.com' }, - { shortCode: 'foo', tags: [] }, - ], - }, - loading: false, - error: false, - }); - }); - - it('updates meta on matching URL on SHORT_URL_META_EDITED', () => { - const shortCode = 'abc123'; - const domain = 'example.com'; - const meta = { - maxVisits: 5, - validSince: '2020-05-05', - }; - const state = { - shortUrls: Mock.of({ - data: [ - Mock.of({ shortCode, meta: { maxVisits: 10 }, domain }), - Mock.of({ shortCode, meta: { maxVisits: 50 } }), - Mock.of({ shortCode: 'foo', meta: {} }), - ], - }), - loading: false, - error: false, - }; - - expect(reducer(state, { type: SHORT_URL_META_EDITED, shortCode, meta, domain } as any)).toEqual({ - shortUrls: { - data: [ - { shortCode, meta, domain: 'example.com' }, - { shortCode, meta: { maxVisits: 50 } }, - { shortCode: 'foo', meta: {} }, - ], - }, - loading: false, - error: false, - }); - }); - it('removes matching URL and reduces total on SHORT_URL_DELETED', () => { const shortCode = 'abc123'; const state = { @@ -123,33 +60,6 @@ describe('shortUrlsListReducer', () => { }); }); - it('updates edited short URL on SHORT_URL_EDITED', () => { - const shortCode = 'abc123'; - const state = { - shortUrls: Mock.of({ - data: [ - Mock.of({ shortCode, longUrl: 'old' }), - Mock.of({ shortCode, domain: 'example.com', longUrl: 'foo' }), - Mock.of({ shortCode: 'foo', longUrl: 'bar' }), - ], - }), - loading: false, - error: false, - }; - - expect(reducer(state, { type: SHORT_URL_EDITED, shortCode, longUrl: 'newValue' } as any)).toEqual({ - shortUrls: { - data: [ - { shortCode, longUrl: 'newValue' }, - { shortCode, longUrl: 'foo', domain: 'example.com' }, - { shortCode: 'foo', longUrl: 'bar' }, - ], - }, - loading: false, - error: false, - }); - }); - const createNewShortUrlVisit = (visitsCount: number) => ({ shortUrl: { shortCode: 'abc123', visitsCount }, });