Replace local settings UI with the one from shlink-web-component

This commit is contained in:
Alejandro Celaya
2024-05-20 20:03:50 +02:00
parent d4bc9dd62a
commit 202a69bdf5
27 changed files with 88 additions and 1096 deletions

View File

@@ -1,86 +0,0 @@
import type { RealTimeUpdatesSettings as RealTimeUpdatesSettingsOptions } from '@shlinkio/shlink-web-component';
import { screen } from '@testing-library/react';
import { fromPartial } from '@total-typescript/shoehorn';
import { RealTimeUpdatesSettings } from '../../src/settings/RealTimeUpdatesSettings';
import { checkAccessibility } from '../__helpers__/accessibility';
import { renderWithEvents } from '../__helpers__/setUpTest';
describe('<RealTimeUpdatesSettings />', () => {
const toggleRealTimeUpdates = vi.fn();
const setRealTimeUpdatesInterval = vi.fn();
const setUp = (realTimeUpdates: Partial<RealTimeUpdatesSettingsOptions> = {}) => renderWithEvents(
<RealTimeUpdatesSettings
settings={fromPartial({ realTimeUpdates })}
toggleRealTimeUpdates={toggleRealTimeUpdates}
setRealTimeUpdatesInterval={setRealTimeUpdatesInterval}
/>,
);
it('passes a11y checks', () => checkAccessibility(setUp()));
it('renders enabled real time updates as expected', () => {
setUp({ enabled: true });
expect(screen.getByLabelText(/^Enable or disable real-time updates./)).toBeChecked();
expect(screen.getByText(/^Real-time updates are currently being/)).toHaveTextContent('processed');
expect(screen.getByText(/^Real-time updates are currently being/)).not.toHaveTextContent('ignored');
expect(screen.getByText('Real-time updates frequency (in minutes):')).not.toHaveAttribute(
'class',
expect.stringContaining('text-muted'),
);
expect(screen.getByLabelText('Real-time updates frequency (in minutes):')).not.toHaveAttribute('disabled');
expect(screen.getByText('Updates will be reflected in the UI as soon as they happen.')).toBeInTheDocument();
});
it('renders disabled real time updates as expected', () => {
setUp({ enabled: false });
expect(screen.getByLabelText(/^Enable or disable real-time updates./)).not.toBeChecked();
expect(screen.getByText(/^Real-time updates are currently being/)).not.toHaveTextContent('processed');
expect(screen.getByText(/^Real-time updates are currently being/)).toHaveTextContent('ignored');
expect(screen.getByText('Real-time updates frequency (in minutes):')).toHaveAttribute(
'class',
expect.stringContaining('text-muted'),
);
expect(screen.getByLabelText('Real-time updates frequency (in minutes):')).toHaveAttribute('disabled');
expect(screen.queryByText('Updates will be reflected in the UI as soon as they happen.')).not.toBeInTheDocument();
});
it.each([
[1, 'minute'],
[2, 'minutes'],
[10, 'minutes'],
[100, 'minutes'],
])('shows expected children when interval is greater than 0', (interval, minutesWord) => {
setUp({ enabled: true, interval });
expect(screen.getByText(/^Updates will be reflected in the UI every/)).toHaveTextContent(
`${interval} ${minutesWord}`,
);
expect(screen.getByLabelText('Real-time updates frequency (in minutes):')).toHaveValue(interval);
expect(screen.queryByText('Updates will be reflected in the UI as soon as they happen.')).not.toBeInTheDocument();
});
it.each([[undefined], [0]])('shows expected children when interval is 0 or undefined', (interval) => {
setUp({ enabled: true, interval });
expect(screen.queryByText(/^Updates will be reflected in the UI every/)).not.toBeInTheDocument();
expect(screen.getByText('Updates will be reflected in the UI as soon as they happen.')).toBeInTheDocument();
});
it('updates real time updates when typing on input', async () => {
const { user } = setUp({ enabled: true });
expect(setRealTimeUpdatesInterval).not.toHaveBeenCalled();
await user.type(screen.getByLabelText('Real-time updates frequency (in minutes):'), '5');
expect(setRealTimeUpdatesInterval).toHaveBeenCalledWith(5);
});
it('toggles real time updates on switch change', async () => {
const { user } = setUp({ enabled: true });
expect(toggleRealTimeUpdates).not.toHaveBeenCalled();
await user.click(screen.getByText(/^Enable or disable real-time updates./));
expect(toggleRealTimeUpdates).toHaveBeenCalled();
});
});

