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

15 lines
2.5 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* selector外部store的缓存与观察者测试。模拟不可变上游快照,核对相等选择复用引用、异常重读及多个订阅独立清理。
*/
import {test} from 'node:test';import assert from 'node:assert/strict';
import {createSelectorStore} from '../../src/lib/canvas-bindings/selectors.ts';
/** 最小store:set替换state并遍历监听副本,size观察退订;没有真实React挂载或渲染。 */
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;}};}
// selector只取a,自定义比较器忽略新对象引用;改b不通知,改a才发布,重复读取不重复选择计算。
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);});
// 负值使 selector 抛错,通知计数递增,随后 getSnapshot 必须重新抛出错误;恢复 2 后读到新值。
// 旧成功缓存可以仍在内部保留,但不能作为失败输入的成功返回;这里没有实际挂载 React 错误边界。
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();});
// A在第一次通知中退订,B仍收到两次更新;最后退订上游无监听残留。
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);});