import { describe, it, expect } from 'vitest' import { dia, shapes } from '@joint/core' import { PipelineNode, createNode, createLink, applyNodeValues, applyEdgeValues, portMedium, cellValues } from '@diagram/shapes' import { canConnectMedia, type MediumId } from '@diagram/relations' const cellNamespace = { ...shapes, pipeline: { Node: PipelineNode } } const makeGraph = (): dia.Graph => new dia.Graph({}, { cellNamespace }) describe('node creation', () => { it('creates a pump node with its ports and medium metadata', () => { const node = createNode('pump', 100, 100) expect(node.get('kind')).toBe('node') expect(node.get('symbolId')).toBe('pump') expect(node.getPorts().map((p) => p.id).sort()).toEqual(['in', 'out']) expect(portMedium(node, 'in')).toBe('water') }) it('applies title, size and rotation from values', () => { const node = createNode('valve', 0, 0) applyNodeValues(node, { ...cellValues(node), title: 'V-101', width: 90, height: 60, rotation: 90 }) expect(node.attr('label/text')).toBe('V-101') expect(node.size()).toEqual({ width: 90, height: 60 }) expect(node.angle()).toBe(90) }) it('hides the label when showLabel is no', () => { const node = createNode('tank', 0, 0) applyNodeValues(node, { ...cellValues(node), showLabel: 'no', title: 'T' }) expect(node.attr('label/text')).toBe('') }) }) describe('connection rule (port media)', () => { it('permits compatible ports and blocks incompatible ones', () => { const pump = createNode('pump', 0, 0) const sensor = createNode('sensor', 0, 0) const water = portMedium(pump, 'out') as MediumId const signal = portMedium(sensor, 'sig') as MediumId expect(canConnectMedia(water, water)).toBe(true) expect(canConnectMedia(water, signal)).toBe(false) }) }) describe('edge creation', () => { it('creates a styled link and applies markers', () => { const link = createLink({ id: 'a', port: 'out' }, { id: 'b', port: 'in' }) expect(link.get('kind')).toBe('edge') applyEdgeValues(link, { ...cellValues(link), color: '#ff0000', headMarker: 'diamond', strokeWidth: 5 }) expect(link.attr('line/stroke')).toBe('#ff0000') expect(link.attr('line/strokeWidth')).toBe(5) expect(link.attr('line/targetMarker')).toMatchObject({ type: 'path' }) }) }) describe('serialization round-trip', () => { it('preserves nodes, links, ports and values', () => { const g1 = makeGraph() const a = createNode('reservoir', 40, 40) const b = createNode('outlet', 300, 40) a.addTo(g1) b.addTo(g1) const link = createLink({ id: a.id as string, port: 'out' }, { id: b.id as string, port: 'in' }) link.addTo(g1) applyNodeValues(a, { ...cellValues(a), title: 'Main reservoir' }) const json = JSON.stringify(g1.toJSON()) const g2 = makeGraph() g2.fromJSON(JSON.parse(json)) expect(g2.getElements().length).toBe(2) expect(g2.getLinks().length).toBe(1) const restored = g2.getCell(a.id) as dia.Element expect((restored.get('values') as Record).title).toBe('Main reservoir') expect(restored.getPort('out')).toBeTruthy() expect(portMedium(restored, 'out')).toBe('water') }) })