View File

@@ -1,52 +1,14 @@
import { render, screen } from '@testing-library/react';
import { fromPartial } from '@total-typescript/shoehorn';
import { createMemoryHistory } from 'history';
import { Router } from 'react-router-dom';
import { SettingsFactory } from '../../src/settings/Settings';
import { render } from '@testing-library/react';
import { MemoryRouter } from 'react-router-dom';
import { Settings } from '../../src/settings/Settings';
import { checkAccessibility } from '../__helpers__/accessibility';
describe('<Settings />', () => {
const Settings = SettingsFactory(fromPartial({
RealTimeUpdatesSettings: () => <span>RealTimeUpdates</span>,
ShortUrlCreationSettings: () => <span>ShortUrlCreation</span>,
ShortUrlsListSettings: () => <span>ShortUrlsList</span>,
UserInterfaceSettings: () => <span>UserInterface</span>,
VisitsSettings: () => <span>Visits</span>,
TagsSettings: () => <span>Tags</span>,
}));
const setUp = (activeRoute = '/') => {
const history = createMemoryHistory();
history.push(activeRoute);
return render(<Router location={history.location} navigator={history}><Settings /></Router>);
};
const setUp = () => render(
<MemoryRouter>
<Settings settings={{}} setSettings={vi.fn()} />
</MemoryRouter>,
);
it('passes a11y checks', () => checkAccessibility(setUp()));
it.each([
['/general', {
visibleComps: ['UserInterface', 'RealTimeUpdates'],
hiddenComps: ['ShortUrlCreation', 'ShortUrlsList', 'Tags', 'Visits'],
}],
['/short-urls', {
visibleComps: ['ShortUrlCreation', 'ShortUrlsList'],
hiddenComps: ['UserInterface', 'RealTimeUpdates', 'Tags', 'Visits'],
}],
['/other-items', {
visibleComps: ['Tags', 'Visits'],
hiddenComps: ['UserInterface', 'RealTimeUpdates', 'ShortUrlCreation', 'ShortUrlsList'],
}],
])('renders expected sections based on route', (activeRoute, { visibleComps, hiddenComps }) => {
setUp(activeRoute);
visibleComps.forEach((comp) => expect(screen.getByText(comp)).toBeInTheDocument());
hiddenComps.forEach((comp) => expect(screen.queryByText(comp)).not.toBeInTheDocument());
});
it('renders expected menu', () => {
setUp();
expect(screen.getByRole('link', { name: 'General' })).toHaveAttribute('href', '/general');
expect(screen.getByRole('link', { name: 'Short URLs' })).toHaveAttribute('href', '/short-urls');
expect(screen.getByRole('link', { name: 'Other items' })).toHaveAttribute('href', '/other-items');
});
});

View File

