## Summary See https://typescript-eslint.io/blog/project-service/ for context. Creates a browser_tests specific tsconfig so that they can be linted. Does not add a package.json script to do the linting yet, but `pnpm exec eslint browser_tests` should work for now. ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-5633-lint-add-tsconfig-for-browser_tests-fix-existing-violations-2726d73d3650819d8ef2c4b0abc31e14) by [Unito](https://www.unito.io)
Subgraph Testing Fixtures and Utilities
This directory contains the testing infrastructure for LiteGraph's subgraph functionality. These utilities provide a consistent, easy-to-use API for writing subgraph tests.
What is a Subgraph?
A subgraph in LiteGraph is a graph-within-a-graph that can be reused as a single node. It has:
- Input slots that map to an internal input node
- Output slots that map to an internal output node
- Internal nodes and connections
- The ability to be instantiated multiple times as SubgraphNode instances
Quick Start
// Import what you need
import { createTestSubgraph, assertSubgraphStructure } from "./fixtures/subgraphHelpers"
import { subgraphTest } from "./fixtures/subgraphFixtures"
// Option 1: Create a subgraph manually
it("should do something", () => {
const subgraph = createTestSubgraph({
name: "My Test Subgraph",
inputCount: 2,
outputCount: 1
})
// Test your functionality
expect(subgraph.inputs).toHaveLength(2)
})
// Option 2: Use pre-configured fixtures
subgraphTest("should handle events", ({ simpleSubgraph, eventCapture }) => {
// simpleSubgraph comes pre-configured with 1 input, 1 output, and 2 nodes
expect(simpleSubgraph.inputs).toHaveLength(1)
// Your test logic here
})
Files Overview
subgraphHelpers.ts - Core Helper Functions
Main Factory Functions:
createTestSubgraph(options?)- Creates a fully configured Subgraph instance with root graphcreateTestSubgraphNode(subgraph, options?)- Creates a SubgraphNode (instance of a subgraph)createNestedSubgraphs(options?)- Creates nested subgraph hierarchies for testing deep structures
Assertion & Validation:
assertSubgraphStructure(subgraph, expected)- Validates subgraph has expected inputs/outputs/nodesverifyEventSequence(events, expectedSequence)- Ensures events fired in correct orderlogSubgraphStructure(subgraph, label?)- Debug helper to print subgraph structure
Test Data & Events:
createTestSubgraphData(overrides?)- Creates raw ExportedSubgraph data for serialization testscreateComplexSubgraphData(nodeCount?)- Generates complex subgraph with internal connectionscreateEventCapture(eventTarget, eventTypes)- Sets up event monitoring with automatic cleanup
subgraphFixtures.ts - Vitest Fixtures
Pre-configured test scenarios that automatically set up and tear down:
Basic Fixtures (subgraphTest):
emptySubgraph- Minimal subgraph with no inputs/outputs/nodessimpleSubgraph- 1 input ("input": number), 1 output ("output": number), 2 internal nodescomplexSubgraph- 3 inputs (data, control, text), 2 outputs (result, status), 5 nodesnestedSubgraph- 3-level deep hierarchy with 2 nodes per levelsubgraphWithNode- Complete setup: subgraph definition + SubgraphNode instance + parent grapheventCapture- Subgraph with event monitoring for all I/O events
Edge Case Fixtures (edgeCaseTest):
circularSubgraph- Two subgraphs set up for circular reference testingdeeplyNestedSubgraph- 50 levels deep for performance/limit testingmaxIOSubgraph- 20 inputs and 20 outputs for stress testing
testSubgraphs.json - Sample Test Data
Pre-defined subgraph configurations for consistent testing across different scenarios.
Note on Static UUIDs: The hardcoded UUIDs in this file (e.g., "simple-subgraph-uuid", "complex-subgraph-uuid") are intentionally static to ensure test reproducibility and snapshot testing compatibility.
Usage Examples
Basic Test Creation
import { describe, expect, it } from "vitest"
import { createTestSubgraph, assertSubgraphStructure } from "./fixtures/subgraphHelpers"
describe("My Subgraph Feature", () => {
it("should work correctly", () => {
const subgraph = createTestSubgraph({
name: "My Test",
inputCount: 2,
outputCount: 1,
nodeCount: 3
})
assertSubgraphStructure(subgraph, {
inputCount: 2,
outputCount: 1,
nodeCount: 3,
name: "My Test"
})
// Your specific test logic...
})
})
Using Fixtures
import { subgraphTest } from "./fixtures/subgraphFixtures"
subgraphTest("should handle events", ({ eventCapture }) => {
const { subgraph, capture } = eventCapture
subgraph.addInput("test", "number")
expect(capture.events).toHaveLength(2) // adding-input, input-added
})
Event Testing
import { createEventCapture, verifyEventSequence } from "./fixtures/subgraphHelpers"
it("should fire events in correct order", () => {
const subgraph = createTestSubgraph()
const capture = createEventCapture(subgraph.events, ["adding-input", "input-added"])
subgraph.addInput("test", "number")
verifyEventSequence(capture.events, ["adding-input", "input-added"])
capture.cleanup() // Important: clean up listeners
})
Nested Structure Testing
import { createNestedSubgraphs } from "./fixtures/subgraphHelpers"
it("should handle deep nesting", () => {
const nested = createNestedSubgraphs({
depth: 5,
nodesPerLevel: 2
})
expect(nested.subgraphs).toHaveLength(5)
expect(nested.leafSubgraph.nodes).toHaveLength(2)
})
Common Patterns
Testing SubgraphNode Instances
it("should create and configure a SubgraphNode", () => {
// First create the subgraph definition
const subgraph = createTestSubgraph({
inputs: [{ name: "value", type: "number" }],
outputs: [{ name: "result", type: "number" }]
})
// Then create an instance of it
const subgraphNode = createTestSubgraphNode(subgraph, {
pos: [100, 200],
size: [180, 100]
})
// The SubgraphNode will have matching slots
expect(subgraphNode.inputs).toHaveLength(1)
expect(subgraphNode.outputs).toHaveLength(1)
expect(subgraphNode.subgraph).toBe(subgraph)
})
Complete Test with Parent Graph
subgraphTest("should work in a parent graph", ({ subgraphWithNode }) => {
const { subgraph, subgraphNode, parentGraph } = subgraphWithNode
// Everything is pre-configured and connected
expect(parentGraph.nodes).toContain(subgraphNode)
expect(subgraphNode.graph).toBe(parentGraph)
expect(subgraphNode.subgraph).toBe(subgraph)
})
Configuration Options
createTestSubgraph(options)
interface TestSubgraphOptions {
id?: UUID // Custom UUID
name?: string // Custom name
nodeCount?: number // Number of internal nodes
inputCount?: number // Number of inputs (uses generic types)
outputCount?: number // Number of outputs (uses generic types)
inputs?: Array<{ // Specific input definitions
name: string
type: ISlotType
}>
outputs?: Array<{ // Specific output definitions
name: string
type: ISlotType
}>
}
Note: Cannot specify both inputs array and inputCount (or outputs array and outputCount) - the function will throw an error with details.
createNestedSubgraphs(options)
interface NestedSubgraphOptions {
depth?: number // Nesting depth (default: 2)
nodesPerLevel?: number // Nodes per subgraph (default: 2)
inputsPerSubgraph?: number // Inputs per subgraph (default: 1)
outputsPerSubgraph?: number // Outputs per subgraph (default: 1)
}
Important Architecture Notes
Subgraph vs SubgraphNode
- Subgraph: The definition/template (like a class definition)
- SubgraphNode: An instance of a subgraph placed in a graph (like a class instance)
- One Subgraph can have many SubgraphNode instances
Special Node IDs
- Input node always has ID
-10(SUBGRAPH_INPUT_ID) - Output node always has ID
-20(SUBGRAPH_OUTPUT_ID) - These are virtual nodes that exist in every subgraph
Common Pitfalls
-
Array vs Index: The
inputsandoutputsarrays don't have anindexproperty on items. UseindexOf():// ❌ Wrong expect(input.index).toBe(0) // ✅ Correct expect(subgraph.inputs.indexOf(input)).toBe(0) -
Graph vs Subgraph Property: SubgraphInputNode/OutputNode have
subgraph, notgraph:// ❌ Wrong expect(inputNode.graph).toBe(subgraph) // ✅ Correct expect(inputNode.subgraph).toBe(subgraph) -
Event Detail Structure: Events have specific detail structures:
// Input events "adding-input": { name: string, type: string } "input-added": { input: SubgraphInput, index: number } // Output events "adding-output": { name: string, type: string } "output-added": { output: SubgraphOutput, index: number } -
Links are stored in a Map: Use
.sizenot.length:// ❌ Wrong expect(subgraph.links.length).toBe(1) // ✅ Correct expect(subgraph.links.size).toBe(1)
Testing Best Practices
- Always use helper functions instead of manual setup
- Use fixtures for common scenarios to avoid repetitive code
- Clean up event listeners with
capture.cleanup()after event tests - Use
verifyEventSequence()to test event ordering - Remember fixtures are created fresh for each test (no shared state)
- Use
assertSubgraphStructure()for comprehensive validation
Debugging Tips
- Use
logSubgraphStructure(subgraph)to print subgraph details - Check
subgraph.rootGraphto verify graph hierarchy - Event capture includes timestamps for debugging timing issues
- All factory functions accept optional parameters for customization
Adding New Test Utilities
When extending the test infrastructure:
- Add new helper functions to
subgraphHelpers.ts - Add new fixtures to
subgraphFixtures.ts - Update this README with usage examples
- Follow existing patterns for consistency
- Add TypeScript types for all parameters
Performance Notes
- Helper functions are optimized for test clarity, not performance
- Use
structuredClone()for deep copying test data - Event capture systems automatically clean up listeners
- Fixtures are created fresh for each test to avoid state contamination