Files
BlackVortexAI-bv_nodepack/tests/ui_control_integration.test.mjs
BlackVortexAIandClaude Fable 5.1 95ace90041 Add BV Text writing assistance, preparation caches and the 1.4.4 hardening
Features: BV Text node with management-gated writing assistance (translate,
improve, undo, prompt profiles); reuse of prepared MODEL/CLIP state for Krea 2
and Anima LLLite consumers; Regional refactoring.

Security boundaries: one wall-clock budget per Remote LLM request enforced by
a socket-closing watchdog across connect, TLS handshake, headers, chunk framing
and body; writing assistance uses the profile's configured timeout; a shared
bounded JSON reader with route-specific limits on every JSON route; endpoint
binding negative tests for the assist path.

Release and packaging: SECURITY.md; bundled-library notices and a readable
bundle header; Registry scan replica as a hard test with a pinned count; a
packaged-file-set test; publish only from a validated push to main with
provenance checks, pinned comfy-cli, an input manifest and a byte-exact
comparison of the stored archive; npm audit and a whole js/ rebuild gate in
Validate; design references, brand sources and a type declaration excluded
from the package. README and changelog for 1.4.4.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-13 16:51:38 +02:00

548 lines
34 KiB
JavaScript

import assert from "node:assert/strict";
import { readFileSync } from "node:fs";
import test from "node:test";
const options = readFileSync(new URL("../ui/src/regional/OptionsPanel.tsx", import.meta.url), "utf8");
const detailer = readFileSync(new URL("../ui/src/regional/detailerPlanDialog.ts", import.meta.url), "utf8");
const detailerView = readFileSync(new URL("../ui/src/regional/DetailerPlanDialogView.tsx", import.meta.url), "utf8");
const detectorView = readFileSync(new URL("../ui/src/regional/DetectorRegistryDialogView.tsx", import.meta.url), "utf8");
const lutRegistryView = readFileSync(new URL("../ui/src/regional/LutRegistryDialogView.tsx", import.meta.url), "utf8");
const lutPlanView = readFileSync(new URL("../ui/src/regional/LutPlanDialogView.tsx", import.meta.url), "utf8");
const dataComponents = readFileSync(new URL("../ui/src/ui/components/data.tsx", import.meta.url), "utf8");
const controls = readFileSync(new URL("../ui/src/ui/controls.tsx", import.meta.url), "utf8");
const forms = readFileSync(new URL("../ui/src/ui/components/forms.tsx", import.meta.url), "utf8");
const overlays = readFileSync(new URL("../ui/src/ui/components/overlays.tsx", import.meta.url), "utf8");
const floating = readFileSync(new URL("../ui/src/ui/components/floating.tsx", import.meta.url), "utf8");
const windowChrome = readFileSync(new URL("../ui/src/ui/window.tsx", import.meta.url), "utf8");
const windowMount = readFileSync(new URL("../ui/src/ui/mount.tsx", import.meta.url), "utf8");
const toolbarLauncher = readFileSync(new URL("../ui/src/ui/ToolbarWindowLauncher.tsx", import.meta.url), "utf8");
const windowActivity = readFileSync(new URL("../ui/src/ui/windowActivity.ts", import.meta.url), "utf8");
const windowRegistry = readFileSync(new URL("../ui/src/ui/windowRegistry.ts", import.meta.url), "utf8");
const layoutProfiles = readFileSync(new URL("../ui/src/ui/layoutProfiles.ts", import.meta.url), "utf8");
const dock = readFileSync(new URL("../ui/src/ui/dock.tsx", import.meta.url), "utf8");
const windowFocus = readFileSync(new URL("../ui/src/ui/windowFocus.ts", import.meta.url), "utf8");
const uiEntry = readFileSync(new URL("../ui/src/index.tsx", import.meta.url), "utf8");
const editorMenus = readFileSync(new URL("../ui/src/regional/EditorMenus.tsx", import.meta.url), "utf8");
const regionalToolPalette = readFileSync(new URL("../ui/src/ui/components/RegionalToolPalette.tsx", import.meta.url), "utf8");
const regionalEditor = readFileSync(new URL("../ui/src/regional/RegionalEditor.tsx", import.meta.url), "utf8");
const quickPromptEditor = readFileSync(new URL("../ui/src/regional/QuickPromptEditor.tsx", import.meta.url), "utf8");
const promptTextarea = readFileSync(new URL("../ui/src/completion/PromptTextarea.tsx", import.meta.url), "utf8");
const styles = readFileSync(new URL("../ui/src/index.css", import.meta.url), "utf8");
const showcase = readFileSync(new URL("../ui/src/showcase.tsx", import.meta.url), "utf8");
const smartPipeEditor = readFileSync(new URL("../ui/src/components/SmartPipeEditorWindow.tsx", import.meta.url), "utf8");
const smartPipeBridge = readFileSync(new URL("../js/bv_smart_pipe.js", import.meta.url), "utf8");
const historyController = readFileSync(new URL("../ui/src/ui/history.ts", import.meta.url), "utf8");
const toastStore = readFileSync(new URL("../ui/src/ui/toastStore.tsx", import.meta.url), "utf8");
const remoteLlm = readFileSync(new URL("../ui/src/remoteLLM.ts", import.meta.url), "utf8");
const lutDownload = readFileSync(new URL("../ui/src/regional/lutDownloadDialog.tsx", import.meta.url), "utf8");
test("regional inspector composes shared production controls instead of restyling raw widgets", () => {
for (const component of ["BvSelect", "BvNumberField", "FieldFrame", "PromptPairFields", "ToolTabs", "SegmentedToggleGroup"]) assert.match(options, new RegExp(`<${component}\\b`));
assert.doesNotMatch(options, /<select\b|<input\s+type="number"|<textarea\b/);
assert.doesNotMatch(options, /className="option-section"/);
});
test("imperative detailer entrypoint mounts a React framework view", () => {
assert.match(detailer, /mountBvView/);
for (const component of ["BvManagedWindow", "NumberField", "SelectField", "TagSelect", "TextField"]) assert.match(detailerView, new RegExp(`<${component}\\b`));
assert.doesNotMatch(detailerView, /<(?:button|input|select|textarea)\b/);
});
test("node-owned windows reactivate by type and node id instead of mounting duplicates", () => {
assert.match(windowMount, /mountedViews\.get\(options\.key\)/);
assert.match(windowMount, /existing\.activate\(render\)/);
assert.match(windowMount, /bv-ui-activate/);
assert.match(windowMount, /currentState=current\?\.state\(\)/);
assert.match(windowMount, /pendingStates\.set\(targetKey,currentState\)/);
assert.match(windowMount, /getBoundingClientRect/);
assert.match(windowMount, /geometry:box\?\{x:box\.left,y:box\.top,width:box\.width,height:box\.height\}/);
assert.match(windowChrome, /bv-ui-set-window-state/);
assert.match(windowChrome, /addEventListener\("bv-ui-activate"/);
assert.match(uiEntry, /`detailer-plan:\$\{scopedNodeKey\(node\)\}`/);
assert.match(uiEntry, /`detector-registry:\$\{scopedNodeKey\(node\)\}`/);
});
test("workflow switches close workflow-owned imperative windows but preserve global system windows", () => {
assert.match(windowMount, /scope\?:\s*"workflow"\s*\|\s*"global"/);
assert.match(windowMount, /scope:\s*options\.scope\s*\?\?\s*"workflow"/);
assert.match(windowMount, /export function closeWorkflowBvViews/);
assert.match(uiEntry, /closeWorkflowBvViews\(\);[\s\S]*bvReactRoot\?\.unmount\(\)/);
assert.match(remoteLlm, /scope:\s*"global"/);
assert.match(lutDownload, /scope:\s*"global"/);
});
test("every node editor uses the conditional shared window navigator", () => {
for (const source of [regionalEditor, quickPromptEditor, detailerView, detectorView]) assert.match(source, /BvWindowNavigator/);
assert.match(windowChrome, /options\.filter\(option=>option\.value!==value\)/);
assert.match(windowChrome, /if\(!targets\.length\)return null/);
assert.match(windowChrome, /meta\.shiftKey\?keep:!keep/);
assert.match(windowChrome, /bv-window-switch-mode/);
assert.match(styles, /\.bv-window-navigator\s*\{[^}]*display:flex/s);
assert.match(styles, /\.bv-ui-window-shelf-close/);
assert.match(regionalEditor, /activationToken/);
assert.match(regionalEditor, /transferredWindow/);
});
test("shelf restore uses node-owned geometry while navigator switches transfer the active workspace", () => {
for(const source of [regionalEditor,quickPromptEditor]) assert.match(source,/onRestore=\{\(\)=>navigateNode\(id,false,false\)\}/);
assert.match(quickPromptEditor,/transferredGeometry\.current=transferWindow\?geometry:null/);
assert.match(quickPromptEditor,/quickPromptWindow: nextGeometry/);
assert.match(quickPromptEditor,/initialGeometry=\{geometry\}/);
});
test("toolbar and node-button activation use the same keep-mode lifecycle", () => {
assert.match(uiEntry,/quickEditActivation/);
assert.match(uiEntry,/<QuickPromptEditor[^>]*activationToken=\{quickEditActivation\}/);
for(const source of [regionalEditor,quickPromptEditor]){
assert.match(source,/previousActivation/);
assert.match(source,/getWindowSwitchMode\(\)==="keep"/);
assert.match(source,/transferred(?:Window|Geometry)\.current=wasOpen\?/);
}
});
test("ComfyUI toolbar exposes a node-aware multi-column window launcher", () => {
assert.match(uiEntry, /<ToolbarWindowLauncher\s+getColumns=\{launcherColumns\}/);
for (const label of ["Regional Prompts", "Regional LoRA", "Detailer Plan", "Detector Registry"]) assert.match(uiEntry, new RegExp(`label:\"${label}\"`));
assert.match(uiEntry,/workflowNodesOfType\("BV Regional LoRA"\)/);
assert.match(uiEntry,/secondary:\{label:`Quick edit/);
assert.match(toolbarLauncher, /filter\(column=>column\.items\.length\)/);
assert.match(toolbarLauncher, /\.bv-regional-action/);
assert.match(toolbarLauncher, /pointerenter/);
assert.doesNotMatch(toolbarLauncher, /addEventListener\("focus"/);
assert.match(toolbarLauncher, /event\.key===\"Escape\"/);
assert.match(styles, /\.bv-toolbar-window-launcher__body\s*\{[^}]*grid-template-columns:repeat\(var\(--bv-launcher-columns\)/s);
assert.match(styles, /\.bv-toolbar-window-launcher \.bv-button\s*\{[^}]*grid-template-columns:minmax\(0,1fr\) auto[^}]*min-height:26px/s);
});
test("toolbar primary actions restore the last active node per editor type", () => {
assert.match(windowActivity, /const states=new WeakMap<object,BvWindowActivityState>/);
assert.match(uiEntry, /createOpenLastBvEditorAction/);
assert.match(uiEntry, /activity\.lastInstance\("regional"\)/);
assert.match(quickPromptEditor, /bvWindowActivity\(activityScope\)\.remember\("quick",nextId\)/);
assert.match(regionalEditor, /bvWindowActivity\(activityScope\)\.remember\("regional",nextId\)/);
assert.match(uiEntry, /activityFor\(scope\)\.remember\("lora",scopedNodeKey\(node\)\)/);
assert.doesNotMatch(uiEntry, /lastBvWindowInstance|rememberBvWindowInstance|lastBvFullWindowType/);
});
test("detector and detailer editors use shared compact collection and grid modules", () => {
for (const component of ["UiDensity", "FieldGrid"]) assert.match(detectorView, new RegExp(`<${component}\\b`));
for (const component of ["UiDensity", "FieldGrid", "TagSelect", "SortableList"]) assert.match(detailerView, new RegExp(`<${component}\\b`));
assert.match(dataComponents, /summary\?:ReactNode/);
assert.match(styles, /\.bv-field-grid,\.bv-choice-grid/);
assert.doesNotMatch(`${detectorView}\n${detailerView}`, /bv-detailer-control-grid|bv-detailer-region-grid/);
});
test("detailer regions use a closed tag selector that preserves one selection", () => {
assert.match(detailerView, /<TagSelect\b[^>]*minSelected=\{1\}/);
assert.doesNotMatch(detailerView, /<CheckboxField\b|<ChoiceGrid\b/);
assert.match(forms, /export function TagSelect/);
assert.match(forms, /selected&&value\.length<=minSelected/);
assert.match(forms, /aria-multiselectable="true"/);
const tagSelect = forms.slice(forms.indexOf("export function TagSelect"), forms.indexOf("export function SearchField"));
assert.doesNotMatch(tagSelect, /<input\b|contentEditable|onValue\(\[\.\.\.value,draft/);
});
test("shared dialogs reserve header and footer space at narrow heights", () => {
assert.match(styles, /\.bv-dialog\s*\{[^}]*display:grid[^}]*grid-template-rows:auto minmax\(0,1fr\) auto/s);
assert.match(styles, /\.bv-dialog-body\s*\{[^}]*min-height:0[^}]*overflow:auto/s);
assert.doesNotMatch(styles, /\.bv-dialog-body\s*\{[^}]*max-height:calc\(86vh - 120px\)/s);
});
test("BV roots normalize inherited native control metrics without resetting ComfyUI globally", () => {
assert.match(styles, /:is\(\.bv-ui,#bv-root,\.bv-ui-portal\)/);
assert.match(styles, /:where\(button,input,select,textarea\)/);
assert.doesNotMatch(styles, /(^|\n)\s*(button|input|select|textarea)\s*\{/m);
});
test("number fields preserve the approved value-first adaptive structure", () => {
assert.match(forms, /className="bv-number-value-row"/);
assert.match(forms, /className="bv-number-box"/);
assert.match(forms, /className="bv-number-slider"/);
assert.match(controls, /return <NumberField/);
assert.match(styles, /@container\s+bv-number-field/);
});
test("compact controls use a real framework density instead of inheriting comfortable height", () => {
assert.match(forms, /CompactSelect[\s\S]*bv-density-compact/);
assert.match(styles, /--bv-ui-control-height-compact/);
assert.match(styles, /\.bv-density-compact\s*\{/);
assert.match(styles, /\.bv-ui-window-context \.bv-select-shell/);
});
test("anchored select menus keep viewport geometry instead of inheriting portal width", () => {
assert.doesNotMatch(styles, /\.bv-select-menu\s*\{[^}]*min-width:\s*100%/s);
assert.doesNotMatch(styles, /\.bv-select-menu\s*\{[^}]*(?:left:\s*0|right:\s*0)/s);
assert.match(styles, /\.bv-anchored-popover\s*\{[^}]*position:\s*fixed[^}]*right:\s*auto[^}]*min-width:\s*0/s);
});
test("scrollable textareas theme the scrollbar corner and native resize affordance", () => {
assert.match(styles, /\.bv-textarea-shell>textarea::-webkit-scrollbar-corner\s*\{/);
assert.match(styles, /\.bv-textarea-shell>textarea::-webkit-resizer\s*\{/);
assert.match(styles, /\.bv-textarea-shell>textarea\s*\{[^}]*scrollbar-color:/s);
});
test("regional inspector uses compact controls and a viewport-safe framework color picker", () => {
assert.match(options, /<UiDensity\s+density="compact"/);
assert.doesNotMatch(options, /<code>\{props\.region\.authoring\.color/);
assert.match(forms, /<AnchoredPopover\b/);
assert.match(overlays, /export\s*\{\s*AnchoredPopover,\s*Portal\s*\}\s*from\s*"\.\/floating"/);
assert.match(floating, /export function AnchoredPopover/);
assert.match(styles, /\.bv-anchored-popover\s*\{[^}]*position:\s*fixed/s);
assert.doesNotMatch(styles, /\.region-color-control label\s*\{[^}]*grid-template-columns/s);
});
test("window chrome keeps title and compact context controls in one refined header row", () => {
assert.match(windowChrome, /bv-ui-window-title/);
assert.doesNotMatch(windowChrome, /bv-ui-window-copy/);
assert.match(windowChrome, /viewBox="0 0 32 32"/);
assert.match(windowChrome, /M11\.08 2\.736a2 2 0 0 1-/);
assert.doesNotMatch(windowChrome, /M12 3\.5a8\.5 8\.5/);
assert.match(styles, /\.bv-ui-window-identity\s*\{[^}]*display:flex[^}]*align-items:center/s);
assert.match(styles, /\.bv-ui-window-control\s*\{[^}]*width:calc\(24px/s);
assert.match(editorMenus, /<Button intent="ghost" density="compact"/);
assert.match(windowChrome, /aria-label=\{switchLabel\}/);
assert.match(styles, /\.bv-managed-window \.bv-ui-window-header\s*\{[^}]*grid-template-columns:minmax\(0,1fr\) auto/s);
assert.match(styles, /\.bv-window-navigator \.bv-select-trigger\s*\{[^}]*height:22px/s);
assert.match(styles, /\.bv-window-switch-mode>span:last-child\s*\{\s*display:none/);
});
test("layout profile footer icon remains compact and readable in modified state", () => {
assert.match(styles, /\.bv-ui-window-footer-actions \.bv-button>svg\s*\{[^}]*width:calc\(16px[^}]*height:calc\(16px[^}]*fill:none[^}]*stroke:currentColor/s);
assert.match(styles, /\.bv-ui-window-footer-actions \.bv-button\s*\{[^}]*white-space:nowrap/s);
});
test("regional editor delegates its complete window lifecycle to the shared managed window", () => {
assert.match(regionalEditor, /<BvManagedWindow\b/);
assert.doesNotMatch(regionalEditor, /<BvWindowHeader\b|beginWindowDrag|new ResizeObserver/);
assert.match(regionalEditor, /bodyClassName="bv-regional-window-body"/);
assert.match(windowChrome, /mode\?:BvWindowMode/);
assert.match(windowChrome, /onModeChange\?:/);
});
test("compact window navigation progressively moves mode and node selection into overflow", () => {
assert.match(windowChrome, /bv-window-navigator-overflow/);
assert.match(windowChrome, /nodeInOverflow=layout\.minimal,modeInOverflow=layout\.narrow/);
assert.match(windowChrome, /!nodeInOverflow&&select/);
assert.match(windowChrome, /!modeInOverflow&&toggle/);
assert.match(windowChrome, /modeInOverflow&&<div ref=\{overflow\}/);
});
test("window navigation overflow is a complete surfaced control group", () => {
assert.match(windowChrome, /className="bv-window-navigator-panel"/);
assert.match(windowChrome, /nodeInOverflow&&<div className="bv-window-navigator-panel-row is-node">\{select\}/);
assert.match(windowChrome, /className="bv-window-navigator-panel-row is-mode"/);
assert.equal((windowChrome.match(/<CompactSelect/g)??[]).length,1);
assert.equal((windowChrome.match(/role="switch"/g)??[]).length,1);
assert.match(styles, /\.bv-window-navigator-menu\s*\{[^}]*background:var\(--bv-ui-surface-raised\)[^}]*border:/s);
assert.match(styles, /\.bv-window-navigator-panel\s*\{[^}]*display:grid[^}]*width:/s);
});
test("managed windows share keyboard cycling, escape handling and focus restoration", () => {
assert.match(windowFocus, /event\.key !== "Tab" \|\| !event\.ctrlKey/);
assert.match(windowFocus, /node\.style\.zIndex=`calc\(var\(--bv-layer-window\) \+ \$\{index\}\)`/);
assert.match(windowFocus, /windows\.push\(node\);\s*syncLayers\(\)/);
assert.match(windowChrome, /opener\.current\?\.focus/);
assert.match(windowChrome, /event\.key==="Escape"/);
assert.match(windowChrome, /registerBvWindow/);
});
test("compact editors expose explanatory help as non-blocking overlays", () => {
assert.match(styles, /\.bv-density-compact \.bv-control-help-overlay[^}]*position:absolute[^}]*pointer-events:none/s);
assert.match(styles, /\.bv-density-compact \.bv-control-field:focus-within>\.bv-control-help-overlay/);
});
test("detailer and detector lists start summary-first", () => {
assert.match(detailerView, /bv-editor-item-compact-summary/);
assert.match(detailerView, /content: <div className="bv-editor-item-details"/);
assert.match(detectorView, /bv-detector-summary/);
assert.match(detectorView, /<Accordion value=\{expanded\}/);
});
test("prompt pairs allow independent vertical resizing", () => {
assert.match(styles, /\.bv-prompt-editor-fields \{[^}]*grid-auto-rows:auto[^}]*height:auto/s);
assert.match(styles, /\.bv-prompt-editor-fields textarea \{[^}]*resize:vertical/s);
});
test("managed windows separate rounded clipping from overlay escape", () => {
assert.match(windowChrome, /bv-managed-window-surface/);
assert.match(styles, /\.bv-managed-window-surface\s*\{[^}]*overflow:hidden[^}]*border-radius:inherit/s);
assert.match(windowChrome, /effectiveMinWidth=Math\.min\(min\.width,420,availableWidth\)/);
assert.match(windowChrome, /resizeMin=\{\.\.\.minSize,width:Math\.min\(minSize\.width,420\)\}/);
});
test("header overflow follows actual available controls and toggle reaches both ends", () => {
assert.match(windowChrome, /ResizeObserver/);
assert.match(windowChrome, /BvWindowHeaderLayoutContext\.Provider/);
assert.match(editorMenus, /useBvWindowHeaderLayout/);
assert.doesNotMatch(editorMenus, /new ResizeObserver/);
assert.match(styles, /\.bv-window-switch-mode\[aria-checked=true\] \.bv-toggle-track>span\s*\{[^}]*margin-left:auto/s);
});
test("header launcher supplies its actual trigger as the popup anchor", () => {
assert.match(windowChrome, /detail:\{anchor:/);
assert.match(toolbarLauncher, /CustomEvent<\{anchor\?:HTMLElement\}>/);
assert.match(toolbarLauncher, /positionLauncher/);
});
test("minimal controls use overlay help and compact symbolic color actions", () => {
assert.match(forms, /bv-control-help-overlay/);
assert.doesNotMatch(styles, /\.bv-density-compact \.bv-control-help[^}]*min-height/);
assert.match(options, /region-color-split/);
assert.match(options, /aria-label="Reset display color to automatic"/);
});
test("sortable jobs rely on drag reorder and grouped actions only", () => {
assert.doesNotMatch(dataComponents, /Move \$\{String\(item\.title\)\} up|Move \$\{String\(item\.title\)\} down/);
assert.match(dataComponents, /bv-segmented-actions/);
assert.match(styles, /\.bv-sortable-list article>header[^}]*align-items:center/s);
});
test("window footer has stacked and overflow states instead of word columns", () => {
assert.match(windowChrome, /BvFooterActions/);
assert.match(windowChrome, /footerStackAt=700/);
assert.match(windowChrome, /stacked=\{mode==="floating"&&geometry\.width<footerStackAt\}/);
assert.match(windowChrome, /minimal=\{mode==="floating"&&geometry\.width<470\}/);
assert.match(styles, /\.bv-ui-window-footer\.is-stacked/);
assert.match(styles, /\.bv-ui-window-footer\.is-minimal/);
assert.match(styles, /\.bv-ui-window-status\s*\{[^}]*min-width:0[^}]*white-space:nowrap/s);
});
test("showcase window reference measures and demonstrates real responsive states", () => {
assert.match(showcase,/function WindowContractDemo/);
assert.match(showcase,/new ResizeObserver/);
assert.match(showcase,/stacked=\{width<700\}/);
assert.match(showcase,/minimal=\{width<470\}/);
assert.doesNotMatch(showcase,/stacked=\{false\}|minimal=\{false\}/);
});
test("save dialogs do not duplicate the title-bar close action in their footers", () => {
for (const source of [detailerView, detectorView]) {
assert.doesNotMatch(source, />Cancel<|label:"Cancel"/);
}
});
test("production chrome uses the restrained radius scale", () => {
assert.match(styles, /--bv-ui-radius-sm:calc\(2px \* var\(--bv-ui-scale\)\)/);
assert.match(styles, /--bv-ui-radius:calc\(4px \* var\(--bv-ui-scale\)\)/);
assert.match(styles, /--bv-ui-radius-lg:calc\(6px \* var\(--bv-ui-scale\)\)/);
assert.match(styles, /\.bv-managed-window\s*\{[^}]*border-radius:var\(--bv-ui-radius\)/s);
assert.match(styles, /\.bv-callout\s*\{[^}]*border-radius:var\(--bv-ui-radius-sm\)/s);
});
test("managed workspace windows follow the regional editor inset chrome", () => {
assert.match(styles, /\.bv-managed-window\.workspace\s*\{[^}]*left:24px[^}]*top:58px[^}]*width:calc\(100vw - 48px\)[^}]*height:calc\(100vh - 82px\)[^}]*border-radius:/s);
assert.match(styles, /\.bv-managed-window\s*\{[^}]*outline:0/s);
});
test("managed floating windows expose resize hit zones on every edge and corner", () => {
assert.match(windowChrome, /\["n","ne","e","se","s","sw","w","nw"\]/);
assert.match(windowChrome, /beginResize\(direction\)/);
assert.match(windowChrome, /resizeFloatingWindow\(active\.geometry,delta,active\.direction,resizeMin\)/);
assert.match(styles, /\.bv-managed-window\.floating\s*\{\s*resize:none/);
for (const direction of ["n","ne","e","se","s","sw","w","nw"]) assert.match(styles, new RegExp(`\\.bv-window-resize-handle(?:[^}]*|[^\\n]*)\\.${direction}\\b`));
});
test("custom edge resizing has no native ResizeObserver feedback path", () => {
const managedWindow = windowChrome.slice(windowChrome.indexOf("export function BvManagedWindow"), windowChrome.indexOf("export function BvWindowFooter"));
assert.doesNotMatch(managedWindow, /new ResizeObserver/);
});
test("regional toolbar uses compact framework buttons and theme-colored outline icons", () => {
assert.match(regionalToolPalette, /<Toolbar\s+orientation="vertical"[^>]*density="compact"/);
assert.match(styles, /\.bv-toolbar \.bv-button>svg\s*\{[^}]*fill:none[^}]*stroke:currentColor/s);
});
test("regional drawing toolbar remains pointer-interactive inside the passive artboard overlay", () => {
assert.match(styles, /\.artboard-tools\s*\{[^}]*pointer-events:\s*none/s);
assert.match(styles, /\.artboard-tools\s+\.bv-toolbar\s*\{[^}]*pointer-events:\s*auto/s);
});
test("active polygon input closes from a single click near its first screen point", () => {
assert.match(regionalEditor, /shouldClosePolygon\([^)]*event\.clientX[^)]*event\.clientY[^)]*\)\)\s*\{\s*finalizePolygon\(\)/s);
});
test("completion popup overrides generic button layout with compact left-aligned BV styling", () => {
assert.match(styles, /\.bv-completion-popup \.bv-button\s*\{[^}]*justify-content:stretch[^}]*justify-items:start/s);
assert.match(styles, /\.bv-completion-popup button\s*\{[^}]*color:\s*var\(--bv-ui-text\)/s);
assert.match(styles, /\.bv-completion-popup small\s*\{[^}]*color:\s*var\(--bv-ui-muted\)/s);
});
test("regional default layout separates geometric tools left from prompting right", () => {
const selectionStart = options.indexOf('activeTab !== "selection"');
const regionStart = options.indexOf('activeTab !== "region"');
const documentStart = options.indexOf('activeTab !== "document"');
const selectionPanel = options.slice(selectionStart, regionStart);
const regionPanel = options.slice(regionStart, documentStart);
assert.match(selectionPanel, /<RegionMaskAppearance\b/);
assert.match(options, /const RegionMaskAppearance[\s\S]*Display color[\s\S]*label="Feather"|const RegionMaskAppearance[\s\S]*label="Feather"[\s\S]*Display color/);
assert.doesNotMatch(regionPanel, /Display color|label="Feather"/);
assert.match(regionalEditor, /type:"row", weight:22[\s\S]*component:"regions"[\s\S]*component:"selection"/);
assert.match(regionalEditor, /type:"tabset", weight:55[^\n]*component:"regions"/);
assert.match(regionalEditor, /type:"tabset", weight:45[^\n]*component:"selection"/);
assert.match(regionalEditor, /type:"row", weight:24[\s\S]*component:"global"[\s\S]*component:"region"/);
});
test("prompt editors compose only canonical BV UI controls", () => {
assert.match(options, /const PromptEditor[\s\S]*<Accordion\b/);
const promptEditorStart = options.indexOf("const PromptEditor");
const promptEditorEnd = options.indexOf("export default", promptEditorStart);
assert.doesNotMatch(options.slice(promptEditorStart, promptEditorEnd), /<(?:details|summary|button|input|select|textarea)\b/);
for (const component of ["BvManagedWindow", "SelectField", "PromptPairFields", "Callout", "Badge", "Button"]) assert.match(quickPromptEditor, new RegExp(`<${component}\\b`));
assert.doesNotMatch(quickPromptEditor, /<(?:button|input|select|textarea|details|summary)\b/);
assert.match(quickPromptEditor, /bv-density-compact/);
assert.match(quickPromptEditor, /<BvManagedWindow[\s\S]*?allowWorkspace=\{false\}/);
assert.match(detailerView, /<BvManagedWindow\b/);
assert.match(detectorView, /<BvManagedWindow\b/);
assert.match(promptTextarea, /className="bv-textarea-shell resize-vertical"/);
assert.match(promptTextarea, /<TextareaControl\b/);
for (const source of [quickPromptEditor, detailerView, detectorView]) assert.doesNotMatch(source, /<(?:button|input|select|textarea)\b/);
});
test("selection inspector orders appearance before layer geometry and uses a two by two bounds grid", () => {
const selectionStart = options.indexOf('activeTab !== "selection"');
const regionStart = options.indexOf('activeTab !== "region"');
const selection = options.slice(selectionStart, regionStart);
assert.ok(selection.indexOf("<RegionMaskAppearance") < selection.indexOf("<section className=\"bv-inspector-section\"><h3>Layer"));
assert.match(selection, /className="bv-layer-bounds-grid"/);
assert.match(styles, /\.bv-layer-bounds-grid\s*\{[^}]*grid-template-columns:repeat\(2,minmax\(0,1fr\)\)/s);
});
test("toolbar capabilities refresh when the last supported node is removed", () => {
assert.match(uiEntry, /"BV Control Center","BV Regional Prompt"/);
assert.match(uiEntry, /nodeType\.prototype\.onRemoved=function\(\)\{const result=removed/);
assert.match(uiEntry, /queueMicrotask\(refreshBvToolbarCapabilities\)/);
});
test("window menu visibility is workflow-owned, type-defaulted and recoverable",()=>{
assert.match(windowRegistry,/bvWindowMenuVisible/);
assert.match(windowRegistry,/BV Smart Pipe/);
assert.match(windowRegistry,/BV Smart Pipe Merge/);
assert.match(windowRegistry,/setDirtyCanvas/);
assert.match(toolbarLauncher,/Show hidden nodes/);
assert.match(toolbarLauncher,/preserveLauncherFooter/);
assert.match(toolbarLauncher,/before\.bottom-after\.bottom/);
assert.match(toolbarLauncher,/suppressHideUntil/);
assert.match(toolbarLauncher,/matches\(":hover"\)/);
assert.match(toolbarLauncher,/No visible BV windows/);
assert.match(toolbarLauncher,/onReveal/);
});
test("FlexLayout profiles separate explicit user saves from session working drafts",()=>{
assert.match(layoutProfiles,/BV_LAYOUT_PROFILE_LIMIT=20/);
assert.match(layoutProfiles,/localStorage\.setItem/);
assert.match(layoutProfiles,/sessionDrafts=new Map/);
assert.match(layoutProfiles,/editorType/);
assert.match(layoutProfiles,/library/);
assert.match(layoutProfiles,/signature/);
assert.doesNotMatch(dock,/localStorage\.setItem/);
assert.match(dock,/"modified"/);
assert.match(dock,/"adjusted"/);
assert.match(windowChrome,/Save as new layout/);
assert.match(windowChrome,/isCompatibleLayoutProfile/);
assert.match(windowChrome,/Layout could not be loaded/);
assert.match(dock,/draft\?\.profileId/);
});
test("Smart Pipe editor synchronizes native predecessor state and validates required local inputs",()=>{
assert.match(smartPipeEditor,/label="Pipe predecessor"/);
assert.match(smartPipeEditor,/setPredecessor/);
assert.match(smartPipeEditor,/Required Smart Pipe inputs are missing/);
assert.doesNotMatch(smartPipeEditor,/disabled=\{validationIssues\.length>0\}/);
assert.match(smartPipeBridge,/predecessor\(node\)/);
assert.match(smartPipeBridge,/setPredecessor\(node, value\)/);
assert.match(smartPipeBridge,/localSlots \?\? \[\]\)\.filter\(\(slot\) => !slotHasInputConnection/);
});
test("Smart Pipe Merge keeps its wireless source picker usable across window widths",()=>{
assert.match(smartPipeEditor,/bv-ui-inline-footer bv-smart-pipe-merge-source/);
assert.match(styles,/\.bv-smart-pipe-merge-source>\.bv-control-field\s*\{[^}]*flex:1 1 260px[^}]*min-width:min\(260px,100%\)/s);
assert.match(smartPipeEditor,/disabled=\{!candidate\|\|!nextMergeSourceKey\(sources\)\}/);
assert.match(smartPipeEditor,/setSelected\(""\)/);
});
test("BV history shortcuts consume both keyboard phases before ComfyUI can mutate the graph",()=>{
const history = readFileSync(new URL("../ui/src/ui/history.ts", import.meta.url), "utf8");
assert.match(history,/addEventListener\("keydown",keydown,\{capture:true\}\)/);
assert.match(history,/addEventListener\("keyup",keyup,\{capture:true\}\)/);
assert.match(history,/event\.stopImmediatePropagation\(\)/);
});
test("Smart Pipe sessions survive host graph history by remapping their scoped node identity",()=>{
assert.match(smartPipeEditor,/const keyFor=\(target:Target\)=>`\$\{target\.kind\}:\$\{scopedNodeKey\(/);
assert.match(smartPipeEditor,/item\.key===targetKey/);
assert.match(smartPipeEditor,/onRemap\(currentTarget\.node\)/);
});
test("Smart Pipe navigation preserves per-node sessions and honors keep or replace",()=>{
assert.match(smartPipeEditor,/sessions\.filter\(session=>session\.key!==activeKey\)\.map\(session=><BvMinimizedWindow/);
assert.match(smartPipeEditor,/getWindowSwitchMode\(\)==="replace"/);
assert.match(smartPipeEditor,/onNavigate=\{\(key,replace\)=>/);
assert.match(smartPipeEditor,/structuredClone\(current\[currentKey\]\)/);
assert.match(smartPipeEditor,/sessionActions\.current\.get\(key\)\?\.dirty\?setPending/);
assert.match(smartPipeEditor,/<UnsavedChangesDialog[^>]+editorName="Smart Pipe editor"/);
});
test("Smart Pipe sessions absorb external links when their local draft is clean",()=>{
assert.match(smartPipeEditor,/applyingSave\.current/);
assert.match(smartPipeEditor,/if\(!history\.canUndo\)\{baseline\.current=structuredClone\(current\);history\.replace\(current,true\);setConflict\(null\);return\}/);
assert.match(smartPipeEditor,/history\.replace\(saved,true\)/);
});
test("BV histories and fallback messages remain bounded and non-persistent",()=>{
assert.match(historyController,/BV_HISTORY_LIMIT\s*=\s*100/);
assert.doesNotMatch(historyController,/localStorage|sessionStorage/);
assert.match(toastStore,/slice\(-3\)/);
assert.match(toastStore,/--bv-toast-bottom/);
assert.match(overlays,/UnsavedChangesDialog/);
assert.match(toastStore,/addEventListener\("bv-show-toast",show\)/);
assert.match(toastStore,/#graph-canvas-container \[role='toolbar'\]/);
assert.match(toastStore,/className="bv-ui bv-density-compact bv-toast-stack bv-global-toast-stack"/);
assert.match(styles,/\.bv-toast\s*\{[^}]*width:min\(360px,calc\(100vw - 24px\)\)[^}]*min-width:0/s);
assert.match(styles,/\.bv-toast p\s*\{[^}]*overflow-wrap:anywhere/s);
});
test("BV layers remain below host ComfyUI modal dialogs",()=>{
for(const token of ["--bv-layer-window:1400","--bv-layer-shelf:1410","--bv-layer-flyout:1500","--bv-layer-modal:1550","--bv-layer-toast:1600"]) assert.match(styles,new RegExp(token));
assert.doesNotMatch(styles,/z-index:\s*214748364[67]/);
assert.match(styles,/\.bv-managed-window\s*\{[^}]*z-index:var\(--bv-layer-window\)/s);
assert.match(styles,/\.bv-dialog-layer\s*\{[^}]*z-index:var\(--bv-layer-modal\)/s);
});
test("layer bounds present normalized geometry as editable canvas pixels", () => {
assert.match(options,/const axisSize=key==="x"\|\|key==="width"\?props\.canvas\.width:props\.canvas\.height/);
assert.match(options,/value=\{Math\.round\(props\.bounds!\[key\]\*axisSize\)\}/);
assert.match(options,/max=\{axisSize\}/);
assert.match(options,/step=\{1\}/);
assert.match(options,/unit="px"/);
assert.match(options,/\[key\]: value\/axisSize/);
assert.doesNotMatch(options,/label=\{key\.toUpperCase\(\)\}[^>]*slider=\{false\}/);
});
test("regional LUT planner uses the shared sortable job contract", () => {
assert.match(lutPlanView, /SortableItem/);
assert.match(lutPlanView, /<SortableList\b/);
assert.match(lutPlanView, /onReorder=/);
assert.match(lutPlanView, /draft\.jobs=next\.map/);
assert.doesNotMatch(lutPlanView, /<div className="bv-card-list"><Accordion/);
});
test("regional easy mode renders global LUT settings in the Global panel", () => {
assert.match(regionalEditor, /<OptionsPanel mode=\{mode\}[^\n]*lutEasyGlobalEditor=\{globalLutSettings\}/);
assert.match(regionalEditor, /title:"Global"[^\n]+content:optionsPanel\("document"\)/);
assert.match(options, /<ToolTabs label="Global tools"[^\n]+content:props\.lutEasyGlobalEditor/);
});
test("LUT Registry opens the shared download manager and adopts downloaded LUTs", () => {
assert.match(lutRegistryView, /useSyncExternalStore\(lutLibrary\.subscribe,lutLibrary\.getSnapshot,lutLibrary\.getSnapshot\)/);
assert.match(lutRegistryView, /lutLibrary\.seed\(catalog\.luts\)/);
assert.doesNotMatch(lutRegistryView, /setAvailableLuts|const downloaded=/);
assert.match(lutRegistryView, /<Button onClick=\{\(\)=>openLutDownloadDialog\(\)\}>Download LUTs<\/Button>/);
});
test("projected-port resizing follows the owning graph canvas instead of a stale app canvas",()=>{
assert.doesNotMatch(uiEntry,/bvPointerGestureActive/);
assert.match(uiEntry,/node\?\.graph\?\.list_of_graphcanvas/);
assert.match(uiEntry,/appCanvas\?\.constructor\?\.active_canvas/);
assert.match(uiEntry,/host\.LiteGraph\?\.LGraphCanvas\?\.active_canvas/);
assert.match(uiEntry,/canvas\?\.resizing_node===node/);
});