Compare commits

..

16 Commits

Author SHA1 Message Date
Alejandro Celaya
e9afaae619 Merge pull request #1741 from shlinkio/develop
Release 4.6.1
2025-11-15 12:12:54 +01:00
Alejandro Celaya
77f8d63b95 Merge pull request #1730 from shlinkio/develop
Release 4.6.0
2025-11-12 14:53:52 +01:00
Alejandro Celaya
aab26e3736 Merge pull request #1640 from shlinkio/develop
Release 4.5.1
2025-08-13 10:15:59 +02:00
Alejandro Celaya
0dc6d70dd9 Merge pull request #1629 from shlinkio/develop
Release 4.5.0
2025-08-08 09:23:28 +02:00
Alejandro Celaya
4eab3b6935 Merge pull request #1594 from shlinkio/develop
Release 4.4.1
2025-06-23 10:10:47 +02:00
Alejandro Celaya
b747e63d51 Merge pull request #1524 from shlinkio/develop
Release 4.4.0
2025-04-20 17:07:11 +02:00
Alejandro Celaya
5aa113ec16 Merge pull request #1387 from shlinkio/develop
Release 4.3.0
2024-11-30 10:48:52 +01:00
Alejandro Celaya
2e438f9814 Merge pull request #1351 from shlinkio/develop
Release 4.2.2
2024-10-19 12:15:18 +02:00
Alejandro Celaya
9a798c20c0 Merge pull request #1329 from shlinkio/develop
Release 4.2.1
2024-10-09 14:33:56 +02:00
Alejandro Celaya
9e1a803b8d Merge pull request #1324 from shlinkio/develop
Release 4.2.0
2024-10-07 09:54:43 +02:00
Alejandro Celaya
d18ebf8911 Merge pull request #1147 from shlinkio/develop
Release 4.1.2
2024-04-17 09:59:22 +02:00
Alejandro Celaya
5c2e99cba1 Merge pull request #1137 from shlinkio/develop
Release 4.1.1
2024-04-11 09:24:15 +02:00
Alejandro Celaya
c75a3a4073 Merge pull request #1103 from shlinkio/develop
Release 4.1.0
2024-03-17 12:26:34 +01:00
Alejandro Celaya
e68643108a Merge pull request #1049 from shlinkio/develop
Release 4.0.1
2024-02-01 08:58:14 +01:00
Alejandro Celaya
8a7a51be2f Merge pull request #1045 from shlinkio/develop
Release 4.0.0
2024-01-29 19:11:22 +01:00
Alejandro Celaya
f5e92c6897 Merge pull request #848 from shlinkio/develop
Release 3.10.2
2023-07-09 10:17:23 +02:00
18 changed files with 4589 additions and 3218 deletions

View File

