Engineering Journal
Schema Editor
Schema Editor

Our Test Corpus Was All Imports, So Authoring Was Broken for Months

2026-07-17

TLDR

Our schematic analysis engine was tested exclusively against a corpus of imported SVG files. The suite passed 100% of tests for months. However, when users manually placed and wired components on the canvas, the analysis engine reported all components as disconnected. It came down to how each path produces coordinates. Imported SVGs arrive pre-flattened (coordinates baked into paths, zero transforms), whereas live-authored components use container transforms (translate(x,y)). Adding automated browser E2E interactive testing resolved the blind spot.
Asset SourceCoordinate StructureElement Bounds Query (getBBox)Test Suite Status
Imported FilesPre-flattened document coordinatesMatches Document SpacePassed (Masked Defect)
Live Authored CanvasLocal origin + Ancestor TransformsLocal bounds (Fails matching)Failed in Production

Problem statement: the blind spot of single-source corpora

Our connectivity engine extracts component pin ports, identifies wire endpoints, and generates electrical netlists.

During development, we validated the engine using a suite of 50+ imported SVG schematics. The test suite passed consistently.

However, when an author created a new diagram by dragging symbols from the palette and connecting them manually, the linter reported 0/2 pins connected for every component.


Technical failure mode: flattened vs. Transformed geometry

Imported files and live-authored symbols produce structurally different DOM trees:

  1. Imported Files: SVG converters flatten geometry. Transforms are baked directly into path commands. getBBox() returns absolute document coordinates.
  2. Live Canvas Symbols: Palette placement inserts <g transform="translate(500, 300)"> containers holding local symbol paths centered at $(0,0)$.
The connectivity engine evaluated bounds using getBBox() without transforming local coordinates:

// DEFECTIVE IMPLEMENTATION: Un-transformed getBBox()
const symbolBounds = componentElement.getBBox(); // Returns local bounds {-32, -32, 64, 64}
const wireEndpoint = { x: 530, y: 300 };          // Global document coordinate

// Matching fails for all live-authored symbols!

Because the test corpus consisted exclusively of pre-flattened files, the test suite never executed transformed DOM paths.


The fix & architecture: CTM matrix projections & E2E browser testing

1. Unified matrix walk projection

We updated the port matcher to project local symbol pins through ancestor transform chains into Document Space:
// REFACTORED: Project local pins to document space
function getGlobalPinPositions(symbolGroup, cameraGroup) {
  const worldMatrix = getAncestorMatrix(symbolGroup, cameraGroup);

return Array.from(symbolGroup.querySelectorAll('.pin-marker')).map(pin => { const lx = parseFloat(pin.getAttribute('cx') || 0); const ly = parseFloat(pin.getAttribute('cy') || 0); const globalPt = new DOMPoint(lx, ly).matrixTransform(worldMatrix);

return { id: pin.dataset.pinId, x: globalPt.x, y: globalPt.y }; }); }

2. Browser E2E authoring tests

We introduced automated browser E2E tests (Playwright) that simulate real user gestures (drag symbol from palette, place at coordinate, draw connecting wire, assert netlist validity):
test('Live Authoring Interaction Pipeline Validation', async ({ page }) => {
  await page.goto('/editor');
  await page.dragAndDrop('#palette-resistor', '#canvas', { targetPosition: { x: 500, y: 300 } });
  await page.dragAndDrop('#palette-capacitor', '#canvas', { targetPosition: { x: 700, y: 300 } });
  await drawWireBetweenPins(page, 'resistor_pin1', 'capacitor_pin1');

const netlist = await page.evaluate(() => window.editor.getNetlist()); expect(netlist.nets.length).toBe(1); // Asserts live authoring parity! });

Rule of thumb: Never rely solely on static file import corpora to test graphic engines. Validate both imported static files and live-authored interactive DOM structures.
Read this post in the full Engineering Journal →