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

@sigx/lynx-testing

v0.28.0

Published

Testing utilities for SignalX Lynx components - render, fireEvent, queries

Readme

@sigx/lynx-testing

Vitest-native testing utilities for sigx-lynx components — render, fire events, query the rendered tree, and wait for reactive updates.

Renders into an in-memory TestNode tree (no Lynx runtime, no PAPI mocks for the BG side). Pair with vitest in your project — no Jest, no preset.

📚 Documentation

Full API, vitest config, and the MT-worklet test harness → sigx.dev/lynx/modules/testing/overview

Install

pnpm add -D @sigx/lynx-testing vitest

A taste

import { it, expect } from 'vitest';
import { render, fireEvent, act } from '@sigx/lynx-testing';
import { component, signal, jsx } from '@sigx/lynx';

it('updates on tap', async () => {
  const count = signal({ value: 0 });
  const Counter = component(() => () => jsx('view', {
    bindtap: () => { count.value++; },
    children: [jsx('text', { children: String(count.value) })],
  }));

  const { getByType, getByText } = render(jsx(Counter, {}));
  expect(getByText('0')).toBeTruthy();

  await act(() => fireEvent.tap(getByType('view')));
  expect(getByText('1')).toBeTruthy();
});

render() mounts the BG side of a component and covers JSX shape + signal-driven re-renders. For end-to-end main-thread coverage, the @sigx/lynx-testing/mt subpath boots the worklet runtime, runs the real 'main thread' transform, and hands back the registered worklets to drive. The full query/fireEvent/act API, vitest config, and the MT harness are documented on the docs site.

API

Rendering

| | | |---|---| | render(vnode) | Mount into an in-memory TestNode tree. Returns { container, unmount } | | TestNode | The node type — type, props, children, text |

Waiting

| | | |---|---| | await act(fn) | Run fn, then advance one turn | | await waitForUpdate() | Advance one turn | | await waitFor(condition, opts?) | Poll until condition is truthy; returns its value |

Pick waitFor whenever the number of turns isn't knowable — a dynamic import, chained effects, a deferred callback. act/waitForUpdate advance a fixed amount, so waiting on those with a loop or a setTimeout is a guess that holds until the machine is busy. Two tests in @sigx/lynx-emoji were written that way and both failed on CI.

// ✗ guesses — fails once mounting takes a sixth turn
for (let i = 0; i < 5; i++) await act(() => {});
expect(getByType(container, 'tabbar')).toBeTruthy();

// ✓ waits for the thing you actually care about
await findByType(container, 'tabbar');

waitFor options: timeout (default 1000ms), interval (default 0), description (named in the timeout message). A condition that throws counts as "not yet", and on timeout the condition's own error is rethrown — so you get "no element found with text …" plus the tree, not a bare timeout.

Queries

Three flavours per matcher, following @testing-library:

| | Type | Text | Prop | |---|---|---|---| | throws if missing | getByType | getByText | getByProp | | null if missing | queryByType | queryByText | queryByProp | | waits for it | findByType | findByText | findByProp |

Plus getAllByType(container, type), within(node) to scope queries to a subtree, and formatTree(node) to print a tree. A failing getBy*/findBy* prints the tree it searched.

Events

fireEvent(node, name, detail?) and touch(node, points).

Main thread

@sigx/lynx-testing/mt compiles and runs worklets; add @sigx/lynx-testing/mt/setup to your vitest setupFiles.

Directives

The test renderer runs the same use:* directive lifecycle as the real runtime, so components that use directives behave under test like they do on device. use:show toggles a node's effective _style.display while keeping it mounted — assert with the isVisible getter:

import { render, waitForUpdate } from '@sigx/lynx-testing';
import { component, signal, jsx } from '@sigx/lynx';

const shown = signal(true);
const App = component(() => () =>
  jsx('view', { 'use:show': shown.value, children: [jsx('text', { children: 'hi' })] }));

const { getByType } = render(jsx(App, {}));
const view = getByType('view');
expect(view.isVisible).toBe(true);

shown.value = false;
await waitForUpdate();
expect(view.isVisible).toBe(false);          // same node — hidden, not remounted
expect(view._style.display).toBe('none');

Custom directives' created / mounted / updated / unmounted hooks fire too (pass the directive inline as use:name={[dir, value]}, or register it with registerBuiltInDirective / app.directive).

License

MIT