Introduce shoehorn as a possible replacement for ts-mockery

This commit is contained in:
Alejandro Celaya 2023-04-13 21:48:29 +02:00
parent f6334c3618
commit 340f4b8fb5
65 changed files with 357 additions and 375 deletions

16
package-lock.json generated
View file

@ -60,6 +60,7 @@
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^14.4.3",
"@total-typescript/shoehorn": "^0.1.0",
"@types/jest": "^29.2.4",
"@types/json2csv": "^5.0.3",
"@types/leaflet": "^1.9.0",
@ -3563,6 +3564,12 @@
"node": ">= 10"
}
},
"node_modules/@total-typescript/shoehorn": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/@total-typescript/shoehorn/-/shoehorn-0.1.0.tgz",
"integrity": "sha512-XKig6hXxWnUh0fsW3LR2vxpxwLlPFokfOSR0riHKA9uXvdHDfwOOPdAOi4U/YNKLmgYUu/plUfnF3yiAAz1+Zg==",
"dev": true
},
"node_modules/@types/aria-query": {
"version": "4.2.2",
"dev": true,
@ -14987,6 +14994,12 @@
"version": "2.0.0",
"dev": true
},
"@total-typescript/shoehorn": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/@total-typescript/shoehorn/-/shoehorn-0.1.0.tgz",
"integrity": "sha512-XKig6hXxWnUh0fsW3LR2vxpxwLlPFokfOSR0riHKA9uXvdHDfwOOPdAOi4U/YNKLmgYUu/plUfnF3yiAAz1+Zg==",
"dev": true
},
"@types/aria-query": {
"version": "4.2.2",
"dev": true
@ -15884,9 +15897,6 @@
}
}
},
"caniuse-lite": {
"version": "1.0.30001415"
},
"chalk": {
"version": "5.2.0",
"dev": true

View file

@ -77,6 +77,7 @@
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^14.4.3",
"@total-typescript/shoehorn": "^0.1.0",
"@types/jest": "^29.2.4",
"@types/json2csv": "^5.0.3",
"@types/leaflet": "^1.9.0",

View file

@ -1,4 +1,4 @@
import { Mock } from 'ts-mockery';
import { fromAny, fromPartial } from '@total-typescript/shoehorn';
const createLinkMock = () => ({
setAttribute: jest.fn(),
@ -10,9 +10,9 @@ export const appendChild = jest.fn();
export const removeChild = jest.fn();
export const windowMock = Mock.of<Window>({
document: {
export const windowMock = fromPartial<Window>({
document: fromAny({
createElement: jest.fn(createLinkMock),
body: { appendChild, removeChild },
},
}),
});

View file

@ -1,5 +1,5 @@
import { render, screen } from '@testing-library/react';
import { Mock } from 'ts-mockery';
import { fromPartial } from '@total-typescript/shoehorn';
import type { ShlinkApiErrorProps } from '../../src/api/ShlinkApiError';
import { ShlinkApiError } from '../../src/api/ShlinkApiError';
import type { InvalidArgumentError, ProblemDetailsError } from '../../src/api/types/errors';
@ -10,8 +10,8 @@ describe('<ShlinkApiError />', () => {
it.each([
[undefined, 'the fallback', 'the fallback'],
[Mock.all<ProblemDetailsError>(), 'the fallback', 'the fallback'],
[Mock.of<ProblemDetailsError>({ detail: 'the detail' }), 'the fallback', 'the detail'],
[fromPartial<ProblemDetailsError>({}), 'the fallback', 'the fallback'],
[fromPartial<ProblemDetailsError>({ detail: 'the detail' }), 'the fallback', 'the detail'],
])('renders proper message', (errorData, fallbackMessage, expectedMessage) => {
const { container } = setUp({ errorData, fallbackMessage });
@ -21,9 +21,9 @@ describe('<ShlinkApiError />', () => {
it.each([
[undefined, 0],
[Mock.all<ProblemDetailsError>(), 0],
[Mock.of<InvalidArgumentError>({ type: ErrorTypeV2.INVALID_ARGUMENT, invalidElements: [] }), 1],
[Mock.of<InvalidArgumentError>({ type: ErrorTypeV3.INVALID_ARGUMENT, invalidElements: [] }), 1],
[fromPartial<ProblemDetailsError>({}), 0],
[fromPartial<InvalidArgumentError>({ type: ErrorTypeV2.INVALID_ARGUMENT, invalidElements: [] }), 1],
[fromPartial<InvalidArgumentError>({ type: ErrorTypeV3.INVALID_ARGUMENT, invalidElements: [] }), 1],
])('renders list of invalid elements when provided error is an InvalidError', (errorData, expectedElementsCount) => {
setUp({ errorData });
expect(screen.queryAllByText(/^Invalid elements/)).toHaveLength(expectedElementsCount);

View file

@ -1,4 +1,4 @@
import { Mock } from 'ts-mockery';
import { fromPartial } from '@total-typescript/shoehorn';
import { ShlinkApiClient } from '../../../src/api/services/ShlinkApiClient';
import type { ShlinkDomain, ShlinkVisits, ShlinkVisitsOverview } from '../../../src/api/types';
import { ErrorTypeV2, ErrorTypeV3 } from '../../../src/api/types/errors';
@ -9,7 +9,7 @@ import type { OptionalString } from '../../../src/utils/utils';
describe('ShlinkApiClient', () => {
const fetchJson = jest.fn().mockResolvedValue({});
const fetchEmpty = jest.fn().mockResolvedValue(undefined);
const httpClient = Mock.of<HttpClient>({ fetchJson, fetchEmpty });
const httpClient = fromPartial<HttpClient>({ fetchJson, fetchEmpty });
const buildApiClient = () => new ShlinkApiClient(httpClient, '', '');
const shortCodesWithDomainCombinations: [string, OptionalString][] = [
['abc123', null],
@ -177,7 +177,7 @@ describe('ShlinkApiClient', () => {
maxVisits: 50,
validSince: '2025-01-01T10:00:00+01:00',
};
const expectedResp = Mock.of<ShortUrl>();
const expectedResp = fromPartial<ShortUrl>({});
fetchJson.mockResolvedValue(expectedResp);
const { updateShortUrl } = buildApiClient();
const expectedQuery = domain ? `?domain=${domain}` : '';
@ -311,7 +311,7 @@ describe('ShlinkApiClient', () => {
describe('listDomains', () => {
it('returns domains', async () => {
const expectedData = { data: [Mock.all<ShlinkDomain>(), Mock.all<ShlinkDomain>()] };
const expectedData = { data: [fromPartial<ShlinkDomain>({}), fromPartial<ShlinkDomain>({})] };
fetchJson.mockResolvedValue({ domains: expectedData });
const { listDomains } = buildApiClient();
@ -324,7 +324,7 @@ describe('ShlinkApiClient', () => {
describe('getVisitsOverview', () => {
it('returns visits overview', async () => {
const expectedData = Mock.all<ShlinkVisitsOverview>();
const expectedData = fromPartial<ShlinkVisitsOverview>({});
fetchJson.mockResolvedValue({ visits: expectedData });
const { getVisitsOverview } = buildApiClient();
@ -337,7 +337,7 @@ describe('ShlinkApiClient', () => {
describe('getOrphanVisits', () => {
it('returns orphan visits', async () => {
fetchJson.mockResolvedValue({ visits: Mock.of<ShlinkVisits>({ data: [] }) });
fetchJson.mockResolvedValue({ visits: fromPartial<ShlinkVisits>({ data: [] }) });
const { getOrphanVisits } = buildApiClient();
const result = await getOrphanVisits();
@ -349,7 +349,7 @@ describe('ShlinkApiClient', () => {
describe('getNonOrphanVisits', () => {
it('returns non-orphan visits', async () => {
fetchJson.mockResolvedValue({ visits: Mock.of<ShlinkVisits>({ data: [] }) });
fetchJson.mockResolvedValue({ visits: fromPartial<ShlinkVisits>({ data: [] }) });
const { getNonOrphanVisits } = buildApiClient();
const result = await getNonOrphanVisits();

View file

@ -1,15 +1,13 @@
import { Mock } from 'ts-mockery';
import { fromPartial } from '@total-typescript/shoehorn';
import { buildShlinkApiClient } from '../../../src/api/services/ShlinkApiClientBuilder';
import type { HttpClient } from '../../../src/common/services/HttpClient';
import type { ShlinkState } from '../../../src/container/types';
import type { ReachableServer, SelectedServer } from '../../../src/servers/data';
describe('ShlinkApiClientBuilder', () => {
const server = (data: Partial<ReachableServer>) => Mock.of<ReachableServer>(data);
const server = fromPartial<ReachableServer>;
const createBuilder = () => {
const builder = buildShlinkApiClient(Mock.of<HttpClient>());
return (selectedServer: SelectedServer) => builder(() => Mock.of<ShlinkState>({ selectedServer }));
const builder = buildShlinkApiClient(fromPartial({}));
return (selectedServer: SelectedServer) => builder(() => fromPartial({ selectedServer }));
};
it('creates new instances when provided params are different', async () => {
@ -42,7 +40,7 @@ describe('ShlinkApiClientBuilder', () => {
it('does not fetch from state when provided param is already selected server', () => {
const url = 'url';
const apiKey = 'apiKey';
const apiClient = buildShlinkApiClient(Mock.of<HttpClient>())(server({ url, apiKey }));
const apiClient = buildShlinkApiClient(fromPartial({}))(server({ url, apiKey }));
expect(apiClient['baseUrl']).toEqual(url); // eslint-disable-line @typescript-eslint/dot-notation
expect(apiClient['apiKey']).toEqual(apiKey); // eslint-disable-line @typescript-eslint/dot-notation

View file

@ -1,9 +1,8 @@
import { render, screen } from '@testing-library/react';
import { fromPartial } from '@total-typescript/shoehorn';
import { createMemoryHistory } from 'history';
import { Router } from 'react-router-dom';
import { Mock } from 'ts-mockery';
import { App as createApp } from '../../src/app/App';
import type { Settings } from '../../src/settings/reducers/settings';
describe('<App />', () => {
const App = createApp(
@ -25,7 +24,7 @@ describe('<App />', () => {
<App
fetchServers={() => {}}
servers={{}}
settings={Mock.all<Settings>()}
settings={fromPartial({})}
appUpdated
resetAppUpdate={() => {}}
/>

View file

@ -1,14 +1,13 @@
import { render, screen } from '@testing-library/react';
import { fromPartial } from '@total-typescript/shoehorn';
import { MemoryRouter } from 'react-router-dom';
import { Mock } from 'ts-mockery';
import { AsideMenu as createAsideMenu } from '../../src/common/AsideMenu';
import type { ReachableServer } from '../../src/servers/data';
describe('<AsideMenu />', () => {
const AsideMenu = createAsideMenu(() => <>DeleteServerButton</>);
const setUp = (id: string | false = 'abc123') => render(
<MemoryRouter>
<AsideMenu selectedServer={Mock.of<ReachableServer>({ id: id || undefined, version: '2.8.0' })} />
<AsideMenu selectedServer={fromPartial({ id: id || undefined, version: '2.8.0' })} />
</MemoryRouter>,
);

View file

@ -1,5 +1,5 @@
import { render, screen } from '@testing-library/react';
import { Mock } from 'ts-mockery';
import { fromPartial } from '@total-typescript/shoehorn';
import { ErrorHandler as createErrorHandler } from '../../src/common/ErrorHandler';
import { renderWithEvents } from '../__helpers__/setUpTest';
@ -9,10 +9,10 @@ const ComponentWithError = () => {
describe('<ErrorHandler />', () => {
const reload = jest.fn();
const window = Mock.of<Window>({
const window = fromPartial<Window>({
location: { reload },
});
const cons = Mock.of<Console>({ error: jest.fn() });
const cons = fromPartial<Console>({ error: jest.fn() });
const ErrorHandler = createErrorHandler(window, cons);
beforeEach(() => {

View file

@ -1,6 +1,6 @@
import { render, screen } from '@testing-library/react';
import { fromPartial } from '@total-typescript/shoehorn';
import { MemoryRouter } from 'react-router-dom';
import { Mock } from 'ts-mockery';
import { Home } from '../../src/common/Home';
import type { ServersMap, ServerWithId } from '../../src/servers/data';
@ -19,9 +19,9 @@ describe('<Home />', () => {
it.each([
[
{
'1a': Mock.of<ServerWithId>({ name: 'foo', id: '1' }),
'2b': Mock.of<ServerWithId>({ name: 'bar', id: '2' }),
'3c': Mock.of<ServerWithId>({ name: 'baz', id: '3' }),
'1a': fromPartial<ServerWithId>({ name: 'foo', id: '1' }),
'2b': fromPartial<ServerWithId>({ name: 'bar', id: '2' }),
'3c': fromPartial<ServerWithId>({ name: 'baz', id: '3' }),
},
3,
],

View file

@ -1,9 +1,9 @@
import { render, screen } from '@testing-library/react';
import { fromPartial } from '@total-typescript/shoehorn';
import { createMemoryHistory } from 'history';
import { Router, useParams } from 'react-router-dom';
import { Mock } from 'ts-mockery';
import { MenuLayout as createMenuLayout } from '../../src/common/MenuLayout';
import type { NonReachableServer, NotFoundServer, ReachableServer, SelectedServer } from '../../src/servers/data';
import type { NonReachableServer, NotFoundServer, SelectedServer } from '../../src/servers/data';
import type { SemVer } from '../../src/utils/helpers/version';
jest.mock('react-router-dom', () => ({ ...jest.requireActual('react-router-dom'), useParams: jest.fn() }));
@ -54,8 +54,8 @@ describe('<MenuLayout />', () => {
});
it.each([
[Mock.of<NotFoundServer>({ serverNotFound: true })],
[Mock.of<NonReachableServer>({ serverNotReachable: true })],
[fromPartial<NotFoundServer>({ serverNotFound: true })],
[fromPartial<NonReachableServer>({ serverNotReachable: true })],
])('shows error for non reachable servers', (selectedServer) => {
setUp(selectedServer);
@ -81,7 +81,7 @@ describe('<MenuLayout />', () => {
])(
'renders expected component based on location and server version',
(version, currentPath, expectedContent) => {
setUp(Mock.of<ReachableServer>({ version }), currentPath);
setUp(fromPartial({ version }), currentPath);
expect(screen.getByText(expectedContent)).toBeInTheDocument();
},
);

View file

@ -1,5 +1,5 @@
import { render, screen } from '@testing-library/react';
import { Mock } from 'ts-mockery';
import { fromPartial } from '@total-typescript/shoehorn';
import type { ShlinkVersionsProps } from '../../src/common/ShlinkVersions';
import { ShlinkVersions } from '../../src/common/ShlinkVersions';
import type { NonReachableServer, NotFoundServer, ReachableServer } from '../../src/servers/data';
@ -8,11 +8,11 @@ describe('<ShlinkVersions />', () => {
const setUp = (props: ShlinkVersionsProps) => render(<ShlinkVersions {...props} />);
it.each([
['1.2.3', Mock.of<ReachableServer>({ version: '1.0.0', printableVersion: 'foo' }), 'v1.2.3', 'foo'],
['foo', Mock.of<ReachableServer>({ version: '1.0.0', printableVersion: '1.2.3' }), 'latest', '1.2.3'],
['latest', Mock.of<ReachableServer>({ version: '1.0.0', printableVersion: 'latest' }), 'latest', 'latest'],
['5.5.0', Mock.of<ReachableServer>({ version: '1.0.0', printableVersion: '0.2.8' }), 'v5.5.0', '0.2.8'],
['not-semver', Mock.of<ReachableServer>({ version: '1.0.0', printableVersion: 'some' }), 'latest', 'some'],
['1.2.3', fromPartial<ReachableServer>({ version: '1.0.0', printableVersion: 'foo' }), 'v1.2.3', 'foo'],
['foo', fromPartial<ReachableServer>({ version: '1.0.0', printableVersion: '1.2.3' }), 'latest', '1.2.3'],
['latest', fromPartial<ReachableServer>({ version: '1.0.0', printableVersion: 'latest' }), 'latest', 'latest'],
['5.5.0', fromPartial<ReachableServer>({ version: '1.0.0', printableVersion: '0.2.8' }), 'v5.5.0', '0.2.8'],
['not-semver', fromPartial<ReachableServer>({ version: '1.0.0', printableVersion: 'some' }), 'latest', 'some'],
])(
'displays expected versions when selected server is reachable',
(clientVersion, selectedServer, expectedClientVersion, expectedServerVersion) => {
@ -34,8 +34,8 @@ describe('<ShlinkVersions />', () => {
it.each([
['1.2.3', null],
['1.2.3', Mock.of<NotFoundServer>({ serverNotFound: true })],
['1.2.3', Mock.of<NonReachableServer>({ serverNotReachable: true })],
['1.2.3', fromPartial<NotFoundServer>({ serverNotFound: true })],
['1.2.3', fromPartial<NonReachableServer>({ serverNotReachable: true })],
])('displays only client version when selected server is not reachable', (clientVersion, selectedServer) => {
setUp({ clientVersion, selectedServer });
const links = screen.getAllByRole('link');

View file

@ -1,12 +1,11 @@
import { render } from '@testing-library/react';
import { Mock } from 'ts-mockery';
import { fromPartial } from '@total-typescript/shoehorn';
import type { Sidebar } from '../../src/common/reducers/sidebar';
import { ShlinkVersionsContainer } from '../../src/common/ShlinkVersionsContainer';
import type { SelectedServer } from '../../src/servers/data';
describe('<ShlinkVersionsContainer />', () => {
const setUp = (sidebar: Sidebar) => render(
<ShlinkVersionsContainer selectedServer={Mock.all<SelectedServer>()} sidebar={sidebar} />,
<ShlinkVersionsContainer selectedServer={fromPartial({})} sidebar={sidebar} />,
);
it.each([

View file

@ -1,11 +1,11 @@
import { Mock } from 'ts-mockery';
import { fromPartial } from '@total-typescript/shoehorn';
import type { HttpClient } from '../../../src/common/services/HttpClient';
import { ImageDownloader } from '../../../src/common/services/ImageDownloader';
import { windowMock } from '../../__mocks__/Window.mock';
describe('ImageDownloader', () => {
const fetchBlob = jest.fn();
const httpClient = Mock.of<HttpClient>({ fetchBlob });
const httpClient = fromPartial<HttpClient>({ fetchBlob });
let imageDownloader: ImageDownloader;
beforeEach(() => {

View file

@ -1,17 +1,16 @@
import { render, screen } from '@testing-library/react';
import { Mock } from 'ts-mockery';
import { fromPartial } from '@total-typescript/shoehorn';
import type { ShlinkDomainRedirects } from '../../src/api/types';
import type { Domain } from '../../src/domains/data';
import { DomainRow } from '../../src/domains/DomainRow';
import type { SelectedServer } from '../../src/servers/data';
describe('<DomainRow />', () => {
const redirectsCombinations = [
[Mock.of<ShlinkDomainRedirects>({ baseUrlRedirect: 'foo' })],
[Mock.of<ShlinkDomainRedirects>({ invalidShortUrlRedirect: 'bar' })],
[Mock.of<ShlinkDomainRedirects>({ baseUrlRedirect: 'baz', regular404Redirect: 'foo' })],
[fromPartial<ShlinkDomainRedirects>({ baseUrlRedirect: 'foo' })],
[fromPartial<ShlinkDomainRedirects>({ invalidShortUrlRedirect: 'bar' })],
[fromPartial<ShlinkDomainRedirects>({ baseUrlRedirect: 'baz', regular404Redirect: 'foo' })],
[
Mock.of<ShlinkDomainRedirects>(
fromPartial<ShlinkDomainRedirects>(
{ baseUrlRedirect: 'baz', regular404Redirect: 'bar', invalidShortUrlRedirect: 'foo' },
),
],
@ -22,7 +21,7 @@ describe('<DomainRow />', () => {
<DomainRow
domain={domain}
defaultRedirects={defaultRedirects}
selectedServer={Mock.all<SelectedServer>()}
selectedServer={fromPartial({})}
editDomainRedirects={jest.fn()}
checkDomainHealth={jest.fn()}
/>
@ -31,7 +30,7 @@ describe('<DomainRow />', () => {
);
it.each(redirectsCombinations)('shows expected redirects', (redirects) => {
setUp(Mock.of<Domain>({ domain: '', isDefault: true, redirects }));
setUp(fromPartial({ domain: '', isDefault: true, redirects }));
const cells = screen.getAllByRole('cell');
redirects?.baseUrlRedirect && expect(cells[1]).toHaveTextContent(redirects.baseUrlRedirect);
@ -42,9 +41,9 @@ describe('<DomainRow />', () => {
it.each([
[undefined],
[Mock.of<ShlinkDomainRedirects>()],
[fromPartial<ShlinkDomainRedirects>({})],
])('shows expected "no redirects"', (redirects) => {
setUp(Mock.of<Domain>({ domain: '', isDefault: true, redirects }));
setUp(fromPartial({ domain: '', isDefault: true, redirects }));
const cells = screen.getAllByRole('cell');
expect(cells[1]).toHaveTextContent('No redirect');
@ -54,7 +53,7 @@ describe('<DomainRow />', () => {
});
it.each(redirectsCombinations)('shows expected fallback redirects', (fallbackRedirects) => {
setUp(Mock.of<Domain>({ domain: '', isDefault: true }), fallbackRedirects);
setUp(fromPartial({ domain: '', isDefault: true }), fallbackRedirects);
const cells = screen.getAllByRole('cell');
fallbackRedirects?.baseUrlRedirect && expect(cells[1]).toHaveTextContent(
@ -69,7 +68,7 @@ describe('<DomainRow />', () => {
});
it.each([[true], [false]])('shows icon on default domain only', (isDefault) => {
const { container } = setUp(Mock.of<Domain>({ domain: '', isDefault }));
const { container } = setUp(fromPartial({ domain: '', isDefault }));
if (isDefault) {
expect(container.querySelector('#defaultDomainIcon')).toBeInTheDocument();

View file

@ -1,16 +1,15 @@
import { screen, waitFor } from '@testing-library/react';
import { Mock } from 'ts-mockery';
import type { ShlinkDomain } from '../../src/api/types';
import { fromPartial } from '@total-typescript/shoehorn';
import { DomainSelector } from '../../src/domains/DomainSelector';
import type { DomainsList } from '../../src/domains/reducers/domainsList';
import { renderWithEvents } from '../__helpers__/setUpTest';
describe('<DomainSelector />', () => {
const domainsList = Mock.of<DomainsList>({
const domainsList = fromPartial<DomainsList>({
domains: [
Mock.of<ShlinkDomain>({ domain: 'default.com', isDefault: true }),
Mock.of<ShlinkDomain>({ domain: 'foo.com' }),
Mock.of<ShlinkDomain>({ domain: 'bar.com' }),
fromPartial({ domain: 'default.com', isDefault: true }),
fromPartial({ domain: 'foo.com' }),
fromPartial({ domain: 'bar.com' }),
],
});
const setUp = (value = '') => renderWithEvents(

View file

@ -1,10 +1,9 @@
import { screen, waitFor } from '@testing-library/react';
import { Mock } from 'ts-mockery';
import { fromPartial } from '@total-typescript/shoehorn';
import type { ShlinkDomain } from '../../src/api/types';
import type { ProblemDetailsError } from '../../src/api/types/errors';
import { ManageDomains } from '../../src/domains/ManageDomains';
import type { DomainsList } from '../../src/domains/reducers/domainsList';
import type { SelectedServer } from '../../src/servers/data';
import { renderWithEvents } from '../__helpers__/setUpTest';
describe('<ManageDomains />', () => {
@ -17,14 +16,14 @@ describe('<ManageDomains />', () => {
editDomainRedirects={jest.fn()}
checkDomainHealth={jest.fn()}
domainsList={domainsList}
selectedServer={Mock.all<SelectedServer>()}
selectedServer={fromPartial({})}
/>,
);
afterEach(jest.clearAllMocks);
it('shows loading message while domains are loading', () => {
setUp(Mock.of<DomainsList>({ loading: true, filteredDomains: [] }));
setUp(fromPartial({ loading: true, filteredDomains: [] }));
expect(screen.getByText('Loading...')).toBeInTheDocument();
expect(screen.queryByText('Error loading domains :(')).not.toBeInTheDocument();
@ -32,17 +31,17 @@ describe('<ManageDomains />', () => {
it.each([
[undefined, 'Error loading domains :('],
[Mock.of<ProblemDetailsError>(), 'Error loading domains :('],
[Mock.of<ProblemDetailsError>({ detail: 'Foo error!!' }), 'Foo error!!'],
[fromPartial<ProblemDetailsError>({}), 'Error loading domains :('],
[fromPartial<ProblemDetailsError>({ detail: 'Foo error!!' }), 'Foo error!!'],
])('shows error result when domains loading fails', (errorData, expectedErrorMessage) => {
setUp(Mock.of<DomainsList>({ loading: false, error: true, errorData, filteredDomains: [] }));
setUp(fromPartial({ loading: false, error: true, errorData, filteredDomains: [] }));
expect(screen.queryByText('Loading...')).not.toBeInTheDocument();
expect(screen.getByText(expectedErrorMessage)).toBeInTheDocument();
});
it('filters domains when SearchField changes', async () => {
const { user } = setUp(Mock.of<DomainsList>({ loading: false, error: false, filteredDomains: [] }));
const { user } = setUp(fromPartial({ loading: false, error: false, filteredDomains: [] }));
expect(filterDomains).not.toHaveBeenCalled();
await user.type(screen.getByPlaceholderText('Search...'), 'Foo');
@ -50,19 +49,19 @@ describe('<ManageDomains />', () => {
});
it('shows expected headers and one row when list of domains is empty', () => {
setUp(Mock.of<DomainsList>({ loading: false, error: false, filteredDomains: [] }));
setUp(fromPartial({ loading: false, error: false, filteredDomains: [] }));
expect(screen.getAllByRole('columnheader')).toHaveLength(7);
expect(screen.getByText('No results found')).toBeInTheDocument();
});
it('has many rows if multiple domains are provided', () => {
const filteredDomains = [
Mock.of<ShlinkDomain>({ domain: 'foo' }),
Mock.of<ShlinkDomain>({ domain: 'bar' }),
Mock.of<ShlinkDomain>({ domain: 'baz' }),
const filteredDomains: ShlinkDomain[] = [
fromPartial({ domain: 'foo' }),
fromPartial({ domain: 'bar' }),
fromPartial({ domain: 'baz' }),
];
setUp(Mock.of<DomainsList>({ loading: false, error: false, filteredDomains }));
setUp(fromPartial({ loading: false, error: false, filteredDomains }));
expect(screen.getAllByRole('row')).toHaveLength(filteredDomains.length + 1);
expect(screen.getByText('foo')).toBeInTheDocument();

View file

@ -1,9 +1,9 @@
import { screen, waitForElementToBeRemoved } from '@testing-library/react';
import { fromPartial } from '@total-typescript/shoehorn';
import { MemoryRouter } from 'react-router-dom';
import { Mock } from 'ts-mockery';
import type { Domain } from '../../../src/domains/data';
import { DomainDropdown } from '../../../src/domains/helpers/DomainDropdown';
import type { ReachableServer, SelectedServer } from '../../../src/servers/data';
import type { SelectedServer } from '../../../src/servers/data';
import type { SemVer } from '../../../src/utils/helpers/version';
import { renderWithEvents } from '../../__helpers__/setUpTest';
@ -12,8 +12,8 @@ describe('<DomainDropdown />', () => {
const setUp = (domain?: Domain, selectedServer?: SelectedServer) => renderWithEvents(
<MemoryRouter>
<DomainDropdown
domain={domain ?? Mock.all<Domain>()}
selectedServer={selectedServer ?? Mock.all<SelectedServer>()}
domain={domain ?? fromPartial({})}
selectedServer={selectedServer ?? fromPartial({})}
editDomainRedirects={editDomainRedirects}
/>
</MemoryRouter>,
@ -33,8 +33,8 @@ describe('<DomainDropdown />', () => {
[false, ''],
])('points first link to the proper section', (isDefault, expectedLink) => {
setUp(
Mock.of<Domain>({ domain: 'foo.com', isDefault }),
Mock.of<ReachableServer>({ version: '3.1.0', id: '123' }),
fromPartial({ domain: 'foo.com', isDefault }),
fromPartial({ version: '3.1.0', id: '123' }),
);
expect(screen.getByText('Visit stats')).toHaveAttribute('href', `/server/123/domain/foo.com${expectedLink}/visits`);
@ -46,8 +46,8 @@ describe('<DomainDropdown />', () => {
[false, '2.9.0' as SemVer, true],
])('allows editing certain the domains', (isDefault, serverVersion, canBeEdited) => {
setUp(
Mock.of<Domain>({ domain: 'foo.com', isDefault }),
Mock.of<ReachableServer>({ version: serverVersion, id: '123' }),
fromPartial({ domain: 'foo.com', isDefault }),
fromPartial({ version: serverVersion, id: '123' }),
);
if (canBeEdited) {
@ -62,7 +62,7 @@ describe('<DomainDropdown />', () => {
['bar.org'],
['baz.net'],
])('displays modal when editing redirects', async (domain) => {
const { user } = setUp(Mock.of<Domain>({ domain, isDefault: false }));
const { user } = setUp(fromPartial({ domain, isDefault: false }));
expect(screen.queryByRole('dialog')).not.toBeInTheDocument();
expect(screen.queryByRole('form')).not.toBeInTheDocument();

View file

@ -1,11 +1,11 @@
import { screen } from '@testing-library/react';
import { Mock } from 'ts-mockery';
import { fromPartial } from '@total-typescript/shoehorn';
import type { DomainStatus } from '../../../src/domains/data';
import { DomainStatusIcon } from '../../../src/domains/helpers/DomainStatusIcon';
import { renderWithEvents } from '../../__helpers__/setUpTest';
describe('<DomainStatusIcon />', () => {
const matchMedia = jest.fn().mockReturnValue(Mock.of<MediaQueryList>({ matches: false }));
const matchMedia = jest.fn().mockReturnValue(fromPartial<MediaQueryList>({ matches: false }));
const setUp = (status: DomainStatus) => renderWithEvents(
<DomainStatusIcon status={status} matchMedia={matchMedia} />,
);

View file

@ -1,5 +1,5 @@
import { fireEvent, screen, waitFor } from '@testing-library/react';
import { Mock } from 'ts-mockery';
import { fromPartial } from '@total-typescript/shoehorn';
import type { ShlinkDomain } from '../../../src/api/types';
import { EditDomainRedirectsModal } from '../../../src/domains/helpers/EditDomainRedirectsModal';
import { renderWithEvents } from '../../__helpers__/setUpTest';
@ -7,7 +7,7 @@ import { renderWithEvents } from '../../__helpers__/setUpTest';
describe('<EditDomainRedirectsModal />', () => {
const editDomainRedirects = jest.fn().mockResolvedValue(undefined);
const toggle = jest.fn();
const domain = Mock.of<ShlinkDomain>({
const domain = fromPartial<ShlinkDomain>({
domain: 'foo.com',
redirects: {
baseUrlRedirect: 'baz',

View file

@ -1,7 +1,6 @@
import { Mock } from 'ts-mockery';
import { fromPartial } from '@total-typescript/shoehorn';
import type { ShlinkApiClient } from '../../../src/api/services/ShlinkApiClient';
import type { ShlinkDomainRedirects } from '../../../src/api/types';
import type { EditDomainRedirects } from '../../../src/domains/reducers/domainRedirects';
import { editDomainRedirects } from '../../../src/domains/reducers/domainRedirects';
describe('domainRedirectsReducer', () => {
@ -9,17 +8,17 @@ describe('domainRedirectsReducer', () => {
describe('editDomainRedirects', () => {
const domain = 'example.com';
const redirects = Mock.all<ShlinkDomainRedirects>();
const redirects = fromPartial<ShlinkDomainRedirects>({});
const dispatch = jest.fn();
const getState = jest.fn();
const editDomainRedirectsCall = jest.fn();
const buildShlinkApiClient = () => Mock.of<ShlinkApiClient>({ editDomainRedirects: editDomainRedirectsCall });
const buildShlinkApiClient = () => fromPartial<ShlinkApiClient>({ editDomainRedirects: editDomainRedirectsCall });
const editDomainRedirectsAction = editDomainRedirects(buildShlinkApiClient);
it('dispatches domain and redirects once loaded', async () => {
editDomainRedirectsCall.mockResolvedValue(redirects);
await editDomainRedirectsAction(Mock.of<EditDomainRedirects>({ domain }))(dispatch, getState, {});
await editDomainRedirectsAction(fromPartial({ domain }))(dispatch, getState, {});
expect(dispatch).toHaveBeenCalledTimes(2);
expect(dispatch).toHaveBeenLastCalledWith(expect.objectContaining({

View file

@ -1,4 +1,4 @@
import { Mock } from 'ts-mockery';
import { fromPartial } from '@total-typescript/shoehorn';
import type { ShlinkApiClient } from '../../../src/api/services/ShlinkApiClient';
import type { ShlinkDomainRedirects } from '../../../src/api/types';
import { parseApiError } from '../../../src/api/utils';
@ -6,26 +6,23 @@ import type { ShlinkState } from '../../../src/container/types';
import type { Domain } from '../../../src/domains/data';
import type { EditDomainRedirects } from '../../../src/domains/reducers/domainRedirects';
import { editDomainRedirects } from '../../../src/domains/reducers/domainRedirects';
import type {
DomainsList } from '../../../src/domains/reducers/domainsList';
import {
domainsListReducerCreator,
replaceRedirectsOnDomain,
replaceStatusOnDomain,
} from '../../../src/domains/reducers/domainsList';
import type { SelectedServer, ServerData } from '../../../src/servers/data';
describe('domainsListReducer', () => {
const dispatch = jest.fn();
const getState = jest.fn();
const listDomains = jest.fn();
const health = jest.fn();
const buildShlinkApiClient = () => Mock.of<ShlinkApiClient>({ listDomains, health });
const filteredDomains = [
Mock.of<Domain>({ domain: 'foo', status: 'validating' }),
Mock.of<Domain>({ domain: 'Boo', status: 'validating' }),
const buildShlinkApiClient = () => fromPartial<ShlinkApiClient>({ listDomains, health });
const filteredDomains: Domain[] = [
fromPartial({ domain: 'foo', status: 'validating' }),
fromPartial({ domain: 'Boo', status: 'validating' }),
];
const domains = [...filteredDomains, Mock.of<Domain>({ domain: 'bar', 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 { reducer, listDomains: listDomainsAction, checkDomainHealth, filterDomains } = domainsListReducerCreator(
@ -55,7 +52,7 @@ describe('domainsListReducer', () => {
});
it('filters domains on FILTER_DOMAINS', () => {
expect(reducer(Mock.of<DomainsList>({ domains }), filterDomains('oO'))).toEqual({ domains, filteredDomains });
expect(reducer(fromPartial({ domains }), filterDomains('oO'))).toEqual({ domains, filteredDomains });
});
it.each([
@ -71,7 +68,7 @@ describe('domainsListReducer', () => {
const editDomainRedirects: EditDomainRedirects = { domain, redirects };
expect(reducer(
Mock.of<DomainsList>({ domains, filteredDomains }),
fromPartial({ domains, filteredDomains }),
editDomainRedirectsThunk.fulfilled(editDomainRedirects, '', editDomainRedirects),
)).toEqual({
domains: domains.map(replaceRedirectsOnDomain(editDomainRedirects)),
@ -85,7 +82,7 @@ describe('domainsListReducer', () => {
['does_not_exist'],
])('replaces status on proper domain on VALIDATE_DOMAIN', (domain) => {
expect(reducer(
Mock.of<DomainsList>({ domains, filteredDomains }),
fromPartial({ domains, filteredDomains }),
checkDomainHealth.fulfilled({ domain, status: 'valid' }, '', ''),
)).toEqual({
domains: domains.map(replaceStatusOnDomain(domain, 'valid')),
@ -122,8 +119,8 @@ describe('domainsListReducer', () => {
const domain = 'example.com';
it('dispatches invalid status when selected server does not have all required data', async () => {
getState.mockReturnValue(Mock.of<ShlinkState>({
selectedServer: Mock.all<SelectedServer>(),
getState.mockReturnValue(fromPartial<ShlinkState>({
selectedServer: {},
}));
await checkDomainHealth(domain)(dispatch, getState, {});
@ -136,11 +133,11 @@ describe('domainsListReducer', () => {
});
it('dispatches invalid status when health endpoint returns an error', async () => {
getState.mockReturnValue(Mock.of<ShlinkState>({
selectedServer: Mock.of<ServerData>({
getState.mockReturnValue(fromPartial<ShlinkState>({
selectedServer: {
url: 'https://myerver.com',
apiKey: '123',
}),
},
}));
health.mockRejectedValue({});
@ -160,11 +157,11 @@ describe('domainsListReducer', () => {
healthStatus,
expectedStatus,
) => {
getState.mockReturnValue(Mock.of<ShlinkState>({
selectedServer: Mock.of<ServerData>({
getState.mockReturnValue(fromPartial<ShlinkState>({
selectedServer: {
url: 'https://myerver.com',
apiKey: '123',
}),
},
}));
health.mockResolvedValue({ status: healthStatus });

View file

@ -1,6 +1,6 @@
import { fromPartial } from '@total-typescript/shoehorn';
import { EventSourcePolyfill } from 'event-source-polyfill';
import { identity } from 'ramda';
import { Mock } from 'ts-mockery';
import { bindToMercureTopic } from '../../../src/mercure/helpers';
import type { MercureInfo } from '../../../src/mercure/reducers/mercureInfo';
@ -14,11 +14,11 @@ describe('helpers', () => {
const onTokenExpired = jest.fn();
it.each([
[Mock.of<MercureInfo>({ loading: true, error: false, mercureHubUrl: 'foo' })],
[Mock.of<MercureInfo>({ loading: false, error: true, mercureHubUrl: 'foo' })],
[Mock.of<MercureInfo>({ loading: true, error: true, mercureHubUrl: 'foo' })],
[Mock.of<MercureInfo>({ loading: false, error: false, mercureHubUrl: undefined })],
[Mock.of<MercureInfo>({ loading: true, error: true, mercureHubUrl: undefined })],
[fromPartial<MercureInfo>({ loading: true, error: false, mercureHubUrl: 'foo' })],
[fromPartial<MercureInfo>({ loading: false, error: true, mercureHubUrl: 'foo' })],
[fromPartial<MercureInfo>({ loading: true, error: true, mercureHubUrl: 'foo' })],
[fromPartial<MercureInfo>({ loading: false, error: false, mercureHubUrl: undefined })],
[fromPartial<MercureInfo>({ loading: true, error: true, mercureHubUrl: undefined })],
])('does not bind an EventSource when loading, error or no hub URL', (mercureInfo) => {
bindToMercureTopic(mercureInfo, [''], identity, () => {});

View file

@ -1,4 +1,4 @@
import { Mock } from 'ts-mockery';
import { fromPartial } from '@total-typescript/shoehorn';
import type { ShlinkApiClient } from '../../../src/api/services/ShlinkApiClient';
import type { GetState } from '../../../src/container/types';
import { mercureInfoReducerCreator } from '../../../src/mercure/reducers/mercureInfo';
@ -9,7 +9,7 @@ describe('mercureInfoReducer', () => {
token: 'abc.123.def',
};
const getMercureInfo = jest.fn();
const buildShlinkApiClient = () => Mock.of<ShlinkApiClient>({ mercureInfo: getMercureInfo });
const buildShlinkApiClient = () => fromPartial<ShlinkApiClient>({ mercureInfo: getMercureInfo });
const { loadMercureInfo, reducer } = mercureInfoReducerCreator(buildShlinkApiClient);
beforeEach(jest.resetAllMocks);

View file

@ -1,6 +1,6 @@
import { fireEvent, screen, waitFor } from '@testing-library/react';
import { fromPartial } from '@total-typescript/shoehorn';
import { useNavigate } from 'react-router-dom';
import { Mock } from 'ts-mockery';
import { CreateServer as createCreateServer } from '../../src/servers/CreateServer';
import type { ServerWithId } from '../../src/servers/data';
import { renderWithEvents } from '../__helpers__/setUpTest';
@ -10,7 +10,7 @@ jest.mock('react-router-dom', () => ({ ...jest.requireActual('react-router-dom')
describe('<CreateServer />', () => {
const createServersMock = jest.fn();
const navigate = jest.fn();
const servers = { foo: Mock.of<ServerWithId>({ url: 'https://existing_url.com', apiKey: 'existing_api_key' }) };
const servers = { foo: fromPartial<ServerWithId>({ url: 'https://existing_url.com', apiKey: 'existing_api_key' }) };
const setUp = (serversImported = false, importFailed = false) => {
(useNavigate as any).mockReturnValue(navigate);

View file

@ -1,7 +1,6 @@
import { screen, waitFor } from '@testing-library/react';
import { fromPartial } from '@total-typescript/shoehorn';
import type { ReactNode } from 'react';
import { Mock } from 'ts-mockery';
import type { ServerWithId } from '../../src/servers/data';
import { DeleteServerButton as createDeleteServerButton } from '../../src/servers/DeleteServerButton';
import { renderWithEvents } from '../__helpers__/setUpTest';
@ -10,7 +9,7 @@ describe('<DeleteServerButton />', () => {
({ isOpen }) => <>DeleteServerModal {isOpen ? '[Open]' : '[Closed]'}</>,
);
const setUp = (children?: ReactNode) => renderWithEvents(
<DeleteServerButton server={Mock.all<ServerWithId>()} textClassName="button">{children}</DeleteServerButton>,
<DeleteServerButton server={fromPartial({})} textClassName="button">{children}</DeleteServerButton>,
);
it.each([

View file

@ -1,7 +1,6 @@
import { screen, waitFor } from '@testing-library/react';
import { fromPartial } from '@total-typescript/shoehorn';
import { useNavigate } from 'react-router-dom';
import { Mock } from 'ts-mockery';
import type { ServerWithId } from '../../src/servers/data';
import { DeleteServerModal } from '../../src/servers/DeleteServerModal';
import { renderWithEvents } from '../__helpers__/setUpTest';
import { TestModalWrapper } from '../__helpers__/TestModalWrapper';
@ -20,7 +19,7 @@ describe('<DeleteServerModal />', () => {
renderModal={(args) => (
<DeleteServerModal
{...args}
server={Mock.of<ServerWithId>({ name: serverName })}
server={fromPartial({ name: serverName })}
deleteServer={deleteServerMock}
/>
)}

View file

@ -1,6 +1,6 @@
import { fireEvent, screen } from '@testing-library/react';
import { fromPartial } from '@total-typescript/shoehorn';
import { MemoryRouter, useNavigate } from 'react-router-dom';
import { Mock } from 'ts-mockery';
import type { ReachableServer, SelectedServer } from '../../src/servers/data';
import { EditServer as editServerConstruct } from '../../src/servers/EditServer';
import { renderWithEvents } from '../__helpers__/setUpTest';
@ -11,7 +11,7 @@ describe('<EditServer />', () => {
const ServerError = jest.fn();
const editServerMock = jest.fn();
const navigate = jest.fn();
const defaultSelectedServer = Mock.of<ReachableServer>({
const defaultSelectedServer = fromPartial<ReachableServer>({
id: 'abc123',
name: 'the_name',
url: 'the_url',
@ -31,7 +31,7 @@ describe('<EditServer />', () => {
afterEach(jest.clearAllMocks);
it('renders nothing if selected server is not reachable', () => {
setUp(Mock.all<SelectedServer>());
setUp(fromPartial<SelectedServer>({}));
expect(screen.queryByText('Edit')).not.toBeInTheDocument();
expect(screen.queryByText('Cancel')).not.toBeInTheDocument();

View file

@ -1,6 +1,6 @@
import { screen, waitFor } from '@testing-library/react';
import { fromPartial } from '@total-typescript/shoehorn';
import { MemoryRouter } from 'react-router-dom';
import { Mock } from 'ts-mockery';
import type { ServersMap, ServerWithId } from '../../src/servers/data';
import { ManageServers as createManageServers } from '../../src/servers/ManageServers';
import type { ServersExporter } from '../../src/servers/services/ServersExporter';
@ -8,7 +8,7 @@ import { renderWithEvents } from '../__helpers__/setUpTest';
describe('<ManageServers />', () => {
const exportServers = jest.fn();
const serversExporter = Mock.of<ServersExporter>({ exportServers });
const serversExporter = fromPartial<ServersExporter>({ exportServers });
const useTimeoutToggle = jest.fn().mockReturnValue([false, jest.fn()]);
const ManageServers = createManageServers(
serversExporter,
@ -16,7 +16,7 @@ describe('<ManageServers />', () => {
useTimeoutToggle,
({ hasAutoConnect }) => <tr><td>ManageServersRow {hasAutoConnect ? '[YES]' : '[NO]'}</td></tr>,
);
const createServerMock = (value: string, autoConnect = false) => Mock.of<ServerWithId>(
const createServerMock = (value: string, autoConnect = false) => fromPartial<ServerWithId>(
{ id: value, name: value, url: value, autoConnect },
);
const setUp = (servers: ServersMap = {}) => renderWithEvents(

View file

@ -1,6 +1,6 @@
import { screen } from '@testing-library/react';
import { fromPartial } from '@total-typescript/shoehorn';
import { MemoryRouter } from 'react-router-dom';
import { Mock } from 'ts-mockery';
import type { ServerWithId } from '../../src/servers/data';
import { ManageServersRowDropdown as createManageServersRowDropdown } from '../../src/servers/ManageServersRowDropdown';
import { renderWithEvents } from '../__helpers__/setUpTest';
@ -11,7 +11,7 @@ describe('<ManageServersRowDropdown />', () => {
);
const setAutoConnect = jest.fn();
const setUp = (autoConnect = false) => {
const server = Mock.of<ServerWithId>({ id: 'abc123', autoConnect });
const server = fromPartial<ServerWithId>({ id: 'abc123', autoConnect });
return renderWithEvents(
<MemoryRouter>
<ManageServersRowDropdown setAutoConnect={setAutoConnect} server={server} />

View file

@ -1,14 +1,9 @@
import { screen, waitFor } from '@testing-library/react';
import { fromPartial } from '@total-typescript/shoehorn';
import { MemoryRouter } from 'react-router-dom';
import { Mock } from 'ts-mockery';
import type { MercureInfo } from '../../src/mercure/reducers/mercureInfo';
import type { ReachableServer } from '../../src/servers/data';
import { Overview as overviewCreator } from '../../src/servers/Overview';
import type { Settings } from '../../src/settings/reducers/settings';
import type { ShortUrlsList as ShortUrlsListState } from '../../src/short-urls/reducers/shortUrlsList';
import type { TagsList } from '../../src/tags/reducers/tagsList';
import { prettify } from '../../src/utils/helpers/numbers';
import type { VisitsOverview } from '../../src/visits/reducers/visitsOverview';
import { renderWithEvents } from '../__helpers__/setUpTest';
describe('<Overview />', () => {
@ -28,18 +23,18 @@ describe('<Overview />', () => {
listShortUrls={listShortUrls}
listTags={listTags}
loadVisitsOverview={loadVisitsOverview}
shortUrlsList={Mock.of<ShortUrlsListState>({ loading, shortUrls })}
tagsList={Mock.of<TagsList>({ loading, tags: ['foo', 'bar', 'baz'] })}
visitsOverview={Mock.of<VisitsOverview>({
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={Mock.of<ReachableServer>({ id: serverId })}
selectedServer={fromPartial({ id: serverId })}
createNewVisits={jest.fn()}
loadMercureInfo={jest.fn()}
mercureInfo={Mock.all<MercureInfo>()}
settings={Mock.of<Settings>({ visits: { excludeBots } })}
mercureInfo={fromPartial<MercureInfo>({})}
settings={fromPartial({ visits: { excludeBots } })}
/>
</MemoryRouter>,
);

View file

@ -1,16 +1,16 @@
import { screen } from '@testing-library/react';
import { fromPartial } from '@total-typescript/shoehorn';
import { values } from 'ramda';
import { MemoryRouter } from 'react-router-dom';
import { Mock } from 'ts-mockery';
import type { ServersMap, ServerWithId } from '../../src/servers/data';
import type { ServersMap } from '../../src/servers/data';
import { ServersDropdown } from '../../src/servers/ServersDropdown';
import { renderWithEvents } from '../__helpers__/setUpTest';
describe('<ServersDropdown />', () => {
const fallbackServers: ServersMap = {
'1a': Mock.of<ServerWithId>({ name: 'foo', id: '1a' }),
'2b': Mock.of<ServerWithId>({ name: 'bar', id: '2b' }),
'3c': Mock.of<ServerWithId>({ name: 'baz', id: '3c' }),
'1a': fromPartial({ name: 'foo', id: '1a' }),
'2b': fromPartial({ name: 'bar', id: '2b' }),
'3c': fromPartial({ name: 'baz', id: '3c' }),
};
const setUp = (servers: ServersMap = fallbackServers) => renderWithEvents(
<MemoryRouter><ServersDropdown servers={servers} selectedServer={null} /></MemoryRouter>,

View file

@ -1,13 +1,13 @@
import { render, screen } from '@testing-library/react';
import { fromPartial } from '@total-typescript/shoehorn';
import { MemoryRouter } from 'react-router-dom';
import { Mock } from 'ts-mockery';
import type { ServerWithId } from '../../src/servers/data';
import { ServersListGroup } from '../../src/servers/ServersListGroup';
describe('<ServersListGroup />', () => {
const servers = [
Mock.of<ServerWithId>({ name: 'foo', id: '123' }),
Mock.of<ServerWithId>({ name: 'bar', id: '456' }),
const servers: ServerWithId[] = [
fromPartial({ name: 'foo', id: '123' }),
fromPartial({ name: 'bar', id: '456' }),
];
const setUp = (params: { servers?: ServerWithId[]; withChildren?: boolean; embedded?: boolean }) => {
const { servers = [], withChildren = true, embedded } = params;

View file

@ -1,5 +1,5 @@
import { screen } from '@testing-library/react';
import { Mock } from 'ts-mockery';
import { fromPartial } from '@total-typescript/shoehorn';
import type { ServerData } from '../../../src/servers/data';
import { DuplicatedServersModal } from '../../../src/servers/helpers/DuplicatedServersModal';
import { renderWithEvents } from '../../__helpers__/setUpTest';
@ -10,15 +10,16 @@ describe('<DuplicatedServersModal />', () => {
const setUp = (duplicatedServers: ServerData[] = []) => renderWithEvents(
<DuplicatedServersModal isOpen duplicatedServers={duplicatedServers} onDiscard={onDiscard} onSave={onSave} />,
);
const mockServer = (data: Partial<ServerData> = {}) => fromPartial<ServerData>(data);
beforeEach(jest.clearAllMocks);
it.each([
[[], 0],
[[Mock.all<ServerData>()], 2],
[[Mock.all<ServerData>(), Mock.all<ServerData>()], 2],
[[Mock.all<ServerData>(), Mock.all<ServerData>(), Mock.all<ServerData>()], 3],
[[Mock.all<ServerData>(), Mock.all<ServerData>(), Mock.all<ServerData>(), Mock.all<ServerData>()], 4],
[[mockServer()], 2],
[[mockServer(), mockServer()], 2],
[[mockServer(), mockServer(), mockServer()], 3],
[[mockServer(), mockServer(), mockServer(), mockServer()], 4],
])('renders expected amount of items', (duplicatedServers, expectedItems) => {
setUp(duplicatedServers);
expect(screen.queryAllByRole('listitem')).toHaveLength(expectedItems);
@ -26,7 +27,7 @@ describe('<DuplicatedServersModal />', () => {
it.each([
[
[Mock.all<ServerData>()],
[mockServer()],
{
header: 'Duplicated server',
firstParagraph: 'There is already a server with:',
@ -35,7 +36,7 @@ describe('<DuplicatedServersModal />', () => {
},
],
[
[Mock.all<ServerData>(), Mock.all<ServerData>()],
[mockServer(), mockServer()],
{
header: 'Duplicated servers',
firstParagraph: 'The next servers already exist:',
@ -54,10 +55,10 @@ describe('<DuplicatedServersModal />', () => {
it.each([
[[]],
[[Mock.of<ServerData>({ url: 'url', apiKey: 'apiKey' })]],
[[mockServer({ url: 'url', apiKey: 'apiKey' })]],
[[
Mock.of<ServerData>({ url: 'url_1', apiKey: 'apiKey_1' }),
Mock.of<ServerData>({ url: 'url_2', apiKey: 'apiKey_2' }),
mockServer({ url: 'url_1', apiKey: 'apiKey_1' }),
mockServer({ url: 'url_2', apiKey: 'apiKey_2' }),
]],
])('displays provided server data', (duplicatedServers) => {
setUp(duplicatedServers);

View file

@ -1,5 +1,5 @@
import { fireEvent, screen, waitFor } from '@testing-library/react';
import { Mock } from 'ts-mockery';
import { fromPartial } from '@total-typescript/shoehorn';
import type { ServersMap, ServerWithId } from '../../../src/servers/data';
import type {
ImportServersBtnProps } from '../../../src/servers/helpers/ImportServersBtn';
@ -13,7 +13,7 @@ describe('<ImportServersBtn />', () => {
const onImportMock = jest.fn();
const createServersMock = jest.fn();
const importServersFromFile = jest.fn().mockResolvedValue([]);
const serversImporterMock = Mock.of<ServersImporter>({ importServersFromFile });
const serversImporterMock = fromPartial<ServersImporter>({ importServersFromFile });
const ImportServersBtn = createImportServersBtn(serversImporterMock);
const setUp = (props: Partial<ImportServersBtnProps> = {}, servers: ServersMap = {}) => renderWithEvents(
<ImportServersBtn
@ -67,8 +67,8 @@ describe('<ImportServersBtn />', () => {
['Save anyway', true],
['Discard', false],
])('creates expected servers depending on selected option in modal', async (btnName, savesDuplicatedServers) => {
const existingServer = Mock.of<ServerWithId>({ id: 'abc', url: 'existingUrl', apiKey: 'existingApiKey' });
const newServer = Mock.of<ServerWithId>({ url: 'newUrl', apiKey: 'newApiKey' });
const existingServer = fromPartial<ServerWithId>({ id: 'abc', url: 'existingUrl', apiKey: 'existingApiKey' });
const newServer = fromPartial<ServerWithId>({ url: 'newUrl', apiKey: 'newApiKey' });
const { container, user } = setUp({}, { abc: existingServer });
const input = container.querySelector('[type=file]');
importServersFromFile.mockResolvedValue([existingServer, newServer]);

View file

@ -1,6 +1,6 @@
import { render, screen } from '@testing-library/react';
import { fromPartial } from '@total-typescript/shoehorn';
import { MemoryRouter } from 'react-router-dom';
import { Mock } from 'ts-mockery';
import type { NonReachableServer, NotFoundServer } from '../../../src/servers/data';
import { ServerError as createServerError } from '../../../src/servers/helpers/ServerError';
@ -9,7 +9,7 @@ describe('<ServerError />', () => {
it.each([
[
Mock.all<NotFoundServer>(),
fromPartial<NotFoundServer>({}),
{
found: ['Could not find this Shlink server.'],
notFound: [
@ -20,7 +20,7 @@ describe('<ServerError />', () => {
},
],
[
Mock.of<NonReachableServer>({ id: 'abc123' }),
fromPartial<NonReachableServer>({ id: 'abc123' }),
{
found: [
'Oops! Could not connect to this Shlink server.',

View file

@ -1,4 +1,4 @@
import { Mock } from 'ts-mockery';
import { fromPartial } from '@total-typescript/shoehorn';
import type { HttpClient } from '../../../src/common/services/HttpClient';
import { fetchServers } from '../../../src/servers/reducers/remoteServers';
@ -8,7 +8,7 @@ describe('remoteServersReducer', () => {
describe('fetchServers', () => {
const dispatch = jest.fn();
const fetchJson = jest.fn();
const httpClient = Mock.of<HttpClient>({ fetchJson });
const httpClient = fromPartial<HttpClient>({ fetchJson });
it.each([
[

View file

@ -1,4 +1,4 @@
import { Mock } from 'ts-mockery';
import { fromPartial } from '@total-typescript/shoehorn';
import { v4 as uuid } from 'uuid';
import type { ShlinkApiClient } from '../../../src/api/services/ShlinkApiClient';
import type { ShlinkState } from '../../../src/container/types';
@ -15,7 +15,7 @@ import {
describe('selectedServerReducer', () => {
const dispatch = jest.fn();
const health = jest.fn();
const buildApiClient = jest.fn().mockReturnValue(Mock.of<ShlinkApiClient>({ health }));
const buildApiClient = jest.fn().mockReturnValue(fromPartial<ShlinkApiClient>({ health }));
const selectServer = selectServerCreator(buildApiClient);
const { reducer } = selectedServerReducerCreator(selectServer);
@ -26,8 +26,7 @@ describe('selectedServerReducer', () => {
expect(reducer(null, resetSelectedServer())).toBeNull());
it('returns selected server when action is SELECT_SERVER', () => {
const payload = Mock.of<RegularServer>({ id: 'abc123' });
const payload = fromPartial<RegularServer>({ id: 'abc123' });
expect(reducer(null, selectServer.fulfilled(payload, '', ''))).toEqual(payload);
});
});
@ -66,7 +65,7 @@ describe('selectedServerReducer', () => {
it('dispatches error when health endpoint fails', async () => {
const id = uuid();
const getState = createGetStateMock(id);
const expectedSelectedServer = Mock.of<NonReachableServer>({ id, serverNotReachable: true });
const expectedSelectedServer = fromPartial<NonReachableServer>({ id, serverNotReachable: true });
health.mockRejectedValue({});
@ -78,7 +77,7 @@ describe('selectedServerReducer', () => {
it('dispatches error when server is not found', async () => {
const id = uuid();
const getState = jest.fn(() => Mock.of<ShlinkState>({ servers: {} }));
const getState = jest.fn(() => fromPartial<ShlinkState>({ servers: {} }));
const expectedSelectedServer: NotFoundServer = { serverNotFound: true };
await selectServer(id)(dispatch, getState, {});
@ -95,9 +94,9 @@ describe('selectedServerReducer', () => {
const { middleware } = selectServerListener(selectServer, loadMercureInfo);
it.each([
[Mock.of<ReachableServer>({ version: '1.2.3' }), 1],
[Mock.of<NotFoundServer>({ serverNotFound: true }), 0],
[Mock.of<NonReachableServer>({ serverNotReachable: true }), 0],
[fromPartial<ReachableServer>({ version: '1.2.3' }), 1],
[fromPartial<NotFoundServer>({ serverNotFound: true }), 0],
[fromPartial<NonReachableServer>({ serverNotReachable: true }), 0],
])('dispatches loadMercureInfo when provided server is reachable', (payload, expectedCalls) => {
middleware({ dispatch, getState })(jest.fn())({
payload,
@ -110,7 +109,7 @@ describe('selectedServerReducer', () => {
it('does not dispatch loadMercureInfo when action is not of the proper type', () => {
middleware({ dispatch, getState })(jest.fn())({
payload: Mock.of<ReachableServer>({ version: '1.2.3' }),
payload: fromPartial<ReachableServer>({ version: '1.2.3' }),
type: 'something_else',
});

View file

@ -1,6 +1,6 @@
import { fromPartial } from '@total-typescript/shoehorn';
import { dissoc, values } from 'ramda';
import { Mock } from 'ts-mockery';
import type { RegularServer, ServerWithId } from '../../../src/servers/data';
import type { RegularServer, ServersMap, ServerWithId } from '../../../src/servers/data';
import {
createServers,
deleteServer,
@ -10,9 +10,9 @@ import {
} from '../../../src/servers/reducers/servers';
describe('serversReducer', () => {
const list = {
abc123: Mock.of<RegularServer>({ id: 'abc123' }),
def456: Mock.of<RegularServer>({ id: 'def456' }),
const list: ServersMap = {
abc123: fromPartial({ id: 'abc123' }),
def456: fromPartial({ id: 'def456' }),
};
afterEach(jest.clearAllMocks);
@ -31,12 +31,12 @@ describe('serversReducer', () => {
}));
it('removes server when action is DELETE_SERVER', () =>
expect(serversReducer(list, deleteServer(Mock.of<ServerWithId>({ id: 'abc123' })))).toEqual({
expect(serversReducer(list, deleteServer(fromPartial<ServerWithId>({ id: 'abc123' })))).toEqual({
def456: { id: 'def456' },
}));
it('appends server when action is CREATE_SERVERS', () =>
expect(serversReducer(list, createServers([Mock.of<ServerWithId>({ id: 'ghi789' })]))).toEqual({
expect(serversReducer(list, createServers([fromPartial<ServerWithId>({ id: 'ghi789' })]))).toEqual({
abc123: { id: 'abc123' },
def456: { id: 'def456' },
ghi789: { id: 'ghi789' },
@ -46,7 +46,7 @@ describe('serversReducer', () => {
[true],
[false],
])('returns state as it is when trying to set auto-connect on invalid server', (autoConnect) =>
expect(serversReducer(list, setAutoConnect(Mock.of<ServerWithId>({ id: 'invalid' }), autoConnect))).toEqual({
expect(serversReducer(list, setAutoConnect(fromPartial<ServerWithId>({ id: 'invalid' }), autoConnect))).toEqual({
abc123: { id: 'abc123' },
def456: { id: 'def456' },
}));
@ -59,7 +59,7 @@ describe('serversReducer', () => {
expect(serversReducer(
listWithDisabledAutoConnect,
setAutoConnect(Mock.of<ServerWithId>({ id: 'abc123' }), false),
setAutoConnect(fromPartial<ServerWithId>({ id: 'abc123' }), false),
)).toEqual({
abc123: { id: 'abc123', autoConnect: false },
def456: { id: 'def456' },
@ -74,7 +74,7 @@ describe('serversReducer', () => {
expect(serversReducer(
listWithEnabledAutoConnect,
setAutoConnect(Mock.of<ServerWithId>({ id: 'def456' }), true),
setAutoConnect(fromPartial<ServerWithId>({ id: 'def456' }), true),
)).toEqual({
abc123: { id: 'abc123', autoConnect: false },
def456: { id: 'def456', autoConnect: true },
@ -94,7 +94,7 @@ describe('serversReducer', () => {
describe('deleteServer', () => {
it('returns expected action', () => {
const serverToDelete = Mock.of<RegularServer>({ id: 'abc123' });
const serverToDelete = fromPartial<RegularServer>({ id: 'abc123' });
const { payload } = deleteServer(serverToDelete);
expect(payload).toEqual({ id: 'abc123' });
@ -122,7 +122,7 @@ describe('serversReducer', () => {
[true],
[false],
])('returns expected action', (autoConnect) => {
const serverToEdit = Mock.of<RegularServer>({ id: 'abc123' });
const serverToEdit = fromPartial<RegularServer>({ id: 'abc123' });
const { payload } = setAutoConnect(serverToEdit, autoConnect);
expect(payload).toEqual({ serverId: 'abc123', autoConnect });

View file

@ -1,10 +1,10 @@
import { Mock } from 'ts-mockery';
import { fromPartial } from '@total-typescript/shoehorn';
import { ServersExporter } from '../../../src/servers/services/ServersExporter';
import type { LocalStorage } from '../../../src/utils/services/LocalStorage';
import { appendChild, removeChild, windowMock } from '../../__mocks__/Window.mock';
describe('ServersExporter', () => {
const storageMock = Mock.of<LocalStorage>({
const storageMock = fromPartial<LocalStorage>({
get: jest.fn(() => ({
abc123: {
id: 'abc123',
@ -16,7 +16,7 @@ describe('ServersExporter', () => {
name: 'bar',
autoConnect: false,
},
})),
} as any)),
});
const erroneousToCsv = jest.fn(() => {
throw new Error('');
@ -31,7 +31,7 @@ describe('ServersExporter', () => {
beforeEach(() => {
originalConsole = global.console;
global.console = Mock.of<Console>({ error });
global.console = fromPartial<Console>({ error });
(global as any).Blob = class Blob {};
(global as any).URL = { createObjectURL: () => '' };
});

View file

@ -1,16 +1,16 @@
import { Mock } from 'ts-mockery';
import { fromPartial } from '@total-typescript/shoehorn';
import type { RegularServer } from '../../../src/servers/data';
import { ServersImporter } from '../../../src/servers/services/ServersImporter';
describe('ServersImporter', () => {
const servers: RegularServer[] = [Mock.all<RegularServer>(), Mock.all<RegularServer>()];
const servers: RegularServer[] = [fromPartial<RegularServer>({}), fromPartial<RegularServer>({})];
const csvjsonMock = jest.fn().mockResolvedValue(servers);
const readAsText = jest.fn();
const fileReaderMock = Mock.of<FileReader>({
const fileReaderMock = fromPartial<FileReader>({
readAsText,
addEventListener: (_eventName: string, listener: (e: ProgressEvent<FileReader>) => void) => listener(
Mock.of<ProgressEvent<FileReader>>({ target: { result: '' } }),
),
addEventListener: ((_eventName: string, listener: (e: ProgressEvent<FileReader>) => void) => listener(
fromPartial({ target: { result: '' } }),
)) as any,
});
const importer = new ServersImporter(csvjsonMock, () => fileReaderMock);
@ -28,7 +28,7 @@ describe('ServersImporter', () => {
csvjsonMock.mockRejectedValue(expectedError);
await expect(importer.importServersFromFile(Mock.of<File>({ type: 'text/html' }))).rejects.toEqual(expectedError);
await expect(importer.importServersFromFile(fromPartial({ type: 'text/html' }))).rejects.toEqual(expectedError);
});
it.each([
@ -57,7 +57,7 @@ describe('ServersImporter', () => {
])('rejects with error if provided file does not parse to valid list of servers', async (parsedObject) => {
csvjsonMock.mockResolvedValue(parsedObject);
await expect(importer.importServersFromFile(Mock.of<File>({ type: 'text/html' }))).rejects.toEqual(
await expect(importer.importServersFromFile(fromPartial({ type: 'text/html' }))).rejects.toEqual(
new Error('Provided file does not have the right format.'),
);
});
@ -78,7 +78,7 @@ describe('ServersImporter', () => {
csvjsonMock.mockResolvedValue(expectedServers);
const result = await importer.importServersFromFile(Mock.all<File>());
const result = await importer.importServersFromFile(fromPartial({}));
expect(result).toEqual(expectedServers);
expect(readAsText).toHaveBeenCalledTimes(1);

View file

@ -1,9 +1,8 @@
import { screen } from '@testing-library/react';
import { Mock } from 'ts-mockery';
import { fromPartial } from '@total-typescript/shoehorn';
import { RealTimeUpdatesSettings } from '../../src/settings/RealTimeUpdatesSettings';
import type {
RealTimeUpdatesSettings as RealTimeUpdatesSettingsOptions,
Settings,
} from '../../src/settings/reducers/settings';
import { renderWithEvents } from '../__helpers__/setUpTest';
@ -12,7 +11,7 @@ describe('<RealTimeUpdatesSettings />', () => {
const setRealTimeUpdatesInterval = jest.fn();
const setUp = (realTimeUpdates: Partial<RealTimeUpdatesSettingsOptions> = {}) => renderWithEvents(
<RealTimeUpdatesSettings
settings={Mock.of<Settings>({ realTimeUpdates })}
settings={fromPartial({ realTimeUpdates })}
toggleRealTimeUpdates={toggleRealTimeUpdates}
setRealTimeUpdatesInterval={setRealTimeUpdatesInterval}
/>,

View file

@ -1,6 +1,6 @@
import { screen } from '@testing-library/react';
import { Mock } from 'ts-mockery';
import type { Settings, ShortUrlCreationSettings as ShortUrlsSettings } from '../../src/settings/reducers/settings';
import { fromPartial } from '@total-typescript/shoehorn';
import type { ShortUrlCreationSettings as ShortUrlsSettings } from '../../src/settings/reducers/settings';
import { ShortUrlCreationSettings } from '../../src/settings/ShortUrlCreationSettings';
import { renderWithEvents } from '../__helpers__/setUpTest';
@ -8,7 +8,7 @@ describe('<ShortUrlCreationSettings />', () => {
const setShortUrlCreationSettings = jest.fn();
const setUp = (shortUrlCreation?: ShortUrlsSettings) => renderWithEvents(
<ShortUrlCreationSettings
settings={Mock.of<Settings>({ shortUrlCreation })}
settings={fromPartial({ shortUrlCreation })}
setShortUrlCreationSettings={setShortUrlCreationSettings}
/>,
);

View file

@ -1,6 +1,6 @@
import { screen } from '@testing-library/react';
import { Mock } from 'ts-mockery';
import type { Settings, ShortUrlsListSettings as ShortUrlsSettings } from '../../src/settings/reducers/settings';
import { fromPartial } from '@total-typescript/shoehorn';
import type { ShortUrlsListSettings as ShortUrlsSettings } from '../../src/settings/reducers/settings';
import { ShortUrlsListSettings } from '../../src/settings/ShortUrlsListSettings';
import type { ShortUrlsOrder } from '../../src/short-urls/data';
import { renderWithEvents } from '../__helpers__/setUpTest';
@ -8,7 +8,7 @@ import { renderWithEvents } from '../__helpers__/setUpTest';
describe('<ShortUrlsListSettings />', () => {
const setSettings = jest.fn();
const setUp = (shortUrlsList?: ShortUrlsSettings) => renderWithEvents(
<ShortUrlsListSettings settings={Mock.of<Settings>({ shortUrlsList })} setShortUrlsListSettings={setSettings} />,
<ShortUrlsListSettings settings={fromPartial({ shortUrlsList })} setShortUrlsListSettings={setSettings} />,
);
afterEach(jest.clearAllMocks);

View file

@ -1,6 +1,6 @@
import { screen } from '@testing-library/react';
import { Mock } from 'ts-mockery';
import type { Settings, TagsSettings as TagsSettingsOptions } from '../../src/settings/reducers/settings';
import { fromPartial } from '@total-typescript/shoehorn';
import type { TagsSettings as TagsSettingsOptions } from '../../src/settings/reducers/settings';
import { TagsSettings } from '../../src/settings/TagsSettings';
import type { TagsOrder } from '../../src/tags/data/TagsListChildrenProps';
import { renderWithEvents } from '../__helpers__/setUpTest';
@ -8,7 +8,7 @@ import { renderWithEvents } from '../__helpers__/setUpTest';
describe('<TagsSettings />', () => {
const setTagsSettings = jest.fn();
const setUp = (tags?: TagsSettingsOptions) => renderWithEvents(
<TagsSettings settings={Mock.of<Settings>({ tags })} setTagsSettings={setTagsSettings} />,
<TagsSettings settings={fromPartial({ tags })} setTagsSettings={setTagsSettings} />,
);
afterEach(jest.clearAllMocks);

View file

@ -1,6 +1,6 @@
import { screen } from '@testing-library/react';
import { Mock } from 'ts-mockery';
import type { Settings, UiSettings } from '../../src/settings/reducers/settings';
import { fromPartial } from '@total-typescript/shoehorn';
import type { UiSettings } from '../../src/settings/reducers/settings';
import { UserInterfaceSettings } from '../../src/settings/UserInterfaceSettings';
import type { Theme } from '../../src/utils/theme';
import { renderWithEvents } from '../__helpers__/setUpTest';
@ -8,7 +8,7 @@ import { renderWithEvents } from '../__helpers__/setUpTest';
describe('<UserInterfaceSettings />', () => {
const setUiSettings = jest.fn();
const setUp = (ui?: UiSettings) => renderWithEvents(
<UserInterfaceSettings settings={Mock.of<Settings>({ ui })} setUiSettings={setUiSettings} />,
<UserInterfaceSettings settings={fromPartial({ ui })} setUiSettings={setUiSettings} />,
);
afterEach(jest.clearAllMocks);

View file

@ -1,5 +1,5 @@
import { screen } from '@testing-library/react';
import { Mock } from 'ts-mockery';
import { fromPartial } from '@total-typescript/shoehorn';
import type { Settings } from '../../src/settings/reducers/settings';
import { VisitsSettings } from '../../src/settings/VisitsSettings';
import { renderWithEvents } from '../__helpers__/setUpTest';
@ -7,7 +7,7 @@ import { renderWithEvents } from '../__helpers__/setUpTest';
describe('<VisitsSettings />', () => {
const setVisitsSettings = jest.fn();
const setUp = (settings: Partial<Settings> = {}) => renderWithEvents(
<VisitsSettings settings={Mock.of<Settings>(settings)} setVisitsSettings={setVisitsSettings} />,
<VisitsSettings settings={fromPartial(settings)} setVisitsSettings={setVisitsSettings} />,
);
afterEach(jest.clearAllMocks);
@ -21,10 +21,10 @@ describe('<VisitsSettings />', () => {
});
it.each([
[Mock.all<Settings>(), 'Last 30 days'],
[Mock.of<Settings>({ visits: {} }), 'Last 30 days'],
[fromPartial<Settings>({}), 'Last 30 days'],
[fromPartial<Settings>({ visits: {} }), 'Last 30 days'],
[
Mock.of<Settings>({
fromPartial<Settings>({
visits: {
defaultInterval: 'last7Days',
},
@ -32,7 +32,7 @@ describe('<VisitsSettings />', () => {
'Last 7 days',
],
[
Mock.of<Settings>({
fromPartial<Settings>({
visits: {
defaultInterval: 'today',
},
@ -63,17 +63,17 @@ describe('<VisitsSettings />', () => {
it.each([
[
Mock.all<Settings>(),
fromPartial<Settings>({}),
/The visits coming from potential bots will be included.$/,
/The visits coming from potential bots will be excluded.$/,
],
[
Mock.of<Settings>({ visits: { excludeBots: false } }),
fromPartial<Settings>({ visits: { excludeBots: false } }),
/The visits coming from potential bots will be included.$/,
/The visits coming from potential bots will be excluded.$/,
],
[
Mock.of<Settings>({ visits: { excludeBots: true } }),
fromPartial<Settings>({ visits: { excludeBots: true } }),
/The visits coming from potential bots will be excluded.$/,
/The visits coming from potential bots will be included.$/,
],

View file

@ -1,4 +1,4 @@
import { Mock } from 'ts-mockery';
import { fromPartial } from '@total-typescript/shoehorn';
import type { ShlinkState } from '../../../src/container/types';
import { migrateDeprecatedSettings } from '../../../src/settings/helpers';
@ -9,7 +9,7 @@ describe('settings-helpers', () => {
});
it('updates settings as expected', () => {
const state = Mock.of<ShlinkState>({
const state = fromPartial<ShlinkState>({
settings: {
visits: {
defaultInterval: 'last180days' as any,

View file

@ -1,6 +1,5 @@
import { render, screen } from '@testing-library/react';
import { Mock } from 'ts-mockery';
import type { Settings } from '../../src/settings/reducers/settings';
import { fromPartial } from '@total-typescript/shoehorn';
import { CreateShortUrl as createShortUrlsCreator } from '../../src/short-urls/CreateShortUrl';
import type { ShortUrlCreation } from '../../src/short-urls/reducers/shortUrlCreation';
@ -8,7 +7,7 @@ describe('<CreateShortUrl />', () => {
const ShortUrlForm = () => <span>ShortUrlForm</span>;
const CreateShortUrlResult = () => <span>CreateShortUrlResult</span>;
const shortUrlCreation = { validateUrls: true };
const shortUrlCreationResult = Mock.all<ShortUrlCreation>();
const shortUrlCreationResult = fromPartial<ShortUrlCreation>({});
const createShortUrl = jest.fn(async () => Promise.resolve());
const CreateShortUrl = createShortUrlsCreator(ShortUrlForm, CreateShortUrlResult);
const setUp = () => render(
@ -17,7 +16,7 @@ describe('<CreateShortUrl />', () => {
createShortUrl={createShortUrl}
selectedServer={null}
resetCreateShortUrl={() => {}}
settings={Mock.of<Settings>({ shortUrlCreation })}
settings={fromPartial({ shortUrlCreation })}
/>,
);

View file

@ -1,8 +1,6 @@
import { render, screen } from '@testing-library/react';
import { fromPartial } from '@total-typescript/shoehorn';
import { MemoryRouter } from 'react-router-dom';
import { Mock } from 'ts-mockery';
import type { Settings } from '../../src/settings/reducers/settings';
import type { ShortUrl } from '../../src/short-urls/data';
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';
@ -13,10 +11,10 @@ describe('<EditShortUrl />', () => {
const setUp = (detail: Partial<ShortUrlDetail> = {}, edition: Partial<ShortUrlEdition> = {}) => render(
<MemoryRouter>
<EditShortUrl
settings={Mock.of<Settings>({ shortUrlCreation })}
settings={fromPartial({ shortUrlCreation })}
selectedServer={null}
shortUrlDetail={Mock.of<ShortUrlDetail>(detail)}
shortUrlEdition={Mock.of<ShortUrlEdition>(edition)}
shortUrlDetail={fromPartial(detail)}
shortUrlEdition={fromPartial(edition)}
getShortUrlDetail={jest.fn()}
editShortUrl={jest.fn(async () => Promise.resolve())}
/>
@ -38,7 +36,7 @@ describe('<EditShortUrl />', () => {
});
it('renders form when detail properly loads', () => {
setUp({ shortUrl: Mock.of<ShortUrl>({ meta: {} }) });
setUp({ shortUrl: fromPartial({ meta: {} }) });
expect(screen.getByText('ShortUrlForm')).toBeInTheDocument();
expect(screen.queryByText('Loading...')).not.toBeInTheDocument();

View file

@ -1,12 +1,12 @@
import { render, screen } from '@testing-library/react';
import { fromPartial } from '@total-typescript/shoehorn';
import { MemoryRouter } from 'react-router-dom';
import { Mock } from 'ts-mockery';
import type { ShlinkPaginator } from '../../src/api/types';
import { Paginator } from '../../src/short-urls/Paginator';
import { ELLIPSIS } from '../../src/utils/helpers/pagination';
describe('<Paginator />', () => {
const buildPaginator = (pagesCount?: number) => Mock.of<ShlinkPaginator>({ pagesCount, currentPage: 1 });
const buildPaginator = (pagesCount?: number) => fromPartial<ShlinkPaginator>({ pagesCount, currentPage: 1 });
const setUp = (paginator?: ShlinkPaginator, currentQueryString?: string) => render(
<MemoryRouter>
<Paginator serverId="abc123" paginator={paginator} currentQueryString={currentQueryString} />

View file

@ -1,7 +1,7 @@
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 { Mock } from 'ts-mockery';
import type { ReachableServer, SelectedServer } from '../../src/servers/data';
import type { Mode } from '../../src/short-urls/ShortUrlForm';
import { ShortUrlForm as createShortUrlForm } from '../../src/short-urls/ShortUrlForm';
@ -51,7 +51,7 @@ describe('<ShortUrlForm />', () => {
ios: 'https://ios.com',
},
},
Mock.of<ReachableServer>({ version: '3.5.0' }),
fromPartial<ReachableServer>({ version: '3.5.0' }),
],
])('saves short URL with data set in form controls', async (extraFields, extraExpectedValues, selectedServer) => {
const { user } = setUp(selectedServer);
@ -102,7 +102,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(Mock.of<ReachableServer>({ version: '2.6.0' }), 'create', originalTitle);
const { user } = setUp(fromPartial({ version: '2.6.0' }), 'create', originalTitle);
await user.type(screen.getByPlaceholderText('URL to be shortened'), 'https://long-domain.com/foo/bar');
await user.clear(screen.getByPlaceholderText('Title'));
@ -117,10 +117,10 @@ describe('<ShortUrlForm />', () => {
});
it.each([
[Mock.of<ReachableServer>({ version: '3.0.0' }), false],
[Mock.of<ReachableServer>({ version: '3.4.0' }), false],
[Mock.of<ReachableServer>({ version: '3.5.0' }), true],
[Mock.of<ReachableServer>({ version: '3.6.0' }), true],
[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'];

View file

@ -1,9 +1,8 @@
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 { Mock } from 'ts-mockery';
import type { ReachableServer, SelectedServer } from '../../src/servers/data';
import type { Settings } from '../../src/settings/reducers/settings';
import { ShortUrlsFilteringBar as filteringBarCreator } from '../../src/short-urls/ShortUrlsFilteringBar';
import { formatDate } from '../../src/utils/helpers/date';
import type { DateRange } from '../../src/utils/helpers/dateIntervals';
@ -28,10 +27,10 @@ describe('<ShortUrlsFilteringBar />', () => {
return renderWithEvents(
<MemoryRouter>
<ShortUrlsFilteringBar
selectedServer={selectedServer ?? Mock.all<SelectedServer>()}
selectedServer={selectedServer ?? fromPartial({})}
order={{}}
handleOrderBy={handleOrderBy}
settings={Mock.of<Settings>({ visits: {} })}
settings={fromPartial({ visits: {} })}
/>
</MemoryRouter>,
);
@ -74,12 +73,12 @@ describe('<ShortUrlsFilteringBar />', () => {
});
it.each([
['tags=foo,bar,baz', Mock.of<ReachableServer>({ version: '3.0.0' }), true],
['tags=foo,bar', Mock.of<ReachableServer>({ version: '3.1.0' }), true],
['tags=foo', Mock.of<ReachableServer>({ version: '3.0.0' }), false],
['', Mock.of<ReachableServer>({ version: '3.0.0' }), false],
['tags=foo,bar,baz', Mock.of<ReachableServer>({ version: '2.10.0' }), false],
['', Mock.of<ReachableServer>({ version: '2.10.0' }), false],
['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],
])(
'renders tags mode toggle if the server supports it and there is more than one tag selected',
(search, selectedServer, shouldHaveComponent) => {
@ -98,7 +97,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}`, Mock.of<ReachableServer>({ version: '3.0.0' }));
const { user } = setUp(`tags=foo,bar${initialTagsMode}`, fromPartial({ version: '3.0.0' }));
await user.hover(screen.getByLabelText('Change tags mode'));
expect(await screen.findByRole('tooltip')).toHaveTextContent(expectedToggleText);
@ -109,7 +108,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}`, Mock.of<ReachableServer>({ version: '3.0.0' }));
const { user } = setUp(`tags=foo,bar${initialTagsMode}`, fromPartial({ version: '3.0.0' }));
expect(navigate).not.toHaveBeenCalled();
await user.click(screen.getByLabelText('Change tags mode'));
@ -127,7 +126,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, Mock.of<ReachableServer>({ version: '3.4.0' }));
const { user } = setUp(search, fromPartial({ version: '3.4.0' }));
const toggleFilter = async (name: RegExp) => {
await user.click(screen.getByRole('button', { name: 'Filters' }));
await waitFor(() => screen.findByRole('menu'));

View file

@ -1,10 +1,9 @@
import { screen } from '@testing-library/react';
import { fromPartial } from '@total-typescript/shoehorn';
import { MemoryRouter, useNavigate } from 'react-router-dom';
import { Mock } from 'ts-mockery';
import type { MercureBoundProps } from '../../src/mercure/helpers/boundToMercureHub';
import type { ReachableServer } from '../../src/servers/data';
import type { Settings } from '../../src/settings/reducers/settings';
import type { ShortUrl, ShortUrlsOrder } from '../../src/short-urls/data';
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';
@ -22,15 +21,15 @@ describe('<ShortUrlsList />', () => {
const ShortUrlsFilteringBar = () => <span>ShortUrlsFilteringBar</span>;
const listShortUrlsMock = jest.fn();
const navigate = jest.fn();
const shortUrlsList = Mock.of<ShortUrlsListModel>({
const shortUrlsList = fromPartial<ShortUrlsListModel>({
shortUrls: {
data: [
Mock.of<ShortUrl>({
{
shortCode: 'testShortCode',
shortUrl: 'https://www.example.com/testShortUrl',
longUrl: 'https://www.example.com/testLongUrl',
tags: ['test tag'],
}),
},
],
pagination: { pagesCount: 3 },
},
@ -39,11 +38,11 @@ describe('<ShortUrlsList />', () => {
const setUp = (settings: Partial<Settings> = {}, version: SemVer = '3.0.0') => renderWithEvents(
<MemoryRouter>
<ShortUrlsList
{...Mock.of<MercureBoundProps>({ mercureInfo: { loading: true } })}
{...fromPartial<MercureBoundProps>({ mercureInfo: { loading: true } })}
listShortUrls={listShortUrlsMock}
shortUrlsList={shortUrlsList}
selectedServer={Mock.of<ReachableServer>({ id: '1', version })}
settings={Mock.of<Settings>(settings)}
selectedServer={fromPartial({ id: '1', version })}
settings={fromPartial(settings)}
/>
</MemoryRouter>,
);
@ -81,9 +80,9 @@ describe('<ShortUrlsList />', () => {
});
it.each([
[Mock.of<ShortUrlsOrder>({ field: 'visits', dir: 'ASC' }), 'visits', 'ASC'],
[Mock.of<ShortUrlsOrder>({ field: 'title', dir: 'DESC' }), 'title', 'DESC'],
[Mock.all<ShortUrlsOrder>(), undefined, undefined],
[fromPartial<ShortUrlsOrder>({ field: 'visits', dir: 'ASC' }), 'visits', 'ASC'],
[fromPartial<ShortUrlsOrder>({ field: 'title', dir: 'DESC' }), 'title', 'DESC'],
[fromPartial<ShortUrlsOrder>({}), undefined, undefined],
])('has expected initial ordering based on settings', (defaultOrdering, field, dir) => {
setUp({ shortUrlsList: { defaultOrdering } });
expect(listShortUrlsMock).toHaveBeenCalledWith(expect.objectContaining({
@ -92,23 +91,23 @@ describe('<ShortUrlsList />', () => {
});
it.each([
[Mock.of<Settings>({
[fromPartial<Settings>({
shortUrlsList: {
defaultOrdering: { field: 'visits', dir: 'ASC' },
},
}), '3.3.0' as SemVer, { field: 'visits', dir: 'ASC' }],
[Mock.of<Settings>({
[fromPartial<Settings>({
shortUrlsList: {
defaultOrdering: { field: 'visits', dir: 'ASC' },
},
visits: { excludeBots: true },
}), '3.3.0' as SemVer, { field: 'visits', dir: 'ASC' }],
[Mock.of<Settings>({
[fromPartial<Settings>({
shortUrlsList: {
defaultOrdering: { field: 'visits', dir: 'ASC' },
},
}), '3.4.0' as SemVer, { field: 'visits', dir: 'ASC' }],
[Mock.of<Settings>({
[fromPartial<Settings>({
shortUrlsList: {
defaultOrdering: { field: 'visits', dir: 'ASC' },
},

View file

@ -1,6 +1,6 @@
import { fireEvent, screen } from '@testing-library/react';
import { Mock } from 'ts-mockery';
import type { ReachableServer, SelectedServer } from '../../src/servers/data';
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';
@ -8,7 +8,7 @@ import { ShortUrlsTable as shortUrlsTableCreator } from '../../src/short-urls/Sh
import { renderWithEvents } from '../__helpers__/setUpTest';
describe('<ShortUrlsTable />', () => {
const shortUrlsList = Mock.all<ShortUrlsList>();
const shortUrlsList = fromPartial<ShortUrlsList>({});
const orderByColumn = jest.fn();
const ShortUrlsTable = shortUrlsTableCreator(() => <span>ShortUrlsRow</span>);
const setUp = (server: SelectedServer = null) => renderWithEvents(
@ -56,7 +56,7 @@ describe('<ShortUrlsTable />', () => {
});
it('should render composed title column', () => {
setUp(Mock.of<ReachableServer>({ version: '2.0.0' }));
setUp(fromPartial({ version: '2.0.0' }));
const { innerHTML } = screen.getAllByRole('columnheader')[2];

View file

@ -1,6 +1,5 @@
import { screen } from '@testing-library/react';
import { Mock } from 'ts-mockery';
import type { ShortUrl } from '../../../src/short-urls/data';
import { fromPartial } from '@total-typescript/shoehorn';
import { CreateShortUrlResult as createResult } from '../../../src/short-urls/helpers/CreateShortUrlResult';
import type { ShortUrlCreation } from '../../../src/short-urls/reducers/shortUrlCreation';
import type { TimeoutToggle } from '../../../src/utils/helpers/hooks';
@ -28,14 +27,14 @@ describe('<CreateShortUrlResult />', () => {
it('renders a result message when result is provided', () => {
setUp(
{ result: Mock.of<ShortUrl>({ shortUrl: 'https://s.test/abc123' }), saving: false, saved: true, error: false },
{ result: fromPartial({ shortUrl: 'https://s.test/abc123' }), saving: false, saved: true, error: false },
);
expect(screen.getByText(/The short URL is/)).toHaveTextContent('Great! The short URL is https://s.test/abc123');
});
it('Invokes tooltip timeout when copy to clipboard button is clicked', async () => {
const { user } = setUp(
{ result: Mock.of<ShortUrl>({ shortUrl: 'https://s.test/abc123' }), saving: false, saved: true, error: false },
{ result: fromPartial({ shortUrl: 'https://s.test/abc123' }), saving: false, saved: true, error: false },
);
expect(copyToClipboard).not.toHaveBeenCalled();

View file

@ -1,6 +1,6 @@
import { screen, waitFor } from '@testing-library/react';
import { Mock } from 'ts-mockery';
import type { InvalidShortUrlDeletion, ProblemDetailsError } from '../../../src/api/types/errors';
import { fromPartial } from '@total-typescript/shoehorn';
import type { InvalidShortUrlDeletion } from '../../../src/api/types/errors';
import { ErrorTypeV2, ErrorTypeV3 } from '../../../src/api/types/errors';
import type { ShortUrl } from '../../../src/short-urls/data';
import { DeleteShortUrlModal } from '../../../src/short-urls/helpers/DeleteShortUrlModal';
@ -9,7 +9,7 @@ import { renderWithEvents } from '../../__helpers__/setUpTest';
import { TestModalWrapper } from '../../__helpers__/TestModalWrapper';
describe('<DeleteShortUrlModal />', () => {
const shortUrl = Mock.of<ShortUrl>({
const shortUrl = fromPartial<ShortUrl>({
tags: [],
shortCode: 'abc123',
longUrl: 'https://long-domain.com/foo/bar',
@ -22,7 +22,7 @@ describe('<DeleteShortUrlModal />', () => {
<DeleteShortUrlModal
{...args}
shortUrl={shortUrl}
shortUrlDeletion={Mock.of<ShortUrlDeletion>(shortUrlDeletion)}
shortUrlDeletion={fromPartial(shortUrlDeletion)}
deleteShortUrl={deleteShortUrl}
shortUrlDeleted={shortUrlDeleted}
resetDeleteShortUrl={jest.fn()}
@ -38,7 +38,7 @@ describe('<DeleteShortUrlModal />', () => {
loading: false,
error: true,
shortCode: 'abc123',
errorData: Mock.of<ProblemDetailsError>({ type: 'OTHER_ERROR' }),
errorData: fromPartial({ type: 'OTHER_ERROR' }),
});
expect(screen.getByText('Something went wrong while deleting the URL :(').parentElement).not.toHaveClass(
'bg-warning',
@ -46,8 +46,8 @@ describe('<DeleteShortUrlModal />', () => {
});
it.each([
[Mock.of<InvalidShortUrlDeletion>({ type: ErrorTypeV3.INVALID_SHORT_URL_DELETION })],
[Mock.of<InvalidShortUrlDeletion>({ type: ErrorTypeV2.INVALID_SHORT_URL_DELETION })],
[fromPartial<InvalidShortUrlDeletion>({ type: ErrorTypeV3.INVALID_SHORT_URL_DELETION })],
[fromPartial<InvalidShortUrlDeletion>({ type: ErrorTypeV2.INVALID_SHORT_URL_DELETION })],
])('shows specific error when threshold error occurs', (errorData) => {
setUp({ loading: false, error: true, shortCode: 'abc123', errorData });
expect(screen.getByText('Something went wrong while deleting the URL :(').parentElement).toHaveClass('bg-warning');

View file

@ -1,8 +1,8 @@
import { screen } from '@testing-library/react';
import { fromPartial } from '@total-typescript/shoehorn';
import { MemoryRouter } from 'react-router-dom';
import { Mock } from 'ts-mockery';
import type { ReportExporter } from '../../../src/common/services/ReportExporter';
import type { NotFoundServer, ReachableServer, SelectedServer } from '../../../src/servers/data';
import type { NotFoundServer, SelectedServer } from '../../../src/servers/data';
import { ExportShortUrlsBtn as createExportShortUrlsBtn } from '../../../src/short-urls/helpers/ExportShortUrlsBtn';
import { renderWithEvents } from '../../__helpers__/setUpTest';
@ -10,11 +10,11 @@ describe('<ExportShortUrlsBtn />', () => {
const listShortUrls = jest.fn();
const buildShlinkApiClient = jest.fn().mockReturnValue({ listShortUrls });
const exportShortUrls = jest.fn();
const reportExporter = Mock.of<ReportExporter>({ exportShortUrls });
const reportExporter = fromPartial<ReportExporter>({ exportShortUrls });
const ExportShortUrlsBtn = createExportShortUrlsBtn(buildShlinkApiClient, reportExporter);
const setUp = (amount?: number, selectedServer?: SelectedServer) => renderWithEvents(
<MemoryRouter>
<ExportShortUrlsBtn selectedServer={selectedServer ?? Mock.all<SelectedServer>()} amount={amount} />
<ExportShortUrlsBtn selectedServer={selectedServer ?? fromPartial({})} amount={amount} />
</MemoryRouter>,
);
@ -31,7 +31,7 @@ describe('<ExportShortUrlsBtn />', () => {
it.each([
[null],
[Mock.of<NotFoundServer>()],
[fromPartial<NotFoundServer>({})],
])('does nothing on click if selected server is not reachable', async (selectedServer) => {
const { user } = setUp(0, selectedServer);
@ -49,7 +49,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, Mock.of<ReachableServer>({ id: '123' }));
const { user } = setUp(amount, fromPartial({ id: '123' }));
await user.click(screen.getByRole('button'));

View file

@ -1,21 +1,18 @@
import { fireEvent, screen } from '@testing-library/react';
import { Mock } from 'ts-mockery';
import type { ImageDownloader } from '../../../src/common/services/ImageDownloader';
import type { ReachableServer } from '../../../src/servers/data';
import type { ShortUrl } from '../../../src/short-urls/data';
import { fromPartial } from '@total-typescript/shoehorn';
import { QrCodeModal as createQrCodeModal } from '../../../src/short-urls/helpers/QrCodeModal';
import type { SemVer } from '../../../src/utils/helpers/version';
import { renderWithEvents } from '../../__helpers__/setUpTest';
describe('<QrCodeModal />', () => {
const saveImage = jest.fn().mockReturnValue(Promise.resolve());
const QrCodeModal = createQrCodeModal(Mock.of<ImageDownloader>({ saveImage }));
const QrCodeModal = createQrCodeModal(fromPartial({ saveImage }));
const shortUrl = 'https://s.test/abc123';
const setUp = (version: SemVer = '2.8.0') => renderWithEvents(
<QrCodeModal
isOpen
shortUrl={Mock.of<ShortUrl>({ shortUrl })}
selectedServer={Mock.of<ReachableServer>({ version })}
shortUrl={fromPartial({ shortUrl })}
selectedServer={fromPartial({ version })}
toggle={() => {}}
/>,
);

View file

@ -1,6 +1,6 @@
import { render, screen } from '@testing-library/react';
import { fromPartial } from '@total-typescript/shoehorn';
import { MemoryRouter } from 'react-router-dom';
import { Mock } from 'ts-mockery';
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';
@ -10,11 +10,11 @@ describe('<ShortUrlDetailLink />', () => {
it.each([
[undefined, undefined],
[null, null],
[Mock.of<ReachableServer>({ id: '1' }), null],
[Mock.of<ReachableServer>({ id: '1' }), undefined],
[Mock.of<NotFoundServer>(), Mock.all<ShortUrl>()],
[null, Mock.all<ShortUrl>()],
[undefined, Mock.all<ShortUrl>()],
[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) => {
render(
<ShortUrlDetailLink selectedServer={selectedServer} shortUrl={shortUrl} suffix="visits">
@ -28,26 +28,26 @@ describe('<ShortUrlDetailLink />', () => {
it.each([
[
Mock.of<ReachableServer>({ id: '1' }),
Mock.of<ShortUrl>({ shortCode: 'abc123' }),
fromPartial<ReachableServer>({ id: '1' }),
fromPartial<ShortUrl>({ shortCode: 'abc123' }),
'visits' as LinkSuffix,
'/server/1/short-code/abc123/visits',
],
[
Mock.of<ReachableServer>({ id: '3' }),
Mock.of<ShortUrl>({ shortCode: 'def456', domain: 'example.com' }),
fromPartial<ReachableServer>({ id: '3' }),
fromPartial<ShortUrl>({ shortCode: 'def456', domain: 'example.com' }),
'visits' as LinkSuffix,
'/server/3/short-code/def456/visits?domain=example.com',
],
[
Mock.of<ReachableServer>({ id: '1' }),
Mock.of<ShortUrl>({ shortCode: 'abc123' }),
fromPartial<ReachableServer>({ id: '1' }),
fromPartial<ShortUrl>({ shortCode: 'abc123' }),
'edit' as LinkSuffix,
'/server/1/short-code/abc123/edit',
],
[
Mock.of<ReachableServer>({ id: '3' }),
Mock.of<ShortUrl>({ shortCode: 'def456', domain: 'example.com' }),
fromPartial<ReachableServer>({ id: '3' }),
fromPartial<ShortUrl>({ shortCode: 'def456', domain: 'example.com' }),
'edit' as LinkSuffix,
'/server/3/short-code/def456/edit?domain=example.com',
],

View file

@ -1,6 +1,6 @@
import { render, screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { Mock } from 'ts-mockery';
import { fromPartial } from '@total-typescript/shoehorn';
import type { ShlinkVisitsSummary } from '../../../src/api/types';
import type { ShortUrl, ShortUrlMeta } from '../../../src/short-urls/data';
import { ShortUrlStatus } from '../../../src/short-urls/helpers/ShortUrlStatus';
@ -13,35 +13,35 @@ describe('<ShortUrlStatus />', () => {
it.each([
[
Mock.of<ShortUrlMeta>({ validSince: '2099-01-01T10:30:15' }),
fromPartial<ShortUrlMeta>({ validSince: '2099-01-01T10:30:15' }),
{},
'This short URL will start working on 2099-01-01 10:30.',
],
[
Mock.of<ShortUrlMeta>({ validUntil: '2020-01-01T10:30:15' }),
fromPartial<ShortUrlMeta>({ validUntil: '2020-01-01T10:30:15' }),
{},
'This short URL cannot be visited since 2020-01-01 10:30.',
],
[
Mock.of<ShortUrlMeta>({ maxVisits: 10 }),
Mock.of<ShlinkVisitsSummary>({ total: 10 }),
fromPartial<ShortUrlMeta>({ maxVisits: 10 }),
fromPartial<ShlinkVisitsSummary>({ total: 10 }),
'This short URL cannot be currently visited because it has reached the maximum amount of 10 visits.',
],
[
Mock.of<ShortUrlMeta>({ maxVisits: 1 }),
Mock.of<ShlinkVisitsSummary>({ total: 1 }),
fromPartial<ShortUrlMeta>({ maxVisits: 1 }),
fromPartial<ShlinkVisitsSummary>({ total: 1 }),
'This short URL cannot be currently visited because it has reached the maximum amount of 1 visit.',
],
[{}, {}, 'This short URL can be visited normally.'],
[Mock.of<ShortUrlMeta>({ validUntil: '2099-01-01T10:30:15' }), {}, 'This short URL can be visited normally.'],
[Mock.of<ShortUrlMeta>({ validSince: '2020-01-01T10:30:15' }), {}, 'This short URL can be visited normally.'],
[fromPartial<ShortUrlMeta>({ validUntil: '2099-01-01T10:30:15' }), {}, 'This short URL can be visited normally.'],
[fromPartial<ShortUrlMeta>({ validSince: '2020-01-01T10:30:15' }), {}, 'This short URL can be visited normally.'],
[
Mock.of<ShortUrlMeta>({ maxVisits: 10 }),
Mock.of<ShlinkVisitsSummary>({ total: 1 }),
fromPartial<ShortUrlMeta>({ maxVisits: 10 }),
fromPartial<ShlinkVisitsSummary>({ total: 1 }),
'This short URL can be visited normally.',
],
])('shows expected tooltip', async (meta, visitsSummary, expectedTooltip) => {
const { user } = setUp(Mock.of<ShortUrl>({ meta, visitsSummary }));
const { user } = setUp(fromPartial({ meta, visitsSummary }));
await user.hover(screen.getByRole('img', { hidden: true }));
await waitFor(() => expect(screen.getByRole('tooltip')).toHaveTextContent(expectedTooltip));

View file

@ -1,6 +1,6 @@
import { render, screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { Mock } from 'ts-mockery';
import { fromPartial } from '@total-typescript/shoehorn';
import type { ShortUrl } from '../../../src/short-urls/data';
import { ShortUrlVisitsCount } from '../../../src/short-urls/helpers/ShortUrlVisitsCount';
@ -14,7 +14,7 @@ describe('<ShortUrlVisitsCount />', () => {
it.each([undefined, {}])('just returns visits when no limits are provided', (meta) => {
const visitsCount = 45;
const { container } = setUp(visitsCount, Mock.of<ShortUrl>({ meta }));
const { container } = setUp(visitsCount, fromPartial({ meta }));
expect(container.firstChild).toHaveTextContent(`${visitsCount}`);
expect(container.querySelector('.short-urls-visits-count__max-visits-control')).not.toBeInTheDocument();
@ -24,7 +24,7 @@ describe('<ShortUrlVisitsCount />', () => {
const visitsCount = 45;
const maxVisits = 500;
const meta = { maxVisits };
const { container } = setUp(visitsCount, Mock.of<ShortUrl>({ meta }));
const { container } = setUp(visitsCount, fromPartial({ meta }));
expect(container.firstChild).toHaveTextContent(`/ ${maxVisits}`);
});
@ -44,7 +44,7 @@ describe('<ShortUrlVisitsCount />', () => {
'This short URL will not accept visits after 2023-05-05 15:30',
], { validSince: '2023-01-01T10:00:00', validUntil: '2023-05-05T15:30:30', maxVisits: 100 }],
])('displays proper amount of tooltip list items', async (expectedListItems, meta) => {
const { user } = setUp(100, Mock.of<ShortUrl>({ meta }));
const { user } = setUp(100, fromPartial({ meta }));
await user.hover(screen.getByRole('img', { hidden: true }));
await waitFor(() => expect(screen.getByRole('list')));

View file

@ -1,8 +1,8 @@
import { screen } from '@testing-library/react';
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 { Mock } from 'ts-mockery';
import type { ReachableServer } from '../../../src/servers/data';
import type { Settings } from '../../../src/settings/reducers/settings';
import type { ShortUrl, ShortUrlMeta } from '../../../src/short-urls/data';
@ -28,7 +28,7 @@ jest.mock('react-router-dom', () => ({
describe('<ShortUrlsRow />', () => {
const timeoutToggle = jest.fn(() => true);
const useTimeoutToggle = jest.fn(() => [false, timeoutToggle]) as TimeoutToggle;
const server = Mock.of<ReachableServer>({ url: 'https://s.test' });
const server = fromPartial<ReachableServer>({ url: 'https://s.test' });
const shortUrl: ShortUrl = {
shortCode: 'abc123',
shortUrl: 'https://s.test/abc123',
@ -60,7 +60,7 @@ describe('<ShortUrlsRow />', () => {
selectedServer={server}
shortUrl={{ ...shortUrl, title, tags, meta: { ...shortUrl.meta, ...meta } }}
onTagClick={() => null}
settings={Mock.of<Settings>(settings)}
settings={fromPartial(settings)}
/>
</tbody>
</table>
@ -118,13 +118,13 @@ describe('<ShortUrlsRow />', () => {
it.each([
[{}, '', shortUrl.visitsSummary?.total],
[Mock.of<Settings>({ visits: { excludeBots: false } }), '', shortUrl.visitsSummary?.total],
[Mock.of<Settings>({ visits: { excludeBots: true } }), '', shortUrl.visitsSummary?.nonBots],
[Mock.of<Settings>({ visits: { excludeBots: false } }), 'excludeBots=true', shortUrl.visitsSummary?.nonBots],
[Mock.of<Settings>({ visits: { excludeBots: true } }), 'excludeBots=true', shortUrl.visitsSummary?.nonBots],
[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],
[{}, 'excludeBots=true', shortUrl.visitsSummary?.nonBots],
[Mock.of<Settings>({ visits: { excludeBots: true } }), 'excludeBots=false', shortUrl.visitsSummary?.total],
[Mock.of<Settings>({ visits: { excludeBots: false } }), 'excludeBots=false', shortUrl.visitsSummary?.total],
[fromPartial<Settings>({ visits: { excludeBots: true } }), 'excludeBots=false', shortUrl.visitsSummary?.total],
[fromPartial<Settings>({ visits: { excludeBots: false } }), 'excludeBots=false', shortUrl.visitsSummary?.total],
[{}, 'excludeBots=false', shortUrl.visitsSummary?.total],
])('renders visits count in fifth row', (settings, search, expectedAmount) => {
setUp({ settings }, search);

View file

@ -1,6 +1,6 @@
import { screen } from '@testing-library/react';
import { fromPartial } from '@total-typescript/shoehorn';
import { MemoryRouter } from 'react-router-dom';
import { Mock } from 'ts-mockery';
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';
@ -8,8 +8,8 @@ import { renderWithEvents } from '../../__helpers__/setUpTest';
describe('<ShortUrlsRowMenu />', () => {
const ShortUrlsRowMenu = createShortUrlsRowMenu(() => <i>DeleteShortUrlModal</i>, () => <i>QrCodeModal</i>);
const selectedServer = Mock.of<ReachableServer>({ id: 'abc123' });
const shortUrl = Mock.of<ShortUrl>({
const selectedServer = fromPartial<ReachableServer>({ id: 'abc123' });
const shortUrl = fromPartial<ShortUrl>({
shortCode: 'abc123',
shortUrl: 'https://s.test/abc123',
});

View file

@ -1,4 +1,4 @@
import { Mock } from 'ts-mockery';
import { fromPartial } from '@total-typescript/shoehorn';
import type { ShortUrl } from '../../../src/short-urls/data';
import { shortUrlDataFromShortUrl, urlDecodeShortCode, urlEncodeShortCode } from '../../../src/short-urls/helpers';
@ -8,7 +8,7 @@ describe('helpers', () => {
[undefined, { validateUrls: true }, { longUrl: '', validateUrl: true }],
[undefined, undefined, { longUrl: '', validateUrl: false }],
[
Mock.of<ShortUrl>({ meta: {} }),
fromPartial<ShortUrl>({ meta: {} }),
{ validateUrls: false },
{
longUrl: undefined,