PagerankTable displays aggregated connections
Previously, expanding a node would display the individual connections that contributed cred to that node. For nodes with high degree, this was a pretty noisy UI. Now, expanding a node displays "aggregations": for every type of adjacent connection (where type is the union of the edge type and the adjacent node type), we show a summary of the total cred from connections of that type. The result is a much more managable summary view. Naturally, these aggregations can be further expanded to see the individual connections. Closes #502. Test plan: The new behavior is unit tested. You can also launch the cred explorer and experience the UI directly. I have used the new UI a lot, as well as demo'd it to people, and I like it quite a bit.
This commit is contained in:
parent
8df0056f08
commit
094582be32
|
@ -2,4 +2,5 @@
|
||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
- Start tracking changes in `CHANGELOG.md`
|
- Start tracking changes in `CHANGELOG.md`
|
||||||
|
- Aggregate over connection types in the cred explorer (#502)
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,111 @@
|
||||||
|
// @flow
|
||||||
|
|
||||||
|
import React from "react";
|
||||||
|
import * as NullUtil from "../../../util/null";
|
||||||
|
|
||||||
|
import type {NodeAddressT} from "../../../core/graph";
|
||||||
|
import {ConnectionRowList} from "./Connection";
|
||||||
|
|
||||||
|
import {aggregateFlat, type FlatAggregation, aggregationKey} from "./aggregate";
|
||||||
|
|
||||||
|
import {Badge, type SharedProps} from "./shared";
|
||||||
|
import {TableRow} from "./TableRow";
|
||||||
|
|
||||||
|
type AggregationRowListProps = {|
|
||||||
|
+depth: number,
|
||||||
|
+node: NodeAddressT,
|
||||||
|
+sharedProps: SharedProps,
|
||||||
|
|};
|
||||||
|
|
||||||
|
export class AggregationRowList extends React.PureComponent<
|
||||||
|
AggregationRowListProps
|
||||||
|
> {
|
||||||
|
render() {
|
||||||
|
const {depth, node, sharedProps} = this.props;
|
||||||
|
const {pnd, adapters} = sharedProps;
|
||||||
|
const {scoredConnections} = NullUtil.get(pnd.get(node));
|
||||||
|
const aggregations = aggregateFlat(
|
||||||
|
scoredConnections,
|
||||||
|
adapters.static().nodeTypes(),
|
||||||
|
adapters.static().edgeTypes()
|
||||||
|
);
|
||||||
|
return (
|
||||||
|
<React.Fragment>
|
||||||
|
{aggregations.map((agg) => (
|
||||||
|
<AggregationRow
|
||||||
|
key={aggregationKey(agg)}
|
||||||
|
depth={depth}
|
||||||
|
target={node}
|
||||||
|
sharedProps={sharedProps}
|
||||||
|
aggregation={agg}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type AggregationRowProps = {|
|
||||||
|
+depth: number,
|
||||||
|
+target: NodeAddressT,
|
||||||
|
+aggregation: FlatAggregation,
|
||||||
|
+sharedProps: SharedProps,
|
||||||
|
|};
|
||||||
|
|
||||||
|
export class AggregationRow extends React.PureComponent<AggregationRowProps> {
|
||||||
|
render() {
|
||||||
|
const {sharedProps, target, depth, aggregation} = this.props;
|
||||||
|
const {pnd} = sharedProps;
|
||||||
|
const score = aggregation.summary.score;
|
||||||
|
const {score: targetScore} = NullUtil.get(pnd.get(target));
|
||||||
|
const connectionProportion = score / targetScore;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TableRow
|
||||||
|
depth={depth}
|
||||||
|
indent={1}
|
||||||
|
showPadding={false}
|
||||||
|
connectionProportion={connectionProportion}
|
||||||
|
score={score}
|
||||||
|
description={<AggregationView aggregation={aggregation} />}
|
||||||
|
>
|
||||||
|
<ConnectionRowList
|
||||||
|
key="children"
|
||||||
|
depth={depth}
|
||||||
|
node={target}
|
||||||
|
connections={aggregation.connections}
|
||||||
|
sharedProps={sharedProps}
|
||||||
|
/>
|
||||||
|
</TableRow>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class AggregationView extends React.PureComponent<{|
|
||||||
|
+aggregation: FlatAggregation,
|
||||||
|
|}> {
|
||||||
|
render() {
|
||||||
|
const {aggregation} = this.props;
|
||||||
|
const {connectionType, summary, nodeType} = aggregation;
|
||||||
|
function connectionDescription() {
|
||||||
|
switch (connectionType.type) {
|
||||||
|
case "SYNTHETIC_LOOP":
|
||||||
|
return "synthetic loop";
|
||||||
|
case "IN_EDGE":
|
||||||
|
return connectionType.edgeType.backwardName;
|
||||||
|
case "OUT_EDGE":
|
||||||
|
return connectionType.edgeType.forwardName;
|
||||||
|
default:
|
||||||
|
throw new Error((connectionType.type: empty));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const nodeName = summary.size === 1 ? nodeType.name : nodeType.pluralName;
|
||||||
|
return (
|
||||||
|
<span>
|
||||||
|
<Badge>{connectionDescription()}</Badge>
|
||||||
|
<span> {summary.size} </span>
|
||||||
|
<span style={{textTransform: "lowercase"}}>{nodeName}</span>
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,238 @@
|
||||||
|
// @flow
|
||||||
|
|
||||||
|
import React from "react";
|
||||||
|
import {shallow} from "enzyme";
|
||||||
|
import * as NullUtil from "../../../util/null";
|
||||||
|
import {NodeAddress, EdgeAddress} from "../../../core/graph";
|
||||||
|
import type {NodeType, EdgeType} from "../../adapters/pluginAdapter";
|
||||||
|
import {
|
||||||
|
AggregationRowList,
|
||||||
|
AggregationRow,
|
||||||
|
AggregationView,
|
||||||
|
} from "./Aggregation";
|
||||||
|
import {ConnectionRowList} from "./Connection";
|
||||||
|
import {Badge} from "./shared";
|
||||||
|
import {example} from "./sharedTestUtils";
|
||||||
|
import {aggregateFlat, type FlatAggregation} from "./aggregate";
|
||||||
|
import {TableRow} from "./TableRow";
|
||||||
|
|
||||||
|
require("../../testUtil").configureEnzyme();
|
||||||
|
|
||||||
|
describe("app/credExplorer/pagerankTable/Aggregation", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
// $ExpectFlowError
|
||||||
|
console.error = jest.fn();
|
||||||
|
// $ExpectFlowError
|
||||||
|
console.warn = jest.fn();
|
||||||
|
});
|
||||||
|
afterEach(() => {
|
||||||
|
expect(console.warn).not.toHaveBeenCalled();
|
||||||
|
expect(console.error).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
describe("AggregationRowList", () => {
|
||||||
|
it("instantiates AggregationRows for each aggregation", async () => {
|
||||||
|
const {adapters, pnd, nodes} = await example();
|
||||||
|
const node = nodes.bar1;
|
||||||
|
const depth = 20;
|
||||||
|
const maxEntriesPerList = 50;
|
||||||
|
const sharedProps = {adapters, pnd, maxEntriesPerList};
|
||||||
|
const connections = NullUtil.get(pnd.get(node)).scoredConnections;
|
||||||
|
const aggregations = aggregateFlat(
|
||||||
|
connections,
|
||||||
|
adapters.static().nodeTypes(),
|
||||||
|
adapters.static().edgeTypes()
|
||||||
|
);
|
||||||
|
const el = shallow(
|
||||||
|
<AggregationRowList
|
||||||
|
depth={depth}
|
||||||
|
node={node}
|
||||||
|
sharedProps={sharedProps}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
const aggregationRows = el.children(AggregationRow);
|
||||||
|
expect(aggregationRows).toHaveLength(aggregations.length);
|
||||||
|
|
||||||
|
for (let i = 0; i < aggregations.length; i++) {
|
||||||
|
const aggregationRow = aggregationRows.at(i);
|
||||||
|
const props = aggregationRow.props();
|
||||||
|
expect(props.depth).toEqual(depth);
|
||||||
|
expect(props.target).toEqual(node);
|
||||||
|
expect(props.sharedProps).toEqual(sharedProps);
|
||||||
|
expect(props.aggregation).toEqual(aggregations[i]);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("AggregationRow", () => {
|
||||||
|
async function setup() {
|
||||||
|
const {pnd, adapters, nodes} = await example();
|
||||||
|
const sharedProps = {adapters, pnd, maxEntriesPerList: 123};
|
||||||
|
const target = nodes.bar1;
|
||||||
|
const {scoredConnections} = NullUtil.get(pnd.get(target));
|
||||||
|
const aggregations = aggregateFlat(
|
||||||
|
scoredConnections,
|
||||||
|
adapters.static().nodeTypes(),
|
||||||
|
adapters.static().edgeTypes()
|
||||||
|
);
|
||||||
|
const aggregation = aggregations[0];
|
||||||
|
const depth = 23;
|
||||||
|
const component = (
|
||||||
|
<AggregationRow
|
||||||
|
depth={depth}
|
||||||
|
target={target}
|
||||||
|
aggregation={aggregation}
|
||||||
|
sharedProps={sharedProps}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
const element = shallow(component);
|
||||||
|
const row = element.find(TableRow);
|
||||||
|
return {
|
||||||
|
element,
|
||||||
|
row,
|
||||||
|
depth,
|
||||||
|
target,
|
||||||
|
aggregation,
|
||||||
|
sharedProps,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
describe("instantiates a TableRow", () => {
|
||||||
|
it("with the correct depth", async () => {
|
||||||
|
const {row, depth} = await setup();
|
||||||
|
expect(row.props().depth).toBe(depth);
|
||||||
|
});
|
||||||
|
it("with indent=1", async () => {
|
||||||
|
const {row} = await setup();
|
||||||
|
expect(row.props().indent).toBe(1);
|
||||||
|
});
|
||||||
|
it("with showPadding=false", async () => {
|
||||||
|
const {row} = await setup();
|
||||||
|
expect(row.props().showPadding).toBe(false);
|
||||||
|
});
|
||||||
|
it("with the aggregation score", async () => {
|
||||||
|
const {row, aggregation} = await setup();
|
||||||
|
expect(row.props().score).toBe(aggregation.summary.score);
|
||||||
|
});
|
||||||
|
it("with the aggregation's contribution proportion", async () => {
|
||||||
|
const {row, target, aggregation, sharedProps} = await setup();
|
||||||
|
const targetScore = NullUtil.get(sharedProps.pnd.get(target)).score;
|
||||||
|
expect(row.props().connectionProportion).toBe(
|
||||||
|
aggregation.summary.score / targetScore
|
||||||
|
);
|
||||||
|
});
|
||||||
|
it("with a AggregationView as description", async () => {
|
||||||
|
const {row, aggregation} = await setup();
|
||||||
|
const description = row.props().description;
|
||||||
|
const cv = shallow(description).instance();
|
||||||
|
expect(cv).toBeInstanceOf(AggregationView);
|
||||||
|
expect(cv.props.aggregation).toEqual(aggregation);
|
||||||
|
});
|
||||||
|
describe("with a ConnectionRowList as children", () => {
|
||||||
|
function getChildren(row) {
|
||||||
|
const children = row.props().children;
|
||||||
|
return shallow(children).instance();
|
||||||
|
}
|
||||||
|
it("which is a ConnectionRowList", async () => {
|
||||||
|
const {row} = await setup();
|
||||||
|
expect(getChildren(row)).toBeInstanceOf(ConnectionRowList);
|
||||||
|
});
|
||||||
|
it("which has the same depth", async () => {
|
||||||
|
const {row, depth} = await setup();
|
||||||
|
expect(getChildren(row).props.depth).toBe(depth);
|
||||||
|
});
|
||||||
|
it("which has the aggregation target as its node target", async () => {
|
||||||
|
const {row, target} = await setup();
|
||||||
|
expect(getChildren(row).props.node).toBe(target);
|
||||||
|
});
|
||||||
|
it("which has the right sharedProps", async () => {
|
||||||
|
const {row, sharedProps} = await setup();
|
||||||
|
expect(getChildren(row).props.sharedProps).toBe(sharedProps);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe("AggregationView", () => {
|
||||||
|
const nodeType: NodeType = {
|
||||||
|
name: "whatDoes",
|
||||||
|
pluralName: "whatDoth",
|
||||||
|
defaultWeight: 1,
|
||||||
|
prefix: NodeAddress.empty,
|
||||||
|
};
|
||||||
|
const edgeType: EdgeType = {
|
||||||
|
forwardName: "marsellus",
|
||||||
|
backwardName: "wallace",
|
||||||
|
prefix: EdgeAddress.fromParts(["look", "like"]),
|
||||||
|
};
|
||||||
|
function aggView(aggregation: FlatAggregation) {
|
||||||
|
const el = shallow(<AggregationView aggregation={aggregation} />);
|
||||||
|
const stuff = el.find("span").children();
|
||||||
|
const connectionDescription = stuff.at(0);
|
||||||
|
expect(connectionDescription.type()).toBe(Badge);
|
||||||
|
const summarySize = stuff.at(1);
|
||||||
|
expect(summarySize.type()).toBe("span");
|
||||||
|
const nodeName = stuff.at(2);
|
||||||
|
expect(nodeName.type()).toBe("span");
|
||||||
|
return {
|
||||||
|
connectionDescription: connectionDescription.props().children,
|
||||||
|
summarySize: summarySize.text(),
|
||||||
|
nodeName: nodeName.text(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
it("renders a synthetic connection", () => {
|
||||||
|
const synthetic = {
|
||||||
|
nodeType,
|
||||||
|
connectionType: {type: "SYNTHETIC_LOOP"},
|
||||||
|
summary: {size: 1, score: 1},
|
||||||
|
connections: [],
|
||||||
|
};
|
||||||
|
const {connectionDescription, summarySize, nodeName} = aggView(synthetic);
|
||||||
|
expect(connectionDescription).toBe("synthetic loop");
|
||||||
|
expect(summarySize).toBe(" 1 ");
|
||||||
|
expect(nodeName).toBe("whatDoes");
|
||||||
|
});
|
||||||
|
it("renders an inEdge connection", () => {
|
||||||
|
const inEdge = {
|
||||||
|
nodeType,
|
||||||
|
connectionType: {type: "IN_EDGE", edgeType},
|
||||||
|
summary: {size: 2, score: 1},
|
||||||
|
connections: [],
|
||||||
|
};
|
||||||
|
const {connectionDescription, summarySize, nodeName} = aggView(inEdge);
|
||||||
|
expect(connectionDescription).toBe("wallace");
|
||||||
|
expect(summarySize).toBe(" 2 ");
|
||||||
|
expect(nodeName).toBe("whatDoth");
|
||||||
|
});
|
||||||
|
it("renders an outEdge connection", () => {
|
||||||
|
const outEdge = {
|
||||||
|
nodeType,
|
||||||
|
connectionType: {type: "OUT_EDGE", edgeType},
|
||||||
|
summary: {size: 3, score: 1},
|
||||||
|
connections: [],
|
||||||
|
};
|
||||||
|
const {connectionDescription, summarySize, nodeName} = aggView(outEdge);
|
||||||
|
expect(connectionDescription).toBe("marsellus");
|
||||||
|
expect(summarySize).toBe(" 3 ");
|
||||||
|
expect(nodeName).toBe("whatDoth");
|
||||||
|
});
|
||||||
|
it("does not pluralize connections containing one element", () => {
|
||||||
|
const inEdge = {
|
||||||
|
nodeType,
|
||||||
|
connectionType: {type: "IN_EDGE", edgeType},
|
||||||
|
summary: {size: 1, score: 1},
|
||||||
|
connections: [],
|
||||||
|
};
|
||||||
|
const {nodeName} = aggView(inEdge);
|
||||||
|
expect(nodeName).toBe("whatDoes");
|
||||||
|
});
|
||||||
|
it("does pluralize connections containing multiple elements", () => {
|
||||||
|
const inEdge = {
|
||||||
|
nodeType,
|
||||||
|
connectionType: {type: "IN_EDGE", edgeType},
|
||||||
|
summary: {size: 2, score: 1},
|
||||||
|
connections: [],
|
||||||
|
};
|
||||||
|
const {nodeName} = aggView(inEdge);
|
||||||
|
expect(nodeName).toBe("whatDoth");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -16,18 +16,18 @@ type ConnectionRowListProps = {|
|
||||||
+depth: number,
|
+depth: number,
|
||||||
+node: NodeAddressT,
|
+node: NodeAddressT,
|
||||||
+sharedProps: SharedProps,
|
+sharedProps: SharedProps,
|
||||||
|
+connections: $ReadOnlyArray<ScoredConnection>,
|
||||||
|};
|
|};
|
||||||
|
|
||||||
export class ConnectionRowList extends React.PureComponent<
|
export class ConnectionRowList extends React.PureComponent<
|
||||||
ConnectionRowListProps
|
ConnectionRowListProps
|
||||||
> {
|
> {
|
||||||
render() {
|
render() {
|
||||||
const {depth, node, sharedProps} = this.props;
|
const {depth, node, sharedProps, connections} = this.props;
|
||||||
const {pnd, maxEntriesPerList} = sharedProps;
|
const {maxEntriesPerList} = sharedProps;
|
||||||
const {scoredConnections} = NullUtil.get(pnd.get(node));
|
|
||||||
return (
|
return (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
{scoredConnections
|
{connections
|
||||||
.slice(0, maxEntriesPerList)
|
.slice(0, maxEntriesPerList)
|
||||||
.map((sc) => (
|
.map((sc) => (
|
||||||
<ConnectionRow
|
<ConnectionRow
|
||||||
|
@ -67,7 +67,7 @@ export class ConnectionRow extends React.PureComponent<ConnectionRowProps> {
|
||||||
);
|
);
|
||||||
return (
|
return (
|
||||||
<TableRow
|
<TableRow
|
||||||
indent={1}
|
indent={2}
|
||||||
depth={depth}
|
depth={depth}
|
||||||
description={connectionView}
|
description={connectionView}
|
||||||
connectionProportion={connectionProportion}
|
connectionProportion={connectionProportion}
|
||||||
|
|
|
@ -30,11 +30,14 @@ describe("app/credExplorer/pagerankTable/Connection", () => {
|
||||||
const depth = 2;
|
const depth = 2;
|
||||||
const node = nodes.bar1;
|
const node = nodes.bar1;
|
||||||
const sharedProps = {adapters, pnd, maxEntriesPerList};
|
const sharedProps = {adapters, pnd, maxEntriesPerList};
|
||||||
|
const connections = NullUtil.get(sharedProps.pnd.get(node))
|
||||||
|
.scoredConnections;
|
||||||
const component = (
|
const component = (
|
||||||
<ConnectionRowList
|
<ConnectionRowList
|
||||||
depth={depth}
|
depth={depth}
|
||||||
node={node}
|
node={node}
|
||||||
sharedProps={sharedProps}
|
sharedProps={sharedProps}
|
||||||
|
connections={connections}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
const element = shallow(component);
|
const element = shallow(component);
|
||||||
|
@ -99,9 +102,9 @@ describe("app/credExplorer/pagerankTable/Connection", () => {
|
||||||
const {row, depth} = await setup();
|
const {row, depth} = await setup();
|
||||||
expect(row.props().depth).toBe(depth);
|
expect(row.props().depth).toBe(depth);
|
||||||
});
|
});
|
||||||
it("with indent=1", async () => {
|
it("with indent=2", async () => {
|
||||||
const {row} = await setup();
|
const {row} = await setup();
|
||||||
expect(row.props().indent).toBe(1);
|
expect(row.props().indent).toBe(2);
|
||||||
});
|
});
|
||||||
it("with showPadding=false", async () => {
|
it("with showPadding=false", async () => {
|
||||||
const {row} = await setup();
|
const {row} = await setup();
|
||||||
|
@ -132,7 +135,7 @@ describe("app/credExplorer/pagerankTable/Connection", () => {
|
||||||
const children = row.props().children;
|
const children = row.props().children;
|
||||||
return shallow(children).instance();
|
return shallow(children).instance();
|
||||||
}
|
}
|
||||||
it("which is a ConnectionRowList", async () => {
|
it("which is a NodeRow", async () => {
|
||||||
const {row} = await setup();
|
const {row} = await setup();
|
||||||
expect(getChildren(row)).toBeInstanceOf(NodeRow);
|
expect(getChildren(row)).toBeInstanceOf(NodeRow);
|
||||||
});
|
});
|
||||||
|
|
|
@ -9,7 +9,7 @@ import {TableRow} from "./TableRow";
|
||||||
|
|
||||||
import {nodeDescription, type SharedProps} from "./shared";
|
import {nodeDescription, type SharedProps} from "./shared";
|
||||||
|
|
||||||
import {ConnectionRowList} from "./Connection";
|
import {AggregationRowList} from "./Aggregation";
|
||||||
|
|
||||||
type NodeRowListProps = {|
|
type NodeRowListProps = {|
|
||||||
+nodes: $ReadOnlyArray<NodeAddressT>,
|
+nodes: $ReadOnlyArray<NodeAddressT>,
|
||||||
|
@ -60,7 +60,7 @@ export class NodeRow extends React.PureComponent<NodeRowProps> {
|
||||||
connectionProportion={null}
|
connectionProportion={null}
|
||||||
score={score}
|
score={score}
|
||||||
>
|
>
|
||||||
<ConnectionRowList
|
<AggregationRowList
|
||||||
depth={depth}
|
depth={depth}
|
||||||
node={node}
|
node={node}
|
||||||
sharedProps={sharedProps}
|
sharedProps={sharedProps}
|
||||||
|
|
|
@ -5,7 +5,7 @@ import {shallow} from "enzyme";
|
||||||
import sortBy from "lodash.sortby";
|
import sortBy from "lodash.sortby";
|
||||||
import * as NullUtil from "../../../util/null";
|
import * as NullUtil from "../../../util/null";
|
||||||
import {TableRow} from "./TableRow";
|
import {TableRow} from "./TableRow";
|
||||||
import {ConnectionRowList} from "./Connection";
|
import {AggregationRowList} from "./Aggregation";
|
||||||
|
|
||||||
import {type NodeAddressT, NodeAddress} from "../../../core/graph";
|
import {type NodeAddressT, NodeAddress} from "../../../core/graph";
|
||||||
|
|
||||||
|
@ -128,14 +128,14 @@ describe("app/credExplorer/pagerankTable/Node", () => {
|
||||||
const description = shallow(row.props().description);
|
const description = shallow(row.props().description);
|
||||||
expect(description.text()).toEqual(nodeDescription(node, adapters));
|
expect(description.text()).toEqual(nodeDescription(node, adapters));
|
||||||
});
|
});
|
||||||
describe("with a ConnectionRowList as children", () => {
|
describe("with a AggregationRowList as children", () => {
|
||||||
function getChildren(row) {
|
function getChildren(row) {
|
||||||
const children = row.props().children;
|
const children = row.props().children;
|
||||||
return shallow(children).instance();
|
return shallow(children).instance();
|
||||||
}
|
}
|
||||||
it("which is a ConnectionRowList", async () => {
|
it("which is a AggregationRowList", async () => {
|
||||||
const {row} = await setup();
|
const {row} = await setup();
|
||||||
expect(getChildren(row)).toBeInstanceOf(ConnectionRowList);
|
expect(getChildren(row)).toBeInstanceOf(AggregationRowList);
|
||||||
});
|
});
|
||||||
it("which has the same depth", async () => {
|
it("which has the same depth", async () => {
|
||||||
const {row} = await setup({depth: 13});
|
const {row} = await setup({depth: 13});
|
||||||
|
|
Loading…
Reference in New Issue