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

@coherent.js/testing

v1.0.0-beta.5

Published

Testing utilities for Coherent.js applications

Readme

@coherent.js/testing

Complete testing utilities for Coherent.js applications.

Installation

npm install --save-dev @coherent.js/testing

Features

  • Test Renderer - Render components in test environment
  • Query Utilities - Find elements by testId, text, className
  • Event Simulation - Simulate user interactions
  • Async Testing - Wait for conditions and elements
  • Mock Functions - Create mocks and spies
  • Custom Matchers - Coherent.js-specific assertions
  • Snapshot Testing - Component snapshot support
  • User Events - Realistic user interaction simulation

Quick Start

import { describe, it, expect } from 'vitest';
import { renderComponent, extendExpect } from '@coherent.js/testing';

// Extend expect with custom matchers
extendExpect(expect);

describe('MyComponent', () => {
  it('should render correctly', () => {
    const component = {
      div: {
        'data-testid': 'my-div',
        text: 'Hello World'
      }
    };

    const { getByTestId } = renderComponent(component);
    
    expect(getByTestId('my-div')).toHaveText('Hello World');
  });
});

API Reference

Rendering

renderComponent(component, options)

Render a component for testing.

const { getByTestId, getByText } = renderComponent({
  div: { 'data-testid': 'test', text: 'Hello' }
});

renderComponentAsync(component, props, options)

Render an async component.

const result = await renderComponentAsync(asyncComponent, { name: 'World' });

createTestRenderer(component, options)

Create a test renderer for component updates.

const renderer = createTestRenderer(component);
renderer.render();
renderer.update(newComponent);

shallowRender(component)

Shallow render (top-level only).

const shallow = shallowRender(component);

Queries

getByTestId(testId)

Get element by test ID (throws if not found).

const element = getByTestId('submit-btn');

queryByTestId(testId)

Query element by test ID (returns null if not found).

const element = queryByTestId('submit-btn');

getByText(text)

Get element by text content.

const element = getByText('Submit');

getByClassName(className)

Get element by class name.

const element = getByClassName('btn-primary');

getAllByTagName(tagName)

Get all elements by tag name.

const items = getAllByTagName('li');

Events

fireEvent(element, eventType, eventData)

Simulate an event.

fireEvent(button, 'click');
fireEvent(input, 'change', { target: { value: 'test' } });

userEvent

Realistic user interactions.

await userEvent.click(button);
await userEvent.type(input, 'Hello');
await userEvent.clear(input);

Async Utilities

waitFor(condition, options)

Wait for a condition to be true.

await waitFor(() => getByText('Loaded').exists, { timeout: 2000 });

waitForElement(queryFn, options)

Wait for an element to appear.

const element = await waitForElement(() => queryByTestId('loaded'));

waitForElementToBeRemoved(queryFn, options)

Wait for an element to disappear.

await waitForElementToBeRemoved(() => queryByTestId('loading'));

act(callback)

Batch updates.

await act(async () => {
  // Perform updates
});

Mocks & Spies

createMock(implementation)

Create a mock function.

const mock = createMock((x) => x * 2);
mock(5); // returns 10

expect(mock).toHaveBeenCalledWith(5);
expect(mock).toHaveBeenCalledTimes(1);

createSpy(object, method)

Spy on an object method.

const spy = createSpy(obj, 'method');
obj.method('test');
expect(spy).toHaveBeenCalledWith('test');
spy.mockRestore();

Custom Matchers

Extend expect with Coherent.js-specific matchers:

import { extendExpect } from '@coherent.js/testing';
extendExpect(expect);

Available matchers:

  • toHaveText(text) - Element has exact text
  • toContainText(text) - Element contains text
  • toHaveClass(className) - Element has class
  • toBeInTheDocument() - Element exists
  • toBeVisible() - Element has visible content
  • toBeEmpty() - Element is empty
  • toContainHTML(html) - HTML contains string
  • toHaveAttribute(attr, value) - Element has attribute
  • toHaveTagName(tagName) - Element has tag name
  • toRenderSuccessfully() - Component rendered
  • toBeValidHTML() - HTML is valid
  • toHaveBeenCalled() - Mock was called
  • toHaveBeenCalledWith(...args) - Mock called with args
  • toHaveBeenCalledTimes(n) - Mock called n times

Utilities

within(container)

Scope queries to a container.

const container = getByTestId('container');
const scoped = within(container);
scoped.getByText('Inner text');

screen

Global query utility.

screen.setResult(result);
screen.getByTestId('test');
screen.debug();

cleanup()

Clean up after tests.

afterEach(() => {
  cleanup();
});

Examples

Testing a Button Component

import { renderComponent, fireEvent, createMock } from '@coherent.js/testing';

it('should handle click events', () => {
  const handleClick = createMock();
  
  const button = {
    button: {
      'data-testid': 'my-btn',
      text: 'Click me',
      onclick: handleClick
    }
  };

  const { getByTestId } = renderComponent(button);
  fireEvent(getByTestId('my-btn'), 'click');

  expect(handleClick).toHaveBeenCalled();
});

Testing Async Components

import { renderComponentAsync, waitForElement } from '@coherent.js/testing';

it('should load data', async () => {
  const AsyncComponent = async () => {
    const data = await fetchData();
    return { div: { text: data.message } };
  };

  const result = await renderComponentAsync(AsyncComponent);
  const element = await waitForElement(() => result.queryByText('Loaded'));

  expect(element).toBeInTheDocument();
});

Testing Forms

import { renderComponent, userEvent } from '@coherent.js/testing';

it('should submit form', async () => {
  const handleSubmit = createMock();
  
  const form = {
    form: {
      onsubmit: handleSubmit,
      children: [
        { input: { 'data-testid': 'name-input', type: 'text' } },
        { button: { 'data-testid': 'submit-btn', text: 'Submit' } }
      ]
    }
  };

  const { getByTestId } = renderComponent(form);
  
  await userEvent.type(getByTestId('name-input'), 'John');
  await userEvent.click(getByTestId('submit-btn'));

  expect(handleSubmit).toHaveBeenCalled();
});

Snapshot Testing

import { renderComponent } from '@coherent.js/testing';

it('should match snapshot', () => {
  const component = {
    div: {
      className: 'card',
      children: [
        { h2: { text: 'Title' } },
        { p: { text: 'Content' } }
      ]
    }
  };

  const result = renderComponent(component);
  expect(result.toSnapshot()).toMatchSnapshot();
});

Best Practices

1. Use Test IDs

Add data-testid attributes for reliable querying:

const component = {
  button: {
    'data-testid': 'submit-button',
    text: 'Submit'
  }
};

2. Clean Up After Tests

Always clean up to avoid test interference:

afterEach(() => {
  cleanup();
});

3. Use Custom Matchers

Extend expect for better assertions:

extendExpect(expect);
expect(element).toHaveText('Hello');

4. Test User Interactions

Use userEvent for realistic interactions:

await userEvent.click(button);
await userEvent.type(input, 'text');

5. Wait for Async Updates

Use waitFor for async operations:

await waitFor(() => getByText('Loaded').exists);

Integration with Testing Frameworks

Vitest

import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import { renderComponent, extendExpect, cleanup } from '@coherent.js/testing';

extendExpect(expect);

describe('MyComponent', () => {
  afterEach(cleanup);

  it('should work', () => {
    // Test code
  });
});

Jest

import { renderComponent, extendExpect, cleanup } from '@coherent.js/testing';

extendExpect(expect);

afterEach(cleanup);

test('should work', () => {
  // Test code
});

License

MIT