Replaced redux action to create one visit by action that allows multiple visits at once

This commit is contained in:
Alejandro Celaya 2020-09-12 11:31:44 +02:00
parent ad437f655e
commit 6fc4963663
14 changed files with 73 additions and 55 deletions

View file

@ -4,7 +4,7 @@ import { MercureInfo } from '../reducers/mercureInfo';
import { bindToMercureTopic } from './index'; import { bindToMercureTopic } from './index';
export interface MercureBoundProps { export interface MercureBoundProps {
createNewVisit: (visitData: CreateVisit) => void; createNewVisits: (createdVisits: CreateVisit[]) => void;
loadMercureInfo: Function; loadMercureInfo: Function;
mercureInfo: MercureInfo; mercureInfo: MercureInfo;
} }
@ -16,14 +16,14 @@ export function boundToMercureHub<T = {}>(
const pendingUpdates = new Set<CreateVisit>(); const pendingUpdates = new Set<CreateVisit>();
return (props: MercureBoundProps & T) => { return (props: MercureBoundProps & T) => {
const { createNewVisit, loadMercureInfo, mercureInfo } = props; const { createNewVisits, loadMercureInfo, mercureInfo } = props;
const { interval } = mercureInfo; const { interval } = mercureInfo;
useEffect(() => { useEffect(() => {
const onMessage = (visit: CreateVisit) => interval ? pendingUpdates.add(visit) : createNewVisit(visit); const onMessage = (visit: CreateVisit) => interval ? pendingUpdates.add(visit) : createNewVisits([ visit ]);
interval && setInterval(() => { interval && setInterval(() => {
pendingUpdates.forEach(createNewVisit); createNewVisits([ ...pendingUpdates ]);
pendingUpdates.clear(); pendingUpdates.clear();
}, interval * 1000 * 60); }, interval * 1000 * 60);

View file

@ -1,7 +1,7 @@
import { assoc, assocPath, reject } from 'ramda'; import { assoc, assocPath, last, reject } from 'ramda';
import { Action, Dispatch } from 'redux'; import { Action, Dispatch } from 'redux';
import { shortUrlMatches } from '../helpers'; import { shortUrlMatches } from '../helpers';
import { CREATE_VISIT, CreateVisitAction } from '../../visits/reducers/visitCreation'; import { CREATE_VISITS, CreateVisitsAction } from '../../visits/reducers/visitCreation';
import { ShortUrl, ShortUrlIdentifier } from '../data'; import { ShortUrl, ShortUrlIdentifier } from '../data';
import { buildReducer } from '../../utils/helpers/redux'; import { buildReducer } from '../../utils/helpers/redux';
import { GetState } from '../../container/types'; import { GetState } from '../../container/types';
@ -31,7 +31,7 @@ export interface ListShortUrlsAction extends Action<string> {
} }
export type ListShortUrlsCombinedAction = ( export type ListShortUrlsCombinedAction = (
ListShortUrlsAction & EditShortUrlTagsAction & ShortUrlEditedAction & ShortUrlMetaEditedAction & CreateVisitAction ListShortUrlsAction & EditShortUrlTagsAction & ShortUrlEditedAction & ShortUrlMetaEditedAction & CreateVisitsAction
); );
const initialState: ShortUrlsList = { const initialState: ShortUrlsList = {
@ -63,12 +63,17 @@ export default buildReducer<ShortUrlsList, ListShortUrlsCombinedAction>({
[SHORT_URL_TAGS_EDITED]: setPropFromActionOnMatchingShortUrl<EditShortUrlTagsAction>('tags'), [SHORT_URL_TAGS_EDITED]: setPropFromActionOnMatchingShortUrl<EditShortUrlTagsAction>('tags'),
[SHORT_URL_META_EDITED]: setPropFromActionOnMatchingShortUrl<ShortUrlMetaEditedAction>('meta'), [SHORT_URL_META_EDITED]: setPropFromActionOnMatchingShortUrl<ShortUrlMetaEditedAction>('meta'),
[SHORT_URL_EDITED]: setPropFromActionOnMatchingShortUrl<ShortUrlEditedAction>('longUrl'), [SHORT_URL_EDITED]: setPropFromActionOnMatchingShortUrl<ShortUrlEditedAction>('longUrl'),
[CREATE_VISIT]: (state, { shortUrl: { shortCode, domain, visitsCount } }) => assocPath( [CREATE_VISITS]: (state, { createdVisits }) => assocPath(
[ 'shortUrls', 'data' ], [ 'shortUrls', 'data' ],
state.shortUrls && state.shortUrls.data && state.shortUrls.data.map( state.shortUrls?.data?.map(
(shortUrl) => shortUrlMatches(shortUrl, shortCode, domain) (currentShortUrl) => {
? assoc('visitsCount', visitsCount, shortUrl) // Find the last of the new visit for this short URL, and pick the amount of visits from it
: shortUrl, const lastVisit = last(
createdVisits.filter(({ shortUrl }) => shortUrlMatches(currentShortUrl, shortUrl.shortCode, shortUrl.domain)),
);
return lastVisit ? assoc('visitsCount', lastVisit.shortUrl.visitsCount, currentShortUrl) : currentShortUrl;
},
), ),
state, state,
), ),

View file

@ -35,7 +35,7 @@ const provideServices = (bottle: Bottle, connect: ConnectDecorator) => {
bottle.serviceFactory('ShortUrlsList', ShortUrlsList, 'ShortUrlsRow'); bottle.serviceFactory('ShortUrlsList', ShortUrlsList, 'ShortUrlsRow');
bottle.decorator('ShortUrlsList', connect( bottle.decorator('ShortUrlsList', connect(
[ 'selectedServer', 'shortUrlsListParams', 'mercureInfo' ], [ 'selectedServer', 'shortUrlsListParams', 'mercureInfo' ],
[ 'listShortUrls', 'resetShortUrlParams', 'createNewVisit', 'loadMercureInfo' ], [ 'listShortUrls', 'resetShortUrlParams', 'createNewVisits', 'loadMercureInfo' ],
)); ));
bottle.serviceFactory('ShortUrlsRow', ShortUrlsRow, 'ShortUrlsRowMenu', 'ColorGenerator', 'useStateFlagTimeout'); bottle.serviceFactory('ShortUrlsRow', ShortUrlsRow, 'ShortUrlsRowMenu', 'ColorGenerator', 'useStateFlagTimeout');

View file

@ -56,6 +56,7 @@ const TagsSelector = (colorGenerator: ColorGenerator) => (
</React.Fragment> </React.Fragment>
)} )}
onSuggestionsFetchRequested={() => {}} onSuggestionsFetchRequested={() => {}}
onSuggestionsClearRequested={() => {}}
onSuggestionSelected={(_, { suggestion }: SuggestionSelectedEventData<string>) => { onSuggestionSelected={(_, { suggestion }: SuggestionSelectedEventData<string>) => {
addTag(suggestion); addTag(suggestion);
}} }}

View file

@ -1,11 +1,12 @@
import { isEmpty, reject } from 'ramda'; import { isEmpty, reject } from 'ramda';
import { Action, Dispatch } from 'redux'; import { Action, Dispatch } from 'redux';
import { CREATE_VISIT, CreateVisitAction } from '../../visits/reducers/visitCreation'; import { CREATE_VISITS, CreateVisitsAction } from '../../visits/reducers/visitCreation';
import { buildReducer } from '../../utils/helpers/redux'; import { buildReducer } from '../../utils/helpers/redux';
import { ShlinkTags } from '../../utils/services/types'; import { ShlinkTags } from '../../utils/services/types';
import { GetState } from '../../container/types'; import { GetState } from '../../container/types';
import { ShlinkApiClientBuilder } from '../../utils/services/ShlinkApiClientBuilder'; import { ShlinkApiClientBuilder } from '../../utils/services/ShlinkApiClientBuilder';
import { TagStats } from '../data'; import { TagStats } from '../data';
import { CreateVisit, Stats } from '../../visits/types';
import { DeleteTagAction, TAG_DELETED } from './tagDelete'; import { DeleteTagAction, TAG_DELETED } from './tagDelete';
import { EditTagAction, TAG_EDITED } from './tagEdit'; import { EditTagAction, TAG_EDITED } from './tagEdit';
@ -35,7 +36,7 @@ interface FilterTagsAction extends Action<string> {
searchTerm: string; searchTerm: string;
} }
type ListTagsCombinedAction = ListTagsAction & DeleteTagAction & CreateVisitAction & EditTagAction & FilterTagsAction; type ListTagsCombinedAction = ListTagsAction & DeleteTagAction & CreateVisitsAction & EditTagAction & FilterTagsAction;
const initialState = { const initialState = {
tags: [], tags: [],
@ -45,20 +46,31 @@ const initialState = {
error: false, error: false,
}; };
type TagIncrease = [string, number];
const renameTag = (oldName: string, newName: string) => (tag: string) => tag === oldName ? newName : tag; const renameTag = (oldName: string, newName: string) => (tag: string) => tag === oldName ? newName : tag;
const rejectTag = (tags: string[], tagToReject: string) => reject((tag) => tag === tagToReject, tags); const rejectTag = (tags: string[], tagToReject: string) => reject((tag) => tag === tagToReject, tags);
const increaseVisitsForTags = (tags: string[], stats: TagsStatsMap) => tags.reduce((stats, tag) => { const increaseVisitsForTags = (tags: TagIncrease[], stats: TagsStatsMap) => tags.reduce((stats, [ tag, increase ]) => {
if (!stats[tag]) { if (!stats[tag]) {
return stats; return stats;
} }
const tagStats = stats[tag]; const tagStats = stats[tag];
tagStats.visitsCount = tagStats.visitsCount + 1; tagStats.visitsCount = tagStats.visitsCount + increase;
stats[tag] = tagStats; stats[tag] = tagStats;
return stats; return stats;
}, { ...stats }); }, { ...stats });
const calculateVisitsPerTag = (createdVisits: CreateVisit[]): TagIncrease[] => Object.entries(
createdVisits.reduce((acc, { shortUrl }) => {
shortUrl.tags.forEach((tag) => {
acc[tag] = (acc[tag] || 0) + 1;
});
return acc;
}, {} as Stats),
);
export default buildReducer<TagsList, ListTagsCombinedAction>({ export default buildReducer<TagsList, ListTagsCombinedAction>({
[LIST_TAGS_START]: () => ({ ...initialState, loading: true }), [LIST_TAGS_START]: () => ({ ...initialState, loading: true }),
@ -78,9 +90,9 @@ export default buildReducer<TagsList, ListTagsCombinedAction>({
...state, ...state,
filteredTags: state.tags.filter((tag) => tag.toLowerCase().match(searchTerm)), filteredTags: state.tags.filter((tag) => tag.toLowerCase().match(searchTerm)),
}), }),
[CREATE_VISIT]: (state, { shortUrl }) => ({ [CREATE_VISITS]: (state, { createdVisits }) => ({
...state, ...state,
stats: increaseVisitsForTags(shortUrl.tags, state.stats), stats: increaseVisitsForTags(calculateVisitsPerTag(createdVisits), state.stats),
}), }),
}, initialState); }, initialState);

View file

@ -32,7 +32,7 @@ const provideServices = (bottle: Bottle, connect: ConnectDecorator) => {
bottle.serviceFactory('TagsList', TagsList, 'TagCard'); bottle.serviceFactory('TagsList', TagsList, 'TagCard');
bottle.decorator('TagsList', connect( bottle.decorator('TagsList', connect(
[ 'tagsList', 'selectedServer', 'mercureInfo' ], [ 'tagsList', 'selectedServer', 'mercureInfo' ],
[ 'forceListTags', 'filterTags', 'createNewVisit', 'loadMercureInfo' ], [ 'forceListTags', 'filterTags', 'createNewVisits', 'loadMercureInfo' ],
)); ));
// Actions // Actions

View file

@ -7,7 +7,7 @@ import { ShlinkApiClientBuilder } from '../../utils/services/ShlinkApiClientBuil
import { GetState } from '../../container/types'; import { GetState } from '../../container/types';
import { OptionalString } from '../../utils/utils'; import { OptionalString } from '../../utils/utils';
import { getVisitsWithLoader } from './common'; import { getVisitsWithLoader } from './common';
import { CREATE_VISIT, CreateVisitAction } from './visitCreation'; import { CREATE_VISITS, CreateVisitsAction } from './visitCreation';
/* eslint-disable padding-line-between-statements */ /* eslint-disable padding-line-between-statements */
export const GET_SHORT_URL_VISITS_START = 'shlink/shortUrlVisits/GET_SHORT_URL_VISITS_START'; export const GET_SHORT_URL_VISITS_START = 'shlink/shortUrlVisits/GET_SHORT_URL_VISITS_START';
@ -24,7 +24,7 @@ interface ShortUrlVisitsAction extends Action<string>, ShortUrlIdentifier {
visits: Visit[]; visits: Visit[];
} }
type ShortUrlVisitsCombinedAction = ShortUrlVisitsAction & VisitsLoadProgressChangedAction & CreateVisitAction; type ShortUrlVisitsCombinedAction = ShortUrlVisitsAction & VisitsLoadProgressChangedAction & CreateVisitsAction;
const initialState: ShortUrlVisits = { const initialState: ShortUrlVisits = {
visits: [], visits: [],
@ -49,14 +49,14 @@ export default buildReducer<ShortUrlVisits, ShortUrlVisitsCombinedAction>({
[GET_SHORT_URL_VISITS_LARGE]: (state) => ({ ...state, loadingLarge: true }), [GET_SHORT_URL_VISITS_LARGE]: (state) => ({ ...state, loadingLarge: true }),
[GET_SHORT_URL_VISITS_CANCEL]: (state) => ({ ...state, cancelLoad: true }), [GET_SHORT_URL_VISITS_CANCEL]: (state) => ({ ...state, cancelLoad: true }),
[GET_SHORT_URL_VISITS_PROGRESS_CHANGED]: (state, { progress }) => ({ ...state, progress }), [GET_SHORT_URL_VISITS_PROGRESS_CHANGED]: (state, { progress }) => ({ ...state, progress }),
[CREATE_VISIT]: (state, { shortUrl, visit }) => { // eslint-disable-line object-shorthand [CREATE_VISITS]: (state, { createdVisits }) => { // eslint-disable-line object-shorthand
const { shortCode, domain, visits } = state; const { shortCode, domain, visits } = state;
if (!shortUrlMatches(shortUrl, shortCode, domain)) { const newVisits = createdVisits
return state; .filter(({ shortUrl }) => shortUrlMatches(shortUrl, shortCode, domain))
} .map(({ visit }) => visit);
return { ...state, visits: [ ...visits, visit ] }; return { ...state, visits: [ ...visits, ...newVisits ] };
}, },
}, initialState); }, initialState);

View file

@ -4,7 +4,7 @@ import { buildActionCreator, buildReducer } from '../../utils/helpers/redux';
import { ShlinkApiClientBuilder } from '../../utils/services/ShlinkApiClientBuilder'; import { ShlinkApiClientBuilder } from '../../utils/services/ShlinkApiClientBuilder';
import { GetState } from '../../container/types'; import { GetState } from '../../container/types';
import { getVisitsWithLoader } from './common'; import { getVisitsWithLoader } from './common';
import { CREATE_VISIT, CreateVisitAction } from './visitCreation'; import { CREATE_VISITS, CreateVisitsAction } from './visitCreation';
/* eslint-disable padding-line-between-statements */ /* eslint-disable padding-line-between-statements */
export const GET_TAG_VISITS_START = 'shlink/tagVisits/GET_TAG_VISITS_START'; export const GET_TAG_VISITS_START = 'shlink/tagVisits/GET_TAG_VISITS_START';
@ -34,21 +34,20 @@ const initialState: TagVisits = {
progress: 0, progress: 0,
}; };
export default buildReducer<TagVisits, TagVisitsAction & VisitsLoadProgressChangedAction & CreateVisitAction>({ export default buildReducer<TagVisits, TagVisitsAction & VisitsLoadProgressChangedAction & CreateVisitsAction>({
[GET_TAG_VISITS_START]: () => ({ ...initialState, loading: true }), [GET_TAG_VISITS_START]: () => ({ ...initialState, loading: true }),
[GET_TAG_VISITS_ERROR]: () => ({ ...initialState, error: true }), [GET_TAG_VISITS_ERROR]: () => ({ ...initialState, error: true }),
[GET_TAG_VISITS]: (_, { visits, tag }) => ({ ...initialState, visits, tag }), [GET_TAG_VISITS]: (_, { visits, tag }) => ({ ...initialState, visits, tag }),
[GET_TAG_VISITS_LARGE]: (state) => ({ ...state, loadingLarge: true }), [GET_TAG_VISITS_LARGE]: (state) => ({ ...state, loadingLarge: true }),
[GET_TAG_VISITS_CANCEL]: (state) => ({ ...state, cancelLoad: true }), [GET_TAG_VISITS_CANCEL]: (state) => ({ ...state, cancelLoad: true }),
[GET_TAG_VISITS_PROGRESS_CHANGED]: (state, { progress }) => ({ ...state, progress }), [GET_TAG_VISITS_PROGRESS_CHANGED]: (state, { progress }) => ({ ...state, progress }),
[CREATE_VISIT]: (state, { shortUrl, visit }) => { // eslint-disable-line object-shorthand [CREATE_VISITS]: (state, { createdVisits }) => { // eslint-disable-line object-shorthand
const { tag, visits } = state; const { tag, visits } = state;
const newVisits = createdVisits
.filter(({ shortUrl }) => shortUrl.tags.includes(tag))
.map(({ visit }) => visit);
if (!shortUrl.tags.includes(tag)) { return { ...state, visits: [ ...visits, ...newVisits ] };
return state;
}
return { ...state, visits: [ ...visits, visit ] };
}, },
}, initialState); }, initialState);

View file

@ -1,12 +1,13 @@
import { Action } from 'redux'; import { Action } from 'redux';
import { CreateVisit } from '../types'; import { CreateVisit } from '../types';
export const CREATE_VISIT = 'shlink/visitCreation/CREATE_VISIT'; export const CREATE_VISITS = 'shlink/visitCreation/CREATE_VISITS';
export type CreateVisitAction = Action<typeof CREATE_VISIT> & CreateVisit; export interface CreateVisitsAction extends Action<typeof CREATE_VISITS> {
createdVisits: CreateVisit[];
}
export const createNewVisit = ({ shortUrl, visit }: CreateVisit): CreateVisitAction => ({ export const createNewVisits = (createdVisits: CreateVisit[]): CreateVisitsAction => ({
type: CREATE_VISIT, type: CREATE_VISITS,
shortUrl, createdVisits,
visit,
}); });

View file

@ -3,7 +3,7 @@ import ShortUrlVisits from '../ShortUrlVisits';
import { cancelGetShortUrlVisits, getShortUrlVisits } from '../reducers/shortUrlVisits'; import { cancelGetShortUrlVisits, getShortUrlVisits } from '../reducers/shortUrlVisits';
import { getShortUrlDetail } from '../reducers/shortUrlDetail'; import { getShortUrlDetail } from '../reducers/shortUrlDetail';
import MapModal from '../helpers/MapModal'; import MapModal from '../helpers/MapModal';
import { createNewVisit } from '../reducers/visitCreation'; import { createNewVisits } from '../reducers/visitCreation';
import { cancelGetTagVisits, getTagVisits } from '../reducers/tagVisits'; import { cancelGetTagVisits, getTagVisits } from '../reducers/tagVisits';
import TagVisits from '../TagVisits'; import TagVisits from '../TagVisits';
import { ConnectDecorator } from '../../container/types'; import { ConnectDecorator } from '../../container/types';
@ -15,12 +15,12 @@ const provideServices = (bottle: Bottle, connect: ConnectDecorator) => {
bottle.serviceFactory('ShortUrlVisits', () => ShortUrlVisits); bottle.serviceFactory('ShortUrlVisits', () => ShortUrlVisits);
bottle.decorator('ShortUrlVisits', connect( bottle.decorator('ShortUrlVisits', connect(
[ 'shortUrlVisits', 'shortUrlDetail', 'mercureInfo' ], [ 'shortUrlVisits', 'shortUrlDetail', 'mercureInfo' ],
[ 'getShortUrlVisits', 'getShortUrlDetail', 'cancelGetShortUrlVisits', 'createNewVisit', 'loadMercureInfo' ], [ 'getShortUrlVisits', 'getShortUrlDetail', 'cancelGetShortUrlVisits', 'createNewVisits', 'loadMercureInfo' ],
)); ));
bottle.serviceFactory('TagVisits', TagVisits, 'ColorGenerator'); bottle.serviceFactory('TagVisits', TagVisits, 'ColorGenerator');
bottle.decorator('TagVisits', connect( bottle.decorator('TagVisits', connect(
[ 'tagVisits', 'mercureInfo' ], [ 'tagVisits', 'mercureInfo' ],
[ 'getTagVisits', 'cancelGetTagVisits', 'createNewVisit', 'loadMercureInfo' ], [ 'getTagVisits', 'cancelGetTagVisits', 'createNewVisits', 'loadMercureInfo' ],
)); ));
// Services // Services
@ -34,7 +34,7 @@ const provideServices = (bottle: Bottle, connect: ConnectDecorator) => {
bottle.serviceFactory('getTagVisits', getTagVisits, 'buildShlinkApiClient'); bottle.serviceFactory('getTagVisits', getTagVisits, 'buildShlinkApiClient');
bottle.serviceFactory('cancelGetTagVisits', () => cancelGetTagVisits); bottle.serviceFactory('cancelGetTagVisits', () => cancelGetTagVisits);
bottle.serviceFactory('createNewVisit', () => createNewVisit); bottle.serviceFactory('createNewVisits', () => createNewVisits);
}; };
export default provideServices; export default provideServices;

View file

@ -8,7 +8,7 @@ import reducer, {
import { SHORT_URL_TAGS_EDITED } from '../../../src/short-urls/reducers/shortUrlTags'; import { SHORT_URL_TAGS_EDITED } from '../../../src/short-urls/reducers/shortUrlTags';
import { SHORT_URL_DELETED } from '../../../src/short-urls/reducers/shortUrlDeletion'; import { SHORT_URL_DELETED } from '../../../src/short-urls/reducers/shortUrlDeletion';
import { SHORT_URL_META_EDITED } from '../../../src/short-urls/reducers/shortUrlMeta'; import { SHORT_URL_META_EDITED } from '../../../src/short-urls/reducers/shortUrlMeta';
import { CREATE_VISIT } from '../../../src/visits/reducers/visitCreation'; import { CREATE_VISITS } from '../../../src/visits/reducers/visitCreation';
import { ShortUrl } from '../../../src/short-urls/data'; import { ShortUrl } from '../../../src/short-urls/data';
import ShlinkApiClient from '../../../src/utils/services/ShlinkApiClient'; import ShlinkApiClient from '../../../src/utils/services/ShlinkApiClient';
import { ShlinkShortUrlsResponse } from '../../../src/utils/services/types'; import { ShlinkShortUrlsResponse } from '../../../src/utils/services/types';
@ -135,7 +135,7 @@ describe('shortUrlsListReducer', () => {
error: false, error: false,
}; };
expect(reducer(state, { type: CREATE_VISIT, shortUrl } as any)).toEqual({ expect(reducer(state, { type: CREATE_VISITS, createdVisits: [{ shortUrl }] } as any)).toEqual({
shortUrls: { shortUrls: {
data: [ data: [
{ shortCode, domain: 'example.com', visitsCount: 5 }, { shortCode, domain: 'example.com', visitsCount: 5 },

View file

@ -10,7 +10,7 @@ import reducer, {
GET_SHORT_URL_VISITS_PROGRESS_CHANGED, GET_SHORT_URL_VISITS_PROGRESS_CHANGED,
ShortUrlVisits, ShortUrlVisits,
} from '../../../src/visits/reducers/shortUrlVisits'; } from '../../../src/visits/reducers/shortUrlVisits';
import { CREATE_VISIT } from '../../../src/visits/reducers/visitCreation'; import { CREATE_VISITS } from '../../../src/visits/reducers/visitCreation';
import { rangeOf } from '../../../src/utils/utils'; import { rangeOf } from '../../../src/utils/utils';
import { Visit } from '../../../src/visits/types'; import { Visit } from '../../../src/visits/types';
import { ShlinkVisits } from '../../../src/utils/services/types'; import { ShlinkVisits } from '../../../src/utils/services/types';
@ -77,7 +77,7 @@ describe('shortUrlVisitsReducer', () => {
visits: visitsMocks, visits: visitsMocks,
}); });
const { visits } = reducer(prevState, { type: CREATE_VISIT, shortUrl, visit: {} } as any); const { visits } = reducer(prevState, { type: CREATE_VISITS, createdVisits: [{ shortUrl, visit: {} }] } as any);
expect(visits).toEqual(expectedVisits); expect(visits).toEqual(expectedVisits);
}); });

View file

@ -10,7 +10,7 @@ import reducer, {
GET_TAG_VISITS_PROGRESS_CHANGED, GET_TAG_VISITS_PROGRESS_CHANGED,
TagVisits, TagVisits,
} from '../../../src/visits/reducers/tagVisits'; } from '../../../src/visits/reducers/tagVisits';
import { CREATE_VISIT } from '../../../src/visits/reducers/visitCreation'; import { CREATE_VISITS } from '../../../src/visits/reducers/visitCreation';
import { rangeOf } from '../../../src/utils/utils'; import { rangeOf } from '../../../src/utils/utils';
import { Visit } from '../../../src/visits/types'; import { Visit } from '../../../src/visits/types';
import { ShlinkVisits } from '../../../src/utils/services/types'; import { ShlinkVisits } from '../../../src/utils/services/types';
@ -77,7 +77,7 @@ describe('tagVisitsReducer', () => {
visits: visitsMocks, visits: visitsMocks,
}); });
const { visits } = reducer(prevState, { type: CREATE_VISIT, shortUrl, visit: {} } as any); const { visits } = reducer(prevState, { type: CREATE_VISITS, createdVisits: [{ shortUrl, visit: {} }] } as any);
expect(visits).toEqual(expectedVisits); expect(visits).toEqual(expectedVisits);
}); });

View file

@ -1,16 +1,16 @@
import { Mock } from 'ts-mockery'; import { Mock } from 'ts-mockery';
import { CREATE_VISIT, createNewVisit } from '../../../src/visits/reducers/visitCreation'; import { CREATE_VISITS, createNewVisits } from '../../../src/visits/reducers/visitCreation';
import { ShortUrl } from '../../../src/short-urls/data'; import { ShortUrl } from '../../../src/short-urls/data';
import { Visit } from '../../../src/visits/types'; import { Visit } from '../../../src/visits/types';
describe('visitCreationReducer', () => { describe('visitCreationReducer', () => {
describe('createNewVisit', () => { describe('createNewVisits', () => {
const shortUrl = Mock.all<ShortUrl>(); const shortUrl = Mock.all<ShortUrl>();
const visit = Mock.all<Visit>(); const visit = Mock.all<Visit>();
it('just returns the action with proper type', () => it('just returns the action with proper type', () =>
expect(createNewVisit({ shortUrl, visit })).toEqual( expect(createNewVisits([{ shortUrl, visit }])).toEqual(
{ type: CREATE_VISIT, shortUrl, visit }, { type: CREATE_VISITS, createdVisits: [{ shortUrl, visit }] },
)); ));
}); });
}); });