import { Graph } from '../../../src'; import '../../../src/behavior'; import { scale, translate } from '../../../src/util/math'; import { GraphData, Item } from '../../../src/types'; import Plugin from '../../../src/plugins'; import { timerOut } from '../util/timeOut'; const div = document.createElement('div'); div.id = 'global-spec'; document.body.appendChild(div); describe('graph', () => { const globalGraph = new Graph({ container: div, width: 500, height: 500, modes: { default: ['drag-node'], }, }); it('invalid container', () => { expect(() => { // eslint-disable-next-line no-new new Graph({} as any); }).toThrowError('invalid container'); }); it('new & destroy graph', () => { const inst = new Graph({ container: div, width: 500, height: 500, modes: { default: ['drag-node'], }, }); const length = div.childNodes.length; expect(inst).not.toBe(undefined); expect(inst instanceof Graph).toBe(true); expect(length > 1).toBe(true); expect(inst.get('canvas')).not.toBe(undefined); expect(inst.get('group')).not.toBe(undefined); expect(inst.get('group').get('className')).toEqual('root-container'); expect( inst .get('group') .get('id') .endsWith('-root'), ).toBe(true); const children = inst.get('group').get('children'); expect(children.length).toBe(4); expect(children[1].get('className')).toEqual('edge-container'); expect(children[0].get('className')).toEqual('custom-group-container'); const nodes = inst.getNodes(); expect(nodes).not.toBe(undefined); expect(nodes.length).toBe(0); const edges = inst.getEdges(); expect(edges).not.toBe(undefined); expect(edges.length).toBe(0); const canvas = inst.get('canvas'); inst.destroy(); expect(inst.destroyed).toBe(true); expect(canvas.destroyed).toBe(true); expect(length - div.childNodes.length).toBe(1); }); it('render without data', () => { const inst = new Graph({ container: div, width: 500, height: 500, }); inst.data(null); expect(() => { inst.render(); }).toThrowError('data must be defined first'); }); it('groupByTypes is false & toDataURL', () => { const inst = new Graph({ container: div, width: 500, height: 500, groupByTypes: false, }); const data = { nodes: [ { id: 'node1', label: 'node1', }, { id: 'node2', }, ], edges: [ { id: 'edge1', source: 'node1', target: 'node2', }, { id: 'edge2', source: 'node1', target: 'node1', }, { id: 'edge3', source: 'node2', target: 'node2', }, ], }; inst.data(data); inst.render(); const nodeGroup = inst.get('nodeGroup'); const edgeGroup = inst.get('edgeGroup'); expect(nodeGroup).toBe(undefined); expect(edgeGroup).toBe(undefined); const node = inst.findById('node1'); const edge = inst.findById('edge1'); const group1 = node.get('group').getParent(); const group2 = edge.get('group').getParent(); expect(group1).toEqual(group2); const url = inst.toDataURL(); expect(url).not.toBe(null); }); it('translate', () => { const canvasMatrix = globalGraph.get('canvas').getMatrix(); globalGraph.translate(100, 100); const matrix = globalGraph.get('group').getMatrix(); expect(canvasMatrix).toBe(null); expect(matrix[6]).toBe(100); expect(matrix[7]).toBe(100); globalGraph.get('group').resetMatrix(); }); it('moveTo', () => { let group = globalGraph.get('group'); expect(group.get('x')).toBe(undefined); expect(group.get('y')).toBe(undefined); globalGraph.moveTo(100, 100); group = globalGraph.get('group'); const matrix = globalGraph.get('group').getMatrix(); expect(matrix).not.toBe(null); expect(group.get('x')).toBe(100); expect(group.get('y')).toBe(100); globalGraph.get('group').resetMatrix(); }); it('zoom', () => { globalGraph.zoom(3, { x: 100, y: 100 }); const matrix = globalGraph.get('group').getMatrix(); expect(matrix[0]).toBe(3); expect(matrix[4]).toBe(3); expect(matrix[6]).toBe(-200); expect(matrix[7]).toBe(-200); expect(globalGraph.getZoom()).toBe(3); globalGraph.get('group').resetMatrix(); }); it('minZoom & maxZoom', () => { const graph = new Graph({ container: div, minZoom: 2, maxZoom: 5, width: 500, height: 500, }); const data = { nodes: [ { id: 'node', }, ], }; graph.data(data); graph.render(); let matrix = graph.get('group').getMatrix(); expect(matrix).toBe(null); graph.zoom(0.5, { x: 100, y: 100 }); matrix = graph.get('group').getMatrix(); expect(matrix).toBe(null); graph.zoom(5.5); matrix = graph.get('group').getMatrix(); expect(matrix).toBe(null); }); it('zoomTo', () => { let matrix = globalGraph.get('group').getMatrix(); expect(matrix).toBe(null); globalGraph.zoomTo(2); matrix = globalGraph.get('group').getMatrix(); expect(matrix[0]).toBe(2); expect(matrix[4]).toBe(2); expect(matrix[6]).toBe(0); expect(matrix[7]).toBe(0); globalGraph.zoomTo(1.5, { x: 250, y: 250 }); matrix = globalGraph.get('group').getMatrix(); expect(matrix[0]).toBe(1.5); expect(matrix[4]).toBe(1.5); expect(matrix[6]).toBe(62.5); expect(matrix[7]).toBe(62.5); }); it('change size', () => { const graph = new Graph({ container: div, width: 500, height: 500, }); expect(graph.get('width')).toBe(500); expect(graph.get('height')).toBe(500); expect(typeof graph.changeSize).toEqual('function'); graph.changeSize(300, 300); expect(graph.get('width')).toBe(300); expect(graph.get('height')).toBe(300); // 专门用于测试使用非 number 类型 会报错的情况 // TODO 可以移走这个测试, TS 本身就限制了类型参数 // expect(() => { // graph.changeSize('x', 10); // }).toThrowError( // 'invalid canvas width & height, please make sure width & height type is number', // ); graph.destroy(); }); it('getCurrentMode', () => { const mode = globalGraph.getCurrentMode(); expect(mode).toBe('default'); }); it('data & changeData & save', () => { const data = { nodes: [ { id: 'a', type: 'circle', color: '#333', x: 30, y: 30, size: 20, label: 'a', }, { id: 'b', type: 'ellipse', color: '#666', x: 50, y: 60, size: [30, 40], label: 'b', }, { id: 'c', type: 'rect', color: '#999', x: 100, y: 70, size: 20, label: 'c', }, ], edges: [ { source: 'a', target: 'b', id: 'd', }, { source: 'a', target: 'c', id: 'e', }, ], }; globalGraph.data(data); globalGraph.render(); expect(globalGraph.get('nodes').length).toBe(3); expect(globalGraph.get('edges').length).toBe(2); let map = globalGraph.get('itemMap'); expect(map.a).not.toBe(undefined); expect(map.b).not.toBe(undefined); expect(map.c).not.toBe(undefined); expect(map.d).not.toBe(undefined); const edges = globalGraph.getEdges(); expect(edges.length).toBe(2); const nodes = globalGraph.getNodes(); expect(nodes.length).toBe(3); expect(map.e).not.toBe(undefined); data.nodes.splice(0, 1); data.edges.splice(0, 1); data.edges[0].source = 'b'; data.nodes.push({ id: 'f', type: 'circle', color: '#333', x: 100, y: 80, size: 30, label: 'f', }); globalGraph.changeData(data); map = globalGraph.get('itemMap'); expect(globalGraph.get('nodes').length).toBe(3); expect(globalGraph.get('edges').length).toBe(1); expect(map.a).toBe(undefined); expect(map.b).not.toBe(undefined); expect(map.c).not.toBe(undefined); expect(map.d).toBe(undefined); expect(map.e).not.toBe(undefined); expect(map.f).not.toBe(undefined); const exported: GraphData = globalGraph.save() as GraphData; // expect(JSON.stringify(exported)).not.to.throw; expect(exported.nodes.length).toBe(3); expect(exported.edges.length).toBe(1); const edge = exported.edges[0]; expect(edge.id).toBe('e'); expect(edge.source).toBe('b'); expect(edge.target).toBe('c'); }); it('change data with null', () => { const data = { nodes: [ { id: 'a', type: 'circle', color: '#333', x: 30, y: 30, size: 20, label: 'a', }, { id: 'b', type: 'ellipse', color: '#666', x: 50, y: 60, size: [30, 40], label: 'b', }, { id: 'c', type: 'rect', color: '#999', x: 100, y: 70, size: 20, label: 'c', }, ], edges: [ { source: 'a', target: 'b', id: 'd', }, { source: 'a', target: 'c', id: 'e', }, ], }; globalGraph.data(data); globalGraph.render(); const newData = null; const nodeNumBeforeChange = globalGraph.getNodes().length; globalGraph.changeData(newData); const nodeNumAfterChange = globalGraph.getNodes().length; expect(nodeNumBeforeChange).toBe(nodeNumAfterChange); }); it('change data with animate', () => { const data = { nodes: [ { id: 'a', type: 'circle', color: '#333', x: 30, y: 30, size: 20, label: 'a', }, { id: 'b', type: 'ellipse', color: '#666', x: 50, y: 60, size: [30, 40], label: 'b', }, { id: 'c', type: 'rect', color: '#999', x: 100, y: 70, size: 20, label: 'c', }, ], edges: [ { source: 'a', target: 'b', id: 'd', }, { source: 'a', target: 'c', id: 'e', }, ], }; globalGraph.data(data); globalGraph.render(); globalGraph.set('animate', true); data.nodes[0].x = 100; data.nodes[0].y = 100; globalGraph.changeData(data); const nodeModel = globalGraph.getNodes()[0].getModel(); expect(nodeModel.x).toBe(100); expect(nodeModel.y).toBe(100); }); it('find', () => { globalGraph.clear(); globalGraph.addItem('node', { id: 'node', x: 50, y: 100, size: 50, className: 'test test2' }); const item = globalGraph.addItem('node', { id: 'node2', x: 100, y: 100, size: 50, className: 'test', }); const findNode = globalGraph.find('node', (node: any) => node.get('model').x === 100); expect(findNode).not.toBe(undefined); expect(findNode).toEqual(item); }); it('findAll', () => { globalGraph.clear(); const node1 = globalGraph.addItem('node', { id: 'node', x: 100, y: 100, size: 50, className: 'test test2', }); const node2 = globalGraph.addItem('node', { id: 'node2', x: 100, y: 100, size: 50, className: 'test', }); const node3 = globalGraph.addItem('node', { id: 'node2', x: 100, y: 100, size: 50 }); node1.setState('active', true); node2.setState('selected', true); node3.setState('active', true); let nodes = globalGraph.findAllByState('node', 'active'); expect(nodes.length).toEqual(2); expect(nodes[0]).toEqual(node1); expect(nodes[1]).toEqual(node3); nodes = globalGraph.findAllByState('node', 'selected'); expect(nodes.length).toEqual(1); expect(nodes[0]).toEqual(node2); }); it('refresh positions', () => { const data = { id: 'node', x: 100, y: 50, size: 50, className: 'test test2' }; const node = globalGraph.addItem('node', data); const group = node.get('group'); expect(group.getMatrix()[6]).toBe(100); expect(group.getMatrix()[7]).toBe(50); data.x = 50; data.y = 100; globalGraph.refreshPositions(); expect(group.getMatrix()[6]).toBe(50); expect(group.getMatrix()[7]).toBe(100); }); it('removeItem', () => { let removeNode = globalGraph.findById('remove-item'); expect(removeNode).toBe(undefined); const data = { id: 'remove-item', x: 10, y: 50, size: 50, className: 'test test2' }; const node = globalGraph.addItem('node', data); expect(node).not.toBe(undefined); globalGraph.removeItem('remove-item'); removeNode = globalGraph.findById('remove-item'); expect(removeNode).toBe(undefined); }); it('canvas point & model point convert', () => { const group = globalGraph.get('group'); let point = globalGraph.getPointByCanvas(100, 100); expect(point.x).toBe(100); expect(point.y).toBe(100); translate(group, { x: 100, y: 100, }); point = globalGraph.getPointByCanvas(100, 100); expect(point.x).toBe(0); expect(point.y).toBe(0); scale(group, [1.5, 1.5]); point = globalGraph.getPointByCanvas(100, 100); expect(point.x).toBe(-33.33333333333334); expect(point.y).toBe(-33.33333333333334); group.resetMatrix(); point = globalGraph.getCanvasByPoint(100, 100); expect(point.x).toBe(100); expect(point.y).toBe(100); translate(group, { x: 100, y: 100, }); point = globalGraph.getCanvasByPoint(0, 0); expect(point.x).toBe(100); expect(point.y).toBe(100); group.resetMatrix(); }); it('client point & model point convert', () => { const group = globalGraph.get('group'); const bbox = globalGraph .get('canvas') .get('el') .getBoundingClientRect(); let point = globalGraph.getPointByClient(bbox.left + 100, bbox.top + 100); expect(point.x).toBe(100); expect(point.y).toBe(100); translate(group, { x: 100, y: 100, }); point = globalGraph.getPointByClient(bbox.left + 100, bbox.top + 100); expect(point.x).toBe(0); expect(point.y).toBe(0); scale(group, [1.5, 1.5]); point = globalGraph.getPointByClient(bbox.left + 100, bbox.top + 100); expect(point.x).toBe(-33.33333333333334); expect(point.y).toBe(-33.33333333333334); group.resetMatrix(); point = globalGraph.getClientByPoint(100, 100); expect(point.x).toBe(bbox.left + 100); expect(point.y).toBe(bbox.top + 100); translate(group, { x: 100, y: 100, }); point = globalGraph.getClientByPoint(100, 100); expect(point.x).toBe(bbox.left + 200); expect(point.y).toBe(bbox.top + 200); }); it('clear', () => { globalGraph.destroy(); expect(globalGraph.destroyed).toBe(true); }); }); describe('all node link center', () => { const graph = new Graph({ container: div, width: 500, height: 500, linkCenter: true, }); it('init', () => { expect(graph.get('linkCenter')).toBe(true); graph.data({ nodes: [ { id: '1', x: 10, y: 10, }, { id: '2', x: 100, y: 100, }, ], edges: [{ id: 'e1', source: '1', target: '2' }], }); graph.render(); const edge = graph.findById('e1'); expect(edge.get('keyShape').attr('path')).toEqual([ ['M', 10, 10], ['L', 100, 100], ]); }); it('loop', () => { graph.set('linkCenter', false); const node = graph.addItem('node', { id: 'circleNode', x: 150, y: 150, style: { fill: 'yellow' }, anchorPoints: [ [0, 0], [0, 1], ], }); const edge1 = graph.addItem('edge', { id: 'edge', source: node, target: node, type: 'loop', loopCfg: { position: 'top', dist: 60, clockwise: true, }, style: { endArrow: true }, }); const edge2 = graph.addItem('edge', { id: 'edge1', source: node, target: node, type: 'loop', loopCfg: { position: 'top-left', dist: 60, clockwise: false, }, style: { endArrow: true }, }); const edge3 = graph.addItem('edge', { id: 'edge2', source: node, target: node, type: 'loop', loopCfg: { position: 'top-right', dist: 60, }, style: { endArrow: true }, }); const edge4 = graph.addItem('edge', { id: 'edge4', source: node, target: node, type: 'loop', loopCfg: { position: 'right', dist: 60, clockwise: true, }, style: { endArrow: true }, }); const edgeWithAnchor = graph.addItem('edge', { id: 'edge5', source: node, target: node, type: 'loop', sourceAnchor: 0, targetAnchor: 1, loopCfg: { position: 'bottom-right', dist: 60, clockwise: true, }, style: { endArrow: true }, }); graph.addItem('edge', { id: 'edge6', source: node, target: node, type: 'loop', loopCfg: { position: 'bottom', dist: 60, clockwise: true, }, style: { endArrow: true }, }); graph.addItem('edge', { id: 'edge7', source: node, target: node, type: 'loop', loopCfg: { position: 'bottom-left', dist: 60, clockwise: true, }, style: { endArrow: true }, }); graph.addItem('edge', { id: 'edge8', source: node, target: node, type: 'loop', loopCfg: { position: 'left', dist: 60, clockwise: true, }, style: { endArrow: true }, }); const edgeShape = edge1.getKeyShape().attr('path'); const edge2Shape = edge2.getKeyShape().attr('path'); expect(edge2Shape[0][1]).toEqual(edgeShape[0][1]); expect(edge2Shape[0][2]).toEqual(edgeShape[0][2]); expect(edge3.getKeyShape().attr('path')[1][0]).toEqual('C'); expect(edge3.getKeyShape().attr('path')[0][1]).toEqual(edgeShape[1][5]); expect(edge4.getKeyShape().attr('path')[0][1]).toEqual(edge3.getKeyShape().attr('path')[1][5]); expect(edge4.getKeyShape().attr('path')[0][2]).toEqual(edge3.getKeyShape().attr('path')[1][6]); const pathWithAnchor = edgeWithAnchor.getKeyShape().attr('path'); expect(pathWithAnchor[0][1]).toEqual(139.5); expect(pathWithAnchor[0][2]).toEqual(139.5); expect(pathWithAnchor[1][0]).toEqual('C'); expect(pathWithAnchor[1][5]).toEqual(139.5); expect(pathWithAnchor[1][6]).toEqual(160.5); }); it('clear states', () => { graph.clear(); const node = graph.addItem('node', { id: 'a', x: 50, y: 100, size: 50 }); graph.setItemState(node, 'a', true); graph.setItemState(node, 'b', true); expect(graph.findAllByState('node', 'a').length).toBe(1); graph.clearItemStates(node); expect(graph.findAllByState('node', 'a').length).toBe(0); expect(graph.findAllByState('node', 'b').length).toBe(0); graph.setItemState(node, 'a', true); graph.setItemState(node, 'b', true); graph.clearItemStates('a', ['a']); expect(graph.findAllByState('node', 'a').length).toBe(0); expect(graph.findAllByState('node', 'b').length).toBe(1); graph.clearItemStates(node, 'b'); expect(graph.findAllByState('node', 'b').length).toBe(0); }); it('default node & edge style', () => { const defaultGraph = new Graph({ container: div, width: 500, height: 500, defaultNode: { style: { fill: 'red', stroke: 'blue', }, }, nodeStateStyles: { default: { fill: 'red', stroke: 'blue', }, selected: { fill: 'green', stroke: 'red', }, }, defaultEdge: { style: { stroke: 'blue', strokeOpacity: 0.5, }, }, edgeStateStyles: { default: { stroke: 'blue', strokeOpacity: 0.5, }, selected: { stroke: 'red', strokeOpacity: 1, }, active: { stroke: 'green', shadowColor: '#ccc', }, }, }); const node = defaultGraph.addItem('node', { id: 'node1', x: 100, y: 100, type: 'rect', label: 'test label', style: { stroke: '#666', }, }); defaultGraph.on('node:click', e => { e.item.setState('selected', true); e.item.refresh(); }); defaultGraph.paint(); const keyShape = node.get('keyShape'); expect(keyShape.get('type')).toEqual('rect'); expect(keyShape.attr('fill')).toEqual('red'); expect(keyShape.attr('stroke')).toEqual('#666'); defaultGraph.setItemState(node, 'selected', true); expect(keyShape.attr('fill')).toEqual('green'); expect(keyShape.attr('fillStyle')).toBe(undefined); expect(keyShape.attr('stroke')).toEqual('red'); expect(keyShape.attr('strokeStyle')).toBe(undefined); defaultGraph.setItemState(node, 'selected', false); expect(keyShape.attr('fill')).toEqual('red'); expect(keyShape.attr('fillStyle')).toBe(undefined); expect(keyShape.attr('stroke')).toEqual('#666'); expect(keyShape.attr('strokeStyle')).toBe(undefined); defaultGraph.updateItem(node, { style: { fill: '#ccc', stroke: '#444' } }); expect(keyShape.attr('fill')).toEqual('#ccc'); defaultGraph.setItemState(node, 'selected', true); expect(keyShape.attr('fill')).toEqual('green'); expect(keyShape.attr('fillStyle')).toBe(undefined); expect(keyShape.attr('stroke')).toEqual('red'); expect(keyShape.attr('strokeStyle')).toBe(undefined); defaultGraph.setItemState(node, 'selected', false); expect(keyShape.attr('fill')).toEqual('#ccc'); expect(keyShape.attr('fillStyle')).toBe(undefined); expect(keyShape.attr('stroke')).toEqual('#444'); expect(keyShape.attr('strokeStyle')).toBe(undefined); defaultGraph.addItem('node', { id: 'node2' }); const edge = defaultGraph.addItem('edge', { id: 'edge', source: node, target: 'node2' }); const edgeKeyShape = edge.get('keyShape'); expect(edgeKeyShape.attr('stroke')).toEqual('blue'); expect(edgeKeyShape.attr('strokeOpacity')).toEqual(0.5); defaultGraph.setItemState(edge, 'selected', true); expect(edgeKeyShape.attr('stroke')).toEqual('red'); expect(edgeKeyShape.attr('strokeOpacity')).toEqual(1); defaultGraph.setItemState(edge, 'selected', false); expect(edgeKeyShape.attr('stroke')).toEqual('blue'); expect(edgeKeyShape.attr('strokeOpacity')).toEqual(0.5); // 测试default状态不存在的属性 expect(edgeKeyShape.attr('shadowColor')).toBe(undefined); defaultGraph.setItemState(edge, 'active', true); expect(edgeKeyShape.attr('stroke')).toEqual('green'); expect(edgeKeyShape.attr('shadowColor')).toEqual('#ccc'); defaultGraph.setItemState(edge, 'active', false); expect(edgeKeyShape.attr('stroke')).toEqual('blue'); expect(edgeKeyShape.attr('shadowColor')).toBe(null); defaultGraph.destroy(); }); it('graph with default cfg', () => { const defaultGraph = new Graph({ container: div, width: 500, height: 500, defaultNode: { type: 'rect', size: [60, 40], color: '#ccc', labelCfg: { position: 'right', offset: 5, style: { fontSize: 14, fill: 'blue', }, }, }, defaultEdge: { type: 'cubic', color: '#666', }, }); const node = defaultGraph.addItem('node', { id: 'node1', x: 100, y: 150, label: '111' }); let model = node.get('model'); expect(model.id).toEqual('node1'); expect(model.x).toEqual(100); expect(model.y).toEqual(150); expect(model.type).toEqual('rect'); expect(model.size[0]).toEqual(60); expect(model.size[1]).toEqual(40); expect(model.color).toEqual('#ccc'); expect(model.labelCfg.position).toEqual('right'); expect(model.labelCfg.style.fill).toEqual('blue'); const node2 = defaultGraph.addItem('node', { id: 'node2', x: 150, y: 100, label: '222', color: '#666', type: 'circle', }); model = node2.get('model'); expect(model.type).toEqual('circle'); expect(model.size[0]).toEqual(60); expect(model.size[1]).toEqual(40); expect(model.color).toEqual('#666'); model.size[1] = 50; expect(model.size[1]).toEqual(50); expect(node.get('model').size[1]).toEqual(40); expect(model.labelCfg.position).toEqual('right'); expect(model.labelCfg.style.fill).toEqual('blue'); model.labelCfg.position = 'left'; model.labelCfg.style.fill = 'red'; expect(node.get('model').labelCfg.position).toEqual('right'); expect(node.get('model').labelCfg.style.fill).toEqual('blue'); const edge = defaultGraph.addItem('edge', { id: 'edge', source: 'node1', target: 'node2', type: 'line', }); model = edge.get('model'); expect(model.id).toEqual('edge'); expect(model.source).toEqual('node1'); expect(model.type).toEqual('line'); expect(model.color).toEqual('#666'); defaultGraph.destroy(); expect(defaultGraph.destroyed).toBe(true); }); }); describe('mapper fn', () => { const graph = new Graph({ container: div, width: 500, height: 500, defaultNode: { type: 'circle', style: { fill: 'red', opacity: 1, }, }, }); it('node & edge mapper', () => { graph.node(node => ({ id: `${node.id}Mapped`, size: [30, 30], label: node.id, type: 'rect', style: { fill: node.value === 100 ? '#666' : '#ccc' }, labelCfg: { style: { fill: '#666' }, }, })); graph.edge(edge => ({ id: `edge${edge.id}`, label: edge.id, labelCfg: { position: 'start', }, style: { fill: '#ccc', opacity: 0.5, }, })); const node: Item = graph.addItem('node', { id: 'node', x: 100, y: 100, value: 100 }); expect(node.get('id')).toEqual('nodeMapped'); let keyShape = node.getKeyShape(); expect(keyShape.attr('width')).toEqual(30); expect(keyShape.attr('height')).toEqual(30); expect(keyShape.attr('fill')).toEqual('#666'); const container = node.getContainer(); let label = container.find(element => element.get('className') === 'node-label'); expect(label).not.toBe(undefined); expect(label.attr('text')).toEqual('node'); expect(label.attr('fill')).toEqual('#666'); graph.addItem('node', { id: 'node2', x: 200, y: 200 }); const edge = graph.addItem('edge', { id: 'edge', source: 'nodeMapped', target: 'node2Mapped' }); keyShape = edge.getKeyShape(); expect(keyShape.attr('fill')).toEqual('#ccc'); expect(keyShape.attr('opacity')).toEqual(0.5); expect(keyShape.get('type')).toEqual('path'); label = edge.getContainer().find(element => element.get('className') === 'edge-label'); expect(label).not.toBe(undefined); expect(label.attr('text')).toEqual('edge'); expect(label.attr('x')).toEqual(115.5); expect(label.attr('y')).toEqual(100); graph.updateItem(node, { value: 50 }); expect(node.getKeyShape().attr('fill')).toEqual('#ccc'); }); it('node & edge mapper with states', () => { graph.node(node => ({ type: 'rect', label: node.id, style: { fill: '#666', opacity: 1, }, stateStyles: { selected: { fill: 'blue' }, custom: { fill: 'green', opacity: 0.5 }, }, })); graph.edge(() => ({ stateStyles: { selected: { lineWidth: 2 }, custom: { opacity: 0.5 }, }, })); const node = graph.addItem('node', { id: 'node', x: 50, y: 50 }); let keyShape = node.getKeyShape(); expect(keyShape.attr('fill')).toEqual('#666'); expect(node.getContainer().find(element => element.get('className') === 'node-label')).not.toBe( undefined, ); graph.setItemState(node, 'selected', true); expect(keyShape.attr('blue')); graph.setItemState(node, 'custom', true); expect(keyShape.attr('green')); graph.clearItemStates(node); expect(keyShape.attr('fill')).toEqual('#666'); const edge = graph.addItem('edge', { id: 'edge2', source: 'node', target: 'node2Mapped' }); keyShape = edge.getKeyShape(); expect(keyShape.attr('stroke')).toEqual('#e2e2e2'); expect(keyShape.attr('lineWidth')).toEqual(1); expect(keyShape.attr('fillOpacity')).toEqual(1); graph.setItemState(edge, 'selected', true); expect(keyShape.attr('stroke')).toEqual('#e2e2e2'); expect(keyShape.attr('lineWidth')).toEqual(2); expect(keyShape.attr('fillOpacity')).toEqual(1); graph.setItemState(edge, 'custom', true); expect(keyShape.attr('stroke')).toEqual('#e2e2e2'); expect(keyShape.attr('lineWidth')).toEqual(2); expect(keyShape.attr('opacity')).toEqual(0.5); }); }); describe('plugins & layout', () => { it('add & remove plugins', () => { const graph = new Graph({ container: div, height: 500, width: 500, }); const data = { nodes: [ { id: 'node', label: 'node', }, ], }; graph.data(data); graph.render(); let plugins = graph.get('plugins'); expect(plugins.length).toBe(0); const minimap = new Plugin.Minimap({ size: [200, 200], }); graph.addPlugin(minimap); plugins = graph.get('plugins'); expect(plugins.length).toBe(1); graph.removePlugin(minimap); plugins = graph.get('plugins'); expect(plugins.length).toBe(0); graph.destroy(); expect(graph.destroyed).toBe(true); }); it('graph animate', () => { const graph = new Graph({ container: div, height: 500, width: 500, }); const data = { nodes: [ { id: 'node', label: 'node', groupId: 'g1', }, { id: 'node1', groupId: 'g2', }, ], groups: [ { id: 'g1', title: 'cokkdl', }, { id: 'g2', }, ], }; graph.data(data); graph.render(); graph.stopAnimate(); const isAnimating = graph.isAnimating(); expect(isAnimating).toBe(false); graph.collapseGroup('g1'); let gnode = graph.findById('node'); expect(gnode.get('visible')).toBe(false); const g2Node = graph.findById('node1'); expect(g2Node.get('visible')).toBe(true); graph.expandGroup('g1'); timerOut(() => { gnode = graph.findById('node'); expect(gnode.get('visible')).toBe(true); }, 500); }); }); describe('auto rotate label on edge', () => { const graph = new Graph({ container: div, width: 500, height: 500, modes: { default: ['drag-node', 'zoom-canvas', 'drag-canvas'], }, }); const data = { nodes: [ { id: 'node1', x: 50, y: 50, }, { id: 'node2', x: 80, y: 150, }, { id: 'node3', x: 180, y: 120, }, ], edges: [ { source: 'node1', target: 'node2', label: 'node1-node2', style: { startArrow: true, endArrow: true, }, labelCfg: { autoRotate: true, }, }, { source: 'node2', target: 'node3', label: 'node2-node3', style: { startArrow: true, endArrow: true, }, }, ], }; it('render', () => { graph.data(data); graph.render(); const edge1 = graph.getEdges()[0]; const label1 = edge1.get('group').get('children')[1]; const label1Matrix = label1.attr('matrix'); expect(label1Matrix[0]).toBe(0.2873478855664496); expect(label1Matrix[1]).toBe(0.9578262852211201); expect(label1Matrix[3]).toBe(-0.9578262852211201); expect(label1Matrix[4]).toBe(0.2873478855664496); expect(label1Matrix[6]).toBe(142.10501596029277); expect(label1Matrix[7]).toBe(9.006502903982238); const edge2 = graph.getEdges()[1]; const label2 = edge2.get('group').get('children')[1]; const label2Matrix = label2.attr('matrix'); expect(label2Matrix).toBe(null); }); it('drag node', () => { const node = graph.getNodes()[1]; graph.emit('node:dragstart', { x: 80, y: 150, item: node }); graph.emit('node:drag', { x: 200, y: 200, item: node }); graph.emit('node:dragend', { x: 200, y: 200, item: node }); const edge1 = graph.getEdges()[0]; const label1 = edge1.get('group').get('children')[1]; const label1Matrix = label1.attr('matrix'); expect(label1Matrix[0]).toBe(0.7071067811865476); expect(label1Matrix[1]).toBe(0.7071067811865475); expect(label1Matrix[3]).toBe(-0.7071067811865475); expect(label1Matrix[4]).toBe(0.7071067811865476); expect(label1Matrix[6]).toBe(124.99999999999999); expect(label1Matrix[7]).toBe(-51.77669529663689); const edge2 = graph.getEdges()[1]; const label2 = edge2.get('group').get('children')[1]; const label2Matrix = label2.attr('matrix'); expect(label2Matrix).toBe(null); }); it('zoom and pan', () => { graph.zoom(0.5); graph.moveTo(100, 120); const group = graph.get('group'); const groupMatrix = group.attr('matrix'); expect(groupMatrix[0]).toBe(0.5); expect(groupMatrix[4]).toBe(0.5); expect(groupMatrix[6]).toBe(100); expect(groupMatrix[7]).toBe(120); }); });