@@ -4,40 +4,6 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org). The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org).
## [4.7.0] - 2026-02-04
### Added
* [shlink-web-component] Add support for Shlink 5.0.0, by supporting date-based redirect conditions.
### Changed
* *Nothing*
### Deprecated
* *Nothing*
### Removed
* *Nothing*
### Fixed
* *Nothing*
## [4.6.2] - 2025-11-15
### Added
* *Nothing*
### Changed
* *Nothing*
### Deprecated
* *Nothing*
### Removed
* *Nothing*
### Fixed
* [shlink-web-component#878](https://github.com/shlinkio/shlink-web-component/issues/878) Fix real-time updates interval setting being ignored.
## [4.6.1] - 2025-11-15 ## [4.6.1] - 2025-11-15
### Added ### Added
* *Nothing* * *Nothing*

View File

@@ -1,4 +1,4 @@
FROM node:25.6-alpine AS node FROM node:25.2-alpine AS node
COPY . /shlink-web-client COPY . /shlink-web-client
ARG VERSION="latest" ARG VERSION="latest"
ENV VERSION=${VERSION} ENV VERSION=${VERSION}

View File

@@ -1,4 +1,4 @@
FROM mcr.microsoft.com/playwright:v1.58.2-noble FROM mcr.microsoft.com/playwright:v1.56.1-noble
ENV NODE_VERSION 22.14 ENV NODE_VERSION 22.14
ENV TINI_VERSION v0.19.0 ENV TINI_VERSION v0.19.0

7590
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -20,54 +20,53 @@
"test:verbose": "node --run test -- --verbose" "test:verbose": "node --run test -- --verbose"
}, },
"dependencies": { "dependencies": {
"@fortawesome/fontawesome-free": "^7.2.0", "@fortawesome/fontawesome-free": "^7.1.0",
"@fortawesome/fontawesome-svg-core": "^7.2.0", "@fortawesome/fontawesome-svg-core": "^7.1.0",
"@fortawesome/free-brands-svg-icons": "^7.2.0", "@fortawesome/free-brands-svg-icons": "^7.1.0",
"@fortawesome/free-regular-svg-icons": "^7.2.0", "@fortawesome/free-regular-svg-icons": "^7.1.0",
"@fortawesome/free-solid-svg-icons": "^7.2.0", "@fortawesome/free-solid-svg-icons": "^7.1.0",
"@fortawesome/react-fontawesome": "^3.2.0", "@fortawesome/react-fontawesome": "^3.1.0",
"@json2csv/plainjs": "^7.0.6", "@json2csv/plainjs": "^7.0.6",
"@reduxjs/toolkit": "^2.11.2", "@reduxjs/toolkit": "^2.10.1",
"@shlinkio/data-manipulation": "^1.0.4", "@shlinkio/data-manipulation": "^1.0.4",
"@shlinkio/shlink-frontend-kit": "^1.4.0", "@shlinkio/shlink-frontend-kit": "^1.3.1",
"@shlinkio/shlink-js-sdk": "^3.1.0", "@shlinkio/shlink-js-sdk": "^3.0.1",
"@shlinkio/shlink-web-component": "^0.18.0", "@shlinkio/shlink-web-component": "^0.17.0",
"@vitest/browser-playwright": "^4.0.18", "@vitest/browser-playwright": "^4.0.9",
"bottlejs": "^2.0.1", "bottlejs": "^2.0.1",
"clsx": "^2.1.1", "clsx": "^2.1.1",
"compare-versions": "^6.1.1", "compare-versions": "^6.1.1",
"csvtojson": "^2.0.14", "csvtojson": "^2.0.14",
"date-fns": "^4.1.0", "date-fns": "^4.1.0",
"react": "^19.2.4", "react": "^19.2.0",
"react-dom": "^19.2.4", "react-dom": "^19.2.0",
"react-external-link": "^2.6.1", "react-external-link": "^2.6.1",
"react-redux": "^9.2.0", "react-redux": "^9.2.0",
"react-router": "^7.13.0", "react-router": "^7.9.6",
"redux-localstorage-simple": "^2.5.1", "redux-localstorage-simple": "^2.5.1",
"workbox-core": "^7.4.0", "workbox-core": "^7.3.0",
"workbox-expiration": "^7.4.0", "workbox-expiration": "^7.3.0",
"workbox-precaching": "^7.4.0", "workbox-precaching": "^7.3.0",
"workbox-routing": "^7.4.0", "workbox-routing": "^7.3.0",
"workbox-strategies": "^7.4.0" "workbox-strategies": "^7.3.0"
}, },
"devDependencies": { "devDependencies": {
"@shlinkio/eslint-config-js-coding-standard": "~3.7.0", "@shlinkio/eslint-config-js-coding-standard": "~3.7.0",
"@stylistic/eslint-plugin": "^5.7.1", "@stylistic/eslint-plugin": "^5.5.0",
"@tailwindcss/vite": "^4.2.0", "@tailwindcss/vite": "^4.1.17",
"@testing-library/jest-dom": "^6.9.1", "@testing-library/jest-dom": "^6.9.1",
"@testing-library/react": "^16.3.2", "@testing-library/react": "^16.3.0",
"@testing-library/user-event": "^14.6.1", "@testing-library/user-event": "^14.6.1",
"@total-typescript/shoehorn": "^0.1.2", "@total-typescript/shoehorn": "^0.1.2",
"@types/node": "^25.3.0", "@types/react": "^19.2.5",
"@types/react": "^19.2.14",
"@types/react-dom": "^19.2.3", "@types/react-dom": "^19.2.3",
"@vitejs/plugin-react": "^5.1.4", "@vitejs/plugin-react": "^5.1.1",
"@vitest/browser": "^4.0.3", "@vitest/browser": "^4.0.3",
"@vitest/coverage-v8": "^4.0.18", "@vitest/coverage-v8": "^4.0.9",
"adm-zip": "^0.5.16", "adm-zip": "^0.5.16",
"axe-core": "^4.11.1", "axe-core": "^4.11.0",
"chalk": "^5.6.2", "chalk": "^5.6.2",
"eslint": "^9.39.2", "eslint": "^9.39.1",
"eslint-plugin-import": "^2.32.0", "eslint-plugin-import": "^2.32.0",
"eslint-plugin-jsx-a11y": "^6.10.2", "eslint-plugin-jsx-a11y": "^6.10.2",
"eslint-plugin-react": "^7.37.5", "eslint-plugin-react": "^7.37.5",
@@ -75,12 +74,12 @@
"eslint-plugin-react-hooks": "^7.0.1", "eslint-plugin-react-hooks": "^7.0.1",
"eslint-plugin-simple-import-sort": "^12.1.1", "eslint-plugin-simple-import-sort": "^12.1.1",
"history": "^5.3.0", "history": "^5.3.0",
"playwright": "^1.58.2", "playwright": "^1.56.1",
"tailwindcss": "^4.1.3", "tailwindcss": "^4.1.3",
"typescript": "^5.9.3", "typescript": "^5.9.3",
"typescript-eslint": "^8.54.0", "typescript-eslint": "^8.46.4",
"vite": "^7.3.1", "vite": "^7.2.2",
"vite-plugin-pwa": "^1.2.0", "vite-plugin-pwa": "^1.1.0",
"vitest": "^4.0.3" "vitest": "^4.0.3"
}, },
"browserslist": [ "browserslist": [

View File

@@ -30,11 +30,8 @@ export const useDependencies = <T extends unknown[]>(...names: string[]): T => {
}) as T; }) as T;
}; };
type Optionalize<P, K extends keyof P> = Omit<P, K> & Partial<Pick<P, K>>;
/** /**
* Higher Order Component used to inject services into components as props. * Higher Order Component used to inject services into components as props.
* All dependencies become optional props so that they can still be explicitly set in tests if desired.
*/ */
export function withDependencies< export function withDependencies<
Props extends Record<string, unknown>, Props extends Record<string, unknown>,
@@ -42,16 +39,16 @@ export function withDependencies<
>( >(
Component: ComponentType<Props>, Component: ComponentType<Props>,
dependencyNames: DependencyName[], dependencyNames: DependencyName[],
): ComponentType<Optionalize<Props, DependencyName>> { ): ComponentType<Omit<Props, DependencyName>> {
function Wrapper(props: Omit<Props, DependencyName>) { function Wrapper(props: Omit<Props, DependencyName>) {
const container = useContext(ContainerContext); const container = useContainer('withDependencies');
// Inject services, unless they have been overridden by props passed from // Inject services, unless they have been overridden by props passed from
// the parent component. // the parent component.
const dependencies: Partial<Record<DependencyName, unknown>> = {}; const dependencies: Partial<Record<DependencyName, unknown>> = {};
for (const dependency of dependencyNames) { for (const dependency of dependencyNames) {
if (!(dependency in props)) { if (!(dependency in props)) {
dependencies[dependency] = container?.[dependency]; dependencies[dependency] = container[dependency];
} }
} }

View File

@@ -1,11 +1,8 @@
import type { ShlinkApiClient } from '@shlinkio/shlink-js-sdk';
import type { RenderOptions } from '@testing-library/react'; import type { RenderOptions } from '@testing-library/react';
import { render } from '@testing-library/react'; import { render } 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 type { PropsWithChildren, ReactElement } from 'react'; import type { PropsWithChildren, ReactElement } from 'react';
import { Provider } from 'react-redux'; import { Provider } from 'react-redux';
import { ContainerProvider } from '../../src/container/context';
import type { RootState } from '../../src/store'; import type { RootState } from '../../src/store';
import { setUpStore } from '../../src/store'; import { setUpStore } from '../../src/store';
@@ -15,33 +12,15 @@ export const renderWithEvents = (element: ReactElement, options?: RenderOptions)
}); });
export type RenderOptionsWithState = Omit<RenderOptions, 'wrapper'> & { export type RenderOptionsWithState = Omit<RenderOptions, 'wrapper'> & {
/** Initial state for the redux store */
initialState?: Partial<RootState>; initialState?: Partial<RootState>;
/**
* If provided, it will set this as the `buildShlinkApiClient` dependency in the `ContainerProvider`.
* If more dependencies are needed, then explicitly define your own `ContainerProvider` and make sure it includes a
* `buildShlinkApiClient` service.
*
* Defaults to vi.fn()
*/
buildShlinkApiClient?: () => ShlinkApiClient;
}; };
/**
* Render provided ReactElement wrapped in a redux `Provider` and a `ContainerProvider` with a single
* `buildShlinkApiClient` dependency.
*/
export const renderWithStore = ( export const renderWithStore = (
element: ReactElement, element: ReactElement,
{ initialState = {}, buildShlinkApiClient = vi.fn(), ...options }: RenderOptionsWithState = {}, { initialState = {}, ...options }: RenderOptionsWithState = {},
) => { ) => {
const store = setUpStore(initialState); const store = setUpStore(initialState);
const Wrapper = ({ children }: PropsWithChildren) => ( const Wrapper = ({ children }: PropsWithChildren) => <Provider store={store}>{children}</Provider>;
<ContainerProvider value={fromPartial({ buildShlinkApiClient })}>
<Provider store={store}>{children}</Provider>
</ContainerProvider>
);
return { return {
store, store,

View File

@@ -2,6 +2,7 @@ import { screen } from '@testing-library/react';
import { fromPartial } from '@total-typescript/shoehorn'; import { fromPartial } from '@total-typescript/shoehorn';
import { MemoryRouter } from 'react-router'; import { MemoryRouter } from 'react-router';
import { Home } from '../../src/common/Home'; import { Home } from '../../src/common/Home';
import { ContainerProvider } from '../../src/container/context';
import type { ServersMap, ServerWithId } from '../../src/servers/data'; import type { ServersMap, ServerWithId } from '../../src/servers/data';
import { checkAccessibility } from '../__helpers__/accessibility'; import { checkAccessibility } from '../__helpers__/accessibility';
import { renderWithStore } from '../__helpers__/setUpTest'; import { renderWithStore } from '../__helpers__/setUpTest';
@@ -9,7 +10,9 @@ import { renderWithStore } from '../__helpers__/setUpTest';
describe('<Home />', () => { describe('<Home />', () => {
const setUp = (servers: ServersMap = {}) => renderWithStore( const setUp = (servers: ServersMap = {}) => renderWithStore(
<MemoryRouter> <MemoryRouter>
<Home /> <ContainerProvider value={fromPartial({ buildShlinkApiClient: vi.fn() })}>
<Home />
</ContainerProvider>
</MemoryRouter>, </MemoryRouter>,
{ {
initialState: { servers }, initialState: { servers },

View File

@@ -1,7 +1,9 @@
import { screen } from '@testing-library/react'; import { screen } from '@testing-library/react';
import { fromPartial } from '@total-typescript/shoehorn';
import { createMemoryHistory } from 'history'; import { createMemoryHistory } from 'history';
import { Router } from 'react-router'; import { Router } from 'react-router';
import { MainHeader } from '../../src/common/MainHeader'; import { MainHeader } from '../../src/common/MainHeader';
import { ContainerProvider } from '../../src/container/context';
import { checkAccessibility } from '../__helpers__/accessibility'; import { checkAccessibility } from '../__helpers__/accessibility';
import { renderWithStore } from '../__helpers__/setUpTest'; import { renderWithStore } from '../__helpers__/setUpTest';
@@ -11,8 +13,10 @@ describe('<MainHeader />', () => {
history.push(pathname); history.push(pathname);
return renderWithStore( return renderWithStore(
<Router location={history.location} navigator={history} unstable_useTransitions={false}> <Router location={history.location} navigator={history}>
<MainHeader /> <ContainerProvider value={fromPartial({ buildShlinkApiClient: vi.fn() })}>
<MainHeader />
</ContainerProvider>
</Router>, </Router>,
); );
}; };

View File

@@ -1,13 +1,19 @@
import { fromPartial } from '@total-typescript/shoehorn'; import { fromPartial } from '@total-typescript/shoehorn';
import { ShlinkVersionsContainer } from '../../src/common/ShlinkVersionsContainer'; import { ShlinkVersionsContainer } from '../../src/common/ShlinkVersionsContainer';
import { ContainerProvider } from '../../src/container/context';
import type { ReachableServer, SelectedServer } from '../../src/servers/data'; import type { ReachableServer, SelectedServer } from '../../src/servers/data';
import { checkAccessibility } from '../__helpers__/accessibility'; import { checkAccessibility } from '../__helpers__/accessibility';
import { renderWithStore } from '../__helpers__/setUpTest'; import { renderWithStore } from '../__helpers__/setUpTest';
describe('<ShlinkVersionsContainer />', () => { describe('<ShlinkVersionsContainer />', () => {
const setUp = (selectedServer: SelectedServer = null) => renderWithStore(<ShlinkVersionsContainer />, { const setUp = (selectedServer: SelectedServer = null) => renderWithStore(
initialState: { selectedServer }, <ContainerProvider value={fromPartial({ buildShlinkApiClient: vi.fn() })}>
}); <ShlinkVersionsContainer />
</ContainerProvider>,
{
initialState: { selectedServer },
},
);
it.each([ it.each([
[null], [null],

View File

@@ -2,6 +2,7 @@ import { screen } from '@testing-library/react';
import { fromPartial } from '@total-typescript/shoehorn'; import { fromPartial } from '@total-typescript/shoehorn';
import { MemoryRouter } from 'react-router'; import { MemoryRouter } from 'react-router';
import { ShlinkWebComponentContainer } from '../../src/common/ShlinkWebComponentContainer'; import { ShlinkWebComponentContainer } from '../../src/common/ShlinkWebComponentContainer';
import { ContainerProvider } from '../../src/container/context';
import type { NonReachableServer, NotFoundServer, SelectedServer } from '../../src/servers/data'; import type { NonReachableServer, NotFoundServer, SelectedServer } from '../../src/servers/data';
import { checkAccessibility } from '../__helpers__/accessibility'; import { checkAccessibility } from '../__helpers__/accessibility';
import { renderWithStore } from '../__helpers__/setUpTest'; import { renderWithStore } from '../__helpers__/setUpTest';
@@ -15,7 +16,12 @@ vi.mock('@shlinkio/shlink-web-component', () => ({
describe('<ShlinkWebComponentContainer />', () => { describe('<ShlinkWebComponentContainer />', () => {
const setUp = (selectedServer: SelectedServer) => renderWithStore( const setUp = (selectedServer: SelectedServer) => renderWithStore(
<MemoryRouter> <MemoryRouter>
<ShlinkWebComponentContainer TagColorsStorage={fromPartial({})} /> <ContainerProvider value={fromPartial({
buildShlinkApiClient: vi.fn(),
TagColorsStorage: fromPartial({}),
})}>
<ShlinkWebComponentContainer />
</ContainerProvider>
</MemoryRouter>, </MemoryRouter>,
{ {
initialState: { selectedServer, servers: {}, settings: {} }, initialState: { selectedServer, servers: {}, settings: {} },

View File

@@ -2,6 +2,7 @@ import { fireEvent, screen, waitFor } from '@testing-library/react';
import { fromPartial } from '@total-typescript/shoehorn'; import { fromPartial } from '@total-typescript/shoehorn';
import { createMemoryHistory } from 'history'; import { createMemoryHistory } from 'history';
import { Router } from 'react-router'; import { Router } from 'react-router';
import { ContainerProvider } from '../../src/container/context';
import { CreateServer } from '../../src/servers/CreateServer'; import { CreateServer } from '../../src/servers/CreateServer';
import type { ServersMap } from '../../src/servers/data'; import type { ServersMap } from '../../src/servers/data';
import { checkAccessibility } from '../__helpers__/accessibility'; import { checkAccessibility } from '../__helpers__/accessibility';
@@ -29,8 +30,14 @@ describe('<CreateServer />', () => {
return { return {
history, history,
...renderWithStore( ...renderWithStore(
<Router location={history.location} navigator={history} unstable_useTransitions={false}> <Router location={history.location} navigator={history}>
<CreateServer useTimeoutToggle={useTimeoutToggle} /> <ContainerProvider value={fromPartial({
ImportServersBtn: () => <>ImportServersBtn</>,
useTimeoutToggle,
buildShlinkApiClient: vi.fn(),
})}>
<CreateServer />
</ContainerProvider>
</Router>, </Router>,
{ {
initialState: { servers }, initialState: { servers },

View File

@@ -11,7 +11,7 @@ describe('<DeleteServerButton />', () => {
const setUp = (children: ReactNode = 'Remove this server') => { const setUp = (children: ReactNode = 'Remove this server') => {
const history = createMemoryHistory({ initialEntries: ['/foo'] }); const history = createMemoryHistory({ initialEntries: ['/foo'] });
const result = renderWithStore( const result = renderWithStore(
<Router location={history.location} navigator={history} unstable_useTransitions={false}> <Router location={history.location} navigator={history}>
<DeleteServerButton server={fromPartial({})}>{children}</DeleteServerButton> <DeleteServerButton server={fromPartial({})}>{children}</DeleteServerButton>
</Router>, </Router>,
); );

View File

@@ -2,6 +2,7 @@ import { fireEvent, screen, waitFor } from '@testing-library/react';
import { fromPartial } from '@total-typescript/shoehorn'; import { fromPartial } from '@total-typescript/shoehorn';
import { createMemoryHistory } from 'history'; import { createMemoryHistory } from 'history';
import { Router } from 'react-router'; import { Router } from 'react-router';
import { ContainerProvider } from '../../src/container/context';
import type { ReachableServer, SelectedServer } from '../../src/servers/data'; import type { ReachableServer, SelectedServer } from '../../src/servers/data';
import { isServerWithId } from '../../src/servers/data'; import { isServerWithId } from '../../src/servers/data';
import { EditServer } from '../../src/servers/EditServer'; import { EditServer } from '../../src/servers/EditServer';
@@ -20,8 +21,10 @@ describe('<EditServer />', () => {
return { return {
history, history,
...renderWithStore( ...renderWithStore(
<Router location={history.location} navigator={history} unstable_useTransitions={false}> <Router location={history.location} navigator={history}>
<EditServer /> <ContainerProvider value={fromPartial({ buildShlinkApiClient: vi.fn() })}>
<EditServer />
</ContainerProvider>
</Router>, </Router>,
{ {
initialState: { initialState: {

View File

@@ -1,6 +1,7 @@
import { screen, waitFor } from '@testing-library/react'; import { screen, waitFor } from '@testing-library/react';
import { fromPartial } from '@total-typescript/shoehorn'; import { fromPartial } from '@total-typescript/shoehorn';
import { MemoryRouter } from 'react-router'; import { MemoryRouter } from 'react-router';
import { ContainerProvider } from '../../src/container/context';
import type { ServersMap, ServerWithId } from '../../src/servers/data'; import type { ServersMap, ServerWithId } from '../../src/servers/data';
import { ManageServers } from '../../src/servers/ManageServers'; import { ManageServers } from '../../src/servers/ManageServers';
import type { ServersExporter } from '../../src/servers/services/ServersExporter'; import type { ServersExporter } from '../../src/servers/services/ServersExporter';
@@ -16,7 +17,14 @@ describe('<ManageServers />', () => {
); );
const setUp = (servers: ServersMap = {}) => renderWithStore( const setUp = (servers: ServersMap = {}) => renderWithStore(
<MemoryRouter> <MemoryRouter>
<ManageServers useTimeoutToggle={useTimeoutToggle} ServersExporter={serversExporter} /> <ContainerProvider value={fromPartial({
ServersExporter: serversExporter,
ImportServersBtn: () => <span>ImportServersBtn</span>,
useTimeoutToggle,
buildShlinkApiClient: vi.fn(),
})}>
<ManageServers />
</ContainerProvider>
</MemoryRouter>, </MemoryRouter>,
{ {
initialState: { servers }, initialState: { servers },

View File

@@ -1,6 +1,7 @@
import { screen } from '@testing-library/react'; import { screen } from '@testing-library/react';
import { fromPartial } from '@total-typescript/shoehorn'; import { fromPartial } from '@total-typescript/shoehorn';
import { MemoryRouter } from 'react-router'; import { MemoryRouter } from 'react-router';
import { ContainerProvider } from '../../src/container/context';
import type { ServersMap } from '../../src/servers/data'; import type { ServersMap } from '../../src/servers/data';
import { ServersDropdown } from '../../src/servers/ServersDropdown'; import { ServersDropdown } from '../../src/servers/ServersDropdown';
import { checkAccessibility } from '../__helpers__/accessibility'; import { checkAccessibility } from '../__helpers__/accessibility';
@@ -14,9 +15,11 @@ describe('<ServersDropdown />', () => {
}; };
const setUp = (servers: ServersMap = fallbackServers) => renderWithStore( const setUp = (servers: ServersMap = fallbackServers) => renderWithStore(
<MemoryRouter> <MemoryRouter>
<ul role="menu"> <ContainerProvider value={fromPartial({ buildShlinkApiClient: vi.fn() })}>
<ServersDropdown /> <ul role="menu">
</ul> <ServersDropdown />
</ul>
</ContainerProvider>
</MemoryRouter>, </MemoryRouter>,
{ {
initialState: { selectedServer: null, servers }, initialState: { selectedServer: null, servers },

View File

@@ -1,5 +1,6 @@
import { screen, waitFor } from '@testing-library/react'; import { screen, waitFor } from '@testing-library/react';
import { fromPartial } from '@total-typescript/shoehorn'; import { fromPartial } from '@total-typescript/shoehorn';
import { ContainerProvider } from '../../../src/container/context';
import type { ServerData, ServersMap, ServerWithId } from '../../../src/servers/data'; import type { ServerData, ServersMap, ServerWithId } from '../../../src/servers/data';
import type { ImportServersBtnProps } from '../../../src/servers/helpers/ImportServersBtn'; import type { ImportServersBtnProps } from '../../../src/servers/helpers/ImportServersBtn';
import { ImportServersBtn } from '../../../src/servers/helpers/ImportServersBtn'; import { ImportServersBtn } from '../../../src/servers/helpers/ImportServersBtn';
@@ -13,7 +14,9 @@ describe('<ImportServersBtn />', () => {
const importServersFromFile = vi.fn().mockResolvedValue([]); const importServersFromFile = vi.fn().mockResolvedValue([]);
const serversImporterMock = fromPartial<ServersImporter>({ importServersFromFile }); const serversImporterMock = fromPartial<ServersImporter>({ importServersFromFile });
const setUp = (props: Partial<ImportServersBtnProps> = {}, servers: ServersMap = {}) => renderWithStore( const setUp = (props: Partial<ImportServersBtnProps> = {}, servers: ServersMap = {}) => renderWithStore(
<ImportServersBtn {...props} onImport={onImportMock} ServersImporter={serversImporterMock} />, <ContainerProvider value={fromPartial({ ServersImporter: serversImporterMock })}>
<ImportServersBtn {...props} onImport={onImportMock} />
</ContainerProvider>,
{ {
initialState: { servers }, initialState: { servers },
}, },

View File

@@ -1,6 +1,7 @@
import { screen } from '@testing-library/react'; import { screen } from '@testing-library/react';
import { fromPartial } from '@total-typescript/shoehorn'; import { fromPartial } from '@total-typescript/shoehorn';
import { MemoryRouter } from 'react-router'; import { MemoryRouter } from 'react-router';
import { ContainerProvider } from '../../../src/container/context';
import type { NonReachableServer, NotFoundServer, SelectedServer } from '../../../src/servers/data'; import type { NonReachableServer, NotFoundServer, SelectedServer } from '../../../src/servers/data';
import { ServerError } from '../../../src/servers/helpers/ServerError'; import { ServerError } from '../../../src/servers/helpers/ServerError';
import { checkAccessibility } from '../../__helpers__/accessibility'; import { checkAccessibility } from '../../__helpers__/accessibility';
@@ -9,7 +10,9 @@ import { renderWithStore } from '../../__helpers__/setUpTest';
describe('<ServerError />', () => { describe('<ServerError />', () => {
const setUp = (selectedServer: SelectedServer) => renderWithStore( const setUp = (selectedServer: SelectedServer) => renderWithStore(
<MemoryRouter> <MemoryRouter>
<ServerError /> <ContainerProvider value={fromPartial({ buildShlinkApiClient: vi.fn() })}>
<ServerError />
</ContainerProvider>
</MemoryRouter>, </MemoryRouter>,
{ {
initialState: { selectedServer, servers: {} }, initialState: { selectedServer, servers: {} },