npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@suerg/test-kit

v0.1.33

Published

Typed, batteries-included helpers for building stable, readable integration tests with React Testing Library on both React (web) and React Native.

Readme

@suerg/test-kit

Typed, batteries-included helpers for building stable, readable integration tests with React Testing Library on both React (web) and React Native.

  • Purpose: provide a small, strongly-typed “kit” you compose once per test that bundles common test ergonomics: Redux state setup, API mocking, interactions, router control, time control, and more.
  • Design: plugin-based. Add only what you need; bring your own app providers via global setup.

Default plugins included when you create a kit:

  • flow, api, interactions, keyboard, date, performance, dnd, page (web)
  • flow, api, interactions, date, performance (native)
  • state (web and native; requires a Redux store factory via global setup)
  • router is available via routerPlugin(...) when a router environment is configured

Install

React (web):

npm i -D @suerg/test-kit @testing-library/react @testing-library/user-event @reduxjs/toolkit react-redux react-dom
# Optional but common in web apps
npm i -D @testing-library/jest-dom

React Native:

npm i -D @suerg/test-kit @testing-library/react-native @reduxjs/toolkit react-redux react-dom

If you intend to use the router plugin on web with Next.js, also ensure a single router instance is available to tests (e.g., next-router-mock or Next’s useRouter() module instance).

Global setup (one-time)

Provide your Redux store factory and any app-wide providers. Optionally provide a router getter.

React (example):

// jest.setup.ts
/* eslint-disable */
import React from 'react';
import { setupTestKit, NextRouterLike } from '@suerg/test-kit';
import { configureStore } from '@reduxjs/toolkit';
import { ThemeProvider } from '@mui/material/styles';
import rootReducer from '@/redux/rootReducer';
import { TestQueryClientProvider } from './__tests__/helpers/testQueryClientWrapper';
import theme from './theme';

setupTestKit({
    makeStore: (preloaded) =>
        configureStore({ reducer: rootReducer, preloadedState: preloaded }),
    contextProviders: [
        ({ children }) =>
            React.createElement(ThemeProvider, { theme }, children),
        ({ children }) =>
            React.createElement(TestQueryClientProvider, null, children),
    ],
    router: {
        getRouter: (): NextRouterLike | undefined => {
            try {
                return require('next/router').default as NextRouterLike;
            } catch {
                return undefined;
            }
        },
    },
});

React Native (example):

// jest.setup.(ts|js)
/* eslint-disable */
import React from 'react';
import { setupTestKit } from '@suerg/test-kit';
import { Provider as PaperProvider } from 'react-native-paper';
import {
    SafeAreaProvider,
    initialWindowMetrics,
} from 'react-native-safe-area-context';
import { makeStore } from '@/src/store/store';
import { theme } from '@/src/constants/theme';

setupTestKit({
    makeStore: (preloaded) => makeStore(preloaded),
    contextProviders: [
        ({ children }) =>
            React.createElement(PaperProvider, { theme }, children),
        ({ children }) =>
            React.createElement(
                SafeAreaProvider,
                { initialMetrics: initialWindowMetrics },
                children
            ),
    ],
});

Notes

  • The statePlugin() uses your makeStore(preloaded) and wraps the UI under react-redux’s Provider plus any contextProviders you pass above.
  • For web Next.js routing, routerPlugin({ type: 'next' }) requires setupTestKit({ router: { getRouter } }) to return the single live router instance used by tests and app.

React Native router setup (React Navigation):

// jest.setup.(ts|js)
/* eslint-disable */
import { setupTestKit } from '@suerg/test-kit';
import { createNavigationContainerRef } from '@react-navigation/native';

export const navigationRef = createNavigationContainerRef();

setupTestKit({
    makeStore: (preloaded) => makeStore(preloaded),
    router: {
        getRouter: () => navigationRef.current,
    },
});

In tests, render your navigator with the same ref and use the router plugin:

import { NavigationContainer } from '@react-navigation/native';
import { navigationRef } from '../jest.setup';