@@ -1,115 +0,0 @@
import type { ShortUrlCreationSettings as ShortUrlsSettings } from '@shlinkio/shlink-web-component';
import { screen } from '@testing-library/react';
import { fromPartial } from '@total-typescript/shoehorn';
import { ShortUrlCreationSettings } from '../../src/settings/ShortUrlCreationSettings';
import { checkAccessibility } from '../__helpers__/accessibility';
import { renderWithEvents } from '../__helpers__/setUpTest';
describe('<ShortUrlCreationSettings />', () => {
const setShortUrlCreationSettings = vi.fn();
const setUp = (shortUrlCreation?: ShortUrlsSettings) => renderWithEvents(
<ShortUrlCreationSettings
settings={fromPartial({ shortUrlCreation })}
setShortUrlCreationSettings={setShortUrlCreationSettings}
/>,
);
it('passes a11y checks', () => checkAccessibility(setUp()));
it.each([
[{ validateUrls: true }, true],
[{ validateUrls: false }, false],
[undefined, false],
])('URL validation switch has proper initial state', (shortUrlCreation, expectedChecked) => {
const matcher = /^Request validation on long URLs when creating new short URLs/;
setUp(shortUrlCreation);
const checkbox = screen.getByLabelText(matcher);
const label = screen.getByText(matcher);
if (expectedChecked) {
expect(checkbox).toBeChecked();
expect(label).toHaveTextContent('Validate URL checkbox will be checked');
expect(label).not.toHaveTextContent('Validate URL checkbox will be unchecked');
} else {
expect(checkbox).not.toBeChecked();
expect(label).toHaveTextContent('Validate URL checkbox will be unchecked');
expect(label).not.toHaveTextContent('Validate URL checkbox will be checked');
}
});
it.each([
[{ forwardQuery: true }, true],
[{ forwardQuery: false }, false],
[{}, true],
])('forward query switch is toggled if option is true', (shortUrlCreation, expectedChecked) => {
const matcher = /^Make all new short URLs forward their query params to the long URL/;
setUp({ validateUrls: true, ...shortUrlCreation });
const checkbox = screen.getByLabelText(matcher);
const label = screen.getByText(matcher);
if (expectedChecked) {
expect(checkbox).toBeChecked();
expect(label).toHaveTextContent('Forward query params on redirect checkbox will be checked');
expect(label).not.toHaveTextContent('Forward query params on redirect checkbox will be unchecked');
} else {
expect(checkbox).not.toBeChecked();
expect(label).toHaveTextContent('Forward query params on redirect checkbox will be unchecked');
expect(label).not.toHaveTextContent('Forward query params on redirect checkbox will be checked');
}
});
it.each([
[{ tagFilteringMode: 'includes' } as ShortUrlsSettings, 'Suggest tags including input', 'including'],
[
{ tagFilteringMode: 'startsWith' } as ShortUrlsSettings,
'Suggest tags starting with input',
'starting with',
],
[undefined, 'Suggest tags starting with input', 'starting with'],
])('shows expected texts for tags suggestions', (shortUrlCreation, expectedText, expectedHint) => {
setUp(shortUrlCreation);
expect(screen.getByRole('button', { name: expectedText })).toBeInTheDocument();
expect(screen.getByText(/^The list of suggested tags will contain those/)).toHaveTextContent(expectedHint);
});
it.each([[true], [false]])('invokes setShortUrlCreationSettings when URL validation toggle value changes', async (validateUrls) => {
const { user } = setUp({ validateUrls });
expect(setShortUrlCreationSettings).not.toHaveBeenCalled();
await user.click(screen.getByLabelText(/^Request validation on long URLs when creating new short URLs/));
expect(setShortUrlCreationSettings).toHaveBeenCalledWith({ validateUrls: !validateUrls });
});
it.each([[true], [false]])('invokes setShortUrlCreationSettings when forward query toggle value changes', async (forwardQuery) => {
const { user } = setUp({ validateUrls: true, forwardQuery });
expect(setShortUrlCreationSettings).not.toHaveBeenCalled();
await user.click(screen.getByLabelText(/^Make all new short URLs forward their query params to the long URL/));
expect(setShortUrlCreationSettings).toHaveBeenCalledWith(expect.objectContaining({ forwardQuery: !forwardQuery }));
});
it('invokes setShortUrlCreationSettings when dropdown value changes', async () => {
const { user } = setUp();
const clickItem = async (name: string) => {
await user.click(screen.getByRole('button', { name: 'Suggest tags starting with input' }));
await user.click(await screen.findByRole('menuitem', { name }));
};
expect(setShortUrlCreationSettings).not.toHaveBeenCalled();
await clickItem('Suggest tags including input');
expect(setShortUrlCreationSettings).toHaveBeenCalledWith(expect.objectContaining(
{ tagFilteringMode: 'includes' },
));
await clickItem('Suggest tags starting with input');
expect(setShortUrlCreationSettings).toHaveBeenCalledWith(expect.objectContaining(
{ tagFilteringMode: 'startsWith' },
));
});
});

