217 lines
12 KiB
JavaScript
217 lines
12 KiB
JavaScript
import assert from "node:assert/strict";
|
|
import test from "node:test";
|
|
|
|
import {
|
|
applyNodePreviewProjection,
|
|
nodePreviewPolicy,
|
|
previewRowName,
|
|
previewTargetsForMutationRoot,
|
|
syncPreviewDetailSections,
|
|
} from "../ui/src/regional/nodePreviewProjection.ts";
|
|
import { setLegacyDebugVisible } from "../ui/src/regional/legacyPorts.ts";
|
|
|
|
test("Regional Prompt declares its ghost-only action widgets outside the backend schema", () => {
|
|
assert.deepEqual(nodePreviewPolicy("BV Regional Prompt"), {
|
|
actions: ["Open Regional Editor", "Quick Edit Prompts"],
|
|
});
|
|
});
|
|
|
|
test("technical and dynamic nodes declare shared ghost projections",()=>{
|
|
assert.deepEqual(nodePreviewPolicy("BV Inspect Any"),{rows:[{label:"Value preview available after execution",kind:"value"}]});
|
|
assert.deepEqual(nodePreviewPolicy("BV Seed")?.actions,["🎲 Randomize Each Time","🎲 New Fixed Random","♻️ (Use Last Queued Seed)"]);
|
|
assert.deepEqual(nodePreviewPolicy("BV Smart Pipe"),{actions:["Configure Smart Pipe"]});
|
|
assert.deepEqual(nodePreviewPolicy("BV Pipe"),{actions:[]});
|
|
assert.deepEqual(nodePreviewPolicy("BV Regional LoRA"),{actions:["Open LoRA Editor"]});
|
|
assert.deepEqual(nodePreviewPolicy("BV Regional Native Conditioning"),{actions:[]});
|
|
assert.equal(nodePreviewPolicy("BV Regional Debug"),undefined);
|
|
assert.deepEqual(nodePreviewPolicy("BV LoRA Registry"),{actions:["Open LoRA Registry"]});
|
|
});
|
|
|
|
test("preview projection appends native-looking button rows only to the Vue preview body", () => {
|
|
const appended = [];
|
|
let widgetsMounted = false;
|
|
const widgetRows = [
|
|
"regional_json",
|
|
"lora_bindings_json",
|
|
"lora_v3_config_json",
|
|
"detailer_v3_config_json",
|
|
"lut_v3_config_json",
|
|
"unrelated_widget",
|
|
].map((textContent) => ({ textContent, hidden: false, style: { display: "" } }));
|
|
const body = {
|
|
querySelector: () => null,
|
|
querySelectorAll: (selector) => selector === '[data-testid="node-widget"]' && widgetsMounted ? widgetRows : [],
|
|
append: (node) => appended.push(node),
|
|
};
|
|
const preview = {
|
|
dataset: { nodeId: "preview-BV Regional Prompt" },
|
|
querySelector: (selector) => selector === '[data-testid="node-body-preview-BV Regional Prompt"]' ? body : null,
|
|
};
|
|
const documentLike = {
|
|
createElement(tagName) {
|
|
return {
|
|
tagName,
|
|
className: "",
|
|
dataset: {},
|
|
attributes: {},
|
|
children: [],
|
|
append(...children) { this.children.push(...children); },
|
|
setAttribute(name, value) { this.attributes[name] = value; },
|
|
textContent: "",
|
|
};
|
|
},
|
|
};
|
|
|
|
assert.equal(applyNodePreviewProjection(preview, documentLike), true);
|
|
assert.equal(appended.length, 1);
|
|
assert.equal(appended[0].dataset.bvPreviewActions, "BV Regional Prompt");
|
|
assert.deepEqual(appended[0].children.map((row) => row.children[1].children[0].textContent), [
|
|
"Open Regional Editor",
|
|
"Quick Edit Prompts",
|
|
]);
|
|
assert.equal(preview.dataset.bvPreviewProjected, "BV Regional Prompt");
|
|
widgetsMounted = true;
|
|
assert.equal(applyNodePreviewProjection(preview, documentLike), true);
|
|
assert.equal(appended.length, 1);
|
|
assert.deepEqual(widgetRows.map((row) => row.hidden), [true, true, true, true, true, false]);
|
|
assert.deepEqual(widgetRows.map((row) => row.style.display), ["none", "none", "none", "none", "none", ""]);
|
|
});
|
|
|
|
function previewFixture(nodeType,bodyRows,detailRows=[]){
|
|
const body={querySelectorAll(selector){
|
|
if(selector==='[data-testid="node-widget"]')return bodyRows;
|
|
if(selector==='.lg-slot--input, .lg-slot--output')return bodyRows.filter(row=>row.classList?.contains?.("lg-slot--input")||row.classList?.contains?.("lg-slot--output"));
|
|
return[];
|
|
},append(){}};
|
|
return {dataset:{nodeId:`preview-${nodeType}`,bvPreviewProjected:nodeType},querySelector:selector=>selector===`[data-testid="node-body-preview-${nodeType}"]`?body:null,querySelectorAll:selector=>selector==='[data-bv-preview-port]'?detailRows:[]};
|
|
}
|
|
|
|
test("Smart Pipe reserve ports stay hidden in the debug Ghost",()=>{
|
|
const row={textContent:"v_001",hidden:false,style:{display:""},dataset:{bvPreviewPort:"input"}};
|
|
setLegacyDebugVisible(true);
|
|
try{assert.equal(applyNodePreviewProjection(previewFixture("BV Smart Pipe",[row],[row]),{createElement(){throw new Error("no projected rows expected")}}),true);assert.equal(row.hidden,true);assert.equal(row.style.display,"none")}
|
|
finally{setLegacyDebugVisible(false)}
|
|
});
|
|
|
|
test("normal preview hides legacy inputs and outputs in body and lower details",()=>{
|
|
const rows=["pipe","v_001","pipe","out_001"].map((textContent,index)=>({textContent,hidden:false,style:{display:""},dataset:{bvPreviewPort:index<2?"input":"output"}}));
|
|
const preview=previewFixture("BV Smart Pipe",[],rows);
|
|
assert.equal(applyNodePreviewProjection(preview,{createElement(){throw new Error("no projected rows expected")}}),true);
|
|
assert.deepEqual(rows.map(row=>row.hidden),[false,true,false,true]);
|
|
});
|
|
|
|
test("a legacy-only Pipe Ghost keeps its public pipe input and output instead of reserve fallbacks",()=>{
|
|
const rows=[
|
|
{textContent:"pipe",hidden:false,style:{display:""},dataset:{bvPreviewPort:"input"}},
|
|
{textContent:"v_001",hidden:false,style:{display:""},dataset:{bvPreviewPort:"input"}},
|
|
{textContent:"pipe",hidden:false,style:{display:""},dataset:{bvPreviewPort:"output"}},
|
|
{textContent:"out_001",hidden:false,style:{display:""},dataset:{bvPreviewPort:"output"}},
|
|
];
|
|
assert.equal(applyNodePreviewProjection(previewFixture("BV Pipe",rows,rows),{createElement(){throw new Error("no projected rows expected")}}),true);
|
|
assert.deepEqual(rows.map(row=>row.hidden),[false,true,false,true]);
|
|
});
|
|
|
|
test("Regional Prompt ghost projects body and detail rows through semantic presentation roles",()=>{
|
|
const rows=[
|
|
{textContent:"regional",hidden:false,style:{display:""},dataset:{bvPreviewPort:"output"}},
|
|
{textContent:"lora_bindings",hidden:false,style:{display:""},dataset:{bvPreviewPort:"output"}},
|
|
{textContent:"regional_json STRING",hidden:false,style:{display:""},dataset:{bvPreviewPort:"input"}},
|
|
{textContent:"resource_provider_1 BV_RUNTIME_RESOURCE_PROVIDER",hidden:false,style:{display:""},dataset:{bvPreviewPort:"input"}},
|
|
];
|
|
const preview=previewFixture("BV Regional Prompt",[],rows);
|
|
assert.equal(applyNodePreviewProjection(preview,{createElement(){throw new Error("no projected rows expected")}}),true);
|
|
assert.deepEqual(rows.map(row=>row.hidden),[false,true,true,true]);
|
|
setLegacyDebugVisible(true);
|
|
try{
|
|
assert.equal(applyNodePreviewProjection(preview,{createElement(){throw new Error("no projected rows expected")}}),true);
|
|
assert.deepEqual(rows.map(row=>row.hidden),[false,false,true,true]);
|
|
}finally{setLegacyDebugVisible(false)}
|
|
});
|
|
|
|
test("Regional LoRA ghost hides internal and provider rows in body and lower details",()=>{
|
|
const bodyRows=[
|
|
{textContent:"regional BV_REGIONAL",hidden:false,style:{display:""},classList:{contains:name=>name==="lg-slot--input"}},
|
|
{textContent:"regional BV_REGIONAL",hidden:false,style:{display:""},classList:{contains:name=>name==="lg-slot--output"}},
|
|
{textContent:"operation COMBO",hidden:false,style:{display:""}},
|
|
{textContent:"config_json STRING",hidden:false,style:{display:""}},
|
|
{textContent:"resource_provider_1 BV_RUNTIME_RESOURCE_PROVIDER",hidden:false,style:{display:""},classList:{contains:name=>name==="lg-slot--input"}},
|
|
];
|
|
const detailRows=[
|
|
{textContent:"regional BV_REGIONAL",hidden:false,style:{display:""},dataset:{bvPreviewPort:"input"}},
|
|
{textContent:"regional BV_REGIONAL",hidden:false,style:{display:""},dataset:{bvPreviewPort:"output"}},
|
|
{textContent:"operation COMBO",hidden:false,style:{display:""},dataset:{bvPreviewPort:"input"}},
|
|
{textContent:"config_json STRING",hidden:false,style:{display:""},dataset:{bvPreviewPort:"input"}},
|
|
{textContent:"resource_provider_1 BV_RUNTIME_RESOURCE_PROVIDER",hidden:false,style:{display:""},dataset:{bvPreviewPort:"input"}},
|
|
];
|
|
const preview=previewFixture("BV Regional LoRA",bodyRows,detailRows);
|
|
for(const legacyDebug of [false,true]){
|
|
setLegacyDebugVisible(legacyDebug);
|
|
assert.equal(applyNodePreviewProjection(preview,{createElement(){return{className:"",dataset:{},append(){},setAttribute(){},textContent:""}}}),true);
|
|
assert.deepEqual(bodyRows.map(row=>row.hidden),[false,false,true,true,true]);
|
|
assert.deepEqual(detailRows.map(row=>row.hidden),[false,false,true,true,true]);
|
|
}
|
|
setLegacyDebugVisible(false);
|
|
});
|
|
|
|
test("Smart Pipe Merge ghost hides every reserve and its real JSON row in body and details",()=>{
|
|
const slot=(textContent,direction)=>({textContent,hidden:false,style:{display:""},classList:{contains:name=>name===`lg-slot--${direction}`}});
|
|
const bodyRows=[
|
|
slot("pipe_001 BV_SMART_PIPE","input"),
|
|
slot("pipe_016 BV_SMART_PIPE","input"),
|
|
slot("pipe BV_SMART_PIPE","output"),
|
|
{textContent:"bv_smart_pipe_merge_json STRING",hidden:false,style:{display:""}},
|
|
];
|
|
const detailRows=[
|
|
{textContent:"pipe_001 BV_SMART_PIPE",hidden:false,style:{display:""},dataset:{bvPreviewPort:"input"}},
|
|
{textContent:"pipe_016 BV_SMART_PIPE",hidden:false,style:{display:""},dataset:{bvPreviewPort:"input"}},
|
|
{textContent:"bv_smart_pipe_merge_json STRING",hidden:false,style:{display:""},dataset:{bvPreviewPort:"input"}},
|
|
{textContent:"pipe BV_SMART_PIPE",hidden:false,style:{display:""},dataset:{bvPreviewPort:"output"}},
|
|
];
|
|
const preview=previewFixture("BV Smart Pipe Merge",bodyRows,detailRows);
|
|
for(const legacyDebug of[false,true]){
|
|
setLegacyDebugVisible(legacyDebug);
|
|
assert.equal(applyNodePreviewProjection(preview,{createElement(){return{className:"",dataset:{},append(){},setAttribute(){},textContent:""}}}),true);
|
|
assert.deepEqual(bodyRows.map(row=>row.hidden),[true,true,false,true]);
|
|
assert.deepEqual(detailRows.map(row=>row.hidden),[true,true,true,false]);
|
|
}
|
|
setLegacyDebugVisible(false);
|
|
});
|
|
|
|
test("a late lower-detail mutation resolves the sibling preview through its card",()=>{
|
|
const preview={dataset:{nodeId:"preview-BV Regional Prompt"}};
|
|
const card={querySelector:selector=>selector==='[data-node-id^="preview-"]'?preview:null};
|
|
const detailRow={closest:selector=>selector==='[data-testid="node-preview-card"]'?card:null};
|
|
assert.deepEqual(previewTargetsForMutationRoot(detailRow),[preview]);
|
|
});
|
|
|
|
test("lower detail names come from the label span instead of concatenated row text",()=>{
|
|
const label={textContent:"regional_json"};
|
|
const row={textContent:"regional_jsonSTRING",querySelector:selector=>selector==="span"?label:null};
|
|
assert.equal(previewRowName(row),"regional_json");
|
|
});
|
|
|
|
test("an empty lower detail section is hidden while a public section remains",()=>{
|
|
const heading={textContent:"INPUTS"};
|
|
const hiddenRow={hidden:true,style:{display:"none"}};
|
|
const visibleRow={hidden:false,style:{display:""}};
|
|
const hiddenSection={children:[heading,hiddenRow],hidden:false,style:{display:""}};
|
|
heading.parentElement=hiddenSection;
|
|
const outputHeading={textContent:"OUTPUTS"};
|
|
const visibleSection={children:[outputHeading,visibleRow],hidden:false,style:{display:""}};
|
|
outputHeading.parentElement=visibleSection;
|
|
const card={querySelectorAll:()=>[heading,outputHeading]};
|
|
syncPreviewDetailSections({closest:()=>card});
|
|
assert.equal(hiddenSection.hidden,true);
|
|
assert.equal(hiddenSection.style.display,"none");
|
|
assert.equal(visibleSection.hidden,false);
|
|
});
|
|
|
|
test("lower detail cleanup leaves unrelated card sections untouched",()=>{
|
|
const heading={textContent:"Description"};
|
|
const section={children:[heading],hidden:false,style:{display:""}};
|
|
heading.parentElement=section;
|
|
syncPreviewDetailSections({closest:()=>({querySelectorAll:()=>[heading]})});
|
|
assert.equal(section.hidden,false);
|
|
assert.equal(section.style.display,"");
|
|
});
|