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

18
package-lock.json generated
View file

@ -60,6 +60,7 @@
"@testing-library/jest-dom": "^5.16.5", "@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^13.4.0", "@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^14.4.3", "@testing-library/user-event": "^14.4.3",
"@total-typescript/shoehorn": "^0.1.0",
"@types/jest": "^29.2.4", "@types/jest": "^29.2.4",
"@types/json2csv": "^5.0.3", "@types/json2csv": "^5.0.3",
"@types/leaflet": "^1.9.0", "@types/leaflet": "^1.9.0",
@ -3563,6 +3564,12 @@
"node": ">= 10" "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": { "node_modules/@types/aria-query": {
"version": "4.2.2", "version": "4.2.2",
"dev": true, "dev": true,
@ -14987,6 +14994,12 @@
"version": "2.0.0", "version": "2.0.0",
"dev": true "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": { "@types/aria-query": {
"version": "4.2.2", "version": "4.2.2",
"dev": true "dev": true
@ -15884,9 +15897,6 @@
} }
} }
}, },
"caniuse-lite": {
"version": "1.0.30001415"
},
"chalk": { "chalk": {
"version": "5.2.0", "version": "5.2.0",
"dev": true "dev": true
@ -20949,4 +20959,4 @@
"dev": true "dev": true
} }
} }
} }

View file

@ -77,6 +77,7 @@
"@testing-library/jest-dom": "^5.16.5", "@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^13.4.0", "@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^14.4.3", "@testing-library/user-event": "^14.4.3",
"@total-typescript/shoehorn": "^0.1.0",
"@types/jest": "^29.2.4", "@types/jest": "^29.2.4",
"@types/json2csv": "^5.0.3", "@types/json2csv": "^5.0.3",
"@types/leaflet": "^1.9.0", "@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 = () => ({ const createLinkMock = () => ({
setAttribute: jest.fn(), setAttribute: jest.fn(),
@ -10,9 +10,9 @@ export const appendChild = jest.fn();
export const removeChild = jest.fn(); export const removeChild = jest.fn();
export const windowMock = Mock.of<Window>({ export const windowMock = fromPartial<Window>({
document: { document: fromAny({
createElement: jest.fn(createLinkMock), createElement: jest.fn(createLinkMock),
body: { appendChild, removeChild }, body: { appendChild, removeChild },
}, }),
}); });

View file

