/* * Description: Static and helper regression tests for MXPIC EDA frontend/backend integration contracts. * Inside functions: N/A - assertion-based test/module script. * Developer : Qin Yue @ 2026 * Organization : OptiHK Limited */ const assert = require('assert'); const fs = require('fs'); const path = require('path'); const root = path.resolve(__dirname, '..'); const canvasHtml = fs.readFileSync(path.join(root, 'frontend', 'canvas.html'), 'utf8'); const canvasHelpers = fs.readFileSync(path.join(root, 'frontend', 'canvas-helpers.js'), 'utf8'); assert( canvasHtml.includes('Build GDS'), 'Project Tree header should include a Build GDS button' ); assert( canvasHtml.includes('Save YAML for all canvases') && canvasHtml.includes('handleSaveProjectLayouts'), 'Project Tree should include a save button that writes YAML for all canvases' ); assert( canvasHtml.includes('/api/build-gds'), 'Build GDS button should call the backend build-gds API' ); assert( canvasHtml.includes(':layout'), 'Build Layout should open an SVG preview tab named like canvas_1:layout' ); assert( canvasHtml.includes('svg_url'), 'Build Layout should use the backend svg_url response' ); assert( canvasHtml.includes('result.preview_error') && canvasHtml.includes('Preview skipped: '), 'Build Layout should log when the backend saves YAML but skips SVG preview because the router stack is unavailable' ); assert( canvasHtml.includes('layoutPreview'), 'canvas pages should support a layoutPreview tab type' ); assert( canvasHtml.includes('LayoutSvgPreview'), 'layout preview tabs should use the auto-scaling SVG viewer' ); assert( canvasHtml.includes('layoutScale'), 'layout SVG preview should expose an editable scale value' ); assert( canvasHtml.includes('objectFit: \'contain\''), '100% layout preview scale should fit the full SVG within the screen' ); assert( canvasHtml.includes('className="build-gds-btn"'), 'Build GDS should use a dedicated polished button class' ); assert( canvasHtml.includes('buildGdsBusy'), 'Build GDS should expose an in-progress state to prevent duplicate requests' ); assert( canvasHtml.includes('Build GDS network error'), 'Build GDS fetch failures should produce a specific network diagnostic' ); assert( canvasHtml.includes('className="build-layout-btn"'), 'Build Layout should use the polished primary action class' ); assert( canvasHtml.includes('Route Editor'), 'Selecting an edge should expose a route editor' ); assert( canvasHtml.includes('selectedEdge'), 'canvas should track selected edges separately from selected nodes' ); assert( canvasHtml.includes('technologyManifest'), 'canvas should load the selected technology manifest' ); assert( canvasHtml.includes('standard_bend'), 'route editor should offer standard_bend as a routing type' ); assert( canvasHtml.includes('findSameTypeRouteCrossing'), 'canvas should validate same-xsection route crossings' ); assert( canvasHtml.includes('link-mode-tabs') && canvasHtml.includes('link-mode-summary') && canvasHtml.includes('link-mode-menu') && canvasHtml.includes('currentLinkXsection') && canvasHtml.includes('setCurrentLinkXsection') && canvasHtml.includes("['strip', 'rib_low', 'metal_1', 'metal_2']"), 'canvas should expose a collapsed route-type selector for new links' ); assert( canvasHtml.includes('handleBasicConnection') && canvasHtml.includes('onConnect={handleBasicConnection}') && canvasHtml.includes('nodesConnectable={true}') && canvasHtml.includes('connectionMode="loose"') && canvasHtml.includes('const conflict = findSameTypeRouteCrossing(candidate, activePage.edges, nodeMap, technologyManifest);') && canvasHtml.includes('Connection rejected:') && canvasHtml.includes('data: { route }') && canvasHtml.includes('addEdge(candidate, p.edges)'), 'canvas should use React Flow native pin-to-pin connections and reject same-xsection crossings for new links' ); assert( !canvasHtml.includes('linkDraft') && !canvasHtml.includes('routingMode') && !canvasHtml.includes('toggleRoutingMode') && !canvasHtml.includes('handleLinkCanvasMouseDown') && !canvasHtml.includes('handleLinkCanvasMouseMove') && !canvasHtml.includes('finalizeLinkDraft') && !canvasHtml.includes('__link_draft_edge__') && !canvasHtml.includes('Routing mode: click anywhere to start a point route.'), 'current interactive point-link drawing mode should be removed from the canvas' ); assert( canvasHtml.includes('= 2') && canvasHtml.includes('points.slice(1).map(point => `L ${point.x},${point.y}`)') && canvasHtml.includes('position: { x: first.x - 6, y: first.y - 6 }') && canvasHtml.includes('position: { x: last.x - 6, y: last.y - 6 }'), 'free routes should render all saved point-to-point line segments and keep hidden endpoints aligned' ); assert( !canvasHtml.includes('buildOrthogonalPoints') && !canvasHtml.includes('buildManhattanRoutePoints') && !canvasHtml.includes('findNearestPort') && !canvasHtml.includes('linkPreviewSnapPort'), 'custom link drawing geometry helpers should be deleted from the current canvas code' ); const routePointNodeBlock = canvasHtml.slice( canvasHtml.indexOf('const RulerPointNode'), canvasHtml.indexOf('const RulerMeasurementNode') ); assert( routePointNodeBlock.includes('