github.com/turbot/steampipe@v1.7.0-rc.0.0.20240517123944-7cef272d4458/ui/dashboard/src/components/dashboards/common/index.test.ts (about)

     1  import "../../../test/matchMedia";
     2  import {
     3    adjustMinValue,
     4    adjustMaxValue,
     5    buildNodesAndEdges,
     6    foldNodesAndEdges,
     7    themeColors,
     8  } from "./index";
     9  import { Edge, Node } from "./types";
    10  import { Graph } from "graphlib";
    11  
    12  describe("common.adjustMinValue", () => {
    13    test("5", () => {
    14      expect(adjustMinValue(5)).toEqual(0);
    15    });
    16  
    17    test("-8", () => {
    18      expect(adjustMinValue(-8)).toEqual(-9);
    19    });
    20  
    21    test("-13", () => {
    22      expect(adjustMinValue(-13)).toEqual(-14);
    23    });
    24  
    25    test("-20", () => {
    26      expect(adjustMinValue(-20)).toEqual(-25);
    27    });
    28  
    29    test("-26", () => {
    30      expect(adjustMinValue(-26)).toEqual(-30);
    31    });
    32  
    33    test("-35", () => {
    34      expect(adjustMinValue(-35)).toEqual(-40);
    35    });
    36  
    37    test("-50", () => {
    38      expect(adjustMinValue(-50)).toEqual(-60);
    39    });
    40  
    41    test("-52", () => {
    42      expect(adjustMinValue(-52)).toEqual(-60);
    43    });
    44  
    45    test("-180", () => {
    46      expect(adjustMinValue(-180)).toEqual(-190);
    47    });
    48  
    49    test("-210", () => {
    50      expect(adjustMinValue(-200)).toEqual(-250);
    51    });
    52  
    53    test("-250", () => {
    54      expect(adjustMinValue(-250)).toEqual(-300);
    55    });
    56  
    57    test("-362", () => {
    58      expect(adjustMinValue(-362)).toEqual(-400);
    59    });
    60  
    61    test("-1000", () => {
    62      expect(adjustMinValue(-1000)).toEqual(-1100);
    63    });
    64  
    65    test("-2363", () => {
    66      expect(adjustMinValue(-2363)).toEqual(-2400);
    67    });
    68  
    69    test("-7001", () => {
    70      expect(adjustMinValue(-7001)).toEqual(-7100);
    71    });
    72  
    73    test("-10000", () => {
    74      expect(adjustMinValue(-10000)).toEqual(-11000);
    75    });
    76  
    77    test("-26526", () => {
    78      expect(adjustMinValue(-26526)).toEqual(-27000);
    79    });
    80  });
    81  
    82  describe("common.adjustMaxValue", () => {
    83    test("-5", () => {
    84      expect(adjustMaxValue(-5)).toEqual(0);
    85    });
    86  
    87    test("8", () => {
    88      expect(adjustMaxValue(8)).toEqual(9);
    89    });
    90  
    91    test("13", () => {
    92      expect(adjustMaxValue(13)).toEqual(14);
    93    });
    94  
    95    test("20", () => {
    96      expect(adjustMaxValue(20)).toEqual(25);
    97    });
    98  
    99    test("26", () => {
   100      expect(adjustMaxValue(26)).toEqual(30);
   101    });
   102  
   103    test("35", () => {
   104      expect(adjustMaxValue(35)).toEqual(40);
   105    });
   106  
   107    test("50", () => {
   108      expect(adjustMaxValue(50)).toEqual(60);
   109    });
   110  
   111    test("52", () => {
   112      expect(adjustMaxValue(52)).toEqual(60);
   113    });
   114  
   115    test("180", () => {
   116      expect(adjustMaxValue(180)).toEqual(190);
   117    });
   118  
   119    test("210", () => {
   120      expect(adjustMaxValue(210)).toEqual(250);
   121    });
   122  
   123    test("250", () => {
   124      expect(adjustMaxValue(250)).toEqual(300);
   125    });
   126  
   127    test("362", () => {
   128      expect(adjustMaxValue(362)).toEqual(400);
   129    });
   130  
   131    test("1000", () => {
   132      expect(adjustMaxValue(1000)).toEqual(1100);
   133    });
   134  
   135    test("2363", () => {
   136      expect(adjustMaxValue(2363)).toEqual(2400);
   137    });
   138  
   139    test("7001", () => {
   140      expect(adjustMaxValue(7001)).toEqual(7100);
   141    });
   142  
   143    test("10000", () => {
   144      expect(adjustMaxValue(10000)).toEqual(11000);
   145    });
   146  
   147    test("26526", () => {
   148      expect(adjustMaxValue(26526)).toEqual(27000);
   149    });
   150  });
   151  
   152  describe("common.buildNodesAndEdges", () => {
   153    test("single node", () => {
   154      const rawData = {
   155        columns: [{ name: "id", data_type: "TEXT" }],
   156        rows: [{ id: "node" }],
   157      };
   158      const node = {
   159        id: "node",
   160        title: null,
   161        category: null,
   162        depth: null,
   163        row_data: { id: "node" },
   164        href: null,
   165        symbol: null,
   166        isFolded: false,
   167      };
   168      const nodesAndEdges = buildNodesAndEdges({}, rawData);
   169      delete nodesAndEdges.graph;
   170      expect(nodesAndEdges).toEqual({
   171        categories: {},
   172        edgeMap: {},
   173        edges: [],
   174        metadata: { contains_duplicate_edges: false, has_multiple_roots: false },
   175        next_color_index: 0,
   176        nodeCategoryMap: {},
   177        nodeMap: { [node.id]: node },
   178        nodes: [node],
   179        root_nodes: {
   180          node,
   181        },
   182      });
   183    });
   184  
   185    test("single node with depth", () => {
   186      const rawData = {
   187        columns: [
   188          { name: "id", data_type: "TEXT" },
   189          { name: "depth", data_type: "INT4" },
   190        ],
   191        rows: [{ id: "node", depth: 0 }],
   192      };
   193      const node = {
   194        id: "node",
   195        title: null,
   196        category: null,
   197        depth: 0,
   198        row_data: { id: "node", depth: 0 },
   199        href: null,
   200        symbol: null,
   201        isFolded: false,
   202      };
   203      const nodesAndEdges = buildNodesAndEdges({}, rawData);
   204      delete nodesAndEdges.graph;
   205      expect(nodesAndEdges).toEqual({
   206        categories: {},
   207        edgeMap: {},
   208        edges: [],
   209        metadata: { contains_duplicate_edges: false, has_multiple_roots: false },
   210        next_color_index: 0,
   211        nodeCategoryMap: {},
   212        nodeMap: { [node.id]: node },
   213        nodes: [node],
   214        root_nodes: {
   215          node,
   216        },
   217      });
   218    });
   219  
   220    test("single node with category", () => {
   221      const rawData = {
   222        columns: [
   223          { name: "id", data_type: "TEXT" },
   224          { name: "category", data_type: "TEXT" },
   225        ],
   226        rows: [{ id: "node", category: "c1" }],
   227      };
   228      const node = {
   229        id: "node",
   230        title: null,
   231        category: "c1",
   232        depth: null,
   233        row_data: rawData.rows[0],
   234        href: null,
   235        symbol: null,
   236        isFolded: false,
   237      };
   238      const nodesAndEdges = buildNodesAndEdges({}, rawData);
   239      delete nodesAndEdges.graph;
   240      expect(nodesAndEdges).toEqual({
   241        categories: {
   242          c1: {
   243            color: themeColors[0],
   244          },
   245        },
   246        edgeMap: {},
   247        edges: [],
   248        metadata: { contains_duplicate_edges: false, has_multiple_roots: false },
   249        next_color_index: 1,
   250        nodeCategoryMap: { c1: { [node.id]: node } },
   251        nodeMap: { [node.id]: node },
   252        nodes: [node],
   253        root_nodes: {
   254          node,
   255        },
   256      });
   257    });
   258  
   259    test("single node with from_id", () => {
   260      const rawData = {
   261        columns: [
   262          { name: "id", data_type: "TEXT" },
   263          { name: "from_id", data_type: "TEXT" },
   264        ],
   265        rows: [{ id: "node", from_id: "from_node" }],
   266      };
   267      const nodesAndEdges = buildNodesAndEdges({}, rawData);
   268      delete nodesAndEdges.graph;
   269      const sourceNode = {
   270        id: "from_node",
   271        title: null,
   272        category: null,
   273        depth: null,
   274        row_data: null,
   275        href: null,
   276        symbol: null,
   277        isFolded: false,
   278      };
   279      const targetNode = {
   280        id: "node",
   281        title: null,
   282        category: null,
   283        depth: null,
   284        row_data: { id: "node", from_id: "from_node" },
   285        href: null,
   286        symbol: null,
   287        isFolded: false,
   288      };
   289      const edge = {
   290        id: "from_node_node",
   291        from_id: "from_node",
   292        to_id: "node",
   293        title: null,
   294        category: null,
   295        row_data: null,
   296        isFolded: false,
   297      };
   298      expect(nodesAndEdges).toEqual({
   299        categories: {},
   300        edgeMap: { [edge.id]: edge },
   301        edges: [edge],
   302        metadata: { contains_duplicate_edges: false, has_multiple_roots: false },
   303        next_color_index: 0,
   304        nodeCategoryMap: {},
   305        nodeMap: { [targetNode.id]: targetNode, [sourceNode.id]: sourceNode },
   306        nodes: [targetNode, sourceNode],
   307        root_nodes: {
   308          [sourceNode.id]: sourceNode,
   309        },
   310      });
   311    });
   312  
   313    test("single node with to_id", () => {
   314      const rawData = {
   315        columns: [
   316          { name: "id", data_type: "TEXT" },
   317          { name: "to_id", data_type: "TEXT" },
   318        ],
   319        rows: [{ id: "node", to_id: "to_node" }],
   320      };
   321      const sourceNode = {
   322        id: "node",
   323        title: null,
   324        category: null,
   325        depth: null,
   326        row_data: { id: "node", to_id: "to_node" },
   327        href: null,
   328        symbol: null,
   329        isFolded: false,
   330      };
   331      const targetNode = {
   332        id: "to_node",
   333        title: null,
   334        category: null,
   335        depth: null,
   336        row_data: null,
   337        href: null,
   338        symbol: null,
   339        isFolded: false,
   340      };
   341      const edge = {
   342        id: "node_to_node",
   343        from_id: "node",
   344        to_id: "to_node",
   345        title: null,
   346        category: null,
   347        row_data: null,
   348        isFolded: false,
   349      };
   350      const nodesAndEdges = buildNodesAndEdges({}, rawData);
   351      delete nodesAndEdges.graph;
   352      expect(nodesAndEdges).toEqual({
   353        categories: {},
   354        edgeMap: { [edge.id]: edge },
   355        edges: [edge],
   356        metadata: { contains_duplicate_edges: false, has_multiple_roots: false },
   357        next_color_index: 0,
   358        nodeCategoryMap: {},
   359        nodeMap: { [sourceNode.id]: sourceNode, [targetNode.id]: targetNode },
   360        nodes: [sourceNode, targetNode],
   361        root_nodes: {
   362          node: sourceNode,
   363        },
   364      });
   365    });
   366  
   367    test("single node with from_id and to_id", () => {
   368      const rawData = {
   369        columns: [
   370          { name: "id", data_type: "TEXT" },
   371          { name: "from_id", data_type: "TEXT" },
   372          { name: "to_id", data_type: "TEXT" },
   373        ],
   374        rows: [{ id: "node", from_id: "from_node", to_id: "to_node" }],
   375      };
   376      const edge = {
   377        id: "from_node_to_node",
   378        from_id: "from_node",
   379        to_id: "to_node",
   380        title: null,
   381        category: null,
   382        row_data: null,
   383        isFolded: false,
   384      };
   385      const node = {
   386        id: "node",
   387        title: null,
   388        category: null,
   389        depth: null,
   390        row_data: { id: "node", from_id: "from_node", to_id: "to_node" },
   391        href: null,
   392        symbol: null,
   393        isFolded: false,
   394      };
   395      const sourceNode = {
   396        id: "from_node",
   397        title: null,
   398        category: null,
   399        depth: null,
   400        row_data: null,
   401        href: null,
   402        symbol: null,
   403        isFolded: false,
   404      };
   405      const targetNode = {
   406        id: "to_node",
   407        title: null,
   408        category: null,
   409        depth: null,
   410        row_data: null,
   411        href: null,
   412        symbol: null,
   413        isFolded: false,
   414      };
   415      const nodesAndEdges = buildNodesAndEdges({}, rawData);
   416      delete nodesAndEdges.graph;
   417      expect(nodesAndEdges).toEqual({
   418        categories: {},
   419        edgeMap: { [edge.id]: edge },
   420        edges: [edge],
   421        metadata: { contains_duplicate_edges: false, has_multiple_roots: true },
   422        next_color_index: 0,
   423        nodeCategoryMap: {},
   424        nodeMap: {
   425          [node.id]: node,
   426          [sourceNode.id]: sourceNode,
   427          [targetNode.id]: targetNode,
   428        },
   429        nodes: [node, sourceNode, targetNode],
   430        root_nodes: {
   431          node,
   432          from_node: sourceNode,
   433        },
   434      });
   435    });
   436  
   437    test("two nodes with separate edge declaration", () => {
   438      const rawData = {
   439        columns: [
   440          { name: "id", data_type: "TEXT" },
   441          { name: "from_id", data_type: "TEXT" },
   442          { name: "to_id", data_type: "TEXT" },
   443          { name: "depth", data_type: "INT4" },
   444        ],
   445        rows: [
   446          { id: "from_node", depth: 0 },
   447          { id: "to_node", depth: 1 },
   448          { from_id: "from_node", to_id: "to_node" },
   449        ],
   450      };
   451      const edge = {
   452        id: "from_node_to_node",
   453        from_id: "from_node",
   454        to_id: "to_node",
   455        title: null,
   456        category: null,
   457        row_data: { from_id: "from_node", to_id: "to_node" },
   458        isFolded: false,
   459      };
   460      const sourceNode = {
   461        id: "from_node",
   462        title: null,
   463        category: null,
   464        depth: 0,
   465        row_data: { id: "from_node", depth: 0 },
   466        href: null,
   467        symbol: null,
   468        isFolded: false,
   469      };
   470      const targetNode = {
   471        id: "to_node",
   472        title: null,
   473        category: null,
   474        depth: 1,
   475        row_data: { id: "to_node", depth: 1 },
   476        href: null,
   477        symbol: null,
   478        isFolded: false,
   479      };
   480      const nodesAndEdges = buildNodesAndEdges({}, rawData);
   481      delete nodesAndEdges.graph;
   482      expect(nodesAndEdges).toEqual({
   483        categories: {},
   484        edgeMap: { [edge.id]: edge },
   485        edges: [edge],
   486        metadata: { contains_duplicate_edges: false, has_multiple_roots: false },
   487        next_color_index: 0,
   488        nodeCategoryMap: {},
   489        nodeMap: { [sourceNode.id]: sourceNode, [targetNode.id]: targetNode },
   490        nodes: [sourceNode, targetNode],
   491        root_nodes: {
   492          from_node: sourceNode,
   493        },
   494      });
   495    });
   496  
   497    test("two nodes with separate edge declaration and title set on explicit node rows", () => {
   498      const rawData = {
   499        columns: [
   500          { name: "id", data_type: "TEXT" },
   501          { name: "from_id", data_type: "TEXT" },
   502          { name: "to_id", data_type: "TEXT" },
   503          { name: "depth", data_type: "INT4" },
   504        ],
   505        rows: [
   506          { from_id: "from_node", to_id: "to_node" },
   507          { id: "from_node", depth: 0, title: "from_node" },
   508          { id: "to_node", depth: 1, title: "to_node" },
   509        ],
   510      };
   511      const edge = {
   512        id: "from_node_to_node",
   513        from_id: "from_node",
   514        to_id: "to_node",
   515        title: null,
   516        category: null,
   517        row_data: { from_id: "from_node", to_id: "to_node" },
   518        isFolded: false,
   519      };
   520      const sourceNode = {
   521        id: "from_node",
   522        title: "from_node",
   523        category: null,
   524        depth: 0,
   525        row_data: { id: "from_node", depth: 0, title: "from_node" },
   526        href: null,
   527        symbol: null,
   528        isFolded: false,
   529      };
   530      const targetNode = {
   531        id: "to_node",
   532        title: "to_node",
   533        category: null,
   534        depth: 1,
   535        row_data: { id: "to_node", depth: 1, title: "to_node" },
   536        href: null,
   537        symbol: null,
   538        isFolded: false,
   539      };
   540      const nodesAndEdges = buildNodesAndEdges({}, rawData);
   541      delete nodesAndEdges.graph;
   542      expect(nodesAndEdges).toEqual({
   543        categories: {},
   544        edgeMap: { [edge.id]: edge },
   545        edges: [edge],
   546        metadata: { contains_duplicate_edges: false, has_multiple_roots: false },
   547        next_color_index: 0,
   548        nodeCategoryMap: {},
   549        nodeMap: { [sourceNode.id]: sourceNode, [targetNode.id]: targetNode },
   550        nodes: [sourceNode, targetNode],
   551        root_nodes: {
   552          from_node: sourceNode,
   553        },
   554      });
   555    });
   556  
   557    test("two nodes with separate edge declaration including properties", () => {
   558      const rawData = {
   559        columns: [
   560          { name: "id", data_type: "TEXT" },
   561          { name: "from_id", data_type: "TEXT" },
   562          { name: "to_id", data_type: "TEXT" },
   563          { name: "title", data_type: "TEXT" },
   564          { name: "properties", data_type: "jsonb" },
   565        ],
   566        rows: [
   567          { id: "from_node", title: "From Node", properties: { foo: "bar" } },
   568          { id: "to_node", title: "To Node", properties: { bar: "foo" } },
   569          {
   570            from_id: "from_node",
   571            to_id: "to_node",
   572            title: "The Edge",
   573            properties: { foobar: "barfoo" },
   574          },
   575        ],
   576      };
   577      const edge = {
   578        id: "from_node_to_node",
   579        from_id: "from_node",
   580        to_id: "to_node",
   581        title: "The Edge",
   582        category: null,
   583        row_data: {
   584          from_id: "from_node",
   585          to_id: "to_node",
   586          title: "The Edge",
   587          properties: { foobar: "barfoo" },
   588        },
   589        isFolded: false,
   590      };
   591      const sourceNode = {
   592        id: "from_node",
   593        title: "From Node",
   594        category: null,
   595        depth: null,
   596        row_data: {
   597          id: "from_node",
   598          title: "From Node",
   599          properties: { foo: "bar" },
   600        },
   601        href: null,
   602        symbol: null,
   603        isFolded: false,
   604      };
   605      const targetNode = {
   606        id: "to_node",
   607        title: "To Node",
   608        category: null,
   609        depth: null,
   610        row_data: {
   611          id: "to_node",
   612          title: "To Node",
   613          properties: { bar: "foo" },
   614        },
   615        href: null,
   616        symbol: null,
   617        isFolded: false,
   618      };
   619      const nodesAndEdges = buildNodesAndEdges({}, rawData);
   620      delete nodesAndEdges.graph;
   621      expect(nodesAndEdges).toEqual({
   622        categories: {},
   623        edgeMap: { [edge.id]: edge },
   624        edges: [edge],
   625        metadata: { contains_duplicate_edges: false, has_multiple_roots: false },
   626        next_color_index: 0,
   627        nodeCategoryMap: {},
   628        nodeMap: { [sourceNode.id]: sourceNode, [targetNode.id]: targetNode },
   629        nodes: [sourceNode, targetNode],
   630        root_nodes: {
   631          from_node: sourceNode,
   632        },
   633      });
   634    });
   635  
   636    // test("single node with title", () => {
   637    //   const rawData = {
   638    //     columns: [
   639    //       { name: "id", data_type: "TEXT" },
   640    //       { name: "title", data_type: "TEXT" },
   641    //     ],
   642    //     rows: [{ id: "a_node", title: "A Node Title" }],
   643    //   };
   644    //   expect(buildNodesAndEdges(rawData)).toEqual({
   645    //     categories: {},
   646    //     edges: [],
   647    //     metadata: { contains_duplicate_edges: false, has_multiple_roots: false },
   648    //     next_color_index: 0,
   649    //     nodes: [
   650    //       { category: null, depth: null, id: "a_node", title: "A Node Title" },
   651    //     ],
   652    //     root_nodes: {
   653    //       a_node: {
   654    //         category: null,
   655    //         depth: null,
   656    //         id: "a_node",
   657    //         title: "A Node Title",
   658    //       },
   659    //     },
   660    //   });
   661    // });
   662    //
   663    // test("single node with category", () => {
   664    //   const rawData = {
   665    //     columns: [
   666    //       { name: "id", data_type: "TEXT" },
   667    //       { name: "category", data_type: "TEXT" },
   668    //     ],
   669    //     rows: [{ id: "a_node", category: "a_category" }],
   670    //   };
   671    //   expect(buildNodesAndEdges(rawData)).toEqual({
   672    //     categories: {},
   673    //     edges: [],
   674    //     metadata: { contains_duplicate_edges: false, has_multiple_roots: false },
   675    //     next_color_index: 0,
   676    //     nodes: [
   677    //       { category: "a_category", depth: null, id: "a_node", title: null },
   678    //     ],
   679    //     root_nodes: {
   680    //       a_node: {
   681    //         category: "a_category",
   682    //         depth: null,
   683    //         id: "a_node",
   684    //         title: null,
   685    //       },
   686    //     },
   687    //   });
   688    // });
   689  });
   690  
   691  const createNode = ({ id, category }): Node => {
   692    return {
   693      id,
   694      category,
   695      depth: null,
   696      href: null,
   697      isFolded: false,
   698      row_data: null,
   699      symbol: null,
   700      title: null,
   701    };
   702  };
   703  
   704  const createEdge = ({ id, from_id, to_id }): Edge => {
   705    return {
   706      id,
   707      from_id,
   708      to_id,
   709      category: null,
   710      row_data: null,
   711      title: null,
   712      isFolded: false,
   713    };
   714  };
   715  
   716  describe("common.foldNodesAndEdges", () => {
   717    test("Basic fold", () => {
   718      const graph = new Graph({ directed: true });
   719      graph.setNode("c1-1");
   720      graph.setNode("c2-1");
   721      graph.setNode("c2-2");
   722      graph.setNode("c2-3");
   723      graph.setEdge("c1-1", "c2-1");
   724      graph.setEdge("c1-1", "c2-2");
   725      graph.setEdge("c1-1", "c2-3");
   726      const node_c1_1 = createNode({
   727        id: "c1-1",
   728        category: "c1",
   729      });
   730      const node_c2_1 = createNode({
   731        id: "c2-1",
   732        category: "c2",
   733      });
   734      const node_c2_2 = createNode({
   735        id: "c2-2",
   736        category: "c2",
   737      });
   738      const node_c2_3 = createNode({
   739        id: "c2-3",
   740        category: "c2",
   741      });
   742      const edge_c1_1_c2_1 = createEdge({
   743        id: "c1-1_c2-1",
   744        from_id: "c1-1",
   745        to_id: "c2-1",
   746      });
   747      const edge_c1_1_c2_2 = createEdge({
   748        id: "c1-1_c2-2",
   749        from_id: "c1-1",
   750        to_id: "c2-2",
   751      });
   752      const edge_c1_1_c2_3 = createEdge({
   753        id: "c1-1_c2-3",
   754        from_id: "c1-1",
   755        to_id: "c2-3",
   756      });
   757      const category_1 = { id: "c1" };
   758      const category_2 = { id: "c2", fold: { threshold: 2 } };
   759      const nodesAndEdgesInput = {
   760        graph,
   761        nodeCategoryMap: {
   762          c1: {
   763            [node_c1_1.id]: node_c1_1,
   764          },
   765          c2: {
   766            [node_c2_1.id]: node_c2_1,
   767            [node_c2_2.id]: node_c2_2,
   768            [node_c2_3.id]: node_c2_3,
   769          },
   770        },
   771        nodeMap: {
   772          [node_c1_1.id]: node_c1_1,
   773          [node_c2_1.id]: node_c2_1,
   774          [node_c2_2.id]: node_c2_2,
   775          [node_c2_3.id]: node_c2_3,
   776        },
   777        nodes: [node_c1_1, node_c2_1, node_c2_2, node_c2_3],
   778        edgeMap: {
   779          [edge_c1_1_c2_1.id]: edge_c1_1_c2_1,
   780          [edge_c1_1_c2_2.id]: edge_c1_1_c2_2,
   781          [edge_c1_1_c2_3.id]: edge_c1_1_c2_3,
   782        },
   783        edges: [edge_c1_1_c2_1, edge_c1_1_c2_2, edge_c1_1_c2_3],
   784        root_nodes: { [node_c1_1.id]: node_c1_1 },
   785        categories: { [category_1.id]: category_1, [category_2.id]: category_2 },
   786      };
   787      // @ts-ignore
   788      const nodesAndEdges = foldNodesAndEdges(nodesAndEdgesInput);
   789      delete nodesAndEdges.graph;
   790      const foldedNode = {
   791        id: "fold-c2-1",
   792        category: "c2",
   793        depth: null,
   794        href: null,
   795        icon: undefined,
   796        isFolded: true,
   797        foldedNodes: [
   798          { id: node_c2_1.id, title: node_c2_1.title },
   799          { id: node_c2_2.id, title: node_c2_2.title },
   800          { id: node_c2_3.id, title: node_c2_3.title },
   801        ],
   802        row_data: null,
   803        symbol: null,
   804        title: null,
   805      };
   806      expect(nodesAndEdges).toEqual({
   807        nodeCategoryMap: {
   808          c1: {
   809            [node_c1_1.id]: node_c1_1,
   810          },
   811          c2: {
   812            [foldedNode.id]: foldedNode,
   813          },
   814        },
   815        nodeMap: {
   816          [node_c1_1.id]: node_c1_1,
   817          [foldedNode.id]: foldedNode,
   818        },
   819        nodes: [node_c1_1, foldedNode],
   820        edgeMap: {
   821          "c1-1_fold-c2-1": {
   822            id: "c1-1_fold-c2-1",
   823            from_id: "c1-1",
   824            to_id: "fold-c2-1",
   825            category: null,
   826            title: null,
   827            isFolded: true,
   828            row_data: null,
   829          },
   830        },
   831        edges: [
   832          {
   833            id: "c1-1_fold-c2-1",
   834            from_id: "c1-1",
   835            to_id: "fold-c2-1",
   836            category: null,
   837            title: null,
   838            isFolded: true,
   839            row_data: null,
   840          },
   841        ],
   842        root_nodes: { [node_c1_1.id]: node_c1_1 },
   843        categories: { [category_1.id]: category_1, [category_2.id]: category_2 },
   844      });
   845    });
   846  
   847    test("Middle fold", () => {
   848      const graph = new Graph({ directed: true });
   849      graph.setNode("c1-1");
   850      graph.setNode("c2-1");
   851      graph.setNode("c2-2");
   852      graph.setNode("c2-3");
   853      graph.setNode("c3-1");
   854      graph.setEdge("c1-1", "c2-1");
   855      graph.setEdge("c1-1", "c2-2");
   856      graph.setEdge("c1-1", "c2-3");
   857      graph.setEdge("c3-1", "c2-1");
   858      graph.setEdge("c3-1", "c2-2");
   859      graph.setEdge("c3-1", "c2-3");
   860      const node_c1_1 = createNode({
   861        id: "c1-1",
   862        category: "c1",
   863      });
   864      const node_c2_1 = createNode({
   865        id: "c2-1",
   866        category: "c2",
   867      });
   868      const node_c2_2 = createNode({
   869        id: "c2-2",
   870        category: "c2",
   871      });
   872      const node_c2_3 = createNode({
   873        id: "c2-3",
   874        category: "c2",
   875      });
   876      const node_c3_1 = createNode({
   877        id: "c3-1",
   878        category: "c3",
   879      });
   880      const edge_c1_1_c2_1 = createEdge({
   881        id: "c1-1_c2-1",
   882        from_id: "c1-1",
   883        to_id: "c2-1",
   884      });
   885      const edge_c1_1_c2_2 = createEdge({
   886        id: "c1-1_c2-2",
   887        from_id: "c1-1",
   888        to_id: "c2-2",
   889      });
   890      const edge_c1_1_c2_3 = createEdge({
   891        id: "c1-1_c2-3",
   892        from_id: "c1-1",
   893        to_id: "c2-3",
   894      });
   895      const edge_c3_1_c2_1 = createEdge({
   896        id: "c3-1_c2-1",
   897        from_id: "c3-1",
   898        to_id: "c2-1",
   899      });
   900      const edge_c3_1_c2_2 = createEdge({
   901        id: "c3-1_c2-2",
   902        from_id: "c3-1",
   903        to_id: "c2-2",
   904      });
   905      const edge_c3_1_c2_3 = createEdge({
   906        id: "c3-1_c2-3",
   907        from_id: "c3-1",
   908        to_id: "c2-3",
   909      });
   910      const category_1 = { id: "c1" };
   911      const category_2 = { id: "c2", fold: { threshold: 2 } };
   912      const category_3 = { id: "c3" };
   913      const nodesAndEdgesInput = {
   914        graph,
   915        nodeCategoryMap: {
   916          c1: {
   917            [node_c1_1.id]: node_c1_1,
   918          },
   919          c2: {
   920            [node_c2_1.id]: node_c2_1,
   921            [node_c2_2.id]: node_c2_2,
   922            [node_c2_3.id]: node_c2_3,
   923          },
   924          c3: {
   925            [node_c3_1.id]: node_c3_1,
   926          },
   927        },
   928        nodeMap: {
   929          [node_c1_1.id]: node_c1_1,
   930          [node_c2_1.id]: node_c2_1,
   931          [node_c2_2.id]: node_c2_2,
   932          [node_c2_3.id]: node_c2_3,
   933          [node_c3_1.id]: node_c3_1,
   934        },
   935        nodes: [node_c1_1, node_c2_1, node_c2_2, node_c2_3, node_c3_1],
   936        edgeMap: {
   937          [edge_c1_1_c2_1.id]: edge_c1_1_c2_1,
   938          [edge_c1_1_c2_2.id]: edge_c1_1_c2_2,
   939          [edge_c1_1_c2_3.id]: edge_c1_1_c2_3,
   940          [edge_c3_1_c2_1.id]: edge_c3_1_c2_1,
   941          [edge_c3_1_c2_2.id]: edge_c3_1_c2_2,
   942          [edge_c3_1_c2_3.id]: edge_c3_1_c2_3,
   943        },
   944        edges: [
   945          edge_c1_1_c2_1,
   946          edge_c1_1_c2_2,
   947          edge_c1_1_c2_3,
   948          edge_c3_1_c2_1,
   949          edge_c3_1_c2_2,
   950          edge_c3_1_c2_3,
   951        ],
   952        root_nodes: { [node_c1_1.id]: node_c1_1, [node_c3_1.id]: node_c3_1 },
   953        categories: {
   954          [category_1.id]: category_1,
   955          [category_2.id]: category_2,
   956          [category_3.id]: category_3,
   957        },
   958      };
   959      // @ts-ignore
   960      const nodesAndEdges = foldNodesAndEdges(nodesAndEdgesInput);
   961      delete nodesAndEdges.graph;
   962      const foldedNode = {
   963        id: "fold-c2-1",
   964        category: "c2",
   965        depth: null,
   966        href: null,
   967        icon: undefined,
   968        isFolded: true,
   969        foldedNodes: [
   970          { id: node_c2_1.id, title: node_c2_1.title },
   971          { id: node_c2_2.id, title: node_c2_2.title },
   972          { id: node_c2_3.id, title: node_c2_3.title },
   973        ],
   974        row_data: null,
   975        symbol: null,
   976        title: null,
   977      };
   978      expect(nodesAndEdges).toEqual({
   979        nodeCategoryMap: {
   980          c1: {
   981            [node_c1_1.id]: node_c1_1,
   982          },
   983          c2: {
   984            [foldedNode.id]: foldedNode,
   985          },
   986          c3: {
   987            [node_c3_1.id]: node_c3_1,
   988          },
   989        },
   990        nodeMap: {
   991          [node_c1_1.id]: node_c1_1,
   992          [node_c3_1.id]: node_c3_1,
   993          [foldedNode.id]: foldedNode,
   994        },
   995        nodes: [node_c1_1, node_c3_1, foldedNode],
   996        edgeMap: {
   997          "c1-1_fold-c2-1": {
   998            id: "c1-1_fold-c2-1",
   999            from_id: "c1-1",
  1000            to_id: "fold-c2-1",
  1001            category: null,
  1002            title: null,
  1003            isFolded: true,
  1004            row_data: null,
  1005          },
  1006          "c3-1_fold-c2-1": {
  1007            id: "c3-1_fold-c2-1",
  1008            from_id: "c3-1",
  1009            to_id: "fold-c2-1",
  1010            category: null,
  1011            title: null,
  1012            isFolded: true,
  1013            row_data: null,
  1014          },
  1015        },
  1016        edges: [
  1017          {
  1018            id: "c1-1_fold-c2-1",
  1019            from_id: "c1-1",
  1020            to_id: "fold-c2-1",
  1021            category: null,
  1022            title: null,
  1023            isFolded: true,
  1024            row_data: null,
  1025          },
  1026          {
  1027            id: "c3-1_fold-c2-1",
  1028            from_id: "c3-1",
  1029            to_id: "fold-c2-1",
  1030            category: null,
  1031            title: null,
  1032            isFolded: true,
  1033            row_data: null,
  1034          },
  1035        ],
  1036        root_nodes: { [node_c1_1.id]: node_c1_1, [node_c3_1.id]: node_c3_1 },
  1037        categories: {
  1038          [category_1.id]: category_1,
  1039          [category_2.id]: category_2,
  1040          [category_3.id]: category_3,
  1041        },
  1042      });
  1043    });
  1044  
  1045    test("3-way fold", () => {
  1046      const graph = new Graph({ directed: true });
  1047      graph.setNode("c1-1");
  1048      graph.setNode("c2-1");
  1049      graph.setNode("c2-2");
  1050      graph.setNode("c2-3");
  1051      graph.setNode("c3-1");
  1052      graph.setNode("c4-1");
  1053      graph.setEdge("c1-1", "c2-1");
  1054      graph.setEdge("c1-1", "c2-2");
  1055      graph.setEdge("c1-1", "c2-3");
  1056      graph.setEdge("c3-1", "c2-1");
  1057      graph.setEdge("c3-1", "c2-2");
  1058      graph.setEdge("c3-1", "c2-3");
  1059      graph.setEdge("c4-1", "c2-1");
  1060      graph.setEdge("c4-1", "c2-2");
  1061      graph.setEdge("c4-1", "c2-3");
  1062      const node_c1_1 = createNode({
  1063        id: "c1-1",
  1064        category: "c1",
  1065      });
  1066      const node_c2_1 = createNode({
  1067        id: "c2-1",
  1068        category: "c2",
  1069      });
  1070      const node_c2_2 = createNode({
  1071        id: "c2-2",
  1072        category: "c2",
  1073      });
  1074      const node_c2_3 = createNode({
  1075        id: "c2-3",
  1076        category: "c2",
  1077      });
  1078      const node_c3_1 = createNode({
  1079        id: "c3-1",
  1080        category: "c3",
  1081      });
  1082      const node_c4_1 = createNode({
  1083        id: "c4-1",
  1084        category: "c4",
  1085      });
  1086      const edge_c1_1_c2_1 = createEdge({
  1087        id: "c1-1_c2-1",
  1088        from_id: "c1-1",
  1089        to_id: "c2-1",
  1090      });
  1091      const edge_c1_1_c2_2 = createEdge({
  1092        id: "c1-1_c2-2",
  1093        from_id: "c1-1",
  1094        to_id: "c2-2",
  1095      });
  1096      const edge_c1_1_c2_3 = createEdge({
  1097        id: "c1-1_c2-3",
  1098        from_id: "c1-1",
  1099        to_id: "c2-3",
  1100      });
  1101      const edge_c3_1_c2_1 = createEdge({
  1102        id: "c3-1_c2-1",
  1103        from_id: "c3-1",
  1104        to_id: "c2-1",
  1105      });
  1106      const edge_c3_1_c2_2 = createEdge({
  1107        id: "c3-1_c2-2",
  1108        from_id: "c3-1",
  1109        to_id: "c2-2",
  1110      });
  1111      const edge_c3_1_c2_3 = createEdge({
  1112        id: "c3-1_c2-3",
  1113        from_id: "c3-1",
  1114        to_id: "c2-3",
  1115      });
  1116      const edge_c4_1_c2_1 = createEdge({
  1117        id: "c4-1_c2-1",
  1118        from_id: "c4-1",
  1119        to_id: "c2-1",
  1120      });
  1121      const edge_c4_1_c2_2 = createEdge({
  1122        id: "c4-1_c2-2",
  1123        from_id: "c4-1",
  1124        to_id: "c2-2",
  1125      });
  1126      const edge_c4_1_c2_3 = createEdge({
  1127        id: "c4-1_c2-3",
  1128        from_id: "c4-1",
  1129        to_id: "c2-3",
  1130      });
  1131      const category_1 = { id: "c1" };
  1132      const category_2 = { id: "c2", fold: { threshold: 2 } };
  1133      const category_3 = { id: "c3" };
  1134      const category_4 = { id: "c4" };
  1135      const nodesAndEdgesInput = {
  1136        graph,
  1137        nodeCategoryMap: {
  1138          c1: {
  1139            [node_c1_1.id]: node_c1_1,
  1140          },
  1141          c2: {
  1142            [node_c2_1.id]: node_c2_1,
  1143            [node_c2_2.id]: node_c2_2,
  1144            [node_c2_3.id]: node_c2_3,
  1145          },
  1146          c3: {
  1147            [node_c3_1.id]: node_c3_1,
  1148          },
  1149          c4: {
  1150            [node_c4_1.id]: node_c4_1,
  1151          },
  1152        },
  1153        edgeMap: {
  1154          [edge_c1_1_c2_1.id]: edge_c1_1_c2_1,
  1155          [edge_c1_1_c2_2.id]: edge_c1_1_c2_2,
  1156          [edge_c1_1_c2_3.id]: edge_c1_1_c2_3,
  1157          [edge_c3_1_c2_1.id]: edge_c3_1_c2_1,
  1158          [edge_c3_1_c2_2.id]: edge_c3_1_c2_2,
  1159          [edge_c3_1_c2_3.id]: edge_c3_1_c2_3,
  1160          [edge_c4_1_c2_1.id]: edge_c4_1_c2_1,
  1161          [edge_c4_1_c2_2.id]: edge_c4_1_c2_2,
  1162          [edge_c4_1_c2_3.id]: edge_c4_1_c2_3,
  1163        },
  1164        edges: [
  1165          edge_c1_1_c2_1,
  1166          edge_c1_1_c2_2,
  1167          edge_c1_1_c2_3,
  1168          edge_c3_1_c2_1,
  1169          edge_c3_1_c2_2,
  1170          edge_c3_1_c2_3,
  1171          edge_c4_1_c2_1,
  1172          edge_c4_1_c2_2,
  1173          edge_c4_1_c2_3,
  1174        ],
  1175        nodeMap: {
  1176          [node_c1_1.id]: node_c1_1,
  1177          [node_c2_1.id]: node_c2_1,
  1178          [node_c2_2.id]: node_c2_2,
  1179          [node_c2_3.id]: node_c2_3,
  1180          [node_c3_1.id]: node_c3_1,
  1181          [node_c4_1.id]: node_c4_1,
  1182        },
  1183        nodes: [node_c1_1, node_c2_1, node_c2_2, node_c2_3, node_c3_1, node_c4_1],
  1184        root_nodes: {
  1185          [node_c1_1.id]: node_c1_1,
  1186          [node_c3_1.id]: node_c3_1,
  1187          [node_c4_1.id]: node_c4_1,
  1188        },
  1189        categories: {
  1190          [category_1.id]: category_1,
  1191          [category_2.id]: category_2,
  1192          [category_3.id]: category_3,
  1193          [category_4.id]: category_4,
  1194        },
  1195      };
  1196      // @ts-ignore
  1197      const nodesAndEdges = foldNodesAndEdges(nodesAndEdgesInput);
  1198      delete nodesAndEdges.graph;
  1199      const foldedNode = {
  1200        id: "fold-c2-1",
  1201        category: "c2",
  1202        depth: null,
  1203        href: null,
  1204        icon: undefined,
  1205        isFolded: true,
  1206        foldedNodes: [
  1207          { id: node_c2_1.id, title: node_c2_1.title },
  1208          { id: node_c2_2.id, title: node_c2_2.title },
  1209          { id: node_c2_3.id, title: node_c2_3.title },
  1210        ],
  1211        row_data: null,
  1212        symbol: null,
  1213        title: null,
  1214      };
  1215      expect(nodesAndEdges).toEqual({
  1216        nodeCategoryMap: {
  1217          c1: {
  1218            [node_c1_1.id]: node_c1_1,
  1219          },
  1220          c2: {
  1221            [foldedNode.id]: foldedNode,
  1222          },
  1223          c3: {
  1224            [node_c3_1.id]: node_c3_1,
  1225          },
  1226          c4: {
  1227            [node_c4_1.id]: node_c4_1,
  1228          },
  1229        },
  1230        nodeMap: {
  1231          [node_c1_1.id]: node_c1_1,
  1232          [node_c3_1.id]: node_c3_1,
  1233          [node_c4_1.id]: node_c4_1,
  1234          [foldedNode.id]: foldedNode,
  1235        },
  1236        nodes: [node_c1_1, node_c3_1, node_c4_1, foldedNode],
  1237        edgeMap: {
  1238          "c1-1_fold-c2-1": {
  1239            id: "c1-1_fold-c2-1",
  1240            from_id: "c1-1",
  1241            to_id: "fold-c2-1",
  1242            category: null,
  1243            title: null,
  1244            isFolded: true,
  1245            row_data: null,
  1246          },
  1247          "c3-1_fold-c2-1": {
  1248            id: "c3-1_fold-c2-1",
  1249            from_id: "c3-1",
  1250            to_id: "fold-c2-1",
  1251            category: null,
  1252            title: null,
  1253            isFolded: true,
  1254            row_data: null,
  1255          },
  1256          "c4-1_fold-c2-1": {
  1257            id: "c4-1_fold-c2-1",
  1258            from_id: "c4-1",
  1259            to_id: "fold-c2-1",
  1260            category: null,
  1261            title: null,
  1262            isFolded: true,
  1263            row_data: null,
  1264          },
  1265        },
  1266        edges: [
  1267          {
  1268            id: "c1-1_fold-c2-1",
  1269            from_id: "c1-1",
  1270            to_id: "fold-c2-1",
  1271            category: null,
  1272            title: null,
  1273            isFolded: true,
  1274            row_data: null,
  1275          },
  1276          {
  1277            id: "c3-1_fold-c2-1",
  1278            from_id: "c3-1",
  1279            to_id: "fold-c2-1",
  1280            category: null,
  1281            title: null,
  1282            isFolded: true,
  1283            row_data: null,
  1284          },
  1285          {
  1286            id: "c4-1_fold-c2-1",
  1287            from_id: "c4-1",
  1288            to_id: "fold-c2-1",
  1289            category: null,
  1290            title: null,
  1291            isFolded: true,
  1292            row_data: null,
  1293          },
  1294        ],
  1295        root_nodes: {
  1296          [node_c1_1.id]: node_c1_1,
  1297          [node_c3_1.id]: node_c3_1,
  1298          [node_c4_1.id]: node_c4_1,
  1299        },
  1300        categories: {
  1301          [category_1.id]: category_1,
  1302          [category_2.id]: category_2,
  1303          [category_3.id]: category_3,
  1304          [category_4.id]: category_4,
  1305        },
  1306      });
  1307    });
  1308  
  1309    test("Multiple inheritance", () => {
  1310      const graph = new Graph({ directed: true });
  1311      graph.setNode("c1-1");
  1312      graph.setNode("c2-1");
  1313      graph.setNode("c2-2");
  1314      graph.setNode("c2-3");
  1315      graph.setNode("c3-1");
  1316      graph.setEdge("c1-1", "c2-1");
  1317      graph.setEdge("c1-1", "c2-2");
  1318      graph.setEdge("c1-1", "c2-3");
  1319      graph.setEdge("c3-1", "c2-3");
  1320      const node_c1_1 = createNode({
  1321        id: "c1-1",
  1322        category: "c1",
  1323      });
  1324      const node_c2_1 = createNode({
  1325        id: "c2-1",
  1326        category: "c2",
  1327      });
  1328      const node_c2_2 = createNode({
  1329        id: "c2-2",
  1330        category: "c2",
  1331      });
  1332      const node_c2_3 = createNode({
  1333        id: "c2-3",
  1334        category: "c2",
  1335      });
  1336      const node_c3_1 = createNode({
  1337        id: "c3-1",
  1338        category: "c3",
  1339      });
  1340      const edge_c1_1_c2_1 = createEdge({
  1341        id: "c1-1_c2-1",
  1342        from_id: "c1-1",
  1343        to_id: "c2-1",
  1344      });
  1345      const edge_c1_1_c2_2 = createEdge({
  1346        id: "c1-1_c2-2",
  1347        from_id: "c1-1",
  1348        to_id: "c2-2",
  1349      });
  1350      const edge_c1_1_c2_3 = createEdge({
  1351        id: "c1-1_c2-3",
  1352        from_id: "c1-1",
  1353        to_id: "c2-3",
  1354      });
  1355      const edge_c3_1_c2_3 = createEdge({
  1356        id: "c3-1_c2-3",
  1357        from_id: "c3-1",
  1358        to_id: "c2-3",
  1359      });
  1360      const category_1 = {};
  1361      const category_2 = { fold: { threshold: 2 } };
  1362      const category_3 = {};
  1363      const nodesAndEdgesInput = {
  1364        graph,
  1365        edgeMap: {
  1366          [edge_c1_1_c2_1.id]: edge_c1_1_c2_1,
  1367          [edge_c1_1_c2_2.id]: edge_c1_1_c2_2,
  1368          [edge_c1_1_c2_3.id]: edge_c1_1_c2_3,
  1369          [edge_c3_1_c2_3.id]: edge_c3_1_c2_3,
  1370        },
  1371        edges: [edge_c1_1_c2_1, edge_c1_1_c2_2, edge_c1_1_c2_3, edge_c3_1_c2_3],
  1372        nodeCategoryMap: {
  1373          c1: {
  1374            [node_c1_1.id]: node_c1_1,
  1375          },
  1376          c2: {
  1377            [node_c2_1.id]: node_c2_1,
  1378            [node_c2_2.id]: node_c2_2,
  1379            [node_c2_3.id]: node_c2_3,
  1380          },
  1381          c3: {
  1382            [node_c3_1.id]: node_c3_1,
  1383          },
  1384        },
  1385        nodeMap: {
  1386          [node_c1_1.id]: node_c1_1,
  1387          [node_c2_1.id]: node_c2_1,
  1388          [node_c2_2.id]: node_c2_2,
  1389          [node_c2_3.id]: node_c2_3,
  1390          [node_c3_1.id]: node_c3_1,
  1391        },
  1392        nodes: [node_c1_1, node_c2_1, node_c2_2, node_c2_3, node_c3_1],
  1393        root_nodes: { [node_c1_1.id]: node_c1_1, [node_c3_1.id]: node_c3_1 },
  1394        categories: { c1: category_1, c2: category_2, c3: category_3 },
  1395      };
  1396      // @ts-ignore
  1397      const nodesAndEdges = foldNodesAndEdges(nodesAndEdgesInput);
  1398      delete nodesAndEdges.graph;
  1399      const foldedNode = {
  1400        id: "fold-c2-1",
  1401        category: "c2",
  1402        depth: null,
  1403        href: null,
  1404        icon: undefined,
  1405        isFolded: true,
  1406        foldedNodes: [
  1407          { id: node_c2_1.id, title: node_c2_1.title },
  1408          { id: node_c2_2.id, title: node_c2_2.title },
  1409        ],
  1410        row_data: null,
  1411        symbol: null,
  1412        title: null,
  1413      };
  1414      expect(nodesAndEdges).toEqual({
  1415        nodeCategoryMap: {
  1416          c1: {
  1417            [node_c1_1.id]: node_c1_1,
  1418          },
  1419          c2: {
  1420            [node_c2_3.id]: node_c2_3,
  1421            [foldedNode.id]: foldedNode,
  1422          },
  1423          c3: {
  1424            [node_c3_1.id]: node_c3_1,
  1425          },
  1426        },
  1427        nodeMap: {
  1428          [node_c1_1.id]: node_c1_1,
  1429          [node_c2_3.id]: node_c2_3,
  1430          [node_c3_1.id]: node_c3_1,
  1431          [foldedNode.id]: foldedNode,
  1432        },
  1433        nodes: [node_c1_1, node_c2_3, node_c3_1, foldedNode],
  1434        edgeMap: {
  1435          [edge_c1_1_c2_3.id]: edge_c1_1_c2_3,
  1436          [edge_c3_1_c2_3.id]: edge_c3_1_c2_3,
  1437          "c1-1_fold-c2-1": {
  1438            id: "c1-1_fold-c2-1",
  1439            from_id: "c1-1",
  1440            to_id: "fold-c2-1",
  1441            category: null,
  1442            title: null,
  1443            isFolded: true,
  1444            row_data: null,
  1445          },
  1446        },
  1447        edges: [
  1448          edge_c1_1_c2_3,
  1449          edge_c3_1_c2_3,
  1450          {
  1451            id: "c1-1_fold-c2-1",
  1452            from_id: "c1-1",
  1453            to_id: "fold-c2-1",
  1454            category: null,
  1455            title: null,
  1456            isFolded: true,
  1457            row_data: null,
  1458          },
  1459        ],
  1460        root_nodes: { [node_c1_1.id]: node_c1_1, [node_c3_1.id]: node_c3_1 },
  1461        categories: { c1: category_1, c2: category_2, c3: category_3 },
  1462      });
  1463    });
  1464  
  1465    test("Edge direction is included in fold group decision, so this does not collapse", () => {
  1466      const graph = new Graph({ directed: true });
  1467      graph.setNode("c1-1");
  1468      graph.setNode("c2-1");
  1469      graph.setNode("c2-2");
  1470      graph.setNode("c3-1");
  1471      graph.setEdge("c1-1", "c2-1");
  1472      graph.setEdge("c1-1", "c2-2");
  1473      graph.setEdge("c3-1", "c2-1");
  1474      graph.setEdge("c2-2", "c3-1");
  1475      const node_c1_1 = createNode({
  1476        id: "c1-1",
  1477        category: "c1",
  1478      });
  1479      const node_c2_1 = createNode({
  1480        id: "c2-1",
  1481        category: "c2",
  1482      });
  1483      const node_c2_2 = createNode({
  1484        id: "c2-2",
  1485        category: "c2",
  1486      });
  1487      const node_c3_1 = createNode({
  1488        id: "c3-1",
  1489        category: "c3",
  1490      });
  1491      const edge_c1_1_c2_1 = createEdge({
  1492        id: "c1-1_c2-1",
  1493        from_id: "c1-1",
  1494        to_id: "c2-1",
  1495      });
  1496      const edge_c1_1_c2_2 = createEdge({
  1497        id: "c1-1_c2-2",
  1498        from_id: "c1-1",
  1499        to_id: "c2-2",
  1500      });
  1501      const edge_c3_1_c2_1 = createEdge({
  1502        id: "c3-1_c2-1",
  1503        from_id: "c3-1",
  1504        to_id: "c2-1",
  1505      });
  1506      const edge_c2_2_c3_1 = createEdge({
  1507        id: "c2-2_c3-1",
  1508        from_id: "c2-2",
  1509        to_id: "c3-1",
  1510      });
  1511      const category_1 = { id: "c1" };
  1512      const category_2 = { id: "c2", fold: { threshold: 2 } };
  1513      const category_3 = { id: "c3" };
  1514      const nodesAndEdgesInput = {
  1515        graph,
  1516        nodeCategoryMap: {
  1517          c1: {
  1518            [node_c1_1.id]: node_c1_1,
  1519          },
  1520          c2: {
  1521            [node_c2_1.id]: node_c2_1,
  1522            [node_c2_2.id]: node_c2_2,
  1523          },
  1524          c3: {
  1525            [node_c3_1.id]: node_c3_1,
  1526          },
  1527        },
  1528        nodeMap: {
  1529          [node_c1_1.id]: node_c1_1,
  1530          [node_c2_1.id]: node_c2_1,
  1531          [node_c2_2.id]: node_c2_2,
  1532          [node_c3_1.id]: node_c3_1,
  1533        },
  1534        nodes: [node_c1_1, node_c2_1, node_c2_2, node_c3_1],
  1535        edgeMap: {
  1536          [edge_c1_1_c2_1.id]: edge_c1_1_c2_1,
  1537          [edge_c1_1_c2_2.id]: edge_c1_1_c2_2,
  1538          [edge_c3_1_c2_1.id]: edge_c3_1_c2_1,
  1539          [edge_c2_2_c3_1.id]: edge_c2_2_c3_1,
  1540        },
  1541        edges: [edge_c1_1_c2_1, edge_c1_1_c2_2, edge_c3_1_c2_1, edge_c2_2_c3_1],
  1542        root_nodes: { [node_c1_1.id]: node_c1_1 },
  1543        categories: {
  1544          [category_1.id]: category_1,
  1545          [category_2.id]: category_2,
  1546          [category_3.id]: category_3,
  1547        },
  1548      };
  1549      // @ts-ignore
  1550      const nodesAndEdges = foldNodesAndEdges(nodesAndEdgesInput);
  1551      delete nodesAndEdges.graph;
  1552      delete nodesAndEdgesInput.graph;
  1553      expect(nodesAndEdges).toEqual(nodesAndEdgesInput);
  1554    });
  1555  
  1556    test("All edges are included in fold group decision", () => {
  1557      const graph = new Graph({ directed: true });
  1558      graph.setNode("c1-1");
  1559      graph.setNode("c2-1");
  1560      graph.setNode("c2-2");
  1561      graph.setNode("c2-3");
  1562      graph.setNode("c3-1");
  1563      graph.setNode("c4-1");
  1564      graph.setEdge("c1-1", "c2-1");
  1565      graph.setEdge("c1-1", "c2-2");
  1566      graph.setEdge("c1-1", "c2-3");
  1567      graph.setEdge("c3-1", "c2-1");
  1568      graph.setEdge("c3-1", "c2-2");
  1569      graph.setEdge("c3-1", "c2-3");
  1570      graph.setEdge("c4-1", "c2-3");
  1571      const node_c1_1 = createNode({
  1572        id: "c1-1",
  1573        category: "c1",
  1574      });
  1575      const node_c2_1 = createNode({
  1576        id: "c2-1",
  1577        category: "c2",
  1578      });
  1579      const node_c2_2 = createNode({
  1580        id: "c2-2",
  1581        category: "c2",
  1582      });
  1583      const node_c2_3 = createNode({
  1584        id: "c2-3",
  1585        category: "c2",
  1586      });
  1587      const node_c3_1 = createNode({
  1588        id: "c3-1",
  1589        category: "c3",
  1590      });
  1591      const node_c4_1 = createNode({
  1592        id: "c4-1",
  1593        category: "c4",
  1594      });
  1595      const edge_c1_1_c2_1 = createEdge({
  1596        id: "c1-1_c2-1",
  1597        from_id: "c1-1",
  1598        to_id: "c2-1",
  1599      });
  1600      const edge_c1_1_c2_2 = createEdge({
  1601        id: "c1-1_c2-2",
  1602        from_id: "c1-1",
  1603        to_id: "c2-2",
  1604      });
  1605      const edge_c1_1_c2_3 = createEdge({
  1606        id: "c1-1_c2-3",
  1607        from_id: "c1-1",
  1608        to_id: "c2-3",
  1609      });
  1610      const edge_c3_1_c2_1 = createEdge({
  1611        id: "c3-1_c2-1",
  1612        from_id: "c3-1",
  1613        to_id: "c2-1",
  1614      });
  1615      const edge_c3_1_c2_2 = createEdge({
  1616        id: "c3-1_c2-2",
  1617        from_id: "c3-1",
  1618        to_id: "c2-2",
  1619      });
  1620      const edge_c3_1_c2_3 = createEdge({
  1621        id: "c3-1_c2-3",
  1622        from_id: "c3-1",
  1623        to_id: "c2-3",
  1624      });
  1625      const edge_c4_1_c2_3 = createEdge({
  1626        id: "c4-1_c2-3",
  1627        from_id: "c4-1",
  1628        to_id: "c2-3",
  1629      });
  1630      const category_1 = { id: "c1" };
  1631      const category_2 = { id: "c2", fold: { threshold: 2 } };
  1632      const category_3 = { id: "c3" };
  1633      const category_4 = { id: "c4" };
  1634      const nodesAndEdgesInput = {
  1635        graph,
  1636        nodeCategoryMap: {
  1637          c1: {
  1638            [node_c1_1.id]: node_c1_1,
  1639          },
  1640          c2: {
  1641            [node_c2_1.id]: node_c2_1,
  1642            [node_c2_2.id]: node_c2_2,
  1643            [node_c2_3.id]: node_c2_3,
  1644          },
  1645          c3: {
  1646            [node_c3_1.id]: node_c3_1,
  1647          },
  1648          c4: {
  1649            [node_c4_1.id]: node_c4_1,
  1650          },
  1651        },
  1652        nodeMap: {
  1653          [node_c1_1.id]: node_c1_1,
  1654          [node_c2_1.id]: node_c2_1,
  1655          [node_c2_2.id]: node_c2_2,
  1656          [node_c2_3.id]: node_c2_3,
  1657          [node_c3_1.id]: node_c3_1,
  1658          [node_c4_1.id]: node_c4_1,
  1659        },
  1660        nodes: [node_c1_1, node_c2_1, node_c2_2, node_c2_3, node_c3_1, node_c4_1],
  1661        edgeMap: {
  1662          [edge_c1_1_c2_1.id]: edge_c1_1_c2_1,
  1663          [edge_c1_1_c2_2.id]: edge_c1_1_c2_2,
  1664          [edge_c1_1_c2_3.id]: edge_c1_1_c2_3,
  1665          [edge_c3_1_c2_1.id]: edge_c3_1_c2_1,
  1666          [edge_c3_1_c2_2.id]: edge_c3_1_c2_2,
  1667          [edge_c3_1_c2_3.id]: edge_c3_1_c2_3,
  1668          [edge_c4_1_c2_3.id]: edge_c4_1_c2_3,
  1669        },
  1670        edges: [
  1671          edge_c1_1_c2_1,
  1672          edge_c1_1_c2_2,
  1673          edge_c1_1_c2_3,
  1674          edge_c3_1_c2_1,
  1675          edge_c3_1_c2_2,
  1676          edge_c3_1_c2_3,
  1677          edge_c4_1_c2_3,
  1678        ],
  1679        root_nodes: {
  1680          [node_c1_1.id]: node_c1_1,
  1681          [node_c3_1.id]: node_c3_1,
  1682          [node_c4_1.id]: node_c4_1,
  1683        },
  1684        categories: {
  1685          [category_1.id]: category_1,
  1686          [category_2.id]: category_2,
  1687          [category_3.id]: category_3,
  1688          [category_4.id]: category_4,
  1689        },
  1690      };
  1691      // @ts-ignore
  1692      const nodesAndEdges = foldNodesAndEdges(nodesAndEdgesInput);
  1693      delete nodesAndEdges.graph;
  1694      const foldedNode = {
  1695        id: "fold-c2-1",
  1696        category: "c2",
  1697        depth: null,
  1698        href: null,
  1699        icon: undefined,
  1700        isFolded: true,
  1701        foldedNodes: [
  1702          { id: node_c2_1.id, title: node_c2_1.title },
  1703          { id: node_c2_2.id, title: node_c2_2.title },
  1704        ],
  1705        row_data: null,
  1706        symbol: null,
  1707        title: null,
  1708      };
  1709      expect(nodesAndEdges).toEqual({
  1710        nodeCategoryMap: {
  1711          c1: {
  1712            [node_c1_1.id]: node_c1_1,
  1713          },
  1714          c2: {
  1715            [node_c2_3.id]: node_c2_3,
  1716            [foldedNode.id]: foldedNode,
  1717          },
  1718          c3: {
  1719            [node_c3_1.id]: node_c3_1,
  1720          },
  1721          c4: {
  1722            [node_c4_1.id]: node_c4_1,
  1723          },
  1724        },
  1725        nodeMap: {
  1726          [node_c1_1.id]: node_c1_1,
  1727          [node_c2_3.id]: node_c2_3,
  1728          [node_c3_1.id]: node_c3_1,
  1729          [node_c4_1.id]: node_c4_1,
  1730          [foldedNode.id]: foldedNode,
  1731        },
  1732        nodes: [node_c1_1, node_c2_3, node_c3_1, node_c4_1, foldedNode],
  1733        edgeMap: {
  1734          [edge_c1_1_c2_3.id]: edge_c1_1_c2_3,
  1735          [edge_c3_1_c2_3.id]: edge_c3_1_c2_3,
  1736          [edge_c4_1_c2_3.id]: edge_c4_1_c2_3,
  1737          "c1-1_fold-c2-1": {
  1738            id: "c1-1_fold-c2-1",
  1739            from_id: "c1-1",
  1740            to_id: "fold-c2-1",
  1741            category: null,
  1742            title: null,
  1743            isFolded: true,
  1744            row_data: null,
  1745          },
  1746          "c3-1_fold-c2-1": {
  1747            id: "c3-1_fold-c2-1",
  1748            from_id: "c3-1",
  1749            to_id: "fold-c2-1",
  1750            category: null,
  1751            title: null,
  1752            isFolded: true,
  1753            row_data: null,
  1754          },
  1755        },
  1756        edges: [
  1757          edge_c1_1_c2_3,
  1758          edge_c3_1_c2_3,
  1759          edge_c4_1_c2_3,
  1760          {
  1761            id: "c1-1_fold-c2-1",
  1762            from_id: "c1-1",
  1763            to_id: "fold-c2-1",
  1764            category: null,
  1765            title: null,
  1766            isFolded: true,
  1767            row_data: null,
  1768          },
  1769          {
  1770            id: "c3-1_fold-c2-1",
  1771            from_id: "c3-1",
  1772            to_id: "fold-c2-1",
  1773            category: null,
  1774            title: null,
  1775            isFolded: true,
  1776            row_data: null,
  1777          },
  1778        ],
  1779        root_nodes: {
  1780          [node_c1_1.id]: node_c1_1,
  1781          [node_c3_1.id]: node_c3_1,
  1782          [node_c4_1.id]: node_c4_1,
  1783        },
  1784        categories: {
  1785          [category_1.id]: category_1,
  1786          [category_2.id]: category_2,
  1787          [category_3.id]: category_3,
  1788          [category_4.id]: category_4,
  1789        },
  1790      });
  1791    });
  1792  });