@ -1,5 +1,5 @@
import { render, screen } from '@testing-library/react'; 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 type { ShlinkApiErrorProps } from '../../src/api/ShlinkApiError';
import { ShlinkApiError } from '../../src/api/ShlinkApiError'; import { ShlinkApiError } from '../../src/api/ShlinkApiError';
import type { InvalidArgumentError, ProblemDetailsError } from '../../src/api/types/errors'; import type { InvalidArgumentError, ProblemDetailsError } from '../../src/api/types/errors';
@ -10,8 +10,8 @@ describe('<ShlinkApiError />', () => {
it.each([ it.each([
[undefined, 'the fallback', 'the fallback'], [undefined, 'the fallback', 'the fallback'],
[Mock.all<ProblemDetailsError>(), 'the fallback', 'the fallback'], [fromPartial<ProblemDetailsError>({}), 'the fallback', 'the fallback'],
[Mock.of<ProblemDetailsError>({ detail: 'the detail' }), 'the fallback', 'the detail'], [fromPartial<ProblemDetailsError>({ detail: 'the detail' }), 'the fallback', 'the detail'],
])('renders proper message', (errorData, fallbackMessage, expectedMessage) => { ])('renders proper message', (errorData, fallbackMessage, expectedMessage) => {
const { container } = setUp({ errorData, fallbackMessage }); const { container } = setUp({ errorData, fallbackMessage });
@ -21,9 +21,9 @@ describe('<ShlinkApiError />', () => {
it.each([ it.each([
[undefined, 0], [undefined, 0],
[Mock.all<ProblemDetailsError>(), 0], [fromPartial<ProblemDetailsError>({}), 0],
[Mock.of<InvalidArgumentError>({ type: ErrorTypeV2.INVALID_ARGUMENT, invalidElements: [] }), 1], [fromPartial<InvalidArgumentError>({ type: ErrorTypeV2.INVALID_ARGUMENT, invalidElements: [] }), 1],
[Mock.of<InvalidArgumentError>({ type: ErrorTypeV3.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) => { ])('renders list of invalid elements when provided error is an InvalidError', (errorData, expectedElementsCount) => {
setUp({ errorData }); setUp({ errorData });
expect(screen.queryAllByText(/^Invalid elements/)).toHaveLength(expectedElementsCount); 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 { ShlinkApiClient } from '../../../src/api/services/ShlinkApiClient';
import type { ShlinkDomain, ShlinkVisits, ShlinkVisitsOverview } from '../../../src/api/types'; import type { ShlinkDomain, ShlinkVisits, ShlinkVisitsOverview } from '../../../src/api/types';
import { ErrorTypeV2, ErrorTypeV3 } from '../../../src/api/types/errors'; import { ErrorTypeV2, ErrorTypeV3 } from '../../../src/api/types/errors';
@ -9,7 +9,7 @@ import type { OptionalString } from '../../../src/utils/utils';
describe('ShlinkApiClient', () => { describe('ShlinkApiClient', () => {
const fetchJson = jest.fn().mockResolvedValue({}); const fetchJson = jest.fn().mockResolvedValue({});
const fetchEmpty = jest.fn().mockResolvedValue(undefined); 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 buildApiClient = () => new ShlinkApiClient(httpClient, '', '');
const shortCodesWithDomainCombinations: [string, OptionalString][] = [ const shortCodesWithDomainCombinations: [string, OptionalString][] = [
['abc123', null], ['abc123', null],
@ -177,7 +177,7 @@ describe('ShlinkApiClient', () => {
maxVisits: 50, maxVisits: 50,
validSince: '2025-01-01T10:00:00+01:00', validSince: '2025-01-01T10:00:00+01:00',
}; };
const expectedResp = Mock.of<ShortUrl>(); const expectedResp = fromPartial<ShortUrl>({});
fetchJson.mockResolvedValue(expectedResp); fetchJson.mockResolvedValue(expectedResp);
const { updateShortUrl } = buildApiClient(); const { updateShortUrl } = buildApiClient();
const expectedQuery = domain ? `?domain=${domain}` : ''; const expectedQuery = domain ? `?domain=${domain}` : '';
@ -311,7 +311,7 @@ describe('ShlinkApiClient', () => {
describe('listDomains', () => { describe('listDomains', () => {
it('returns domains', async () => { it('returns domains', async () => {
const expectedData = { data: [Mock.all<ShlinkDomain>(), Mock.all<ShlinkDomain>()] }; const expectedData = { data: [fromPartial<ShlinkDomain>({}), fromPartial<ShlinkDomain>({})] };
fetchJson.mockResolvedValue({ domains: expectedData }); fetchJson.mockResolvedValue({ domains: expectedData });
const { listDomains } = buildApiClient(); const { listDomains } = buildApiClient();
@ -324,7 +324,7 @@ describe('ShlinkApiClient', () => {
describe('getVisitsOverview', () => { describe('getVisitsOverview', () => {
it('returns visits overview', async () => { it('returns visits overview', async () => {
const expectedData = Mock.all<ShlinkVisitsOverview>(); const expectedData = fromPartial<ShlinkVisitsOverview>({});
fetchJson.mockResolvedValue({ visits: expectedData }); fetchJson.mockResolvedValue({ visits: expectedData });
const { getVisitsOverview } = buildApiClient(); const { getVisitsOverview } = buildApiClient();
@ -337,7 +337,7 @@ describe('ShlinkApiClient', () => {
describe('getOrphanVisits', () => { describe('getOrphanVisits', () => {
it('returns orphan visits', async () => { it('returns orphan visits', async () => {
fetchJson.mockResolvedValue({ visits: Mock.of<ShlinkVisits>({ data: [] }) }); fetchJson.mockResolvedValue({ visits: fromPartial<ShlinkVisits>({ data: [] }) });
const { getOrphanVisits } = buildApiClient(); const { getOrphanVisits } = buildApiClient();
const result = await getOrphanVisits(); const result = await getOrphanVisits();
@ -349,7 +349,7 @@ describe('ShlinkApiClient', () => {
describe('getNonOrphanVisits', () => { describe('getNonOrphanVisits', () => {
it('returns non-orphan visits', async () => { it('returns non-orphan visits', async () => {
fetchJson.mockResolvedValue({ visits: Mock.of<ShlinkVisits>({ data: [] }) }); fetchJson.mockResolvedValue({ visits: fromPartial<ShlinkVisits>({ data: [] }) });
const { getNonOrphanVisits } = buildApiClient(); const { getNonOrphanVisits } = buildApiClient();
const result = await getNonOrphanVisits(); 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 { 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'; import type { ReachableServer, SelectedServer } from '../../../src/servers/data';
describe('ShlinkApiClientBuilder', () => { describe('ShlinkApiClientBuilder', () => {
const server = (data: Partial<ReachableServer>) => Mock.of<ReachableServer>(data); const server = fromPartial<ReachableServer>;
const createBuilder = () => { const createBuilder = () => {
const builder = buildShlinkApiClient(Mock.of<HttpClient>()); const builder = buildShlinkApiClient(fromPartial({}));
return (selectedServer: SelectedServer) => builder(() => Mock.of<ShlinkState>({ selectedServer })); return (selectedServer: SelectedServer) => builder(() => fromPartial({ selectedServer }));
}; };
it('creates new instances when provided params are different', async () => { 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', () => { it('does not fetch from state when provided param is already selected server', () => {
const url = 'url'; const url = 'url';
const apiKey = 'apiKey'; 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['baseUrl']).toEqual(url); // eslint-disable-line @typescript-eslint/dot-notation
expect(apiClient['apiKey']).toEqual(apiKey); // 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 { render, screen } from '@testing-library/react';
import { fromPartial } from '@total-typescript/shoehorn';
import { createMemoryHistory } from 'history'; import { createMemoryHistory } from 'history';
import { Router } from 'react-router-dom'; import { Router } from 'react-router-dom';
import { Mock } from 'ts-mockery';
import { App as createApp } from '../../src/app/App'; import { App as createApp } from '../../src/app/App';
import type { Settings } from '../../src/settings/reducers/settings';
describe('<App />', () => { describe('<App />', () => {
const App = createApp( const App = createApp(
@ -25,7 +24,7 @@ describe('<App />', () => {
<App <App
fetchServers={() => {}} fetchServers={() => {}}
servers={{}} servers={{}}
settings={Mock.all<Settings>()} settings={fromPartial({})}
appUpdated appUpdated
resetAppUpdate={() => {}} resetAppUpdate={() => {}}
/> />

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,12 +1,11 @@
import { render } from '@testing-library/react'; 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 type { Sidebar } from '../../src/common/reducers/sidebar';
import { ShlinkVersionsContainer } from '../../src/common/ShlinkVersionsContainer'; import { ShlinkVersionsContainer } from '../../src/common/ShlinkVersionsContainer';
import type { SelectedServer } from '../../src/servers/data';
describe('<ShlinkVersionsContainer />', () => { describe('<ShlinkVersionsContainer />', () => {
const setUp = (sidebar: Sidebar) => render( const setUp = (sidebar: Sidebar) => render(
<ShlinkVersionsContainer selectedServer={Mock.all<SelectedServer>()} sidebar={sidebar} />, <ShlinkVersionsContainer selectedServer={fromPartial({})} sidebar={sidebar} />,
); );
it.each([ 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 type { HttpClient } from '../../../src/common/services/HttpClient';
import { ImageDownloader } from '../../../src/common/services/ImageDownloader'; import { ImageDownloader } from '../../../src/common/services/ImageDownloader';
import { windowMock } from '../../__mocks__/Window.mock'; import { windowMock } from '../../__mocks__/Window.mock';
describe('ImageDownloader', () => { describe('ImageDownloader', () => {
const fetchBlob = jest.fn(); const fetchBlob = jest.fn();
const httpClient = Mock.of<HttpClient>({ fetchBlob }); const httpClient = fromPartial<HttpClient>({ fetchBlob });
let imageDownloader: ImageDownloader; let imageDownloader: ImageDownloader;
beforeEach(() => { beforeEach(() => {

View file

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

View file

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

View file

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

View file

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

View file

@ -1,11 +1,11 @@
import { screen } from '@testing-library/react'; 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 type { DomainStatus } from '../../../src/domains/data';
import { DomainStatusIcon } from '../../../src/domains/helpers/DomainStatusIcon'; import { DomainStatusIcon } from '../../../src/domains/helpers/DomainStatusIcon';
import { renderWithEvents } from '../../__helpers__/setUpTest'; import { renderWithEvents } from '../../__helpers__/setUpTest';
describe('<DomainStatusIcon />', () => { 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( const setUp = (status: DomainStatus) => renderWithEvents(
<DomainStatusIcon status={status} matchMedia={matchMedia} />, <DomainStatusIcon status={status} matchMedia={matchMedia} />,
); );

View file

@ -1,5 +1,5 @@
import { fireEvent, screen, waitFor } from '@testing-library/react'; 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 type { ShlinkDomain } from '../../../src/api/types';
import { EditDomainRedirectsModal } from '../../../src/domains/helpers/EditDomainRedirectsModal'; import { EditDomainRedirectsModal } from '../../../src/domains/helpers/EditDomainRedirectsModal';
import { renderWithEvents } from '../../__helpers__/setUpTest'; import { renderWithEvents } from '../../__helpers__/setUpTest';
@ -7,7 +7,7 @@ import { renderWithEvents } from '../../__helpers__/setUpTest';
describe('<EditDomainRedirectsModal />', () => { describe('<EditDomainRedirectsModal />', () => {
const editDomainRedirects = jest.fn().mockResolvedValue(undefined); const editDomainRedirects = jest.fn().mockResolvedValue(undefined);
const toggle = jest.fn(); const toggle = jest.fn();
const domain = Mock.of<ShlinkDomain>({ const domain = fromPartial<ShlinkDomain>({
domain: 'foo.com', domain: 'foo.com',
redirects: { redirects: {
baseUrlRedirect: 'baz', 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 { ShlinkApiClient } from '../../../src/api/services/ShlinkApiClient';
import type { ShlinkDomainRedirects } from '../../../src/api/types'; import type { ShlinkDomainRedirects } from '../../../src/api/types';
import type { EditDomainRedirects } from '../../../src/domains/reducers/domainRedirects';
import { editDomainRedirects } from '../../../src/domains/reducers/domainRedirects'; import { editDomainRedirects } from '../../../src/domains/reducers/domainRedirects';
describe('domainRedirectsReducer', () => { describe('domainRedirectsReducer', () => {
@ -9,17 +8,17 @@ describe('domainRedirectsReducer', () => {
describe('editDomainRedirects', () => { describe('editDomainRedirects', () => {
const domain = 'example.com'; const domain = 'example.com';
const redirects = Mock.all<ShlinkDomainRedirects>(); const redirects = fromPartial<ShlinkDomainRedirects>({});
const dispatch = jest.fn(); const dispatch = jest.fn();
const getState = jest.fn(); const getState = jest.fn();
const editDomainRedirectsCall = jest.fn(); const editDomainRedirectsCall = jest.fn();
const buildShlinkApiClient = () => Mock.of<ShlinkApiClient>({ editDomainRedirects: editDomainRedirectsCall }); const buildShlinkApiClient = () => fromPartial<ShlinkApiClient>({ editDomainRedirects: editDomainRedirectsCall });
const editDomainRedirectsAction = editDomainRedirects(buildShlinkApiClient); const editDomainRedirectsAction = editDomainRedirects(buildShlinkApiClient);
it('dispatches domain and redirects once loaded', async () => { it('dispatches domain and redirects once loaded', async () => {
editDomainRedirectsCall.mockResolvedValue(redirects); editDomainRedirectsCall.mockResolvedValue(redirects);
await editDomainRedirectsAction(Mock.of<EditDomainRedirects>({ domain }))(dispatch, getState, {}); await editDomainRedirectsAction(fromPartial({ domain }))(dispatch, getState, {});
expect(dispatch).toHaveBeenCalledTimes(2); expect(dispatch).toHaveBeenCalledTimes(2);
expect(dispatch).toHaveBeenLastCalledWith(expect.objectContaining({ 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 { ShlinkApiClient } from '../../../src/api/services/ShlinkApiClient';
import type { ShlinkDomainRedirects } from '../../../src/api/types'; import type { ShlinkDomainRedirects } from '../../../src/api/types';
import { parseApiError } from '../../../src/api/utils'; 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 { Domain } from '../../../src/domains/data';
import type { EditDomainRedirects } from '../../../src/domains/reducers/domainRedirects'; import type { EditDomainRedirects } from '../../../src/domains/reducers/domainRedirects';
import { editDomainRedirects } from '../../../src/domains/reducers/domainRedirects'; import { editDomainRedirects } from '../../../src/domains/reducers/domainRedirects';
import type {
DomainsList } from '../../../src/domains/reducers/domainsList';
import { import {
domainsListReducerCreator, domainsListReducerCreator,
replaceRedirectsOnDomain, replaceRedirectsOnDomain,
replaceStatusOnDomain, replaceStatusOnDomain,
} from '../../../src/domains/reducers/domainsList'; } from '../../../src/domains/reducers/domainsList';
import type { SelectedServer, ServerData } from '../../../src/servers/data';
describe('domainsListReducer', () => { describe('domainsListReducer', () => {
const dispatch = jest.fn(); const dispatch = jest.fn();
const getState = jest.fn(); const getState = jest.fn();
const listDomains = jest.fn(); const listDomains = jest.fn();
const health = jest.fn(); const health = jest.fn();
const buildShlinkApiClient = () => Mock.of<ShlinkApiClient>({ listDomains, health }); const buildShlinkApiClient = () => fromPartial<ShlinkApiClient>({ listDomains, health });
const filteredDomains = [ const filteredDomains: Domain[] = [
Mock.of<Domain>({ domain: 'foo', status: 'validating' }), fromPartial({ domain: 'foo', status: 'validating' }),
Mock.of<Domain>({ domain: 'Boo', 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 error = { type: 'NOT_FOUND', status: 404 } as unknown as Error;
const editDomainRedirectsThunk = editDomainRedirects(buildShlinkApiClient); const editDomainRedirectsThunk = editDomainRedirects(buildShlinkApiClient);
const { reducer, listDomains: listDomainsAction, checkDomainHealth, filterDomains } = domainsListReducerCreator( const { reducer, listDomains: listDomainsAction, checkDomainHealth, filterDomains } = domainsListReducerCreator(
@ -55,7 +52,7 @@ describe('domainsListReducer', () => {
}); });
it('filters domains on FILTER_DOMAINS', () => { 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([ it.each([
@ -71,7 +68,7 @@ describe('domainsListReducer', () => {
const editDomainRedirects: EditDomainRedirects = { domain, redirects }; const editDomainRedirects: EditDomainRedirects = { domain, redirects };
expect(reducer( expect(reducer(
Mock.of<DomainsList>({ domains, filteredDomains }), fromPartial({ domains, filteredDomains }),
editDomainRedirectsThunk.fulfilled(editDomainRedirects, '', editDomainRedirects), editDomainRedirectsThunk.fulfilled(editDomainRedirects, '', editDomainRedirects),
)).toEqual({ )).toEqual({
domains: domains.map(replaceRedirectsOnDomain(editDomainRedirects)), domains: domains.map(replaceRedirectsOnDomain(editDomainRedirects)),
@ -85,7 +82,7 @@ describe('domainsListReducer', () => {
['does_not_exist'], ['does_not_exist'],
])('replaces status on proper domain on VALIDATE_DOMAIN', (domain) => { ])('replaces status on proper domain on VALIDATE_DOMAIN', (domain) => {
expect(reducer( expect(reducer(
Mock.of<DomainsList>({ domains, filteredDomains }), fromPartial({ domains, filteredDomains }),
checkDomainHealth.fulfilled({ domain, status: 'valid' }, '', ''), checkDomainHealth.fulfilled({ domain, status: 'valid' }, '', ''),
)).toEqual({ )).toEqual({
domains: domains.map(replaceStatusOnDomain(domain, 'valid')), domains: domains.map(replaceStatusOnDomain(domain, 'valid')),
@ -122,8 +119,8 @@ describe('domainsListReducer', () => {
const domain = 'example.com'; const domain = 'example.com';
it('dispatches invalid status when selected server does not have all required data', async () => { it('dispatches invalid status when selected server does not have all required data', async () => {
getState.mockReturnValue(Mock.of<ShlinkState>({ getState.mockReturnValue(fromPartial<ShlinkState>({
selectedServer: Mock.all<SelectedServer>(), selectedServer: {},
})); }));
await checkDomainHealth(domain)(dispatch, getState, {}); await checkDomainHealth(domain)(dispatch, getState, {});
@ -136,11 +133,11 @@ describe('domainsListReducer', () => {
}); });
it('dispatches invalid status when health endpoint returns an error', async () => { it('dispatches invalid status when health endpoint returns an error', async () => {
getState.mockReturnValue(Mock.of<ShlinkState>({ getState.mockReturnValue(fromPartial<ShlinkState>({
selectedServer: Mock.of<ServerData>({ selectedServer: {
url: 'https://myerver.com', url: 'https://myerver.com',
apiKey: '123', apiKey: '123',
}), },
})); }));
health.mockRejectedValue({}); health.mockRejectedValue({});
@ -160,11 +157,11 @@ describe('domainsListReducer', () => {
healthStatus, healthStatus,
expectedStatus, expectedStatus,
) => { ) => {
getState.mockReturnValue(Mock.of<ShlinkState>({ getState.mockReturnValue(fromPartial<ShlinkState>({
selectedServer: Mock.of<ServerData>({ selectedServer: {
url: 'https://myerver.com', url: 'https://myerver.com',
apiKey: '123', apiKey: '123',
}), },
})); }));
health.mockResolvedValue({ status: healthStatus }); health.mockResolvedValue({ status: healthStatus });

View file

@ -1,6 +1,6 @@
import { fromPartial } from '@total-typescript/shoehorn';
import { EventSourcePolyfill } from 'event-source-polyfill'; import { EventSourcePolyfill } from 'event-source-polyfill';
import { identity } from 'ramda'; import { identity } from 'ramda';
import { Mock } from 'ts-mockery';
import { bindToMercureTopic } from '../../../src/mercure/helpers'; import { bindToMercureTopic } from '../../../src/mercure/helpers';
import type { MercureInfo } from '../../../src/mercure/reducers/mercureInfo'; import type { MercureInfo } from '../../../src/mercure/reducers/mercureInfo';
@ -14,11 +14,11 @@ describe('helpers', () => {
const onTokenExpired = jest.fn(); const onTokenExpired = jest.fn();
it.each([ it.each([
[Mock.of<MercureInfo>({ loading: true, error: false, mercureHubUrl: 'foo' })], [fromPartial<MercureInfo>({ loading: true, error: false, mercureHubUrl: 'foo' })],
[Mock.of<MercureInfo>({ loading: false, error: true, mercureHubUrl: 'foo' })], [fromPartial<MercureInfo>({ loading: false, error: true, mercureHubUrl: 'foo' })],
[Mock.of<MercureInfo>({ loading: true, error: true, mercureHubUrl: 'foo' })], [fromPartial<MercureInfo>({ loading: true, error: true, mercureHubUrl: 'foo' })],
[Mock.of<MercureInfo>({ loading: false, error: false, mercureHubUrl: undefined })], [fromPartial<MercureInfo>({ loading: false, error: false, mercureHubUrl: undefined })],
[Mock.of<MercureInfo>({ loading: true, error: true, mercureHubUrl: undefined })], [fromPartial<MercureInfo>({ loading: true, error: true, mercureHubUrl: undefined })],
])('does not bind an EventSource when loading, error or no hub URL', (mercureInfo) => { ])('does not bind an EventSource when loading, error or no hub URL', (mercureInfo) => {
bindToMercureTopic(mercureInfo, [''], identity, () => {}); 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 { ShlinkApiClient } from '../../../src/api/services/ShlinkApiClient';
import type { GetState } from '../../../src/container/types'; import type { GetState } from '../../../src/container/types';
import { mercureInfoReducerCreator } from '../../../src/mercure/reducers/mercureInfo'; import { mercureInfoReducerCreator } from '../../../src/mercure/reducers/mercureInfo';
@ -9,7 +9,7 @@ describe('mercureInfoReducer', () => {
token: 'abc.123.def', token: 'abc.123.def',
}; };
const getMercureInfo = jest.fn(); const getMercureInfo = jest.fn();
const buildShlinkApiClient = () => Mock.of<ShlinkApiClient>({ mercureInfo: getMercureInfo }); const buildShlinkApiClient = () => fromPartial<ShlinkApiClient>({ mercureInfo: getMercureInfo });
const { loadMercureInfo, reducer } = mercureInfoReducerCreator(buildShlinkApiClient); const { loadMercureInfo, reducer } = mercureInfoReducerCreator(buildShlinkApiClient);
beforeEach(jest.resetAllMocks); beforeEach(jest.resetAllMocks);

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,6 +1,6 @@
import { render, screen } from '@testing-library/react'; import { render, screen } from '@testing-library/react';
import { fromPartial } from '@total-typescript/shoehorn';
import { MemoryRouter } from 'react-router-dom'; import { MemoryRouter } from 'react-router-dom';
import { Mock } from 'ts-mockery';
import type { NonReachableServer, NotFoundServer } from '../../../src/servers/data'; import type { NonReachableServer, NotFoundServer } from '../../../src/servers/data';
import { ServerError as createServerError } from '../../../src/servers/helpers/ServerError'; import { ServerError as createServerError } from '../../../src/servers/helpers/ServerError';
@ -9,7 +9,7 @@ describe('<ServerError />', () => {
it.each([ it.each([
[ [
Mock.all<NotFoundServer>(), fromPartial<NotFoundServer>({}),
{ {
found: ['Could not find this Shlink server.'], found: ['Could not find this Shlink server.'],
notFound: [ notFound: [
@ -20,7 +20,7 @@ describe('<ServerError />', () => {
}, },
], ],
[ [
Mock.of<NonReachableServer>({ id: 'abc123' }), fromPartial<NonReachableServer>({ id: 'abc123' }),
{ {
found: [ found: [
'Oops! Could not connect to this Shlink server.', '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 type { HttpClient } from '../../../src/common/services/HttpClient';
import { fetchServers } from '../../../src/servers/reducers/remoteServers'; import { fetchServers } from '../../../src/servers/reducers/remoteServers';
@ -8,7 +8,7 @@ describe('remoteServersReducer', () => {
describe('fetchServers', () => { describe('fetchServers', () => {
const dispatch = jest.fn(); const dispatch = jest.fn();
const fetchJson = jest.fn(); const fetchJson = jest.fn();
const httpClient = Mock.of<HttpClient>({ fetchJson }); const httpClient = fromPartial<HttpClient>({ fetchJson });
it.each([ 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 { v4 as uuid } from 'uuid';
import type { ShlinkApiClient } from '../../../src/api/services/ShlinkApiClient'; import type { ShlinkApiClient } from '../../../src/api/services/ShlinkApiClient';
import type { ShlinkState } from '../../../src/container/types'; import type { ShlinkState } from '../../../src/container/types';
@ -15,7 +15,7 @@ import {
describe('selectedServerReducer', () => { describe('selectedServerReducer', () => {
const dispatch = jest.fn(); const dispatch = jest.fn();
const health = 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 selectServer = selectServerCreator(buildApiClient);
const { reducer } = selectedServerReducerCreator(selectServer); const { reducer } = selectedServerReducerCreator(selectServer);
@ -26,8 +26,7 @@ describe('selectedServerReducer', () => {
expect(reducer(null, resetSelectedServer())).toBeNull()); expect(reducer(null, resetSelectedServer())).toBeNull());
it('returns selected server when action is SELECT_SERVER', () => { 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); expect(reducer(null, selectServer.fulfilled(payload, '', ''))).toEqual(payload);
}); });
}); });
@ -66,7 +65,7 @@ describe('selectedServerReducer', () => {
it('dispatches error when health endpoint fails', async () => { it('dispatches error when health endpoint fails', async () => {
const id = uuid(); const id = uuid();
const getState = createGetStateMock(id); const getState = createGetStateMock(id);
const expectedSelectedServer = Mock.of<NonReachableServer>({ id, serverNotReachable: true }); const expectedSelectedServer = fromPartial<NonReachableServer>({ id, serverNotReachable: true });
health.mockRejectedValue({}); health.mockRejectedValue({});
@ -78,7 +77,7 @@ describe('selectedServerReducer', () => {
it('dispatches error when server is not found', async () => { it('dispatches error when server is not found', async () => {
const id = uuid(); const id = uuid();
const getState = jest.fn(() => Mock.of<ShlinkState>({ servers: {} })); const getState = jest.fn(() => fromPartial<ShlinkState>({ servers: {} }));
const expectedSelectedServer: NotFoundServer = { serverNotFound: true }; const expectedSelectedServer: NotFoundServer = { serverNotFound: true };
await selectServer(id)(dispatch, getState, {}); await selectServer(id)(dispatch, getState, {});
@ -95,9 +94,9 @@ describe('selectedServerReducer', () => {
const { middleware } = selectServerListener(selectServer, loadMercureInfo); const { middleware } = selectServerListener(selectServer, loadMercureInfo);
it.each([ it.each([
[Mock.of<ReachableServer>({ version: '1.2.3' }), 1], [fromPartial<ReachableServer>({ version: '1.2.3' }), 1],
[Mock.of<NotFoundServer>({ serverNotFound: true }), 0], [fromPartial<NotFoundServer>({ serverNotFound: true }), 0],
[Mock.of<NonReachableServer>({ serverNotReachable: true }), 0], [fromPartial<NonReachableServer>({ serverNotReachable: true }), 0],
])('dispatches loadMercureInfo when provided server is reachable', (payload, expectedCalls) => { ])('dispatches loadMercureInfo when provided server is reachable', (payload, expectedCalls) => {
middleware({ dispatch, getState })(jest.fn())({ middleware({ dispatch, getState })(jest.fn())({
payload, payload,
@ -110,7 +109,7 @@ describe('selectedServerReducer', () => {
it('does not dispatch loadMercureInfo when action is not of the proper type', () => { it('does not dispatch loadMercureInfo when action is not of the proper type', () => {
middleware({ dispatch, getState })(jest.fn())({ middleware({ dispatch, getState })(jest.fn())({
payload: Mock.of<ReachableServer>({ version: '1.2.3' }), payload: fromPartial<ReachableServer>({ version: '1.2.3' }),
type: 'something_else', type: 'something_else',
}); });

View file

@ -1,6 +1,6 @@
import { fromPartial } from '@total-typescript/shoehorn';
import { dissoc, values } from 'ramda'; import { dissoc, values } from 'ramda';
import { Mock } from 'ts-mockery'; import type { RegularServer, ServersMap, ServerWithId } from '../../../src/servers/data';
import type { RegularServer, ServerWithId } from '../../../src/servers/data';
import { import {
createServers, createServers,
deleteServer, deleteServer,
@ -10,9 +10,9 @@ import {
} from '../../../src/servers/reducers/servers'; } from '../../../src/servers/reducers/servers';
describe('serversReducer', () => { describe('serversReducer', () => {
const list = { const list: ServersMap = {
abc123: Mock.of<RegularServer>({ id: 'abc123' }), abc123: fromPartial({ id: 'abc123' }),
def456: Mock.of<RegularServer>({ id: 'def456' }), def456: fromPartial({ id: 'def456' }),
}; };
afterEach(jest.clearAllMocks); afterEach(jest.clearAllMocks);
@ -31,12 +31,12 @@ describe('serversReducer', () => {
})); }));
it('removes server when action is DELETE_SERVER', () => 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' }, def456: { id: 'def456' },
})); }));
it('appends server when action is CREATE_SERVERS', () => 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' }, abc123: { id: 'abc123' },
def456: { id: 'def456' }, def456: { id: 'def456' },
ghi789: { id: 'ghi789' }, ghi789: { id: 'ghi789' },
@ -46,7 +46,7 @@ describe('serversReducer', () => {
[true], [true],
[false], [false],
])('returns state as it is when trying to set auto-connect on invalid server', (autoConnect) => ])('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' }, abc123: { id: 'abc123' },
def456: { id: 'def456' }, def456: { id: 'def456' },
})); }));
@ -59,7 +59,7 @@ describe('serversReducer', () => {
expect(serversReducer( expect(serversReducer(
listWithDisabledAutoConnect, listWithDisabledAutoConnect,
setAutoConnect(Mock.of<ServerWithId>({ id: 'abc123' }), false), setAutoConnect(fromPartial<ServerWithId>({ id: 'abc123' }), false),
)).toEqual({ )).toEqual({
abc123: { id: 'abc123', autoConnect: false }, abc123: { id: 'abc123', autoConnect: false },
def456: { id: 'def456' }, def456: { id: 'def456' },
@ -74,7 +74,7 @@ describe('serversReducer', () => {
expect(serversReducer( expect(serversReducer(
listWithEnabledAutoConnect, listWithEnabledAutoConnect,
setAutoConnect(Mock.of<ServerWithId>({ id: 'def456' }), true), setAutoConnect(fromPartial<ServerWithId>({ id: 'def456' }), true),
)).toEqual({ )).toEqual({
abc123: { id: 'abc123', autoConnect: false }, abc123: { id: 'abc123', autoConnect: false },
def456: { id: 'def456', autoConnect: true }, def456: { id: 'def456', autoConnect: true },
@ -94,7 +94,7 @@ describe('serversReducer', () => {
describe('deleteServer', () => { describe('deleteServer', () => {
it('returns expected action', () => { it('returns expected action', () => {
const serverToDelete = Mock.of<RegularServer>({ id: 'abc123' }); const serverToDelete = fromPartial<RegularServer>({ id: 'abc123' });
const { payload } = deleteServer(serverToDelete); const { payload } = deleteServer(serverToDelete);
expect(payload).toEqual({ id: 'abc123' }); expect(payload).toEqual({ id: 'abc123' });
@ -122,7 +122,7 @@ describe('serversReducer', () => {
[true], [true],
[false], [false],
])('returns expected action', (autoConnect) => { ])('returns expected action', (autoConnect) => {
const serverToEdit = Mock.of<RegularServer>({ id: 'abc123' }); const serverToEdit = fromPartial<RegularServer>({ id: 'abc123' });
const { payload } = setAutoConnect(serverToEdit, autoConnect); const { payload } = setAutoConnect(serverToEdit, autoConnect);
expect(payload).toEqual({ serverId: 'abc123', 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 { ServersExporter } from '../../../src/servers/services/ServersExporter';
import type { LocalStorage } from '../../../src/utils/services/LocalStorage'; import type { LocalStorage } from '../../../src/utils/services/LocalStorage';
import { appendChild, removeChild, windowMock } from '../../__mocks__/Window.mock'; import { appendChild, removeChild, windowMock } from '../../__mocks__/Window.mock';
describe('ServersExporter', () => { describe('ServersExporter', () => {
const storageMock = Mock.of<LocalStorage>({ const storageMock = fromPartial<LocalStorage>({
get: jest.fn(() => ({ get: jest.fn(() => ({
abc123: { abc123: {
id: 'abc123', id: 'abc123',
@ -16,7 +16,7 @@ describe('ServersExporter', () => {
name: 'bar', name: 'bar',
autoConnect: false, autoConnect: false,
}, },
})), } as any)),
}); });
const erroneousToCsv = jest.fn(() => { const erroneousToCsv = jest.fn(() => {
throw new Error(''); throw new Error('');
@ -31,7 +31,7 @@ describe('ServersExporter', () => {
beforeEach(() => { beforeEach(() => {
originalConsole = global.console; originalConsole = global.console;
global.console = Mock.of<Console>({ error }); global.console = fromPartial<Console>({ error });
(global as any).Blob = class Blob {}; (global as any).Blob = class Blob {};
(global as any).URL = { createObjectURL: () => '' }; (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 type { RegularServer } from '../../../src/servers/data';
import { ServersImporter } from '../../../src/servers/services/ServersImporter'; import { ServersImporter } from '../../../src/servers/services/ServersImporter';
describe('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 csvjsonMock = jest.fn().mockResolvedValue(servers);
const readAsText = jest.fn(); const readAsText = jest.fn();
const fileReaderMock = Mock.of<FileReader>({ const fileReaderMock = fromPartial<FileReader>({
readAsText, readAsText,
addEventListener: (_eventName: string, listener: (e: ProgressEvent<FileReader>) => void) => listener( addEventListener: ((_eventName: string, listener: (e: ProgressEvent<FileReader>) => void) => listener(
Mock.of<ProgressEvent<FileReader>>({ target: { result: '' } }), fromPartial({ target: { result: '' } }),
), )) as any,
}); });
const importer = new ServersImporter(csvjsonMock, () => fileReaderMock); const importer = new ServersImporter(csvjsonMock, () => fileReaderMock);
@ -28,7 +28,7 @@ describe('ServersImporter', () => {
csvjsonMock.mockRejectedValue(expectedError); 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([ it.each([
@ -57,7 +57,7 @@ describe('ServersImporter', () => {
])('rejects with error if provided file does not parse to valid list of servers', async (parsedObject) => { ])('rejects with error if provided file does not parse to valid list of servers', async (parsedObject) => {
csvjsonMock.mockResolvedValue(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.'), new Error('Provided file does not have the right format.'),
); );
}); });
@ -78,7 +78,7 @@ describe('ServersImporter', () => {
csvjsonMock.mockResolvedValue(expectedServers); csvjsonMock.mockResolvedValue(expectedServers);
const result = await importer.importServersFromFile(Mock.all<File>()); const result = await importer.importServersFromFile(fromPartial({}));
expect(result).toEqual(expectedServers); expect(result).toEqual(expectedServers);
expect(readAsText).toHaveBeenCalledTimes(1); expect(readAsText).toHaveBeenCalledTimes(1);

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,5 +1,5 @@
import { screen } from '@testing-library/react'; 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 type { Settings } from '../../src/settings/reducers/settings';
import { VisitsSettings } from '../../src/settings/VisitsSettings'; import { VisitsSettings } from '../../src/settings/VisitsSettings';
import { renderWithEvents } from '../__helpers__/setUpTest'; import { renderWithEvents } from '../__helpers__/setUpTest';
@ -7,7 +7,7 @@ import { renderWithEvents } from '../__helpers__/setUpTest';
describe('<VisitsSettings />', () => { describe('<VisitsSettings />', () => {
const setVisitsSettings = jest.fn(); const setVisitsSettings = jest.fn();
const setUp = (settings: Partial<Settings> = {}) => renderWithEvents( const setUp = (settings: Partial<Settings> = {}) => renderWithEvents(
<VisitsSettings settings={Mock.of<Settings>(settings)} setVisitsSettings={setVisitsSettings} />, <VisitsSettings settings={fromPartial(settings)} setVisitsSettings={setVisitsSettings} />,
); );
afterEach(jest.clearAllMocks); afterEach(jest.clearAllMocks);
@ -21,10 +21,10 @@ describe('<VisitsSettings />', () => {
}); });
it.each([ it.each([
[Mock.all<Settings>(), 'Last 30 days'], [fromPartial<Settings>({}), 'Last 30 days'],
[Mock.of<Settings>({ visits: {} }), 'Last 30 days'], [fromPartial<Settings>({ visits: {} }), 'Last 30 days'],
[ [
Mock.of<Settings>({ fromPartial<Settings>({
visits: { visits: {
defaultInterval: 'last7Days', defaultInterval: 'last7Days',
}, },
@ -32,7 +32,7 @@ describe('<VisitsSettings />', () => {
'Last 7 days', 'Last 7 days',
], ],
[ [
Mock.of<Settings>({ fromPartial<Settings>({
visits: { visits: {
defaultInterval: 'today', defaultInterval: 'today',
}, },
@ -63,17 +63,17 @@ describe('<VisitsSettings />', () => {
it.each([ it.each([
[ [
Mock.all<Settings>(), fromPartial<Settings>({}),
/The visits coming from potential bots will be included.$/, /The visits coming from potential bots will be included.$/,
/The visits coming from potential bots will be excluded.$/, /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 included.$/,
/The visits coming from potential bots will be excluded.$/, /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 excluded.$/,
/The visits coming from potential bots will be included.$/, /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 type { ShlinkState } from '../../../src/container/types';
import { migrateDeprecatedSettings } from '../../../src/settings/helpers'; import { migrateDeprecatedSettings } from '../../../src/settings/helpers';
@ -9,7 +9,7 @@ describe('settings-helpers', () => {
}); });
it('updates settings as expected', () => { it('updates settings as expected', () => {
const state = Mock.of<ShlinkState>({ const state = fromPartial<ShlinkState>({
settings: { settings: {
visits: { visits: {
defaultInterval: 'last180days' as any, defaultInterval: 'last180days' as any,

View file

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

View file

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

View file

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

View file

@ -1,7 +1,7 @@
import { screen } from '@testing-library/react'; import { screen } from '@testing-library/react';
import type { UserEvent } from '@testing-library/user-event/setup/setup'; import type { UserEvent } from '@testing-library/user-event/setup/setup';
import { fromPartial } from '@total-typescript/shoehorn';
import { formatISO } from 'date-fns'; import { formatISO } from 'date-fns';
import { Mock } from 'ts-mockery';
import type { ReachableServer, SelectedServer } from '../../src/servers/data'; import type { ReachableServer, SelectedServer } from '../../src/servers/data';
import type { Mode } from '../../src/short-urls/ShortUrlForm'; import type { Mode } from '../../src/short-urls/ShortUrlForm';
import { ShortUrlForm as createShortUrlForm } from '../../src/short-urls/ShortUrlForm'; import { ShortUrlForm as createShortUrlForm } from '../../src/short-urls/ShortUrlForm';
@ -51,7 +51,7 @@ describe('<ShortUrlForm />', () => {
ios: 'https://ios.com', 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) => { ])('saves short URL with data set in form controls', async (extraFields, extraExpectedValues, selectedServer) => {
const { user } = setUp(selectedServer); const { user } = setUp(selectedServer);
@ -102,7 +102,7 @@ describe('<ShortUrlForm />', () => {
[undefined, false, undefined], [undefined, false, undefined],
['old title', false, null], ['old title', false, null],
])('sends expected title based on original and new values', async (originalTitle, withNewTitle, expectedSentTitle) => { ])('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.type(screen.getByPlaceholderText('URL to be shortened'), 'https://long-domain.com/foo/bar');
await user.clear(screen.getByPlaceholderText('Title')); await user.clear(screen.getByPlaceholderText('Title'));
@ -117,10 +117,10 @@ describe('<ShortUrlForm />', () => {
}); });
it.each([ it.each([
[Mock.of<ReachableServer>({ version: '3.0.0' }), false], [fromPartial<ReachableServer>({ version: '3.0.0' }), false],
[Mock.of<ReachableServer>({ version: '3.4.0' }), false], [fromPartial<ReachableServer>({ version: '3.4.0' }), false],
[Mock.of<ReachableServer>({ version: '3.5.0' }), true], [fromPartial<ReachableServer>({ version: '3.5.0' }), true],
[Mock.of<ReachableServer>({ version: '3.6.0' }), true], [fromPartial<ReachableServer>({ version: '3.6.0' }), true],
])('shows device-specific long URLs only for servers supporting it', (selectedServer, fieldsExist) => { ])('shows device-specific long URLs only for servers supporting it', (selectedServer, fieldsExist) => {
setUp(selectedServer); setUp(selectedServer);
const placeholders = ['Android-specific redirection', 'iOS-specific redirection', 'Desktop-specific redirection']; 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 { screen, waitFor } from '@testing-library/react';
import { fromPartial } from '@total-typescript/shoehorn';
import { endOfDay, formatISO, startOfDay } from 'date-fns'; import { endOfDay, formatISO, startOfDay } from 'date-fns';
import { MemoryRouter, useLocation, useNavigate } from 'react-router-dom'; import { MemoryRouter, useLocation, useNavigate } from 'react-router-dom';
import { Mock } from 'ts-mockery';
import type { ReachableServer, SelectedServer } from '../../src/servers/data'; 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 { ShortUrlsFilteringBar as filteringBarCreator } from '../../src/short-urls/ShortUrlsFilteringBar';
import { formatDate } from '../../src/utils/helpers/date'; import { formatDate } from '../../src/utils/helpers/date';
import type { DateRange } from '../../src/utils/helpers/dateIntervals'; import type { DateRange } from '../../src/utils/helpers/dateIntervals';
@ -28,10 +27,10 @@ describe('<ShortUrlsFilteringBar />', () => {
return renderWithEvents( return renderWithEvents(
<MemoryRouter> <MemoryRouter>
<ShortUrlsFilteringBar <ShortUrlsFilteringBar
selectedServer={selectedServer ?? Mock.all<SelectedServer>()} selectedServer={selectedServer ?? fromPartial({})}
order={{}} order={{}}
handleOrderBy={handleOrderBy} handleOrderBy={handleOrderBy}
settings={Mock.of<Settings>({ visits: {} })} settings={fromPartial({ visits: {} })}
/> />
</MemoryRouter>, </MemoryRouter>,
); );
@ -74,12 +73,12 @@ describe('<ShortUrlsFilteringBar />', () => {
}); });
it.each([ it.each([
['tags=foo,bar,baz', Mock.of<ReachableServer>({ version: '3.0.0' }), true], ['tags=foo,bar,baz', fromPartial<ReachableServer>({ version: '3.0.0' }), true],
['tags=foo,bar', Mock.of<ReachableServer>({ version: '3.1.0' }), true], ['tags=foo,bar', fromPartial<ReachableServer>({ version: '3.1.0' }), true],
['tags=foo', Mock.of<ReachableServer>({ version: '3.0.0' }), false], ['tags=foo', fromPartial<ReachableServer>({ version: '3.0.0' }), false],
['', Mock.of<ReachableServer>({ version: '3.0.0' }), false], ['', fromPartial<ReachableServer>({ version: '3.0.0' }), false],
['tags=foo,bar,baz', Mock.of<ReachableServer>({ version: '2.10.0' }), false], ['tags=foo,bar,baz', fromPartial<ReachableServer>({ version: '2.10.0' }), false],
['', Mock.of<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', 'renders tags mode toggle if the server supports it and there is more than one tag selected',
(search, selectedServer, shouldHaveComponent) => { (search, selectedServer, shouldHaveComponent) => {
@ -98,7 +97,7 @@ describe('<ShortUrlsFilteringBar />', () => {
['&tagsMode=all', 'With all the tags.'], ['&tagsMode=all', 'With all the tags.'],
['&tagsMode=any', 'With any of the tags.'], ['&tagsMode=any', 'With any of the tags.'],
])('expected tags mode tooltip title', async (initialTagsMode, expectedToggleText) => { ])('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')); await user.hover(screen.getByLabelText('Change tags mode'));
expect(await screen.findByRole('tooltip')).toHaveTextContent(expectedToggleText); expect(await screen.findByRole('tooltip')).toHaveTextContent(expectedToggleText);
@ -109,7 +108,7 @@ describe('<ShortUrlsFilteringBar />', () => {
['&tagsMode=all', 'tagsMode=any'], ['&tagsMode=all', 'tagsMode=any'],
['&tagsMode=any', 'tagsMode=all'], ['&tagsMode=any', 'tagsMode=all'],
])('redirects to first page when tags mode changes', async (initialTagsMode, expectedRedirectTagsMode) => { ])('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(); expect(navigate).not.toHaveBeenCalled();
await user.click(screen.getByLabelText('Change tags mode')); await user.click(screen.getByLabelText('Change tags mode'));
@ -127,7 +126,7 @@ describe('<ShortUrlsFilteringBar />', () => {
['excludePastValidUntil=false', /Exclude enabled in the past/, 'excludePastValidUntil=true'], ['excludePastValidUntil=false', /Exclude enabled in the past/, 'excludePastValidUntil=true'],
['excludePastValidUntil=true', /Exclude enabled in the past/, 'excludePastValidUntil=false'], ['excludePastValidUntil=true', /Exclude enabled in the past/, 'excludePastValidUntil=false'],
])('allows to toggle filters through filtering dropdown', async (search, menuItemName, expectedQuery) => { ])('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) => { const toggleFilter = async (name: RegExp) => {
await user.click(screen.getByRole('button', { name: 'Filters' })); await user.click(screen.getByRole('button', { name: 'Filters' }));
await waitFor(() => screen.findByRole('menu')); await waitFor(() => screen.findByRole('menu'));

View file

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

View file

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

View file

@ -1,6 +1,5 @@
import { screen } from '@testing-library/react'; import { screen } from '@testing-library/react';
import { Mock } from 'ts-mockery'; import { fromPartial } from '@total-typescript/shoehorn';
import type { ShortUrl } from '../../../src/short-urls/data';
import { CreateShortUrlResult as createResult } from '../../../src/short-urls/helpers/CreateShortUrlResult'; import { CreateShortUrlResult as createResult } from '../../../src/short-urls/helpers/CreateShortUrlResult';
import type { ShortUrlCreation } from '../../../src/short-urls/reducers/shortUrlCreation'; import type { ShortUrlCreation } from '../../../src/short-urls/reducers/shortUrlCreation';
import type { TimeoutToggle } from '../../../src/utils/helpers/hooks'; import type { TimeoutToggle } from '../../../src/utils/helpers/hooks';
@ -28,14 +27,14 @@ describe('<CreateShortUrlResult />', () => {
it('renders a result message when result is provided', () => { it('renders a result message when result is provided', () => {
setUp( 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'); 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 () => { it('Invokes tooltip timeout when copy to clipboard button is clicked', async () => {
const { user } = setUp( 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(); expect(copyToClipboard).not.toHaveBeenCalled();

View file

@ -1,6 +1,6 @@
import { screen, waitFor } from '@testing-library/react'; import { screen, waitFor } from '@testing-library/react';
import { Mock } from 'ts-mockery'; import { fromPartial } from '@total-typescript/shoehorn';
import type { InvalidShortUrlDeletion, ProblemDetailsError } from '../../../src/api/types/errors'; import type { InvalidShortUrlDeletion } from '../../../src/api/types/errors';
import { ErrorTypeV2, ErrorTypeV3 } from '../../../src/api/types/errors'; import { ErrorTypeV2, ErrorTypeV3 } from '../../../src/api/types/errors';
import type { ShortUrl } from '../../../src/short-urls/data'; import type { ShortUrl } from '../../../src/short-urls/data';
import { DeleteShortUrlModal } from '../../../src/short-urls/helpers/DeleteShortUrlModal'; import { DeleteShortUrlModal } from '../../../src/short-urls/helpers/DeleteShortUrlModal';
@ -9,7 +9,7 @@ import { renderWithEvents } from '../../__helpers__/setUpTest';
import { TestModalWrapper } from '../../__helpers__/TestModalWrapper'; import { TestModalWrapper } from '../../__helpers__/TestModalWrapper';
describe('<DeleteShortUrlModal />', () => { describe('<DeleteShortUrlModal />', () => {
const shortUrl = Mock.of<ShortUrl>({ const shortUrl = fromPartial<ShortUrl>({
tags: [], tags: [],
shortCode: 'abc123', shortCode: 'abc123',
longUrl: 'https://long-domain.com/foo/bar', longUrl: 'https://long-domain.com/foo/bar',
@ -22,7 +22,7 @@ describe('<DeleteShortUrlModal />', () => {
<DeleteShortUrlModal <DeleteShortUrlModal
{...args} {...args}
shortUrl={shortUrl} shortUrl={shortUrl}
shortUrlDeletion={Mock.of<ShortUrlDeletion>(shortUrlDeletion)} shortUrlDeletion={fromPartial(shortUrlDeletion)}
deleteShortUrl={deleteShortUrl} deleteShortUrl={deleteShortUrl}
shortUrlDeleted={shortUrlDeleted} shortUrlDeleted={shortUrlDeleted}
resetDeleteShortUrl={jest.fn()} resetDeleteShortUrl={jest.fn()}
@ -38,7 +38,7 @@ describe('<DeleteShortUrlModal />', () => {
loading: false, loading: false,
error: true, error: true,
shortCode: 'abc123', 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( expect(screen.getByText('Something went wrong while deleting the URL :(').parentElement).not.toHaveClass(
'bg-warning', 'bg-warning',
@ -46,8 +46,8 @@ describe('<DeleteShortUrlModal />', () => {
}); });
it.each([ it.each([
[Mock.of<InvalidShortUrlDeletion>({ type: ErrorTypeV3.INVALID_SHORT_URL_DELETION })], [fromPartial<InvalidShortUrlDeletion>({ type: ErrorTypeV3.INVALID_SHORT_URL_DELETION })],
[Mock.of<InvalidShortUrlDeletion>({ type: ErrorTypeV2.INVALID_SHORT_URL_DELETION })], [fromPartial<InvalidShortUrlDeletion>({ type: ErrorTypeV2.INVALID_SHORT_URL_DELETION })],
])('shows specific error when threshold error occurs', (errorData) => { ])('shows specific error when threshold error occurs', (errorData) => {
setUp({ loading: false, error: true, shortCode: 'abc123', errorData }); setUp({ loading: false, error: true, shortCode: 'abc123', errorData });
expect(screen.getByText('Something went wrong while deleting the URL :(').parentElement).toHaveClass('bg-warning'); 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 { screen } from '@testing-library/react';
import { fromPartial } from '@total-typescript/shoehorn';
import { MemoryRouter } from 'react-router-dom'; import { MemoryRouter } from 'react-router-dom';
import { Mock } from 'ts-mockery';
import type { ReportExporter } from '../../../src/common/services/ReportExporter'; 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 { ExportShortUrlsBtn as createExportShortUrlsBtn } from '../../../src/short-urls/helpers/ExportShortUrlsBtn';
import { renderWithEvents } from '../../__helpers__/setUpTest'; import { renderWithEvents } from '../../__helpers__/setUpTest';
@ -10,11 +10,11 @@ describe('<ExportShortUrlsBtn />', () => {
const listShortUrls = jest.fn(); const listShortUrls = jest.fn();
const buildShlinkApiClient = jest.fn().mockReturnValue({ listShortUrls }); const buildShlinkApiClient = jest.fn().mockReturnValue({ listShortUrls });
const exportShortUrls = jest.fn(); const exportShortUrls = jest.fn();
const reportExporter = Mock.of<ReportExporter>({ exportShortUrls }); const reportExporter = fromPartial<ReportExporter>({ exportShortUrls });
const ExportShortUrlsBtn = createExportShortUrlsBtn(buildShlinkApiClient, reportExporter); const ExportShortUrlsBtn = createExportShortUrlsBtn(buildShlinkApiClient, reportExporter);
const setUp = (amount?: number, selectedServer?: SelectedServer) => renderWithEvents( const setUp = (amount?: number, selectedServer?: SelectedServer) => renderWithEvents(
<MemoryRouter> <MemoryRouter>
<ExportShortUrlsBtn selectedServer={selectedServer ?? Mock.all<SelectedServer>()} amount={amount} /> <ExportShortUrlsBtn selectedServer={selectedServer ?? fromPartial({})} amount={amount} />
</MemoryRouter>, </MemoryRouter>,
); );
@ -31,7 +31,7 @@ describe('<ExportShortUrlsBtn />', () => {
it.each([ it.each([
[null], [null],
[Mock.of<NotFoundServer>()], [fromPartial<NotFoundServer>({})],
])('does nothing on click if selected server is not reachable', async (selectedServer) => { ])('does nothing on click if selected server is not reachable', async (selectedServer) => {
const { user } = setUp(0, selectedServer); const { user } = setUp(0, selectedServer);
@ -49,7 +49,7 @@ describe('<ExportShortUrlsBtn />', () => {
[385, 20], [385, 20],
])('loads proper amount of pages based on the amount of results', async (amount, expectedPageLoads) => { ])('loads proper amount of pages based on the amount of results', async (amount, expectedPageLoads) => {
listShortUrls.mockResolvedValue({ data: [] }); 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')); await user.click(screen.getByRole('button'));

View file

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

View file

@ -1,6 +1,6 @@
import { render, screen } from '@testing-library/react'; import { render, screen } from '@testing-library/react';
import { fromPartial } from '@total-typescript/shoehorn';
import { MemoryRouter } from 'react-router-dom'; import { MemoryRouter } from 'react-router-dom';
import { Mock } from 'ts-mockery';
import type { NotFoundServer, ReachableServer } from '../../../src/servers/data'; import type { NotFoundServer, ReachableServer } from '../../../src/servers/data';
import type { ShortUrl } from '../../../src/short-urls/data'; import type { ShortUrl } from '../../../src/short-urls/data';
import type { LinkSuffix } from '../../../src/short-urls/helpers/ShortUrlDetailLink'; import type { LinkSuffix } from '../../../src/short-urls/helpers/ShortUrlDetailLink';
@ -10,11 +10,11 @@ describe('<ShortUrlDetailLink />', () => {
it.each([ it.each([
[undefined, undefined], [undefined, undefined],
[null, null], [null, null],
[Mock.of<ReachableServer>({ id: '1' }), null], [fromPartial<ReachableServer>({ id: '1' }), null],
[Mock.of<ReachableServer>({ id: '1' }), undefined], [fromPartial<ReachableServer>({ id: '1' }), undefined],
[Mock.of<NotFoundServer>(), Mock.all<ShortUrl>()], [fromPartial<NotFoundServer>({}), fromPartial<ShortUrl>({})],
[null, Mock.all<ShortUrl>()], [null, fromPartial<ShortUrl>({})],
[undefined, Mock.all<ShortUrl>()], [undefined, fromPartial<ShortUrl>({})],
])('only renders a plain span when either server or short URL are not set', (selectedServer, shortUrl) => { ])('only renders a plain span when either server or short URL are not set', (selectedServer, shortUrl) => {
render( render(
<ShortUrlDetailLink selectedServer={selectedServer} shortUrl={shortUrl} suffix="visits"> <ShortUrlDetailLink selectedServer={selectedServer} shortUrl={shortUrl} suffix="visits">
@ -28,26 +28,26 @@ describe('<ShortUrlDetailLink />', () => {
it.each([ it.each([
[ [
Mock.of<ReachableServer>({ id: '1' }), fromPartial<ReachableServer>({ id: '1' }),
Mock.of<ShortUrl>({ shortCode: 'abc123' }), fromPartial<ShortUrl>({ shortCode: 'abc123' }),
'visits' as LinkSuffix, 'visits' as LinkSuffix,
'/server/1/short-code/abc123/visits', '/server/1/short-code/abc123/visits',
], ],
[ [
Mock.of<ReachableServer>({ id: '3' }), fromPartial<ReachableServer>({ id: '3' }),
Mock.of<ShortUrl>({ shortCode: 'def456', domain: 'example.com' }), fromPartial<ShortUrl>({ shortCode: 'def456', domain: 'example.com' }),
'visits' as LinkSuffix, 'visits' as LinkSuffix,
'/server/3/short-code/def456/visits?domain=example.com', '/server/3/short-code/def456/visits?domain=example.com',
], ],
[ [
Mock.of<ReachableServer>({ id: '1' }), fromPartial<ReachableServer>({ id: '1' }),
Mock.of<ShortUrl>({ shortCode: 'abc123' }), fromPartial<ShortUrl>({ shortCode: 'abc123' }),
'edit' as LinkSuffix, 'edit' as LinkSuffix,
'/server/1/short-code/abc123/edit', '/server/1/short-code/abc123/edit',
], ],
[ [
Mock.of<ReachableServer>({ id: '3' }), fromPartial<ReachableServer>({ id: '3' }),
Mock.of<ShortUrl>({ shortCode: 'def456', domain: 'example.com' }), fromPartial<ShortUrl>({ shortCode: 'def456', domain: 'example.com' }),
'edit' as LinkSuffix, 'edit' as LinkSuffix,
'/server/3/short-code/def456/edit?domain=example.com', '/server/3/short-code/def456/edit?domain=example.com',
], ],

View file

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

View file

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

View file

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

View file

@ -1,6 +1,6 @@
import { screen } from '@testing-library/react'; import { screen } from '@testing-library/react';
import { fromPartial } from '@total-typescript/shoehorn';
import { MemoryRouter } from 'react-router-dom'; import { MemoryRouter } from 'react-router-dom';
import { Mock } from 'ts-mockery';
import type { ReachableServer } from '../../../src/servers/data'; import type { ReachableServer } from '../../../src/servers/data';
import type { ShortUrl } from '../../../src/short-urls/data'; import type { ShortUrl } from '../../../src/short-urls/data';
import { ShortUrlsRowMenu as createShortUrlsRowMenu } from '../../../src/short-urls/helpers/ShortUrlsRowMenu'; import { ShortUrlsRowMenu as createShortUrlsRowMenu } from '../../../src/short-urls/helpers/ShortUrlsRowMenu';
@ -8,8 +8,8 @@ import { renderWithEvents } from '../../__helpers__/setUpTest';
describe('<ShortUrlsRowMenu />', () => { describe('<ShortUrlsRowMenu />', () => {
const ShortUrlsRowMenu = createShortUrlsRowMenu(() => <i>DeleteShortUrlModal</i>, () => <i>QrCodeModal</i>); const ShortUrlsRowMenu = createShortUrlsRowMenu(() => <i>DeleteShortUrlModal</i>, () => <i>QrCodeModal</i>);
const selectedServer = Mock.of<ReachableServer>({ id: 'abc123' }); const selectedServer = fromPartial<ReachableServer>({ id: 'abc123' });
const shortUrl = Mock.of<ShortUrl>({ const shortUrl = fromPartial<ShortUrl>({
shortCode: 'abc123', shortCode: 'abc123',
shortUrl: 'https://s.test/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 type { ShortUrl } from '../../../src/short-urls/data';
import { shortUrlDataFromShortUrl, urlDecodeShortCode, urlEncodeShortCode } from '../../../src/short-urls/helpers'; import { shortUrlDataFromShortUrl, urlDecodeShortCode, urlEncodeShortCode } from '../../../src/short-urls/helpers';
@ -8,7 +8,7 @@ describe('helpers', () => {
[undefined, { validateUrls: true }, { longUrl: '', validateUrl: true }], [undefined, { validateUrls: true }, { longUrl: '', validateUrl: true }],
[undefined, undefined, { longUrl: '', validateUrl: false }], [undefined, undefined, { longUrl: '', validateUrl: false }],
[ [
Mock.of<ShortUrl>({ meta: {} }), fromPartial<ShortUrl>({ meta: {} }),
{ validateUrls: false }, { validateUrls: false },
{ {
longUrl: undefined, longUrl: undefined,