154 lines
10 KiB
JavaScript
154 lines
10 KiB
JavaScript
import assert from "node:assert/strict";
|
|
import test from "node:test";
|
|
import {configureReactNodeWidgetHost,installReactNodeWidgetHost,intrinsicNodeWidgetContentHeight,refreshReactNodeWidget} from "../ui/src/regional/reactNodeWidgetHost.tsx";
|
|
|
|
test("intrinsic DOM widget measurement ignores height stretched by the node",()=>{
|
|
const content={
|
|
children:[{offsetTop:0,scrollHeight:318,offsetHeight:318,getBoundingClientRect:()=>({height:318})}],
|
|
scrollHeight:820,
|
|
getBoundingClientRect:()=>({height:820}),
|
|
};
|
|
assert.equal(intrinsicNodeWidgetContentHeight(content),318);
|
|
});
|
|
|
|
test("central React node widget host mounts once, rerenders and owns cleanup",()=>{
|
|
const events=[];
|
|
const host={className:"",dataset:{},remove(){events.push("remove")}};
|
|
const content={className:"",dataset:{}};
|
|
configureReactNodeWidgetHost({
|
|
createHost:()=>host,
|
|
createContentHost:()=>content,
|
|
createRoot:element=>{assert.equal(element,content);return{render:value=>events.push(["render",value]),unmount:()=>events.push("unmount")}},
|
|
schedule:action=>action(),
|
|
applyPresentation:(_node,nodeType)=>events.push(["presentation",nodeType]),
|
|
});
|
|
function NodeType(){}
|
|
NodeType.prototype.onNodeCreated=function(){events.push("created")};
|
|
NodeType.prototype.onConfigure=function(){events.push("configured")};
|
|
NodeType.prototype.onRemoved=function(){events.push("removed")};
|
|
installReactNodeWidgetHost(NodeType,"BV LoRA Registry",{id:"registry",name:"registry_widget",minHeight:220,render:node=>`view-${node.value}`});
|
|
const node=new NodeType();node.value=1;node.addDOMWidget=(name,type,element,options)=>{events.push(["add",name,type,element,options.getMinHeight()]);return{serialize:true}};
|
|
node.onNodeCreated();node.value=2;node.onConfigure();node.onRemoved();
|
|
assert.equal(events.filter(event=>Array.isArray(event)&&event[0]==="add").length,1);
|
|
assert.deepEqual(events.filter(event=>Array.isArray(event)&&event[0]==="render").map(event=>event[1]),["view-1","view-2"]);
|
|
assert.deepEqual(events.filter(event=>Array.isArray(event)&&event[0]==="presentation").map(event=>event[1]),["BV LoRA Registry","BV LoRA Registry"]);
|
|
assert.deepEqual(events.slice(-3),["unmount","remove","removed"]);
|
|
});
|
|
|
|
test("central React node widget host tracks content height and disconnects measurement",()=>{
|
|
const events=[];let resize,disconnects=0,options;
|
|
const host={className:"",dataset:{},remove(){events.push("remove")}};
|
|
const content={className:"",dataset:{}};
|
|
configureReactNodeWidgetHost({
|
|
createHost:()=>host,
|
|
createContentHost:()=>content,
|
|
createRoot:()=>({render:()=>{},unmount:()=>events.push("unmount")}),
|
|
schedule:action=>action(),
|
|
applyPresentation:()=>events.push("presentation"),
|
|
observeHost:(element,onHeight)=>{assert.equal(element,content);resize=onHeight;return()=>{disconnects++}},
|
|
});
|
|
function NodeType(){}
|
|
installReactNodeWidgetHost(NodeType,"BV LoRA Registry",{id:"registry-size",name:"registry_widget",minHeight:72,render:()=>"view"});
|
|
const node=new NodeType();node.addDOMWidget=(_name,_type,_host,next)=>{options=next;return{serialize:true}};
|
|
node.onNodeCreated();
|
|
assert.equal(typeof resize,"function");assert.equal(options.getMinHeight(),72);
|
|
resize(344);assert.equal(options.getMinHeight(),72);assert.equal(events.filter(item=>item==="presentation").length,2);
|
|
resize(344);assert.equal(events.filter(item=>item==="presentation").length,2);
|
|
resize(40);assert.equal(options.getMinHeight(),72);
|
|
node.onRemoved();assert.equal(disconnects,1);
|
|
});
|
|
|
|
test("central React node widget host measures intrinsic content and coalesces resize reconciliation",()=>{
|
|
const outer={className:"",dataset:{},remove(){}},content={className:"",dataset:{}},queue=[];let resize,disconnects=0,options,presentations=0;
|
|
configureReactNodeWidgetHost({
|
|
createHost:()=>outer,
|
|
createContentHost:host=>{assert.equal(host,outer);return content},
|
|
createRoot:element=>{assert.equal(element,content);return{render:()=>{},unmount:()=>{}}},
|
|
schedule:action=>queue.push(action),
|
|
applyPresentation:()=>{presentations++},
|
|
observeHost:(element,onHeight)=>{assert.equal(element,content);resize=onHeight;return()=>{disconnects++}},
|
|
});
|
|
function NodeType(){}
|
|
installReactNodeWidgetHost(NodeType,"BV LoRA Registry",{id:"registry-intrinsic",name:"registry_widget",minHeight:72,render:()=>"view"});
|
|
const node=new NodeType();node.addDOMWidget=(_name,_type,element,next)=>{assert.equal(element,outer);options=next;return{serialize:true}};
|
|
node.onNodeCreated();assert.equal(queue.length,1);queue.shift()();assert.equal(presentations,1);
|
|
resize(180);resize(240);assert.equal(queue.length,1);assert.equal(options.getMinHeight(),72);queue.shift()();assert.equal(presentations,2);
|
|
resize(120);assert.equal(queue.length,1);queue.shift()();assert.equal(options.getMinHeight(),72);assert.equal(presentations,3);
|
|
node.onRemoved();resize(300);assert.equal(queue.length,0);assert.equal(disconnects,1);
|
|
});
|
|
|
|
test("central React node widget host cancels pending mounts after node removal",()=>{
|
|
const queue=[];let adds=0,roots=0,observers=0;
|
|
const outer=()=>({className:"",dataset:{},remove(){}}),content=()=>({className:"",dataset:{}});
|
|
configureReactNodeWidgetHost({
|
|
createHost:outer,createContentHost:()=>content(),
|
|
createRoot:()=>{roots++;return{render:()=>{},unmount:()=>{}}},
|
|
schedule:action=>queue.push(action),applyPresentation:()=>{},
|
|
observeHost:()=>{observers++;return()=>{}},
|
|
});
|
|
function NodeType(){}
|
|
installReactNodeWidgetHost(NodeType,"BV LoRA Registry",{id:"registry-cancel",name:"registry_widget",minHeight:72,render:()=>"view"});
|
|
const makeNode=()=>{const node=new NodeType();node.addDOMWidget=()=>{adds++;return{serialize:true}};return node};
|
|
const created=makeNode();created.onNodeCreated();created.onRemoved();while(queue.length)queue.shift()();
|
|
const configured=makeNode();configured.onConfigure();configured.onRemoved();while(queue.length)queue.shift()();
|
|
assert.equal(adds,0);assert.equal(roots,0);assert.equal(observers,0);
|
|
assert.equal(created.__bvReactNodeWidgets,undefined);assert.equal(configured.__bvReactNodeWidgets,undefined);
|
|
});
|
|
|
|
test("central React node widget host rejects direct refresh after node removal",()=>{
|
|
let adds=0,roots=0,observers=0;
|
|
configureReactNodeWidgetHost({
|
|
createHost:()=>({className:"",dataset:{},remove(){}}),createContentHost:()=>({className:"",dataset:{}}),
|
|
createRoot:()=>{roots++;return{render:()=>{},unmount:()=>{}}},schedule:action=>action(),applyPresentation:()=>{},
|
|
observeHost:()=>{observers++;return()=>{}},
|
|
});
|
|
function NodeType(){}
|
|
const spec={id:"registry-refresh",name:"registry_widget",minHeight:72,render:()=>"view"};
|
|
installReactNodeWidgetHost(NodeType,"BV LoRA Registry",spec);
|
|
const node=new NodeType();node.addDOMWidget=()=>{adds++;return{serialize:true}};
|
|
node.onNodeCreated();assert.deepEqual([adds,roots,observers],[1,1,1]);node.onRemoved();
|
|
assert.equal(refreshReactNodeWidget(node,"BV LoRA Registry",spec),null);
|
|
assert.deepEqual([adds,roots,observers],[1,1,1]);assert.equal(node.__bvReactNodeWidgets,undefined);
|
|
});
|
|
|
|
test("central React node widget host caps intrinsic height against both pixels and viewport",()=>{
|
|
const events=[];let resize,options,viewportHeight=900;
|
|
const host={className:"",dataset:{},style:{},remove(){}},content={className:"",dataset:{},style:{}};
|
|
configureReactNodeWidgetHost({
|
|
createHost:()=>host,createContentHost:()=>content,
|
|
createRoot:()=>({render:()=>{},unmount:()=>{}}),schedule:action=>action(),applyPresentation:()=>events.push("presentation"),
|
|
viewportHeight:()=>viewportHeight,
|
|
observeHost:(_element,onHeight)=>{resize=onHeight;return()=>{}},
|
|
});
|
|
function NodeType(){}
|
|
const spec={id:"registry-cap",name:"registry_widget",minHeight:72,maxHeight:420,overflow:"auto",nativeActions:[{id:"open",name:"open_lora_registry",label:"Open LoRA Registry",invoke:node=>events.push(["open",node.id])}],render:()=>"view"};
|
|
installReactNodeWidgetHost(NodeType,"BV LoRA Registry",spec);
|
|
const node=new NodeType();node.id=9;node.widgets=[];
|
|
node.addWidget=(type,name,_value,callback,next)=>{const widget={type,name,callback,serialize:true,spec:{existing:"kept"},...next};node.widgets.push(widget);events.push(["action",type,name]);return widget};
|
|
node.addDOMWidget=(_name,_type,_host,next)=>{options=next;events.push("dom");return{serialize:true}};
|
|
node.onNodeCreated();node.onConfigure();
|
|
assert.equal(events.filter(item=>Array.isArray(item)&&item[0]==="action").length,1);
|
|
assert.deepEqual(node.widgets[0].spec,{existing:"kept",socketless:true});
|
|
assert.equal(node.widgets[0].serialize,false);node.widgets[0].callback();assert.deepEqual(events.at(-1),["open",9]);
|
|
resize(900);assert.equal(options.getMinHeight(),72);assert.equal(options.getMaxHeight(),420);
|
|
viewportHeight=500;assert.equal(options.getMinHeight(),72);assert.equal(options.getMaxHeight(),300);
|
|
resize(900);assert.equal(options.getMinHeight(),72);
|
|
viewportHeight=900;
|
|
resize(180);assert.equal(options.getMinHeight(),72);
|
|
assert.match(content.className,/bv-react-node-widget-scroll/);
|
|
assert.equal(options.margin,10);assert.equal(options.getHeight,undefined);assert.equal(content.style.maxHeight,"min(420px, 60vh, 100%)");assert.equal(content.style.overflowY,"auto");
|
|
});
|
|
|
|
test("opt-in content growth caps intrinsic height and yields to explicit user height",()=>{
|
|
let resize,options;const content={className:"",style:{}};
|
|
configureReactNodeWidgetHost({createHost:()=>({className:"",dataset:{},remove(){}}),createContentHost:()=>content,createRoot:()=>({render(){},unmount(){}}),schedule:action=>action(),applyPresentation:()=>{},viewportHeight:()=>1000,observeHost:(_host,measure)=>{resize=measure;return()=>{}}});
|
|
function NodeType(){}
|
|
installReactNodeWidgetHost(NodeType,"BV LoRA Registry",{id:"grow",name:"grow",minHeight:72,maxHeight:340,growWithContent:true,render:()=>null});
|
|
const node=new NodeType();node.addDOMWidget=(_name,_type,_host,value)=>{options=value;return{}};node.onNodeCreated();
|
|
resize(180);assert.equal(options.getMinHeight(),180);
|
|
resize(700);assert.equal(options.getMinHeight(),340);
|
|
node.properties={bvPresentationSizeVersion:1,bvPresentationUserHeight:300};
|
|
resize(800);assert.equal(options.getMinHeight(),72);assert.equal(options.getMaxHeight(),Infinity);assert.equal(content.style.maxHeight,"100%");
|
|
node.onRemoved();
|
|
});
|