Fix shlink-web-component tests

This commit is contained in:
Alejandro Celaya
2023-08-04 11:16:01 +02:00
parent bdcfcee60e
commit 4d8477a32c
54 changed files with 345 additions and 431 deletions

View File

@@ -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', () => {

View File

@@ -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>,
);

View File

@@ -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>,
);

View File

@@ -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());
});
});

View File

@@ -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'));

View File

@@ -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 }));
});
});

View File

@@ -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];

View File

@@ -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'));

View File

@@ -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/ }));

View File

@@ -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));

View File

@@ -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';

View File

@@ -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>,
);
};

View File

@@ -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>,
);

View File

@@ -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);

View File

@@ -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,

View File

@@ -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],

View File

@@ -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);

View File

@@ -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({});