View File

@@ -1,40 +0,0 @@
import type { ShortUrlsListSettings as ShortUrlsSettings } from '@shlinkio/shlink-web-component';
import { screen } from '@testing-library/react';
import { fromPartial } from '@total-typescript/shoehorn';
import { ShortUrlsListSettings } from '../../src/settings/ShortUrlsListSettings';
import { checkAccessibility } from '../__helpers__/accessibility';
import { renderWithEvents } from '../__helpers__/setUpTest';
describe('<ShortUrlsListSettings />', () => {
const setSettings = vi.fn();
const setUp = (shortUrlsList?: ShortUrlsSettings) => renderWithEvents(
<ShortUrlsListSettings settings={fromPartial({ shortUrlsList })} setShortUrlsListSettings={setSettings} />,
);
it('passes a11y checks', () => checkAccessibility(setUp()));
it.each([
[undefined, 'Order by: Created at - DESC'],
[fromPartial<ShortUrlsSettings>({}), 'Order by: Created at - DESC'],
[fromPartial<ShortUrlsSettings>({ defaultOrdering: {} }), 'Order by...'],
[fromPartial<ShortUrlsSettings>({ defaultOrdering: { field: 'longUrl', dir: 'DESC' } }), 'Order by: Long URL - DESC'],
[fromPartial<ShortUrlsSettings>({ defaultOrdering: { field: 'visits', dir: 'ASC' } }), 'Order by: Visits - ASC'],
])('shows expected ordering', (shortUrlsList, expectedOrder) => {
setUp(shortUrlsList);
expect(screen.getByRole('button')).toHaveTextContent(expectedOrder);
});
it.each([
['Clear selection', undefined, undefined],
['Long URL', 'longUrl', 'ASC'],
['Visits', 'visits', 'ASC'],
['Title', 'title', 'ASC'],
])('invokes setSettings when ordering changes', async (name, field, dir) => {
const { user } = setUp();
expect(setSettings).not.toHaveBeenCalled();
await user.click(screen.getByRole('button'));
await user.click(screen.getByRole('menuitem', { name }));
expect(setSettings).toHaveBeenCalledWith({ defaultOrdering: { field, dir } });
});
});

View File

@@ -1,47 +0,0 @@
import type { TagsSettings as TagsSettingsOptions } from '@shlinkio/shlink-web-component';
import { screen } from '@testing-library/react';
import { fromPartial } from '@total-typescript/shoehorn';
import type { TagsOrder } from '../../src/settings/TagsSettings';
import { TagsSettings } from '../../src/settings/TagsSettings';
import { checkAccessibility } from '../__helpers__/accessibility';
import { renderWithEvents } from '../__helpers__/setUpTest';
describe('<TagsSettings />', () => {
const setTagsSettings = vi.fn();
const setUp = (tags?: TagsSettingsOptions) => renderWithEvents(
<TagsSettings settings={fromPartial({ tags })} setTagsSettings={setTagsSettings} />,
);
it('passes a11y checks', () => checkAccessibility(setUp()));
it('renders expected amount of groups', () => {
setUp();
expect(screen.getByText('Default ordering for tags list:')).toBeInTheDocument();
expect(screen.getByRole('button', { name: 'Order by...' })).toBeInTheDocument();
});
it.each([
[undefined, 'Order by...'],
[{}, 'Order by...'],
[{ defaultOrdering: {} }, 'Order by...'],
[{ defaultOrdering: { field: 'tag', dir: 'DESC' } as TagsOrder }, 'Order by: Tag - DESC'],
[{ defaultOrdering: { field: 'visits', dir: 'ASC' } as TagsOrder }, 'Order by: Visits - ASC'],
])('shows expected ordering', (tags, expectedOrder) => {
setUp(tags);
expect(screen.getByRole('button', { name: expectedOrder })).toBeInTheDocument();
});
it.each([
['Tag', 'tag', 'ASC'],
['Visits', 'visits', 'ASC'],
['Short URLs', 'shortUrls', 'ASC'],
])('invokes setTagsSettings when ordering changes', async (name, field, dir) => {
const { user } = setUp();
expect(setTagsSettings).not.toHaveBeenCalled();
await user.click(screen.getByText('Order by...'));
await user.click(screen.getByRole('menuitem', { name }));
expect(setTagsSettings).toHaveBeenCalledWith({ defaultOrdering: { field, dir } });
});
});