const kit = createKitNative(routerPlugin({ type: 'react-navigation' }));
render(
    kit.state.renderWithState(
        <NavigationContainer ref={navigationRef}>
            <AppNavigator />
        </NavigationContainer>
    )
);

await kit.router.navigate('Details', { id: '1' });

Creating a kit

Web:

import {
    createKit,
    makeKitBuilder,
    statePlugin,
    routerPlugin,
    pagePlugin,
} from '@suerg/test-kit';

// One-off kit
const kit = createKit(statePlugin(), routerPlugin({ type: 'next' }));

// Reusable builder with defaults
export const makeAppKit = makeKitBuilder(
    statePlugin(),
    routerPlugin({ type: 'next' })
);
const appKit = makeAppKit(
    pagePlugin(({ screen, user }) => ({
        clickSave: () => user.click(screen.getByText('Save')),
    }))
);

React Native:

import { createKitNative, statePlugin } from '@suerg/test-kit';

const kit = createKitNative(statePlugin());

Rendering and interacting (integration test examples)

Web (Next.js style):

import { render, screen, waitFor } from '@testing-library/react';
import { makeKitBuilder, statePlugin, routerPlugin } from '@suerg/test-kit';
import { MonthView } from '@/features/calendar/components/views/MonthView';

const makeMonthKit = makeKitBuilder(statePlugin(), routerPlugin({ type: 'next' }));
const kit = makeMonthKit();

render(kit.state.renderWithState(<MonthView locationId="1" addShiftMode={false} addBlockMode={false} />));

await kit.flow.act(async (user) => {
  await user.click(await screen.findByRole('gridcell', { name: /January 15, 2024/i }));
});

await waitFor(() => expect(kit.router.getLocation().path).toContain('date=2024-01-15'));

React Native (React Navigation style):

import React from 'react';
import { render, screen, waitFor } from '@testing-library/react-native';
import { createKitNative, statePlugin } from '@suerg/test-kit';
import RootStackNavigator from '@/src/navigation/RootStackNavigator';

const kit = createKitNative(statePlugin());
render(kit.state.renderWithState(<RootStackNavigator />));

await kit.flow.act(async (user) => {
  await user.press(await screen.findByText('Save'));
});

await waitFor(() => expect(screen.getByText('Login')).toBeDefined());

If your component relies on selector-driven effects and you need to rerender while preserving the same Redux store, use the stable-store helpers:

import React from 'react';
import { render, act } from '@testing-library/react-native';
import { createKitNative, statePlugin } from '@suerg/test-kit';

const kit = createKitNative(statePlugin());
const ui = <RootStackNavigator />;
const { rerender } = render(kit.state.renderWithStableStore(ui));

// flip a flag in state and rerender without recreating the store
await act(async () => {
    kit.state.rerenderWithStableStore(rerender, ui);
});

API Plugin (HTTP mocking)

Available as part of defaults (web and native):

  • onGet/onPost/onPut/onPatch/onDelete(path, body, status = 200, repeat = 1)
  • onGetHang(path)
  • chaos: rateLimit(path), serverError(path), timeout(path), networkError(path)
  • getCalls(method?, path?) -> ApiCallRecord[]
  • expectCalledTimes(method, path, times)
  • expectAbortedTimes(method, path, times)
  • waitForIdle(), expectNoPending(), clear()
  • getAbortedCalls()

Debug logging

  • Pass a boolean to enable logs programmatically: apiPlugin({ debug: true }).
  • Or set the environment variable to a boolean string: TEST_KIT_API_DEBUG=true.

Example:

cross-env TEST_KIT_API_DEBUG=true npm test

Example:

kit.api.onGet('/api/users', { users: [] }, 200);
await fetch('/api/users');
await kit.api.expectCalledTimes('GET', '/api/users', 1);
await kit.api.expectNoPending();

PATCH example:

kit.api.onPatch('/api/items/1', { ok: true }, 200);
await fetch('/api/items/1', { method: 'PATCH' });
await kit.api.expectCalledTimes('PATCH', '/api/items/1', 1);

