// Headless tooltip guard for the `aura graph` viewer (graph-viewer.js). // // Property protected: the INFO maps genDot populates (INFO.B for a node body, // INFO.C for an expanded composite's cluster frame, INFO.P for a port) and the // md() markdown-to-HTML step that turns an INFO entry into the #tip div's // innerHTML all keep their CURRENT exact string shape. Before this guard the DOT // half of the viewer (normalizeModel/genDot output) was covered by the sibling // viewer_*.mjs suite, but the INFO population (addInfo, the composite-cluster // caption) and md() were asserted by no test — a silent wording/markup change // there would reach the browser tooltip undetected. // // It loads the real viewer module and drives the exported pure `genDot` (for // INFO.B/INFO.C/INFO.P) and `md` (for the markdown step). `node` is REQUIRED: a // skipped guard is not a guard. import { createRequire } from "node:module"; import { fileURLToPath } from "node:url"; import { dirname, join } from "node:path"; const require = createRequire(import.meta.url); const here = dirname(fileURLToPath(import.meta.url)); // One composite ("sub", holding a primitive "double") + one root-level // primitive ("gauge"), wired as: root.nodes = { "0": , "1": // }. Small enough that every INFO entry below is traceable by // hand; big enough to cover collapsed-composite, expanded-composite, and // primitive INFO shapes in one fixture. const model = { root: { nodes: { "0": { comp: "sub" }, "1": { prim: { name: "g", type: "Mul2", role: "node", params: [["k", "f64"]], bound: [], ins: [["f64", "any", "a"]], outs: [["y", "f64"]], } }, }, edges: [], }, composites: { sub: { inputs: [["f64", "any", "x"]], outputs: [["y", "f64"]], nodes: { "0": { prim: { name: "double", type: "Mul2", role: "node", params: [], bound: [], ins: [["f64", "any", "a"]], outs: [["y", "f64"]], } }, }, edges: [], }, }, }; // COMP (used by genDot to resolve a composite reference by name) is captured at // require time from window.AURA_MODEL — mirrors the browser bootstrap and the // sibling viewer_nested_depth.mjs guard. global.window = { AURA_MODEL: model }; const { normalizeModel, genDot, md } = require(join(here, "..", "assets", "graph-viewer.js")); const ROOT = normalizeModel(model).root; const fail = (msg) => { console.error(msg); process.exit(1); }; const expect = (actual, expected, msg) => { if (actual !== expected) { fail(`${msg}\n--- expected ---\n${JSON.stringify(expected)}\n--- actual ---\n${JSON.stringify(actual)}`); } }; // --- Property 1: collapsed composite — INFO.B carries type/role + params line. { const { info } = genDot(ROOT, "root", new Set(), false); expect( info.B["n0"], "**sub** · composite\nparams: none", "collapsed composite INFO.B[n0] shape changed" ); } // --- Property 2: expanded composite — INFO.C carries the cluster caption. { const { info } = genDot(ROOT, "root", new Set(["n0"]), false); expect( info.C["clust_n0"], "**sub** · composite · click frame to collapse", "expanded composite INFO.C[clust_n0] shape changed" ); } // --- Property 3: primitive node — INFO.B body line + INFO.P port entries. { const { info } = genDot(ROOT, "root", new Set(), false); expect( info.B["n1"], "**Mul2** · node\nparams: `k:f64`", "primitive INFO.B[n1] shape changed" ); expect(info.P["P_n1_i0"], "**a** · `f64`", "primitive input port INFO.P[P_n1_i0] shape changed"); expect(info.P["P_n1_o0"], "**y** · `f64`", "primitive output port INFO.P[P_n1_o0] shape changed"); } // --- Property 4: md() — the markdown-to-HTML step the #tip div consumes. { expect(md("**x**\n`y`"), "x
y", "md() markdown conversion changed"); // End-to-end: md() applied to the real INFO.B[n1] string from Property 3 — // bold the type, wrap the param signature in , turn the newline into //
, and leave the hand-authored markup untouched // (md() only rewrites **, `` ` ``, and \n — it does not touch raw HTML). const { info } = genDot(ROOT, "root", new Set(), false); expect( md(info.B["n1"]), "Mul2 · node
params: k:f64", "md() applied to a real INFO.B entry changed shape" ); } // --- #125: doc'd composite + root ------------------------------------------- // A minimal model whose composite def and root both carry a "doc"; pins that // the doc reaches INFO.B (collapsed), INFO.C (expanded), and root.doc. // // genDot resolves a nested composite reference via the module-scope COMP // captured once at require time from window.AURA_MODEL (see the file-top // comment above the `model`/`ROOT` constants and the sibling // viewer_nested_depth.mjs guard's identical note) — reusing the // already-required module's genDot would resolve "sub" back to THIS file's // original doc-less "sub" composite of the same name, not docModel's. Fresh- // require the module against docModel so its own COMP reflects the doc'd // composite. const docModel = { root: { nodes: { "0": { comp: "sub" } }, edges: [], doc: "root rationale line", }, composites: { sub: { inputs: [["f64", "any", "a"]], outputs: [["y", "f64"]], nodes: {}, edges: [], doc: "why this sub-graph is wired this way", }, }, }; const viewerPath = join(here, "..", "assets", "graph-viewer.js"); delete require.cache[require.resolve(viewerPath)]; global.window = { AURA_MODEL: docModel }; const { normalizeModel: normalizeModelDoc, genDot: genDotDoc } = require(viewerPath); const docNorm = normalizeModelDoc(docModel); expect(docNorm.root.doc, "root rationale line", "root doc carried"); expect(docNorm.composites.sub.doc, "why this sub-graph is wired this way", "composite doc carried"); { const { info } = genDotDoc(docNorm.root, "root", new Set(), false); expect( info.B["n0"], "**sub** · composite\nparams: none\nwhy this sub-graph is wired this way", "collapsed tooltip carries the doc paragraph" ); } { const { info } = genDotDoc(docNorm.root, "root", new Set(["n0"]), false); expect( info.C["clust_n0"], "**sub** · composite · click frame to collapse\nwhy this sub-graph is wired this way", "expanded cluster tooltip carries the doc paragraph" ); } console.log("OK — INFO.B/INFO.C/INFO.P tooltip shapes and md() conversion pinned."); process.exit(0);