@ndbx/runtime
Version:
The `@ndbx/runtime` package provides a runtime environment to embed NodeBox visualizations directly into React applications. NodeBox is a powerful tool for creating interactive and generative visualizations, and this runtime allows you to integrate those
144 lines (131 loc) • 4.81 kB
text/typescript
import { expect, it } from "vitest";
import {
Context,
Network,
evaluateItem,
evaluateNetwork,
findRenderedNodeOutputs,
getValue,
PortType,
PortValue,
getNetworkOutput,
ParameterType,
WidgetType,
createRuntimeNodeForItem,
evaluateRuntimeNode,
} from "../src";
import {
connectNodeToNode,
connectNodeToOutlet,
createNode,
createOutlet,
setRenderedNode,
setValue,
markDirty,
publishPortToOutlet,
markItemParameterDirty,
} from "../src/mutation.js";
import { createTestContext } from "./util.js";
function networkNumberIn(network: Network, name: string, defaultValue: number) {
network.parameters.push({
name,
type: ParameterType.Number,
widget: WidgetType.Number,
label: name,
defaultValue,
min: 0,
max: 10,
step: 1,
});
}
async function expectNetworkResult(
cx: Context,
network: Network,
values: Record<string, number>,
portName: string,
expectedResult: PortValue,
) {
for (const [key, value] of Object.entries(values)) {
markItemParameterDirty(cx, network, key);
}
await evaluateNetwork(cx, network, values);
const result = getNetworkOutput(cx, network, portName);
expect(result).toEqual(expectedResult);
}
it("can eval a network with an outlet", async () => {
const { cx, network } = createTestContext();
const value1 = createNode(cx, network, "test/math/value");
setValue(cx, network, value1, "value", { type: "VALUE", value: 5 });
const negate1 = createNode(cx, network, "test/math/negate");
const valueFn = cx.lookupItemById(value1.fn);
const negateFn = cx.lookupItemById(negate1.fn);
const valueOut = valueFn.outputPorts[0];
const negateIn = negateFn.inputPorts[0];
const negateOut = negateFn.outputPorts[0];
connectNodeToNode(cx, network, value1, valueOut, negate1, negateIn);
expect(network.outputPorts).toEqual([]);
publishPortToOutlet(cx, network, negate1, negateOut);
expect(network.outputPorts).toEqual([{ name: "out", type: PortType.Table }]);
const valueBeforeRender = getNetworkOutput(cx, network, "out");
expect(valueBeforeRender).toBeUndefined();
// We explicitly don't set a renderedNode.
await evaluateNetwork(cx, network);
const value = getNetworkOutput(cx, network, "out");
expect(value).toEqual([{ value: -5 }]);
});
it("can eval a network with parameters", async () => {
const { cx, network } = createTestContext();
const value1 = createNode(cx, network, "test/math/value");
setValue(cx, network, value1, "value", { type: "EXPRESSION", expression: "network.valueFromNetwork" });
const valueFn = cx.lookupItemById(value1.fn);
const valueOut = valueFn.outputPorts[0];
publishPortToOutlet(cx, network, value1, valueOut);
networkNumberIn(network, "valueFromNetwork", 5);
{
// Test without providing values
await evaluateNetwork(cx, network);
const result = getNetworkOutput(cx, network, "out");
expect(result).toEqual([{ value: 5 }]);
}
markItemParameterDirty(cx, network, "valueFromNetwork");
{
// Test with providing values
await evaluateNetwork(cx, network, { valueFromNetwork: 42 });
const result = getNetworkOutput(cx, network, "out");
expect(result).toEqual([{ value: 42 }]);
}
});
it("can do dirty propagation", async () => {
const { cx, network } = createTestContext();
const value1 = createNode(cx, network, "test/math/value");
setValue(cx, network, value1, "value", { type: "EXPRESSION", expression: "network.foo" });
const valueFn = cx.lookupItemById(value1.fn);
const valueOut = valueFn.outputPorts[0];
publishPortToOutlet(cx, network, value1, valueOut);
networkNumberIn(network, "foo", 5);
await expectNetworkResult(cx, network, { foo: 1234 }, "out", [{ value: 1234 }]);
await expectNetworkResult(cx, network, { foo: 1111 }, "out", [{ value: 1111 }]);
});
it.skip("can support embedded values", async () => {
const { cx, network } = createTestContext();
const value1 = createNode(cx, network, "test/math/value");
setValue(cx, network, value1, "value", { type: "EXPRESSION", expression: "network.valueFromNetwork" });
const valueFn = cx.lookupItemById(value1.fn);
const valueOut = valueFn.outputPorts[0];
publishPortToOutlet(cx, network, value1, valueOut);
network.parameters.push({
name: "valueFromNetwork",
type: ParameterType.Number,
widget: WidgetType.Number,
label: "Value From Network",
defaultValue: 5,
min: 0,
max: 10,
step: 1,
});
const results = evaluateItem(cx, "self/self/test", network, { valueFromNetwork: 42 });
const runtimeNode = await createRuntimeNodeForItem(cx, "self/self/test", "self/self/test");
await evaluateRuntimeNode(runtimeNode, { valueFromNetwork: { type: "VALUE", value: 42 } });
const outPort = runtimeNode.outputPorts[0];
expect(outPort.value).toEqual(42);
});