View File

@@ -1,56 +0,0 @@
import { screen } from '@testing-library/react';
import { fromPartial } from '@total-typescript/shoehorn';
import type { UiSettings } from '../../src/settings/reducers/settings';
import { UserInterfaceSettings } from '../../src/settings/UserInterfaceSettings';
import { checkAccessibility } from '../__helpers__/accessibility';
import { renderWithEvents } from '../__helpers__/setUpTest';
describe('<UserInterfaceSettings />', () => {
const setUiSettings = vi.fn();
const setUp = (ui?: UiSettings, defaultDarkTheme = false) => renderWithEvents(
<UserInterfaceSettings
settings={fromPartial({ ui })}
setUiSettings={setUiSettings}
_matchMedia={vi.fn().mockReturnValue({ matches: defaultDarkTheme })}
/>,
);
it('passes a11y checks', () => checkAccessibility(setUp()));
it.each([
[{ theme: 'dark' as const }, true, true],
[{ theme: 'dark' as const }, false, true],
[{ theme: 'light' as const }, true, false],
[{ theme: 'light' as const }, false, false],
[undefined, false, false],
[undefined, true, true],
])('toggles switch if theme is dark', (ui, defaultDarkTheme, expectedChecked) => {
setUp(ui, defaultDarkTheme);
if (expectedChecked) {
expect(screen.getByLabelText('Use dark theme.')).toBeChecked();
} else {
expect(screen.getByLabelText('Use dark theme.')).not.toBeChecked();
}
});
it.each([
[{ theme: 'dark' as const }],
[{ theme: 'light' as const }],
[undefined],
])('shows different icons based on theme', (ui) => {
setUp(ui);
expect(screen.getByRole('img', { hidden: true })).toMatchSnapshot();
});
it.each([
['light' as const, 'dark' as const],
['dark' as const, 'light' as const],
])('invokes setUiSettings when theme toggle value changes', async (initialTheme, expectedTheme) => {
const { user } = setUp({ theme: initialTheme });
expect(setUiSettings).not.toHaveBeenCalled();
await user.click(screen.getByLabelText('Use dark theme.'));
expect(setUiSettings).toHaveBeenCalledWith({ theme: expectedTheme });
});
});

View File

