@triggery/testing
Testing utilities for Triggery. Zero runtime dependencies, framework-agnostic — works the same in React, Solid, Vue tests, Node, or a worker; compatible with Vitest, Jest, and node:test.
The same trigger code you ship runs in tests without React, JSDOM, or a host framework. You test scenarios, not components.
Install
Section titled “Install”pnpm add -D @triggery/core @triggery/testing npm install --save-dev @triggery/core @triggery/testing yarn add --save-dev @triggery/core @triggery/testing bun add -D @triggery/core @triggery/testing Peer deps: @triggery/core. Test-runner agnostic — no dependency on vi.useFakeTimers() / jest.useFakeTimers().
What’s inside
Section titled “What’s inside”| Export | Purpose |
|---|---|
createTestRuntime({ triggers? }) | Isolated runtime per test. No global state pollution. |
mockCondition(trigger, name, value | getter) | Supply a condition without rendering a component. |
mockAction(trigger, name, fn) | Register an action handler — typically vi.fn() / jest.fn(). |
flushMicrotasks() | Drain the default microtask scheduler before asserting. |
createFakeScheduler() | Controllable virtual clock for actions.debounce / throttle / defer. |
createFakeScheduler() exposes:
install()/uninstall()swapglobalThis.setTimeout/clearTimeoutfor a controlled implementation.advance(ms)runs every timer due within the window and drains microtasks.flushAll()runs every pending timer regardless of scheduled time.
Quick example
Section titled “Quick example”A trigger that gates on a user.isMod condition and fires two actions:
message.test.ts
import { createTrigger } from '@triggery/core';
import { createTestRuntime } from '@triggery/testing';
import { expect, test, vi } from 'vitest';
test('mod with notifications shows toast and plays sound', () => {
const rt = createTestRuntime();
const t = createTrigger<{
events: { 'new-message': string };
conditions: { user: { isMod: boolean } };
actions: { showToast: string; playSound: 'beep' | 'mod-alert' };
}>(
{
id: 'msg',
events: ['new-message'],
required: ['user'],
handler: ({ event, conditions, actions, check }) => {
if (!check.is('user', (u) => u.isMod)) return;
actions.showToast?.(event.payload);
actions.playSound?.('mod-alert');
},
},
rt,
);
rt.mockCondition(t, 'user', { isMod: true });
const showToast = vi.fn();
const playSound = vi.fn();
rt.mockAction(t, 'showToast', showToast);
rt.mockAction(t, 'playSound', playSound);
rt.fireSync('new-message', 'hi');
expect(showToast).toHaveBeenCalledWith('hi');
expect(playSound).toHaveBeenCalledWith('mod-alert');
});No DOM, no provider, no act(). The trigger is exercised directly.
Fake scheduler — testing debounce
Section titled “Fake scheduler — testing debounce”import { createTestRuntime, createFakeScheduler } from '@triggery/testing';
import { afterEach, beforeEach, expect, test, vi } from 'vitest';
let clock: ReturnType<typeof createFakeScheduler>;
beforeEach(() => { clock = createFakeScheduler(); clock.install(); });
afterEach(() => { clock.uninstall(); });
test('actions.debounce(200) collapses bursts', () => {
const rt = createTestRuntime();
const search = vi.fn();
rt.mockAction(searchTrigger, 'search', search);
rt.fireSync('input', 'a');
rt.fireSync('input', 'ab');
rt.fireSync('input', 'abc');
clock.advance(199);
expect(search).not.toHaveBeenCalled();
clock.advance(1); // total = 200 ms
expect(search).toHaveBeenCalledTimes(1);
expect(search).toHaveBeenCalledWith('abc');
});Async handlers — take-latest
Section titled “Async handlers — take-latest”test('take-latest cancels stale runs', async () => {
const rt = createTestRuntime();
rt.fire('search', 'a'); // gets cancelled
rt.fire('search', 'ab'); // gets cancelled
await rt.fire('search', 'abc'); // wins
expect(rt.inspect()?.outcome).toBe('completed');
});Related packages
Section titled “Related packages” @triggery/core Same runtime, just instantiated via createTestRuntime.
@triggery/react Component-level testing with the React testing library on top.
@triggery/solid Solid testing via @solidjs/testing-library.
@triggery/vue Vue testing via @vue/test-utils.
@triggery/eslint-plugin Static checks that catch issues tests can miss.