mirror of
https://github.com/shlinkio/shlink-web-client.git
synced 2024-12-23 17:40:23 +03:00
Added changes to load orphan visits and fixed tests
This commit is contained in:
parent
60929342fb
commit
8fbe6bb17d
10 changed files with 63 additions and 34 deletions
|
@ -60,6 +60,10 @@ export default class ShlinkApiClient {
|
||||||
this.performRequest<{ visits: ShlinkVisits }>('/visits/orphan', 'GET', query)
|
this.performRequest<{ visits: ShlinkVisits }>('/visits/orphan', 'GET', query)
|
||||||
.then(({ data }) => data.visits);
|
.then(({ data }) => data.visits);
|
||||||
|
|
||||||
|
public readonly getNonOrphanVisits = async (query?: Omit<ShlinkVisitsParams, 'domain'>): Promise<ShlinkVisits> =>
|
||||||
|
this.performRequest<{ visits: ShlinkVisits }>('/visits/non-orphan', 'GET', query)
|
||||||
|
.then(({ data }) => data.visits);
|
||||||
|
|
||||||
public readonly getVisitsOverview = async (): Promise<ShlinkVisitsOverview> =>
|
public readonly getVisitsOverview = async (): Promise<ShlinkVisitsOverview> =>
|
||||||
this.performRequest<{ visits: ShlinkVisitsOverview }>('/visits', 'GET')
|
this.performRequest<{ visits: ShlinkVisitsOverview }>('/visits', 'GET')
|
||||||
.then(({ data }) => data.visits);
|
.then(({ data }) => data.visits);
|
||||||
|
|
|
@ -19,6 +19,7 @@ const MenuLayout = (
|
||||||
ShortUrlVisits: FC,
|
ShortUrlVisits: FC,
|
||||||
TagVisits: FC,
|
TagVisits: FC,
|
||||||
OrphanVisits: FC,
|
OrphanVisits: FC,
|
||||||
|
NonOrphanVisits: FC,
|
||||||
ServerError: FC,
|
ServerError: FC,
|
||||||
Overview: FC,
|
Overview: FC,
|
||||||
EditShortUrl: FC,
|
EditShortUrl: FC,
|
||||||
|
@ -56,7 +57,7 @@ const MenuLayout = (
|
||||||
<Route path="/server/:serverId/short-code/:shortCode/edit" component={EditShortUrl} />
|
<Route path="/server/:serverId/short-code/:shortCode/edit" component={EditShortUrl} />
|
||||||
<Route path="/server/:serverId/tag/:tag/visits" component={TagVisits} />
|
<Route path="/server/:serverId/tag/:tag/visits" component={TagVisits} />
|
||||||
{addOrphanVisitsRoute && <Route path="/server/:serverId/orphan-visits" component={OrphanVisits} />}
|
{addOrphanVisitsRoute && <Route path="/server/:serverId/orphan-visits" component={OrphanVisits} />}
|
||||||
{addNonOrphanVisitsRoute && <Route path="/server/:serverId/visits" render={() => 'Non orphan'} />}
|
{addNonOrphanVisitsRoute && <Route path="/server/:serverId/non-orphan-visits" component={NonOrphanVisits} />}
|
||||||
<Route exact path="/server/:serverId/manage-tags" component={TagsList} />
|
<Route exact path="/server/:serverId/manage-tags" component={TagsList} />
|
||||||
{addManageDomainsRoute && <Route exact path="/server/:serverId/manage-domains" component={ManageDomains} />}
|
{addManageDomainsRoute && <Route exact path="/server/:serverId/manage-domains" component={ManageDomains} />}
|
||||||
<Route
|
<Route
|
||||||
|
|
|
@ -25,6 +25,7 @@ export interface ShlinkState {
|
||||||
shortUrlVisits: ShortUrlVisits;
|
shortUrlVisits: ShortUrlVisits;
|
||||||
tagVisits: TagVisits;
|
tagVisits: TagVisits;
|
||||||
orphanVisits: VisitsInfo;
|
orphanVisits: VisitsInfo;
|
||||||
|
nonOrphanVisits: VisitsInfo;
|
||||||
shortUrlDetail: ShortUrlDetail;
|
shortUrlDetail: ShortUrlDetail;
|
||||||
tagsList: TagsList;
|
tagsList: TagsList;
|
||||||
tagDelete: TagDeletion;
|
tagDelete: TagDeletion;
|
||||||
|
|
|
@ -8,6 +8,7 @@ import shortUrlEditionReducer from '../short-urls/reducers/shortUrlEdition';
|
||||||
import shortUrlVisitsReducer from '../visits/reducers/shortUrlVisits';
|
import shortUrlVisitsReducer from '../visits/reducers/shortUrlVisits';
|
||||||
import tagVisitsReducer from '../visits/reducers/tagVisits';
|
import tagVisitsReducer from '../visits/reducers/tagVisits';
|
||||||
import orphanVisitsReducer from '../visits/reducers/orphanVisits';
|
import orphanVisitsReducer from '../visits/reducers/orphanVisits';
|
||||||
|
import nonOrphanVisitsReducer from '../visits/reducers/nonOrphanVisits';
|
||||||
import shortUrlDetailReducer from '../short-urls/reducers/shortUrlDetail';
|
import shortUrlDetailReducer from '../short-urls/reducers/shortUrlDetail';
|
||||||
import tagsListReducer from '../tags/reducers/tagsList';
|
import tagsListReducer from '../tags/reducers/tagsList';
|
||||||
import tagDeleteReducer from '../tags/reducers/tagDelete';
|
import tagDeleteReducer from '../tags/reducers/tagDelete';
|
||||||
|
@ -29,6 +30,7 @@ export default combineReducers<ShlinkState>({
|
||||||
shortUrlVisits: shortUrlVisitsReducer,
|
shortUrlVisits: shortUrlVisitsReducer,
|
||||||
tagVisits: tagVisitsReducer,
|
tagVisits: tagVisitsReducer,
|
||||||
orphanVisits: orphanVisitsReducer,
|
orphanVisits: orphanVisitsReducer,
|
||||||
|
nonOrphanVisits: nonOrphanVisitsReducer,
|
||||||
shortUrlDetail: shortUrlDetailReducer,
|
shortUrlDetail: shortUrlDetailReducer,
|
||||||
tagsList: tagsListReducer,
|
tagsList: tagsListReducer,
|
||||||
tagDelete: tagDeleteReducer,
|
tagDelete: tagDeleteReducer,
|
||||||
|
|
|
@ -56,15 +56,12 @@ export const Overview = (
|
||||||
<>
|
<>
|
||||||
<Row>
|
<Row>
|
||||||
<div className="col-lg-6 col-xl-3 mb-3">
|
<div className="col-lg-6 col-xl-3 mb-3">
|
||||||
<HighlightCard title="Visits" link={linkToNonOrphanVisits ? `/server/${serverId}/visits` : undefined}>
|
<HighlightCard title="Visits" link={linkToNonOrphanVisits && `/server/${serverId}/non-orphan-visits`}>
|
||||||
{loadingVisits ? 'Loading...' : prettify(visitsCount)}
|
{loadingVisits ? 'Loading...' : prettify(visitsCount)}
|
||||||
</HighlightCard>
|
</HighlightCard>
|
||||||
</div>
|
</div>
|
||||||
<div className="col-lg-6 col-xl-3 mb-3">
|
<div className="col-lg-6 col-xl-3 mb-3">
|
||||||
<HighlightCard
|
<HighlightCard title="Orphan visits" link={linkToOrphanVisits && `/server/${serverId}/orphan-visits`}>
|
||||||
title="Orphan visits"
|
|
||||||
link={linkToOrphanVisits ? `/server/${serverId}/orphan-visits` : undefined}
|
|
||||||
>
|
|
||||||
<ForServerVersion minVersion="2.6.0">
|
<ForServerVersion minVersion="2.6.0">
|
||||||
{loadingVisits ? 'Loading...' : prettify(orphanVisitsCount ?? 0)}
|
{loadingVisits ? 'Loading...' : prettify(orphanVisitsCount ?? 0)}
|
||||||
</ForServerVersion>
|
</ForServerVersion>
|
||||||
|
|
|
@ -7,10 +7,10 @@ import './HighlightCard.scss';
|
||||||
|
|
||||||
export interface HighlightCardProps {
|
export interface HighlightCardProps {
|
||||||
title: string;
|
title: string;
|
||||||
link?: string;
|
link?: string | false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const buildExtraProps = (link?: string) => !link ? {} : { tag: Link, to: link };
|
const buildExtraProps = (link?: string | false) => !link ? {} : { tag: Link, to: link };
|
||||||
|
|
||||||
export const HighlightCard: FC<HighlightCardProps> = ({ children, title, link }) => (
|
export const HighlightCard: FC<HighlightCardProps> = ({ children, title, link }) => (
|
||||||
<Card className="highlight-card" body {...buildExtraProps(link)}>
|
<Card className="highlight-card" body {...buildExtraProps(link)}>
|
||||||
|
|
|
@ -313,6 +313,20 @@ describe('ShlinkApiClient', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('getNonOrphanVisits', () => {
|
||||||
|
it('returns non-orphan visits', async () => {
|
||||||
|
const expectedData: Visit[] = [];
|
||||||
|
const resp = { visits: expectedData };
|
||||||
|
const axiosSpy = createAxiosMock({ data: resp });
|
||||||
|
const { getNonOrphanVisits } = new ShlinkApiClient(axiosSpy, '', '');
|
||||||
|
|
||||||
|
const result = await getNonOrphanVisits();
|
||||||
|
|
||||||
|
expect(axiosSpy).toHaveBeenCalled();
|
||||||
|
expect(result).toEqual(expectedData);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('editDomainRedirects', () => {
|
describe('editDomainRedirects', () => {
|
||||||
it('returns the redirects', async () => {
|
it('returns the redirects', async () => {
|
||||||
const resp = { baseUrlRedirect: null, regular404Redirect: 'foo', invalidShortUrlRedirect: 'bar' };
|
const resp = { baseUrlRedirect: null, regular404Redirect: 'foo', invalidShortUrlRedirect: 'bar' };
|
||||||
|
|
|
@ -11,7 +11,7 @@ import { SemVer } from '../../src/utils/helpers/version';
|
||||||
describe('<MenuLayout />', () => {
|
describe('<MenuLayout />', () => {
|
||||||
const ServerError = jest.fn();
|
const ServerError = jest.fn();
|
||||||
const C = jest.fn();
|
const C = jest.fn();
|
||||||
const MenuLayout = createMenuLayout(C, C, C, C, C, C, C, ServerError, C, C, C);
|
const MenuLayout = createMenuLayout(C, C, C, C, C, C, C, C, ServerError, C, C, C);
|
||||||
let wrapper: ShallowWrapper;
|
let wrapper: ShallowWrapper;
|
||||||
const createWrapper = (selectedServer: SelectedServer) => {
|
const createWrapper = (selectedServer: SelectedServer) => {
|
||||||
wrapper = shallow(
|
wrapper = shallow(
|
||||||
|
@ -52,6 +52,9 @@ describe('<MenuLayout />', () => {
|
||||||
[ '2.5.0' as SemVer, 8 ],
|
[ '2.5.0' as SemVer, 8 ],
|
||||||
[ '2.6.0' as SemVer, 9 ],
|
[ '2.6.0' as SemVer, 9 ],
|
||||||
[ '2.7.0' as SemVer, 9 ],
|
[ '2.7.0' as SemVer, 9 ],
|
||||||
|
[ '2.8.0' as SemVer, 10 ],
|
||||||
|
[ '2.10.0' as SemVer, 10 ],
|
||||||
|
[ '3.0.0' as SemVer, 11 ],
|
||||||
])('has expected amount of routes based on selected server\'s version', (version, expectedAmountOfRoutes) => {
|
])('has expected amount of routes based on selected server\'s version', (version, expectedAmountOfRoutes) => {
|
||||||
const selectedServer = Mock.of<ReachableServer>({ version });
|
const selectedServer = Mock.of<ReachableServer>({ version });
|
||||||
const wrapper = createWrapper(selectedServer).dive();
|
const wrapper = createWrapper(selectedServer).dive();
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
import { FC } from 'react';
|
import { FC } from 'react';
|
||||||
import { shallow, ShallowWrapper } from 'enzyme';
|
import { mount, ReactWrapper } from 'enzyme';
|
||||||
import { Mock } from 'ts-mockery';
|
import { Mock } from 'ts-mockery';
|
||||||
import { CardText } from 'reactstrap';
|
import { Link, MemoryRouter } from 'react-router-dom';
|
||||||
import { Link } from 'react-router-dom';
|
|
||||||
import { ShortUrlsList as ShortUrlsListState } from '../../src/short-urls/reducers/shortUrlsList';
|
import { ShortUrlsList as ShortUrlsListState } from '../../src/short-urls/reducers/shortUrlsList';
|
||||||
import { Overview as overviewCreator } from '../../src/servers/Overview';
|
import { Overview as overviewCreator } from '../../src/servers/Overview';
|
||||||
import { TagsList } from '../../src/tags/reducers/tagsList';
|
import { TagsList } from '../../src/tags/reducers/tagsList';
|
||||||
|
@ -10,9 +9,10 @@ import { VisitsOverview } from '../../src/visits/reducers/visitsOverview';
|
||||||
import { MercureInfo } from '../../src/mercure/reducers/mercureInfo';
|
import { MercureInfo } from '../../src/mercure/reducers/mercureInfo';
|
||||||
import { ReachableServer } from '../../src/servers/data';
|
import { ReachableServer } from '../../src/servers/data';
|
||||||
import { prettify } from '../../src/utils/helpers/numbers';
|
import { prettify } from '../../src/utils/helpers/numbers';
|
||||||
|
import { HighlightCard } from '../../src/servers/helpers/HighlightCard';
|
||||||
|
|
||||||
describe('<Overview />', () => {
|
describe('<Overview />', () => {
|
||||||
let wrapper: ShallowWrapper;
|
let wrapper: ReactWrapper;
|
||||||
const ShortUrlsTable = () => null;
|
const ShortUrlsTable = () => null;
|
||||||
const CreateShortUrl = () => null;
|
const CreateShortUrl = () => null;
|
||||||
const ForServerVersion: FC = ({ children }) => <>{children}</>;
|
const ForServerVersion: FC = ({ children }) => <>{children}</>;
|
||||||
|
@ -25,20 +25,22 @@ describe('<Overview />', () => {
|
||||||
};
|
};
|
||||||
const serverId = '123';
|
const serverId = '123';
|
||||||
const createWrapper = (loading = false) => {
|
const createWrapper = (loading = false) => {
|
||||||
wrapper = shallow(
|
wrapper = mount(
|
||||||
<Overview
|
<MemoryRouter>
|
||||||
listShortUrls={listShortUrls}
|
<Overview
|
||||||
listTags={listTags}
|
listShortUrls={listShortUrls}
|
||||||
loadVisitsOverview={loadVisitsOverview}
|
listTags={listTags}
|
||||||
shortUrlsList={Mock.of<ShortUrlsListState>({ loading, shortUrls })}
|
loadVisitsOverview={loadVisitsOverview}
|
||||||
tagsList={Mock.of<TagsList>({ loading, tags: [ 'foo', 'bar', 'baz' ] })}
|
shortUrlsList={Mock.of<ShortUrlsListState>({ loading, shortUrls })}
|
||||||
visitsOverview={Mock.of<VisitsOverview>({ loading, visitsCount: 3456, orphanVisitsCount: 28 })}
|
tagsList={Mock.of<TagsList>({ loading, tags: [ 'foo', 'bar', 'baz' ] })}
|
||||||
selectedServer={Mock.of<ReachableServer>({ id: serverId })}
|
visitsOverview={Mock.of<VisitsOverview>({ loading, visitsCount: 3456, orphanVisitsCount: 28 })}
|
||||||
createNewVisits={jest.fn()}
|
selectedServer={Mock.of<ReachableServer>({ id: serverId })}
|
||||||
loadMercureInfo={jest.fn()}
|
createNewVisits={jest.fn()}
|
||||||
mercureInfo={Mock.all<MercureInfo>()}
|
loadMercureInfo={jest.fn()}
|
||||||
/>,
|
mercureInfo={Mock.all<MercureInfo>()}
|
||||||
).dive(); // Dive is needed as this component is wrapped in a HOC
|
/>
|
||||||
|
</MemoryRouter>,
|
||||||
|
);
|
||||||
|
|
||||||
return wrapper;
|
return wrapper;
|
||||||
};
|
};
|
||||||
|
@ -47,7 +49,7 @@ describe('<Overview />', () => {
|
||||||
|
|
||||||
it('displays loading messages when still loading', () => {
|
it('displays loading messages when still loading', () => {
|
||||||
const wrapper = createWrapper(true);
|
const wrapper = createWrapper(true);
|
||||||
const cards = wrapper.find(CardText);
|
const cards = wrapper.find(HighlightCard);
|
||||||
|
|
||||||
expect(cards).toHaveLength(4);
|
expect(cards).toHaveLength(4);
|
||||||
cards.forEach((card) => expect(card.html()).toContain('Loading...'));
|
cards.forEach((card) => expect(card.html()).toContain('Loading...'));
|
||||||
|
@ -55,7 +57,7 @@ describe('<Overview />', () => {
|
||||||
|
|
||||||
it('displays amounts in cards after finishing loading', () => {
|
it('displays amounts in cards after finishing loading', () => {
|
||||||
const wrapper = createWrapper();
|
const wrapper = createWrapper();
|
||||||
const cards = wrapper.find(CardText);
|
const cards = wrapper.find(HighlightCard);
|
||||||
|
|
||||||
expect(cards).toHaveLength(4);
|
expect(cards).toHaveLength(4);
|
||||||
expect(cards.at(0).html()).toContain(prettify(3456));
|
expect(cards.at(0).html()).toContain(prettify(3456));
|
||||||
|
@ -75,8 +77,10 @@ describe('<Overview />', () => {
|
||||||
const wrapper = createWrapper();
|
const wrapper = createWrapper();
|
||||||
const links = wrapper.find(Link);
|
const links = wrapper.find(Link);
|
||||||
|
|
||||||
expect(links).toHaveLength(2);
|
expect(links).toHaveLength(4);
|
||||||
expect(links.at(0).prop('to')).toEqual(`/server/${serverId}/create-short-url`);
|
expect(links.at(0).prop('to')).toEqual(`/server/${serverId}/list-short-urls/1`);
|
||||||
expect(links.at(1).prop('to')).toEqual(`/server/${serverId}/list-short-urls/1`);
|
expect(links.at(1).prop('to')).toEqual(`/server/${serverId}/manage-tags`);
|
||||||
|
expect(links.at(2).prop('to')).toEqual(`/server/${serverId}/create-short-url`);
|
||||||
|
expect(links.at(3).prop('to')).toEqual(`/server/${serverId}/list-short-urls/1`);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -15,8 +15,11 @@ describe('<HighlightCard />', () => {
|
||||||
|
|
||||||
afterEach(() => wrapper?.unmount());
|
afterEach(() => wrapper?.unmount());
|
||||||
|
|
||||||
it('renders expected components', () => {
|
it.each([
|
||||||
const wrapper = createWrapper({ title: 'foo' });
|
[ undefined ],
|
||||||
|
[ false ],
|
||||||
|
])('renders expected components', (link) => {
|
||||||
|
const wrapper = createWrapper({ title: 'foo', link: link as undefined | false });
|
||||||
|
|
||||||
expect(wrapper.find(Card)).toHaveLength(1);
|
expect(wrapper.find(Card)).toHaveLength(1);
|
||||||
expect(wrapper.find(CardTitle)).toHaveLength(1);
|
expect(wrapper.find(CardTitle)).toHaveLength(1);
|
||||||
|
|
Loading…
Reference in a new issue