mirror of
https://github.com/shlinkio/shlink-web-client.git
synced 2025-01-09 09:47:28 +03:00
Fix shlink-web-component tests
This commit is contained in:
parent
bdcfcee60e
commit
4d8477a32c
54 changed files with 345 additions and 431 deletions
|
@ -14,7 +14,7 @@ export const VisitsHighlightCard: FC<VisitsHighlightCardProps> = ({ loading, exc
|
|||
<HighlightCard
|
||||
tooltip={
|
||||
visitsSummary.bots !== undefined
|
||||
? <>{excludeBots ? 'Plus' : 'Including'} <b>{prettify(visitsSummary.bots)}</b> potential bot visits</>
|
||||
? <>{excludeBots ? 'Plus' : 'Including'} <strong>{prettify(visitsSummary.bots)}</strong> potential bot visits</>
|
||||
: undefined
|
||||
}
|
||||
{...rest}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import type { FC, ReactElement } from 'react';
|
||||
import { useToggle } from '../../src/utils/helpers/hooks';
|
||||
import { useToggle } from '../../../shlink-frontend-kit/src';
|
||||
|
||||
interface RenderModalArgs {
|
||||
isOpen: boolean;
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
import { render, screen } from '@testing-library/react';
|
||||
import { fromPartial } from '@total-typescript/shoehorn';
|
||||
import { MemoryRouter } from 'react-router';
|
||||
import { AsideMenu } from '../../src/common/AsideMenu';
|
||||
|
||||
describe('<AsideMenu />', () => {
|
||||
const setUp = () => render(
|
||||
<MemoryRouter>
|
||||
<AsideMenu selectedServer={fromPartial({ id: 'abc123', version: '2.8.0' })} />
|
||||
<AsideMenu routePrefix="/abc123" />
|
||||
</MemoryRouter>,
|
||||
);
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { render, screen } from '@testing-library/react';
|
||||
import { fromPartial } from '@total-typescript/shoehorn';
|
||||
import type { InvalidArgumentError, ProblemDetailsError } from '../../src/api/types/errors';
|
||||
import { ErrorTypeV2, ErrorTypeV3 } from '../../src/api/types/errors';
|
||||
import type { InvalidArgumentError, ProblemDetailsError } from '../../src/api-contract';
|
||||
import { ErrorTypeV2, ErrorTypeV3 } from '../../src/api-contract';
|
||||
import type { ShlinkApiErrorProps } from '../../src/common/ShlinkApiError';
|
||||
import { ShlinkApiError } from '../../src/common/ShlinkApiError';
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { render, screen } from '@testing-library/react';
|
||||
import { fromPartial } from '@total-typescript/shoehorn';
|
||||
import type { ShlinkDomainRedirects } from '../../src/api/types';
|
||||
import type { ShlinkDomainRedirects } from '../../src/api-contract';
|
||||
import type { Domain } from '../../src/domains/data';
|
||||
import { DomainRow } from '../../src/domains/DomainRow';
|
||||
|
||||
|
@ -21,7 +21,6 @@ describe('<DomainRow />', () => {
|
|||
<DomainRow
|
||||
domain={domain}
|
||||
defaultRedirects={defaultRedirects}
|
||||
selectedServer={fromPartial({})}
|
||||
editDomainRedirects={vi.fn()}
|
||||
checkDomainHealth={vi.fn()}
|
||||
/>
|
||||
|
|
|
@ -1,26 +1,29 @@
|
|||
import { screen, waitForElementToBeRemoved } from '@testing-library/react';
|
||||
import { fromPartial } from '@total-typescript/shoehorn';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
import type { SelectedServer } from '../../../../src/servers/data';
|
||||
import type { SemVer } from '../../../../src/utils/helpers/version';
|
||||
import type { Domain } from '../../../src/domains/data';
|
||||
import { DomainDropdown } from '../../../src/domains/helpers/DomainDropdown';
|
||||
import { FeaturesProvider } from '../../../src/utils/features';
|
||||
import { RoutesPrefixProvider } from '../../../src/utils/routesPrefix';
|
||||
import { renderWithEvents } from '../../__helpers__/setUpTest';
|
||||
|
||||
describe('<DomainDropdown />', () => {
|
||||
const editDomainRedirects = vi.fn().mockResolvedValue(undefined);
|
||||
const setUp = (domain?: Domain, selectedServer?: SelectedServer) => renderWithEvents(
|
||||
const setUp = ({ domain, withVisits = true }: { domain?: Domain; withVisits?: boolean } = {}) => renderWithEvents(
|
||||
<MemoryRouter>
|
||||
<DomainDropdown
|
||||
domain={domain ?? fromPartial({})}
|
||||
selectedServer={selectedServer ?? fromPartial({})}
|
||||
editDomainRedirects={editDomainRedirects}
|
||||
/>
|
||||
<RoutesPrefixProvider value="/server/123">
|
||||
<FeaturesProvider value={fromPartial({ domainVisits: withVisits })}>
|
||||
<DomainDropdown
|
||||
domain={domain ?? fromPartial({})}
|
||||
editDomainRedirects={editDomainRedirects}
|
||||
/>
|
||||
</FeaturesProvider>
|
||||
</RoutesPrefixProvider>
|
||||
</MemoryRouter>,
|
||||
);
|
||||
|
||||
it('renders expected menu items', () => {
|
||||
setUp();
|
||||
setUp({ withVisits: false });
|
||||
|
||||
expect(screen.queryByText('Visit stats')).not.toBeInTheDocument();
|
||||
expect(screen.getByText('Edit redirects')).toBeInTheDocument();
|
||||
|
@ -30,37 +33,17 @@ describe('<DomainDropdown />', () => {
|
|||
[true, '_DEFAULT'],
|
||||
[false, ''],
|
||||
])('points first link to the proper section', (isDefault, expectedLink) => {
|
||||
setUp(
|
||||
fromPartial({ domain: 'foo.com', isDefault }),
|
||||
fromPartial({ version: '3.1.0', id: '123' }),
|
||||
);
|
||||
setUp({ domain: fromPartial({ domain: 'foo.com', isDefault }) });
|
||||
|
||||
expect(screen.getByText('Visit stats')).toHaveAttribute('href', `/server/123/domain/foo.com${expectedLink}/visits`);
|
||||
});
|
||||
|
||||
it.each([
|
||||
[true, '2.9.0' as SemVer, false],
|
||||
[true, '2.10.0' as SemVer, true],
|
||||
[false, '2.9.0' as SemVer, true],
|
||||
])('allows editing certain the domains', (isDefault, serverVersion, canBeEdited) => {
|
||||
setUp(
|
||||
fromPartial({ domain: 'foo.com', isDefault }),
|
||||
fromPartial({ version: serverVersion, id: '123' }),
|
||||
);
|
||||
|
||||
if (canBeEdited) {
|
||||
expect(screen.getByText('Edit redirects')).not.toHaveAttribute('disabled');
|
||||
} else {
|
||||
expect(screen.getByText('Edit redirects')).toHaveAttribute('disabled');
|
||||
}
|
||||
});
|
||||
|
||||
it.each([
|
||||
['foo.com'],
|
||||
['bar.org'],
|
||||
['baz.net'],
|
||||
])('displays modal when editing redirects', async (domain) => {
|
||||
const { user } = setUp(fromPartial({ domain, isDefault: false }));
|
||||
const { user } = setUp({ domain: fromPartial({ domain, isDefault: false }) });
|
||||
|
||||
expect(screen.queryByRole('dialog')).not.toBeInTheDocument();
|
||||
expect(screen.queryByRole('form')).not.toBeInTheDocument();
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { fromPartial } from '@total-typescript/shoehorn';
|
||||
import type { ShlinkApiClient } from '../../../../src/api/services/ShlinkApiClient';
|
||||
import type { ShlinkDomainRedirects } from '../../../src/api/types';
|
||||
import type { ShlinkDomainRedirects } from '../../../src/api-contract';
|
||||
import { editDomainRedirects } from '../../../src/domains/reducers/domainRedirects';
|
||||
|
||||
describe('domainRedirectsReducer', () => {
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
import { fromPartial } from '@total-typescript/shoehorn';
|
||||
import type { ShlinkApiClient } from '../../../../src/api/services/ShlinkApiClient';
|
||||
import type { ShlinkState } from '../../../../src/container/types';
|
||||
import type { ShlinkDomainRedirects } from '../../../src/api/types';
|
||||
import { parseApiError } from '../../../src/api/utils';
|
||||
import type { ShlinkApiClient, ShlinkDomainRedirects } from '../../../src/api-contract';
|
||||
import { parseApiError } from '../../../src/api-contract/utils';
|
||||
import type { Domain } from '../../../src/domains/data';
|
||||
import type { EditDomainRedirects } from '../../../src/domains/reducers/domainRedirects';
|
||||
import { editDomainRedirects } from '../../../src/domains/reducers/domainRedirects';
|
||||
|
@ -17,35 +15,35 @@ describe('domainsListReducer', () => {
|
|||
const getState = vi.fn();
|
||||
const listDomains = vi.fn();
|
||||
const health = vi.fn();
|
||||
const buildShlinkApiClient = () => fromPartial<ShlinkApiClient>({ listDomains, health });
|
||||
const apiClientFactory = () => fromPartial<ShlinkApiClient>({ listDomains, health });
|
||||
const filteredDomains: Domain[] = [
|
||||
fromPartial({ domain: 'foo', status: 'validating' }),
|
||||
fromPartial({ domain: 'Boo', status: 'validating' }),
|
||||
];
|
||||
const domains: Domain[] = [...filteredDomains, fromPartial({ domain: 'bar', status: 'validating' })];
|
||||
const error = { type: 'NOT_FOUND', status: 404 } as unknown as Error;
|
||||
const editDomainRedirectsThunk = editDomainRedirects(buildShlinkApiClient);
|
||||
const editDomainRedirectsThunk = editDomainRedirects(apiClientFactory);
|
||||
const { reducer, listDomains: listDomainsAction, checkDomainHealth, filterDomains } = domainsListReducerCreator(
|
||||
buildShlinkApiClient,
|
||||
apiClientFactory,
|
||||
editDomainRedirectsThunk,
|
||||
);
|
||||
|
||||
describe('reducer', () => {
|
||||
it('returns loading on LIST_DOMAINS_START', () => {
|
||||
expect(reducer(undefined, listDomainsAction.pending(''))).toEqual(
|
||||
expect(reducer(undefined, listDomainsAction.pending('', {}))).toEqual(
|
||||
{ domains: [], filteredDomains: [], loading: true, error: false },
|
||||
);
|
||||
});
|
||||
|
||||
it('returns error on LIST_DOMAINS_ERROR', () => {
|
||||
expect(reducer(undefined, listDomainsAction.rejected(error, ''))).toEqual(
|
||||
expect(reducer(undefined, listDomainsAction.rejected(error, '', {}))).toEqual(
|
||||
{ domains: [], filteredDomains: [], loading: false, error: true, errorData: parseApiError(error) },
|
||||
);
|
||||
});
|
||||
|
||||
it('returns domains on LIST_DOMAINS', () => {
|
||||
expect(
|
||||
reducer(undefined, listDomainsAction.fulfilled({ domains }, '')),
|
||||
reducer(undefined, listDomainsAction.fulfilled({ domains }, '', {})),
|
||||
).toEqual({ domains, filteredDomains: domains, loading: false, error: false });
|
||||
});
|
||||
|
||||
|
@ -93,7 +91,7 @@ describe('domainsListReducer', () => {
|
|||
it('dispatches domains once loaded', async () => {
|
||||
listDomains.mockResolvedValue({ data: domains });
|
||||
|
||||
await listDomainsAction()(dispatch, getState, {});
|
||||
await listDomainsAction({})(dispatch, getState, {});
|
||||
|
||||
expect(dispatch).toHaveBeenCalledTimes(2);
|
||||
expect(dispatch).toHaveBeenLastCalledWith(expect.objectContaining({
|
||||
|
@ -116,33 +114,13 @@ describe('domainsListReducer', () => {
|
|||
describe('checkDomainHealth', () => {
|
||||
const domain = 'example.com';
|
||||
|
||||
it('dispatches invalid status when selected server does not have all required data', async () => {
|
||||
getState.mockReturnValue(fromPartial<ShlinkState>({
|
||||
selectedServer: {},
|
||||
}));
|
||||
|
||||
await checkDomainHealth(domain)(dispatch, getState, {});
|
||||
|
||||
expect(getState).toHaveBeenCalledTimes(1);
|
||||
expect(health).not.toHaveBeenCalled();
|
||||
expect(dispatch).toHaveBeenLastCalledWith(expect.objectContaining({
|
||||
payload: { domain, status: 'invalid' },
|
||||
}));
|
||||
});
|
||||
|
||||
it('dispatches invalid status when health endpoint returns an error', async () => {
|
||||
getState.mockReturnValue(fromPartial<ShlinkState>({
|
||||
selectedServer: {
|
||||
url: 'https://myerver.com',
|
||||
apiKey: '123',
|
||||
},
|
||||
}));
|
||||
health.mockRejectedValue({});
|
||||
|
||||
await checkDomainHealth(domain)(dispatch, getState, {});
|
||||
|
||||
expect(getState).toHaveBeenCalledTimes(1);
|
||||
expect(health).toHaveBeenCalledTimes(1);
|
||||
expect(health).toHaveBeenCalledWith(domain);
|
||||
expect(dispatch).toHaveBeenLastCalledWith(expect.objectContaining({
|
||||
payload: { domain, status: 'invalid' },
|
||||
}));
|
||||
|
@ -155,18 +133,12 @@ describe('domainsListReducer', () => {
|
|||
healthStatus,
|
||||
expectedStatus,
|
||||
) => {
|
||||
getState.mockReturnValue(fromPartial<ShlinkState>({
|
||||
selectedServer: {
|
||||
url: 'https://myerver.com',
|
||||
apiKey: '123',
|
||||
},
|
||||
}));
|
||||
health.mockResolvedValue({ status: healthStatus });
|
||||
|
||||
await checkDomainHealth(domain)(dispatch, getState, {});
|
||||
|
||||
expect(getState).toHaveBeenCalledTimes(1);
|
||||
expect(health).toHaveBeenCalledTimes(1);
|
||||
expect(health).toHaveBeenCalledWith(domain);
|
||||
expect(dispatch).toHaveBeenLastCalledWith(expect.objectContaining({
|
||||
payload: { domain, status: expectedStatus },
|
||||
}));
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { fromPartial } from '@total-typescript/shoehorn';
|
||||
import type { ShlinkApiClient } from '../../../../src/api/services/ShlinkApiClient';
|
||||
import type { GetState } from '../../../../src/container/types';
|
||||
import type { Settings } from '../../../src';
|
||||
import type { ShlinkApiClient } from '../../../src/api-contract';
|
||||
import { mercureInfoReducerCreator } from '../../../src/mercure/reducers/mercureInfo';
|
||||
|
||||
describe('mercureInfoReducer', () => {
|
||||
|
@ -14,21 +14,21 @@ describe('mercureInfoReducer', () => {
|
|||
|
||||
describe('reducer', () => {
|
||||
it('returns loading on GET_MERCURE_INFO_START', () => {
|
||||
expect(reducer(undefined, loadMercureInfo.pending(''))).toEqual({
|
||||
expect(reducer(undefined, loadMercureInfo.pending('', {}))).toEqual({
|
||||
loading: true,
|
||||
error: false,
|
||||
});
|
||||
});
|
||||
|
||||
it('returns error on GET_MERCURE_INFO_ERROR', () => {
|
||||
expect(reducer(undefined, loadMercureInfo.rejected(null, ''))).toEqual({
|
||||
expect(reducer(undefined, loadMercureInfo.rejected(null, '', {}))).toEqual({
|
||||
loading: false,
|
||||
error: true,
|
||||
});
|
||||
});
|
||||
|
||||
it('returns mercure info on GET_MERCURE_INFO', () => {
|
||||
expect(reducer(undefined, loadMercureInfo.fulfilled(mercureInfo, ''))).toEqual(
|
||||
expect(reducer(undefined, loadMercureInfo.fulfilled(mercureInfo, '', {}))).toEqual(
|
||||
expect.objectContaining({ ...mercureInfo, loading: false, error: false }),
|
||||
);
|
||||
});
|
||||
|
@ -36,17 +36,15 @@ describe('mercureInfoReducer', () => {
|
|||
|
||||
describe('loadMercureInfo', () => {
|
||||
const dispatch = vi.fn();
|
||||
const createGetStateMock = (enabled: boolean): GetState => vi.fn().mockReturnValue({
|
||||
settings: {
|
||||
realTimeUpdates: { enabled },
|
||||
},
|
||||
const createSettings = (enabled: boolean): Settings => fromPartial({
|
||||
realTimeUpdates: { enabled },
|
||||
});
|
||||
|
||||
it('dispatches error when real time updates are disabled', async () => {
|
||||
getMercureInfo.mockResolvedValue(mercureInfo);
|
||||
const getState = createGetStateMock(false);
|
||||
const settings = createSettings(false);
|
||||
|
||||
await loadMercureInfo()(dispatch, getState, {});
|
||||
await loadMercureInfo(settings)(dispatch, vi.fn(), {});
|
||||
|
||||
expect(getMercureInfo).not.toHaveBeenCalled();
|
||||
expect(dispatch).toHaveBeenCalledTimes(2);
|
||||
|
@ -57,9 +55,9 @@ describe('mercureInfoReducer', () => {
|
|||
|
||||
it('calls API on success', async () => {
|
||||
getMercureInfo.mockResolvedValue(mercureInfo);
|
||||
const getState = createGetStateMock(true);
|
||||
const settings = createSettings(true);
|
||||
|
||||
await loadMercureInfo()(dispatch, getState, {});
|
||||
await loadMercureInfo(settings)(dispatch, vi.fn(), {});
|
||||
|
||||
expect(getMercureInfo).toHaveBeenCalledTimes(1);
|
||||
expect(dispatch).toHaveBeenCalledTimes(2);
|
||||
|
|
|
@ -4,6 +4,8 @@ import { MemoryRouter } from 'react-router-dom';
|
|||
import type { MercureInfo } from '../../src/mercure/reducers/mercureInfo';
|
||||
import { Overview as overviewCreator } from '../../src/overview/Overview';
|
||||
import { prettify } from '../../src/utils/helpers/numbers';
|
||||
import { RoutesPrefixProvider } from '../../src/utils/routesPrefix';
|
||||
import { SettingsProvider } from '../../src/utils/settings';
|
||||
import { renderWithEvents } from '../__helpers__/setUpTest';
|
||||
|
||||
describe('<Overview />', () => {
|
||||
|
@ -16,26 +18,28 @@ describe('<Overview />', () => {
|
|||
const shortUrls = {
|
||||
pagination: { totalItems: 83710 },
|
||||
};
|
||||
const serverId = '123';
|
||||
const routesPrefix = '/server/123';
|
||||
const setUp = (loading = false, excludeBots = false) => renderWithEvents(
|
||||
<MemoryRouter>
|
||||
<Overview
|
||||
listShortUrls={listShortUrls}
|
||||
listTags={listTags}
|
||||
loadVisitsOverview={loadVisitsOverview}
|
||||
shortUrlsList={fromPartial({ loading, shortUrls })}
|
||||
tagsList={fromPartial({ loading, tags: ['foo', 'bar', 'baz'] })}
|
||||
visitsOverview={fromPartial({
|
||||
loading,
|
||||
nonOrphanVisits: { total: 3456, bots: 1000, nonBots: 2456 },
|
||||
orphanVisits: { total: 28, bots: 15, nonBots: 13 },
|
||||
})}
|
||||
selectedServer={fromPartial({ id: serverId })}
|
||||
createNewVisits={vi.fn()}
|
||||
loadMercureInfo={vi.fn()}
|
||||
mercureInfo={fromPartial<MercureInfo>({})}
|
||||
settings={fromPartial({ visits: { excludeBots } })}
|
||||
/>
|
||||
<SettingsProvider value={fromPartial({ visits: { excludeBots } })}>
|
||||
<RoutesPrefixProvider value={routesPrefix}>
|
||||
<Overview
|
||||
listShortUrls={listShortUrls}
|
||||
listTags={listTags}
|
||||
loadVisitsOverview={loadVisitsOverview}
|
||||
shortUrlsList={fromPartial({ loading, shortUrls })}
|
||||
tagsList={fromPartial({ loading, tags: ['foo', 'bar', 'baz'] })}
|
||||
visitsOverview={fromPartial({
|
||||
loading,
|
||||
nonOrphanVisits: { total: 3456, bots: 1000, nonBots: 2456 },
|
||||
orphanVisits: { total: 28, bots: 15, nonBots: 13 },
|
||||
})}
|
||||
createNewVisits={vi.fn()}
|
||||
loadMercureInfo={vi.fn()}
|
||||
mercureInfo={fromPartial<MercureInfo>({})}
|
||||
/>
|
||||
</RoutesPrefixProvider>
|
||||
</SettingsProvider>
|
||||
</MemoryRouter>,
|
||||
);
|
||||
|
||||
|
@ -75,12 +79,13 @@ describe('<Overview />', () => {
|
|||
|
||||
const links = screen.getAllByRole('link');
|
||||
|
||||
expect(links).toHaveLength(5);
|
||||
expect(links[0]).toHaveAttribute('href', `/server/${serverId}/orphan-visits`);
|
||||
expect(links[1]).toHaveAttribute('href', `/server/${serverId}/list-short-urls/1`);
|
||||
expect(links[2]).toHaveAttribute('href', `/server/${serverId}/manage-tags`);
|
||||
expect(links[3]).toHaveAttribute('href', `/server/${serverId}/create-short-url`);
|
||||
expect(links[4]).toHaveAttribute('href', `/server/${serverId}/list-short-urls/1`);
|
||||
expect(links).toHaveLength(6);
|
||||
expect(links[0]).toHaveAttribute('href', `${routesPrefix}/non-orphan-visits`);
|
||||
expect(links[1]).toHaveAttribute('href', `${routesPrefix}/orphan-visits`);
|
||||
expect(links[2]).toHaveAttribute('href', `${routesPrefix}/list-short-urls/1`);
|
||||
expect(links[3]).toHaveAttribute('href', `${routesPrefix}/manage-tags`);
|
||||
expect(links[4]).toHaveAttribute('href', `${routesPrefix}/create-short-url`);
|
||||
expect(links[5]).toHaveAttribute('href', `${routesPrefix}/list-short-urls/1`);
|
||||
});
|
||||
|
||||
it.each([
|
||||
|
|
|
@ -1,27 +1,17 @@
|
|||
import { screen, waitFor } from '@testing-library/react';
|
||||
import type { ReactNode } from 'react';
|
||||
import type { PropsWithChildren } from 'react';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
import type { HighlightCardProps } from '../../../src/overview/helpers/HighlightCard';
|
||||
import { HighlightCard } from '../../../src/overview/helpers/HighlightCard';
|
||||
import { renderWithEvents } from '../../__helpers__/setUpTest';
|
||||
|
||||
describe('<HighlightCard />', () => {
|
||||
const setUp = (props: HighlightCardProps & { children?: ReactNode }) => renderWithEvents(
|
||||
const setUp = (props: PropsWithChildren<Partial<HighlightCardProps>>) => renderWithEvents(
|
||||
<MemoryRouter>
|
||||
<HighlightCard {...props} />
|
||||
<HighlightCard link="" title="" {...props} />
|
||||
</MemoryRouter>,
|
||||
);
|
||||
|
||||
it.each([
|
||||
[undefined],
|
||||
[''],
|
||||
])('does not render icon when there is no link', (link) => {
|
||||
setUp({ title: 'foo', link });
|
||||
|
||||
expect(screen.queryByRole('img', { hidden: true })).not.toBeInTheDocument();
|
||||
expect(screen.queryByRole('link')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it.each([
|
||||
['foo'],
|
||||
['bar'],
|
||||
|
|
|
@ -1,18 +1,21 @@
|
|||
import { screen, waitFor } from '@testing-library/react';
|
||||
import { MemoryRouter } from 'react-router';
|
||||
import type { VisitsHighlightCardProps } from '../../../src/overview/helpers/VisitsHighlightCard';
|
||||
import { VisitsHighlightCard } from '../../../src/overview/helpers/VisitsHighlightCard';
|
||||
import { renderWithEvents } from '../../__helpers__/setUpTest';
|
||||
|
||||
describe('<VisitsHighlightCard />', () => {
|
||||
const setUp = (props: Partial<VisitsHighlightCardProps> = {}) => renderWithEvents(
|
||||
<VisitsHighlightCard
|
||||
loading={false}
|
||||
visitsSummary={{ total: 0 }}
|
||||
excludeBots={false}
|
||||
title=""
|
||||
link=""
|
||||
{...props}
|
||||
/>,
|
||||
<MemoryRouter>
|
||||
<VisitsHighlightCard
|
||||
loading={false}
|
||||
visitsSummary={{ total: 0 }}
|
||||
excludeBots={false}
|
||||
title=""
|
||||
link=""
|
||||
{...props}
|
||||
/>
|
||||
</MemoryRouter>,
|
||||
);
|
||||
|
||||
it.each([
|
||||
|
|
|
@ -2,6 +2,7 @@ import { render, screen } from '@testing-library/react';
|
|||
import { fromPartial } from '@total-typescript/shoehorn';
|
||||
import { CreateShortUrl as createShortUrlsCreator } from '../../src/short-urls/CreateShortUrl';
|
||||
import type { ShortUrlCreation } from '../../src/short-urls/reducers/shortUrlCreation';
|
||||
import { SettingsProvider } from '../../src/utils/settings';
|
||||
|
||||
describe('<CreateShortUrl />', () => {
|
||||
const ShortUrlForm = () => <span>ShortUrlForm</span>;
|
||||
|
@ -11,13 +12,13 @@ describe('<CreateShortUrl />', () => {
|
|||
const createShortUrl = vi.fn(async () => Promise.resolve());
|
||||
const CreateShortUrl = createShortUrlsCreator(ShortUrlForm, CreateShortUrlResult);
|
||||
const setUp = () => render(
|
||||
<CreateShortUrl
|
||||
shortUrlCreation={shortUrlCreationResult}
|
||||
createShortUrl={createShortUrl}
|
||||
selectedServer={null}
|
||||
resetCreateShortUrl={() => {}}
|
||||
settings={fromPartial({ shortUrlCreation })}
|
||||
/>,
|
||||
<SettingsProvider value={fromPartial({ shortUrlCreation })}>
|
||||
<CreateShortUrl
|
||||
shortUrlCreation={shortUrlCreationResult}
|
||||
createShortUrl={createShortUrl}
|
||||
resetCreateShortUrl={() => {}}
|
||||
/>
|
||||
</SettingsProvider>,
|
||||
);
|
||||
|
||||
it('renders computed initial state', () => {
|
||||
|
|
|
@ -4,20 +4,21 @@ import { MemoryRouter } from 'react-router-dom';
|
|||
import { EditShortUrl as createEditShortUrl } from '../../src/short-urls/EditShortUrl';
|
||||
import type { ShortUrlDetail } from '../../src/short-urls/reducers/shortUrlDetail';
|
||||
import type { ShortUrlEdition } from '../../src/short-urls/reducers/shortUrlEdition';
|
||||
import { SettingsProvider } from '../../src/utils/settings';
|
||||
|
||||
describe('<EditShortUrl />', () => {
|
||||
const shortUrlCreation = { validateUrls: true };
|
||||
const EditShortUrl = createEditShortUrl(() => <span>ShortUrlForm</span>);
|
||||
const setUp = (detail: Partial<ShortUrlDetail> = {}, edition: Partial<ShortUrlEdition> = {}) => render(
|
||||
<MemoryRouter>
|
||||
<EditShortUrl
|
||||
settings={fromPartial({ shortUrlCreation })}
|
||||
selectedServer={null}
|
||||
shortUrlDetail={fromPartial(detail)}
|
||||
shortUrlEdition={fromPartial(edition)}
|
||||
getShortUrlDetail={vi.fn()}
|
||||
editShortUrl={vi.fn(async () => Promise.resolve())}
|
||||
/>
|
||||
<SettingsProvider value={fromPartial({ shortUrlCreation })}>
|
||||
<EditShortUrl
|
||||
shortUrlDetail={fromPartial(detail)}
|
||||
shortUrlEdition={fromPartial(edition)}
|
||||
getShortUrlDetail={vi.fn()}
|
||||
editShortUrl={vi.fn(async () => Promise.resolve())}
|
||||
/>
|
||||
</SettingsProvider>
|
||||
</MemoryRouter>,
|
||||
);
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { render, screen } from '@testing-library/react';
|
||||
import { fromPartial } from '@total-typescript/shoehorn';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
import type { ShlinkPaginator } from '../../src/api/types';
|
||||
import type { ShlinkPaginator } from '../../src/api-contract';
|
||||
import { Paginator } from '../../src/short-urls/Paginator';
|
||||
import { ELLIPSIS } from '../../src/utils/helpers/pagination';
|
||||
|
||||
|
@ -9,7 +9,7 @@ describe('<Paginator />', () => {
|
|||
const buildPaginator = (pagesCount?: number) => fromPartial<ShlinkPaginator>({ pagesCount, currentPage: 1 });
|
||||
const setUp = (paginator?: ShlinkPaginator, currentQueryString?: string) => render(
|
||||
<MemoryRouter>
|
||||
<Paginator serverId="abc123" paginator={paginator} currentQueryString={currentQueryString} />
|
||||
<Paginator paginator={paginator} currentQueryString={currentQueryString} />
|
||||
</MemoryRouter>,
|
||||
);
|
||||
|
||||
|
|
|
@ -2,25 +2,26 @@ import { screen } from '@testing-library/react';
|
|||
import type { UserEvent } from '@testing-library/user-event/setup/setup';
|
||||
import { fromPartial } from '@total-typescript/shoehorn';
|
||||
import { formatISO } from 'date-fns';
|
||||
import type { ReachableServer, SelectedServer } from '../../../src/servers/data';
|
||||
import type { OptionalString } from '../../../src/utils/utils';
|
||||
import type { Mode } from '../../src/short-urls/ShortUrlForm';
|
||||
import { ShortUrlForm as createShortUrlForm } from '../../src/short-urls/ShortUrlForm';
|
||||
import { parseDate } from '../../src/utils/dates/helpers/date';
|
||||
import { FeaturesProvider } from '../../src/utils/features';
|
||||
import { renderWithEvents } from '../__helpers__/setUpTest';
|
||||
|
||||
describe('<ShortUrlForm />', () => {
|
||||
const createShortUrl = vi.fn(async () => Promise.resolve());
|
||||
const ShortUrlForm = createShortUrlForm(() => <span>TagsSelector</span>, () => <span>DomainSelector</span>);
|
||||
const setUp = (selectedServer: SelectedServer = null, mode: Mode = 'create', title?: OptionalString) =>
|
||||
const setUp = (withDeviceLongUrls = false, mode: Mode = 'create', title?: OptionalString) =>
|
||||
renderWithEvents(
|
||||
<ShortUrlForm
|
||||
selectedServer={selectedServer}
|
||||
mode={mode}
|
||||
saving={false}
|
||||
initialState={{ validateUrl: true, findIfExists: false, title, longUrl: '' }}
|
||||
onSave={createShortUrl}
|
||||
/>,
|
||||
<FeaturesProvider value={fromPartial({ deviceLongUrls: withDeviceLongUrls })}>
|
||||
<ShortUrlForm
|
||||
mode={mode}
|
||||
saving={false}
|
||||
initialState={{ validateUrl: true, findIfExists: false, title, longUrl: '' }}
|
||||
onSave={createShortUrl}
|
||||
/>
|
||||
</FeaturesProvider>,
|
||||
);
|
||||
|
||||
it.each([
|
||||
|
@ -29,14 +30,14 @@ describe('<ShortUrlForm />', () => {
|
|||
await user.type(screen.getByPlaceholderText('Custom slug'), 'my-slug');
|
||||
},
|
||||
{ customSlug: 'my-slug' },
|
||||
null,
|
||||
false,
|
||||
],
|
||||
[
|
||||
async (user: UserEvent) => {
|
||||
await user.type(screen.getByPlaceholderText('Short code length'), '15');
|
||||
},
|
||||
{ shortCodeLength: '15' },
|
||||
null,
|
||||
false,
|
||||
],
|
||||
[
|
||||
async (user: UserEvent) => {
|
||||
|
@ -49,10 +50,10 @@ describe('<ShortUrlForm />', () => {
|
|||
ios: 'https://ios.com',
|
||||
},
|
||||
},
|
||||
fromPartial<ReachableServer>({ version: '3.5.0' }),
|
||||
true,
|
||||
],
|
||||
])('saves short URL with data set in form controls', async (extraFields, extraExpectedValues, selectedServer) => {
|
||||
const { user } = setUp(selectedServer);
|
||||
])('saves short URL with data set in form controls', async (extraFields, extraExpectedValues, withDeviceLongUrls) => {
|
||||
const { user } = setUp(withDeviceLongUrls);
|
||||
const validSince = parseDate('2017-01-01', 'yyyy-MM-dd');
|
||||
const validUntil = parseDate('2017-01-06', 'yyyy-MM-dd');
|
||||
|
||||
|
@ -83,7 +84,7 @@ describe('<ShortUrlForm />', () => {
|
|||
])(
|
||||
'renders expected amount of cards based on server capabilities and mode',
|
||||
(mode, expectedAmountOfCards) => {
|
||||
setUp(null, mode);
|
||||
setUp(false, mode);
|
||||
const cards = screen.queryAllByRole('heading');
|
||||
|
||||
expect(cards).toHaveLength(expectedAmountOfCards);
|
||||
|
@ -100,7 +101,7 @@ describe('<ShortUrlForm />', () => {
|
|||
[undefined, false, undefined],
|
||||
['old title', false, null],
|
||||
])('sends expected title based on original and new values', async (originalTitle, withNewTitle, expectedSentTitle) => {
|
||||
const { user } = setUp(fromPartial({ version: '2.6.0' }), 'create', originalTitle);
|
||||
const { user } = setUp(false, 'create', originalTitle);
|
||||
|
||||
await user.type(screen.getByPlaceholderText('URL to be shortened'), 'https://long-domain.com/foo/bar');
|
||||
await user.clear(screen.getByPlaceholderText('Title'));
|
||||
|
@ -114,19 +115,10 @@ describe('<ShortUrlForm />', () => {
|
|||
}));
|
||||
});
|
||||
|
||||
it.each([
|
||||
[fromPartial<ReachableServer>({ version: '3.0.0' }), false],
|
||||
[fromPartial<ReachableServer>({ version: '3.4.0' }), false],
|
||||
[fromPartial<ReachableServer>({ version: '3.5.0' }), true],
|
||||
[fromPartial<ReachableServer>({ version: '3.6.0' }), true],
|
||||
])('shows device-specific long URLs only for servers supporting it', (selectedServer, fieldsExist) => {
|
||||
setUp(selectedServer);
|
||||
const placeholders = ['Android-specific redirection', 'iOS-specific redirection', 'Desktop-specific redirection'];
|
||||
it('shows device-specific long URLs only when supported', () => {
|
||||
setUp(true);
|
||||
|
||||
if (fieldsExist) {
|
||||
placeholders.forEach((placeholder) => expect(screen.getByPlaceholderText(placeholder)).toBeInTheDocument());
|
||||
} else {
|
||||
placeholders.forEach((placeholder) => expect(screen.queryByPlaceholderText(placeholder)).not.toBeInTheDocument());
|
||||
}
|
||||
const placeholders = ['Android-specific redirection', 'iOS-specific redirection', 'Desktop-specific redirection'];
|
||||
placeholders.forEach((placeholder) => expect(screen.getByPlaceholderText(placeholder)).toBeInTheDocument());
|
||||
});
|
||||
});
|
||||
|
|
|
@ -2,10 +2,12 @@ import { screen, waitFor } from '@testing-library/react';
|
|||
import { fromPartial } from '@total-typescript/shoehorn';
|
||||
import { endOfDay, formatISO, startOfDay } from 'date-fns';
|
||||
import { MemoryRouter, useLocation, useNavigate } from 'react-router-dom';
|
||||
import type { ReachableServer, SelectedServer } from '../../../src/servers/data';
|
||||
import { ShortUrlsFilteringBar as filteringBarCreator } from '../../src/short-urls/ShortUrlsFilteringBar';
|
||||
import { formatIsoDate } from '../../src/utils/dates/helpers/date';
|
||||
import type { DateRange } from '../../src/utils/dates/helpers/dateIntervals';
|
||||
import { FeaturesProvider } from '../../src/utils/features';
|
||||
import { RoutesPrefixProvider } from '../../src/utils/routesPrefix';
|
||||
import { SettingsProvider } from '../../src/utils/settings';
|
||||
import { renderWithEvents } from '../__helpers__/setUpTest';
|
||||
|
||||
vi.mock('react-router-dom', async () => ({
|
||||
|
@ -20,18 +22,19 @@ describe('<ShortUrlsFilteringBar />', () => {
|
|||
const navigate = vi.fn();
|
||||
const handleOrderBy = vi.fn();
|
||||
const now = new Date();
|
||||
const setUp = (search = '', selectedServer?: SelectedServer) => {
|
||||
const setUp = (search = '', filterDisabledUrls = true) => {
|
||||
(useLocation as any).mockReturnValue({ search });
|
||||
(useNavigate as any).mockReturnValue(navigate);
|
||||
|
||||
return renderWithEvents(
|
||||
<MemoryRouter>
|
||||
<ShortUrlsFilteringBar
|
||||
selectedServer={selectedServer ?? fromPartial({})}
|
||||
order={{}}
|
||||
handleOrderBy={handleOrderBy}
|
||||
settings={fromPartial({ visits: {} })}
|
||||
/>
|
||||
<SettingsProvider value={fromPartial({ visits: {} })}>
|
||||
<FeaturesProvider value={fromPartial({ filterDisabledUrls })}>
|
||||
<RoutesPrefixProvider value="/server/1">
|
||||
<ShortUrlsFilteringBar order={{}} handleOrderBy={handleOrderBy} />
|
||||
</RoutesPrefixProvider>
|
||||
</FeaturesProvider>
|
||||
</SettingsProvider>
|
||||
</MemoryRouter>,
|
||||
);
|
||||
};
|
||||
|
@ -71,16 +74,14 @@ describe('<ShortUrlsFilteringBar />', () => {
|
|||
});
|
||||
|
||||
it.each([
|
||||
['tags=foo,bar,baz', fromPartial<ReachableServer>({ version: '3.0.0' }), true],
|
||||
['tags=foo,bar', fromPartial<ReachableServer>({ version: '3.1.0' }), true],
|
||||
['tags=foo', fromPartial<ReachableServer>({ version: '3.0.0' }), false],
|
||||
['', fromPartial<ReachableServer>({ version: '3.0.0' }), false],
|
||||
['tags=foo,bar,baz', fromPartial<ReachableServer>({ version: '2.10.0' }), false],
|
||||
['', fromPartial<ReachableServer>({ version: '2.10.0' }), false],
|
||||
{ search: 'tags=foo,bar,baz', shouldHaveComponent: true },
|
||||
{ search: 'tags=foo,bar', shouldHaveComponent: true },
|
||||
{ search: 'tags=foo', shouldHaveComponent: false },
|
||||
{ search: '', shouldHaveComponent: false },
|
||||
])(
|
||||
'renders tags mode toggle if the server supports it and there is more than one tag selected',
|
||||
(search, selectedServer, shouldHaveComponent) => {
|
||||
setUp(search, selectedServer);
|
||||
'renders tags mode toggle if there is more than one tag selected',
|
||||
({ search, shouldHaveComponent }) => {
|
||||
setUp(search);
|
||||
|
||||
if (shouldHaveComponent) {
|
||||
expect(screen.getByLabelText('Change tags mode')).toBeInTheDocument();
|
||||
|
@ -95,7 +96,7 @@ describe('<ShortUrlsFilteringBar />', () => {
|
|||
['&tagsMode=all', 'With all the tags.'],
|
||||
['&tagsMode=any', 'With any of the tags.'],
|
||||
])('expected tags mode tooltip title', async (initialTagsMode, expectedToggleText) => {
|
||||
const { user } = setUp(`tags=foo,bar${initialTagsMode}`, fromPartial({ version: '3.0.0' }));
|
||||
const { user } = setUp(`tags=foo,bar${initialTagsMode}`, true);
|
||||
|
||||
await user.hover(screen.getByLabelText('Change tags mode'));
|
||||
expect(await screen.findByRole('tooltip')).toHaveTextContent(expectedToggleText);
|
||||
|
@ -106,7 +107,7 @@ describe('<ShortUrlsFilteringBar />', () => {
|
|||
['&tagsMode=all', 'tagsMode=any'],
|
||||
['&tagsMode=any', 'tagsMode=all'],
|
||||
])('redirects to first page when tags mode changes', async (initialTagsMode, expectedRedirectTagsMode) => {
|
||||
const { user } = setUp(`tags=foo,bar${initialTagsMode}`, fromPartial({ version: '3.0.0' }));
|
||||
const { user } = setUp(`tags=foo,bar${initialTagsMode}`, true);
|
||||
|
||||
expect(navigate).not.toHaveBeenCalled();
|
||||
await user.click(screen.getByLabelText('Change tags mode'));
|
||||
|
@ -124,7 +125,7 @@ describe('<ShortUrlsFilteringBar />', () => {
|
|||
['excludePastValidUntil=false', /Exclude enabled in the past/, 'excludePastValidUntil=true'],
|
||||
['excludePastValidUntil=true', /Exclude enabled in the past/, 'excludePastValidUntil=false'],
|
||||
])('allows to toggle filters through filtering dropdown', async (search, menuItemName, expectedQuery) => {
|
||||
const { user } = setUp(search, fromPartial({ version: '3.4.0' }));
|
||||
const { user } = setUp(search, true);
|
||||
const toggleFilter = async (name: RegExp) => {
|
||||
await user.click(screen.getByRole('button', { name: 'Filters' }));
|
||||
await waitFor(() => screen.findByRole('menu'));
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
import { screen } from '@testing-library/react';
|
||||
import { fromPartial } from '@total-typescript/shoehorn';
|
||||
import { MemoryRouter, useNavigate } from 'react-router-dom';
|
||||
import type { SemVer } from '../../../src/utils/helpers/version';
|
||||
import type { Settings } from '../../src';
|
||||
import type { MercureBoundProps } from '../../src/mercure/helpers/boundToMercureHub';
|
||||
import type { ShortUrlsOrder } from '../../src/short-urls/data';
|
||||
import type { ShortUrlsList as ShortUrlsListModel } from '../../src/short-urls/reducers/shortUrlsList';
|
||||
import { ShortUrlsList as createShortUrlsList } from '../../src/short-urls/ShortUrlsList';
|
||||
import type { ShortUrlsTableType } from '../../src/short-urls/ShortUrlsTable';
|
||||
import { FeaturesProvider } from '../../src/utils/features';
|
||||
import { SettingsProvider } from '../../src/utils/settings';
|
||||
import { renderWithEvents } from '../__helpers__/setUpTest';
|
||||
|
||||
vi.mock('react-router-dom', async () => ({
|
||||
|
@ -35,15 +36,17 @@ describe('<ShortUrlsList />', () => {
|
|||
},
|
||||
});
|
||||
const ShortUrlsList = createShortUrlsList(ShortUrlsTable, ShortUrlsFilteringBar);
|
||||
const setUp = (settings: Partial<Settings> = {}, version: SemVer = '3.0.0') => renderWithEvents(
|
||||
const setUp = (settings: Partial<Settings> = {}, excludeBotsOnShortUrls = true) => renderWithEvents(
|
||||
<MemoryRouter>
|
||||
<ShortUrlsList
|
||||
{...fromPartial<MercureBoundProps>({ mercureInfo: { loading: true } })}
|
||||
listShortUrls={listShortUrlsMock}
|
||||
shortUrlsList={shortUrlsList}
|
||||
selectedServer={fromPartial({ id: '1', version })}
|
||||
settings={fromPartial(settings)}
|
||||
/>
|
||||
<SettingsProvider value={fromPartial(settings)}>
|
||||
<FeaturesProvider value={fromPartial({ excludeBotsOnShortUrls })}>
|
||||
<ShortUrlsList
|
||||
{...fromPartial<MercureBoundProps>({ mercureInfo: { loading: true } })}
|
||||
listShortUrls={listShortUrlsMock}
|
||||
shortUrlsList={shortUrlsList}
|
||||
/>
|
||||
</FeaturesProvider>
|
||||
</SettingsProvider>
|
||||
</MemoryRouter>,
|
||||
);
|
||||
|
||||
|
@ -93,26 +96,26 @@ describe('<ShortUrlsList />', () => {
|
|||
shortUrlsList: {
|
||||
defaultOrdering: { field: 'visits', dir: 'ASC' },
|
||||
},
|
||||
}), '3.3.0' as SemVer, { field: 'visits', dir: 'ASC' }],
|
||||
}), false, { field: 'visits', dir: 'ASC' }],
|
||||
[fromPartial<Settings>({
|
||||
shortUrlsList: {
|
||||
defaultOrdering: { field: 'visits', dir: 'ASC' },
|
||||
},
|
||||
visits: { excludeBots: true },
|
||||
}), '3.3.0' as SemVer, { field: 'visits', dir: 'ASC' }],
|
||||
}), false, { field: 'visits', dir: 'ASC' }],
|
||||
[fromPartial<Settings>({
|
||||
shortUrlsList: {
|
||||
defaultOrdering: { field: 'visits', dir: 'ASC' },
|
||||
},
|
||||
}), '3.4.0' as SemVer, { field: 'visits', dir: 'ASC' }],
|
||||
}), true, { field: 'visits', dir: 'ASC' }],
|
||||
[fromPartial<Settings>({
|
||||
shortUrlsList: {
|
||||
defaultOrdering: { field: 'visits', dir: 'ASC' },
|
||||
},
|
||||
visits: { excludeBots: true },
|
||||
}), '3.4.0' as SemVer, { field: 'nonBotVisits', dir: 'ASC' }],
|
||||
])('parses order by based on server version and config', (settings, serverVersion, expectedOrderBy) => {
|
||||
setUp(settings, serverVersion);
|
||||
}), true, { field: 'nonBotVisits', dir: 'ASC' }],
|
||||
])('parses order by based on supported features version and config', (settings, excludeBotsOnShortUrls, expectedOrderBy) => {
|
||||
setUp(settings, excludeBotsOnShortUrls);
|
||||
expect(listShortUrlsMock).toHaveBeenCalledWith(expect.objectContaining({ orderBy: expectedOrderBy }));
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import { fireEvent, screen } from '@testing-library/react';
|
||||
import { fromPartial } from '@total-typescript/shoehorn';
|
||||
import type { SelectedServer } from '../../../src/servers/data';
|
||||
import type { ShortUrlsOrderableFields } from '../../src/short-urls/data';
|
||||
import { SHORT_URLS_ORDERABLE_FIELDS } from '../../src/short-urls/data';
|
||||
import type { ShortUrlsList } from '../../src/short-urls/reducers/shortUrlsList';
|
||||
|
@ -11,8 +10,8 @@ describe('<ShortUrlsTable />', () => {
|
|||
const shortUrlsList = fromPartial<ShortUrlsList>({});
|
||||
const orderByColumn = vi.fn();
|
||||
const ShortUrlsTable = shortUrlsTableCreator(() => <span>ShortUrlsRow</span>);
|
||||
const setUp = (server: SelectedServer = null) => renderWithEvents(
|
||||
<ShortUrlsTable shortUrlsList={shortUrlsList} selectedServer={server} orderByColumn={() => orderByColumn} />,
|
||||
const setUp = () => renderWithEvents(
|
||||
<ShortUrlsTable shortUrlsList={shortUrlsList} orderByColumn={() => orderByColumn} />,
|
||||
);
|
||||
|
||||
it('should render inner table by default', () => {
|
||||
|
@ -54,7 +53,7 @@ describe('<ShortUrlsTable />', () => {
|
|||
});
|
||||
|
||||
it('should render composed title column', () => {
|
||||
setUp(fromPartial({ version: '2.0.0' }));
|
||||
setUp();
|
||||
|
||||
const { innerHTML } = screen.getAllByRole('columnheader')[2];
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import { screen } from '@testing-library/react';
|
||||
import { fromPartial } from '@total-typescript/shoehorn';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
import type { NotFoundServer, SelectedServer } from '../../../../src/servers/data';
|
||||
import type { ShortUrl } from '../../../src/short-urls/data';
|
||||
import { ExportShortUrlsBtn as createExportShortUrlsBtn } from '../../../src/short-urls/helpers/ExportShortUrlsBtn';
|
||||
import type { ReportExporter } from '../../../src/utils/services/ReportExporter';
|
||||
|
@ -13,9 +12,9 @@ describe('<ExportShortUrlsBtn />', () => {
|
|||
const exportShortUrls = vi.fn();
|
||||
const reportExporter = fromPartial<ReportExporter>({ exportShortUrls });
|
||||
const ExportShortUrlsBtn = createExportShortUrlsBtn(buildShlinkApiClient, reportExporter);
|
||||
const setUp = (amount?: number, selectedServer?: SelectedServer) => renderWithEvents(
|
||||
const setUp = (amount?: number) => renderWithEvents(
|
||||
<MemoryRouter>
|
||||
<ExportShortUrlsBtn selectedServer={selectedServer ?? fromPartial({})} amount={amount} />
|
||||
<ExportShortUrlsBtn amount={amount} />
|
||||
</MemoryRouter>,
|
||||
);
|
||||
|
||||
|
@ -28,17 +27,6 @@ describe('<ExportShortUrlsBtn />', () => {
|
|||
expect(screen.getByText(/Export/)).toHaveTextContent(`Export (${expectedAmount})`);
|
||||
});
|
||||
|
||||
it.each([
|
||||
[null],
|
||||
[fromPartial<NotFoundServer>({})],
|
||||
])('does nothing on click if selected server is not reachable', async (selectedServer) => {
|
||||
const { user } = setUp(0, selectedServer);
|
||||
|
||||
await user.click(screen.getByRole('button'));
|
||||
expect(listShortUrls).not.toHaveBeenCalled();
|
||||
expect(exportShortUrls).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it.each([
|
||||
[10, 1],
|
||||
[30, 2],
|
||||
|
@ -48,7 +36,7 @@ describe('<ExportShortUrlsBtn />', () => {
|
|||
[385, 20],
|
||||
])('loads proper amount of pages based on the amount of results', async (amount, expectedPageLoads) => {
|
||||
listShortUrls.mockResolvedValue({ data: [] });
|
||||
const { user } = setUp(amount, fromPartial({ id: '123' }));
|
||||
const { user } = setUp(amount);
|
||||
|
||||
await user.click(screen.getByRole('button'));
|
||||
|
||||
|
@ -63,7 +51,7 @@ describe('<ExportShortUrlsBtn />', () => {
|
|||
tags: [],
|
||||
})],
|
||||
});
|
||||
const { user } = setUp(undefined, fromPartial({ id: '123' }));
|
||||
const { user } = setUp();
|
||||
|
||||
await user.click(screen.getByRole('button'));
|
||||
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import { fireEvent, screen } from '@testing-library/react';
|
||||
import { fromPartial } from '@total-typescript/shoehorn';
|
||||
import type { SemVer } from '../../../../src/utils/helpers/version';
|
||||
import { QrCodeModal as createQrCodeModal } from '../../../src/short-urls/helpers/QrCodeModal';
|
||||
import { renderWithEvents } from '../../__helpers__/setUpTest';
|
||||
|
||||
|
@ -8,11 +7,10 @@ describe('<QrCodeModal />', () => {
|
|||
const saveImage = vi.fn().mockReturnValue(Promise.resolve());
|
||||
const QrCodeModal = createQrCodeModal(fromPartial({ saveImage }));
|
||||
const shortUrl = 'https://s.test/abc123';
|
||||
const setUp = (version: SemVer = '2.8.0') => renderWithEvents(
|
||||
const setUp = () => renderWithEvents(
|
||||
<QrCodeModal
|
||||
isOpen
|
||||
shortUrl={fromPartial({ shortUrl })}
|
||||
selectedServer={fromPartial({ version })}
|
||||
toggle={() => {}}
|
||||
/>,
|
||||
);
|
||||
|
@ -63,16 +61,14 @@ describe('<QrCodeModal />', () => {
|
|||
});
|
||||
|
||||
it('shows expected components based on server version', () => {
|
||||
const { container } = setUp();
|
||||
setUp();
|
||||
const dropdowns = screen.getAllByRole('button');
|
||||
const firstCol = container.parentNode?.querySelectorAll('.d-grid').item(0);
|
||||
|
||||
expect(dropdowns).toHaveLength(2 + 1); // Add one because of the close button
|
||||
expect(firstCol).toHaveClass('col-md-4');
|
||||
expect(dropdowns).toHaveLength(2 + 2); // Add two because of the close and download buttons
|
||||
});
|
||||
|
||||
it('saves the QR code image when clicking the Download button', async () => {
|
||||
const { user } = setUp('2.9.0');
|
||||
const { user } = setUp();
|
||||
|
||||
expect(saveImage).not.toHaveBeenCalled();
|
||||
await user.click(screen.getByRole('button', { name: /^Download/ }));
|
||||
|
|
|
@ -1,23 +1,22 @@
|
|||
import { render, screen } from '@testing-library/react';
|
||||
import { fromPartial } from '@total-typescript/shoehorn';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
import type { NotFoundServer, ReachableServer } from '../../../../src/servers/data';
|
||||
import type { ShortUrl } from '../../../src/short-urls/data';
|
||||
import type { LinkSuffix } from '../../../src/short-urls/helpers/ShortUrlDetailLink';
|
||||
import { ShortUrlDetailLink } from '../../../src/short-urls/helpers/ShortUrlDetailLink';
|
||||
import { RoutesPrefixProvider } from '../../../src/utils/routesPrefix';
|
||||
|
||||
describe('<ShortUrlDetailLink />', () => {
|
||||
it.each([
|
||||
[undefined, undefined],
|
||||
[null, null],
|
||||
[fromPartial<ReachableServer>({ id: '1' }), null],
|
||||
[fromPartial<ReachableServer>({ id: '1' }), undefined],
|
||||
[fromPartial<NotFoundServer>({}), fromPartial<ShortUrl>({})],
|
||||
[null, fromPartial<ShortUrl>({})],
|
||||
[undefined, fromPartial<ShortUrl>({})],
|
||||
])('only renders a plain span when either server or short URL are not set', (selectedServer, shortUrl) => {
|
||||
[false, undefined],
|
||||
[false, null],
|
||||
[true, null],
|
||||
[true, undefined],
|
||||
[false, fromPartial<ShortUrl>({})],
|
||||
[false, fromPartial<ShortUrl>({})],
|
||||
])('only renders a plain span when either server or short URL are not set', (asLink, shortUrl) => {
|
||||
render(
|
||||
<ShortUrlDetailLink selectedServer={selectedServer} shortUrl={shortUrl} suffix="visits">
|
||||
<ShortUrlDetailLink shortUrl={shortUrl} asLink={asLink} suffix="visits">
|
||||
Something
|
||||
</ShortUrlDetailLink>,
|
||||
);
|
||||
|
@ -28,35 +27,37 @@ describe('<ShortUrlDetailLink />', () => {
|
|||
|
||||
it.each([
|
||||
[
|
||||
fromPartial<ReachableServer>({ id: '1' }),
|
||||
'/server/1',
|
||||
fromPartial<ShortUrl>({ shortCode: 'abc123' }),
|
||||
'visits' as LinkSuffix,
|
||||
'/server/1/short-code/abc123/visits',
|
||||
],
|
||||
[
|
||||
fromPartial<ReachableServer>({ id: '3' }),
|
||||
'/foobar',
|
||||
fromPartial<ShortUrl>({ shortCode: 'def456', domain: 'example.com' }),
|
||||
'visits' as LinkSuffix,
|
||||
'/server/3/short-code/def456/visits?domain=example.com',
|
||||
'/foobar/short-code/def456/visits?domain=example.com',
|
||||
],
|
||||
[
|
||||
fromPartial<ReachableServer>({ id: '1' }),
|
||||
'/server/1',
|
||||
fromPartial<ShortUrl>({ shortCode: 'abc123' }),
|
||||
'edit' as LinkSuffix,
|
||||
'/server/1/short-code/abc123/edit',
|
||||
],
|
||||
[
|
||||
fromPartial<ReachableServer>({ id: '3' }),
|
||||
'/server/3',
|
||||
fromPartial<ShortUrl>({ shortCode: 'def456', domain: 'example.com' }),
|
||||
'edit' as LinkSuffix,
|
||||
'/server/3/short-code/def456/edit?domain=example.com',
|
||||
],
|
||||
])('renders link with expected query when', (selectedServer, shortUrl, suffix, expectedLink) => {
|
||||
])('renders link with expected query when', (routesPrefix, shortUrl, suffix, expectedLink) => {
|
||||
render(
|
||||
<MemoryRouter>
|
||||
<ShortUrlDetailLink selectedServer={selectedServer} shortUrl={shortUrl} suffix={suffix}>
|
||||
Something
|
||||
</ShortUrlDetailLink>
|
||||
<RoutesPrefixProvider value={routesPrefix}>
|
||||
<ShortUrlDetailLink shortUrl={shortUrl} suffix={suffix} asLink>
|
||||
Something
|
||||
</ShortUrlDetailLink>
|
||||
</RoutesPrefixProvider>
|
||||
</MemoryRouter>,
|
||||
);
|
||||
expect(screen.getByRole('link')).toHaveProperty('href', expect.stringContaining(expectedLink));
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { render, screen, waitFor } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import { fromPartial } from '@total-typescript/shoehorn';
|
||||
import type { ShlinkVisitsSummary } from '../../../src/api/types';
|
||||
import type { ShlinkVisitsSummary } from '../../../src/api-contract';
|
||||
import type { ShortUrl, ShortUrlMeta } from '../../../src/short-urls/data';
|
||||
import { ShortUrlStatus } from '../../../src/short-urls/helpers/ShortUrlStatus';
|
||||
|
||||
|
|
|
@ -3,18 +3,17 @@ import { fromPartial } from '@total-typescript/shoehorn';
|
|||
import { addDays, formatISO, subDays } from 'date-fns';
|
||||
import { last } from 'ramda';
|
||||
import { MemoryRouter, useLocation } from 'react-router-dom';
|
||||
import type { ReachableServer } from '../../../../src/servers/data';
|
||||
import type { TimeoutToggle } from '../../../../src/utils/helpers/hooks';
|
||||
import type { OptionalString } from '../../../../src/utils/utils';
|
||||
import type { Settings } from '../../../src';
|
||||
import type { ShortUrl, ShortUrlMeta } from '../../../src/short-urls/data';
|
||||
import { ShortUrlsRow as createShortUrlsRow } from '../../../src/short-urls/helpers/ShortUrlsRow';
|
||||
import { now, parseDate } from '../../../src/utils/dates/helpers/date';
|
||||
import type { TimeoutToggle } from '../../../src/utils/helpers/hooks';
|
||||
import { SettingsProvider } from '../../../src/utils/settings';
|
||||
import { renderWithEvents } from '../../__helpers__/setUpTest';
|
||||
import { colorGeneratorMock } from '../../utils/services/__mocks__/ColorGenerator.mock';
|
||||
|
||||
interface SetUpOptions {
|
||||
title?: OptionalString;
|
||||
title?: string | null;
|
||||
tags?: string[];
|
||||
meta?: ShortUrlMeta;
|
||||
settings?: Partial<Settings>;
|
||||
|
@ -28,7 +27,6 @@ vi.mock('react-router-dom', async () => ({
|
|||
describe('<ShortUrlsRow />', () => {
|
||||
const timeoutToggle = vi.fn(() => true);
|
||||
const useTimeoutToggle = vi.fn(() => [false, timeoutToggle]) as TimeoutToggle;
|
||||
const server = fromPartial<ReachableServer>({ url: 'https://s.test' });
|
||||
const shortUrl: ShortUrl = {
|
||||
shortCode: 'abc123',
|
||||
shortUrl: 'https://s.test/abc123',
|
||||
|
@ -54,16 +52,16 @@ describe('<ShortUrlsRow />', () => {
|
|||
(useLocation as any).mockReturnValue({ search });
|
||||
return renderWithEvents(
|
||||
<MemoryRouter>
|
||||
<table>
|
||||
<tbody>
|
||||
<ShortUrlsRow
|
||||
selectedServer={server}
|
||||
shortUrl={{ ...shortUrl, title, tags, meta: { ...shortUrl.meta, ...meta } }}
|
||||
onTagClick={() => null}
|
||||
settings={fromPartial(settings)}
|
||||
/>
|
||||
</tbody>
|
||||
</table>
|
||||
<SettingsProvider value={fromPartial(settings)}>
|
||||
<table>
|
||||
<tbody>
|
||||
<ShortUrlsRow
|
||||
shortUrl={{ ...shortUrl, title, tags, meta: { ...shortUrl.meta, ...meta } }}
|
||||
onTagClick={() => null}
|
||||
/>
|
||||
</tbody>
|
||||
</table>
|
||||
</SettingsProvider>
|
||||
</MemoryRouter>,
|
||||
);
|
||||
};
|
||||
|
|
|
@ -1,21 +1,19 @@
|
|||
import { screen } from '@testing-library/react';
|
||||
import { fromPartial } from '@total-typescript/shoehorn';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
import type { ReachableServer } from '../../../../src/servers/data';
|
||||
import type { ShortUrl } from '../../../src/short-urls/data';
|
||||
import { ShortUrlsRowMenu as createShortUrlsRowMenu } from '../../../src/short-urls/helpers/ShortUrlsRowMenu';
|
||||
import { renderWithEvents } from '../../__helpers__/setUpTest';
|
||||
|
||||
describe('<ShortUrlsRowMenu />', () => {
|
||||
const ShortUrlsRowMenu = createShortUrlsRowMenu(() => <i>DeleteShortUrlModal</i>, () => <i>QrCodeModal</i>);
|
||||
const selectedServer = fromPartial<ReachableServer>({ id: 'abc123' });
|
||||
const shortUrl = fromPartial<ShortUrl>({
|
||||
shortCode: 'abc123',
|
||||
shortUrl: 'https://s.test/abc123',
|
||||
});
|
||||
const setUp = () => renderWithEvents(
|
||||
<MemoryRouter>
|
||||
<ShortUrlsRowMenu selectedServer={selectedServer} shortUrl={shortUrl} />
|
||||
<ShortUrlsRowMenu shortUrl={shortUrl} />
|
||||
</MemoryRouter>,
|
||||
);
|
||||
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import { fromPartial } from '@total-typescript/shoehorn';
|
||||
import type { ShlinkApiClient } from '../../../../src/api/services/ShlinkApiClient';
|
||||
import type { ShlinkState } from '../../../../src/container/types';
|
||||
import type { ShlinkApiClient } from '../../../src/api-contract';
|
||||
import type { ShortUrl } from '../../../src/short-urls/data';
|
||||
import {
|
||||
createShortUrl as createShortUrlCreator,
|
||||
|
@ -51,11 +50,10 @@ describe('shortUrlCreationReducer', () => {
|
|||
|
||||
describe('createShortUrl', () => {
|
||||
const dispatch = vi.fn();
|
||||
const getState = () => fromPartial<ShlinkState>({});
|
||||
|
||||
it('calls API on success', async () => {
|
||||
createShortUrlCall.mockResolvedValue(shortUrl);
|
||||
await createShortUrl({ longUrl: 'foo' })(dispatch, getState, {});
|
||||
await createShortUrl({ longUrl: 'foo' })(dispatch, vi.fn(), {});
|
||||
|
||||
expect(createShortUrlCall).toHaveBeenCalledTimes(1);
|
||||
expect(dispatch).toHaveBeenCalledTimes(2);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { fromPartial } from '@total-typescript/shoehorn';
|
||||
import type { ShlinkApiClient } from '../../../../src/api/services/ShlinkApiClient';
|
||||
import type { ProblemDetailsError } from '../../../src/api/types/errors';
|
||||
import type { ProblemDetailsError } from '../../../src/api-contract';
|
||||
import {
|
||||
deleteShortUrl as deleteShortUrlCreator,
|
||||
shortUrlDeletionReducerCreator,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { fromPartial } from '@total-typescript/shoehorn';
|
||||
import type { ShlinkApiClient } from '../../../../src/api/services/ShlinkApiClient';
|
||||
import type { ShlinkState } from '../../../../src/container/types';
|
||||
import type { ShlinkApiClient } from '../../../src/api-contract';
|
||||
import type { RootState } from '../../../src/container/store';
|
||||
import type { ShortUrl } from '../../../src/short-urls/data';
|
||||
import { shortUrlDetailReducerCreator } from '../../../src/short-urls/reducers/shortUrlDetail';
|
||||
import type { ShortUrlsList } from '../../../src/short-urls/reducers/shortUrlsList';
|
||||
|
@ -40,7 +40,7 @@ describe('shortUrlDetailReducer', () => {
|
|||
|
||||
describe('getShortUrlDetail', () => {
|
||||
const dispatchMock = vi.fn();
|
||||
const buildGetState = (shortUrlsList?: ShortUrlsList) => () => fromPartial<ShlinkState>({ shortUrlsList });
|
||||
const buildGetState = (shortUrlsList?: ShortUrlsList) => () => fromPartial<RootState>({ shortUrlsList });
|
||||
|
||||
it.each([
|
||||
[undefined],
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
import { fromPartial } from '@total-typescript/shoehorn';
|
||||
import type { ShlinkState } from '../../../../src/container/types';
|
||||
import type { SelectedServer } from '../../../../src/servers/data';
|
||||
import type { ShortUrl } from '../../../src/short-urls/data';
|
||||
import {
|
||||
editShortUrl as editShortUrlCreator,
|
||||
|
@ -45,12 +43,9 @@ describe('shortUrlEditionReducer', () => {
|
|||
|
||||
describe('editShortUrl', () => {
|
||||
const dispatch = vi.fn();
|
||||
const createGetState = (selectedServer: SelectedServer = null) => () => fromPartial<ShlinkState>({
|
||||
selectedServer,
|
||||
});
|
||||
|
||||
it.each([[undefined], [null], ['example.com']])('dispatches short URL on success', async (domain) => {
|
||||
await editShortUrl({ shortCode, domain, data: { longUrl } })(dispatch, createGetState(), {});
|
||||
await editShortUrl({ shortCode, domain, data: { longUrl } })(dispatch, vi.fn(), {});
|
||||
|
||||
expect(buildShlinkApiClient).toHaveBeenCalledTimes(1);
|
||||
expect(updateShortUrl).toHaveBeenCalledTimes(1);
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import { fromPartial } from '@total-typescript/shoehorn';
|
||||
import type { ShlinkApiClient } from '../../../../src/api/services/ShlinkApiClient';
|
||||
import type { ShlinkShortUrlsResponse } from '../../../src/api/types';
|
||||
import type { ShlinkApiClient, ShlinkShortUrlsResponse } from '../../../src/api-contract';
|
||||
import type { ShortUrl } from '../../../src/short-urls/data';
|
||||
import { createShortUrl as createShortUrlCreator } from '../../../src/short-urls/reducers/shortUrlCreation';
|
||||
import { shortUrlDeleted } from '../../../src/short-urls/reducers/shortUrlDeletion';
|
||||
|
@ -187,7 +186,7 @@ describe('shortUrlsListReducer', () => {
|
|||
|
||||
describe('listShortUrls', () => {
|
||||
const dispatch = vi.fn();
|
||||
const getState = vi.fn().mockReturnValue({ selectedServer: {} });
|
||||
const getState = vi.fn();
|
||||
|
||||
it('dispatches proper actions if API client request succeeds', async () => {
|
||||
listShortUrlsMock.mockResolvedValue({});
|
||||
|
|
|
@ -5,20 +5,22 @@ import type { MercureBoundProps } from '../../src/mercure/helpers/boundToMercure
|
|||
import type { TagsList } from '../../src/tags/reducers/tagsList';
|
||||
import type { TagsListProps } from '../../src/tags/TagsList';
|
||||
import { TagsList as createTagsList } from '../../src/tags/TagsList';
|
||||
import { SettingsProvider } from '../../src/utils/settings';
|
||||
import { renderWithEvents } from '../__helpers__/setUpTest';
|
||||
|
||||
describe('<TagsList />', () => {
|
||||
const filterTags = vi.fn();
|
||||
const TagsListComp = createTagsList(({ sortedTags }) => <>TagsTable ({sortedTags.map((t) => t.visits).join(',')})</>);
|
||||
const setUp = (tagsList: Partial<TagsList>, excludeBots = false) => renderWithEvents(
|
||||
<TagsListComp
|
||||
{...fromPartial<TagsListProps>({})}
|
||||
{...fromPartial<MercureBoundProps>({ mercureInfo: {} })}
|
||||
forceListTags={identity}
|
||||
filterTags={filterTags}
|
||||
tagsList={fromPartial(tagsList)}
|
||||
settings={fromPartial({ visits: { excludeBots } })}
|
||||
/>,
|
||||
<SettingsProvider value={fromPartial({ visits: { excludeBots } })}>
|
||||
<TagsListComp
|
||||
{...fromPartial<TagsListProps>({})}
|
||||
{...fromPartial<MercureBoundProps>({ mercureInfo: {} })}
|
||||
forceListTags={identity}
|
||||
filterTags={filterTags}
|
||||
tagsList={fromPartial(tagsList)}
|
||||
/>
|
||||
</SettingsProvider>,
|
||||
);
|
||||
|
||||
it('shows a loading message when tags are being loaded', () => {
|
||||
|
|
|
@ -19,7 +19,6 @@ describe('<TagsTable />', () => {
|
|||
return renderWithEvents(
|
||||
<TagsTable
|
||||
sortedTags={sortedTags.map((tag) => fromPartial({ tag }))}
|
||||
selectedServer={fromPartial({})}
|
||||
currentOrder={{}}
|
||||
orderByColumn={() => orderByColumn}
|
||||
/>,
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { screen } from '@testing-library/react';
|
||||
import { fromPartial } from '@total-typescript/shoehorn';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
import { TagsTableRow as createTagsTableRow } from '../../src/tags/TagsTableRow';
|
||||
import { RoutesPrefixProvider } from '../../src/utils/routesPrefix';
|
||||
import { renderWithEvents } from '../__helpers__/setUpTest';
|
||||
import { colorGeneratorMock } from '../utils/services/__mocks__/ColorGenerator.mock';
|
||||
|
||||
|
@ -13,14 +13,15 @@ describe('<TagsTableRow />', () => {
|
|||
);
|
||||
const setUp = (tagStats?: { visits?: number; shortUrls?: number }) => renderWithEvents(
|
||||
<MemoryRouter>
|
||||
<table>
|
||||
<tbody>
|
||||
<TagsTableRow
|
||||
tag={{ tag: 'foo&bar', visits: tagStats?.visits ?? 0, shortUrls: tagStats?.shortUrls ?? 0 }}
|
||||
selectedServer={fromPartial({ id: 'abc123' })}
|
||||
/>
|
||||
</tbody>
|
||||
</table>
|
||||
<RoutesPrefixProvider value="/server/abc123">
|
||||
<table>
|
||||
<tbody>
|
||||
<TagsTableRow
|
||||
tag={{ tag: 'foo&bar', visits: tagStats?.visits ?? 0, shortUrls: tagStats?.shortUrls ?? 0 }}
|
||||
/>
|
||||
</tbody>
|
||||
</table>
|
||||
</RoutesPrefixProvider>
|
||||
</MemoryRouter>,
|
||||
);
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@ import { screen } from '@testing-library/react';
|
|||
import { fromPartial } from '@total-typescript/shoehorn';
|
||||
import { TagsSelector as createTagsSelector } from '../../../src/tags/helpers/TagsSelector';
|
||||
import type { TagsList } from '../../../src/tags/reducers/tagsList';
|
||||
import { SettingsProvider } from '../../../src/utils/settings';
|
||||
import { renderWithEvents } from '../../__helpers__/setUpTest';
|
||||
import { colorGeneratorMock } from '../../utils/services/__mocks__/ColorGenerator.mock';
|
||||
|
||||
|
@ -11,13 +12,14 @@ describe('<TagsSelector />', () => {
|
|||
const tags = ['foo', 'bar'];
|
||||
const tagsList = fromPartial<TagsList>({ tags: [...tags, 'baz'] });
|
||||
const setUp = () => renderWithEvents(
|
||||
<TagsSelector
|
||||
selectedTags={tags}
|
||||
tagsList={tagsList}
|
||||
settings={fromPartial({})}
|
||||
listTags={vi.fn()}
|
||||
onChange={onChange}
|
||||
/>,
|
||||
<SettingsProvider value={fromPartial({})}>
|
||||
<TagsSelector
|
||||
selectedTags={tags}
|
||||
tagsList={tagsList}
|
||||
listTags={vi.fn()}
|
||||
onChange={onChange}
|
||||
/>
|
||||
</SettingsProvider>,
|
||||
);
|
||||
|
||||
it('has an input for tags', () => {
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import { fromPartial } from '@total-typescript/shoehorn';
|
||||
import type { ShlinkApiClient } from '../../../../src/api/services/ShlinkApiClient';
|
||||
import type { ShlinkState } from '../../../../src/container/types';
|
||||
import type { ShlinkApiClient } from '../../../src/api-contract';
|
||||
import { tagDeleted, tagDeleteReducerCreator } from '../../../src/tags/reducers/tagDelete';
|
||||
|
||||
describe('tagDeleteReducer', () => {
|
||||
|
@ -42,13 +41,12 @@ describe('tagDeleteReducer', () => {
|
|||
|
||||
describe('deleteTag', () => {
|
||||
const dispatch = vi.fn();
|
||||
const getState = () => fromPartial<ShlinkState>({});
|
||||
|
||||
it('calls API on success', async () => {
|
||||
const tag = 'foo';
|
||||
deleteTagsCall.mockResolvedValue(undefined);
|
||||
|
||||
await deleteTag(tag)(dispatch, getState, {});
|
||||
await deleteTag(tag)(dispatch, vi.fn(), {});
|
||||
|
||||
expect(deleteTagsCall).toHaveBeenCalledTimes(1);
|
||||
expect(deleteTagsCall).toHaveBeenNthCalledWith(1, [tag]);
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import { fromPartial } from '@total-typescript/shoehorn';
|
||||
import type { ShlinkApiClient } from '../../../../src/api/services/ShlinkApiClient';
|
||||
import type { ShlinkState } from '../../../../src/container/types';
|
||||
import type { ShlinkApiClient } from '../../../src/api-contract';
|
||||
import { editTag as editTagCreator, tagEdited, tagEditReducerCreator } from '../../../src/tags/reducers/tagEdit';
|
||||
import type { ColorGenerator } from '../../../src/utils/services/ColorGenerator';
|
||||
|
||||
|
@ -51,12 +50,11 @@ describe('tagEditReducer', () => {
|
|||
|
||||
describe('editTag', () => {
|
||||
const dispatch = vi.fn();
|
||||
const getState = () => fromPartial<ShlinkState>({});
|
||||
|
||||
it('calls API on success', async () => {
|
||||
editTagCall.mockResolvedValue(undefined);
|
||||
|
||||
await editTag({ oldName, newName, color })(dispatch, getState, {});
|
||||
await editTag({ oldName, newName, color })(dispatch, vi.fn(), {});
|
||||
|
||||
expect(editTagCall).toHaveBeenCalledTimes(1);
|
||||
expect(editTagCall).toHaveBeenCalledWith(oldName, newName);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { fromPartial } from '@total-typescript/shoehorn';
|
||||
import type { ShlinkState } from '../../../../src/container/types';
|
||||
import type { RootState } from '../../../src/container/store';
|
||||
import type { ShortUrl } from '../../../src/short-urls/data';
|
||||
import { createShortUrl as createShortUrlCreator } from '../../../src/short-urls/reducers/shortUrlCreation';
|
||||
import { tagDeleted } from '../../../src/tags/reducers/tagDelete';
|
||||
|
@ -195,11 +195,11 @@ describe('tagsListReducer', () => {
|
|||
|
||||
describe('listTags', () => {
|
||||
const dispatch = vi.fn();
|
||||
const getState = vi.fn(() => fromPartial<ShlinkState>({}));
|
||||
const getState = vi.fn(() => fromPartial<RootState>({}));
|
||||
const listTagsMock = vi.fn();
|
||||
|
||||
const assertNoAction = async (tagsList: TagsList) => {
|
||||
getState.mockReturnValue(fromPartial<ShlinkState>({ tagsList }));
|
||||
getState.mockReturnValue(fromPartial<RootState>({ tagsList }));
|
||||
|
||||
await listTagsCreator(buildShlinkApiClient, false)()(dispatch, getState, {});
|
||||
|
||||
|
@ -218,7 +218,7 @@ describe('tagsListReducer', () => {
|
|||
const tags = ['foo', 'bar', 'baz'];
|
||||
|
||||
listTagsMock.mockResolvedValue({ tags, stats: [] });
|
||||
buildShlinkApiClient.mockReturnValue({ listTags: listTagsMock });
|
||||
buildShlinkApiClient.mockReturnValue({ tagsStats: listTagsMock });
|
||||
|
||||
await listTags()(dispatch, getState, {});
|
||||
|
||||
|
|
|
@ -1,23 +1,9 @@
|
|||
import { addDays, formatISO, subDays } from 'date-fns';
|
||||
import { formatDate, formatIsoDate, isBeforeOrEqual, isBetween, parseDate } from '../../../../src/utils/dates/helpers/date';
|
||||
import { formatIsoDate, isBeforeOrEqual, isBetween, parseDate } from '../../../../src/utils/dates/helpers/date';
|
||||
|
||||
describe('date', () => {
|
||||
const now = new Date();
|
||||
|
||||
describe('formatDate', () => {
|
||||
it.each([
|
||||
[parseDate('2020-03-05 10:00:10', 'yyyy-MM-dd HH:mm:ss'), 'dd/MM/yyyy', '05/03/2020'],
|
||||
[parseDate('2020-03-05 10:00:10', 'yyyy-MM-dd HH:mm:ss'), 'yyyy-MM', '2020-03'],
|
||||
[parseDate('2020-03-05 10:00:10', 'yyyy-MM-dd HH:mm:ss'), undefined, '2020-03-05'],
|
||||
['2020-03-05 10:00:10', 'dd-MM-yyyy', '2020-03-05 10:00:10'],
|
||||
['2020-03-05 10:00:10', undefined, '2020-03-05 10:00:10'],
|
||||
[undefined, undefined, undefined],
|
||||
[null, undefined, null],
|
||||
])('formats date as expected', (date, format, expected) => {
|
||||
expect(formatDate(format)(date)).toEqual(expected);
|
||||
});
|
||||
});
|
||||
|
||||
describe('formatIsoDate', () => {
|
||||
it.each([
|
||||
[
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { fromPartial } from '@total-typescript/shoehorn';
|
||||
import type { LocalStorage } from '../../../../src/utils/services/LocalStorage';
|
||||
import { MAIN_COLOR } from '../../../../src/utils/theme';
|
||||
import { ColorGenerator } from '../../../src/utils/services/ColorGenerator';
|
||||
import type { LocalStorage } from '../../../src/utils/services/LocalStorage';
|
||||
|
||||
describe('ColorGenerator', () => {
|
||||
let colorGenerator: ColorGenerator;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { render } from '@testing-library/react';
|
||||
import type { OrderDir } from '../../../../shlink-frontend-kit/src/ordering/ordering';
|
||||
import type { OrderDir } from '../../../../shlink-frontend-kit/src';
|
||||
import { TableOrderIcon } from '../../../src/utils/table/TableOrderIcon';
|
||||
|
||||
describe('<TableOrderIcon />', () => {
|
||||
|
|
|
@ -3,6 +3,7 @@ import { fromPartial } from '@total-typescript/shoehorn';
|
|||
import { formatISO } from 'date-fns';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
import type { MercureBoundProps } from '../../src/mercure/helpers/boundToMercureHub';
|
||||
import { SettingsProvider } from '../../src/utils/settings';
|
||||
import { DomainVisits as createDomainVisits } from '../../src/visits/DomainVisits';
|
||||
import type { DomainVisits } from '../../src/visits/reducers/domainVisits';
|
||||
import { renderWithEvents } from '../__helpers__/setUpTest';
|
||||
|
@ -20,13 +21,14 @@ describe('<DomainVisits />', () => {
|
|||
const DomainVisits = createDomainVisits(fromPartial({ exportVisits }));
|
||||
const setUp = () => renderWithEvents(
|
||||
<MemoryRouter>
|
||||
<DomainVisits
|
||||
{...fromPartial<MercureBoundProps>({ mercureInfo: {} })}
|
||||
getDomainVisits={getDomainVisits}
|
||||
cancelGetDomainVisits={cancelGetDomainVisits}
|
||||
domainVisits={domainVisits}
|
||||
settings={fromPartial({})}
|
||||
/>
|
||||
<SettingsProvider value={fromPartial({})}>
|
||||
<DomainVisits
|
||||
{...fromPartial<MercureBoundProps>({ mercureInfo: {} })}
|
||||
getDomainVisits={getDomainVisits}
|
||||
cancelGetDomainVisits={cancelGetDomainVisits}
|
||||
domainVisits={domainVisits}
|
||||
/>
|
||||
</SettingsProvider>
|
||||
</MemoryRouter>,
|
||||
);
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ import { fromPartial } from '@total-typescript/shoehorn';
|
|||
import { formatISO } from 'date-fns';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
import type { MercureBoundProps } from '../../src/mercure/helpers/boundToMercureHub';
|
||||
import { SettingsProvider } from '../../src/utils/settings';
|
||||
import { NonOrphanVisits as createNonOrphanVisits } from '../../src/visits/NonOrphanVisits';
|
||||
import type { VisitsInfo } from '../../src/visits/reducers/types';
|
||||
import { renderWithEvents } from '../__helpers__/setUpTest';
|
||||
|
@ -15,13 +16,14 @@ describe('<NonOrphanVisits />', () => {
|
|||
const NonOrphanVisits = createNonOrphanVisits(fromPartial({ exportVisits }));
|
||||
const setUp = () => renderWithEvents(
|
||||
<MemoryRouter>
|
||||
<NonOrphanVisits
|
||||
{...fromPartial<MercureBoundProps>({ mercureInfo: {} })}
|
||||
getNonOrphanVisits={getNonOrphanVisits}
|
||||
cancelGetNonOrphanVisits={cancelGetNonOrphanVisits}
|
||||
nonOrphanVisits={nonOrphanVisits}
|
||||
settings={fromPartial({})}
|
||||
/>
|
||||
<SettingsProvider value={fromPartial({})}>
|
||||
<NonOrphanVisits
|
||||
{...fromPartial<MercureBoundProps>({ mercureInfo: {} })}
|
||||
getNonOrphanVisits={getNonOrphanVisits}
|
||||
cancelGetNonOrphanVisits={cancelGetNonOrphanVisits}
|
||||
nonOrphanVisits={nonOrphanVisits}
|
||||
/>
|
||||
</SettingsProvider>
|
||||
</MemoryRouter>,
|
||||
);
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ import { fromPartial } from '@total-typescript/shoehorn';
|
|||
import { formatISO } from 'date-fns';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
import type { MercureBoundProps } from '../../src/mercure/helpers/boundToMercureHub';
|
||||
import { SettingsProvider } from '../../src/utils/settings';
|
||||
import { OrphanVisits as createOrphanVisits } from '../../src/visits/OrphanVisits';
|
||||
import type { VisitsInfo } from '../../src/visits/reducers/types';
|
||||
import { renderWithEvents } from '../__helpers__/setUpTest';
|
||||
|
@ -14,13 +15,14 @@ describe('<OrphanVisits />', () => {
|
|||
const OrphanVisits = createOrphanVisits(fromPartial({ exportVisits }));
|
||||
const setUp = () => renderWithEvents(
|
||||
<MemoryRouter>
|
||||
<OrphanVisits
|
||||
{...fromPartial<MercureBoundProps>({ mercureInfo: {} })}
|
||||
getOrphanVisits={getOrphanVisits}
|
||||
orphanVisits={orphanVisits}
|
||||
cancelGetOrphanVisits={vi.fn()}
|
||||
settings={fromPartial({})}
|
||||
/>
|
||||
<SettingsProvider value={fromPartial({})}>
|
||||
<OrphanVisits
|
||||
{...fromPartial<MercureBoundProps>({ mercureInfo: {} })}
|
||||
getOrphanVisits={getOrphanVisits}
|
||||
orphanVisits={orphanVisits}
|
||||
cancelGetOrphanVisits={vi.fn()}
|
||||
/>
|
||||
</SettingsProvider>
|
||||
</MemoryRouter>,
|
||||
);
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@ import { formatISO } from 'date-fns';
|
|||
import { identity } from 'ramda';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
import type { MercureBoundProps } from '../../src/mercure/helpers/boundToMercureHub';
|
||||
import { SettingsProvider } from '../../src/utils/settings';
|
||||
import type { ShortUrlVisits as ShortUrlVisitsState } from '../../src/visits/reducers/shortUrlVisits';
|
||||
import type { ShortUrlVisitsProps } from '../../src/visits/ShortUrlVisits';
|
||||
import { ShortUrlVisits as createShortUrlVisits } from '../../src/visits/ShortUrlVisits';
|
||||
|
@ -16,16 +17,17 @@ describe('<ShortUrlVisits />', () => {
|
|||
const ShortUrlVisits = createShortUrlVisits(fromPartial({ exportVisits }));
|
||||
const setUp = () => renderWithEvents(
|
||||
<MemoryRouter>
|
||||
<ShortUrlVisits
|
||||
{...fromPartial<ShortUrlVisitsProps>({})}
|
||||
{...fromPartial<MercureBoundProps>({ mercureInfo: {} })}
|
||||
getShortUrlDetail={identity}
|
||||
getShortUrlVisits={getShortUrlVisitsMock}
|
||||
shortUrlVisits={shortUrlVisits}
|
||||
shortUrlDetail={fromPartial({})}
|
||||
settings={fromPartial({})}
|
||||
cancelGetShortUrlVisits={() => {}}
|
||||
/>
|
||||
<SettingsProvider value={fromPartial({})}>
|
||||
<ShortUrlVisits
|
||||
{...fromPartial<ShortUrlVisitsProps>({})}
|
||||
{...fromPartial<MercureBoundProps>({ mercureInfo: {} })}
|
||||
getShortUrlDetail={identity}
|
||||
getShortUrlVisits={getShortUrlVisitsMock}
|
||||
shortUrlVisits={shortUrlVisits}
|
||||
shortUrlDetail={fromPartial({})}
|
||||
cancelGetShortUrlVisits={() => {}}
|
||||
/>
|
||||
</SettingsProvider>
|
||||
</MemoryRouter>,
|
||||
);
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ import { fromPartial } from '@total-typescript/shoehorn';
|
|||
import { formatISO } from 'date-fns';
|
||||
import { MemoryRouter } from 'react-router';
|
||||
import type { MercureBoundProps } from '../../src/mercure/helpers/boundToMercureHub';
|
||||
import { SettingsProvider } from '../../src/utils/settings';
|
||||
import type { TagVisits as TagVisitsStats } from '../../src/visits/reducers/tagVisits';
|
||||
import type { TagVisitsProps } from '../../src/visits/TagVisits';
|
||||
import { TagVisits as createTagVisits } from '../../src/visits/TagVisits';
|
||||
|
@ -23,14 +24,15 @@ describe('<TagVisits />', () => {
|
|||
);
|
||||
const setUp = () => renderWithEvents(
|
||||
<MemoryRouter>
|
||||
<TagVisits
|
||||
{...fromPartial<TagVisitsProps>({})}
|
||||
{...fromPartial<MercureBoundProps>({ mercureInfo: {} })}
|
||||
getTagVisits={getTagVisitsMock}
|
||||
tagVisits={tagVisits}
|
||||
settings={fromPartial({})}
|
||||
cancelGetTagVisits={() => {}}
|
||||
/>
|
||||
<SettingsProvider value={fromPartial({})}>
|
||||
<TagVisits
|
||||
{...fromPartial<TagVisitsProps>({})}
|
||||
{...fromPartial<MercureBoundProps>({ mercureInfo: {} })}
|
||||
getTagVisits={getTagVisitsMock}
|
||||
tagVisits={tagVisits}
|
||||
cancelGetTagVisits={() => {}}
|
||||
/>
|
||||
</SettingsProvider>
|
||||
</MemoryRouter>,
|
||||
);
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ import { fromPartial } from '@total-typescript/shoehorn';
|
|||
import { createMemoryHistory } from 'history';
|
||||
import { Router } from 'react-router-dom';
|
||||
import { rangeOf } from '../../src/utils/helpers';
|
||||
import { SettingsProvider } from '../../src/utils/settings';
|
||||
import type { VisitsInfo } from '../../src/visits/reducers/types';
|
||||
import type { Visit } from '../../src/visits/types';
|
||||
import { VisitsStats } from '../../src/visits/VisitsStats';
|
||||
|
@ -20,13 +21,14 @@ describe('<VisitsStats />', () => {
|
|||
history,
|
||||
...renderWithEvents(
|
||||
<Router location={history.location} navigator={history}>
|
||||
<VisitsStats
|
||||
getVisits={getVisitsMock}
|
||||
visitsInfo={fromPartial(visitsInfo)}
|
||||
cancelGetVisits={() => {}}
|
||||
settings={fromPartial({})}
|
||||
exportCsv={exportCsv}
|
||||
/>
|
||||
<SettingsProvider value={fromPartial({})}>
|
||||
<VisitsStats
|
||||
getVisits={getVisitsMock}
|
||||
visitsInfo={fromPartial(visitsInfo)}
|
||||
cancelGetVisits={() => {}}
|
||||
exportCsv={exportCsv}
|
||||
/>
|
||||
</SettingsProvider>
|
||||
</Router>,
|
||||
),
|
||||
};
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
import { fromPartial } from '@total-typescript/shoehorn';
|
||||
import { addDays, formatISO, subDays } from 'date-fns';
|
||||
import type { ShlinkApiClient } from '../../../../src/api/services/ShlinkApiClient';
|
||||
import type { ShlinkState } from '../../../../src/container/types';
|
||||
import { rangeOf } from '../../../../src/utils/utils';
|
||||
import type { ShlinkVisits } from '../../../src/api/types';
|
||||
import type { ShlinkApiClient, ShlinkVisits } from '../../../src/api-contract';
|
||||
import type { RootState } from '../../../src/container/store';
|
||||
import type { ShortUrl } from '../../../src/short-urls/data';
|
||||
import { formatIsoDate } from '../../../src/utils/dates/helpers/date';
|
||||
import type { DateInterval } from '../../../src/utils/dates/helpers/dateIntervals';
|
||||
|
@ -151,7 +150,7 @@ describe('domainVisitsReducer', () => {
|
|||
|
||||
describe('getDomainVisits', () => {
|
||||
const dispatchMock = vi.fn();
|
||||
const getState = () => fromPartial<ShlinkState>({
|
||||
const getState = () => fromPartial<RootState>({
|
||||
domainVisits: { cancelLoad: false },
|
||||
});
|
||||
const domain = 'foo.com';
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
import { fromPartial } from '@total-typescript/shoehorn';
|
||||
import { addDays, formatISO, subDays } from 'date-fns';
|
||||
import type { ShlinkApiClient } from '../../../../src/api/services/ShlinkApiClient';
|
||||
import type { ShlinkState } from '../../../../src/container/types';
|
||||
import { rangeOf } from '../../../../src/utils/utils';
|
||||
import type { ShlinkVisits } from '../../../src/api/types';
|
||||
import type { ShlinkApiClient, ShlinkVisits } from '../../../src/api-contract';
|
||||
import type { RootState } from '../../../src/container/store';
|
||||
import { formatIsoDate } from '../../../src/utils/dates/helpers/date';
|
||||
import type { DateInterval } from '../../../src/utils/dates/helpers/dateIntervals';
|
||||
import {
|
||||
|
@ -118,7 +117,7 @@ describe('nonOrphanVisitsReducer', () => {
|
|||
|
||||
describe('getNonOrphanVisits', () => {
|
||||
const dispatchMock = vi.fn();
|
||||
const getState = () => fromPartial<ShlinkState>({
|
||||
const getState = () => fromPartial<RootState>({
|
||||
orphanVisits: { cancelLoad: false },
|
||||
});
|
||||
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
import { fromPartial } from '@total-typescript/shoehorn';
|
||||
import { addDays, formatISO, subDays } from 'date-fns';
|
||||
import type { ShlinkApiClient } from '../../../../src/api/services/ShlinkApiClient';
|
||||
import type { ShlinkState } from '../../../../src/container/types';
|
||||
import { rangeOf } from '../../../../src/utils/utils';
|
||||
import type { ShlinkVisits } from '../../../src/api/types';
|
||||
import type { ShlinkApiClient, ShlinkVisits } from '../../../src/api-contract';
|
||||
import type { RootState } from '../../../src/container/store';
|
||||
import { formatIsoDate } from '../../../src/utils/dates/helpers/date';
|
||||
import type { DateInterval } from '../../../src/utils/dates/helpers/dateIntervals';
|
||||
import {
|
||||
|
@ -118,7 +117,7 @@ describe('orphanVisitsReducer', () => {
|
|||
|
||||
describe('getOrphanVisits', () => {
|
||||
const dispatchMock = vi.fn();
|
||||
const getState = () => fromPartial<ShlinkState>({
|
||||
const getState = () => fromPartial<RootState>({
|
||||
orphanVisits: { cancelLoad: false },
|
||||
});
|
||||
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
import { fromPartial } from '@total-typescript/shoehorn';
|
||||
import { addDays, formatISO, subDays } from 'date-fns';
|
||||
import type { ShlinkApiClient } from '../../../../src/api/services/ShlinkApiClient';
|
||||
import type { ShlinkState } from '../../../../src/container/types';
|
||||
import { rangeOf } from '../../../../src/utils/utils';
|
||||
import type { ShlinkVisits } from '../../../src/api/types';
|
||||
import type { ShlinkApiClient, ShlinkVisits } from '../../../src/api-contract';
|
||||
import type { RootState } from '../../../src/container/store';
|
||||
import { formatIsoDate } from '../../../src/utils/dates/helpers/date';
|
||||
import type { DateInterval } from '../../../src/utils/dates/helpers/dateIntervals';
|
||||
import type {
|
||||
|
@ -142,7 +141,7 @@ describe('shortUrlVisitsReducer', () => {
|
|||
|
||||
describe('getShortUrlVisits', () => {
|
||||
const dispatchMock = vi.fn();
|
||||
const getState = () => fromPartial<ShlinkState>({
|
||||
const getState = () => fromPartial<RootState>({
|
||||
shortUrlVisits: { cancelLoad: false },
|
||||
});
|
||||
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
import { fromPartial } from '@total-typescript/shoehorn';
|
||||
import { addDays, formatISO, subDays } from 'date-fns';
|
||||
import type { ShlinkApiClient } from '../../../../src/api/services/ShlinkApiClient';
|
||||
import type { ShlinkState } from '../../../../src/container/types';
|
||||
import { rangeOf } from '../../../../src/utils/utils';
|
||||
import type { ShlinkVisits } from '../../../src/api/types';
|
||||
import type { ShlinkApiClient, ShlinkVisits } from '../../../src/api-contract';
|
||||
import type { RootState } from '../../../src/container/store';
|
||||
import { formatIsoDate } from '../../../src/utils/dates/helpers/date';
|
||||
import type { DateInterval } from '../../../src/utils/dates/helpers/dateIntervals';
|
||||
import type {
|
||||
|
@ -142,7 +141,7 @@ describe('tagVisitsReducer', () => {
|
|||
|
||||
describe('getTagVisits', () => {
|
||||
const dispatchMock = vi.fn();
|
||||
const getState = () => fromPartial<ShlinkState>({
|
||||
const getState = () => fromPartial<RootState>({
|
||||
tagVisits: { cancelLoad: false },
|
||||
});
|
||||
const tag = 'foo';
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import { fromPartial } from '@total-typescript/shoehorn';
|
||||
import type { ShlinkApiClient } from '../../../../src/api/services/ShlinkApiClient';
|
||||
import type { ShlinkState } from '../../../../src/container/types';
|
||||
import type { ShlinkVisitsOverview } from '../../../src/api/types';
|
||||
import type { ShlinkApiClient, ShlinkVisitsOverview } from '../../../src/api-contract';
|
||||
import type { RootState } from '../../../src/container/store';
|
||||
import { createNewVisits } from '../../../src/visits/reducers/visitCreation';
|
||||
import type {
|
||||
PartialVisitsSummary,
|
||||
|
@ -25,7 +24,7 @@ describe('visitsOverviewReducer', () => {
|
|||
it('returns loading on GET_OVERVIEW_START', () => {
|
||||
const { loading } = reducer(
|
||||
state({ loading: false, error: false }),
|
||||
loadVisitsOverview.pending(''),
|
||||
loadVisitsOverview.pending('', {}),
|
||||
);
|
||||
|
||||
expect(loading).toEqual(true);
|
||||
|
@ -34,7 +33,7 @@ describe('visitsOverviewReducer', () => {
|
|||
it('stops loading and returns error on GET_OVERVIEW_ERROR', () => {
|
||||
const { loading, error } = reducer(
|
||||
state({ loading: true, error: false }),
|
||||
loadVisitsOverview.rejected(null, ''),
|
||||
loadVisitsOverview.rejected(null, '', {}),
|
||||
);
|
||||
|
||||
expect(loading).toEqual(false);
|
||||
|
@ -44,7 +43,7 @@ describe('visitsOverviewReducer', () => {
|
|||
it('return visits overview on GET_OVERVIEW', () => {
|
||||
const action = loadVisitsOverview.fulfilled(fromPartial({
|
||||
nonOrphanVisits: { total: 100 },
|
||||
}), 'requestId');
|
||||
}), 'requestId', {});
|
||||
const { loading, error, nonOrphanVisits } = reducer(state({ loading: true, error: false }), action);
|
||||
|
||||
expect(loading).toEqual(false);
|
||||
|
@ -127,7 +126,7 @@ describe('visitsOverviewReducer', () => {
|
|||
|
||||
describe('loadVisitsOverview', () => {
|
||||
const dispatchMock = vi.fn();
|
||||
const getState = () => fromPartial<ShlinkState>({});
|
||||
const getState = () => fromPartial<RootState>({});
|
||||
|
||||
it.each([
|
||||
[
|
||||
|
@ -155,7 +154,7 @@ describe('visitsOverviewReducer', () => {
|
|||
const resolvedOverview = fromPartial<ShlinkVisitsOverview>(serverResult);
|
||||
getVisitsOverview.mockResolvedValue(resolvedOverview);
|
||||
|
||||
await loadVisitsOverview()(dispatchMock, getState, {});
|
||||
await loadVisitsOverview(buildApiClientMock)(dispatchMock, getState, {});
|
||||
|
||||
expect(dispatchMock).toHaveBeenCalledTimes(2);
|
||||
expect(dispatchMock).toHaveBeenNthCalledWith(2, expect.objectContaining({ payload: dispatchedPayload }));
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { fromPartial } from '@total-typescript/shoehorn';
|
||||
import type { ShlinkVisitsParams } from '../../../src/api/types';
|
||||
import type { ShlinkVisitsParams } from '../../../src/api-contract';
|
||||
import { formatIsoDate, parseDate } from '../../../src/utils/dates/helpers/date';
|
||||
import type { CreateVisit, OrphanVisit, VisitsParams } from '../../../src/visits/types';
|
||||
import type { GroupedNewVisits } from '../../../src/visits/types/helpers';
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { pipe } from 'ramda';
|
||||
import { pipe, range } from 'ramda';
|
||||
import type { SyntheticEvent } from 'react';
|
||||
|
||||
type Optional<T> = T | null | undefined;
|
||||
|
@ -9,3 +9,6 @@ export const handleEventPreventingDefault = <T>(handler: () => T) => pipe(
|
|||
(e: SyntheticEvent) => e.preventDefault(),
|
||||
handler,
|
||||
);
|
||||
|
||||
export const rangeOf = <T>(size: number, mappingFn: (value: number) => T, startAt = 1): T[] =>
|
||||
range(startAt, size + 1).map(mappingFn);
|
||||
|
|
Loading…
Reference in a new issue