mirror of https://github.com/grafana/grafana.git
				
				
				
			
		
			
				
	
	
		
			398 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
			
		
		
	
	
			398 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
import { ArrayVector, createTheme, DataFrame, FieldType, MutableDataFrame } from '@grafana/data';
 | 
						|
 | 
						|
import { NodeGraphOptions } from './types';
 | 
						|
import {
 | 
						|
  findConnectedNodesForEdge,
 | 
						|
  findConnectedNodesForNode,
 | 
						|
  getEdgeFields,
 | 
						|
  getNodeFields,
 | 
						|
  getNodeGraphDataFrames,
 | 
						|
  makeEdgesDataFrame,
 | 
						|
  makeNodesDataFrame,
 | 
						|
  processNodes,
 | 
						|
} from './utils';
 | 
						|
 | 
						|
describe('processNodes', () => {
 | 
						|
  const theme = createTheme();
 | 
						|
 | 
						|
  it('handles empty args', async () => {
 | 
						|
    expect(processNodes(undefined, undefined, theme)).toEqual({ nodes: [], edges: [] });
 | 
						|
  });
 | 
						|
 | 
						|
  it('returns proper nodes and edges', async () => {
 | 
						|
    const { nodes, edges, legend } = processNodes(
 | 
						|
      makeNodesDataFrame(3),
 | 
						|
      makeEdgesDataFrame([
 | 
						|
        [0, 1],
 | 
						|
        [0, 2],
 | 
						|
        [1, 2],
 | 
						|
      ]),
 | 
						|
      theme
 | 
						|
    );
 | 
						|
 | 
						|
    const colorField = {
 | 
						|
      config: {
 | 
						|
        color: {
 | 
						|
          mode: 'continuous-GrYlRd',
 | 
						|
        },
 | 
						|
      },
 | 
						|
      index: 7,
 | 
						|
      name: 'color',
 | 
						|
      type: 'number',
 | 
						|
      values: new ArrayVector([0.5, 0.5, 0.5]),
 | 
						|
    };
 | 
						|
 | 
						|
    expect(nodes).toEqual([
 | 
						|
      {
 | 
						|
        arcSections: [
 | 
						|
          {
 | 
						|
            config: {
 | 
						|
              color: {
 | 
						|
                fixedColor: 'green',
 | 
						|
              },
 | 
						|
            },
 | 
						|
            name: 'arc__success',
 | 
						|
            type: 'number',
 | 
						|
            values: new ArrayVector([0.5, 0.5, 0.5]),
 | 
						|
          },
 | 
						|
          {
 | 
						|
            config: {
 | 
						|
              color: {
 | 
						|
                fixedColor: 'red',
 | 
						|
              },
 | 
						|
            },
 | 
						|
            name: 'arc__errors',
 | 
						|
            type: 'number',
 | 
						|
            values: new ArrayVector([0.5, 0.5, 0.5]),
 | 
						|
          },
 | 
						|
        ],
 | 
						|
        color: colorField,
 | 
						|
        dataFrameRowIndex: 0,
 | 
						|
        id: '0',
 | 
						|
        incoming: 0,
 | 
						|
        mainStat: {
 | 
						|
          config: {},
 | 
						|
          index: 3,
 | 
						|
          name: 'mainstat',
 | 
						|
          type: 'number',
 | 
						|
          values: new ArrayVector([0.1, 0.1, 0.1]),
 | 
						|
        },
 | 
						|
        secondaryStat: {
 | 
						|
          config: {},
 | 
						|
          index: 4,
 | 
						|
          name: 'secondarystat',
 | 
						|
          type: 'number',
 | 
						|
          values: new ArrayVector([2, 2, 2]),
 | 
						|
        },
 | 
						|
        subTitle: 'service',
 | 
						|
        title: 'service:0',
 | 
						|
      },
 | 
						|
      {
 | 
						|
        arcSections: [
 | 
						|
          {
 | 
						|
            config: {
 | 
						|
              color: {
 | 
						|
                fixedColor: 'green',
 | 
						|
              },
 | 
						|
            },
 | 
						|
            name: 'arc__success',
 | 
						|
            type: 'number',
 | 
						|
            values: new ArrayVector([0.5, 0.5, 0.5]),
 | 
						|
          },
 | 
						|
          {
 | 
						|
            config: {
 | 
						|
              color: {
 | 
						|
                fixedColor: 'red',
 | 
						|
              },
 | 
						|
            },
 | 
						|
            name: 'arc__errors',
 | 
						|
            type: 'number',
 | 
						|
            values: new ArrayVector([0.5, 0.5, 0.5]),
 | 
						|
          },
 | 
						|
        ],
 | 
						|
        color: colorField,
 | 
						|
        dataFrameRowIndex: 1,
 | 
						|
        id: '1',
 | 
						|
        incoming: 1,
 | 
						|
        mainStat: {
 | 
						|
          config: {},
 | 
						|
          index: 3,
 | 
						|
          name: 'mainstat',
 | 
						|
          type: 'number',
 | 
						|
          values: new ArrayVector([0.1, 0.1, 0.1]),
 | 
						|
        },
 | 
						|
        secondaryStat: {
 | 
						|
          config: {},
 | 
						|
          index: 4,
 | 
						|
          name: 'secondarystat',
 | 
						|
          type: 'number',
 | 
						|
          values: new ArrayVector([2, 2, 2]),
 | 
						|
        },
 | 
						|
        subTitle: 'service',
 | 
						|
        title: 'service:1',
 | 
						|
      },
 | 
						|
      {
 | 
						|
        arcSections: [
 | 
						|
          {
 | 
						|
            config: {
 | 
						|
              color: {
 | 
						|
                fixedColor: 'green',
 | 
						|
              },
 | 
						|
            },
 | 
						|
            name: 'arc__success',
 | 
						|
            type: 'number',
 | 
						|
            values: new ArrayVector([0.5, 0.5, 0.5]),
 | 
						|
          },
 | 
						|
          {
 | 
						|
            config: {
 | 
						|
              color: {
 | 
						|
                fixedColor: 'red',
 | 
						|
              },
 | 
						|
            },
 | 
						|
            name: 'arc__errors',
 | 
						|
            type: 'number',
 | 
						|
            values: new ArrayVector([0.5, 0.5, 0.5]),
 | 
						|
          },
 | 
						|
        ],
 | 
						|
        color: colorField,
 | 
						|
        dataFrameRowIndex: 2,
 | 
						|
        id: '2',
 | 
						|
        incoming: 2,
 | 
						|
        mainStat: {
 | 
						|
          config: {},
 | 
						|
          index: 3,
 | 
						|
          name: 'mainstat',
 | 
						|
          type: 'number',
 | 
						|
          values: new ArrayVector([0.1, 0.1, 0.1]),
 | 
						|
        },
 | 
						|
        secondaryStat: {
 | 
						|
          config: {},
 | 
						|
          index: 4,
 | 
						|
          name: 'secondarystat',
 | 
						|
          type: 'number',
 | 
						|
          values: new ArrayVector([2, 2, 2]),
 | 
						|
        },
 | 
						|
        subTitle: 'service',
 | 
						|
        title: 'service:2',
 | 
						|
      },
 | 
						|
    ]);
 | 
						|
 | 
						|
    expect(edges).toEqual([
 | 
						|
      {
 | 
						|
        dataFrameRowIndex: 0,
 | 
						|
        id: '0--1',
 | 
						|
        mainStat: '',
 | 
						|
        secondaryStat: '',
 | 
						|
        source: '0',
 | 
						|
        target: '1',
 | 
						|
      },
 | 
						|
      {
 | 
						|
        dataFrameRowIndex: 1,
 | 
						|
        id: '0--2',
 | 
						|
        mainStat: '',
 | 
						|
        secondaryStat: '',
 | 
						|
        source: '0',
 | 
						|
        target: '2',
 | 
						|
      },
 | 
						|
      {
 | 
						|
        dataFrameRowIndex: 2,
 | 
						|
        id: '1--2',
 | 
						|
        mainStat: '',
 | 
						|
        secondaryStat: '',
 | 
						|
        source: '1',
 | 
						|
        target: '2',
 | 
						|
      },
 | 
						|
    ]);
 | 
						|
 | 
						|
    expect(legend).toEqual([
 | 
						|
      {
 | 
						|
        color: 'green',
 | 
						|
        name: 'arc__success',
 | 
						|
      },
 | 
						|
      {
 | 
						|
        color: 'red',
 | 
						|
        name: 'arc__errors',
 | 
						|
      },
 | 
						|
    ]);
 | 
						|
  });
 | 
						|
 | 
						|
  it('detects dataframes correctly', () => {
 | 
						|
    const validFrames = [
 | 
						|
      new MutableDataFrame({
 | 
						|
        refId: 'hasPreferredVisualisationType',
 | 
						|
        fields: [],
 | 
						|
        meta: {
 | 
						|
          preferredVisualisationType: 'nodeGraph',
 | 
						|
        },
 | 
						|
      }),
 | 
						|
      new MutableDataFrame({
 | 
						|
        refId: 'hasName',
 | 
						|
        fields: [],
 | 
						|
        name: 'nodes',
 | 
						|
      }),
 | 
						|
      new MutableDataFrame({
 | 
						|
        refId: 'nodes', // hasRefId
 | 
						|
        fields: [],
 | 
						|
      }),
 | 
						|
      new MutableDataFrame({
 | 
						|
        refId: 'hasValidNodesShape',
 | 
						|
        fields: [{ name: 'id', type: FieldType.string }],
 | 
						|
      }),
 | 
						|
      new MutableDataFrame({
 | 
						|
        refId: 'hasValidEdgesShape',
 | 
						|
        fields: [
 | 
						|
          { name: 'id', type: FieldType.string },
 | 
						|
          { name: 'source', type: FieldType.string },
 | 
						|
          { name: 'target', type: FieldType.string },
 | 
						|
        ],
 | 
						|
      }),
 | 
						|
    ];
 | 
						|
    const invalidFrames = [
 | 
						|
      new MutableDataFrame({
 | 
						|
        refId: 'invalidData',
 | 
						|
        fields: [],
 | 
						|
      }),
 | 
						|
    ];
 | 
						|
    const frames = [...validFrames, ...invalidFrames];
 | 
						|
 | 
						|
    const nodeGraphFrames = getNodeGraphDataFrames(frames as DataFrame[]);
 | 
						|
    expect(nodeGraphFrames.length).toBe(5);
 | 
						|
    expect(nodeGraphFrames).toEqual(validFrames);
 | 
						|
  });
 | 
						|
 | 
						|
  it('getting fields is case insensitive', () => {
 | 
						|
    const nodeFrame = new MutableDataFrame({
 | 
						|
      refId: 'nodes',
 | 
						|
      fields: [
 | 
						|
        { name: 'id', type: FieldType.string, values: ['id'] },
 | 
						|
        { name: 'title', type: FieldType.string, values: ['title'] },
 | 
						|
        { name: 'SUBTITLE', type: FieldType.string, values: ['subTitle'] },
 | 
						|
        { name: 'mainstat', type: FieldType.string, values: ['mainStat'] },
 | 
						|
        { name: 'seconDarysTat', type: FieldType.string, values: ['secondaryStat'] },
 | 
						|
      ],
 | 
						|
    });
 | 
						|
 | 
						|
    const nodeFields = getNodeFields(nodeFrame);
 | 
						|
    expect(nodeFields.id).toBeDefined();
 | 
						|
    expect(nodeFields.title).toBeDefined();
 | 
						|
    expect(nodeFields.subTitle).toBeDefined();
 | 
						|
    expect(nodeFields.mainStat).toBeDefined();
 | 
						|
    expect(nodeFields.secondaryStat).toBeDefined();
 | 
						|
 | 
						|
    const edgeFrame = new MutableDataFrame({
 | 
						|
      refId: 'nodes',
 | 
						|
      fields: [
 | 
						|
        { name: 'id', type: FieldType.string, values: ['id'] },
 | 
						|
        { name: 'source', type: FieldType.string, values: ['title'] },
 | 
						|
        { name: 'TARGET', type: FieldType.string, values: ['subTitle'] },
 | 
						|
        { name: 'mainstat', type: FieldType.string, values: ['mainStat'] },
 | 
						|
        { name: 'secondarystat', type: FieldType.string, values: ['secondaryStat'] },
 | 
						|
      ],
 | 
						|
    });
 | 
						|
    const edgeFields = getEdgeFields(edgeFrame);
 | 
						|
    expect(edgeFields.id).toBeDefined();
 | 
						|
    expect(edgeFields.source).toBeDefined();
 | 
						|
    expect(edgeFields.target).toBeDefined();
 | 
						|
    expect(edgeFields.mainStat).toBeDefined();
 | 
						|
    expect(edgeFields.secondaryStat).toBeDefined();
 | 
						|
  });
 | 
						|
 | 
						|
  it('interpolates panel options correctly', () => {
 | 
						|
    const frames = [
 | 
						|
      new MutableDataFrame({
 | 
						|
        refId: 'nodes',
 | 
						|
        fields: [
 | 
						|
          { name: 'id', type: FieldType.string },
 | 
						|
          { name: 'mainStat', type: FieldType.string },
 | 
						|
          { name: 'secondaryStat', type: FieldType.string },
 | 
						|
          { name: 'arc__primary', type: FieldType.string },
 | 
						|
          { name: 'arc__secondary', type: FieldType.string },
 | 
						|
          { name: 'arc__tertiary', type: FieldType.string },
 | 
						|
        ],
 | 
						|
      }),
 | 
						|
      new MutableDataFrame({
 | 
						|
        refId: 'edges',
 | 
						|
        fields: [
 | 
						|
          { name: 'id', type: FieldType.string },
 | 
						|
          { name: 'source', type: FieldType.string },
 | 
						|
          { name: 'target', type: FieldType.string },
 | 
						|
          { name: 'mainStat', type: FieldType.string },
 | 
						|
          { name: 'secondaryStat', type: FieldType.string },
 | 
						|
        ],
 | 
						|
      }),
 | 
						|
    ];
 | 
						|
 | 
						|
    const panelOptions: NodeGraphOptions = {
 | 
						|
      nodes: {
 | 
						|
        mainStatUnit: 'r/min',
 | 
						|
        secondaryStatUnit: 'ms/r',
 | 
						|
        arcs: [
 | 
						|
          { field: 'arc__primary', color: 'red' },
 | 
						|
          { field: 'arc__secondary', color: 'yellow' },
 | 
						|
          { field: 'arc__tertiary', color: '#dd40ec' },
 | 
						|
        ],
 | 
						|
      },
 | 
						|
      edges: {
 | 
						|
        mainStatUnit: 'r/sec',
 | 
						|
        secondaryStatUnit: 'ft^2',
 | 
						|
      },
 | 
						|
    };
 | 
						|
 | 
						|
    const nodeGraphFrames = getNodeGraphDataFrames(frames, panelOptions);
 | 
						|
    expect(nodeGraphFrames).toHaveLength(2);
 | 
						|
 | 
						|
    const nodesFrame = nodeGraphFrames.find((f) => f.refId === 'nodes');
 | 
						|
    expect(nodesFrame).toBeDefined();
 | 
						|
    expect(nodesFrame?.fields.find((f) => f.name === 'mainStat')?.config).toEqual({ unit: 'r/min' });
 | 
						|
    expect(nodesFrame?.fields.find((f) => f.name === 'secondaryStat')?.config).toEqual({ unit: 'ms/r' });
 | 
						|
    expect(nodesFrame?.fields.find((f) => f.name === 'arc__primary')?.config).toEqual({
 | 
						|
      color: { mode: 'fixed', fixedColor: 'red' },
 | 
						|
    });
 | 
						|
    expect(nodesFrame?.fields.find((f) => f.name === 'arc__secondary')?.config).toEqual({
 | 
						|
      color: { mode: 'fixed', fixedColor: 'yellow' },
 | 
						|
    });
 | 
						|
    expect(nodesFrame?.fields.find((f) => f.name === 'arc__tertiary')?.config).toEqual({
 | 
						|
      color: { mode: 'fixed', fixedColor: '#dd40ec' },
 | 
						|
    });
 | 
						|
 | 
						|
    const edgesFrame = nodeGraphFrames.find((f) => f.refId === 'edges');
 | 
						|
    expect(edgesFrame).toBeDefined();
 | 
						|
    expect(edgesFrame?.fields.find((f) => f.name === 'mainStat')?.config).toEqual({ unit: 'r/sec' });
 | 
						|
    expect(edgesFrame?.fields.find((f) => f.name === 'secondaryStat')?.config).toEqual({ unit: 'ft^2' });
 | 
						|
  });
 | 
						|
});
 | 
						|
 | 
						|
