/usr/share/grafana/public/app/plugins/panel/nodeGraph
import { act, renderHook } from '@testing-library/react'; import { useLayout } from './layout'; import { EdgeDatum, NodeDatum } from './types'; let onmessage: jest.Mock; let postMessage: jest.Mock; let terminate: jest.Mock; let worker: { onmessage: (e: MessageEvent) => void; postMessage: (d: unknown) => void; terminate: () => void; }; jest.mock('./createLayoutWorker', () => { return { __esModule: true, createWorker: () => { onmessage = jest.fn(); postMessage = jest.fn(); terminate = jest.fn(); worker = { onmessage: onmessage, postMessage: postMessage, terminate: terminate, }; return worker; }, createMsaglWorker: () => { onmessage = jest.fn(); postMessage = jest.fn(); terminate = jest.fn(); worker = { onmessage: onmessage, postMessage: postMessage, terminate: terminate, }; return worker; }, }; }); describe('layout', () => { it('doesnt fail without any data', async () => { const nodes: NodeDatum[] = []; const edges: EdgeDatum[] = []; const { result } = renderHook(() => { return useLayout(nodes, edges, undefined, 100, 1000); }); expect(result.current.nodes).toEqual([]); expect(result.current.edges).toEqual([]); expect(postMessage).toBeUndefined(); }); it('cancels worker', async () => { const { result, rerender } = renderHook( ({ nodes, edges }) => { return useLayout(nodes, edges, undefined, 100, 1000); }, { initialProps: { nodes: [makeNode(0, 0), makeNode(1, 1)], edges: [makeEdge(0, 1)], }, } ); expect(postMessage).toBeCalledTimes(1); // Bit convoluted but we cannot easily access the worker instance as we only export constructor so the default // export is class and we only store latest instance of the methods as jest.fn here as module local variables. // So we capture the terminate function from current worker so that when we call rerender and new worker is created // we can still access and check the method from the old one that we assume should be canceled. const localTerminate = terminate; rerender({ nodes: [], edges: [], }); expect(result.current.nodes).toEqual([]); expect(result.current.edges).toEqual([]); expect(localTerminate).toBeCalledTimes(1); }); it('correctly maps the layout data', async () => { // This specifically tests whether the mapping of data from the layout code back to the original nodes is correct. const { result, rerender } = renderHook( ({ nodes, edges }) => { return useLayout(nodes, edges, undefined, 100, 1000); }, { initialProps: { nodes: rawNodes, edges: rawEdges } } ); // Simulate response from the layout worker act(() => { worker.onmessage(new MessageEvent('message', { data: { nodes: testNodes, edges: testEdges } })); }); rerender({ nodes: rawNodes, edges: rawEdges }); expect(result.current.nodes).toMatchObject([ { id: 'otel-demo-alt/frontend', title: 'frontend' }, { id: 'otel-demo-alt/cartservice', title: 'cartservice' }, { id: 'otel-demo-alt/checkoutservice', title: 'checkoutservice' }, { id: 'otel-demo-alt/frontend-proxy', title: 'frontend-proxy' }, { id: 'otel-demo-alt/recommendationservice', title: 'recommendationservice' }, ]); }); }); function makeNode(index: number, incoming: number): NodeDatum { return { id: `n${index}`, title: `n${index}`, subTitle: '', dataFrameRowIndex: 0, incoming, arcSections: [], highlighted: false, }; } function makeEdge(source: number, target: number): EdgeDatum { return { id: `${source}-${target}`, source: 'n' + source, target: 'n' + target, mainStat: '', secondaryStat: '', dataFrameRowIndex: 0, sourceNodeRadius: 40, targetNodeRadius: 40, highlighted: false, thickness: 1, }; } const rawNodes: NodeDatum[] = [ { dataFrameRowIndex: 0, id: 'otel-demo-alt/cartservice', incoming: 1, subTitle: 'otel-demo-alt', title: 'cartservice', highlighted: false, arcSections: [], }, { dataFrameRowIndex: 1, id: 'otel-demo-alt/frontend', incoming: 1, subTitle: 'otel-demo-alt', title: 'frontend', highlighted: false, arcSections: [], }, { dataFrameRowIndex: 2, id: 'otel-demo-alt/checkoutservice', incoming: 1, subTitle: 'otel-demo-alt', title: 'checkoutservice', highlighted: false, arcSections: [], }, { dataFrameRowIndex: 3, id: 'otel-demo-alt/frontend-proxy', incoming: 1, subTitle: 'otel-demo-alt', title: 'frontend-proxy', highlighted: false, arcSections: [], }, { dataFrameRowIndex: 4, id: 'otel-demo-alt/recommendationservice', incoming: 1, subTitle: 'otel-demo-alt', title: 'recommendationservice', highlighted: false, arcSections: [], }, ]; const rawEdges = [ { id: 'otel-demo-alt/frontend_otel-demo-alt/cartservice', dataFrameRowIndex: 0, source: 'otel-demo-alt/frontend', target: 'otel-demo-alt/cartservice', sourceNodeRadius: 40, targetNodeRadius: 40, mainStat: '1.08 ms/r', secondaryStat: '0.93 r/sec', highlighted: false, thickness: 1, }, { id: 'otel-demo-alt/frontend_otel-demo-alt/checkoutservice', dataFrameRowIndex: 1, source: 'otel-demo-alt/frontend', target: 'otel-demo-alt/checkoutservice', sourceNodeRadius: 40, targetNodeRadius: 40, mainStat: '12.66 ms/r', secondaryStat: '0.12 r/sec', highlighted: false, thickness: 1, }, { id: 'otel-demo-alt/frontend_otel-demo-alt/frontend-proxy', dataFrameRowIndex: 2, source: 'otel-demo-alt/frontend', target: 'otel-demo-alt/frontend-proxy', sourceNodeRadius: 40, targetNodeRadius: 40, mainStat: '7.45 ms/r', secondaryStat: '0.99 r/sec', highlighted: false, thickness: 1, }, { id: 'otel-demo-alt/frontend_otel-demo-alt/recommendationservice', dataFrameRowIndex: 3, source: 'otel-demo-alt/frontend', target: 'otel-demo-alt/recommendationservice', sourceNodeRadius: 40, targetNodeRadius: 40, mainStat: '13.01 ms/r', secondaryStat: '0.18 r/sec', highlighted: false, thickness: 1, }, { id: 'otel-demo-alt/frontend-proxy_otel-demo-alt/frontend', dataFrameRowIndex: 4, source: 'otel-demo-alt/frontend-proxy', target: 'otel-demo-alt/frontend', sourceNodeRadius: 40, targetNodeRadius: 40, mainStat: '7.50 ms/r', secondaryStat: '6.06 r/sec', highlighted: false, thickness: 1, }, ]; const testNodes = [ { id: 'otel-demo-alt/frontend', incoming: 1, x: -80.84375, y: -70.49999999999997, }, { id: 'otel-demo-alt/cartservice', incoming: 1, x: 80.84375, y: 211.5, }, { id: 'otel-demo-alt/checkoutservice', incoming: 1, x: 80.84375, y: 70.5, }, { id: 'otel-demo-alt/frontend-proxy', incoming: 1, x: 80.84375, y: -70.5, }, { id: 'otel-demo-alt/recommendationservice', incoming: 1, x: 80.84375, y: -211.5, }, ]; const testEdges = [ { id: 'otel-demo-alt/frontend_otel-demo-alt/cartservice', dataFrameRowIndex: 0, source: { id: 'otel-demo-alt/frontend', incoming: 1, x: -80.84375, y: -70.49999999999997, }, target: { id: 'otel-demo-alt/cartservice', incoming: 1, x: 80.84375, y: 211.5, }, sourceNodeRadius: 40, targetNodeRadius: 40, mainStat: '1.07 ms/r', secondaryStat: '1.08 r/sec', highlighted: false, thickness: 1, }, { id: 'otel-demo-alt/frontend_otel-demo-alt/checkoutservice', dataFrameRowIndex: 1, source: { id: 'otel-demo-alt/frontend', incoming: 1, x: -80.84375, y: -70.49999999999997, }, target: { id: 'otel-demo-alt/checkoutservice', incoming: 1, x: 80.84375, y: 70.5, }, sourceNodeRadius: 40, targetNodeRadius: 40, mainStat: '8.18 ms/r', secondaryStat: '0.14 r/sec', highlighted: false, thickness: 1, }, { id: 'otel-demo-alt/frontend_otel-demo-alt/frontend-proxy', dataFrameRowIndex: 2, source: { id: 'otel-demo-alt/frontend', incoming: 1, x: -80.84375, y: -70.49999999999997, }, target: { id: 'otel-demo-alt/frontend-proxy', incoming: 1, x: 80.84375, y: -70.5, }, sourceNodeRadius: 40, targetNodeRadius: 40, mainStat: '7.57 ms/r', secondaryStat: '1.14 r/sec', highlighted: false, thickness: 1, }, { id: 'otel-demo-alt/frontend_otel-demo-alt/recommendationservice', dataFrameRowIndex: 3, source: { id: 'otel-demo-alt/frontend', incoming: 1, x: -80.84375, y: -70.49999999999997, }, target: { id: 'otel-demo-alt/recommendationservice', incoming: 1, x: 80.84375, y: -211.5, }, sourceNodeRadius: 40, targetNodeRadius: 40, mainStat: '8.60 ms/r', secondaryStat: '0.22 r/sec', highlighted: false, thickness: 1, }, { id: 'otel-demo-alt/frontend-proxy_otel-demo-alt/frontend', dataFrameRowIndex: 4, source: { id: 'otel-demo-alt/frontend-proxy', incoming: 1, x: 80.84375, y: -70.5, }, target: { id: 'otel-demo-alt/frontend', incoming: 1, x: -80.84375, y: -70.49999999999997, }, sourceNodeRadius: 40, targetNodeRadius: 40, mainStat: '7.53 ms/r', secondaryStat: '6.82 r/sec', highlighted: false, thickness: 1, }, ];
.
Edit
..
Edit
Edge.tsx
Edit
EdgeArrowMarker.tsx
Edit
EdgeLabel.tsx
Edit
Legend.test.tsx
Edit
Legend.tsx
Edit
Marker.tsx
Edit
Node.test.tsx
Edit
Node.tsx
Edit
NodeGraph.test.tsx
Edit
NodeGraph.tsx
Edit
NodeGraphPanel.tsx
Edit
README.md
Edit
ViewControls.tsx
Edit
createLayoutWorker.ts
Edit
editor
Edit
forceLayout.js
Edit
img
Edit
layeredLayout.js
Edit
layeredLayout.test.ts
Edit
layeredLayout.worker.js
Edit
layout.test.ts
Edit
layout.ts
Edit
layout.worker.js
Edit
module.tsx
Edit
panelcfg.cue
Edit
panelcfg.gen.ts
Edit
plugin.json
Edit
suggestions.ts
Edit
types.ts
Edit
useCategorizeFrames.ts
Edit
useContextMenu.tsx
Edit
useFocusPositionOnLayout.ts
Edit
useHighlight.ts
Edit
useNodeLimit.ts
Edit
usePanning.ts
Edit
useZoom.ts
Edit
utils.test.ts
Edit
utils.ts
Edit