@@ -1,129 +0,0 @@
import type { Settings } from '@shlinkio/shlink-web-component';
import { screen } from '@testing-library/react';
import { fromPartial } from '@total-typescript/shoehorn';
import { VisitsSettings } from '../../src/settings/VisitsSettings';
import { checkAccessibility } from '../__helpers__/accessibility';
import { renderWithEvents } from '../__helpers__/setUpTest';
describe('<VisitsSettings />', () => {
const setVisitsSettings = vi.fn();
const setUp = (settings: Partial<Settings> = {}) => renderWithEvents(
<VisitsSettings settings={fromPartial(settings)} setVisitsSettings={setVisitsSettings} />,
);
it('passes a11y checks', () => checkAccessibility(setUp()));
it('renders expected components', () => {
setUp();
expect(screen.getByRole('heading')).toHaveTextContent('Visits');
expect(screen.getByText('Default interval to load on visits sections:')).toBeInTheDocument();
expect(screen.getByText(/^Exclude bots wherever possible/)).toBeInTheDocument();
expect(screen.getByText('Compare visits with previous period.')).toBeInTheDocument();
});
it.each([
[fromPartial<Settings>({}), 'Last 30 days'],
[fromPartial<Settings>({ visits: {} }), 'Last 30 days'],
[
fromPartial<Settings>({
visits: {
defaultInterval: 'last7Days',
},
}),
'Last 7 days',
],
[
fromPartial<Settings>({
visits: {
defaultInterval: 'today',
},
}),
'Today',
],
])('sets expected interval as active', (settings, expectedInterval) => {
setUp(settings);
expect(screen.getByRole('button')).toHaveTextContent(expectedInterval);
});
it('invokes setVisitsSettings when interval changes', async () => {
const { user } = setUp();
const selectOption = async (name: string) => {
await user.click(screen.getByRole('button'));
await user.click(screen.getByRole('menuitem', { name }));
};
await selectOption('Last 7 days');
await selectOption('Last 180 days');
await selectOption('Yesterday');
expect(setVisitsSettings).toHaveBeenCalledTimes(3);
expect(setVisitsSettings).toHaveBeenNthCalledWith(1, { defaultInterval: 'last7Days' });
expect(setVisitsSettings).toHaveBeenNthCalledWith(2, { defaultInterval: 'last180Days' });
expect(setVisitsSettings).toHaveBeenNthCalledWith(3, { defaultInterval: 'yesterday' });
});
it.each([
[
fromPartial<Settings>({}),
/The visits coming from potential bots will be included.$/,
/The visits coming from potential bots will be excluded.$/,
],
[
fromPartial<Settings>({ visits: { excludeBots: false } }),
/The visits coming from potential bots will be included.$/,
/The visits coming from potential bots will be excluded.$/,
],
[
fromPartial<Settings>({ visits: { excludeBots: true } }),
/The visits coming from potential bots will be excluded.$/,
/The visits coming from potential bots will be included.$/,
],
])('displays expected helper text for exclude bots control', (settings, expectedText, notExpectedText) => {
setUp(settings);
const visitsComponent = screen.getByText(/^Exclude bots wherever possible/);
expect(visitsComponent).toHaveTextContent(expectedText);
expect(visitsComponent).not.toHaveTextContent(notExpectedText);
});
it('invokes setVisitsSettings when bot exclusion is toggled', async () => {
const { user } = setUp();
await user.click(screen.getByText(/^Exclude bots wherever possible/));
expect(setVisitsSettings).toHaveBeenCalledWith(expect.objectContaining({ excludeBots: true }));
});
it.each([
[
fromPartial<Settings>({}),
/When loading visits, previous period won't be loaded by default.$/,
/When loading visits, previous period will be loaded by default.$/,
],
[
fromPartial<Settings>({ visits: { loadPrevInterval: false } }),
/When loading visits, previous period won't be loaded by default.$/,
/When loading visits, previous period will be loaded by default.$/,
],
[
fromPartial<Settings>({ visits: { loadPrevInterval: true } }),
/When loading visits, previous period will be loaded by default.$/,
/When loading visits, previous period won't be loaded by default.$/,
],
])('displays expected helper text for prev interval control', (settings, expectedText, notExpectedText) => {
setUp(settings);
const visitsComponent = screen.getByText('Compare visits with previous period.');
expect(visitsComponent).toHaveTextContent(expectedText);
expect(visitsComponent).not.toHaveTextContent(notExpectedText);
});
it('invokes setVisitsSettings when loading prev visits is toggled', async () => {
const { user } = setUp();
await user.click(screen.getByText('Compare visits with previous period.'));
expect(setVisitsSettings).toHaveBeenCalledWith(expect.objectContaining({ loadPrevInterval: true }));
});
});

View File

