2022-07-16 11:27:29 +03:00
|
|
|
import { screen } from '@testing-library/react';
|
2023-04-13 22:48:29 +03:00
|
|
|
import { fromPartial } from '@total-typescript/shoehorn';
|
2022-12-19 22:00:52 +03:00
|
|
|
import { addDays, formatISO, subDays } from 'date-fns';
|
2023-02-18 13:11:01 +03:00
|
|
|
import { last } from 'ramda';
|
2022-12-23 22:00:59 +03:00
|
|
|
import { MemoryRouter, useLocation } from 'react-router-dom';
|
2023-02-18 13:11:01 +03:00
|
|
|
import type { ReachableServer } from '../../../src/servers/data';
|
|
|
|
import type { Settings } from '../../../src/settings/reducers/settings';
|
|
|
|
import type { ShortUrl, ShortUrlMeta } from '../../../src/short-urls/data';
|
2022-05-28 12:16:59 +03:00
|
|
|
import { ShortUrlsRow as createShortUrlsRow } from '../../../src/short-urls/helpers/ShortUrlsRow';
|
2023-02-18 13:11:01 +03:00
|
|
|
import { now, parseDate } from '../../../src/utils/helpers/date';
|
2023-02-18 12:40:37 +03:00
|
|
|
import type { TimeoutToggle } from '../../../src/utils/helpers/hooks';
|
|
|
|
import type { OptionalString } from '../../../src/utils/utils';
|
2023-02-18 13:11:01 +03:00
|
|
|
import { renderWithEvents } from '../../__helpers__/setUpTest';
|
2022-07-16 11:52:45 +03:00
|
|
|
import { colorGeneratorMock } from '../../utils/services/__mocks__/ColorGenerator.mock';
|
2019-01-13 11:49:02 +03:00
|
|
|
|
2022-12-22 20:39:09 +03:00
|
|
|
interface SetUpOptions {
|
|
|
|
title?: OptionalString;
|
|
|
|
tags?: string[];
|
|
|
|
meta?: ShortUrlMeta;
|
|
|
|
settings?: Partial<Settings>;
|
|
|
|
}
|
|
|
|
|
2023-05-27 12:57:26 +03:00
|
|
|
vi.mock('react-router-dom', async () => ({
|
|
|
|
...(await vi.importActual<any>('react-router-dom')),
|
|
|
|
useLocation: vi.fn().mockReturnValue({}),
|
2022-12-23 22:00:59 +03:00
|
|
|
}));
|
|
|
|
|
2019-01-13 11:49:02 +03:00
|
|
|
describe('<ShortUrlsRow />', () => {
|
2023-05-27 12:57:26 +03:00
|
|
|
const timeoutToggle = vi.fn(() => true);
|
|
|
|
const useTimeoutToggle = vi.fn(() => [false, timeoutToggle]) as TimeoutToggle;
|
2023-04-13 22:48:29 +03:00
|
|
|
const server = fromPartial<ReachableServer>({ url: 'https://s.test' });
|
2020-08-30 10:59:14 +03:00
|
|
|
const shortUrl: ShortUrl = {
|
2019-01-13 11:49:02 +03:00
|
|
|
shortCode: 'abc123',
|
2023-01-18 00:53:49 +03:00
|
|
|
shortUrl: 'https://s.test/abc123',
|
2022-07-16 11:27:29 +03:00
|
|
|
longUrl: 'https://foo.com/bar',
|
2021-06-25 20:52:50 +03:00
|
|
|
dateCreated: formatISO(parseDate('2018-05-23 18:30:41', 'yyyy-MM-dd HH:mm:ss')),
|
2022-07-16 11:27:29 +03:00
|
|
|
tags: [],
|
2019-01-13 11:49:02 +03:00
|
|
|
visitsCount: 45,
|
2022-12-19 22:00:52 +03:00
|
|
|
visitsSummary: {
|
|
|
|
total: 45,
|
|
|
|
nonBots: 40,
|
|
|
|
bots: 5,
|
|
|
|
},
|
2020-08-30 10:59:14 +03:00
|
|
|
domain: null,
|
|
|
|
meta: {
|
|
|
|
validSince: null,
|
|
|
|
validUntil: null,
|
|
|
|
maxVisits: null,
|
|
|
|
},
|
2019-01-13 11:49:02 +03:00
|
|
|
};
|
2022-07-16 11:52:45 +03:00
|
|
|
const ShortUrlsRow = createShortUrlsRow(() => <span>ShortUrlsRowMenu</span>, colorGeneratorMock, useTimeoutToggle);
|
2022-12-23 22:00:59 +03:00
|
|
|
|
|
|
|
const setUp = ({ title, tags = [], meta = {}, settings = {} }: SetUpOptions = {}, search = '') => {
|
|
|
|
(useLocation as any).mockReturnValue({ search });
|
|
|
|
return renderWithEvents(
|
|
|
|
<MemoryRouter>
|
|
|
|
<table>
|
|
|
|
<tbody>
|
|
|
|
<ShortUrlsRow
|
|
|
|
selectedServer={server}
|
|
|
|
shortUrl={{ ...shortUrl, title, tags, meta: { ...shortUrl.meta, ...meta } }}
|
|
|
|
onTagClick={() => null}
|
2023-04-13 22:48:29 +03:00
|
|
|
settings={fromPartial(settings)}
|
2022-12-23 22:00:59 +03:00
|
|
|
/>
|
|
|
|
</tbody>
|
|
|
|
</table>
|
|
|
|
</MemoryRouter>,
|
|
|
|
);
|
|
|
|
};
|
2019-01-13 11:49:02 +03:00
|
|
|
|
2021-03-05 17:23:38 +03:00
|
|
|
it.each([
|
2022-12-19 22:00:52 +03:00
|
|
|
[null, 7],
|
|
|
|
[undefined, 7],
|
|
|
|
['The title', 8],
|
2021-03-05 17:23:38 +03:00
|
|
|
])('renders expected amount of columns', (title, expectedAmount) => {
|
2022-12-19 22:00:52 +03:00
|
|
|
setUp({ title });
|
2022-07-16 11:27:29 +03:00
|
|
|
expect(screen.getAllByRole('cell')).toHaveLength(expectedAmount);
|
2021-03-05 17:23:38 +03:00
|
|
|
});
|
|
|
|
|
2019-01-13 11:49:02 +03:00
|
|
|
it('renders date in first column', () => {
|
2022-07-16 11:27:29 +03:00
|
|
|
setUp();
|
|
|
|
expect(screen.getAllByRole('cell')[0]).toHaveTextContent('2018-05-23 18:30');
|
2019-01-13 11:49:02 +03:00
|
|
|
});
|
|
|
|
|
2022-07-16 11:27:29 +03:00
|
|
|
it.each([
|
|
|
|
[1, shortUrl.shortUrl],
|
|
|
|
[2, shortUrl.longUrl],
|
|
|
|
])('renders expected links on corresponding columns', (colIndex, expectedLink) => {
|
|
|
|
setUp();
|
2019-01-13 11:49:02 +03:00
|
|
|
|
2022-07-16 11:27:29 +03:00
|
|
|
const col = screen.getAllByRole('cell')[colIndex];
|
|
|
|
const link = col.querySelector('a');
|
2021-03-05 17:23:38 +03:00
|
|
|
|
2022-07-16 11:27:29 +03:00
|
|
|
expect(link).toHaveAttribute('href', expectedLink);
|
2021-03-05 17:23:38 +03:00
|
|
|
});
|
|
|
|
|
2022-07-16 11:27:29 +03:00
|
|
|
it.each([
|
|
|
|
['My super cool title', 'My super cool title'],
|
|
|
|
[undefined, shortUrl.longUrl],
|
|
|
|
])('renders title when short URL has it', (title, expectedContent) => {
|
2022-12-19 22:00:52 +03:00
|
|
|
setUp({ title });
|
2019-01-13 11:49:02 +03:00
|
|
|
|
2022-07-16 11:27:29 +03:00
|
|
|
const titleSharedCol = screen.getAllByRole('cell')[2];
|
2019-01-13 11:49:02 +03:00
|
|
|
|
2022-07-16 11:27:29 +03:00
|
|
|
expect(titleSharedCol.querySelector('a')).toHaveAttribute('href', shortUrl.longUrl);
|
|
|
|
expect(titleSharedCol).toHaveTextContent(expectedContent);
|
|
|
|
});
|
2019-01-13 11:49:02 +03:00
|
|
|
|
2022-07-16 11:27:29 +03:00
|
|
|
it.each([
|
|
|
|
[[], ['No tags']],
|
|
|
|
[['nodejs', 'reactjs'], ['nodejs', 'reactjs']],
|
|
|
|
])('renders list of tags in fourth row', (tags, expectedContents) => {
|
2022-12-19 22:00:52 +03:00
|
|
|
setUp({ tags });
|
2022-07-16 11:27:29 +03:00
|
|
|
const cell = screen.getAllByRole('cell')[3];
|
2019-01-13 11:49:02 +03:00
|
|
|
|
2022-07-16 11:27:29 +03:00
|
|
|
expectedContents.forEach((content) => expect(cell).toHaveTextContent(content));
|
2019-01-13 11:49:02 +03:00
|
|
|
});
|
|
|
|
|
2022-12-22 20:39:09 +03:00
|
|
|
it.each([
|
2022-12-23 22:00:59 +03:00
|
|
|
[{}, '', shortUrl.visitsSummary?.total],
|
2023-04-13 22:48:29 +03:00
|
|
|
[fromPartial<Settings>({ visits: { excludeBots: false } }), '', shortUrl.visitsSummary?.total],
|
|
|
|
[fromPartial<Settings>({ visits: { excludeBots: true } }), '', shortUrl.visitsSummary?.nonBots],
|
|
|
|
[fromPartial<Settings>({ visits: { excludeBots: false } }), 'excludeBots=true', shortUrl.visitsSummary?.nonBots],
|
|
|
|
[fromPartial<Settings>({ visits: { excludeBots: true } }), 'excludeBots=true', shortUrl.visitsSummary?.nonBots],
|
2022-12-23 22:00:59 +03:00
|
|
|
[{}, 'excludeBots=true', shortUrl.visitsSummary?.nonBots],
|
2023-04-13 22:48:29 +03:00
|
|
|
[fromPartial<Settings>({ visits: { excludeBots: true } }), 'excludeBots=false', shortUrl.visitsSummary?.total],
|
|
|
|
[fromPartial<Settings>({ visits: { excludeBots: false } }), 'excludeBots=false', shortUrl.visitsSummary?.total],
|
2022-12-23 22:00:59 +03:00
|
|
|
[{}, 'excludeBots=false', shortUrl.visitsSummary?.total],
|
|
|
|
])('renders visits count in fifth row', (settings, search, expectedAmount) => {
|
|
|
|
setUp({ settings }, search);
|
2022-12-22 20:39:09 +03:00
|
|
|
expect(screen.getAllByRole('cell')[4]).toHaveTextContent(`${expectedAmount}`);
|
2019-01-13 11:49:02 +03:00
|
|
|
});
|
|
|
|
|
2022-07-16 11:27:29 +03:00
|
|
|
it('updates state when copied to clipboard', async () => {
|
|
|
|
const { user } = setUp();
|
2019-01-13 11:49:02 +03:00
|
|
|
|
2022-05-29 13:18:21 +03:00
|
|
|
expect(timeoutToggle).not.toHaveBeenCalled();
|
2022-12-19 22:00:52 +03:00
|
|
|
await user.click(screen.getAllByRole('img', { hidden: true })[0]);
|
2022-05-29 13:18:21 +03:00
|
|
|
expect(timeoutToggle).toHaveBeenCalledTimes(1);
|
2019-01-13 11:49:02 +03:00
|
|
|
});
|
2022-12-19 22:00:52 +03:00
|
|
|
|
|
|
|
it.each([
|
|
|
|
[{ validUntil: formatISO(subDays(now(), 1)) }, ['fa-calendar-xmark', 'text-danger']],
|
|
|
|
[{ validSince: formatISO(addDays(now(), 1)) }, ['fa-calendar-xmark', 'text-warning']],
|
|
|
|
[{ maxVisits: 45 }, ['fa-link-slash', 'text-danger']],
|
|
|
|
[{ maxVisits: 45, validSince: formatISO(addDays(now(), 1)) }, ['fa-link-slash', 'text-danger']],
|
|
|
|
[
|
|
|
|
{ validSince: formatISO(addDays(now(), 1)), validUntil: formatISO(subDays(now(), 1)) },
|
|
|
|
['fa-calendar-xmark', 'text-danger'],
|
|
|
|
],
|
|
|
|
[
|
|
|
|
{ validSince: formatISO(subDays(now(), 1)), validUntil: formatISO(addDays(now(), 1)) },
|
|
|
|
['fa-check', 'text-primary'],
|
|
|
|
],
|
|
|
|
[{ maxVisits: 500 }, ['fa-check', 'text-primary']],
|
|
|
|
[{}, ['fa-check', 'text-primary']],
|
|
|
|
])('displays expected status icon', (meta, expectedIconClasses) => {
|
|
|
|
setUp({ meta });
|
|
|
|
const statusIcon = last(screen.getAllByRole('img', { hidden: true }));
|
|
|
|
|
|
|
|
expect(statusIcon).toBeInTheDocument();
|
|
|
|
expectedIconClasses.forEach((expectedClass) => expect(statusIcon).toHaveClass(expectedClass));
|
|
|
|
expect(statusIcon).toMatchSnapshot();
|
|
|
|
});
|
2019-01-13 11:49:02 +03:00
|
|
|
});
|