Created section to display orphan visits stats

This commit is contained in:
Alejandro Celaya
2021-02-27 20:03:51 +01:00
parent 46d012b6ff
commit 5479210366
23 changed files with 342 additions and 36 deletions

View File

@@ -4,6 +4,7 @@ import { OptionalString } from '../../../src/utils/utils';
import { Mock } from 'ts-mockery';
import { ShlinkDomain, ShlinkVisitsOverview } from '../../../src/api/types';
import { ShortUrl } from '../../../src/short-urls/data';
import { Visit } from '../../../src/visits/types';
describe('ShlinkApiClient', () => {
const createAxios = (data: AxiosRequestConfig) => (async () => Promise.resolve(data)) as unknown as AxiosInstance;
@@ -285,4 +286,18 @@ describe('ShlinkApiClient', () => {
expect(result).toEqual(expectedData);
});
});
describe('getOrphanVisits', () => {
it('returns orphan visits', async () => {
const expectedData: Visit[] = [];
const resp = { visits: expectedData };
const axiosSpy = createAxiosMock({ data: resp });
const { getOrphanVisits } = new ShlinkApiClient(axiosSpy, '', '');
const result = await getOrphanVisits();
expect(axiosSpy).toHaveBeenCalled();
expect(result).toEqual(expectedData);
});
});
});

View File

@@ -150,12 +150,18 @@ describe('shortUrlsListReducer', () => {
});
});
it('updates visits count on CREATE_VISIT', () => {
const createNewShortUrlVisit = (visitsCount: number) => ({
shortUrl: { shortCode: 'abc123', visitsCount },
});
it.each([
[[ createNewShortUrlVisit(11) ], 11 ],
[[ createNewShortUrlVisit(30) ], 30 ],
[[ createNewShortUrlVisit(20), createNewShortUrlVisit(40) ], 40 ],
[[{}], 10 ],
[[], 10 ],
])('updates visits count on CREATE_VISITS', (createdVisits, expectedCount) => {
const shortCode = 'abc123';
const shortUrl = {
shortCode,
visitsCount: 11,
};
const state = {
shortUrls: Mock.of<ShlinkShortUrlsResponse>({
data: [
@@ -168,11 +174,11 @@ describe('shortUrlsListReducer', () => {
error: false,
};
expect(reducer(state, { type: CREATE_VISITS, createdVisits: [{ shortUrl }] } as any)).toEqual({
expect(reducer(state, { type: CREATE_VISITS, createdVisits } as any)).toEqual({
shortUrls: {
data: [
{ shortCode, domain: 'example.com', visitsCount: 5 },
{ shortCode, visitsCount: 11 },
{ shortCode, visitsCount: expectedCount },
{ shortCode: 'foo', visitsCount: 8 },
],
},

View File

@@ -7,12 +7,13 @@ import reducer, {
VisitsOverview,
loadVisitsOverview,
} from '../../../src/visits/reducers/visitsOverview';
import { CreateVisitsAction } from '../../../src/visits/reducers/visitCreation';
import { CREATE_VISITS, CreateVisitsAction } from '../../../src/visits/reducers/visitCreation';
import ShlinkApiClient from '../../../src/api/services/ShlinkApiClient';
import { ShlinkVisitsOverview } from '../../../src/api/types';
import { ShlinkState } from '../../../src/container/types';
import { CreateVisit, OrphanVisit, Visit } from '../../../src/visits/types';
describe('visitsOverview', () => {
describe('visitsOverviewReducer', () => {
describe('reducer', () => {
const action = (type: string) =>
Mock.of<GetVisitsOverviewAction>({ type }) as GetVisitsOverviewAction & CreateVisitsAction;
@@ -41,6 +42,31 @@ describe('visitsOverview', () => {
expect(error).toEqual(false);
expect(visitsCount).toEqual(100);
});
it('returns updated amounts on CREATE_VISITS', () => {
const { visitsCount, orphanVisitsCount } = reducer(
state({ visitsCount: 100, orphanVisitsCount: 50 }),
{
type: CREATE_VISITS,
createdVisits: [
Mock.of<CreateVisit>({ visit: Mock.all<Visit>() }),
Mock.of<CreateVisit>({ visit: Mock.all<Visit>() }),
Mock.of<CreateVisit>({
visit: Mock.of<OrphanVisit>({ visitedUrl: '' }),
}),
Mock.of<CreateVisit>({
visit: Mock.of<OrphanVisit>({ visitedUrl: '' }),
}),
Mock.of<CreateVisit>({
visit: Mock.of<OrphanVisit>({ visitedUrl: '' }),
}),
],
} as unknown as GetVisitsOverviewAction & CreateVisitsAction,
);
expect(visitsCount).toEqual(102);
expect(orphanVisitsCount).toEqual(53);
});
});
describe('loadVisitsOverview', () => {

View File

@@ -1,6 +1,6 @@
import { Mock } from 'ts-mockery';
import { processStatsFromVisits, normalizeVisits } from '../../../src/visits/services/VisitsParser';
import { Visit, VisitsStats } from '../../../src/visits/types';
import { OrphanVisit, Visit, VisitsStats } from '../../../src/visits/types';
describe('VisitsParser', () => {
const visits: Visit[] = [
@@ -45,6 +45,36 @@ describe('VisitsParser', () => {
userAgent: 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.106 Safari/537.36 OPR/38.0.2220.41',
}),
];
const orphanVisits: OrphanVisit[] = [
Mock.of<OrphanVisit>({
type: 'base_url',
visitedUrl: 'foo',
userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X x.y; rv:42.0) Gecko/20100101 Firefox/42.0',
referer: 'https://google.com',
visitLocation: {
countryName: 'United States',
cityName: 'New York',
latitude: 1029,
longitude: 6758,
},
}),
Mock.of<OrphanVisit>({
type: 'regular_404',
visitedUrl: 'bar',
}),
Mock.of<OrphanVisit>({
type: 'invalid_short_url',
visitedUrl: 'baz',
userAgent: 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36',
referer: 'https://m.facebook.com',
visitLocation: {
countryName: 'Spain',
cityName: 'Zaragoza',
latitude: 123.45,
longitude: -543.21,
},
}),
];
describe('processStatsFromVisits', () => {
let stats: VisitsStats;
@@ -180,5 +210,46 @@ describe('VisitsParser', () => {
},
]);
});
it('properly parses the list of orphan visits', () => {
expect(normalizeVisits(orphanVisits)).toEqual([
{
browser: 'Firefox',
os: 'macOS',
referer: 'google.com',
country: 'United States',
city: 'New York',
date: undefined,
latitude: 1029,
longitude: 6758,
type: 'base_url',
visitedUrl: 'foo',
},
{
type: 'regular_404',
visitedUrl: 'bar',
browser: 'Others',
city: 'Unknown',
country: 'Unknown',
date: undefined,
latitude: undefined,
longitude: undefined,
os: 'Others',
referer: 'Direct',
},
{
browser: 'Chrome',
os: 'Linux',
referer: 'm.facebook.com',
country: 'Spain',
city: 'Zaragoza',
date: undefined,
latitude: 123.45,
longitude: -543.21,
type: 'invalid_short_url',
visitedUrl: 'baz',
},
]);
});
});
});