describe('finds connections', () => {
 | 
						|
  const theme = createTheme();
 | 
						|
 | 
						|
  it('finds connected nodes given an edge id', () => {
 | 
						|
    const { nodes, edges } = processNodes(
 | 
						|
      makeNodesDataFrame(3),
 | 
						|
      makeEdgesDataFrame([
 | 
						|
        [0, 1],
 | 
						|
        [0, 2],
 | 
						|
        [1, 2],
 | 
						|
      ]),
 | 
						|
      theme
 | 
						|
    );
 | 
						|
 | 
						|
    const linked = findConnectedNodesForEdge(nodes, edges, edges[0].id);
 | 
						|
    expect(linked).toEqual(['0', '1']);
 | 
						|
  });
 | 
						|
 | 
						|
  it('finds connected nodes given a node id', () => {
 | 
						|
    const { nodes, edges } = processNodes(
 | 
						|
      makeNodesDataFrame(4),
 | 
						|
      makeEdgesDataFrame([
 | 
						|
        [0, 1],
 | 
						|
        [0, 2],
 | 
						|
        [1, 2],
 | 
						|
      ]),
 | 
						|
      theme
 | 
						|
    );
 | 
						|
 | 
						|
    const linked = findConnectedNodesForNode(nodes, edges, nodes[0].id);
 | 
						|
    expect(linked).toEqual(['0', '1', '2']);
 | 
						|
  });
 | 
						|
});
 |