@@ -1,55 +0,0 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`<UserInterfaceSettings /> > shows different icons based on theme 1`] = `
<svg
aria-hidden="true"
class="svg-inline--fa fa-moon user-interface__theme-icon"
data-icon="moon"
data-prefix="fas"
focusable="false"
role="img"
viewBox="0 0 384 512"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M223.5 32C100 32 0 132.3 0 256S100 480 223.5 480c60.6 0 115.5-24.2 155.8-63.4c5-4.9 6.3-12.5 3.1-18.7s-10.1-9.7-17-8.5c-9.8 1.7-19.8 2.6-30.1 2.6c-96.9 0-175.5-78.8-175.5-176c0-65.8 36-123.1 89.3-153.3c6.1-3.5 9.2-10.5 7.7-17.3s-7.3-11.9-14.3-12.5c-6.3-.5-12.6-.8-19-.8z"
fill="currentColor"
/>
</svg>
`;
exports[`<UserInterfaceSettings /> > shows different icons based on theme 2`] = `
<svg
aria-hidden="true"
class="svg-inline--fa fa-sun user-interface__theme-icon"
data-icon="sun"
data-prefix="fas"
focusable="false"
role="img"
viewBox="0 0 512 512"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M361.5 1.2c5 2.1 8.6 6.6 9.6 11.9L391 121l107.9 19.8c5.3 1 9.8 4.6 11.9 9.6s1.5 10.7-1.6 15.2L446.9 256l62.3 90.3c3.1 4.5 3.7 10.2 1.6 15.2s-6.6 8.6-11.9 9.6L391 391 371.1 498.9c-1 5.3-4.6 9.8-9.6 11.9s-10.7 1.5-15.2-1.6L256 446.9l-90.3 62.3c-4.5 3.1-10.2 3.7-15.2 1.6s-8.6-6.6-9.6-11.9L121 391 13.1 371.1c-5.3-1-9.8-4.6-11.9-9.6s-1.5-10.7 1.6-15.2L65.1 256 2.8 165.7c-3.1-4.5-3.7-10.2-1.6-15.2s6.6-8.6 11.9-9.6L121 121 140.9 13.1c1-5.3 4.6-9.8 9.6-11.9s10.7-1.5 15.2 1.6L256 65.1 346.3 2.8c4.5-3.1 10.2-3.7 15.2-1.6zM160 256a96 96 0 1 1 192 0 96 96 0 1 1 -192 0zm224 0a128 128 0 1 0 -256 0 128 128 0 1 0 256 0z"
fill="currentColor"
/>
</svg>
`;
exports[`<UserInterfaceSettings /> > shows different icons based on theme 3`] = `
<svg
aria-hidden="true"
class="svg-inline--fa fa-sun user-interface__theme-icon"
data-icon="sun"
data-prefix="fas"
focusable="false"
role="img"
viewBox="0 0 512 512"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M361.5 1.2c5 2.1 8.6 6.6 9.6 11.9L391 121l107.9 19.8c5.3 1 9.8 4.6 11.9 9.6s1.5 10.7-1.6 15.2L446.9 256l62.3 90.3c3.1 4.5 3.7 10.2 1.6 15.2s-6.6 8.6-11.9 9.6L391 391 371.1 498.9c-1 5.3-4.6 9.8-9.6 11.9s-10.7 1.5-15.2-1.6L256 446.9l-90.3 62.3c-4.5 3.1-10.2 3.7-15.2 1.6s-8.6-6.6-9.6-11.9L121 391 13.1 371.1c-5.3-1-9.8-4.6-11.9-9.6s-1.5-10.7 1.6-15.2L65.1 256 2.8 165.7c-3.1-4.5-3.7-10.2-1.6-15.2s6.6-8.6 11.9-9.6L121 121 140.9 13.1c1-5.3 4.6-9.8 9.6-11.9s10.7-1.5 15.2 1.6L256 65.1 346.3 2.8c4.5-3.1 10.2-3.7 15.2-1.6zM160 256a96 96 0 1 1 192 0 96 96 0 1 1 -192 0zm224 0a128 128 0 1 0 -256 0 128 128 0 1 0 256 0z"
fill="currentColor"
/>
</svg>
`;

