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

@pie-lib/test-utils

v2.0.2

Published

Testing utilities for pie-lib packages with React Testing Library and MUI support

Downloads

4,328

Readme

@pie-lib/test-utils

Testing utilities for pie-lib packages with React Testing Library and MUI support.

Installation

This package is typically used as a dev dependency:

yarn add -D @pie-lib/test-utils

Features

  • ✅ React Testing Library utilities
  • ✅ MUI Theme Provider wrapper for tests
  • ✅ Multiple provider composition
  • ✅ Custom theme creation for testing
  • ✅ All RTL exports in one place
  • ✅ userEvent and jest-dom matchers included
  • ✅ Keyboard helpers for keyCode-based components
  • ✅ Web component testing utilities (light DOM only)

Usage

Basic Rendering with MUI Theme

import { renderWithTheme, screen } from '@pie-lib/test-utils';
import { Button } from '@mui/material';

test('renders a button', () => {
  renderWithTheme(<Button>Click me</Button>);
  expect(screen.getByRole('button')).toBeInTheDocument();
});

Custom Theme

import { renderWithTheme, createTestTheme } from '@pie-lib/test-utils';

test('renders with dark theme', () => {
  const darkTheme = createTestTheme({
    palette: { mode: 'dark' }
  });

  renderWithTheme(<MyComponent />, { theme: darkTheme });
  // ... assertions
});

Multiple Providers

import { renderWithProviders } from '@pie-lib/test-utils';

test('renders with multiple providers', () => {
  const ReduxProvider = ({ children }) => (
    <Provider store={store}>{children}</Provider>
  );

  renderWithProviders(<MyComponent />, {
    providers: [ReduxProvider]
  });
  // ... assertions
});

User Interactions

import { renderWithTheme, screen, userEvent } from '@pie-lib/test-utils';

test('handles button click', async () => {
  const onClick = jest.fn();
  const user = userEvent.setup();

  renderWithTheme(<Button onClick={onClick}>Click</Button>);

  await user.click(screen.getByRole('button'));
  expect(onClick).toHaveBeenCalledTimes(1);
});

Keyboard Interactions

NEW: Helper functions for testing keyboard events, especially useful for legacy components checking event.keyCode:

import { render, screen, pressKey, Keys, typeAndSubmit } from '@pie-lib/test-utils';

test('submits on Enter key', async () => {
  const onSubmit = jest.fn();
  render(<SearchInput onSubmit={onSubmit} />);

  const input = screen.getByRole('textbox');
  await typeAndSubmit(input, 'search term');

  expect(onSubmit).toHaveBeenCalledWith('search term');
});

test('handles Escape key', () => {
  const onCancel = jest.fn();
  render(<Modal onCancel={onCancel} />);

  const modal = screen.getByRole('dialog');
  pressKey(modal, Keys.ESCAPE);

  expect(onCancel).toHaveBeenCalled();
});

Async Operations

import { renderWithTheme, screen, waitFor } from '@pie-lib/test-utils';

test('loads data', async () => {
  renderWithTheme(<AsyncComponent />);

  // Wait for element to appear
  const element = await screen.findByText('Loaded');
  expect(element).toBeInTheDocument();

  // Or use waitFor
  await waitFor(() => {
    expect(screen.getByText('Loaded')).toBeInTheDocument();
  });
});

Web Component Testing

Note: Utilities for testing custom elements with light DOM (no Shadow DOM). Standard React Testing Library queries work directly on these components.

import { renderWebComponent, dispatchCustomEvent, screen } from '@pie-lib/test-utils';

test('interacts with web component', async () => {
  const element = await renderWebComponent('my-custom-element', {
    'data-testid': 'my-element',
    value: '42'
  });

  // Query using standard RTL queries (light DOM)
  const button = screen.getByRole('button');
  await user.click(button);

  // Dispatch custom events
  dispatchCustomEvent(element, 'value-changed', { newValue: '100' });
});

API Reference

React Component Rendering

renderWithTheme(ui, options)

Renders a component wrapped with MUI ThemeProvider.

Parameters:

  • ui - React component to render
  • options.theme - Custom MUI theme (optional, defaults to Material-UI default)
  • options... - Additional options passed to RTL's render()

Returns: RTL render result

renderWithProviders(ui, options)

Renders a component with MUI ThemeProvider and additional custom providers.

Parameters:

  • ui - React component to render
  • options.theme - Custom MUI theme (optional)
  • options.providers - Array of provider components to wrap (optional)
  • options... - Additional options passed to RTL's render()

Returns: RTL render result

createTestTheme(themeOptions)

Creates a MUI theme for testing.

Parameters:

  • themeOptions - MUI theme configuration object

Returns: MUI theme object

waitForRemoval(callback)

Alias for waitForElementToBeRemoved from RTL.

Re-exports

This package re-exports everything from:

  • @testing-library/react (render, screen, waitFor, etc.)
  • @testing-library/user-event (as userEvent)
  • @testing-library/jest-dom (for TypeScript support)

Keyboard Helpers API

Keys

Constants for common keyboard key codes:

import { Keys } from '@pie-lib/test-utils';

Keys.ENTER      // 13
Keys.ESCAPE     // 27
Keys.SPACE      // 32
Keys.TAB        // 9
Keys.ARROW_DOWN // 40
Keys.ARROW_UP   // 38
// ... and more

pressKey(element, keyCode, type, options)

Simulate keyboard event with keyCode (useful for legacy components):

