import { describe, expect, it } from 'vitest' import { computeColumnLayout, toCytoscapeElements } from '@/services/graph/graph-layout' const nodes = [ { id: 'product:1', kind: 'product', label: 'Panela' }, { id: 'supplier:5', kind: 'supplier', label: 'La Mesa' }, { id: 'organization:3', kind: 'organization', label: 'Red Solidaria' }, ] const edges = [ { from: 'product:1', to: 'supplier:5' }, { from: 'supplier:5', to: 'organization:3' }, ] function columnOf (node) { return { product: 0, supplier: 1, organization: 2 }[node.kind] } describe('computeColumnLayout', () => { it('ubica cada nodo en su columna según la función columnOf', () => { const result = computeColumnLayout(nodes, edges, { columnOf, columnWidth: 200, rowHeight: 80, padding: 20 }) const product = result.nodes.find(n => n.id === 'product:1') const supplier = result.nodes.find(n => n.id === 'supplier:5') const organization = result.nodes.find(n => n.id === 'organization:3') expect(product.x).toBeLessThan(supplier.x) expect(supplier.x).toBeLessThan(organization.x) expect(product.x).toBeCloseTo(20 + 0 * 200 + 100) expect(supplier.x).toBeCloseTo(20 + 1 * 200 + 100) expect(organization.x).toBeCloseTo(20 + 2 * 200 + 100) }) it('separa verticalmente los nodos que comparten columna y conserva el orden', () => { const many = [ { id: 'supplier:1', kind: 'supplier', label: 'A' }, { id: 'supplier:2', kind: 'supplier', label: 'B' }, ] const result = computeColumnLayout(many, [], { columnOf, columnWidth: 200, rowHeight: 80, padding: 20 }) const a = result.nodes.find(n => n.id === 'supplier:1') const b = result.nodes.find(n => n.id === 'supplier:2') expect(a.y).toBeCloseTo(20 + 80 / 2) expect(b.y).toBeCloseTo(20 + 80 + 80 / 2) expect(a.y).toBeLessThan(b.y) }) it('calcula ancho y alto del lienzo según columnas y filas usadas', () => { const result = computeColumnLayout(nodes, edges, { columnOf, columnWidth: 200, rowHeight: 80, padding: 20 }) expect(result.width).toBeCloseTo(20 * 2 + 3 * 200) expect(result.height).toBeCloseTo(20 * 2 + 80) }) it('respeta el orden de aparición de los nodos', () => { const result = computeColumnLayout(nodes, edges, { columnOf, columnWidth: 200, rowHeight: 80, padding: 20 }) expect(result.nodes.map(n => n.id)).toEqual(['product:1', 'supplier:5', 'organization:3']) }) }) describe('toCytoscapeElements', () => { it('convierte nodos y aristas al formato elements de cytoscape', () => { const layout = computeColumnLayout(nodes, edges, { columnOf, columnWidth: 200, rowHeight: 80, padding: 20 }) const elements = toCytoscapeElements(layout) const productNode = elements.find(e => e.data.id === 'product:1') expect(productNode).toMatchObject({ position: { x: productNode.position.x, y: productNode.position.y }, classes: ['product'], data: { id: 'product:1', kind: 'product', label: 'Panela' }, }) expect(typeof productNode.position.x).toBe('number') expect(typeof productNode.position.y).toBe('number') const edge = elements.find(e => e.data.source === 'product:1') expect(edge).toMatchObject({ data: { source: 'product:1', target: 'supplier:5' } }) }) it('incluye datos extra del nodo en data.entity', () => { const withEntity = [ { id: 'product:1', kind: 'product', label: 'Panela', entity: { name: 'Panela', price: 3000 } }, ] const layout = computeColumnLayout(withEntity, [], { columnOf, columnWidth: 200, rowHeight: 80, padding: 20 }) const elements = toCytoscapeElements(layout) expect(elements[0].data.entity).toEqual({ name: 'Panela', price: 3000 }) }) it('agrega la clase has-image a los nodos con imagen', () => { const withImage = [ { id: 'product:1', kind: 'product', label: 'Panela', image: 'http://x/panela.jpg' }, ] const layout = computeColumnLayout(withImage, [], { columnOf, columnWidth: 200, rowHeight: 80, padding: 20 }) const elements = toCytoscapeElements(layout) expect(elements[0].classes).toContain('has-image') }) })