State Plugin (Redux)

Helpers:

  • store(): returns the configured store instance created with current presets/patch
  • use(preset): register a function that derives a state patch from the initial state
  • withPatch(patch): merge-in static preloaded state
  • withProviders(providers): add more wrapper providers for this kit
  • renderWithState(ui): wraps ui with Provider and extra providers
  • renderWithStableStore(ui): like renderWithState, but keeps a single persistent store across rerenders
  • rerenderWithStableStore(rerender, ui): convenience to rerender with the same persistent store
  • stubState(path, value) or stubState({ nested: patches })

Example:

const kit = createKit(statePlugin());
kit.state.stubState('auth.user.id', '1');
render(kit.state.renderWithState(<App />));

Flow Plugin

  • act(async (user) => { /* interactions */ }): wraps in RTL act() and flushes microtasks

Interactions Plugins

Web (interactionsPlugin):

  • clickCell(label)
  • clickButton(label)
  • clickByText(text)
  • clickByTestId(testId)
  • typeText(labelOrTestId, text)
  • selectViaKb(label)
  • hoverElement(label)
  • hoverText(text)
  • clearSelections()
  • expectSelected(label)
  • expectNotSelected(label)

React Native (interactionsNativePlugin):

  • tapByText(text)
  • tapByTestId(testId)
  • typeText(testIdOrLabel, text)
  • longPressByText(text)
  • longPressByTestId(testId)

Keyboard Plugin (web)

  • keyboard(seq: string): delegates to userEvent.keyboard
await kit.keyboard('{Tab}{Enter}');

DnD Plugin (web)

  • drop(element, data?): fires dragEnter/dragOver/drop with simple dataTransfer

Performance Plugin

  • shouldCompleteWithin(ms)
  • shouldRenderWithin(ms)
  • shouldUpdateWithin(ms)
  • shouldInteractWithin(ms)
  • run(testFn)
kit.performance.shouldRenderWithin(25);
await kit.performance.run(async () => {
  render(kit.state.renderWithState(<App />));
});

Date Plugin

Freezes Date to a fixed moment without switching to fake timers. Useful when you prefer real timers but deterministic time.

import { datePlugin } from '@suerg/test-kit';
const kit = createKit(datePlugin(new Date('2024-02-01T00:00:00Z')));

Router Plugin

Use with a configured environment via setupTestKit({ router: { getRouter } }).

Web (Next.js):

import { routerPlugin } from '@suerg/test-kit';
const kit = createKit(routerPlugin({ type: 'next' }));
kit.router.getLocation();
await kit.router.navigate({ pathname: '/users', query: { q: 'joe' } });

React Native (React Navigation):

Two options:

  1. Use a configured router environment (preferred):
import { routerPlugin } from '@suerg/test-kit';
const kit = createKitNative(routerPlugin({ type: 'react-navigation' }));
await kit.router.navigate('Details', { id: '1' });
  1. Or pass an explicit adapter built from a NavigationContainer ref:
import { routerPlugin, createReactNavigationAdapter } from '@suerg/test-kit';
const adapter = createReactNavigationAdapter(navigationRef.current);
const kit = createKitNative(routerPlugin(adapter));
await kit.router.navigate('Details', { id: '1' });

Typing your Redux RootState (recommended)

Augment the module so helpers are typed against your real RootState.

// types/test-kit.d.ts
import type rootReducer from '@/redux/rootReducer';
export type RootState = ReturnType<typeof rootReducer>;
declare module '@suerg/test-kit' {
    interface TestKitReduxState extends RootState {}
}

Writing integration tests

  • Use statePlugin().renderWithState(ui) to render your real component under real providers.
  • Drive user behavior via kit.flow.act and interactions helpers instead of manual act() and timers.
  • Mock APIs with kit.api instead of ad-hoc jest mocks; assert calls with expectCalledTimes.
  • For router-aware components, prefer routerPlugin and assert via kit.router.getLocation().