Skip to content

Instantly share code, notes, and snippets.

@DanielBarbakadze
Created July 30, 2024 16:38
Show Gist options
  • Save DanielBarbakadze/7ad00231ec8ee2371a83e6b046bfafe5 to your computer and use it in GitHub Desktop.
Save DanielBarbakadze/7ad00231ec8ee2371a83e6b046bfafe5 to your computer and use it in GitHub Desktop.
use-query-params mocked adapter for Vitest (ReactRouter6Adapter, typescript)
import React, { useContext } from 'react';
import {
BrowserRouter,
useNavigate,
useLocation,
UNSAFE_NavigationContext,
UNSAFE_DataRouterContext,
} from 'react-router-dom';
import {
type QueryParamAdapterComponent,
type QueryParamAdapter,
} from 'use-query-params';
import { QueryParamProvider } from 'use-query-params';
type ChildrenProps = {
children?: React.ReactNode;
};
const ProvidersHoC = ({ children }: ChildrenProps) => (
<BrowserRouter>
<QueryParamProvider adapter={MockReactRouter6Adapter}>
{children}
</QueryParamProvider>
</BrowserRouter>
);
export { ProvidersHoC };
type Location = {
search?: string;
state?: unknown;
};
type Adapter = {
location: Location;
replace(location: Location): void;
push(location: Location): void;
};
type MockReactRouter6AdapterProps = {
readonly children: (adapter: QueryParamAdapter) => React.ReactElement | null;
};
const MockReactRouter6Adapter: QueryParamAdapterComponent = ({
children,
}: MockReactRouter6AdapterProps): React.ReactElement | null => {
const navigator = useContext(UNSAFE_NavigationContext)?.navigator;
const navigate = useNavigate();
const router = useContext(UNSAFE_DataRouterContext)?.router;
const location = useLocation();
const adapter: Adapter = {
replace(location2: Location) {
navigate(location2.search ?? '?', {
replace: true,
state: location2.state,
});
},
push(location2: Location) {
navigate(location2.search ?? '?', {
replace: false,
state: location2.state,
});
},
get location() {
return (
router?.state?.location ??
(navigator && 'location' in navigator
? navigator.location
: undefined) ??
location
);
},
};
return <>{children(adapter as QueryParamAdapter)}</>;
};
@DanielBarbakadze
Copy link
Author

Against prompted error:

 FAIL  src/tests/example.test.tsx > TestCase > title
TypeError: Cannot destructure property 'navigator' of '(0 , import_react.useContext)(...)' as it is null.
 ❯ ReactRouter6Adapter node_modules/.pnpm/use-query-params@2.2.1_react-dom@18.2.0_react-router-dom@6.22.1_react@18.2.0/node_modules/use-query-params/adapters/react-router-6/index.cjs.js:32:11

 ❯ renderWithHooks node_modules/.pnpm/react-dom@18.2.0_react@18.2.0/node_modules/react-dom/cjs/react-dom.development.js:16305:18
 ❯ mountIndeterminateComponent node_modules/.pnpm/react-dom@18.2.0_react@18.2.0/node_modules/react-dom/cjs/react-dom.development.js:20074:13
 ❯ beginWork node_modules/.pnpm/react-dom@18.2.0_react@18.2.0/node_modules/react-dom/cjs/react-dom.development.js:21587:16
 ❯ beginWork$1 node_modules/.pnpm/react-dom@18.2.0_react@18.2.0/node_modules/react-dom/cjs/react-dom.development.js:27426:14
 ❯ performUnitOfWork node_modules/.pnpm/react-dom@18.2.0_react@18.2.0/node_modules/react-dom/cjs/react-dom.development.js:26560:12
 ❯ workLoopSync node_modules/.pnpm/react-dom@18.2.0_react@18.2.0/node_modules/react-dom/cjs/react-dom.development.js:26466:5
 ❯ renderRootSync node_modules/.pnpm/react-dom@18.2.0_react@18.2.0/node_modules/react-dom/cjs/react-dom.development.js:26434:7
 ❯ recoverFromConcurrentError node_modules/.pnpm/react-dom@18.2.0_react@18.2.0/node_modules/react-dom/cjs/react-dom.development.js:25850:20
 ❯ performConcurrentWorkOnRoot node_modules/.pnpm/react-dom@18.2.0_react@18.2.0/node_modules/react-dom/cjs/react-dom.development.js:25750:22

@DanielBarbakadze
Copy link
Author

This can be also a simpler implementation

import React from 'react';

type Location = {
  readonly search: string;
  readonly state?: any;
};

type Adapter = {
  location: Location;
  replace(location: Location): void;
  push(location: Location): void;
};

type MockReactRouter6AdapterProps = {
  readonly children: (adapter: Adapter) => React.ReactElement | null;
};

const MockReactRouter6Adapter = ({
  children,
}: MockReactRouter6AdapterProps): React.ReactElement | null => {
  const adapter: Adapter = {
    // eslint-disable-next-line @typescript-eslint/no-unused-vars
    replace(location2: Location) {
      // Does nothing
    },
    // eslint-disable-next-line @typescript-eslint/no-unused-vars
    push(location2: Location) {
      // Does nothing
    },
    get location() {
      return { search: '' };
    },
  };

  return <>{children(adapter)}</>;
};

export { MockReactRouter6Adapter };

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment