Testing
Weblisk ships testing utilities that work with any test runner (Node.js test runner, Vitest, Jest, etc.).
Setup
js
import { createSignalTest, renderIsland, simulateEvent, waitFor, assert, assertEqual, } from 'weblisk/test/utils.js';
Testing Signals
Create an isolated signal environment with change tracking:
js
import { createSignalTest } from 'weblisk/test/utils.js'; const { signal, computed, effect, trackedSignal, flush } = createSignalTest(); // trackedSignal tracks change history const [count, setCount] = trackedSignal(0); setCount(1); setCount(2); assertEqual(count(), 2); assertEqual(count.changes, [0, 1, 2]); // Full history
Testing Computed Values
js
const { signal, computed } = createSignalTest(); const [price] = signal(100); const [qty] = signal(3); const total = computed(() => price() * qty()); assertEqual(total(), 300);
Testing Effects
js
const { signal, effect, flush } = createSignalTest(); const [name, setName] = signal('Alice'); let lastSeen = ''; effect(() => { lastSeen = name(); }); flush(); // Ensure effects run assertEqual(lastSeen, 'Alice'); setName('Bob'); flush(); assertEqual(lastSeen, 'Bob');
Testing Islands
Render an island in a detached DOM element:
js
import { renderIsland, simulateEvent } from 'weblisk/test/utils.js'; const { el, $, $$, cleanup } = renderIsland( '<button>Count: 0</button>', (el, { $ }) => { let count = 0; $('button').addEventListener('click', () => { count++; $('button').textContent = `Count: ${count}`; }); } ); simulateEvent($('button'), 'click'); assertEqual($('button').textContent, 'Count: 1'); cleanup(); // Remove from DOM
Simulating Events
js
import { simulateEvent } from 'weblisk/test/utils.js'; // Click simulateEvent(button, 'click'); // Input simulateEvent(input, 'input', { target: { value: 'hello' } }); // Keyboard simulateEvent(el, 'keydown', { key: 'Enter' }); // Submit simulateEvent(form, 'submit');
Waiting for Async Conditions
js
import { waitFor } from 'weblisk/test/utils.js'; await waitFor(() => el.textContent === 'Loaded', { timeout: 3000, // ms (default: 2000) interval: 50, // polling interval (default: 50) });
Throws if the predicate doesn't become true within the timeout.
Assertions
js
import { assert, assertEqual } from 'weblisk/test/utils.js'; assert(value > 0, 'Expected positive value'); assertEqual(actual, expected, 'Values should match');
assertEqual uses JSON deep comparison — works with objects and arrays.
Example: Full Test File
js
import { describe, it } from 'node:test'; import { createSignalTest, assertEqual } from 'weblisk/test/utils.js'; describe('counter', () => { it('increments', () => { const { trackedSignal } = createSignalTest(); const [count, setCount] = trackedSignal(0); setCount(count() + 1); setCount(count() + 1); assertEqual(count(), 2); assertEqual(count.changes.length, 3); // [0, 1, 2] }); });