Migrate from ESLint to Oxlint and Oxfmt

This commit is contained in:
Alejandro Celaya
2026-07-26 09:12:18 +02:00
parent 2cf85a18b3
commit 54d4d9ebad
81 changed files with 2004 additions and 3790 deletions

View File

@@ -1,14 +1,12 @@
import type { FC, ReactElement } from 'react';
import { useCallback, useEffect , useState } from 'react';
import { useCallback, useEffect, useState } from 'react';
export type RenderModalArgs = {
open: boolean;
onClose: () => void;
};
export const TestModalWrapper: FC<{ renderModal: (args: RenderModalArgs) => ReactElement }> = (
{ renderModal },
) => {
export const TestModalWrapper: FC<{ renderModal: (args: RenderModalArgs) => ReactElement }> = ({ renderModal }) => {
const [open, setOpen] = useState(true);
const onClose = useCallback(() => setOpen(false), []);

View File

@@ -44,12 +44,15 @@ describe('ShlinkApiClientBuilder', () => {
await apiClient.health();
expect(jsonRequest).toHaveBeenCalledWith(expect.stringMatching(new RegExp(`^${url}`)), expect.objectContaining({
credentials: undefined,
headers: {
'X-Api-Key': apiKey,
},
}));
expect(jsonRequest).toHaveBeenCalledWith(
expect.stringMatching(new RegExp(`^${url}`)),
expect.objectContaining({
credentials: undefined,
headers: {
'X-Api-Key': apiKey,
},
}),
);
});
it('includes credentials when forwarding is enabled', async () => {
@@ -61,11 +64,14 @@ describe('ShlinkApiClientBuilder', () => {
await apiClient.health();
expect(jsonRequest).toHaveBeenCalledWith(expect.stringMatching(new RegExp(`^${url}`)), expect.objectContaining({
credentials: 'include',
headers: {
'X-Api-Key': apiKey,
},
}));
expect(jsonRequest).toHaveBeenCalledWith(
expect.stringMatching(new RegExp(`^${url}`)),
expect.objectContaining({
credentials: 'include',
headers: {
'X-Api-Key': apiKey,
},
}),
);
});
});

View File

@@ -13,29 +13,32 @@ vi.mock(import('../../src/common/ShlinkWebComponentContainer'), () => ({
}));
describe('<App />', () => {
const setUp = async (activeRoute = '/') => act(() => renderWithStore(
<MemoryRouter initialEntries={[{ pathname: activeRoute }]}>
<ContainerProvider
value={fromPartial({
HttpClient: fromPartial<HttpClient>({}),
buildShlinkApiClient: vi.fn(),
useTimeoutToggle: vi.fn().mockReturnValue([false, vi.fn()]),
})}
>
<App />
</ContainerProvider>
</MemoryRouter>,
{
initialState: {
servers: {
abc123: fromPartial<ServerWithId>({ id: 'abc123', name: 'abc123 server' }),
def456: fromPartial<ServerWithId>({ id: 'def456', name: 'def456 server' }),
const setUp = async (activeRoute = '/') =>
act(() =>
renderWithStore(
<MemoryRouter initialEntries={[{ pathname: activeRoute }]}>
<ContainerProvider
value={fromPartial({
HttpClient: fromPartial<HttpClient>({}),
buildShlinkApiClient: vi.fn(),
useTimeoutToggle: vi.fn().mockReturnValue([false, vi.fn()]),
})}
>
<App />
</ContainerProvider>
</MemoryRouter>,
{
initialState: {
servers: {
abc123: fromPartial<ServerWithId>({ id: 'abc123', name: 'abc123 server' }),
def456: fromPartial<ServerWithId>({ id: 'def456', name: 'def456 server' }),
},
settings: fromPartial({}),
appUpdated: false,
},
},
settings: fromPartial({}),
appUpdated: false,
},
},
));
),
);
it('passes a11y checks', () => checkAccessibility(setUp()));

View File

@@ -7,8 +7,8 @@ describe('<AppUpdateBanner />', () => {
const onClose = vi.fn();
const forceUpdate = vi.fn();
const setUp = async () => {
const result = await act(
() => renderWithEvents(<AppUpdateBanner isOpen onClose={onClose} forceUpdate={forceUpdate} />),
const result = await act(() =>
renderWithEvents(<AppUpdateBanner isOpen onClose={onClose} forceUpdate={forceUpdate} />),
);
await waitFor(() => screen.getByRole('alert'));

View File

@@ -7,18 +7,18 @@ import { checkAccessibility } from '../__helpers__/accessibility';
import { renderWithStore } from '../__helpers__/setUpTest';
describe('<Home />', () => {
const setUp = (servers: ServersMap = {}) => renderWithStore(
<MemoryRouter>
<Home />
</MemoryRouter>,
{
initialState: { servers },
},
);
const setUp = (servers: ServersMap = {}) =>
renderWithStore(
<MemoryRouter>
<Home />
</MemoryRouter>,
{
initialState: { servers },
},
);
it('passes a11y checks', () => checkAccessibility(
setUp({ '1a': fromPartial<ServerWithId>({ name: 'foo', id: '1' }) }),
));
it('passes a11y checks', () =>
checkAccessibility(setUp({ '1a': fromPartial<ServerWithId>({ name: 'foo', id: '1' }) })));
it('renders title', () => {
setUp();

View File

@@ -33,7 +33,8 @@ describe('<MainHeader />', () => {
])('sets link to settings as active only when current path is settings', (currentPath, isActive) => {
setUp(currentPath);
expect(screen.getByRole('menuitem', { name: /Settings$/ })).toHaveAttribute(
'data-active', isActive ? 'true' : 'false',
'data-active',
isActive ? 'true' : 'false',
);
});
});

View File

@@ -4,7 +4,12 @@ import { NotFound } from '../../src/common/NotFound';
import { checkAccessibility } from '../__helpers__/accessibility';
describe('<NotFound />', () => {
const setUp = (props = {}) => render(<MemoryRouter><NotFound {...props} /></MemoryRouter>);
const setUp = (props = {}) =>
render(
<MemoryRouter>
<NotFound {...props} />
</MemoryRouter>,
);
it('passes a11y checks', () => checkAccessibility(setUp()));
@@ -15,9 +20,11 @@ describe('<NotFound />', () => {
it('shows expected error message', () => {
setUp();
expect(screen.getByText(
'Use your browser\'s back button to navigate to the page you have previously come from, or just press this button.',
)).toBeInTheDocument();
expect(
screen.getByText(
"Use your browser's back button to navigate to the page you have previously come from, or just press this button.",
),
).toBeInTheDocument();
});
it.each([

View File

@@ -4,15 +4,16 @@ import { ScrollToTop } from '../../src/common/ScrollToTop';
import { checkAccessibility } from '../__helpers__/accessibility';
describe('<ScrollToTop />', () => {
const setUp = (children = 'Foo') => render(<MemoryRouter><ScrollToTop>{children}</ScrollToTop></MemoryRouter>);
const setUp = (children = 'Foo') =>
render(
<MemoryRouter>
<ScrollToTop>{children}</ScrollToTop>
</MemoryRouter>,
);
it('passes a11y checks', () => checkAccessibility(setUp()));
it.each([
['Foobar'],
['Barfoo'],
['Something'],
])('just renders children', (children) => {
it.each([['Foobar'], ['Barfoo'], ['Something']])('just renders children', (children) => {
setUp(children);
expect(screen.getByText(children)).toBeInTheDocument();
});

View File

@@ -8,9 +8,8 @@ import { checkAccessibility } from '../__helpers__/accessibility';
describe('<ShlinkVersions />', () => {
const setUp = (props: ShlinkVersionsProps) => render(<ShlinkVersions {...props} />);
it('passes a11y checks', () => checkAccessibility(
setUp({ selectedServer: fromPartial({ version: '1.0.0', printableVersion: '1.0.0' }) }),
));
it('passes a11y checks', () =>
checkAccessibility(setUp({ selectedServer: fromPartial({ version: '1.0.0', printableVersion: '1.0.0' }) })));
it.each([
['1.2.3', fromPartial<ReachableServer>({ version: '1.0.0', printableVersion: 'foo' }), 'v1.2.3', 'foo'],

View File

@@ -5,9 +5,10 @@ import { checkAccessibility } from '../__helpers__/accessibility';
import { renderWithStore } from '../__helpers__/setUpTest';
describe('<ShlinkVersionsContainer />', () => {
const setUp = (selectedServer: SelectedServer = null) => renderWithStore(<ShlinkVersionsContainer />, {
initialState: { selectedServer },
});
const setUp = (selectedServer: SelectedServer = null) =>
renderWithStore(<ShlinkVersionsContainer />, {
initialState: { selectedServer },
});
it.each([
[null],

View File

@@ -13,14 +13,15 @@ vi.mock('@shlinkio/shlink-web-component', () => ({
}));
describe('<ShlinkWebComponentContainer />', () => {
const setUp = (selectedServer: SelectedServer) => renderWithStore(
<MemoryRouter>
<ShlinkWebComponentContainer TagColorsStorage={fromPartial({})} />
</MemoryRouter>,
{
initialState: { selectedServer, servers: {}, settings: {} },
},
);
const setUp = (selectedServer: SelectedServer) =>
renderWithStore(
<MemoryRouter>
<ShlinkWebComponentContainer TagColorsStorage={fromPartial({})} />
</MemoryRouter>,
{
initialState: { selectedServer, servers: {}, settings: {} },
},
);
it('passes a11y checks', () => checkAccessibility(setUp(fromPartial({ version: '3.0.0' }))));

View File

@@ -6,9 +6,9 @@ describe('context', () => {
describe('useDependencies', () => {
let lastDependencies: unknown[];
function TestComponent({ name}: { name: string }) {
function TestComponent({ name }: { name: string }) {
// eslint-disable-next-line react-compiler/react-compiler
lastDependencies = useDependencies(name);
lastDependencies = useDependencies(name);
return null;
}
@@ -19,11 +19,13 @@ describe('context', () => {
});
it('throws when requested dependency is not found in container', () => {
expect(() => render(
<ContainerProvider value={fromPartial({})}>
<TestComponent name="foo" />
</ContainerProvider>,
)).toThrowError('Dependency with name "foo" not found in container');
expect(() =>
render(
<ContainerProvider value={fromPartial({})}>
<TestComponent name="foo" />
</ContainerProvider>,
),
).toThrowError('Dependency with name "foo" not found in container');
});
it('gets dependency from container', () => {

View File

@@ -70,12 +70,14 @@ describe('<CreateServer />', () => {
expect(store.getState().servers[expectedServerId]).not.toBeDefined();
fireEvent.submit(screen.getByRole('form'));
expect(store.getState().servers[expectedServerId]).toEqual(expect.objectContaining({
id: expectedServerId,
name: 'the_name',
url: 'https://the_url.com',
apiKey: 'the_api_key',
}));
expect(store.getState().servers[expectedServerId]).toEqual(
expect.objectContaining({
id: expectedServerId,
name: 'the_name',
url: 'https://the_url.com',
apiKey: 'the_api_key',
}),
);
expect(history.location.pathname).toEqual(`/server/${expectedServerId}`);
expect(screen.queryByRole('dialog')).not.toBeInTheDocument();

View File

@@ -21,11 +21,7 @@ describe('<DeleteServerButton />', () => {
it('passes a11y checks', () => checkAccessibility(setUp('Delete me')));
it.each([
['Foo bar'],
['baz'],
['something'],
])('renders expected content', (children) => {
it.each([['Foo bar'], ['baz'], ['something']])('renders expected content', (children) => {
const { container } = setUp(children);
expect(container.firstChild).toBeTruthy();
expect(container.firstChild).toMatchSnapshot();

View File

@@ -9,16 +9,12 @@ import { TestModalWrapper } from '../__helpers__/TestModalWrapper';
describe('<DeleteServerModal />', () => {
const serverName = 'the_server_name';
const server = fromPartial<ServerWithId>({ id: 'foo', name: serverName });
const setUp = () => renderWithStore(
<TestModalWrapper
renderModal={(args) => <DeleteServerModal {...args} server={server} />}
/>,
{
const setUp = () =>
renderWithStore(<TestModalWrapper renderModal={(args) => <DeleteServerModal {...args} server={server} />} />, {
initialState: {
servers: { foo: server },
},
},
);
});
it('passes a11y checks', () => checkAccessibility(setUp()));
@@ -36,19 +32,19 @@ describe('<DeleteServerModal />', () => {
expect(screen.getByText(serverName)).toBeInTheDocument();
});
it.each([
[() => screen.getByRole('button', { name: 'Cancel' })],
[() => screen.getByLabelText('Close dialog')],
])('closes dialog when clicking cancel button', async (getButton) => {
const { user, store } = setUp();
it.each([[() => screen.getByRole('button', { name: 'Cancel' })], [() => screen.getByLabelText('Close dialog')]])(
'closes dialog when clicking cancel button',
async (getButton) => {
const { user, store } = setUp();
expect(screen.getByRole('dialog')).toBeInTheDocument();
await user.click(getButton());
expect(screen.queryByRole('dialog')).not.toBeInTheDocument();
expect(screen.getByRole('dialog')).toBeInTheDocument();
await user.click(getButton());
expect(screen.queryByRole('dialog')).not.toBeInTheDocument();
// No server has been deleted
expect(Object.keys(store.getState().servers)).toHaveLength(1);
});
// No server has been deleted
expect(Object.keys(store.getState().servers)).toHaveLength(1);
},
);
it('deletes server when clicking accept button', async () => {
const { user, store } = setUp();

View File

@@ -65,27 +65,33 @@ describe('<EditServer />', () => {
// await user.click(screen.getByRole('button', { name: 'Save' }));
fireEvent.submit(screen.getByRole('form'));
expect(store.getState().servers[defaultSelectedServer.id]).toEqual(expect.objectContaining({
name: 'the_name edited',
url: 'the_url edited',
}));
expect(store.getState().servers[defaultSelectedServer.id]).toEqual(
expect.objectContaining({
name: 'the_name edited',
url: 'the_url edited',
}),
);
// After saving we go back, to the first route from history's initialEntries
expect(history.location.pathname).toEqual('/foo');
});
it.each([
{ forwardCredentials: true },
{ forwardCredentials: false },
])('edits advanced options - forward credentials', async ({ forwardCredentials }) => {
const { user, store } = setUp({ ...defaultSelectedServer, forwardCredentials });
it.each([{ forwardCredentials: true }, { forwardCredentials: false }])(
'edits advanced options - forward credentials',
async ({ forwardCredentials }) => {
const { user, store } = setUp({ ...defaultSelectedServer, forwardCredentials });
await user.click(screen.getByText('Advanced options'));
await user.click(screen.getByLabelText('Forward credentials to this server on every request.'));
fireEvent.submit(screen.getByRole('form'));
await user.click(screen.getByText('Advanced options'));
await user.click(screen.getByLabelText('Forward credentials to this server on every request.'));
fireEvent.submit(screen.getByRole('form'));
await waitFor(() => expect(store.getState().servers[defaultSelectedServer.id]).toEqual(expect.objectContaining({
forwardCredentials: !forwardCredentials,
})));
});
await waitFor(() =>
expect(store.getState().servers[defaultSelectedServer.id]).toEqual(
expect.objectContaining({
forwardCredentials: !forwardCredentials,
}),
),
);
},
);
});

View File

@@ -11,23 +11,26 @@ describe('<ManageServers />', () => {
const exportServers = vi.fn();
const serversExporter = fromPartial<ServersExporter>({ exportServers });
const useTimeoutToggle = vi.fn().mockReturnValue([false, vi.fn()]);
const createServerMock = (value: string, autoConnect = false) => fromPartial<ServerWithId>(
{ id: value, name: value, url: value, autoConnect },
);
const setUp = (servers: ServersMap = {}) => renderWithStore(
<MemoryRouter>
<ManageServers useTimeoutToggle={useTimeoutToggle} ServersExporter={serversExporter} />
</MemoryRouter>,
{
initialState: { servers },
},
);
const createServerMock = (value: string, autoConnect = false) =>
fromPartial<ServerWithId>({ id: value, name: value, url: value, autoConnect });
const setUp = (servers: ServersMap = {}) =>
renderWithStore(
<MemoryRouter>
<ManageServers useTimeoutToggle={useTimeoutToggle} ServersExporter={serversExporter} />
</MemoryRouter>,
{
initialState: { servers },
},
);
it('passes a11y checks', () => checkAccessibility(setUp({
foo: createServerMock('foo'),
bar: createServerMock('bar'),
baz: createServerMock('baz'),
})));
it('passes a11y checks', () =>
checkAccessibility(
setUp({
foo: createServerMock('foo'),
bar: createServerMock('bar'),
baz: createServerMock('baz'),
}),
));
it('shows search field which allows searching servers, affecting te amount of rendered rows', async () => {
const { user } = setUp({

View File

@@ -13,13 +13,14 @@ describe('<ManageServersRow />', () => {
apiKey: '123',
id: 'abc',
};
const setUp = (hasAutoConnect = false, autoConnect = false) => renderWithStore(
<MemoryRouter>
<Table header={<Table.Row />}>
<ManageServersRow server={{ ...server, autoConnect }} hasAutoConnect={hasAutoConnect} />
</Table>
</MemoryRouter>,
);
const setUp = (hasAutoConnect = false, autoConnect = false) =>
renderWithStore(
<MemoryRouter>
<Table header={<Table.Row />}>
<ManageServersRow server={{ ...server, autoConnect }} hasAutoConnect={hasAutoConnect} />
</Table>
</MemoryRouter>,
);
it('passes a11y checks', () => checkAccessibility(setUp()));
@@ -36,10 +37,7 @@ describe('<ManageServersRow />', () => {
expect(screen.getByRole('button', { name: 'Options' })).toBeInTheDocument();
});
it.each([
[true],
[false],
])('renders auto-connect icon only if server is autoConnect', (autoConnect) => {
it.each([[true], [false]])('renders auto-connect icon only if server is autoConnect', (autoConnect) => {
const { container } = setUp(true, autoConnect);
expect(container).toMatchSnapshot();
});

View File

@@ -12,16 +12,17 @@ describe('<ServersDropdown />', () => {
'2b': fromPartial({ name: 'bar', id: '2b' }),
'3c': fromPartial({ name: 'baz', id: '3c' }),
};
const setUp = (servers: ServersMap = fallbackServers) => renderWithStore(
<MemoryRouter>
<ul role="menu">
<ServersDropdown />
</ul>
</MemoryRouter>,
{
initialState: { selectedServer: null, servers },
},
);
const setUp = (servers: ServersMap = fallbackServers) =>
renderWithStore(
<MemoryRouter>
<ul role="menu">
<ServersDropdown />
</ul>
</MemoryRouter>,
{
initialState: { selectedServer: null, servers },
},
);
it('passes a11y checks', async () => {
const { user, ...rest } = setUp();

View File

@@ -6,10 +6,7 @@ import { ServersListGroup } from '../../src/servers/ServersListGroup';
import { checkAccessibility } from '../__helpers__/accessibility';
describe('<ServersListGroup />', () => {
const servers: ServerWithId[] = [
fromPartial({ name: 'foo', id: '123' }),
fromPartial({ name: 'bar', id: '456' }),
];
const servers: ServerWithId[] = [fromPartial({ name: 'foo', id: '123' }), fromPartial({ name: 'bar', id: '456' })];
const setUp = (params: { servers?: ServerWithId[]; borderless?: boolean } = {}) => {
const { servers = [], borderless } = params;
@@ -22,21 +19,14 @@ describe('<ServersListGroup />', () => {
it('passes a11y checks', () => checkAccessibility(setUp()));
it.each([
[servers],
[[]],
])('shows servers list', (servers) => {
it.each([[servers], [[]]])('shows servers list', (servers) => {
setUp({ servers });
expect(screen.queryAllByTestId('list')).toHaveLength(servers.length ? 1 : 0);
expect(screen.queryAllByRole('link')).toHaveLength(servers.length);
});
it.each([
[true],
[false],
[undefined],
])('renders proper classes for embedded', (borderless) => {
it.each([[true], [false], [undefined]])('renders proper classes for embedded', (borderless) => {
setUp({ servers, borderless });
const list = screen.getByTestId('list');

View File

@@ -8,9 +8,12 @@ import { renderWithEvents } from '../../__helpers__/setUpTest';
describe('<DuplicatedServersModal />', () => {
const onClose = vi.fn();
const onConfirm = vi.fn();
const setUp = (duplicatedServers: ServerData[] = []) => act(() => renderWithEvents(
<DuplicatedServersModal open duplicatedServers={duplicatedServers} onClose={onClose} onConfirm={onConfirm} />,
));
const setUp = (duplicatedServers: ServerData[] = []) =>
act(() =>
renderWithEvents(
<DuplicatedServersModal open duplicatedServers={duplicatedServers} onClose={onClose} onConfirm={onConfirm} />,
),
);
const mockServer = (data: Partial<ServerData> = {}) => fromPartial<ServerData>(data);
it('passes a11y checks', () => checkAccessibility(setUp()));
@@ -60,10 +63,7 @@ describe('<DuplicatedServersModal />', () => {
it.each([
[[]],
[[mockServer({ url: 'url', apiKey: 'apiKey' })]],
[[
mockServer({ url: 'url_1', apiKey: 'apiKey_1' }),
mockServer({ url: 'url_2', apiKey: 'apiKey_2' }),
]],
[[mockServer({ url: 'url_1', apiKey: 'apiKey_1' }), mockServer({ url: 'url_2', apiKey: 'apiKey_2' })]],
])('displays provided server data', async (duplicatedServers) => {
await setUp(duplicatedServers);

View File

@@ -12,12 +12,10 @@ describe('<ImportServersBtn />', () => {
const onImportMock = vi.fn();
const importServersFromFile = vi.fn().mockResolvedValue([]);
const serversImporterMock = fromPartial<ServersImporter>({ importServersFromFile });
const setUp = (props: Partial<ImportServersBtnProps> = {}, servers: ServersMap = {}) => renderWithStore(
<ImportServersBtn {...props} onImport={onImportMock} ServersImporter={serversImporterMock} />,
{
const setUp = (props: Partial<ImportServersBtnProps> = {}, servers: ServersMap = {}) =>
renderWithStore(<ImportServersBtn {...props} onImport={onImportMock} ServersImporter={serversImporterMock} />, {
initialState: { servers },
},
);
});
it('passes a11y checks', () => checkAccessibility(setUp()));
@@ -26,8 +24,8 @@ describe('<ImportServersBtn />', () => {
expect(screen.queryByText(/^You can create servers by importing a CSV file/)).not.toBeInTheDocument();
await user.hover(screen.getByRole('button'));
await waitFor(
() => expect(screen.getByText(/^You can create servers by importing a CSV file/)).toBeInTheDocument(),
await waitFor(() =>
expect(screen.getByText(/^You can create servers by importing a CSV file/)).toBeInTheDocument(),
);
});
@@ -59,38 +57,41 @@ describe('<ImportServersBtn />', () => {
it.each([
{ btnName: 'Save duplicate', savesDuplicatedServers: true },
{ btnName: 'Discard', savesDuplicatedServers: false },
])('creates duplicated servers depending on selected option in modal', async ({ btnName, savesDuplicatedServers }) => {
const existingServerData: ServerData = {
name: 'existingServer',
url: 'http://s.test/existingUrl',
apiKey: 'existingApiKey',
};
const existingServer: ServerWithId = {
...existingServerData,
id: 'existingserver-s.test',
};
const newServer: ServerData = { name: 'newServer', url: 'http://s.test/newUrl', apiKey: 'newApiKey' };
const { user, store } = setUp({}, { [existingServer.id]: existingServer });
])(
'creates duplicated servers depending on selected option in modal',
async ({ btnName, savesDuplicatedServers }) => {
const existingServerData: ServerData = {
name: 'existingServer',
url: 'http://s.test/existingUrl',
apiKey: 'existingApiKey',
};
const existingServer: ServerWithId = {
...existingServerData,
id: 'existingserver-s.test',
};
const newServer: ServerData = { name: 'newServer', url: 'http://s.test/newUrl', apiKey: 'newApiKey' };
const { user, store } = setUp({}, { [existingServer.id]: existingServer });
importServersFromFile.mockResolvedValue([existingServerData, newServer]);
importServersFromFile.mockResolvedValue([existingServerData, newServer]);
expect(screen.queryByRole('dialog')).not.toBeInTheDocument();
await user.upload(screen.getByTestId('csv-file-input'), csvFile);
expect(screen.queryByRole('dialog')).not.toBeInTheDocument();
await user.upload(screen.getByTestId('csv-file-input'), csvFile);
// Once the file is uploaded, non-duplicated servers are immediately created
const { servers } = store.getState();
expect(Object.keys(servers)).toHaveLength(2);
expect(screen.getByRole('dialog')).toBeInTheDocument();
await user.click(screen.getByRole('button', { name: btnName }));
// If duplicated servers are saved, there's one extra server creation
if (savesDuplicatedServers) {
// Once the file is uploaded, non-duplicated servers are immediately created
const { servers } = store.getState();
expect(Object.keys(servers)).toHaveLength(3);
}
expect(Object.keys(servers)).toHaveLength(2);
// On import is called only once, no matter what
expect(onImportMock).toHaveBeenCalledOnce();
});
expect(screen.getByRole('dialog')).toBeInTheDocument();
await user.click(screen.getByRole('button', { name: btnName }));
// If duplicated servers are saved, there's one extra server creation
if (savesDuplicatedServers) {
const { servers } = store.getState();
expect(Object.keys(servers)).toHaveLength(3);
}
// On import is called only once, no matter what
expect(onImportMock).toHaveBeenCalledOnce();
},
);
});

View File

@@ -7,19 +7,20 @@ import { checkAccessibility } from '../../__helpers__/accessibility';
import { renderWithStore } from '../../__helpers__/setUpTest';
describe('<ServerError />', () => {
const setUp = (selectedServer: SelectedServer) => renderWithStore(
<MemoryRouter>
<ServerError />
</MemoryRouter>,
{
initialState: { selectedServer, servers: {} },
},
);
const setUp = (selectedServer: SelectedServer) =>
renderWithStore(
<MemoryRouter>
<ServerError />
</MemoryRouter>,
{
initialState: { selectedServer, servers: {} },
},
);
it.each([
[fromPartial<NotFoundServer>({})],
[fromPartial<NonReachableServer>({ id: 'abc123' })],
])('passes a11y checks', (selectedServer) => checkAccessibility(setUp(selectedServer)));
it.each([[fromPartial<NotFoundServer>({})], [fromPartial<NonReachableServer>({ id: 'abc123' })]])(
'passes a11y checks',
(selectedServer) => checkAccessibility(setUp(selectedServer)),
);
it.each([
[

View File

@@ -66,7 +66,6 @@ describe('remoteServersReducer', () => {
url: 'http://localhost:8000',
apiKey: '7a531c75-134e-4d5c-86e0-a71b7167b57a',
},
},
},
{

View File

@@ -21,17 +21,20 @@ describe('selectedServerReducer', () => {
it('returns selected server when action is SELECT_SERVER', () => {
const payload = fromPartial<RegularServer>({ id: 'abc123' });
expect(reducer(null, selectServer.fulfilled(payload, '', { serverId: '', buildShlinkApiClient }))).toEqual(payload);
expect(reducer(null, selectServer.fulfilled(payload, '', { serverId: '', buildShlinkApiClient }))).toEqual(
payload,
);
});
});
describe('selectServer', () => {
const version = '1.19.0';
const createGetStateMock = (id: string) => vi.fn().mockReturnValue({
servers: {
[id]: { id },
},
});
const createGetStateMock = (id: string) =>
vi.fn().mockReturnValue({
servers: {
[id]: { id },
},
});
it.each([
[version, version, `v${version}`],

View File

@@ -39,14 +39,16 @@ describe('serversReducer', () => {
ghi789: { id: 'ghi789' },
}));
it.each([
[true],
[false],
])('returns state as it is when trying to set auto-connect on invalid server', (autoConnect) =>
expect(serversReducer(list, setAutoConnect(fromPartial<ServerWithId>({ id: 'invalid' }), autoConnect))).toEqual({
abc123: { id: 'abc123' },
def456: { id: 'def456' },
}));
it.each([[true], [false]])(
'returns state as it is when trying to set auto-connect on invalid server',
(autoConnect) =>
expect(serversReducer(list, setAutoConnect(fromPartial<ServerWithId>({ id: 'invalid' }), autoConnect))).toEqual(
{
abc123: { id: 'abc123' },
def456: { id: 'def456' },
},
),
);
it('disables auto-connect on a server which is already set to auto-connect', () => {
const listWithDisabledAutoConnect = {
@@ -54,10 +56,9 @@ describe('serversReducer', () => {
abc123: { ...list.abc123, autoConnect: true },
};
expect(serversReducer(
listWithDisabledAutoConnect,
setAutoConnect(fromPartial<ServerWithId>({ id: 'abc123' }), false),
)).toEqual({
expect(
serversReducer(listWithDisabledAutoConnect, setAutoConnect(fromPartial<ServerWithId>({ id: 'abc123' }), false)),
).toEqual({
abc123: { id: 'abc123', autoConnect: false },
def456: { id: 'def456' },
});
@@ -69,10 +70,9 @@ describe('serversReducer', () => {
abc123: { ...list.abc123, autoConnect: true },
};
expect(serversReducer(
listWithEnabledAutoConnect,
setAutoConnect(fromPartial<ServerWithId>({ id: 'def456' }), true),
)).toEqual({
expect(
serversReducer(listWithEnabledAutoConnect, setAutoConnect(fromPartial<ServerWithId>({ id: 'def456' }), true)),
).toEqual({
abc123: { id: 'abc123', autoConnect: false },
def456: { id: 'def456', autoConnect: true },
});
@@ -108,10 +108,7 @@ describe('serversReducer', () => {
});
describe('setAutoConnect', () => {
it.each([
[true],
[false],
])('returns expected action', (autoConnect) => {
it.each([[true], [false]])('returns expected action', (autoConnect) => {
const serverToEdit = fromPartial<RegularServer>({ id: 'abc123' });
const { payload } = setAutoConnect(serverToEdit, autoConnect);

View File

@@ -54,7 +54,9 @@ describe('ServersExporter', () => {
it('makes use of download link API', () => {
const jsonToCsvMock = createJsonToCsvMock();
const exporter = new ServersExporter(storageMock, windowMock, jsonToCsvMock);
const { document: { createElement } } = windowMock;
const {
document: { createElement },
} = windowMock;
exporter.exportServers();

View File

@@ -11,9 +11,7 @@ describe('ServersImporter', () => {
describe('importServersFromFile', () => {
it.each([[null], [undefined]])('rejects with error if no file was provided', async (file) => {
await expect(importer.importServersFromFile(file)).rejects.toEqual(
new Error('No file provided'),
);
await expect(importer.importServersFromFile(file)).rejects.toEqual(new Error('No file provided'));
});
it('rejects with error if parsing the file fails', async () => {
@@ -52,13 +50,13 @@ describe('ServersImporter', () => {
],
expectedError: 'Server is missing required "url", "apiKey" and/or "name" properties',
},
])('rejects with error if provided file does not parse to valid list of servers', async ({
parsedObject,
expectedError,
}) => {
csvjsonMock.mockResolvedValue(parsedObject);
await expect(importer.importServersFromFile(fileMock())).rejects.toEqual(new Error(expectedError));
});
])(
'rejects with error if provided file does not parse to valid list of servers',
async ({ parsedObject, expectedError }) => {
csvjsonMock.mockResolvedValue(parsedObject);
await expect(importer.importServersFromFile(fileMock())).rejects.toEqual(new Error(expectedError));
},
);
it('reads file when a CSV containing valid servers is provided', async () => {
const expectedServers: Required<ServerData>[] = [

View File

@@ -4,11 +4,12 @@ import { checkAccessibility } from '../__helpers__/accessibility';
import { renderWithStore } from '../__helpers__/setUpTest';
describe('<Settings />', () => {
const setUp = () => renderWithStore(
<MemoryRouter>
<Settings />
</MemoryRouter>,
);
const setUp = () =>
renderWithStore(
<MemoryRouter>
<Settings />
</MemoryRouter>,
);
it('passes a11y checks', () => checkAccessibility(setUp()));
});

View File

@@ -17,13 +17,15 @@ describe('settings-helpers', () => {
},
});
expect(migrateDeprecatedSettings(state)).toEqual(expect.objectContaining({
settings: expect.objectContaining({
visits: {
defaultInterval: 'last180Days',
},
expect(migrateDeprecatedSettings(state)).toEqual(
expect.objectContaining({
settings: expect.objectContaining({
visits: {
defaultInterval: 'last180Days',
},
}),
}),
}));
);
});
});
});

View File

@@ -15,7 +15,10 @@ describe('TagColorsStorage', () => {
describe('getTagColors', () => {
it.each([
[undefined, {}],
[{ foo: 'red', var: 'green' }, { foo: 'red', var: 'green' }],
[
{ foo: 'red', var: 'green' },
{ foo: 'red', var: 'green' },
],
])('returns colors from local storage', (colorsFromStorage, expectedValue) => {
get.mockReturnValue(colorsFromStorage);