Skip to main content

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]
  });
});