Merge pull request #825 from acelaya-forks/feature/shoehorn

Introduce shoehorn as a possible replacement for ts-mockery
This commit is contained in:
Alejandro Celaya 2023-04-14 09:34:45 +02:00 committed by GitHub
commit 22905a2efc
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
108 changed files with 701 additions and 782 deletions

27
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",
@ -84,7 +85,6 @@
"resize-observer-polyfill": "^1.5.1", "resize-observer-polyfill": "^1.5.1",
"sass": "^1.57.1", "sass": "^1.57.1",
"stylelint": "^14.16.0", "stylelint": "^14.16.0",
"ts-mockery": "^1.2.0",
"typescript": "^5.0.2", "typescript": "^5.0.2",
"vite": "^4.2.0", "vite": "^4.2.0",
"vite-plugin-pwa": "^0.14.4" "vite-plugin-pwa": "^0.14.4"
@ -3563,6 +3563,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,
@ -11895,14 +11901,6 @@
"node": ">=8" "node": ">=8"
} }
}, },
"node_modules/ts-mockery": {
"version": "1.2.0",
"dev": true,
"license": "MIT",
"peerDependencies": {
"typescript": ">= 2.8"
}
},
"node_modules/ts-toolbelt": { "node_modules/ts-toolbelt": {
"version": "6.15.5", "version": "6.15.5",
"dev": true, "dev": true,
@ -14987,6 +14985,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
@ -20275,11 +20279,6 @@
"version": "3.0.1", "version": "3.0.1",
"dev": true "dev": true
}, },
"ts-mockery": {
"version": "1.2.0",
"dev": true,
"requires": {}
},
"ts-toolbelt": { "ts-toolbelt": {
"version": "6.15.5", "version": "6.15.5",
"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",
@ -101,7 +102,6 @@
"resize-observer-polyfill": "^1.5.1", "resize-observer-polyfill": "^1.5.1",
"sass": "^1.57.1", "sass": "^1.57.1",
"stylelint": "^14.16.0", "stylelint": "^14.16.0",
"ts-mockery": "^1.2.0",
"typescript": "^5.0.2", "typescript": "^5.0.2",
"vite": "^4.2.0", "vite": "^4.2.0",
"vite-plugin-pwa": "^0.14.4" "vite-plugin-pwa": "^0.14.4"

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,

View file

@ -1,16 +1,16 @@
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 { ShlinkState } from '../../../src/container/types'; import type { ShlinkState } from '../../../src/container/types';
import type { ShortUrl, ShortUrlData } from '../../../src/short-urls/data'; import type { ShortUrl } from '../../../src/short-urls/data';
import { import {
createShortUrl as createShortUrlCreator, createShortUrl as createShortUrlCreator,
shortUrlCreationReducerCreator, shortUrlCreationReducerCreator,
} from '../../../src/short-urls/reducers/shortUrlCreation'; } from '../../../src/short-urls/reducers/shortUrlCreation';
describe('shortUrlCreationReducer', () => { describe('shortUrlCreationReducer', () => {
const shortUrl = Mock.of<ShortUrl>(); const shortUrl = fromPartial<ShortUrl>({});
const createShortUrlCall = jest.fn(); const createShortUrlCall = jest.fn();
const buildShlinkApiClient = () => Mock.of<ShlinkApiClient>({ createShortUrl: createShortUrlCall }); const buildShlinkApiClient = () => fromPartial<ShlinkApiClient>({ createShortUrl: createShortUrlCall });
const createShortUrl = createShortUrlCreator(buildShlinkApiClient); const createShortUrl = createShortUrlCreator(buildShlinkApiClient);
const { reducer, resetCreateShortUrl } = shortUrlCreationReducerCreator(createShortUrl); const { reducer, resetCreateShortUrl } = shortUrlCreationReducerCreator(createShortUrl);
@ -18,7 +18,7 @@ describe('shortUrlCreationReducer', () => {
describe('reducer', () => { describe('reducer', () => {
it('returns loading on CREATE_SHORT_URL_START', () => { it('returns loading on CREATE_SHORT_URL_START', () => {
expect(reducer(undefined, createShortUrl.pending('', Mock.all<ShortUrlData>()))).toEqual({ expect(reducer(undefined, createShortUrl.pending('', fromPartial({})))).toEqual({
saving: true, saving: true,
saved: false, saved: false,
error: false, error: false,
@ -26,7 +26,7 @@ describe('shortUrlCreationReducer', () => {
}); });
it('returns error on CREATE_SHORT_URL_ERROR', () => { it('returns error on CREATE_SHORT_URL_ERROR', () => {
expect(reducer(undefined, createShortUrl.rejected(null, '', Mock.all<ShortUrlData>()))).toEqual({ expect(reducer(undefined, createShortUrl.rejected(null, '', fromPartial({})))).toEqual({
saving: false, saving: false,
saved: false, saved: false,
error: true, error: true,
@ -34,7 +34,7 @@ describe('shortUrlCreationReducer', () => {
}); });
it('returns result on CREATE_SHORT_URL', () => { it('returns result on CREATE_SHORT_URL', () => {
expect(reducer(undefined, createShortUrl.fulfilled(shortUrl, '', Mock.all<ShortUrlData>()))).toEqual({ expect(reducer(undefined, createShortUrl.fulfilled(shortUrl, '', fromPartial({})))).toEqual({
result: shortUrl, result: shortUrl,
saving: false, saving: false,
saved: true, saved: true,
@ -53,7 +53,7 @@ describe('shortUrlCreationReducer', () => {
describe('createShortUrl', () => { describe('createShortUrl', () => {
const dispatch = jest.fn(); const dispatch = jest.fn();
const getState = () => Mock.all<ShlinkState>(); const getState = () => fromPartial<ShlinkState>({});
it('calls API on success', async () => { it('calls API on success', async () => {
createShortUrlCall.mockResolvedValue(shortUrl); createShortUrlCall.mockResolvedValue(shortUrl);

View file

@ -1,15 +1,15 @@
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 { ProblemDetailsError } from '../../../src/api/types/errors'; import type { ProblemDetailsError } from '../../../src/api/types/errors';
import { import {
deleteShortUrl as deleteShortUrlCretor, deleteShortUrl as deleteShortUrlCreator,
shortUrlDeletionReducerCreator, shortUrlDeletionReducerCreator,
} from '../../../src/short-urls/reducers/shortUrlDeletion'; } from '../../../src/short-urls/reducers/shortUrlDeletion';
describe('shortUrlDeletionReducer', () => { describe('shortUrlDeletionReducer', () => {
const deleteShortUrlCall = jest.fn(); const deleteShortUrlCall = jest.fn();
const buildShlinkApiClient = () => Mock.of<ShlinkApiClient>({ deleteShortUrl: deleteShortUrlCall }); const buildShlinkApiClient = () => fromPartial<ShlinkApiClient>({ deleteShortUrl: deleteShortUrlCall });
const deleteShortUrl = deleteShortUrlCretor(buildShlinkApiClient); const deleteShortUrl = deleteShortUrlCreator(buildShlinkApiClient);
const { reducer, resetDeleteShortUrl } = shortUrlDeletionReducerCreator(deleteShortUrl); const { reducer, resetDeleteShortUrl } = shortUrlDeletionReducerCreator(deleteShortUrl);
beforeEach(jest.clearAllMocks); beforeEach(jest.clearAllMocks);
@ -40,7 +40,9 @@ describe('shortUrlDeletionReducer', () => {
})); }));
it('returns errorData on DELETE_SHORT_URL_ERROR', () => { it('returns errorData on DELETE_SHORT_URL_ERROR', () => {
const errorData = Mock.of<ProblemDetailsError>({ type: 'bar', detail: 'detail', title: 'title', status: 400 }); const errorData = fromPartial<ProblemDetailsError>(
{ type: 'bar', detail: 'detail', title: 'title', status: 400 },
);
const error = errorData as unknown as Error; const error = errorData as unknown as Error;
expect(reducer(undefined, deleteShortUrl.rejected(error, '', { shortCode: '' }))).toEqual({ expect(reducer(undefined, deleteShortUrl.rejected(error, '', { shortCode: '' }))).toEqual({

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 { ShlinkState } from '../../../src/container/types'; import type { ShlinkState } from '../../../src/container/types';
import type { ShortUrl } from '../../../src/short-urls/data'; import type { ShortUrl } from '../../../src/short-urls/data';
@ -7,7 +7,7 @@ import type { ShortUrlsList } from '../../../src/short-urls/reducers/shortUrlsLi
describe('shortUrlDetailReducer', () => { describe('shortUrlDetailReducer', () => {
const getShortUrlCall = jest.fn(); const getShortUrlCall = jest.fn();
const buildShlinkApiClient = () => Mock.of<ShlinkApiClient>({ getShortUrl: getShortUrlCall }); const buildShlinkApiClient = () => fromPartial<ShlinkApiClient>({ getShortUrl: getShortUrlCall });
const { reducer, getShortUrlDetail } = shortUrlDetailReducerCreator(buildShlinkApiClient); const { reducer, getShortUrlDetail } = shortUrlDetailReducerCreator(buildShlinkApiClient);
beforeEach(jest.clearAllMocks); beforeEach(jest.clearAllMocks);
@ -27,7 +27,7 @@ describe('shortUrlDetailReducer', () => {
}); });
it('return short URL on GET_SHORT_URL_DETAIL', () => { it('return short URL on GET_SHORT_URL_DETAIL', () => {
const actionShortUrl = Mock.of<ShortUrl>({ longUrl: 'foo', shortCode: 'bar' }); const actionShortUrl = fromPartial<ShortUrl>({ longUrl: 'foo', shortCode: 'bar' });
const state = reducer( const state = reducer(
{ loading: true, error: false }, { loading: true, error: false },
getShortUrlDetail.fulfilled(actionShortUrl, '', { shortCode: '' }), getShortUrlDetail.fulfilled(actionShortUrl, '', { shortCode: '' }),
@ -42,25 +42,25 @@ describe('shortUrlDetailReducer', () => {
describe('getShortUrlDetail', () => { describe('getShortUrlDetail', () => {
const dispatchMock = jest.fn(); const dispatchMock = jest.fn();
const buildGetState = (shortUrlsList?: ShortUrlsList) => () => Mock.of<ShlinkState>({ shortUrlsList }); const buildGetState = (shortUrlsList?: ShortUrlsList) => () => fromPartial<ShlinkState>({ shortUrlsList });
it.each([ it.each([
[undefined], [undefined],
[Mock.all<ShortUrlsList>()], [fromPartial<ShortUrlsList>({})],
[ [
Mock.of<ShortUrlsList>({ fromPartial<ShortUrlsList>({
shortUrls: { data: [] }, shortUrls: { data: [] },
}), }),
], ],
[ [
Mock.of<ShortUrlsList>({ fromPartial<ShortUrlsList>({
shortUrls: { shortUrls: {
data: [Mock.of<ShortUrl>({ shortCode: 'this_will_not_match' })], data: [{ shortCode: 'this_will_not_match' }],
}, },
}), }),
], ],
])('performs API call when short URL is not found in local state', async (shortUrlsList?: ShortUrlsList) => { ])('performs API call when short URL is not found in local state', async (shortUrlsList?: ShortUrlsList) => {
const resolvedShortUrl = Mock.of<ShortUrl>({ longUrl: 'foo', shortCode: 'abc123' }); const resolvedShortUrl = fromPartial<ShortUrl>({ longUrl: 'foo', shortCode: 'abc123' });
getShortUrlCall.mockResolvedValue(resolvedShortUrl); getShortUrlCall.mockResolvedValue(resolvedShortUrl);
await getShortUrlDetail({ shortCode: 'abc123', domain: '' })(dispatchMock, buildGetState(shortUrlsList), {}); await getShortUrlDetail({ shortCode: 'abc123', domain: '' })(dispatchMock, buildGetState(shortUrlsList), {});
@ -71,12 +71,12 @@ describe('shortUrlDetailReducer', () => {
}); });
it('avoids API calls when short URL is found in local state', async () => { it('avoids API calls when short URL is found in local state', async () => {
const foundShortUrl = Mock.of<ShortUrl>({ longUrl: 'foo', shortCode: 'abc123' }); const foundShortUrl = fromPartial<ShortUrl>({ longUrl: 'foo', shortCode: 'abc123' });
getShortUrlCall.mockResolvedValue(Mock.all<ShortUrl>()); getShortUrlCall.mockResolvedValue(fromPartial<ShortUrl>({}));
await getShortUrlDetail(foundShortUrl)( await getShortUrlDetail(foundShortUrl)(
dispatchMock, dispatchMock,
buildGetState(Mock.of<ShortUrlsList>({ buildGetState(fromPartial({
shortUrls: { shortUrls: {
data: [foundShortUrl], data: [foundShortUrl],
}, },

View file

@ -1,8 +1,7 @@
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 type { SelectedServer } from '../../../src/servers/data'; import type { SelectedServer } from '../../../src/servers/data';
import type { ShortUrl } from '../../../src/short-urls/data'; import type { ShortUrl } from '../../../src/short-urls/data';
import type { EditShortUrl } from '../../../src/short-urls/reducers/shortUrlEdition';
import { import {
editShortUrl as editShortUrlCreator, editShortUrl as editShortUrlCreator,
shortUrlEditionReducerCreator, shortUrlEditionReducerCreator,
@ -11,7 +10,7 @@ import {
describe('shortUrlEditionReducer', () => { describe('shortUrlEditionReducer', () => {
const longUrl = 'https://shlink.io'; const longUrl = 'https://shlink.io';
const shortCode = 'abc123'; const shortCode = 'abc123';
const shortUrl = Mock.of<ShortUrl>({ longUrl, shortCode }); const shortUrl = fromPartial<ShortUrl>({ longUrl, shortCode });
const updateShortUrl = jest.fn().mockResolvedValue(shortUrl); const updateShortUrl = jest.fn().mockResolvedValue(shortUrl);
const buildShlinkApiClient = jest.fn().mockReturnValue({ updateShortUrl }); const buildShlinkApiClient = jest.fn().mockReturnValue({ updateShortUrl });
const editShortUrl = editShortUrlCreator(buildShlinkApiClient); const editShortUrl = editShortUrlCreator(buildShlinkApiClient);
@ -21,7 +20,7 @@ describe('shortUrlEditionReducer', () => {
describe('reducer', () => { describe('reducer', () => {
it('returns loading on EDIT_SHORT_URL_START', () => { it('returns loading on EDIT_SHORT_URL_START', () => {
expect(reducer(undefined, editShortUrl.pending('', Mock.all<EditShortUrl>()))).toEqual({ expect(reducer(undefined, editShortUrl.pending('', fromPartial({})))).toEqual({
saving: true, saving: true,
saved: false, saved: false,
error: false, error: false,
@ -29,7 +28,7 @@ describe('shortUrlEditionReducer', () => {
}); });
it('returns error on EDIT_SHORT_URL_ERROR', () => { it('returns error on EDIT_SHORT_URL_ERROR', () => {
expect(reducer(undefined, editShortUrl.rejected(null, '', Mock.all<EditShortUrl>()))).toEqual({ expect(reducer(undefined, editShortUrl.rejected(null, '', fromPartial({})))).toEqual({
saving: false, saving: false,
saved: false, saved: false,
error: true, error: true,
@ -37,7 +36,7 @@ describe('shortUrlEditionReducer', () => {
}); });
it('returns provided tags and shortCode on SHORT_URL_EDITED', () => { it('returns provided tags and shortCode on SHORT_URL_EDITED', () => {
expect(reducer(undefined, editShortUrl.fulfilled(shortUrl, '', Mock.all<EditShortUrl>()))).toEqual({ expect(reducer(undefined, editShortUrl.fulfilled(shortUrl, '', fromPartial({})))).toEqual({
shortUrl, shortUrl,
saving: false, saving: false,
saved: true, saved: true,
@ -48,7 +47,9 @@ describe('shortUrlEditionReducer', () => {
describe('editShortUrl', () => { describe('editShortUrl', () => {
const dispatch = jest.fn(); const dispatch = jest.fn();
const createGetState = (selectedServer: SelectedServer = null) => () => Mock.of<ShlinkState>({ selectedServer }); const createGetState = (selectedServer: SelectedServer = null) => () => fromPartial<ShlinkState>({
selectedServer,
});
afterEach(jest.clearAllMocks); afterEach(jest.clearAllMocks);

View file

@ -1,10 +1,9 @@
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 { ShlinkPaginator, ShlinkShortUrlsResponse } from '../../../src/api/types'; import type { ShlinkShortUrlsResponse } from '../../../src/api/types';
import type { ShortUrl, ShortUrlData } from '../../../src/short-urls/data'; import type { ShortUrl } from '../../../src/short-urls/data';
import { createShortUrl as createShortUrlCreator } from '../../../src/short-urls/reducers/shortUrlCreation'; import { createShortUrl as createShortUrlCreator } from '../../../src/short-urls/reducers/shortUrlCreation';
import { shortUrlDeleted } from '../../../src/short-urls/reducers/shortUrlDeletion'; import { shortUrlDeleted } from '../../../src/short-urls/reducers/shortUrlDeletion';
import type { EditShortUrl } from '../../../src/short-urls/reducers/shortUrlEdition';
import { editShortUrl as editShortUrlCreator } from '../../../src/short-urls/reducers/shortUrlEdition'; import { editShortUrl as editShortUrlCreator } from '../../../src/short-urls/reducers/shortUrlEdition';
import { import {
listShortUrls as listShortUrlsCreator, listShortUrls as listShortUrlsCreator,
@ -16,7 +15,7 @@ import type { CreateVisit } from '../../../src/visits/types';
describe('shortUrlsListReducer', () => { describe('shortUrlsListReducer', () => {
const shortCode = 'abc123'; const shortCode = 'abc123';
const listShortUrlsMock = jest.fn(); const listShortUrlsMock = jest.fn();
const buildShlinkApiClient = () => Mock.of<ShlinkApiClient>({ listShortUrls: listShortUrlsMock }); const buildShlinkApiClient = () => fromPartial<ShlinkApiClient>({ listShortUrls: listShortUrlsMock });
const listShortUrls = listShortUrlsCreator(buildShlinkApiClient); const listShortUrls = listShortUrlsCreator(buildShlinkApiClient);
const editShortUrl = editShortUrlCreator(buildShlinkApiClient); const editShortUrl = editShortUrlCreator(buildShlinkApiClient);
const createShortUrl = createShortUrlCreator(buildShlinkApiClient); const createShortUrl = createShortUrlCreator(buildShlinkApiClient);
@ -32,7 +31,7 @@ describe('shortUrlsListReducer', () => {
})); }));
it('returns short URLs on LIST_SHORT_URLS', () => it('returns short URLs on LIST_SHORT_URLS', () =>
expect(reducer(undefined, listShortUrls.fulfilled(Mock.of<ShlinkShortUrlsResponse>({ data: [] }), ''))).toEqual({ expect(reducer(undefined, listShortUrls.fulfilled(fromPartial({ data: [] }), ''))).toEqual({
shortUrls: { data: [] }, shortUrls: { data: [] },
loading: false, loading: false,
error: false, error: false,
@ -46,21 +45,19 @@ describe('shortUrlsListReducer', () => {
it('removes matching URL and reduces total on SHORT_URL_DELETED', () => { it('removes matching URL and reduces total on SHORT_URL_DELETED', () => {
const state = { const state = {
shortUrls: Mock.of<ShlinkShortUrlsResponse>({ shortUrls: fromPartial<ShlinkShortUrlsResponse>({
data: [ data: [
Mock.of<ShortUrl>({ shortCode }), { shortCode },
Mock.of<ShortUrl>({ shortCode, domain: 'example.com' }), { shortCode, domain: 'example.com' },
Mock.of<ShortUrl>({ shortCode: 'foo' }), { shortCode: 'foo' },
], ],
pagination: Mock.of<ShlinkPaginator>({ pagination: { totalItems: 10 },
totalItems: 10,
}),
}), }),
loading: false, loading: false,
error: false, error: false,
}; };
expect(reducer(state, shortUrlDeleted(Mock.of<ShortUrl>({ shortCode })))).toEqual({ expect(reducer(state, shortUrlDeleted(fromPartial({ shortCode })))).toEqual({
shortUrls: { shortUrls: {
data: [{ shortCode, domain: 'example.com' }, { shortCode: 'foo' }], data: [{ shortCode, domain: 'example.com' }, { shortCode: 'foo' }],
pagination: { totalItems: 9 }, pagination: { totalItems: 9 },
@ -70,7 +67,7 @@ describe('shortUrlsListReducer', () => {
}); });
}); });
const createNewShortUrlVisit = (visitsCount: number) => Mock.of<CreateVisit>({ const createNewShortUrlVisit = (visitsCount: number) => fromPartial<CreateVisit>({
shortUrl: { shortCode: 'abc123', visitsCount }, shortUrl: { shortCode: 'abc123', visitsCount },
}); });
@ -81,11 +78,11 @@ describe('shortUrlsListReducer', () => {
[[], 10], [[], 10],
])('updates visits count on CREATE_VISITS', (createdVisits, expectedCount) => { ])('updates visits count on CREATE_VISITS', (createdVisits, expectedCount) => {
const state = { const state = {
shortUrls: Mock.of<ShlinkShortUrlsResponse>({ shortUrls: fromPartial<ShlinkShortUrlsResponse>({
data: [ data: [
Mock.of<ShortUrl>({ shortCode, domain: 'example.com', visitsCount: 5 }), { shortCode, domain: 'example.com', visitsCount: 5 },
Mock.of<ShortUrl>({ shortCode, visitsCount: 10 }), { shortCode, visitsCount: 10 },
Mock.of<ShortUrl>({ shortCode: 'foo', visitsCount: 8 }), { shortCode: 'foo', visitsCount: 8 },
], ],
}), }),
loading: false, loading: false,
@ -108,48 +105,46 @@ describe('shortUrlsListReducer', () => {
it.each([ it.each([
[ [
[ [
Mock.of<ShortUrl>({ shortCode }), fromPartial<ShortUrl>({ shortCode }),
Mock.of<ShortUrl>({ shortCode, domain: 'example.com' }), fromPartial<ShortUrl>({ shortCode, domain: 'example.com' }),
Mock.of<ShortUrl>({ shortCode: 'foo' }), fromPartial<ShortUrl>({ shortCode: 'foo' }),
], ],
[{ shortCode: 'newOne' }, { shortCode }, { shortCode, domain: 'example.com' }, { shortCode: 'foo' }], [{ shortCode: 'newOne' }, { shortCode }, { shortCode, domain: 'example.com' }, { shortCode: 'foo' }],
], ],
[ [
[ [
Mock.of<ShortUrl>({ shortCode }), fromPartial<ShortUrl>({ shortCode }),
Mock.of<ShortUrl>({ shortCode: 'code' }), fromPartial<ShortUrl>({ shortCode: 'code' }),
Mock.of<ShortUrl>({ shortCode: 'foo' }), fromPartial<ShortUrl>({ shortCode: 'foo' }),
Mock.of<ShortUrl>({ shortCode: 'bar' }), fromPartial<ShortUrl>({ shortCode: 'bar' }),
Mock.of<ShortUrl>({ shortCode: 'baz' }), fromPartial<ShortUrl>({ shortCode: 'baz' }),
], ],
[{ shortCode: 'newOne' }, { shortCode }, { shortCode: 'code' }, { shortCode: 'foo' }, { shortCode: 'bar' }], [{ shortCode: 'newOne' }, { shortCode }, { shortCode: 'code' }, { shortCode: 'foo' }, { shortCode: 'bar' }],
], ],
[ [
[ [
Mock.of<ShortUrl>({ shortCode }), fromPartial<ShortUrl>({ shortCode }),
Mock.of<ShortUrl>({ shortCode: 'code' }), fromPartial<ShortUrl>({ shortCode: 'code' }),
Mock.of<ShortUrl>({ shortCode: 'foo' }), fromPartial<ShortUrl>({ shortCode: 'foo' }),
Mock.of<ShortUrl>({ shortCode: 'bar' }), fromPartial<ShortUrl>({ shortCode: 'bar' }),
Mock.of<ShortUrl>({ shortCode: 'baz1' }), fromPartial<ShortUrl>({ shortCode: 'baz1' }),
Mock.of<ShortUrl>({ shortCode: 'baz2' }), fromPartial<ShortUrl>({ shortCode: 'baz2' }),
Mock.of<ShortUrl>({ shortCode: 'baz3' }), fromPartial<ShortUrl>({ shortCode: 'baz3' }),
], ],
[{ shortCode: 'newOne' }, { shortCode }, { shortCode: 'code' }, { shortCode: 'foo' }, { shortCode: 'bar' }], [{ shortCode: 'newOne' }, { shortCode }, { shortCode: 'code' }, { shortCode: 'foo' }, { shortCode: 'bar' }],
], ],
])('prepends new short URL and increases total on CREATE_SHORT_URL', (data, expectedData) => { ])('prepends new short URL and increases total on CREATE_SHORT_URL', (data, expectedData) => {
const newShortUrl = Mock.of<ShortUrl>({ shortCode: 'newOne' }); const newShortUrl = fromPartial<ShortUrl>({ shortCode: 'newOne' });
const state = { const state = {
shortUrls: Mock.of<ShlinkShortUrlsResponse>({ shortUrls: fromPartial<ShlinkShortUrlsResponse>({
data, data,
pagination: Mock.of<ShlinkPaginator>({ pagination: { totalItems: 15 },
totalItems: 15,
}),
}), }),
loading: false, loading: false,
error: false, error: false,
}; };
expect(reducer(state, createShortUrl.fulfilled(newShortUrl, '', Mock.all<ShortUrlData>()))).toEqual({ expect(reducer(state, createShortUrl.fulfilled(newShortUrl, '', fromPartial({})))).toEqual({
shortUrls: { shortUrls: {
data: expectedData, data: expectedData,
pagination: { totalItems: 16 }, pagination: { totalItems: 16 },
@ -161,16 +156,16 @@ describe('shortUrlsListReducer', () => {
it.each([ it.each([
((): [ShortUrl, ShortUrl[], ShortUrl[]] => { ((): [ShortUrl, ShortUrl[], ShortUrl[]] => {
const editedShortUrl = Mock.of<ShortUrl>({ shortCode: 'notMatching' }); const editedShortUrl = fromPartial<ShortUrl>({ shortCode: 'notMatching' });
const list = [Mock.of<ShortUrl>({ shortCode: 'foo' }), Mock.of<ShortUrl>({ shortCode: 'bar' })]; const list: ShortUrl[] = [fromPartial({ shortCode: 'foo' }), fromPartial({ shortCode: 'bar' })];
return [editedShortUrl, list, list]; return [editedShortUrl, list, list];
})(), })(),
((): [ShortUrl, ShortUrl[], ShortUrl[]] => { ((): [ShortUrl, ShortUrl[], ShortUrl[]] => {
const editedShortUrl = Mock.of<ShortUrl>({ shortCode: 'matching', longUrl: 'new_one' }); const editedShortUrl = fromPartial<ShortUrl>({ shortCode: 'matching', longUrl: 'new_one' });
const list = [ const list: ShortUrl[] = [
Mock.of<ShortUrl>({ shortCode: 'matching', longUrl: 'old_one' }), fromPartial({ shortCode: 'matching', longUrl: 'old_one' }),
Mock.of<ShortUrl>({ shortCode: 'bar' }), fromPartial({ shortCode: 'bar' }),
]; ];
const expectedList = [editedShortUrl, list[1]]; const expectedList = [editedShortUrl, list[1]];
@ -178,17 +173,15 @@ describe('shortUrlsListReducer', () => {
})(), })(),
])('updates matching short URL on SHORT_URL_EDITED', (editedShortUrl, initialList, expectedList) => { ])('updates matching short URL on SHORT_URL_EDITED', (editedShortUrl, initialList, expectedList) => {
const state = { const state = {
shortUrls: Mock.of<ShlinkShortUrlsResponse>({ shortUrls: fromPartial<ShlinkShortUrlsResponse>({
data: initialList, data: initialList,
pagination: Mock.of<ShlinkPaginator>({ pagination: { totalItems: 15 },
totalItems: 15,
}),
}), }),
loading: false, loading: false,
error: false, error: false,
}; };
const result = reducer(state, editShortUrl.fulfilled(editedShortUrl, '', Mock.of<EditShortUrl>())); const result = reducer(state, editShortUrl.fulfilled(editedShortUrl, '', fromPartial({})));
expect(result.shortUrls?.data).toEqual(expectedList); expect(result.shortUrls?.data).toEqual(expectedList);
}); });

View file

@ -1,8 +1,7 @@
import { screen, waitFor } from '@testing-library/react'; import { screen, waitFor } from '@testing-library/react';
import { fromPartial } from '@total-typescript/shoehorn';
import { identity } from 'ramda'; import { identity } from 'ramda';
import { Mock } from 'ts-mockery';
import type { MercureBoundProps } from '../../src/mercure/helpers/boundToMercureHub'; import type { MercureBoundProps } from '../../src/mercure/helpers/boundToMercureHub';
import type { Settings } from '../../src/settings/reducers/settings';
import type { TagsList } from '../../src/tags/reducers/tagsList'; import type { TagsList } from '../../src/tags/reducers/tagsList';
import type { TagsListProps } from '../../src/tags/TagsList'; import type { TagsListProps } from '../../src/tags/TagsList';
import { TagsList as createTagsList } from '../../src/tags/TagsList'; import { TagsList as createTagsList } from '../../src/tags/TagsList';
@ -13,12 +12,12 @@ describe('<TagsList />', () => {
const TagsListComp = createTagsList(({ sortedTags }) => <>TagsTable ({sortedTags.map((t) => t.visits).join(',')})</>); const TagsListComp = createTagsList(({ sortedTags }) => <>TagsTable ({sortedTags.map((t) => t.visits).join(',')})</>);
const setUp = (tagsList: Partial<TagsList>, excludeBots = false) => renderWithEvents( const setUp = (tagsList: Partial<TagsList>, excludeBots = false) => renderWithEvents(
<TagsListComp <TagsListComp
{...Mock.all<TagsListProps>()} {...fromPartial<TagsListProps>({})}
{...Mock.of<MercureBoundProps>({ mercureInfo: {} })} {...fromPartial<MercureBoundProps>({ mercureInfo: {} })}
forceListTags={identity} forceListTags={identity}
filterTags={filterTags} filterTags={filterTags}
tagsList={Mock.of<TagsList>(tagsList)} tagsList={fromPartial(tagsList)}
settings={Mock.of<Settings>({ visits: { excludeBots } })} settings={fromPartial({ visits: { excludeBots } })}
/>, />,
); );

View file

@ -1,8 +1,6 @@
import { screen } from '@testing-library/react'; import { screen } from '@testing-library/react';
import { fromPartial } from '@total-typescript/shoehorn';
import { useLocation } from 'react-router-dom'; import { useLocation } from 'react-router-dom';
import { Mock } from 'ts-mockery';
import type { SelectedServer } from '../../src/servers/data';
import type { SimplifiedTag } from '../../src/tags/data';
import { TagsTable as createTagsTable } from '../../src/tags/TagsTable'; import { TagsTable as createTagsTable } from '../../src/tags/TagsTable';
import { rangeOf } from '../../src/utils/utils'; import { rangeOf } from '../../src/utils/utils';
import { renderWithEvents } from '../__helpers__/setUpTest'; import { renderWithEvents } from '../__helpers__/setUpTest';
@ -17,8 +15,8 @@ describe('<TagsTable />', () => {
(useLocation as any).mockReturnValue({ search }); (useLocation as any).mockReturnValue({ search });
return renderWithEvents( return renderWithEvents(
<TagsTable <TagsTable
sortedTags={sortedTags.map((tag) => Mock.of<SimplifiedTag>({ tag }))} sortedTags={sortedTags.map((tag) => fromPartial({ tag }))}
selectedServer={Mock.all<SelectedServer>()} selectedServer={fromPartial({})}
currentOrder={{}} currentOrder={{}}
orderByColumn={() => orderByColumn} orderByColumn={() => orderByColumn}
/>, />,

View file

@ -1,7 +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 { TagsTableRow as createTagsTableRow } from '../../src/tags/TagsTableRow'; import { TagsTableRow as createTagsTableRow } from '../../src/tags/TagsTableRow';
import { renderWithEvents } from '../__helpers__/setUpTest'; import { renderWithEvents } from '../__helpers__/setUpTest';
import { colorGeneratorMock } from '../utils/services/__mocks__/ColorGenerator.mock'; import { colorGeneratorMock } from '../utils/services/__mocks__/ColorGenerator.mock';
@ -18,7 +17,7 @@ describe('<TagsTableRow />', () => {
<tbody> <tbody>
<TagsTableRow <TagsTableRow
tag={{ tag: 'foo&bar', visits: tagStats?.visits ?? 0, shortUrls: tagStats?.shortUrls ?? 0 }} tag={{ tag: 'foo&bar', visits: tagStats?.visits ?? 0, shortUrls: tagStats?.shortUrls ?? 0 }}
selectedServer={Mock.of<ReachableServer>({ id: 'abc123' })} selectedServer={fromPartial({ id: 'abc123' })}
/> />
</tbody> </tbody>
</table> </table>

View file

@ -1,17 +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 { ProblemDetailsError } from '../../../src/api/types/errors';
import { EditTagModal as createEditTagModal } from '../../../src/tags/helpers/EditTagModal'; import { EditTagModal as createEditTagModal } from '../../../src/tags/helpers/EditTagModal';
import type { TagEdition } from '../../../src/tags/reducers/tagEdit'; import type { TagEdition } from '../../../src/tags/reducers/tagEdit';
import type { ColorGenerator } from '../../../src/utils/services/ColorGenerator';
import { renderWithEvents } from '../../__helpers__/setUpTest'; import { renderWithEvents } from '../../__helpers__/setUpTest';
describe('<EditTagModal />', () => { describe('<EditTagModal />', () => {
const EditTagModal = createEditTagModal(Mock.of<ColorGenerator>({ getColorForKey: jest.fn(() => 'green') })); const EditTagModal = createEditTagModal(fromPartial({ getColorForKey: jest.fn(() => 'green') }));
const editTag = jest.fn().mockReturnValue(Promise.resolve()); const editTag = jest.fn().mockReturnValue(Promise.resolve());
const toggle = jest.fn(); const toggle = jest.fn();
const setUp = (tagEdit: Partial<TagEdition> = {}) => { const setUp = (tagEdit: Partial<TagEdition> = {}) => {
const edition = Mock.of<TagEdition>(tagEdit); const edition = fromPartial<TagEdition>(tagEdit);
return renderWithEvents( return renderWithEvents(
<EditTagModal isOpen tag="foo" tagEdit={edition} editTag={editTag} tagEdited={jest.fn()} toggle={toggle} />, <EditTagModal isOpen tag="foo" tagEdit={edition} editTag={editTag} tagEdited={jest.fn()} toggle={toggle} />,
); );
@ -43,7 +41,7 @@ describe('<EditTagModal />', () => {
[true, 1], [true, 1],
[false, 0], [false, 0],
])('displays error result in case of error', (error, expectedResultCount) => { ])('displays error result in case of error', (error, expectedResultCount) => {
setUp({ error, errorData: Mock.all<ProblemDetailsError>() }); setUp({ error, errorData: fromPartial({}) });
expect(screen.queryAllByText('Something went wrong while editing the tag :(')).toHaveLength(expectedResultCount); expect(screen.queryAllByText('Something went wrong while editing the tag :(')).toHaveLength(expectedResultCount);
}); });

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 type { ReactNode } from 'react'; import type { ReactNode } from 'react';
import { Mock } from 'ts-mockery';
import { Tag } from '../../../src/tags/helpers/Tag'; import { Tag } from '../../../src/tags/helpers/Tag';
import type { ColorGenerator } from '../../../src/utils/services/ColorGenerator'; import type { ColorGenerator } from '../../../src/utils/services/ColorGenerator';
import { MAIN_COLOR } from '../../../src/utils/theme'; import { MAIN_COLOR } from '../../../src/utils/theme';
@ -24,7 +24,7 @@ describe('<Tag />', () => {
const onClose = jest.fn(); const onClose = jest.fn();
const isColorLightForKey = jest.fn(() => false); const isColorLightForKey = jest.fn(() => false);
const getColorForKey = jest.fn(() => MAIN_COLOR); const getColorForKey = jest.fn(() => MAIN_COLOR);
const colorGenerator = Mock.of<ColorGenerator>({ getColorForKey, isColorLightForKey }); const colorGenerator = fromPartial<ColorGenerator>({ getColorForKey, isColorLightForKey });
const setUp = (text: string, clearable?: boolean, children?: ReactNode) => renderWithEvents( const setUp = (text: string, clearable?: boolean, children?: ReactNode) => renderWithEvents(
<Tag text={text} clearable={clearable} colorGenerator={colorGenerator} onClick={onClick} onClose={onClose}> <Tag text={text} clearable={clearable} colorGenerator={colorGenerator} onClick={onClick} onClose={onClose}>
{children} {children}

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 { Settings } from '../../../src/settings/reducers/settings';
import { TagsSelector as createTagsSelector } from '../../../src/tags/helpers/TagsSelector'; import { TagsSelector as createTagsSelector } from '../../../src/tags/helpers/TagsSelector';
import type { TagsList } from '../../../src/tags/reducers/tagsList'; import type { TagsList } from '../../../src/tags/reducers/tagsList';
import { renderWithEvents } from '../../__helpers__/setUpTest'; import { renderWithEvents } from '../../__helpers__/setUpTest';
@ -10,12 +9,12 @@ describe('<TagsSelector />', () => {
const onChange = jest.fn(); const onChange = jest.fn();
const TagsSelector = createTagsSelector(colorGeneratorMock); const TagsSelector = createTagsSelector(colorGeneratorMock);
const tags = ['foo', 'bar']; const tags = ['foo', 'bar'];
const tagsList = Mock.of<TagsList>({ tags: [...tags, 'baz'] }); const tagsList = fromPartial<TagsList>({ tags: [...tags, 'baz'] });
const setUp = () => renderWithEvents( const setUp = () => renderWithEvents(
<TagsSelector <TagsSelector
selectedTags={tags} selectedTags={tags}
tagsList={tagsList} tagsList={tagsList}
settings={Mock.all<Settings>()} settings={fromPartial({})}
listTags={jest.fn()} listTags={jest.fn()}
onChange={onChange} onChange={onChange}
/>, />,

View file

@ -1,11 +1,11 @@
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 { ShlinkState } from '../../../src/container/types'; import type { ShlinkState } from '../../../src/container/types';
import { tagDeleted, tagDeleteReducerCreator } from '../../../src/tags/reducers/tagDelete'; import { tagDeleted, tagDeleteReducerCreator } from '../../../src/tags/reducers/tagDelete';
describe('tagDeleteReducer', () => { describe('tagDeleteReducer', () => {
const deleteTagsCall = jest.fn(); const deleteTagsCall = jest.fn();
const buildShlinkApiClient = () => Mock.of<ShlinkApiClient>({ deleteTags: deleteTagsCall }); const buildShlinkApiClient = () => fromPartial<ShlinkApiClient>({ deleteTags: deleteTagsCall });
const { reducer, deleteTag } = tagDeleteReducerCreator(buildShlinkApiClient); const { reducer, deleteTag } = tagDeleteReducerCreator(buildShlinkApiClient);
beforeEach(jest.clearAllMocks); beforeEach(jest.clearAllMocks);
@ -44,7 +44,7 @@ describe('tagDeleteReducer', () => {
describe('deleteTag', () => { describe('deleteTag', () => {
const dispatch = jest.fn(); const dispatch = jest.fn();
const getState = () => Mock.all<ShlinkState>(); const getState = () => fromPartial<ShlinkState>({});
it('calls API on success', async () => { it('calls API on success', async () => {
const tag = 'foo'; const tag = 'foo';

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 { ShlinkState } from '../../../src/container/types'; import type { ShlinkState } from '../../../src/container/types';
import type { EditTag } from '../../../src/tags/reducers/tagEdit';
import { editTag as editTagCreator, tagEdited, tagEditReducerCreator } from '../../../src/tags/reducers/tagEdit'; import { editTag as editTagCreator, tagEdited, tagEditReducerCreator } from '../../../src/tags/reducers/tagEdit';
import type { ColorGenerator } from '../../../src/utils/services/ColorGenerator'; import type { ColorGenerator } from '../../../src/utils/services/ColorGenerator';
@ -10,14 +9,14 @@ describe('tagEditReducer', () => {
const newName = 'bar'; const newName = 'bar';
const color = '#ff0000'; const color = '#ff0000';
const editTagCall = jest.fn(); const editTagCall = jest.fn();
const buildShlinkApiClient = () => Mock.of<ShlinkApiClient>({ editTag: editTagCall }); const buildShlinkApiClient = () => fromPartial<ShlinkApiClient>({ editTag: editTagCall });
const colorGenerator = Mock.of<ColorGenerator>({ setColorForKey: jest.fn() }); const colorGenerator = fromPartial<ColorGenerator>({ setColorForKey: jest.fn() });
const editTag = editTagCreator(buildShlinkApiClient, colorGenerator); const editTag = editTagCreator(buildShlinkApiClient, colorGenerator);
const { reducer } = tagEditReducerCreator(editTag); const { reducer } = tagEditReducerCreator(editTag);
describe('reducer', () => { describe('reducer', () => {
it('returns loading on EDIT_TAG_START', () => { it('returns loading on EDIT_TAG_START', () => {
expect(reducer(undefined, editTag.pending('', Mock.all<EditTag>()))).toEqual({ expect(reducer(undefined, editTag.pending('', fromPartial({})))).toEqual({
editing: true, editing: true,
edited: false, edited: false,
error: false, error: false,
@ -25,7 +24,7 @@ describe('tagEditReducer', () => {
}); });
it('returns error on EDIT_TAG_ERROR', () => { it('returns error on EDIT_TAG_ERROR', () => {
expect(reducer(undefined, editTag.rejected(null, '', Mock.all<EditTag>()))).toEqual({ expect(reducer(undefined, editTag.rejected(null, '', fromPartial({})))).toEqual({
editing: false, editing: false,
edited: false, edited: false,
error: true, error: true,
@ -33,7 +32,7 @@ describe('tagEditReducer', () => {
}); });
it('returns tag names on EDIT_TAG', () => { it('returns tag names on EDIT_TAG', () => {
expect(reducer(undefined, editTag.fulfilled({ oldName, newName, color }, '', Mock.all<EditTag>()))).toEqual({ expect(reducer(undefined, editTag.fulfilled({ oldName, newName, color }, '', fromPartial({})))).toEqual({
editing: false, editing: false,
edited: true, edited: true,
error: false, error: false,
@ -52,7 +51,7 @@ describe('tagEditReducer', () => {
describe('editTag', () => { describe('editTag', () => {
const dispatch = jest.fn(); const dispatch = jest.fn();
const getState = () => Mock.of<ShlinkState>(); const getState = () => fromPartial<ShlinkState>({});
afterEach(jest.clearAllMocks); afterEach(jest.clearAllMocks);

View file

@ -1,6 +1,6 @@
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 type { ShortUrl, ShortUrlData } from '../../../src/short-urls/data'; import type { ShortUrl } from '../../../src/short-urls/data';
import { createShortUrl as createShortUrlCreator } from '../../../src/short-urls/reducers/shortUrlCreation'; import { createShortUrl as createShortUrlCreator } from '../../../src/short-urls/reducers/shortUrlCreation';
import { tagDeleted } from '../../../src/tags/reducers/tagDelete'; import { tagDeleted } from '../../../src/tags/reducers/tagDelete';
import { tagEdited } from '../../../src/tags/reducers/tagEdit'; import { tagEdited } from '../../../src/tags/reducers/tagEdit';
@ -12,10 +12,10 @@ import {
tagsListReducerCreator, tagsListReducerCreator,
} from '../../../src/tags/reducers/tagsList'; } from '../../../src/tags/reducers/tagsList';
import { createNewVisits } from '../../../src/visits/reducers/visitCreation'; import { createNewVisits } from '../../../src/visits/reducers/visitCreation';
import type { CreateVisit, Visit } from '../../../src/visits/types'; import type { CreateVisit } from '../../../src/visits/types';
describe('tagsListReducer', () => { describe('tagsListReducer', () => {
const state = (props: Partial<TagsList>) => Mock.of<TagsList>(props); const state = (props: Partial<TagsList>) => fromPartial<TagsList>(props);
const buildShlinkApiClient = jest.fn(); const buildShlinkApiClient = jest.fn();
const listTags = listTagsCreator(buildShlinkApiClient, true); const listTags = listTagsCreator(buildShlinkApiClient, true);
const createShortUrl = createShortUrlCreator(buildShlinkApiClient); const createShortUrl = createShortUrlCreator(buildShlinkApiClient);
@ -41,7 +41,7 @@ describe('tagsListReducer', () => {
it('returns provided tags as filtered and regular tags on LIST_TAGS', () => { it('returns provided tags as filtered and regular tags on LIST_TAGS', () => {
const tags = ['foo', 'bar', 'baz']; const tags = ['foo', 'bar', 'baz'];
expect(reducer(undefined, listTags.fulfilled(Mock.of<TagsList>({ tags }), ''))).toEqual({ expect(reducer(undefined, listTags.fulfilled(fromPartial({ tags }), ''))).toEqual({
tags, tags,
filteredTags: tags, filteredTags: tags,
loading: false, loading: false,
@ -114,30 +114,30 @@ describe('tagsListReducer', () => {
[['new', 'tag'], ['foo', 'bar', 'baz', 'foo2', 'fo', 'new', 'tag']], [['new', 'tag'], ['foo', 'bar', 'baz', 'foo2', 'fo', 'new', 'tag']],
])('appends new short URL\'s tags to the list of tags on CREATE_SHORT_URL', (shortUrlTags, expectedTags) => { ])('appends new short URL\'s tags to the list of tags on CREATE_SHORT_URL', (shortUrlTags, expectedTags) => {
const tags = ['foo', 'bar', 'baz', 'foo2', 'fo']; const tags = ['foo', 'bar', 'baz', 'foo2', 'fo'];
const payload = Mock.of<ShortUrl>({ tags: shortUrlTags }); const payload = fromPartial<ShortUrl>({ tags: shortUrlTags });
expect(reducer(state({ tags }), createShortUrl.fulfilled(payload, '', Mock.of<ShortUrlData>()))).toEqual({ expect(reducer(state({ tags }), createShortUrl.fulfilled(payload, '', fromPartial({})))).toEqual({
tags: expectedTags, tags: expectedTags,
}); });
}); });
it('increases amounts when visits are created', () => { it('increases amounts when visits are created', () => {
const createdVisits = [ const createdVisits: CreateVisit[] = [
Mock.of<CreateVisit>({ fromPartial({
shortUrl: Mock.of<ShortUrl>({ tags: ['foo', 'bar'] }), shortUrl: { tags: ['foo', 'bar'] },
visit: Mock.of<Visit>({ potentialBot: true }), visit: { potentialBot: true },
}), }),
Mock.of<CreateVisit>({ fromPartial({
shortUrl: Mock.of<ShortUrl>({ tags: ['foo', 'bar'] }), shortUrl: { tags: ['foo', 'bar'] },
visit: Mock.all<Visit>(), visit: {},
}), }),
Mock.of<CreateVisit>({ fromPartial({
shortUrl: Mock.of<ShortUrl>({ tags: ['bar'] }), shortUrl: { tags: ['bar'] },
visit: Mock.all<Visit>(), visit: {},
}), }),
Mock.of<CreateVisit>({ fromPartial({
shortUrl: Mock.of<ShortUrl>({ tags: ['baz'] }), shortUrl: { tags: ['baz'] },
visit: Mock.of<Visit>({ potentialBot: true }), visit: { potentialBot: true },
}), }),
]; ];
const tagStats = (total: number) => ({ const tagStats = (total: number) => ({
@ -197,11 +197,11 @@ describe('tagsListReducer', () => {
describe('listTags', () => { describe('listTags', () => {
const dispatch = jest.fn(); const dispatch = jest.fn();
const getState = jest.fn(() => Mock.all<ShlinkState>()); const getState = jest.fn(() => fromPartial<ShlinkState>({}));
const listTagsMock = jest.fn(); const listTagsMock = jest.fn();
const assertNoAction = async (tagsList: TagsList) => { const assertNoAction = async (tagsList: TagsList) => {
getState.mockReturnValue(Mock.of<ShlinkState>({ tagsList })); getState.mockReturnValue(fromPartial<ShlinkState>({ tagsList }));
await listTagsCreator(buildShlinkApiClient, false)()(dispatch, getState, {}); await listTagsCreator(buildShlinkApiClient, false)()(dispatch, getState, {});

View file

@ -1,12 +1,14 @@
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 { DropdownBtnMenuProps } from '../../src/utils/DropdownBtnMenu'; import type { DropdownBtnMenuProps } from '../../src/utils/DropdownBtnMenu';
import { DropdownBtnMenu } from '../../src/utils/DropdownBtnMenu'; import { DropdownBtnMenu } from '../../src/utils/DropdownBtnMenu';
import { renderWithEvents } from '../__helpers__/setUpTest'; import { renderWithEvents } from '../__helpers__/setUpTest';
describe('<DropdownBtnMenu />', () => { describe('<DropdownBtnMenu />', () => {
const setUp = (props: Partial<DropdownBtnMenuProps> = {}) => renderWithEvents( const setUp = (props: Partial<DropdownBtnMenuProps> = {}) => renderWithEvents(
<DropdownBtnMenu {...Mock.of<DropdownBtnMenuProps>({ toggle: jest.fn(), ...props })}>the children</DropdownBtnMenu>, <DropdownBtnMenu {...fromPartial<DropdownBtnMenuProps>({ toggle: jest.fn(), ...props })}>
the children
</DropdownBtnMenu>,
); );
it('renders expected components', () => { it('renders expected components', () => {

View file

@ -1,13 +1,13 @@
import { screen, waitFor } from '@testing-library/react'; import { screen, waitFor } from '@testing-library/react';
import { fromPartial } from '@total-typescript/shoehorn';
import { parseISO } from 'date-fns'; import { parseISO } from 'date-fns';
import { Mock } from 'ts-mockery';
import type { DateInputProps } from '../../../src/utils/dates/DateInput'; import type { DateInputProps } from '../../../src/utils/dates/DateInput';
import { DateInput } from '../../../src/utils/dates/DateInput'; import { DateInput } from '../../../src/utils/dates/DateInput';
import { renderWithEvents } from '../../__helpers__/setUpTest'; import { renderWithEvents } from '../../__helpers__/setUpTest';
describe('<DateInput />', () => { describe('<DateInput />', () => {
const setUp = (props: Partial<DateInputProps> = {}) => renderWithEvents( const setUp = (props: Partial<DateInputProps> = {}) => renderWithEvents(
<DateInput {...Mock.of<DateInputProps>(props)} />, <DateInput {...fromPartial<DateInputProps>(props)} />,
); );
it('shows calendar icon when input is not clearable', () => { it('shows calendar icon when input is not clearable', () => {

View file

@ -1,5 +1,5 @@
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 { DateRangeSelectorProps } from '../../../src/utils/dates/DateRangeSelector'; import type { DateRangeSelectorProps } from '../../../src/utils/dates/DateRangeSelector';
import { DateRangeSelector } from '../../../src/utils/dates/DateRangeSelector'; import { DateRangeSelector } from '../../../src/utils/dates/DateRangeSelector';
import type { DateInterval } from '../../../src/utils/helpers/dateIntervals'; import type { DateInterval } from '../../../src/utils/helpers/dateIntervals';
@ -10,7 +10,7 @@ describe('<DateRangeSelector />', () => {
const setUp = async (props: Partial<DateRangeSelectorProps> = {}) => { const setUp = async (props: Partial<DateRangeSelectorProps> = {}) => {
const result = renderWithEvents( const result = renderWithEvents(
<DateRangeSelector <DateRangeSelector
{...Mock.of<DateRangeSelectorProps>(props)} {...fromPartial<DateRangeSelectorProps>(props)}
defaultText="Default text" defaultText="Default text"
onDatesChange={onDatesChange} onDatesChange={onDatesChange}
/>, />,

View file

@ -1,4 +1,4 @@
import { Mock } from 'ts-mockery'; import { fromPartial } from '@total-typescript/shoehorn';
import type { SemVer, Versions } from '../../../src/utils/helpers/version'; import type { SemVer, Versions } from '../../../src/utils/helpers/version';
import { versionMatch } from '../../../src/utils/helpers/version'; import { versionMatch } from '../../../src/utils/helpers/version';
import type { Empty } from '../../../src/utils/utils'; import type { Empty } from '../../../src/utils/utils';
@ -6,19 +6,19 @@ import type { Empty } from '../../../src/utils/utils';
describe('version', () => { describe('version', () => {
describe('versionMatch', () => { describe('versionMatch', () => {
it.each([ it.each([
[undefined, Mock.all<Versions>(), false], [undefined, fromPartial<Versions>({}), false],
[null, Mock.all<Versions>(), false], [null, fromPartial<Versions>({}), false],
['' as Empty, Mock.all<Versions>(), false], ['' as Empty, fromPartial<Versions>({}), false],
[[], Mock.all<Versions>(), false], [[], fromPartial<Versions>({}), false],
['2.8.3' as SemVer, Mock.all<Versions>(), true], ['2.8.3' as SemVer, fromPartial<Versions>({}), true],
['2.8.3' as SemVer, Mock.of<Versions>({ minVersion: '2.0.0' }), true], ['2.8.3' as SemVer, fromPartial<Versions>({ minVersion: '2.0.0' }), true],
['2.0.0' as SemVer, Mock.of<Versions>({ minVersion: '2.0.0' }), true], ['2.0.0' as SemVer, fromPartial<Versions>({ minVersion: '2.0.0' }), true],
['1.8.0' as SemVer, Mock.of<Versions>({ maxVersion: '1.8.0' }), true], ['1.8.0' as SemVer, fromPartial<Versions>({ maxVersion: '1.8.0' }), true],
['1.7.1' as SemVer, Mock.of<Versions>({ maxVersion: '1.8.0' }), true], ['1.7.1' as SemVer, fromPartial<Versions>({ maxVersion: '1.8.0' }), true],
['1.7.3' as SemVer, Mock.of<Versions>({ minVersion: '1.7.0', maxVersion: '1.8.0' }), true], ['1.7.3' as SemVer, fromPartial<Versions>({ minVersion: '1.7.0', maxVersion: '1.8.0' }), true],
['1.8.3' as SemVer, Mock.of<Versions>({ minVersion: '2.0.0' }), false], ['1.8.3' as SemVer, fromPartial<Versions>({ minVersion: '2.0.0' }), false],
['1.8.3' as SemVer, Mock.of<Versions>({ maxVersion: '1.8.0' }), false], ['1.8.3' as SemVer, fromPartial<Versions>({ maxVersion: '1.8.0' }), false],
['1.8.3' as SemVer, Mock.of<Versions>({ minVersion: '1.7.0', maxVersion: '1.8.0' }), false], ['1.8.3' as SemVer, fromPartial<Versions>({ minVersion: '1.7.0', maxVersion: '1.8.0' }), false],
])('properly matches versions based on what is provided', (version, versionConstraints, expected) => { ])('properly matches versions based on what is provided', (version, versionConstraints, expected) => {
expect(versionMatch(version, versionConstraints)).toEqual(expected); expect(versionMatch(version, versionConstraints)).toEqual(expected);
}); });

View file

@ -1,11 +1,11 @@
import { Mock } from 'ts-mockery'; import { fromPartial } from '@total-typescript/shoehorn';
import { ColorGenerator } from '../../../src/utils/services/ColorGenerator'; import { ColorGenerator } from '../../../src/utils/services/ColorGenerator';
import type { LocalStorage } from '../../../src/utils/services/LocalStorage'; import type { LocalStorage } from '../../../src/utils/services/LocalStorage';
import { MAIN_COLOR } from '../../../src/utils/theme'; import { MAIN_COLOR } from '../../../src/utils/theme';
describe('ColorGenerator', () => { describe('ColorGenerator', () => {
let colorGenerator: ColorGenerator; let colorGenerator: ColorGenerator;
const storageMock = Mock.of<LocalStorage>({ const storageMock = fromPartial<LocalStorage>({
set: jest.fn(), set: jest.fn(),
get: jest.fn(), get: jest.fn(),
}); });

View file

@ -1,10 +1,10 @@
import { Mock } from 'ts-mockery'; import { fromPartial } from '@total-typescript/shoehorn';
import { LocalStorage } from '../../../src/utils/services/LocalStorage'; import { LocalStorage } from '../../../src/utils/services/LocalStorage';
describe('LocalStorage', () => { describe('LocalStorage', () => {
const getItem = jest.fn((key) => (key === 'shlink.foo' ? JSON.stringify({ foo: 'bar' }) : null)); const getItem = jest.fn((key) => (key === 'shlink.foo' ? JSON.stringify({ foo: 'bar' }) : null));
const setItem = jest.fn(); const setItem = jest.fn();
const localStorageMock = Mock.of<Storage>({ getItem, setItem }); const localStorageMock = fromPartial<Storage>({ getItem, setItem });
let storage: LocalStorage; let storage: LocalStorage;
beforeEach(() => { beforeEach(() => {

View file

@ -1,7 +1,7 @@
import { Mock } from 'ts-mockery'; import { fromPartial } from '@total-typescript/shoehorn';
import type { ColorGenerator } from '../../../../src/utils/services/ColorGenerator'; import type { ColorGenerator } from '../../../../src/utils/services/ColorGenerator';
export const colorGeneratorMock = Mock.of<ColorGenerator>({ export const colorGeneratorMock = fromPartial<ColorGenerator>({
getColorForKey: jest.fn(() => 'red'), getColorForKey: jest.fn(() => 'red'),
setColorForKey: jest.fn(), setColorForKey: jest.fn(),
isColorLightForKey: jest.fn(() => false), isColorLightForKey: jest.fn(() => false),

View file

@ -1,13 +1,10 @@
import { screen } from '@testing-library/react'; import { screen } from '@testing-library/react';
import { fromPartial } from '@total-typescript/shoehorn';
import { formatISO } from 'date-fns'; import { formatISO } from 'date-fns';
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 { MercureBoundProps } from '../../src/mercure/helpers/boundToMercureHub'; import type { MercureBoundProps } from '../../src/mercure/helpers/boundToMercureHub';
import type { Settings } from '../../src/settings/reducers/settings';
import { DomainVisits as createDomainVisits } from '../../src/visits/DomainVisits'; import { DomainVisits as createDomainVisits } from '../../src/visits/DomainVisits';
import type { DomainVisits } from '../../src/visits/reducers/domainVisits'; import type { DomainVisits } from '../../src/visits/reducers/domainVisits';
import type { Visit } from '../../src/visits/types';
import { renderWithEvents } from '../__helpers__/setUpTest'; import { renderWithEvents } from '../__helpers__/setUpTest';
jest.mock('react-router-dom', () => ({ jest.mock('react-router-dom', () => ({
@ -19,16 +16,16 @@ describe('<DomainVisits />', () => {
const exportVisits = jest.fn(); const exportVisits = jest.fn();
const getDomainVisits = jest.fn(); const getDomainVisits = jest.fn();
const cancelGetDomainVisits = jest.fn(); const cancelGetDomainVisits = jest.fn();
const domainVisits = Mock.of<DomainVisits>({ visits: [Mock.of<Visit>({ date: formatISO(new Date()) })] }); const domainVisits = fromPartial<DomainVisits>({ visits: [{ date: formatISO(new Date()) }] });
const DomainVisits = createDomainVisits(Mock.of<ReportExporter>({ exportVisits })); const DomainVisits = createDomainVisits(fromPartial({ exportVisits }));
const setUp = () => renderWithEvents( const setUp = () => renderWithEvents(
<MemoryRouter> <MemoryRouter>
<DomainVisits <DomainVisits
{...Mock.of<MercureBoundProps>({ mercureInfo: {} })} {...fromPartial<MercureBoundProps>({ mercureInfo: {} })}
getDomainVisits={getDomainVisits} getDomainVisits={getDomainVisits}
cancelGetDomainVisits={cancelGetDomainVisits} cancelGetDomainVisits={cancelGetDomainVisits}
domainVisits={domainVisits} domainVisits={domainVisits}
settings={Mock.all<Settings>()} settings={fromPartial({})}
/> />
</MemoryRouter>, </MemoryRouter>,
); );

View file

@ -1,29 +1,26 @@
import { screen } from '@testing-library/react'; import { screen } from '@testing-library/react';
import { fromPartial } from '@total-typescript/shoehorn';
import { formatISO } from 'date-fns'; import { formatISO } from 'date-fns';
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 { MercureBoundProps } from '../../src/mercure/helpers/boundToMercureHub'; import type { MercureBoundProps } from '../../src/mercure/helpers/boundToMercureHub';
import type { Settings } from '../../src/settings/reducers/settings';
import { NonOrphanVisits as createNonOrphanVisits } from '../../src/visits/NonOrphanVisits'; import { NonOrphanVisits as createNonOrphanVisits } from '../../src/visits/NonOrphanVisits';
import type { VisitsInfo } from '../../src/visits/reducers/types'; import type { VisitsInfo } from '../../src/visits/reducers/types';
import type { Visit } from '../../src/visits/types';
import { renderWithEvents } from '../__helpers__/setUpTest'; import { renderWithEvents } from '../__helpers__/setUpTest';
describe('<NonOrphanVisits />', () => { describe('<NonOrphanVisits />', () => {
const exportVisits = jest.fn(); const exportVisits = jest.fn();
const getNonOrphanVisits = jest.fn(); const getNonOrphanVisits = jest.fn();
const cancelGetNonOrphanVisits = jest.fn(); const cancelGetNonOrphanVisits = jest.fn();
const nonOrphanVisits = Mock.of<VisitsInfo>({ visits: [Mock.of<Visit>({ date: formatISO(new Date()) })] }); const nonOrphanVisits = fromPartial<VisitsInfo>({ visits: [{ date: formatISO(new Date()) }] });
const NonOrphanVisits = createNonOrphanVisits(Mock.of<ReportExporter>({ exportVisits })); const NonOrphanVisits = createNonOrphanVisits(fromPartial({ exportVisits }));
const setUp = () => renderWithEvents( const setUp = () => renderWithEvents(
<MemoryRouter> <MemoryRouter>
<NonOrphanVisits <NonOrphanVisits
{...Mock.of<MercureBoundProps>({ mercureInfo: {} })} {...fromPartial<MercureBoundProps>({ mercureInfo: {} })}
getNonOrphanVisits={getNonOrphanVisits} getNonOrphanVisits={getNonOrphanVisits}
cancelGetNonOrphanVisits={cancelGetNonOrphanVisits} cancelGetNonOrphanVisits={cancelGetNonOrphanVisits}
nonOrphanVisits={nonOrphanVisits} nonOrphanVisits={nonOrphanVisits}
settings={Mock.all<Settings>()} settings={fromPartial({})}
/> />
</MemoryRouter>, </MemoryRouter>,
); );

View file

@ -1,28 +1,25 @@
import { screen } from '@testing-library/react'; import { screen } from '@testing-library/react';
import { fromPartial } from '@total-typescript/shoehorn';
import { formatISO } from 'date-fns'; import { formatISO } from 'date-fns';
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 { MercureBoundProps } from '../../src/mercure/helpers/boundToMercureHub'; import type { MercureBoundProps } from '../../src/mercure/helpers/boundToMercureHub';
import type { Settings } from '../../src/settings/reducers/settings';
import { OrphanVisits as createOrphanVisits } from '../../src/visits/OrphanVisits'; import { OrphanVisits as createOrphanVisits } from '../../src/visits/OrphanVisits';
import type { VisitsInfo } from '../../src/visits/reducers/types'; import type { VisitsInfo } from '../../src/visits/reducers/types';
import type { Visit } from '../../src/visits/types';
import { renderWithEvents } from '../__helpers__/setUpTest'; import { renderWithEvents } from '../__helpers__/setUpTest';
describe('<OrphanVisits />', () => { describe('<OrphanVisits />', () => {
const getOrphanVisits = jest.fn(); const getOrphanVisits = jest.fn();
const exportVisits = jest.fn(); const exportVisits = jest.fn();
const orphanVisits = Mock.of<VisitsInfo>({ visits: [Mock.of<Visit>({ date: formatISO(new Date()) })] }); const orphanVisits = fromPartial<VisitsInfo>({ visits: [{ date: formatISO(new Date()) }] });
const OrphanVisits = createOrphanVisits(Mock.of<ReportExporter>({ exportVisits })); const OrphanVisits = createOrphanVisits(fromPartial({ exportVisits }));
const setUp = () => renderWithEvents( const setUp = () => renderWithEvents(
<MemoryRouter> <MemoryRouter>
<OrphanVisits <OrphanVisits
{...Mock.of<MercureBoundProps>({ mercureInfo: {} })} {...fromPartial<MercureBoundProps>({ mercureInfo: {} })}
getOrphanVisits={getOrphanVisits} getOrphanVisits={getOrphanVisits}
orphanVisits={orphanVisits} orphanVisits={orphanVisits}
cancelGetOrphanVisits={jest.fn()} cancelGetOrphanVisits={jest.fn()}
settings={Mock.all<Settings>()} settings={fromPartial({})}
/> />
</MemoryRouter>, </MemoryRouter>,
); );

View file

@ -1,33 +1,29 @@
import { screen } from '@testing-library/react'; import { screen } from '@testing-library/react';
import { fromPartial } from '@total-typescript/shoehorn';
import { formatISO } from 'date-fns'; import { formatISO } from 'date-fns';
import { identity } from 'ramda'; import { identity } from 'ramda';
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 { MercureBoundProps } from '../../src/mercure/helpers/boundToMercureHub'; import type { MercureBoundProps } from '../../src/mercure/helpers/boundToMercureHub';
import type { Settings } from '../../src/settings/reducers/settings';
import type { ShortUrlDetail } from '../../src/short-urls/reducers/shortUrlDetail';
import type { ShortUrlVisits as ShortUrlVisitsState } from '../../src/visits/reducers/shortUrlVisits'; import type { ShortUrlVisits as ShortUrlVisitsState } from '../../src/visits/reducers/shortUrlVisits';
import type { ShortUrlVisitsProps } from '../../src/visits/ShortUrlVisits'; import type { ShortUrlVisitsProps } from '../../src/visits/ShortUrlVisits';
import { ShortUrlVisits as createShortUrlVisits } from '../../src/visits/ShortUrlVisits'; import { ShortUrlVisits as createShortUrlVisits } from '../../src/visits/ShortUrlVisits';
import type { Visit } from '../../src/visits/types';
import { renderWithEvents } from '../__helpers__/setUpTest'; import { renderWithEvents } from '../__helpers__/setUpTest';
describe('<ShortUrlVisits />', () => { describe('<ShortUrlVisits />', () => {
const getShortUrlVisitsMock = jest.fn(); const getShortUrlVisitsMock = jest.fn();
const exportVisits = jest.fn(); const exportVisits = jest.fn();
const shortUrlVisits = Mock.of<ShortUrlVisitsState>({ visits: [Mock.of<Visit>({ date: formatISO(new Date()) })] }); const shortUrlVisits = fromPartial<ShortUrlVisitsState>({ visits: [{ date: formatISO(new Date()) }] });
const ShortUrlVisits = createShortUrlVisits(Mock.of<ReportExporter>({ exportVisits })); const ShortUrlVisits = createShortUrlVisits(fromPartial({ exportVisits }));
const setUp = () => renderWithEvents( const setUp = () => renderWithEvents(
<MemoryRouter> <MemoryRouter>
<ShortUrlVisits <ShortUrlVisits
{...Mock.all<ShortUrlVisitsProps>()} {...fromPartial<ShortUrlVisitsProps>({})}
{...Mock.of<MercureBoundProps>({ mercureInfo: {} })} {...fromPartial<MercureBoundProps>({ mercureInfo: {} })}
getShortUrlDetail={identity} getShortUrlDetail={identity}
getShortUrlVisits={getShortUrlVisitsMock} getShortUrlVisits={getShortUrlVisitsMock}
shortUrlVisits={shortUrlVisits} shortUrlVisits={shortUrlVisits}
shortUrlDetail={Mock.all<ShortUrlDetail>()} shortUrlDetail={fromPartial({})}
settings={Mock.all<Settings>()} settings={fromPartial({})}
cancelGetShortUrlVisits={() => {}} cancelGetShortUrlVisits={() => {}}
/> />
</MemoryRouter>, </MemoryRouter>,

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 { formatDistance, parseISO } from 'date-fns'; import { formatDistance, parseISO } from 'date-fns';
import { Mock } from 'ts-mockery';
import type { ShortUrlDetail } from '../../src/short-urls/reducers/shortUrlDetail'; import type { ShortUrlDetail } from '../../src/short-urls/reducers/shortUrlDetail';
import type { ShortUrlVisits } from '../../src/visits/reducers/shortUrlVisits'; import type { ShortUrlVisits } from '../../src/visits/reducers/shortUrlVisits';
import { ShortUrlVisitsHeader } from '../../src/visits/ShortUrlVisitsHeader'; import { ShortUrlVisitsHeader } from '../../src/visits/ShortUrlVisitsHeader';
@ -9,12 +9,12 @@ import { renderWithEvents } from '../__helpers__/setUpTest';
describe('<ShortUrlVisitsHeader />', () => { describe('<ShortUrlVisitsHeader />', () => {
const dateCreated = '2018-01-01T10:00:00+00:00'; const dateCreated = '2018-01-01T10:00:00+00:00';
const longUrl = 'https://foo.bar/bar/foo'; const longUrl = 'https://foo.bar/bar/foo';
const shortUrlVisits = Mock.of<ShortUrlVisits>({ const shortUrlVisits = fromPartial<ShortUrlVisits>({
visits: [{}, {}, {}], visits: [{}, {}, {}],
}); });
const goBack = jest.fn(); const goBack = jest.fn();
const setUp = (title?: string | null) => { const setUp = (title?: string | null) => {
const shortUrlDetail = Mock.of<ShortUrlDetail>({ const shortUrlDetail = fromPartial<ShortUrlDetail>({
shortUrl: { shortUrl: {
shortUrl: 'https://s.test/abc123', shortUrl: 'https://s.test/abc123',
longUrl, longUrl,

View file

@ -1,15 +1,11 @@
import { screen } from '@testing-library/react'; import { screen } from '@testing-library/react';
import { fromPartial } from '@total-typescript/shoehorn';
import { formatISO } from 'date-fns'; import { formatISO } from 'date-fns';
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 { MercureBoundProps } from '../../src/mercure/helpers/boundToMercureHub'; import type { MercureBoundProps } from '../../src/mercure/helpers/boundToMercureHub';
import type { Settings } from '../../src/settings/reducers/settings';
import type { ColorGenerator } from '../../src/utils/services/ColorGenerator';
import type { TagVisits as TagVisitsStats } from '../../src/visits/reducers/tagVisits'; import type { TagVisits as TagVisitsStats } from '../../src/visits/reducers/tagVisits';
import type { TagVisitsProps } from '../../src/visits/TagVisits'; import type { TagVisitsProps } from '../../src/visits/TagVisits';
import { TagVisits as createTagVisits } from '../../src/visits/TagVisits'; import { TagVisits as createTagVisits } from '../../src/visits/TagVisits';
import type { Visit } from '../../src/visits/types';
import { renderWithEvents } from '../__helpers__/setUpTest'; import { renderWithEvents } from '../__helpers__/setUpTest';
jest.mock('react-router-dom', () => ({ jest.mock('react-router-dom', () => ({
@ -20,19 +16,19 @@ jest.mock('react-router-dom', () => ({
describe('<TagVisits />', () => { describe('<TagVisits />', () => {
const getTagVisitsMock = jest.fn(); const getTagVisitsMock = jest.fn();
const exportVisits = jest.fn(); const exportVisits = jest.fn();
const tagVisits = Mock.of<TagVisitsStats>({ visits: [Mock.of<Visit>({ date: formatISO(new Date()) })] }); const tagVisits = fromPartial<TagVisitsStats>({ visits: [{ date: formatISO(new Date()) }] });
const TagVisits = createTagVisits( const TagVisits = createTagVisits(
Mock.of<ColorGenerator>({ isColorLightForKey: () => false, getColorForKey: () => 'red' }), fromPartial({ isColorLightForKey: () => false, getColorForKey: () => 'red' }),
Mock.of<ReportExporter>({ exportVisits }), fromPartial({ exportVisits }),
); );
const setUp = () => renderWithEvents( const setUp = () => renderWithEvents(
<MemoryRouter> <MemoryRouter>
<TagVisits <TagVisits
{...Mock.all<TagVisitsProps>()} {...fromPartial<TagVisitsProps>({})}
{...Mock.of<MercureBoundProps>({ mercureInfo: {} })} {...fromPartial<MercureBoundProps>({ mercureInfo: {} })}
getTagVisits={getTagVisitsMock} getTagVisits={getTagVisitsMock}
tagVisits={tagVisits} tagVisits={tagVisits}
settings={Mock.all<Settings>()} settings={fromPartial({})}
cancelGetTagVisits={() => {}} cancelGetTagVisits={() => {}}
/> />
</MemoryRouter>, </MemoryRouter>,

View file

@ -1,16 +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 { ColorGenerator } from '../../src/utils/services/ColorGenerator'; import type { ColorGenerator } from '../../src/utils/services/ColorGenerator';
import type { TagVisits } from '../../src/visits/reducers/tagVisits'; import type { TagVisits } from '../../src/visits/reducers/tagVisits';
import { TagVisitsHeader } from '../../src/visits/TagVisitsHeader'; import { TagVisitsHeader } from '../../src/visits/TagVisitsHeader';
describe('<TagVisitsHeader />', () => { describe('<TagVisitsHeader />', () => {
const tagVisits = Mock.of<TagVisits>({ const tagVisits = fromPartial<TagVisits>({
tag: 'foo', tag: 'foo',
visits: [{}, {}, {}, {}], visits: [{}, {}, {}, {}],
}); });
const goBack = jest.fn(); const goBack = jest.fn();
const colorGenerator = Mock.of<ColorGenerator>({ isColorLightForKey: () => false, getColorForKey: () => 'red' }); const colorGenerator = fromPartial<ColorGenerator>({ isColorLightForKey: () => false, getColorForKey: () => 'red' });
const setUp = () => render(<TagVisitsHeader tagVisits={tagVisits} goBack={goBack} colorGenerator={colorGenerator} />); const setUp = () => render(<TagVisitsHeader tagVisits={tagVisits} goBack={goBack} colorGenerator={colorGenerator} />);
it('shows expected visits', () => { it('shows expected visits', () => {

View file

@ -1,10 +1,10 @@
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 { Visit } from '../../src/visits/types'; import type { Visit } from '../../src/visits/types';
import { VisitsHeader } from '../../src/visits/VisitsHeader'; import { VisitsHeader } from '../../src/visits/VisitsHeader';
describe('<VisitsHeader />', () => { describe('<VisitsHeader />', () => {
const visits = [Mock.all<Visit>(), Mock.all<Visit>(), Mock.all<Visit>()]; const visits: Visit[] = [fromPartial({}), fromPartial({}), fromPartial({})];
const title = 'My header title'; const title = 'My header title';
const goBack = jest.fn(); const goBack = jest.fn();
const setUp = () => render(<VisitsHeader visits={visits} goBack={goBack} title={title} />); const setUp = () => render(<VisitsHeader visits={visits} goBack={goBack} title={title} />);

View file

@ -1,8 +1,7 @@
import { screen, waitFor } from '@testing-library/react'; import { screen, waitFor } 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 type { Settings } from '../../src/settings/reducers/settings';
import { rangeOf } from '../../src/utils/utils'; import { rangeOf } from '../../src/utils/utils';
import type { VisitsInfo } from '../../src/visits/reducers/types'; import type { VisitsInfo } from '../../src/visits/reducers/types';
import type { Visit } from '../../src/visits/types'; import type { Visit } from '../../src/visits/types';
@ -10,7 +9,7 @@ import { VisitsStats } from '../../src/visits/VisitsStats';
import { renderWithEvents } from '../__helpers__/setUpTest'; import { renderWithEvents } from '../__helpers__/setUpTest';
describe('<VisitsStats />', () => { describe('<VisitsStats />', () => {
const visits = rangeOf(3, () => Mock.of<Visit>({ date: '2020-01-01' })); const visits = rangeOf(3, () => fromPartial<Visit>({ date: '2020-01-01' }));
const getVisitsMock = jest.fn(); const getVisitsMock = jest.fn();
const exportCsv = jest.fn(); const exportCsv = jest.fn();
const setUp = (visitsInfo: Partial<VisitsInfo>, activeRoute = '/by-time') => { const setUp = (visitsInfo: Partial<VisitsInfo>, activeRoute = '/by-time') => {
@ -23,9 +22,9 @@ describe('<VisitsStats />', () => {
<Router location={history.location} navigator={history}> <Router location={history.location} navigator={history}>
<VisitsStats <VisitsStats
getVisits={getVisitsMock} getVisits={getVisitsMock}
visitsInfo={Mock.of<VisitsInfo>(visitsInfo)} visitsInfo={fromPartial(visitsInfo)}
cancelGetVisits={() => {}} cancelGetVisits={() => {}}
settings={Mock.all<Settings>()} settings={fromPartial({})}
exportCsv={exportCsv} exportCsv={exportCsv}
/> />
</Router>, </Router>,

View file

@ -1,5 +1,5 @@
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 { rangeOf } from '../../src/utils/utils'; import { rangeOf } from '../../src/utils/utils';
import type { NormalizedVisit } from '../../src/visits/types'; import type { NormalizedVisit } from '../../src/visits/types';
import type { VisitsTableProps } from '../../src/visits/VisitsTable'; import type { VisitsTableProps } from '../../src/visits/VisitsTable';
@ -7,7 +7,7 @@ import { VisitsTable } from '../../src/visits/VisitsTable';
import { renderWithEvents } from '../__helpers__/setUpTest'; import { renderWithEvents } from '../__helpers__/setUpTest';
describe('<VisitsTable />', () => { describe('<VisitsTable />', () => {
const matchMedia = () => Mock.of<MediaQueryList>({ matches: false }); const matchMedia = () => fromPartial<MediaQueryList>({ matches: false });
const setSelectedVisits = jest.fn(); const setSelectedVisits = jest.fn();
const setUpFactory = (props: Partial<VisitsTableProps> = {}) => renderWithEvents( const setUpFactory = (props: Partial<VisitsTableProps> = {}) => renderWithEvents(
<VisitsTable <VisitsTable
@ -23,8 +23,8 @@ describe('<VisitsTable />', () => {
const setUpForOrphanVisits = (isOrphanVisits: boolean) => setUpFactory({ isOrphanVisits }); const setUpForOrphanVisits = (isOrphanVisits: boolean) => setUpFactory({ isOrphanVisits });
const setUpWithBots = () => setUpFactory({ const setUpWithBots = () => setUpFactory({
visits: [ visits: [
Mock.of<NormalizedVisit>({ potentialBot: false, date: '2022-05-05' }), fromPartial({ potentialBot: false, date: '2022-05-05' }),
Mock.of<NormalizedVisit>({ potentialBot: true, date: '2022-05-05' }), fromPartial({ potentialBot: true, date: '2022-05-05' }),
], ],
}); });
@ -48,7 +48,7 @@ describe('<VisitsTable />', () => {
[115, 7, 2], // This one will have ellipsis [115, 7, 2], // This one will have ellipsis
])('renders the expected amount of pages', (visitsCount, expectedAmountOfPageItems, expectedDisabledItems) => { ])('renders the expected amount of pages', (visitsCount, expectedAmountOfPageItems, expectedDisabledItems) => {
const { container } = setUp( const { container } = setUp(
rangeOf(visitsCount, () => Mock.of<NormalizedVisit>({ browser: '', date: '2022-01-01', referer: '' })), rangeOf(visitsCount, () => fromPartial<NormalizedVisit>({ browser: '', date: '2022-01-01', referer: '' })),
); );
expect(container.querySelectorAll('.page-item')).toHaveLength(expectedAmountOfPageItems); expect(container.querySelectorAll('.page-item')).toHaveLength(expectedAmountOfPageItems);
expect(container.querySelectorAll('.disabled')).toHaveLength(expectedDisabledItems); expect(container.querySelectorAll('.disabled')).toHaveLength(expectedDisabledItems);
@ -58,7 +58,7 @@ describe('<VisitsTable />', () => {
rangeOf(20, (value) => [value]), rangeOf(20, (value) => [value]),
)('does not render footer when there is only one page to render', (visitsCount) => { )('does not render footer when there is only one page to render', (visitsCount) => {
const { container } = setUp( const { container } = setUp(
rangeOf(visitsCount, () => Mock.of<NormalizedVisit>({ browser: '', date: '2022-01-01', referer: '' })), rangeOf(visitsCount, () => fromPartial<NormalizedVisit>({ browser: '', date: '2022-01-01', referer: '' })),
); );
expect(container.querySelector('tfoot')).not.toBeInTheDocument(); expect(container.querySelector('tfoot')).not.toBeInTheDocument();
@ -66,7 +66,7 @@ describe('<VisitsTable />', () => {
}); });
it('selected rows are highlighted', async () => { it('selected rows are highlighted', async () => {
const visits = rangeOf(10, () => Mock.of<NormalizedVisit>({ browser: '', date: '2022-01-01', referer: '' })); const visits = rangeOf(10, () => fromPartial<NormalizedVisit>({ browser: '', date: '2022-01-01', referer: '' }));
const { container, user } = setUp(visits, [visits[1], visits[2]]); const { container, user } = setUp(visits, [visits[1], visits[2]]);
// Initial situation // Initial situation
@ -86,7 +86,7 @@ describe('<VisitsTable />', () => {
}); });
it('orders visits when column is clicked', async () => { it('orders visits when column is clicked', async () => {
const { user } = setUp(rangeOf(9, (index) => Mock.of<NormalizedVisit>({ const { user } = setUp(rangeOf(9, (index) => fromPartial<NormalizedVisit>({
browser: '', browser: '',
date: `2022-01-0${10 - index}`, date: `2022-01-0${10 - index}`,
referer: `${index}`, referer: `${index}`,
@ -108,8 +108,8 @@ describe('<VisitsTable />', () => {
it('filters list when writing in search box', async () => { it('filters list when writing in search box', async () => {
const { user } = setUp([ const { user } = setUp([
...rangeOf(7, () => Mock.of<NormalizedVisit>({ browser: 'aaa', date: '2022-01-01', referer: 'aaa' })), ...rangeOf(7, () => fromPartial<NormalizedVisit>({ browser: 'aaa', date: '2022-01-01', referer: 'aaa' })),
...rangeOf(2, () => Mock.of<NormalizedVisit>({ browser: 'bbb', date: '2022-01-01', referer: 'bbb' })), ...rangeOf(2, () => fromPartial<NormalizedVisit>({ browser: 'bbb', date: '2022-01-01', referer: 'bbb' })),
]); ]);
const searchField = screen.getByPlaceholderText('Search...'); const searchField = screen.getByPlaceholderText('Search...');
const searchText = async (text: string) => { const searchText = async (text: string) => {

View file

@ -1,14 +1,14 @@
import { render, screen } from '@testing-library/react'; import { render, screen } from '@testing-library/react';
import { fromPartial } from '@total-typescript/shoehorn';
import type { Chart, ChartDataset } from 'chart.js'; import type { Chart, ChartDataset } from 'chart.js';
import { Mock } from 'ts-mockery';
import { DoughnutChartLegend } from '../../../src/visits/charts/DoughnutChartLegend'; import { DoughnutChartLegend } from '../../../src/visits/charts/DoughnutChartLegend';
describe('<DoughnutChartLegend />', () => { describe('<DoughnutChartLegend />', () => {
const labels = ['foo', 'bar', 'baz', 'foo2', 'bar2']; const labels = ['foo', 'bar', 'baz', 'foo2', 'bar2'];
const colors = ['green', 'blue', 'yellow']; const colors = ['green', 'blue', 'yellow'];
const defaultColor = 'red'; const defaultColor = 'red';
const datasets = [Mock.of<ChartDataset>({ backgroundColor: colors })]; const datasets = [fromPartial<ChartDataset>({ backgroundColor: colors })];
const chart = Mock.of<Chart>({ const chart = fromPartial<Chart>({
config: { config: {
data: { labels, datasets }, data: { labels, datasets },
options: { defaultColor } as any, options: { defaultColor } as any,

View file

@ -1,7 +1,7 @@
import { screen } from '@testing-library/react'; import { screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event'; import userEvent from '@testing-library/user-event';
import { fromPartial } from '@total-typescript/shoehorn';
import { formatISO, subDays, subMonths, subYears } from 'date-fns'; import { formatISO, subDays, subMonths, subYears } from 'date-fns';
import { Mock } from 'ts-mockery';
import { LineChartCard } from '../../../src/visits/charts/LineChartCard'; import { LineChartCard } from '../../../src/visits/charts/LineChartCard';
import type { NormalizedVisit } from '../../../src/visits/types'; import type { NormalizedVisit } from '../../../src/visits/types';
import { setUpCanvas } from '../../__helpers__/setUpTest'; import { setUpCanvas } from '../../__helpers__/setUpTest';
@ -29,7 +29,7 @@ describe('<LineChartCard />', () => {
visits, visits,
expectedActiveIndex, expectedActiveIndex,
) => { ) => {
const { user } = setUp(visits.map((visit) => Mock.of<NormalizedVisit>(visit))); const { user } = setUp(visits.map((visit) => fromPartial(visit)));
await user.click(screen.getByRole('button', { name: /Group by/ })); await user.click(screen.getByRole('button', { name: /Group by/ }));
@ -46,8 +46,8 @@ describe('<LineChartCard />', () => {
it.each([ it.each([
[undefined, undefined], [undefined, undefined],
[[], []], [[], []],
[[Mock.of<NormalizedVisit>({ date: '2016-04-01' })], []], [[fromPartial<NormalizedVisit>({ date: '2016-04-01' })], []],
[[Mock.of<NormalizedVisit>({ date: '2016-04-01' })], [Mock.of<NormalizedVisit>({ date: '2016-04-01' })]], [[fromPartial<NormalizedVisit>({ date: '2016-04-01' })], [fromPartial<NormalizedVisit>({ date: '2016-04-01' })]],
])('renders chart with expected data', (visits, highlightedVisits) => { ])('renders chart with expected data', (visits, highlightedVisits) => {
const { events } = setUp(visits, highlightedVisits); const { events } = setUp(visits, highlightedVisits);
@ -57,8 +57,8 @@ describe('<LineChartCard />', () => {
it('includes stats for visits with no dates if selected', async () => { it('includes stats for visits with no dates if selected', async () => {
const { getEvents, user } = setUp([ const { getEvents, user } = setUp([
Mock.of<NormalizedVisit>({ date: '2016-04-01' }), fromPartial({ date: '2016-04-01' }),
Mock.of<NormalizedVisit>({ date: '2016-01-01' }), fromPartial({ date: '2016-01-01' }),
]); ]);
const eventsBefore = getEvents(); const eventsBefore = getEvents();

View file

@ -1,14 +1,14 @@
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 { OpenMapModalBtn } from '../../../src/visits/helpers/OpenMapModalBtn'; import { OpenMapModalBtn } from '../../../src/visits/helpers/OpenMapModalBtn';
import type { CityStats } from '../../../src/visits/types'; import type { CityStats } from '../../../src/visits/types';
import { renderWithEvents } from '../../__helpers__/setUpTest'; import { renderWithEvents } from '../../__helpers__/setUpTest';
describe('<OpenMapModalBtn />', () => { describe('<OpenMapModalBtn />', () => {
const title = 'Foo'; const title = 'Foo';
const locations = [ const locations: CityStats[] = [
Mock.of<CityStats>({ cityName: 'foo', count: 30, latLong: [5, 5] }), fromPartial({ cityName: 'foo', count: 30, latLong: [5, 5] }),
Mock.of<CityStats>({ cityName: 'bar', count: 45, latLong: [88, 88] }), fromPartial({ cityName: 'bar', count: 45, latLong: [88, 88] }),
]; ];
const setUp = (activeCities?: string[]) => renderWithEvents( const setUp = (activeCities?: string[]) => renderWithEvents(
<OpenMapModalBtn modalTitle={title} locations={locations} activeCities={activeCities} />, <OpenMapModalBtn modalTitle={title} locations={locations} activeCities={activeCities} />,

View file

@ -1,5 +1,5 @@
import { fromPartial } from '@total-typescript/shoehorn';
import { addDays, formatISO, subDays } from 'date-fns'; import { addDays, formatISO, subDays } from 'date-fns';
import { Mock } from 'ts-mockery';
import type { ShlinkApiClient } from '../../../src/api/services/ShlinkApiClient'; import type { ShlinkApiClient } from '../../../src/api/services/ShlinkApiClient';
import type { ShlinkVisits } from '../../../src/api/types'; import type { ShlinkVisits } from '../../../src/api/types';
import type { ShlinkState } from '../../../src/container/types'; import type { ShlinkState } from '../../../src/container/types';
@ -16,25 +16,25 @@ import {
getDomainVisits as getDomainVisitsCreator, getDomainVisits as getDomainVisitsCreator,
} from '../../../src/visits/reducers/domainVisits'; } from '../../../src/visits/reducers/domainVisits';
import { createNewVisits } from '../../../src/visits/reducers/visitCreation'; import { createNewVisits } from '../../../src/visits/reducers/visitCreation';
import type { CreateVisit, Visit } from '../../../src/visits/types'; import type { Visit } from '../../../src/visits/types';
describe('domainVisitsReducer', () => { describe('domainVisitsReducer', () => {
const now = new Date(); const now = new Date();
const visitsMocks = rangeOf(2, () => Mock.all<Visit>()); const visitsMocks = rangeOf(2, () => fromPartial<Visit>({}));
const getDomainVisitsCall = jest.fn(); const getDomainVisitsCall = jest.fn();
const buildApiClientMock = () => Mock.of<ShlinkApiClient>({ getDomainVisits: getDomainVisitsCall }); const buildApiClientMock = () => fromPartial<ShlinkApiClient>({ getDomainVisits: getDomainVisitsCall });
const getDomainVisits = getDomainVisitsCreator(buildApiClientMock); const getDomainVisits = getDomainVisitsCreator(buildApiClientMock);
const { reducer, cancelGetVisits: cancelGetDomainVisits } = domainVisitsReducerCreator(getDomainVisits); const { reducer, cancelGetVisits: cancelGetDomainVisits } = domainVisitsReducerCreator(getDomainVisits);
beforeEach(jest.clearAllMocks); beforeEach(jest.clearAllMocks);
describe('reducer', () => { describe('reducer', () => {
const buildState = (data: Partial<DomainVisits>) => Mock.of<DomainVisits>(data); const buildState = (data: Partial<DomainVisits>) => fromPartial<DomainVisits>(data);
it('returns loading on GET_DOMAIN_VISITS_START', () => { it('returns loading on GET_DOMAIN_VISITS_START', () => {
const { loading } = reducer( const { loading } = reducer(
buildState({ loading: false }), buildState({ loading: false }),
getDomainVisits.pending('', Mock.all<LoadDomainVisits>()), getDomainVisits.pending('', fromPartial<LoadDomainVisits>({})),
); );
expect(loading).toEqual(true); expect(loading).toEqual(true);
}); });
@ -52,7 +52,7 @@ describe('domainVisitsReducer', () => {
it('stops loading and returns error on GET_DOMAIN_VISITS_ERROR', () => { it('stops loading and returns error on GET_DOMAIN_VISITS_ERROR', () => {
const state = reducer( const state = reducer(
buildState({ loading: true, error: false }), buildState({ loading: true, error: false }),
getDomainVisits.rejected(null, '', Mock.all<LoadDomainVisits>()), getDomainVisits.rejected(null, '', fromPartial({})),
); );
const { loading, error } = state; const { loading, error } = state;
@ -61,10 +61,10 @@ describe('domainVisitsReducer', () => {
}); });
it('return visits on GET_DOMAIN_VISITS', () => { it('return visits on GET_DOMAIN_VISITS', () => {
const actionVisits = [Mock.all<Visit>(), Mock.all<Visit>()]; const actionVisits: Visit[] = [fromPartial({}), fromPartial({})];
const { loading, error, visits } = reducer( const { loading, error, visits } = reducer(
buildState({ loading: true, error: false }), buildState({ loading: true, error: false }),
getDomainVisits.fulfilled({ visits: actionVisits }, '', Mock.all<LoadDomainVisits>()), getDomainVisits.fulfilled({ visits: actionVisits }, '', fromPartial({})),
); );
expect(loading).toEqual(false); expect(loading).toEqual(false);
@ -75,10 +75,10 @@ describe('domainVisitsReducer', () => {
it.each([ it.each([
[{ domain: 'foo.com' }, 'foo.com', visitsMocks.length + 1], [{ domain: 'foo.com' }, 'foo.com', visitsMocks.length + 1],
[{ domain: 'bar.com' }, 'foo.com', visitsMocks.length], [{ domain: 'bar.com' }, 'foo.com', visitsMocks.length],
[Mock.of<DomainVisits>({ domain: 'foo.com' }), 'foo.com', visitsMocks.length + 1], [fromPartial<DomainVisits>({ domain: 'foo.com' }), 'foo.com', visitsMocks.length + 1],
[Mock.of<DomainVisits>({ domain: DEFAULT_DOMAIN }), null, visitsMocks.length + 1], [fromPartial<DomainVisits>({ domain: DEFAULT_DOMAIN }), null, visitsMocks.length + 1],
[ [
Mock.of<DomainVisits>({ fromPartial<DomainVisits>({
domain: 'foo.com', domain: 'foo.com',
query: { endDate: formatIsoDate(subDays(now, 1)) ?? undefined }, query: { endDate: formatIsoDate(subDays(now, 1)) ?? undefined },
}), }),
@ -86,7 +86,7 @@ describe('domainVisitsReducer', () => {
visitsMocks.length, visitsMocks.length,
], ],
[ [
Mock.of<DomainVisits>({ fromPartial<DomainVisits>({
domain: 'foo.com', domain: 'foo.com',
query: { startDate: formatIsoDate(addDays(now, 1)) ?? undefined }, query: { startDate: formatIsoDate(addDays(now, 1)) ?? undefined },
}), }),
@ -94,7 +94,7 @@ describe('domainVisitsReducer', () => {
visitsMocks.length, visitsMocks.length,
], ],
[ [
Mock.of<DomainVisits>({ fromPartial<DomainVisits>({
domain: 'foo.com', domain: 'foo.com',
query: { query: {
startDate: formatIsoDate(subDays(now, 5)) ?? undefined, startDate: formatIsoDate(subDays(now, 5)) ?? undefined,
@ -105,7 +105,7 @@ describe('domainVisitsReducer', () => {
visitsMocks.length, visitsMocks.length,
], ],
[ [
Mock.of<DomainVisits>({ fromPartial<DomainVisits>({
domain: 'foo.com', domain: 'foo.com',
query: { query: {
startDate: formatIsoDate(subDays(now, 5)) ?? undefined, startDate: formatIsoDate(subDays(now, 5)) ?? undefined,
@ -116,7 +116,7 @@ describe('domainVisitsReducer', () => {
visitsMocks.length + 1, visitsMocks.length + 1,
], ],
[ [
Mock.of<DomainVisits>({ fromPartial<DomainVisits>({
domain: 'bar.com', domain: 'bar.com',
query: { query: {
startDate: formatIsoDate(subDays(now, 5)) ?? undefined, startDate: formatIsoDate(subDays(now, 5)) ?? undefined,
@ -127,9 +127,9 @@ describe('domainVisitsReducer', () => {
visitsMocks.length, visitsMocks.length,
], ],
])('prepends new visits on CREATE_VISIT', (state, shortUrlDomain, expectedVisits) => { ])('prepends new visits on CREATE_VISIT', (state, shortUrlDomain, expectedVisits) => {
const shortUrl = Mock.of<ShortUrl>({ domain: shortUrlDomain }); const shortUrl = fromPartial<ShortUrl>({ domain: shortUrlDomain });
const { visits } = reducer(buildState({ ...state, visits: visitsMocks }), createNewVisits([ const { visits } = reducer(buildState({ ...state, visits: visitsMocks }), createNewVisits([
Mock.of<CreateVisit>({ shortUrl, visit: { date: formatIsoDate(now) ?? undefined } }), fromPartial({ shortUrl, visit: { date: formatIsoDate(now) ?? undefined } }),
])); ]));
expect(visits).toHaveLength(expectedVisits); expect(visits).toHaveLength(expectedVisits);
@ -153,7 +153,7 @@ describe('domainVisitsReducer', () => {
describe('getDomainVisits', () => { describe('getDomainVisits', () => {
const dispatchMock = jest.fn(); const dispatchMock = jest.fn();
const getState = () => Mock.of<ShlinkState>({ const getState = () => fromPartial<ShlinkState>({
domainVisits: { cancelLoad: false }, domainVisits: { cancelLoad: false },
}); });
const domain = 'foo.com'; const domain = 'foo.com';
@ -183,12 +183,12 @@ describe('domainVisitsReducer', () => {
it.each([ it.each([
[ [
[Mock.of<Visit>({ date: formatISO(subDays(now, 20)) })], [fromPartial<Visit>({ date: formatISO(subDays(now, 20)) })],
getDomainVisits.fallbackToInterval('last30Days'), getDomainVisits.fallbackToInterval('last30Days'),
3, 3,
], ],
[ [
[Mock.of<Visit>({ date: formatISO(subDays(now, 100)) })], [fromPartial<Visit>({ date: formatISO(subDays(now, 100)) })],
getDomainVisits.fallbackToInterval('last180Days'), getDomainVisits.fallbackToInterval('last180Days'),
3, 3,
], ],

Some files were not shown because too many files have changed in this diff Show more