Files
Endoleap/tests/headless/selectors.test.mjs

7 lines
1.8 KiB
JavaScript

import {test} from 'node:test';import assert from 'node:assert/strict';
import {createSelectorStore} from '../../src/lib/canvas-bindings/selectors.ts';
function source(initial){let state=initial;const listeners=new Set();return{getSnapshot:()=>state,subscribe(fn){listeners.add(fn);return()=>listeners.delete(fn);},set(next){state=next;for(const fn of [...listeners])fn();},get size(){return listeners.size;}};}
test('selector store caches immutable reads and notifies only changed selections',()=>{const s=source({a:1,b:1});let selections=0,notifications=0;const store=createSelectorStore(s,value=>{selections++;return{a:value.a};},(a,b)=>a.a===b.a),first=store.getSnapshot();assert.strictEqual(store.getSnapshot(),first);assert.equal(selections,1);const stop=store.subscribe(()=>notifications++);s.set({a:1,b:2});assert.strictEqual(store.getSnapshot(),first);assert.equal(notifications,0);s.set({a:2,b:2});assert.equal(notifications,1);assert.equal(store.getSnapshot().a,2);stop();assert.equal(s.size,0);});
test('selector errors notify React to retry the snapshot and retain no invalid cached value',()=>{const s=source({value:1});const store=createSelectorStore(s,next=>{if(next.value<0)throw Error('invalid');return next.value;});let notifications=0;const stop=store.subscribe(()=>notifications++);s.set({value:-1});assert.equal(notifications,1);assert.throws(store.getSnapshot,/invalid/);s.set({value:2});assert.equal(store.getSnapshot(),2);assert.equal(notifications,2);stop();});
test('distinct subscribers observe changes independently and unsubscribe during notification',()=>{const s=source(0),store=createSelectorStore(s,n=>n);let a=0,b=0,stopA;stopA=store.subscribe(()=>{a++;stopA();});const stopB=store.subscribe(()=>b++);s.set(1);s.set(2);assert.equal(a,1);assert.equal(b,2);stopB();assert.equal(s.size,0);});