import { pressKey, Keys } from '@pie-lib/test-utils';

// Press Enter
pressKey(input, Keys.ENTER);

// Press Escape with keyup event
pressKey(dialog, Keys.ESCAPE, 'keyup');

// Press Ctrl+Enter
pressKey(input, Keys.ENTER, 'keydown', { ctrlKey: true });

Why? userEvent.type() with {Enter} doesn't work well with components checking event.keyCode.

typeAndSubmit(element, text)

Type text and press Enter (common pattern):

import { typeAndSubmit } from '@pie-lib/test-utils';

await typeAndSubmit(input, 'search query');
// Types "search query" and presses Enter

clearAndType(element, text)

Clear input and type new text:

import { clearAndType } from '@pie-lib/test-utils';

await clearAndType(input, 'new value');

navigateWithKeys(element, steps, direction)

Navigate with arrow keys:

import { navigateWithKeys } from '@pie-lib/test-utils';

// Navigate down 3 items
navigateWithKeys(listbox, 3, 'vertical');

// Navigate left 2 items
navigateWithKeys(tabs, -2, 'horizontal');

Web Component Helpers API

Note: These utilities are for light DOM web components only (no Shadow DOM). For Shadow DOM components, use standard DOM queries or other testing libraries.

waitForCustomElement(tagName, timeout)

Wait for custom element to be defined:

import { waitForCustomElement } from '@pie-lib/test-utils';

await waitForCustomElement('my-component');
await waitForCustomElement('pie-chart', 5000); // 5 second timeout

renderWebComponent(tagName, attributes, properties, container)

Render web component and wait for it to be ready:

import { renderWebComponent } from '@pie-lib/test-utils';

const element = await renderWebComponent('pie-chart',
  { type: 'bar', 'data-testid': 'chart' },  // attributes
  { onDataChange: jest.fn() }               // properties
);

dispatchCustomEvent(element, eventName, detail, options)

Dispatch custom event on element:

import { dispatchCustomEvent } from '@pie-lib/test-utils';

dispatchCustomEvent(chart, 'data-changed', { value: [1, 2, 3] });

waitForEvent(element, eventName, timeout)

Wait for custom event to be fired:

import { waitForEvent } from '@pie-lib/test-utils';

const promise = waitForEvent(component, 'ready');
component.initialize();
const event = await promise;
expect(event.detail).toEqual({ initialized: true });

isCustomElementDefined(tagName)

Check if a custom element is defined:

import { isCustomElementDefined } from '@pie-lib/test-utils';

if (isCustomElementDefined('pie-chart')) {
  // Element is ready to use
}

createCustomElement(tagName, props)

Create and configure a custom element:

import { createCustomElement } from '@pie-lib/test-utils';

const chart = createCustomElement('pie-chart', {
  data: [1, 2, 3],
  type: 'bar'
});
document.body.appendChild(chart);

Migration from Enzyme

Before (Enzyme)

import { shallow } from 'enzyme';

const wrapper = shallow(<Component />);
expect(wrapper.find('button')).toHaveLength(1);
wrapper.find('button').simulate('click');

After (React Testing Library)

import { renderWithTheme, screen, userEvent } from '@pie-lib/test-utils';

renderWithTheme(<Component />);
const button = screen.getByRole('button');
expect(button).toBeInTheDocument();
await userEvent.click(button);

Query Priority

Use queries in this order (best to worst):

  1. getByRole - Best for accessibility
  2. getByLabelText - Good for form fields
  3. getByPlaceholderText - Forms when label not available
  4. getByText - Non-interactive content
  5. getByDisplayValue - Current form values
  6. getByAltText - Images
  7. getByTitle - When other options don't work
  8. getByTestId - Last resort

Common Patterns

Testing Forms

import { renderWithTheme, screen, userEvent } from '@pie-lib/test-utils';

test('submits form', async () => {
  const onSubmit = jest.fn();
  const user = userEvent.setup();

  renderWithTheme(<LoginForm onSubmit={onSubmit} />);

  await user.type(screen.getByLabelText('Username'), 'user123');
  await user.type(screen.getByLabelText('Password'), 'pass123');
  await user.click(screen.getByRole('button', { name: /submit/i }));

  expect(onSubmit).toHaveBeenCalledWith({
    username: 'user123',
    password: 'pass123'
  });
});

Testing Conditional Rendering

test('shows error message', async () => {
  renderWithTheme(<Component />);

  // Initially not present
  expect(screen.queryByText('Error')).not.toBeInTheDocument();

  // Trigger error
  await userEvent.click(screen.getByRole('button'));

  // Now appears
  expect(await screen.findByText('Error occurred')).toBeInTheDocument();
});

Testing with Custom Props

const defaultProps = {
  title: 'Test',
  onChange: jest.fn()
};

test('renders with defaults', () => {
  renderWithTheme(<Component {...defaultProps} />);
  expect(screen.getByText('Test')).toBeInTheDocument();
});

test('renders with overrides', () => {
  renderWithTheme(<Component {...defaultProps} title="Override" />);
  expect(screen.getByText('Override')).toBeInTheDocument();
});

Tips

  1. Use screen queries - More readable than destructuring from render
  2. Prefer findBy for async - Built-in waiting
  3. Use queryBy for non-existence - Won't throw if not found
  4. Test behavior, not implementation - Don't test internal state
  5. Use userEvent over fireEvent - More realistic interactions

Learn More