View File

@@ -1,75 +1,25 @@
import {
DEFAULT_SHORT_URLS_ORDERING,
setRealTimeUpdatesInterval,
setShortUrlCreationSettings,
setShortUrlsListSettings,
setTagsSettings,
settingsReducer,
setUiSettings,
setVisitsSettings,
toggleRealTimeUpdates,
} from '../../../src/settings/reducers/settings';
import type { Settings } from '@shlinkio/shlink-web-component/settings';
import { fromPartial } from '@total-typescript/shoehorn';
import { DEFAULT_SHORT_URLS_ORDERING, setSettings, settingsReducer } from '../../../src/settings/reducers/settings';
describe('settingsReducer', () => {
const realTimeUpdates = { enabled: true };
const shortUrlCreation = { validateUrls: false };
const ui = { theme: 'light' };
const visits = { defaultInterval: 'last30Days' };
const ui = { theme: 'light' as const };
const visits = { defaultInterval: 'last30Days' as const };
const shortUrlsList = { defaultOrdering: DEFAULT_SHORT_URLS_ORDERING };
const settings = { realTimeUpdates, shortUrlCreation, ui, visits, shortUrlsList };
const settings = fromPartial<Settings>({ realTimeUpdates, shortUrlCreation, ui, visits, shortUrlsList });
describe('reducer', () => {
it('returns realTimeUpdates when action is SET_SETTINGS', () => {
expect(settingsReducer(undefined, toggleRealTimeUpdates(realTimeUpdates.enabled))).toEqual(settings);
it('can update settings', () => {
expect(settingsReducer(undefined, setSettings(settings))).toEqual(settings);
});
});
describe('toggleRealTimeUpdates', () => {
it.each([[true], [false]])('updates settings with provided value and then loads updates again', (enabled) => {
const { payload } = toggleRealTimeUpdates(enabled);
expect(payload).toEqual({ realTimeUpdates: { enabled } });
});
});
describe('setRealTimeUpdatesInterval', () => {
it.each([[0], [1], [2], [10]])('updates settings with provided value and then loads updates again', (interval) => {
const { payload } = setRealTimeUpdatesInterval(interval);
expect(payload).toEqual({ realTimeUpdates: { interval } });
});
});
describe('setShortUrlCreationSettings', () => {
it('creates action to set shortUrlCreation settings', () => {
const { payload } = setShortUrlCreationSettings({ validateUrls: true });
expect(payload).toEqual({ shortUrlCreation: { validateUrls: true } });
});
});
describe('setUiSettings', () => {
it('creates action to set ui settings', () => {
const { payload } = setUiSettings({ theme: 'dark' });
expect(payload).toEqual({ ui: { theme: 'dark' } });
});
});
describe('setVisitsSettings', () => {
it('creates action to set visits settings', () => {
const { payload } = setVisitsSettings({ defaultInterval: 'last180Days' });
expect(payload).toEqual({ visits: { defaultInterval: 'last180Days' } });
});
});
describe('setTagsSettings', () => {
it('creates action to set tags settings', () => {
const { payload } = setTagsSettings({ defaultMode: 'list' });
expect(payload).toEqual({ tags: { defaultMode: 'list' } });
});
});
describe('setShortUrlsListSettings', () => {
it('creates action to set short URLs list settings', () => {
const { payload } = setShortUrlsListSettings({ defaultOrdering: DEFAULT_SHORT_URLS_ORDERING });
expect(payload).toEqual({ shortUrlsList: { defaultOrdering: DEFAULT_SHORT_URLS_ORDERING } });
describe('setSettings', () => {
it('creates action to set settings', () => {
const { payload } = setSettings(settings);
expect(payload).toEqual(settings);
});
});
});