Prettier 2.0 (#393)

This commit is contained in:
Steve Faulkner
2021-01-20 09:15:01 -06:00
committed by GitHub
parent c1937ca464
commit 4be53284b5
500 changed files with 41927 additions and 41838 deletions

View File

@@ -1,158 +1,156 @@
import * as sinon from "sinon";
import { D3ForceGraph, LoadMoreDataAction, D3GraphNodeData } from "./D3ForceGraph";
import { D3Node, D3Link, GraphData } from "../GraphExplorerComponent/GraphData";
import GraphTab from "../../Tabs/GraphTab";
describe("D3ForceGraph", () => {
const v1Id = "v1";
const l1: D3Link = {
id: "id1",
inV: v1Id,
outV: "v2",
label: "l1",
source: null,
target: null
};
it("should count neighbors", () => {
const l2: D3Link = {
id: "id1",
inV: "v2",
outV: v1Id,
label: "l2",
source: null,
target: null
};
const l3: D3Link = {
id: "id1",
inV: v1Id,
outV: "v3",
label: "l3",
source: null,
target: null
};
const links = [l1, l2, l3];
const count = D3ForceGraph.countEdges(links);
expect(count.get(v1Id)).toBe(3);
expect(count.get("v2")).toBe(2);
expect(count.get("v3")).toBe(1);
});
describe("Behavior", () => {
let forceGraph: D3ForceGraph;
let rootNode: SVGSVGElement;
const newGraph: GraphData<D3Node, D3Link> = new GraphData();
newGraph.addVertex({
id: v1Id,
label: "vlabel1",
_isRoot: true
});
newGraph.addVertex({
id: "v2",
label: "vlabel2"
});
newGraph.addEdge(l1);
beforeAll(() => {
rootNode = document.createElementNS("http://www.w3.org/2000/svg", "svg");
rootNode.setAttribute("class", "maingraph");
});
afterAll(() => {
rootNode.remove();
});
beforeEach(() => {
forceGraph = new D3ForceGraph({
graphConfig: GraphTab.createGraphConfig(),
onHighlightedNode: sinon.spy(),
onLoadMoreData: (action: LoadMoreDataAction): void => {},
// parent to graph
onInitialized: sinon.spy(),
// For unit testing purposes
onGraphUpdated: null
});
forceGraph.init(rootNode);
});
afterEach(() => {
forceGraph.destroy();
});
it("should render graph d3 nodes and edges", done => {
forceGraph.params.onGraphUpdated = () => {
expect($(rootNode).find(".nodes").length).toBe(1);
expect($(rootNode).find(".links").length).toBe(1);
done();
};
forceGraph.updateGraph(newGraph);
});
it("should render vertices (as circle)", done => {
forceGraph.params.onGraphUpdated = () => {
expect($(rootNode).find(".node circle").length).toBe(2);
done();
};
forceGraph.updateGraph(newGraph);
});
it("should render vertex label", done => {
forceGraph.params.onGraphUpdated = () => {
expect($(rootNode).find(`text:contains(${v1Id})`).length).toBe(1);
done();
};
forceGraph.updateGraph(newGraph);
});
it("should render root vertex", done => {
forceGraph.params.onGraphUpdated = () => {
expect($(rootNode).find(".node.root").length).toBe(1);
done();
};
forceGraph.updateGraph(newGraph);
});
it("should render edge", done => {
forceGraph.params.onGraphUpdated = () => {
expect($(rootNode).find("path.link").length).toBe(1);
done();
};
forceGraph.updateGraph(newGraph);
});
it("should call onInitialized callback", () => {
expect((forceGraph.params.onInitialized as sinon.SinonSpy).calledOnce).toBe(true);
});
it("should call onHighlightedNode callback when mouse hovering over node", () => {
forceGraph.params.onGraphUpdated = () => {
const mouseoverEvent = document.createEvent("Events");
mouseoverEvent.initEvent("mouseover", true, false);
$(rootNode)
.find(".node")[0]
.dispatchEvent(mouseoverEvent); // [0] is v1 vertex
// onHighlightedNode is always called once to clear the selection
expect((forceGraph.params.onHighlightedNode as sinon.SinonSpy).calledTwice).toBe(true);
const onHighlightedNode = (forceGraph.params.onHighlightedNode as sinon.SinonSpy).args[1][0] as D3GraphNodeData;
expect(onHighlightedNode).not.toBe(null);
expect(onHighlightedNode.id).toEqual(v1Id);
};
forceGraph.updateGraph(newGraph);
});
});
});
import * as sinon from "sinon";
import { D3ForceGraph, LoadMoreDataAction, D3GraphNodeData } from "./D3ForceGraph";
import { D3Node, D3Link, GraphData } from "../GraphExplorerComponent/GraphData";
import GraphTab from "../../Tabs/GraphTab";
describe("D3ForceGraph", () => {
const v1Id = "v1";
const l1: D3Link = {
id: "id1",
inV: v1Id,
outV: "v2",
label: "l1",
source: null,
target: null,
};
it("should count neighbors", () => {
const l2: D3Link = {
id: "id1",
inV: "v2",
outV: v1Id,
label: "l2",
source: null,
target: null,
};
const l3: D3Link = {
id: "id1",
inV: v1Id,
outV: "v3",
label: "l3",
source: null,
target: null,
};
const links = [l1, l2, l3];
const count = D3ForceGraph.countEdges(links);
expect(count.get(v1Id)).toBe(3);
expect(count.get("v2")).toBe(2);
expect(count.get("v3")).toBe(1);
});
describe("Behavior", () => {
let forceGraph: D3ForceGraph;
let rootNode: SVGSVGElement;
const newGraph: GraphData<D3Node, D3Link> = new GraphData();
newGraph.addVertex({
id: v1Id,
label: "vlabel1",
_isRoot: true,
});
newGraph.addVertex({
id: "v2",
label: "vlabel2",
});
newGraph.addEdge(l1);
beforeAll(() => {
rootNode = document.createElementNS("http://www.w3.org/2000/svg", "svg");
rootNode.setAttribute("class", "maingraph");
});
afterAll(() => {
rootNode.remove();
});
beforeEach(() => {
forceGraph = new D3ForceGraph({
graphConfig: GraphTab.createGraphConfig(),
onHighlightedNode: sinon.spy(),
onLoadMoreData: (action: LoadMoreDataAction): void => {},
// parent to graph
onInitialized: sinon.spy(),
// For unit testing purposes
onGraphUpdated: null,
});
forceGraph.init(rootNode);
});
afterEach(() => {
forceGraph.destroy();
});
it("should render graph d3 nodes and edges", (done) => {
forceGraph.params.onGraphUpdated = () => {
expect($(rootNode).find(".nodes").length).toBe(1);
expect($(rootNode).find(".links").length).toBe(1);
done();
};
forceGraph.updateGraph(newGraph);
});
it("should render vertices (as circle)", (done) => {
forceGraph.params.onGraphUpdated = () => {
expect($(rootNode).find(".node circle").length).toBe(2);
done();
};
forceGraph.updateGraph(newGraph);
});
it("should render vertex label", (done) => {
forceGraph.params.onGraphUpdated = () => {
expect($(rootNode).find(`text:contains(${v1Id})`).length).toBe(1);
done();
};
forceGraph.updateGraph(newGraph);
});
it("should render root vertex", (done) => {
forceGraph.params.onGraphUpdated = () => {
expect($(rootNode).find(".node.root").length).toBe(1);
done();
};
forceGraph.updateGraph(newGraph);
});
it("should render edge", (done) => {
forceGraph.params.onGraphUpdated = () => {
expect($(rootNode).find("path.link").length).toBe(1);
done();
};
forceGraph.updateGraph(newGraph);
});
it("should call onInitialized callback", () => {
expect((forceGraph.params.onInitialized as sinon.SinonSpy).calledOnce).toBe(true);
});
it("should call onHighlightedNode callback when mouse hovering over node", () => {
forceGraph.params.onGraphUpdated = () => {
const mouseoverEvent = document.createEvent("Events");
mouseoverEvent.initEvent("mouseover", true, false);
$(rootNode).find(".node")[0].dispatchEvent(mouseoverEvent); // [0] is v1 vertex
// onHighlightedNode is always called once to clear the selection
expect((forceGraph.params.onHighlightedNode as sinon.SinonSpy).calledTwice).toBe(true);
const onHighlightedNode = (forceGraph.params.onHighlightedNode as sinon.SinonSpy).args[1][0] as D3GraphNodeData;
expect(onHighlightedNode).not.toBe(null);
expect(onHighlightedNode.id).toEqual(v1Id);
};
forceGraph.updateGraph(newGraph);
});
});
});

File diff suppressed because it is too large Load Diff

View File

@@ -101,7 +101,7 @@ export class EditorNeighborsComponent extends React.Component<EditorNeighborsCom
this.props.editedNeighbors.addedEdges.push({
inputInV: inV,
inputOutV: EditorNeighborsComponent.DEFAULT_BLANK_VALUE,
label: EditorNeighborsComponent.DEFAULT_BLANK_VALUE
label: EditorNeighborsComponent.DEFAULT_BLANK_VALUE,
});
this.onUpdateEdges();
}
@@ -110,7 +110,7 @@ export class EditorNeighborsComponent extends React.Component<EditorNeighborsCom
this.props.editedNeighbors.addedEdges.push({
inputInV: EditorNeighborsComponent.DEFAULT_BLANK_VALUE,
inputOutV: outV,
label: EditorNeighborsComponent.DEFAULT_BLANK_VALUE
label: EditorNeighborsComponent.DEFAULT_BLANK_VALUE,
});
this.onUpdateEdges();
}
@@ -215,7 +215,7 @@ export class EditorNeighborsComponent extends React.Component<EditorNeighborsCom
</td>
<td className="actionCol">
<span className="rightPaneTrashIcon rightPaneBtns">
<img src={DeleteIcon} alt="Delete" onClick={e => this.removeAddedEdgeToNeighbor(index)} />
<img src={DeleteIcon} alt="Delete" onClick={(e) => this.removeAddedEdgeToNeighbor(index)} />
</span>
</td>
</tr>

View File

@@ -14,7 +14,7 @@ describe("<EditorNodePropertiesComponent />", () => {
readOnlyProperties: [
{
key: "singlevalueprop",
values: [{ value: "abcd", type: "string" }]
values: [{ value: "abcd", type: "string" }],
},
{
key: "multivaluesprop",
@@ -24,14 +24,14 @@ describe("<EditorNodePropertiesComponent />", () => {
{ value: true, type: "boolean" },
{ value: false, type: "boolean" },
{ value: undefined, type: "null" },
{ value: null, type: "null" }
]
}
{ value: null, type: "null" },
],
},
],
existingProperties: [
{
key: "singlevalueprop2",
values: [{ value: "ijkl", type: "string" }]
values: [{ value: "ijkl", type: "string" }],
},
{
key: "multivaluesprop2",
@@ -41,14 +41,14 @@ describe("<EditorNodePropertiesComponent />", () => {
{ value: true, type: "boolean" },
{ value: false, type: "boolean" },
{ value: undefined, type: "null" },
{ value: null, type: "null" }
]
}
{ value: null, type: "null" },
],
},
],
addedProperties: [],
droppedKeys: []
droppedKeys: [],
},
onUpdateProperties: (editedProperties: EditedProperties): void => {}
onUpdateProperties: (editedProperties: EditedProperties): void => {},
};
const wrapper = shallow(<EditorNodePropertiesComponent {...props} />);
expect(wrapper).toMatchSnapshot();
@@ -61,27 +61,27 @@ describe("<EditorNodePropertiesComponent />", () => {
readOnlyProperties: [
{
key: "unicode1",
values: [{ value: "Véronique", type: "string" }]
values: [{ value: "Véronique", type: "string" }],
},
{
key: "unicode2",
values: [{ value: "亜妃子", type: "string" }]
}
values: [{ value: "亜妃子", type: "string" }],
},
],
existingProperties: [
{
key: "unicode1",
values: [{ value: "André", type: "string" }]
values: [{ value: "André", type: "string" }],
},
{
key: "unicode2",
values: [{ value: "あきら, アキラ,安喜良", type: "string" }]
}
values: [{ value: "あきら, アキラ,安喜良", type: "string" }],
},
],
addedProperties: [],
droppedKeys: []
droppedKeys: [],
},
onUpdateProperties: (editedProperties: EditedProperties): void => {}
onUpdateProperties: (editedProperties: EditedProperties): void => {},
};
const wrapper = shallow(<EditorNodePropertiesComponent {...props} />);
expect(wrapper).toMatchSnapshot();

View File

@@ -79,7 +79,7 @@ export class EditorNodePropertiesComponent extends React.Component<EditorNodePro
{this.props.editedProperties.readOnlyProperties.map((nodeProp: ViewModels.InputProperty) =>
ReadOnlyNodePropertiesComponent.renderReadOnlyPropertyKeyPair(
nodeProp.key,
nodeProp.values.map(val => val.value)
nodeProp.values.map((val) => val.value)
)
)}
</React.Fragment>
@@ -112,7 +112,7 @@ export class EditorNodePropertiesComponent extends React.Component<EditorNodePro
type="text"
value={singleValue.value.toString()}
placeholder="Value"
onChange={e => {
onChange={(e) => {
singleValue.value = e.target.value;
this.props.onUpdateProperties(this.props.editedProperties);
}}
@@ -123,7 +123,7 @@ export class EditorNodePropertiesComponent extends React.Component<EditorNodePro
<select
className="typeSelect"
value={singleValue.type}
onChange={e => {
onChange={(e) => {
singleValue.type = e.target.value as ViewModels.InputPropertyValueTypeString;
if (singleValue.type === "null") {
singleValue.value = null;
@@ -144,7 +144,7 @@ export class EditorNodePropertiesComponent extends React.Component<EditorNodePro
className="rightPaneTrashIcon rightPaneBtns"
as="span"
aria-label="Delete property"
onActivated={e => this.removeExistingProperty(key)}
onActivated={(e) => this.removeExistingProperty(key)}
>
<img src={DeleteIcon} alt="Delete" />
</AccessibleElement>
@@ -157,14 +157,16 @@ export class EditorNodePropertiesComponent extends React.Component<EditorNodePro
return (
<tr key={nodeProp.key}>
<td className="labelCol propertyId">{nodeProp.key}</td>
<td>{nodeProp.values.map(value => ReadOnlyNodePropertiesComponent.renderSinglePropertyValue(value.value))}</td>
<td>
{nodeProp.values.map((value) => ReadOnlyNodePropertiesComponent.renderSinglePropertyValue(value.value))}
</td>
<td />
<td className="actionCol">
<AccessibleElement
className="rightPaneTrashIcon rightPaneBtns"
as="span"
aria-label="Remove existing property"
onActivated={e => this.removeExistingProperty(nodeProp.key)}
onActivated={(e) => this.removeExistingProperty(nodeProp.key)}
>
<img src={DeleteIcon} alt="Delete" />
</AccessibleElement>
@@ -188,7 +190,7 @@ export class EditorNodePropertiesComponent extends React.Component<EditorNodePro
type="text"
value={addedProperty.key}
placeholder="Key"
onChange={e => {
onChange={(e) => {
addedProperty.key = e.target.value;
this.props.onUpdateProperties(this.props.editedProperties);
}}
@@ -201,7 +203,7 @@ export class EditorNodePropertiesComponent extends React.Component<EditorNodePro
type="text"
value={firstValue.value.toString()}
placeholder="Value"
onChange={e => {
onChange={(e) => {
firstValue.value = e.target.value;
if (firstValue.type === "null") {
firstValue.value = null;
@@ -215,7 +217,7 @@ export class EditorNodePropertiesComponent extends React.Component<EditorNodePro
<select
className="typeSelect"
value={firstValue.type}
onChange={e => {
onChange={(e) => {
firstValue.type = e.target.value as ViewModels.InputPropertyValueTypeString;
this.props.onUpdateProperties(this.props.editedProperties);
}}
@@ -233,7 +235,7 @@ export class EditorNodePropertiesComponent extends React.Component<EditorNodePro
className="rightPaneTrashIcon rightPaneBtns"
as="span"
aria-label="Remove property"
onActivated={e => this.removeAddedProperty(index)}
onActivated={(e) => this.removeAddedProperty(index)}
>
<img src={DeleteIcon} alt="Delete" />
</AccessibleElement>

View File

@@ -69,8 +69,8 @@ describe("Graph Data", () => {
id: "id",
label: "label",
properties: {
testString: [{ id: "123", value: stringValue }]
}
testString: [{ id: "123", value: stringValue }],
},
},
"testString"
);
@@ -85,8 +85,8 @@ describe("Graph Data", () => {
id: "id",
label: "label",
properties: {
testString: [{ id: "123", value: numberValue }]
}
testString: [{ id: "123", value: numberValue }],
},
},
"testString"
);
@@ -101,8 +101,8 @@ describe("Graph Data", () => {
id: "id",
label: "label",
properties: {
testString: [{ id: "123", value: booleanValue }]
}
testString: [{ id: "123", value: booleanValue }],
},
},
"testString"
);

View File

@@ -152,7 +152,7 @@ export class GraphData<V extends GremlinVertex, E extends GremlinEdge> {
const v = this.getVertexById(e.outV);
GraphData.addOutE(v, p, {
id: e.id,
inV: e.outV
inV: e.outV,
});
}
});
@@ -165,7 +165,7 @@ export class GraphData<V extends GremlinVertex, E extends GremlinEdge> {
const v = this.getVertexById(e.inV);
GraphData.addInE(v, p, {
id: e.id,
outV: e.inV
outV: e.inV,
});
}
});

View File

@@ -52,8 +52,8 @@ describe("Check whether query result is edge-vertex array", () => {
GraphExplorer.isEdgeVertexPairArray([
{
e: { id: "ide", type: "edge" },
v: { id: "idv", type: "vertex" }
}
v: { id: "idv", type: "vertex" },
},
])
).toBe(true);
});
@@ -75,7 +75,7 @@ describe("getPkIdFromDocumentId", () => {
_self: "_self",
_etag: "_etag",
_ts: 1234,
...override
...override,
});
it("should create pkid pair from non-partitioned graph", () => {
@@ -171,7 +171,7 @@ describe("GraphExplorer", () => {
/* TODO Figure out how to make this Knockout-free */
graphConfigUiData: graphConfigUi,
graphConfig: graphConfig
graphConfig: graphConfig,
};
};
@@ -244,7 +244,7 @@ describe("GraphExplorer", () => {
selectNode: sinon.spy(),
resetZoom: sinon.spy(),
updateGraph: sinon.stub().callsFake(() => complete()),
enableHighlight: sinon.spy()
enableHighlight: sinon.spy(),
};
graphExplorer.d3ForceGraph = mockGraphRenderer;
});
@@ -268,7 +268,7 @@ describe("GraphExplorer", () => {
client.params.successCallback({
requestId: requestId,
data: backendResponse.response,
requestCharge: gremlinRU
requestCharge: gremlinRU,
});
if (backendResponse.isLast) {
@@ -305,7 +305,7 @@ describe("GraphExplorer", () => {
_query: query,
nextItem: (callback: (error: any, document: DataModels.DocumentId) => void): void => {},
hasMoreResults: () => false,
executeNext: (callback: (error: any, documents: DataModels.DocumentId[], headers: any) => void): void => {}
executeNext: (callback: (error: any, documents: DataModels.DocumentId[], headers: any) => void): void => {},
};
});
(queryDocumentsPage as jest.Mock).mockImplementation(
@@ -318,7 +318,7 @@ describe("GraphExplorer", () => {
documents: docDBResponse.response,
activityId: "",
headers: [] as any[],
requestCharge: gVRU
requestCharge: gVRU,
});
}
);
@@ -343,11 +343,11 @@ describe("GraphExplorer", () => {
});
describe("Load Graph button", () => {
beforeEach(async done => {
beforeEach(async (done) => {
const backendResponses: BackendResponses = {};
backendResponses["g.V()"] = backendResponses["g.V('1')"] = {
response: [{ id: "1", type: "vertex" }],
isLast: false
isLast: false,
};
backendResponses[createFetchOutEQuery("1", GraphExplorer.LOAD_PAGE_SIZE + 1)] = { response: [], isLast: false };
backendResponses[createFetchInEQuery("1", GraphExplorer.LOAD_PAGE_SIZE + 1)] = { response: [], isLast: true };
@@ -370,7 +370,7 @@ describe("GraphExplorer", () => {
it("should submit g.V() as docdb query with proper parameters", () => {
expect(queryDocuments).toBeCalledWith("databaseId", "collectionId", DOCDB_G_DOT_V_QUERY, {
maxItemCount: GraphExplorer.ROOT_LIST_PAGE_SIZE,
enableCrossPartitionQuery: true
enableCrossPartitionQuery: true,
});
});
@@ -380,11 +380,11 @@ describe("GraphExplorer", () => {
});
describe("Execute Gremlin Query button", () => {
beforeEach(done => {
beforeEach((done) => {
const backendResponses: BackendResponses = {};
backendResponses["g.V()"] = backendResponses["g.V('2')"] = {
response: [{ id: "2", type: "vertex" }],
isLast: false
isLast: false,
};
backendResponses[createFetchOutEQuery("2", GraphExplorer.LOAD_PAGE_SIZE + 1)] = { response: [], isLast: false };
backendResponses[createFetchInEQuery("2", GraphExplorer.LOAD_PAGE_SIZE + 1)] = { response: [], isLast: true };
@@ -407,7 +407,7 @@ describe("GraphExplorer", () => {
it("should submit g.V() as docdb query with proper parameters", () => {
expect(queryDocuments).toBeCalledWith("databaseId", "collectionId", DOCDB_G_DOT_V_QUERY, {
maxItemCount: GraphExplorer.ROOT_LIST_PAGE_SIZE,
enableCrossPartitionQuery: true
enableCrossPartitionQuery: true,
});
});
@@ -435,10 +435,10 @@ describe("GraphExplorer", () => {
inV: node2Id,
outV: node1Id,
label: linkLabel,
type: "edge"
type: "edge",
};
beforeEach(done => {
beforeEach((done) => {
const backendResponses: BackendResponses = {};
// TODO Make this less dependent on spaces, order and quotes
backendResponses["g.V()"] = backendResponses[`g.V('${node1Id}','${node2Id}')`] = {
@@ -447,15 +447,15 @@ describe("GraphExplorer", () => {
id: node1Id,
label: label1,
type: "vertex",
properties: { prop1Id: [{ id: "id123", value: prop1Val1 }] }
properties: { prop1Id: [{ id: "id123", value: prop1Val1 }] },
},
{
id: node2Id,
label: label2,
type: "vertex"
}
type: "vertex",
},
],
isLast: false
isLast: false,
};
backendResponses[createFetchOutEQuery(node1Id, GraphExplorer.LOAD_PAGE_SIZE + 1)] = {
@@ -465,17 +465,17 @@ describe("GraphExplorer", () => {
v: {
id: node2Id,
label: label2,
type: "vertex"
}
}
type: "vertex",
},
},
],
isLast: false
isLast: false,
};
backendResponses[createFetchInEQuery(node1Id, GraphExplorer.LOAD_PAGE_SIZE)] = { response: [], isLast: true };
backendResponses[createFetchOutEQuery(node2Id, GraphExplorer.LOAD_PAGE_SIZE + 1)] = {
response: [],
isLast: false
isLast: false,
};
backendResponses[createFetchInEQuery(node2Id, GraphExplorer.LOAD_PAGE_SIZE + 1)] = {
response: [
@@ -485,16 +485,16 @@ describe("GraphExplorer", () => {
inV: node2Id,
outV: node1Id,
label: linkLabel,
type: "edge"
type: "edge",
},
v: {
id: node1Id,
label: label1,
type: "vertex"
}
}
type: "vertex",
},
},
],
isLast: true
isLast: true,
};
const docDBResponse: AjaxResponse = { response: [{ id: node1Id }, { id: node2Id }], isLast: false };
@@ -581,7 +581,7 @@ describe("GraphExplorer", () => {
describe("Select root node", () => {
let loadNeighborsPageStub: sinon.SinonSpy;
beforeEach(done => {
beforeEach((done) => {
loadNeighborsPageStub = sinon.stub(graphExplorerInstance, "loadNeighborsPage").callsFake(() => {
return Q.resolve();
});
@@ -660,12 +660,12 @@ describe("GraphExplorer", () => {
let processGremlinQueryResultsStub: sinon.SinonSpy;
let graphExplorerInstance: GraphExplorer;
beforeEach(done => {
beforeEach((done) => {
const backendResponses: BackendResponses = {};
// TODO Make this less dependent on spaces, order and quotes
backendResponses["g.V()"] = {
response: "invalid response",
isLast: true
isLast: true,
};
const docDBResponse: AjaxResponse = { response: [], isLast: false };
@@ -695,11 +695,11 @@ describe("GraphExplorer", () => {
});
describe("when isGraphAutoVizDisabled setting is true (autoviz disabled)", () => {
beforeEach(done => {
beforeEach((done) => {
const backendResponses: BackendResponses = {};
backendResponses["g.V()"] = backendResponses["g.V('3')"] = {
response: [{ id: "3", type: "vertex" }],
isLast: true
isLast: true,
};
const docDBResponse: AjaxResponse = { response: [{ id: "3" }], isLast: false };

View File

@@ -93,7 +93,7 @@ enum FilterQueryStatus {
GraphResult,
Loading,
NonGraphResult,
ErrorResult
ErrorResult,
}
interface GraphExplorerState {
@@ -159,7 +159,7 @@ enum ResultDisplay {
None,
Graph,
Json,
Stats
Stats,
}
interface UserQueryResult {
@@ -237,7 +237,7 @@ export class GraphExplorer extends React.Component<GraphExplorerProps, GraphExpl
resultDisplay: ResultDisplay.None,
filterQueryError: null,
filterQueryWarning: null,
filterQueryStatus: FilterQueryStatus.NoResult
filterQueryStatus: FilterQueryStatus.NoResult,
};
// Not part of React state
@@ -252,26 +252,26 @@ export class GraphExplorer extends React.Component<GraphExplorerProps, GraphExpl
title: "JSON",
content: {
className: "graphJsonEditor graphTabContent",
render: () => this.renderResultAsJson()
render: () => this.renderResultAsJson(),
},
isVisible: () => true
isVisible: () => true,
},
{
title: "Graph",
content: {
className: "graphTabContent",
render: () => this.renderResultAsGraph()
render: () => this.renderResultAsGraph(),
},
isVisible: () => this.state.filterQueryStatus === FilterQueryStatus.GraphResult
isVisible: () => this.state.filterQueryStatus === FilterQueryStatus.GraphResult,
},
{
title: GraphExplorer.QUERY_STATS_BUTTON_LABEL,
content: {
className: "graphTabContent",
render: () => this.renderResultStats()
render: () => this.renderResultStats(),
},
isVisible: () => true
}
isVisible: () => true,
},
];
this.queryRawData = null;
@@ -286,7 +286,7 @@ export class GraphExplorer extends React.Component<GraphExplorerProps, GraphExpl
}
/* TODO Make this Knockout-free ! */
this.props.graphConfigUiData.nodeCaptionChoice.subscribe(key => {
this.props.graphConfigUiData.nodeCaptionChoice.subscribe((key) => {
this.props.graphConfig.nodeCaption(key);
const selectedNode = this.state.highlightedNode;
if (selectedNode) {
@@ -295,20 +295,20 @@ export class GraphExplorer extends React.Component<GraphExplorerProps, GraphExpl
this.render();
});
this.props.graphConfigUiData.nodeColorKeyChoice.subscribe(val => {
this.props.graphConfigUiData.nodeColorKeyChoice.subscribe((val) => {
this.props.graphConfig.nodeColorKey(val === GraphExplorer.NONE_CHOICE ? null : val);
this.render();
});
this.props.graphConfigUiData.showNeighborType.subscribe(val => {
this.props.graphConfigUiData.showNeighborType.subscribe((val) => {
this.props.graphConfig.showNeighborType(val);
this.render();
});
this.props.graphConfigUiData.nodeIconChoice.subscribe(val => {
this.props.graphConfigUiData.nodeIconChoice.subscribe((val) => {
this.updateNodeIcons(val, this.props.graphConfigUiData.nodeIconSet());
this.render();
});
this.props.graphConfigUiData.nodeIconSet.subscribe(val => {
this.props.graphConfigUiData.nodeIconSet.subscribe((val) => {
this.updateNodeIcons(this.props.graphConfigUiData.nodeIconChoice(), val);
this.render();
});
@@ -316,7 +316,7 @@ export class GraphExplorer extends React.Component<GraphExplorerProps, GraphExpl
props.onGraphAccessorCreated({
applyFilter: this.submitQuery.bind(this),
addVertex: this.addVertex.bind(this)
addVertex: this.addVertex.bind(this),
});
} // constructor
@@ -341,7 +341,7 @@ export class GraphExplorer extends React.Component<GraphExplorerProps, GraphExpl
let pkId = editedProperties.pkId;
let updateQueryFragment = "";
finalProperties.forEach(p => {
finalProperties.forEach((p) => {
// Partition key cannot be updated
if (p.key === partitionKeyProperty) {
return;
@@ -370,7 +370,7 @@ export class GraphExplorer extends React.Component<GraphExplorerProps, GraphExpl
this.setNodePropertiesViewMode(NodeProperties.Mode.READONLY_PROP);
return Q.resolve({
data: [],
isIncomplete: false
isIncomplete: false,
});
}
@@ -390,7 +390,7 @@ export class GraphExplorer extends React.Component<GraphExplorerProps, GraphExpl
} else {
promise = Q.resolve({
data: [],
isIncomplete: false
isIncomplete: false,
});
}
@@ -683,8 +683,8 @@ export class GraphExplorer extends React.Component<GraphExplorerProps, GraphExpl
: null),
currentPage: {
start: offsetIndex,
end: offsetIndex + addedEdgesNb
}
end: offsetIndex + addedEdgesNb,
},
};
}
updateGraphData();
@@ -739,7 +739,7 @@ export class GraphExplorer extends React.Component<GraphExplorerProps, GraphExpl
enableCrossPartitionQuery:
StorageUtility.LocalStorageUtility.getEntryString(
StorageUtility.StorageKey.IsCrossPartitionQueryEnabled
) === "true"
) === "true",
} as FeedOptions
);
const response = await iterator.fetchNext();
@@ -878,7 +878,7 @@ export class GraphExplorer extends React.Component<GraphExplorerProps, GraphExpl
this.setState({
rootMap: {},
hasMoreRoots: false,
selectedRootId: null
selectedRootId: null,
});
this.setFilterQueryStatus(FilterQueryStatus.Loading);
@@ -903,7 +903,7 @@ export class GraphExplorer extends React.Component<GraphExplorerProps, GraphExpl
const errorMsg = `Failure in submitting query: ${query}: ${getErrorMessage(error)}`;
GraphExplorer.reportToConsole(ConsoleDataType.Error, errorMsg);
this.setState({
filterQueryError: errorMsg
filterQueryError: errorMsg,
});
}
}
@@ -1036,7 +1036,7 @@ export class GraphExplorer extends React.Component<GraphExplorerProps, GraphExpl
collectionName: this.props.collectionId,
defaultExperience: Constants.DefaultAccountExperience.Graph,
dataExplorerArea: Constants.Areas.Tab,
tabTitle: "Graph"
tabTitle: "Graph",
},
this.props.onLoadStartKey
);
@@ -1148,7 +1148,7 @@ export class GraphExplorer extends React.Component<GraphExplorerProps, GraphExpl
$.each(documents, (index: number, doc: any) => {
newIconsMap[doc["_graph_icon_property_value"]] = {
data: doc["icon"],
format: doc["format"]
format: doc["format"],
};
});
@@ -1172,7 +1172,7 @@ export class GraphExplorer extends React.Component<GraphExplorerProps, GraphExpl
this.setResultDisplay(ResultDisplay.None);
this.setState({
filterQueryError: null,
filterQueryWarning: null
filterQueryWarning: null,
});
break;
case FilterQueryStatus.NonGraphResult:
@@ -1206,7 +1206,7 @@ export class GraphExplorer extends React.Component<GraphExplorerProps, GraphExpl
let result = GraphData.GraphData.getNodePropValue(value, key);
return {
caption: result !== undefined ? result : value.id,
id: value.id
id: value.id,
};
}
);
@@ -1273,7 +1273,7 @@ export class GraphExplorer extends React.Component<GraphExplorerProps, GraphExpl
databaseId: this.props.databaseId,
collectionId: this.props.collectionId,
masterKey: this.props.masterKey,
maxResultSize: GraphExplorer.MAX_RESULT_SIZE
maxResultSize: GraphExplorer.MAX_RESULT_SIZE,
});
}
@@ -1298,7 +1298,7 @@ export class GraphExplorer extends React.Component<GraphExplorerProps, GraphExpl
const isTabsContentExpanded = !this.state.isTabsContentExpanded;
this.setState({
isTabsContentExpanded: isTabsContentExpanded,
isPropertiesCollapsed: isTabsContentExpanded
isPropertiesCollapsed: isTabsContentExpanded,
});
}
@@ -1399,8 +1399,9 @@ export class GraphExplorer extends React.Component<GraphExplorerProps, GraphExpl
private updatePossibleVertices(): Promise<PossibleVertex[]> {
const highlightedNodeId = this.state.highlightedNode ? this.state.highlightedNode.id : null;
const q = `SELECT c.id, c["${this.props.graphConfigUiData.nodeCaptionChoice() ||
"id"}"] AS p FROM c WHERE NOT IS_DEFINED(c._isEdge)`;
const q = `SELECT c.id, c["${
this.props.graphConfigUiData.nodeCaptionChoice() || "id"
}"] AS p FROM c WHERE NOT IS_DEFINED(c._isEdge)`;
return this.executeNonPagedDocDbQuery(q).then(
(documents: DataModels.DocumentId[]) => {
let possibleVertices = [] as PossibleVertex[];
@@ -1414,13 +1415,13 @@ export class GraphExplorer extends React.Component<GraphExplorerProps, GraphExpl
if (typeof item.p === "string" || item.p instanceof String) {
possibleVertices.push({
value: item.id,
caption: item.p
caption: item.p,
});
} else {
if (item.hasOwnProperty("p")) {
possibleVertices.push({
value: item.id,
caption: item.p[0]["_value"]
caption: item.p[0]["_value"],
});
}
}
@@ -1548,7 +1549,7 @@ export class GraphExplorer extends React.Component<GraphExplorerProps, GraphExpl
// A bit of translation to make it easier to display
let props: { [id: string]: ViewModels.GremlinPropertyValueType[] } = {};
for (let p in data.properties) {
props[p] = data.properties[p].map(gremlinProperty => gremlinProperty.value);
props[p] = data.properties[p].map((gremlinProperty) => gremlinProperty.value);
}
// update neighbors
@@ -1564,7 +1565,7 @@ export class GraphExplorer extends React.Component<GraphExplorerProps, GraphExpl
properties: props,
areNeighborsUnknown: !data._inEdgeIds || !data._outEdgeIds,
sources: sources, //<VertexBasicInfo[]>[],
targets: targets //<VertexBasicInfo[]>[]
targets: targets, //<VertexBasicInfo[]>[]
};
// Update KO
@@ -1627,7 +1628,7 @@ export class GraphExplorer extends React.Component<GraphExplorerProps, GraphExpl
(value: string, index: number, array: string[]): InputTypeaheadComponent.Item => {
return { caption: value, value: value };
}
)
),
});
}
@@ -1687,11 +1688,11 @@ export class GraphExplorer extends React.Component<GraphExplorerProps, GraphExpl
onHighlightedNode: this.onHighlightedNode.bind(this),
onLoadMoreData: this.onLoadMoreData.bind(this),
onInitialized: (instance: D3ForceGraph.GraphRenderer): void => this.onMiddlePaneInitialized(instance),
onGraphUpdated: this.onGraphUpdated.bind(this)
onGraphUpdated: this.onGraphUpdated.bind(this),
};
const graphVizProp: GraphVizComponentProps = {
forceGraphParams: forceGraphParams
forceGraphParams: forceGraphParams,
};
return (
@@ -1741,13 +1742,13 @@ export class GraphExplorer extends React.Component<GraphExplorerProps, GraphExpl
{
maxItemCount: GraphExplorer.ROOT_LIST_PAGE_SIZE,
enableCrossPartitionQuery:
LocalStorageUtility.getEntryString(StorageKey.IsCrossPartitionQueryEnabled) === "true"
LocalStorageUtility.getEntryString(StorageKey.IsCrossPartitionQueryEnabled) === "true",
} as FeedOptions
);
this.currentDocDBQueryInfo = {
iterator: iterator,
index: 0,
query: query
query: query,
};
return await this.loadMoreRootNodes();
} catch (error) {
@@ -1765,8 +1766,9 @@ export class GraphExplorer extends React.Component<GraphExplorerProps, GraphExpl
}
let RU: string = GraphExplorer.REQUEST_CHARGE_UNKNOWN_MSG;
const queryInfoStr = `${this.currentDocDBQueryInfo.query} (${this.currentDocDBQueryInfo.index + 1}-${this
.currentDocDBQueryInfo.index + GraphExplorer.ROOT_LIST_PAGE_SIZE})`;
const queryInfoStr = `${this.currentDocDBQueryInfo.query} (${this.currentDocDBQueryInfo.index + 1}-${
this.currentDocDBQueryInfo.index + GraphExplorer.ROOT_LIST_PAGE_SIZE
})`;
const id = GraphExplorer.reportToConsole(ConsoleDataType.InProgress, `Executing: ${queryInfoStr}`);
try {
@@ -1797,7 +1799,7 @@ export class GraphExplorer extends React.Component<GraphExplorerProps, GraphExpl
const errorMsg = `Failed to query: ${this.currentDocDBQueryInfo.query}. Reason:${getErrorMessage(error)}`;
GraphExplorer.reportToConsole(ConsoleDataType.Error, errorMsg);
this.setState({
filterQueryError: errorMsg
filterQueryError: errorMsg,
});
this.setFilterQueryStatus(FilterQueryStatus.ErrorResult);
throw error;
@@ -1819,7 +1821,7 @@ export class GraphExplorer extends React.Component<GraphExplorerProps, GraphExpl
const errorMsg = `Failed to execute query: ${query}: ${error}`;
GraphExplorer.reportToConsole(ConsoleDataType.Error, errorMsg);
this.setState({
filterQueryError: errorMsg
filterQueryError: errorMsg,
});
this.setFilterQueryStatus(FilterQueryStatus.ErrorResult);
throw error;
@@ -1832,7 +1834,7 @@ export class GraphExplorer extends React.Component<GraphExplorerProps, GraphExpl
const errorMsg = `Failed to process query result: ${getErrorMessage(error)}`;
GraphExplorer.reportToConsole(ConsoleDataType.Error, errorMsg);
this.setState({
filterQueryError: errorMsg
filterQueryError: errorMsg,
});
});

View File

@@ -20,8 +20,8 @@ describe("Process Gremlin vertex", () => {
id: "id",
label: "label",
inE: {
inEdge: [{ id: "id1", outV: "outV1" }]
}
inEdge: [{ id: "id1", outV: "outV1" }],
},
};
GraphUtil.createEdgesfromNode(v, graphData);
const expectedEdge: GremlinEdge = { id: "id1", inV: "id", outV: "outV1", label: "inEdge" };
@@ -33,8 +33,8 @@ describe("Process Gremlin vertex", () => {
id: "id",
label: "label",
outE: {
outEdge: [{ id: "id2", inV: "inV2" }]
}
outEdge: [{ id: "id2", inV: "inV2" }],
},
};
GraphUtil.createEdgesfromNode(v, graphData);
const expectedEdge: GremlinEdge = { id: "id2", inV: "inV2", outV: "id", label: "outEdge" };
@@ -47,14 +47,14 @@ describe("Process Gremlin vertex", () => {
id: "id",
label: "label",
inE: {
inEdge: [{ id: "id1", outV: "outV1" }]
inEdge: [{ id: "id1", outV: "outV1" }],
},
outE: {
outEdge: [
{ id: "id2", inV: "inV2" },
{ id: "id3", inV: "inV3" }
]
}
{ id: "id3", inV: "inV3" },
],
},
};
const newNodes = {};
GraphUtil.createEdgesfromNode(v, graphData, newNodes);
@@ -83,7 +83,7 @@ describe("getLimitedArrayString()", () => {
it("should handle nth element makes it exceed max limit", () => {
const expected = {
result: "'1','2'",
consumedCount: 2
consumedCount: 2,
};
expect(GraphUtil.getLimitedArrayString(["1", "2", "12345", "4", "5"], 10)).toEqual(expected);
});
@@ -91,7 +91,7 @@ describe("getLimitedArrayString()", () => {
it("should consume all elements if limit never exceeding limit", () => {
const expected = {
result: "'1','22','3'",
consumedCount: 3
consumedCount: 3,
};
expect(GraphUtil.getLimitedArrayString(["1", "22", "3"], 12)).toEqual(expected);
});

View File

@@ -32,7 +32,7 @@ export class GraphUtil {
id: edge.id,
label: label,
inV: edge.inV,
outV: vertex.id
outV: vertex.id,
};
graphData.addEdge(e);
@@ -51,7 +51,7 @@ export class GraphUtil {
id: edge.id,
label: label,
inV: vertex.id,
outV: edge.outV
outV: edge.outV,
};
graphData.addEdge(e);
@@ -89,7 +89,7 @@ export class GraphUtil {
return {
result: output,
consumedCount: i + 1
consumedCount: i + 1,
};
}
@@ -113,8 +113,9 @@ export class GraphUtil {
}().as('v').select('e', 'v')`;
} else {
const start = startIndex - joined.consumedCount;
gremlinQuery = `g.V(${pkid}).${outE ? "outE" : "inE"}()${hasWithoutStep}.range(${start},${start +
pageSize}).as('e').${outE ? "inV" : "outV"}().as('v').select('e', 'v')`;
gremlinQuery = `g.V(${pkid}).${outE ? "outE" : "inE"}()${hasWithoutStep}.range(${start},${
start + pageSize
}).as('e').${outE ? "inV" : "outV"}().as('v').select('e', 'v')`;
}
} else {
gremlinQuery = `g.V(${pkid}).${outE ? "outE" : "inE"}().limit(${pageSize}).as('e').${

View File

@@ -9,7 +9,7 @@ describe("Gremlin Client", () => {
collectionId: null,
databaseId: null,
masterKey: null,
maxResultSize: 10000
maxResultSize: 10000,
};
it("should use databaseId, collectionId and masterKey to authenticate", () => {
@@ -23,7 +23,7 @@ describe("Gremlin Client", () => {
collectionId,
databaseId,
masterKey,
maxResultSize: 0
maxResultSize: 0,
});
// User must includes these values
@@ -32,7 +32,7 @@ describe("Gremlin Client", () => {
expect(gremlinClient.client.params.password).toEqual(masterKey);
});
it("should aggregate RU charges across multiple responses", done => {
it("should aggregate RU charges across multiple responses", (done) => {
const gremlinClient = new GremlinClient();
const ru1 = 1;
const ru2 = 2;
@@ -42,23 +42,23 @@ describe("Gremlin Client", () => {
sinon.stub(gremlinClient.client, "executeGremlinQuery").callsFake((query: string): string => requestId);
gremlinClient
.execute("fake query")
.then(result => expect(result.totalRequestCharge).toBe(ru1 + ru2 + ru3))
.then((result) => expect(result.totalRequestCharge).toBe(ru1 + ru2 + ru3))
.finally(done);
gremlinClient.client.params.progressCallback({
data: ["data1"],
requestCharge: ru1,
requestId: requestId
requestId: requestId,
});
gremlinClient.client.params.progressCallback({
data: ["data2"],
requestCharge: ru2,
requestId: requestId
requestId: requestId,
});
gremlinClient.client.params.successCallback({
data: ["data3"],
requestCharge: ru3,
requestId: requestId
requestId: requestId,
});
});
@@ -83,7 +83,7 @@ describe("Gremlin Client", () => {
gremlinClient.client.params.successCallback({
data: ["data1"],
requestCharge: ru1,
requestId: requestId
requestId: requestId,
});
}, 0);
return requestId;
@@ -103,7 +103,7 @@ describe("Gremlin Client", () => {
gremlinClient.client.params.successCallback({
data: ["data1"],
requestCharge: 1,
requestId: "unknownId"
requestId: "unknownId",
});
expect(logConsoleSpy.called).toBe(true);
@@ -121,7 +121,7 @@ describe("Gremlin Client", () => {
expect(GremlinClient.getRequestChargeString("123")).not.toEqual(emptyResult);
});
it("should not aggregate RU if not a number and reset totalRequestCharge to undefined", done => {
it("should not aggregate RU if not a number and reset totalRequestCharge to undefined", (done) => {
const logConsoleSpy = sinon.spy(NotificationConsoleUtils, "logConsoleError");
const logErrorSpy = sinon.spy(Logger, "logError");
@@ -135,7 +135,7 @@ describe("Gremlin Client", () => {
gremlinClient
.execute("fake query")
.then(
result => {
(result) => {
try {
expect(result.totalRequestCharge).toBe(undefined);
expect(logConsoleSpy.called).toBe(true);
@@ -145,7 +145,7 @@ describe("Gremlin Client", () => {
done(e);
}
},
error => done.fail(error)
(error) => done.fail(error)
)
.finally(() => {
logConsoleSpy.restore();
@@ -155,16 +155,16 @@ describe("Gremlin Client", () => {
gremlinClient.client.params.progressCallback({
data: ["data1"],
requestCharge: ru1,
requestId: requestId
requestId: requestId,
});
gremlinClient.client.params.successCallback({
data: ["data2"],
requestCharge: ru2 as any,
requestId: requestId
requestId: requestId,
});
});
it("should not aggregate RU if undefined and reset totalRequestCharge to undefined", done => {
it("should not aggregate RU if undefined and reset totalRequestCharge to undefined", (done) => {
const logConsoleSpy = sinon.spy(NotificationConsoleUtils, "logConsoleError");
const logErrorSpy = sinon.spy(Logger, "logError");
@@ -178,7 +178,7 @@ describe("Gremlin Client", () => {
gremlinClient
.execute("fake query")
.then(
result => {
(result) => {
try {
expect(result.totalRequestCharge).toBe(undefined);
expect(logConsoleSpy.called).toBe(true);
@@ -188,7 +188,7 @@ describe("Gremlin Client", () => {
done(e);
}
},
error => done.fail(error)
(error) => done.fail(error)
)
.finally(() => {
logConsoleSpy.restore();
@@ -198,16 +198,16 @@ describe("Gremlin Client", () => {
gremlinClient.client.params.progressCallback({
data: ["data1"],
requestCharge: ru1,
requestId: requestId
requestId: requestId,
});
gremlinClient.client.params.successCallback({
data: ["data2"],
requestCharge: ru2,
requestId: requestId
requestId: requestId,
});
});
it("should track RUs even on failure", done => {
it("should track RUs even on failure", (done) => {
const gremlinClient = new GremlinClient();
const requestId = "id";
const RU = 1234;
@@ -217,8 +217,8 @@ describe("Gremlin Client", () => {
sinon.stub(gremlinClient.client, "executeGremlinQuery").callsFake((query: string): string => requestId);
const abortPendingRequestSpy = sinon.spy(gremlinClient, "abortPendingRequest");
gremlinClient.execute("fake query").then(
result => done.fail(`Unexpectedly succeeded with ${result}`),
error => {
(result) => done.fail(`Unexpectedly succeeded with ${result}`),
(error) => {
try {
expect(abortPendingRequestSpy.calledWith(requestId, error, RU)).toBe(true);
done();
@@ -232,13 +232,13 @@ describe("Gremlin Client", () => {
{
data: null,
requestCharge: RU,
requestId: requestId
requestId: requestId,
},
error
);
});
it("should abort all pending requests if requestId from failure response", done => {
it("should abort all pending requests if requestId from failure response", (done) => {
const gremlinClient = new GremlinClient();
const requestId = "id";
const error = "Some error";
@@ -258,7 +258,7 @@ describe("Gremlin Client", () => {
{
data: null,
requestCharge: undefined,
requestId: undefined
requestId: undefined,
},
error
);

View File

@@ -79,7 +79,7 @@ export class GremlinClient {
},
infoCallback: (msg: string) => {
NotificationConsoleUtils.logConsoleMessage(ConsoleDataType.Info, msg);
}
},
});
}
@@ -89,13 +89,13 @@ export class GremlinClient {
this.pendingResults.set(requestId, {
result: {
data: [] as any[],
isIncomplete: false
isIncomplete: false,
},
deferred: deferred,
timeoutId: window.setTimeout(
() => this.abortPendingRequest(requestId, GremlinClient.TIMEOUT_ERROR_MSG, null),
GremlinClient.PENDING_REQUEST_TIMEOUT_MS
)
),
});
return deferred.promise;
}

View File

@@ -9,7 +9,7 @@ import {
GremlinSimpleClientParameters,
Result,
GremlinRequestMessage,
GremlinResponseMessage
GremlinResponseMessage,
} from "./GremlinSimpleClient";
describe("Gremlin Simple Client", () => {
@@ -24,7 +24,7 @@ describe("Gremlin Simple Client", () => {
successCallback: (result: Result) => {},
progressCallback: (result: Result) => {},
failureCallback: (result: Result, error: string) => {},
infoCallback: (msg: string) => {}
infoCallback: (msg: string) => {},
};
};
@@ -41,10 +41,10 @@ describe("Gremlin Simple Client", () => {
} => ({
attributes: {
"x-ms-request-charge": requestCharge,
"x-ms-total-request-charge": -123
"x-ms-total-request-charge": -123,
},
code: code,
message: null
message: null,
});
beforeEach(() => {
@@ -60,7 +60,7 @@ describe("Gremlin Simple Client", () => {
fakeSocket.onmessage(fakeSocket.fakeResponse);
}
},
close: () => {}
close: () => {},
};
sandbox.stub(GremlinSimpleClient, "createWebSocket").returns(fakeSocket);
});
@@ -89,9 +89,9 @@ describe("Gremlin Simple Client", () => {
status: {
code: 200,
attributes: { graphExecutionStatus: 200, StorageRU: 2.29, ComputeRU: 1.07, PerPartitionComputeCharges: {} },
message: ""
message: "",
},
result: { data: ["é"], meta: {} }
result: { data: ["é"], meta: {} },
};
const expectedDecodedUint8ArrayValues = [
123,
@@ -323,7 +323,7 @@ describe("Gremlin Simple Client", () => {
123,
125,
125,
125
125,
];
// We do our best here to emulate what the server should return
const gremlinResponseData = new Uint8Array(<any>expectedDecodedUint8ArrayValues).buffer;
@@ -352,7 +352,7 @@ describe("Gremlin Simple Client", () => {
const fakeResponse: GremlinResponseMessage = {
status: fakeStatus(200, null),
requestId: "id",
result: { data: "mydata" }
result: { data: "mydata" },
};
sandbox.stub(client, "decodeMessage").returns(fakeResponse);
const onMessageSpy = sandbox.spy(client, "onMessage");
@@ -368,7 +368,7 @@ describe("Gremlin Simple Client", () => {
fakeSocket.fakeResponse = {
status: fakeStatus(200, null),
requestId: Object.keys(client.pendingRequests)[0],
data: new Uint8Array([1, 1, 1, 1]).buffer
data: new Uint8Array([1, 1, 1, 1]).buffer,
};
client.executeGremlinQuery("test");
fakeSocket.onopen();
@@ -386,7 +386,7 @@ describe("Gremlin Simple Client", () => {
const fakeResponse: GremlinResponseMessage = {
status: fakeStatus(200, RU),
requestId: Object.keys(client.pendingRequests)[0],
result: { data: "mydata" }
result: { data: "mydata" },
};
sandbox.stub(client, "decodeMessage").returns(fakeResponse);
client.onMessage(new MessageEvent("test2"));
@@ -394,7 +394,7 @@ describe("Gremlin Simple Client", () => {
onSuccessSpy.calledWith({
requestId: fakeResponse.requestId,
data: fakeResponse.result.data,
requestCharge: RU
requestCharge: RU,
})
).toBe(true);
});
@@ -410,7 +410,7 @@ describe("Gremlin Simple Client", () => {
const fakeResponse: GremlinResponseMessage = {
status: fakeStatus(204, RU),
requestId: Object.keys(client.pendingRequests)[0],
result: { data: "THIS SHOULD BE IGNORED" }
result: { data: "THIS SHOULD BE IGNORED" },
};
sandbox.stub(client, "decodeMessage").returns(fakeResponse);
client.onMessage(new MessageEvent("test2"));
@@ -418,7 +418,7 @@ describe("Gremlin Simple Client", () => {
onSuccessSpy.calledWith({
requestId: fakeResponse.requestId,
data: null,
requestCharge: RU
requestCharge: RU,
})
).toBe(true);
});
@@ -435,7 +435,7 @@ describe("Gremlin Simple Client", () => {
const fakeResponse: GremlinResponseMessage = {
status: fakeStatus(206, RU),
requestId: Object.keys(client.pendingRequests)[0],
result: { data: [1, 2, 3] }
result: { data: [1, 2, 3] },
};
sandbox.stub(client, "decodeMessage").returns(fakeResponse);
client.onMessage(new MessageEvent("test2"));
@@ -443,7 +443,7 @@ describe("Gremlin Simple Client", () => {
onProgressSpy.calledWith({
requestId: fakeResponse.requestId,
data: fakeResponse.result.data,
requestCharge: RU
requestCharge: RU,
})
).toBe(true);
expect(onSuccessSpy.notCalled).toBe(true);
@@ -460,7 +460,7 @@ describe("Gremlin Simple Client", () => {
const fakeResponse: GremlinResponseMessage = {
status: fakeStatus(407, null),
requestId: Object.keys(client.pendingRequests)[0],
result: { data: <any>null }
result: { data: <any>null },
};
sandbox.stub(client, "decodeMessage").returns(fakeResponse);
client.onMessage(new MessageEvent("test2"));
@@ -489,7 +489,7 @@ describe("Gremlin Simple Client", () => {
const fakeResponse: GremlinResponseMessage = {
status: fakeStatus(401, null),
requestId: "id",
result: { data: <any>null }
result: { data: <any>null },
};
sandbox.stub(client, "decodeMessage").returns(fakeResponse);
client.onMessage(null);
@@ -501,7 +501,7 @@ describe("Gremlin Simple Client", () => {
const fakeResponse: GremlinResponseMessage = {
status: fakeStatus(401, null),
requestId: "id",
result: { data: <any>null }
result: { data: <any>null },
};
sandbox.stub(client, "decodeMessage").returns(fakeResponse);
client.onMessage(null);
@@ -513,7 +513,7 @@ describe("Gremlin Simple Client", () => {
const fakeResponse: GremlinResponseMessage = {
status: fakeStatus(498, null),
requestId: "id",
result: { data: <any>null }
result: { data: <any>null },
};
sandbox.stub(client, "decodeMessage").returns(fakeResponse);
client.onMessage(null);
@@ -525,7 +525,7 @@ describe("Gremlin Simple Client", () => {
const fakeResponse: GremlinResponseMessage = {
status: fakeStatus(500, null),
requestId: "id",
result: { data: <any>null }
result: { data: <any>null },
};
sandbox.stub(client, "decodeMessage").returns(fakeResponse);
client.onMessage(null);
@@ -537,7 +537,7 @@ describe("Gremlin Simple Client", () => {
const fakeResponse: GremlinResponseMessage = {
status: fakeStatus(597, null),
requestId: "id",
result: { data: <any>null }
result: { data: <any>null },
};
sandbox.stub(client, "decodeMessage").returns(fakeResponse);
client.onMessage(null);
@@ -549,7 +549,7 @@ describe("Gremlin Simple Client", () => {
const fakeResponse: GremlinResponseMessage = {
status: fakeStatus(598, null),
requestId: "id",
result: { data: <any>null }
result: { data: <any>null },
};
sandbox.stub(client, "decodeMessage").returns(fakeResponse);
client.onMessage(null);
@@ -561,7 +561,7 @@ describe("Gremlin Simple Client", () => {
const fakeResponse: GremlinResponseMessage = {
status: fakeStatus(599, null),
requestId: "id",
result: { data: <any>null }
result: { data: <any>null },
};
sandbox.stub(client, "decodeMessage").returns(fakeResponse);
client.onMessage(null);
@@ -573,7 +573,7 @@ describe("Gremlin Simple Client", () => {
const fakeResponse: GremlinResponseMessage = {
status: fakeStatus(123123123, null),
requestId: "id",
result: { data: <any>null }
result: { data: <any>null },
};
sandbox.stub(client, "decodeMessage").returns(fakeResponse);
client.onMessage(null);
@@ -595,16 +595,16 @@ describe("Gremlin Simple Client", () => {
args: {
gremlin: "gremlin",
bindings: {},
language: "language"
}
language: "language",
},
};
const expectedResult: GremlinRequestMessage = {
requestId: request.requestId,
processor: request.processor,
op: "authentication",
args: {
SASL: expectedSASLResult
}
SASL: expectedSASLResult,
},
};
const actual = client.buildChallengeResponse(request);
expect(actual).toEqual(expectedResult);

View File

@@ -165,7 +165,7 @@ export class GremlinSimpleClient {
const result: Result = {
requestId: requestId,
data: rawMessage.result ? rawMessage.result.data : null,
requestCharge: rawMessage.status.attributes[GremlinSimpleClient.requestChargeHeader]
requestCharge: rawMessage.status.attributes[GremlinSimpleClient.requestChargeHeader],
};
if (!this.pendingRequests[requestId]) {
@@ -262,8 +262,8 @@ export class GremlinSimpleClient {
args: {
gremlin: query,
bindings: {},
language: "gremlin-groovy"
}
language: "gremlin-groovy",
},
};
this.connect();
return requestId;
@@ -271,19 +271,19 @@ export class GremlinSimpleClient {
public buildChallengeResponse(request: GremlinRequestMessage): GremlinRequestMessage {
var args = {
SASL: GremlinSimpleClient.utf8ToB64("\0" + this.params.user + "\0" + this.params.password)
SASL: GremlinSimpleClient.utf8ToB64("\0" + this.params.user + "\0" + this.params.password),
};
return {
requestId: request.requestId,
processor: request.processor,
op: "authentication",
args
args,
};
}
public static utf8ToB64(utf8Str: string) {
return btoa(
encodeURIComponent(utf8Str).replace(/%([0-9A-F]{2})/g, function(match, p1) {
encodeURIComponent(utf8Str).replace(/%([0-9A-F]{2})/g, function (match, p1) {
return String.fromCharCode(parseInt(p1, 16));
})
);
@@ -342,7 +342,7 @@ export class GremlinSimpleClient {
* RFC4122 version 4 compliant UUID
*/
private static uuidv4() {
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function(c) {
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function (c) {
var r = (Math.random() * 16) | 0,
v = c == "x" ? r : (r & 0x3) | 0x8;
return v.toString(16);

View File

@@ -58,7 +58,7 @@ export class LeftPaneComponent extends React.Component<LeftPaneComponentProps> {
className={className}
as="tr"
aria-label={node.caption}
onActivated={e => this.props.onRootNodeSelected(node.id)}
onActivated={(e) => this.props.onRootNodeSelected(node.id)}
key={node.id}
>
<td className="resultItem">

View File

@@ -20,17 +20,17 @@ describe("Property pane", () => {
name: "sourceName",
id: "sourceId",
edgeId: "edgeId",
edgeLabel: "sourceEdgeLabel"
}
edgeLabel: "sourceEdgeLabel",
},
],
targets: [
{
name: "targetName",
id: "targetId",
edgeId: "edgeId",
edgeLabel: "targetEdgeLabel"
}
]
edgeLabel: "targetEdgeLabel",
},
],
};
const createMockProps = (): NodePropertiesComponentProps => {
@@ -48,7 +48,7 @@ describe("Property pane", () => {
editGraphEdges: (editedEdges: EditedEdges): Q.Promise<any> => Q.resolve(),
deleteHighlightedNode: (): void => {},
onModeChanged: (newMode: Mode): void => {},
viewMode: Mode.READONLY_PROP
viewMode: Mode.READONLY_PROP,
};
};
let wrapper: ReactWrapper;

View File

@@ -25,7 +25,7 @@ export enum Mode {
READONLY_PROP,
PROPERTY_EDITOR,
EDIT_SOURCES,
EDIT_TARGETS
EDIT_TARGETS,
}
export interface NodePropertiesComponentProps {
@@ -71,25 +71,25 @@ export class NodePropertiesComponent extends React.Component<
readOnlyProperties: [],
existingProperties: [],
addedProperties: [],
droppedKeys: []
droppedKeys: [],
},
editedSources: {
vertexId: undefined,
currentNeighbors: [],
droppedIds: [],
addedEdges: []
addedEdges: [],
},
editedTargets: {
vertexId: undefined,
currentNeighbors: [],
droppedIds: [],
addedEdges: []
addedEdges: [],
},
possibleVertices: [],
isDeleteConfirm: false,
isPropertiesExpanded: true,
isSourcesExpanded: true,
isTargetsExpanded: true
isTargetsExpanded: true,
};
}
@@ -162,8 +162,8 @@ export class NodePropertiesComponent extends React.Component<
const readOnlyProps: ViewModels.InputProperty[] = [
{
key: "label",
values: [{ value: this.props.node.label, type: "string" }]
}
values: [{ value: this.props.node.label, type: "string" }],
},
];
const existingProps: ViewModels.InputProperty[] = [];
@@ -174,7 +174,10 @@ export class NodePropertiesComponent extends React.Component<
const propValues = hProps[p];
(p === partitionKeyProperty ? readOnlyProps : existingProps).push({
key: p,
values: propValues.map(val => ({ value: val.toString(), type: NodePropertiesComponent.getTypeOption(val) }))
values: propValues.map((val) => ({
value: val.toString(),
type: NodePropertiesComponent.getTypeOption(val),
})),
});
}
}
@@ -186,8 +189,8 @@ export class NodePropertiesComponent extends React.Component<
readOnlyProperties: readOnlyProps,
existingProperties: existingProps,
addedProperties: [],
droppedKeys: []
}
droppedKeys: [],
},
});
this.props.onModeChanged(newMode);
}
@@ -195,20 +198,20 @@ export class NodePropertiesComponent extends React.Component<
private showSourcesEditor(): void {
this.props.updatePossibleVertices().then((possibleVertices: PossibleVertex[]) => {
this.setState({
possibleVertices: possibleVertices
possibleVertices: possibleVertices,
});
const editedSources: EditedEdges = {
vertexId: this.props.node.id,
currentNeighbors: this.props.node.sources.slice(),
droppedIds: [],
addedEdges: []
addedEdges: [],
};
const newMode = Mode.EDIT_SOURCES;
this.setState({
editedProperties: this.state.editedProperties,
editedSources: editedSources
editedSources: editedSources,
});
this.props.onModeChanged(newMode);
});
@@ -217,20 +220,20 @@ export class NodePropertiesComponent extends React.Component<
private showTargetsEditor(): void {
this.props.updatePossibleVertices().then((possibleVertices: PossibleVertex[]) => {
this.setState({
possibleVertices: possibleVertices
possibleVertices: possibleVertices,
});
const editedTargets: EditedEdges = {
vertexId: this.props.node.id,
currentNeighbors: this.props.node.targets.slice(),
droppedIds: [],
addedEdges: []
addedEdges: [],
};
const newMode = Mode.EDIT_TARGETS;
this.setState({
editedProperties: this.state.editedProperties,
editedTargets: editedTargets
editedTargets: editedTargets,
});
this.props.onModeChanged(newMode);
});
@@ -250,7 +253,7 @@ export class NodePropertiesComponent extends React.Component<
private onUpdateProperties(editedProperties: EditedProperties): void {
this.setState({
editedProperties: editedProperties
editedProperties: editedProperties,
});
}
@@ -264,7 +267,7 @@ export class NodePropertiesComponent extends React.Component<
private setIsDeleteConfirm(state: boolean): void {
this.setState({
isDeleteConfirm: state
isDeleteConfirm: state,
});
}

View File

@@ -21,7 +21,7 @@ export class QueryContainerComponent extends React.Component<
public constructor(props: QueryContainerComponentProps) {
super(props);
this.state = {
query: this.props.initialQuery
query: this.props.initialQuery,
};
}
@@ -82,7 +82,7 @@ export class QueryContainerComponent extends React.Component<
<button
type="button"
className="filterbtnstyle queryButton"
onClick={e => this.props.onExecuteClick(this.state.query)}
onClick={(e) => this.props.onExecuteClick(this.state.query)}
disabled={this.props.isLoading || !QueryContainerComponent.isQueryValid(this.state.query)}
>
Execute Gremlin Query

View File

@@ -48,7 +48,7 @@ export class ReadOnlyNeighborsComponent extends React.Component<ReadOnlyNeighbor
className="clickableLink"
as="a"
aria-label={_neighbor.name}
onActivated={e => this.props.selectNode(_neighbor.id)}
onActivated={(e) => this.props.selectNode(_neighbor.id)}
title={GraphUtil.getNeighborTitle(_neighbor)}
>
{_neighbor.name}

View File

@@ -4,7 +4,7 @@ import { GraphHighlightedNodeData } from "./GraphExplorer";
import {
ReadOnlyNodePropertiesComponent,
ReadOnlyNodePropertiesComponentProps
ReadOnlyNodePropertiesComponentProps,
} from "./ReadOnlyNodePropertiesComponent";
describe("<ReadOnlyNodePropertiesComponent />", () => {
@@ -15,11 +15,11 @@ describe("<ReadOnlyNodePropertiesComponent />", () => {
label: label,
properties: {
key1: ["value1"],
key2: ["value2"]
key2: ["value2"],
},
areNeighborsUnknown: false,
sources: [],
targets: []
targets: [],
};
it("renders id", () => {
@@ -44,8 +44,8 @@ describe("<ReadOnlyNodePropertiesComponent />", () => {
const mockNode2 = {
...mockNode,
properties: {
key3: ["abcd", 1234, true, false, undefined, null]
}
key3: ["abcd", 1234, true, false, undefined, null],
},
};
const props: ReadOnlyNodePropertiesComponentProps = { node: mockNode2 };
const wrapper = shallow(<ReadOnlyNodePropertiesComponent {...props} />);
@@ -57,8 +57,8 @@ describe("<ReadOnlyNodePropertiesComponent />", () => {
...mockNode,
properties: {
key4: ["あきら, アキラ,安喜良"],
key5: ["Véronique"]
}
key5: ["Véronique"],
},
};
const props: ReadOnlyNodePropertiesComponentProps = { node: mockNode2 };
const wrapper = shallow(<ReadOnlyNodePropertiesComponent {...props} />);

View File

@@ -28,7 +28,7 @@ export class ReadOnlyNodePropertiesComponent extends React.Component<ReadOnlyNod
<span className="vertexLabel">{this.props.node.label}</span>
</td>
</tr>
{Object.keys(this.props.node.properties).map(_propkey => {
{Object.keys(this.props.node.properties).map((_propkey) => {
const gremlinValues = this.props.node.properties[_propkey];
return ReadOnlyNodePropertiesComponent.renderReadOnlyPropertyKeyPair(_propkey, gremlinValues);
})}
@@ -41,11 +41,11 @@ export class ReadOnlyNodePropertiesComponent extends React.Component<ReadOnlyNod
key: string,
propertyValues: ViewModels.GremlinPropertyValueType[]
): JSX.Element {
const renderedValues = propertyValues.map(value =>
const renderedValues = propertyValues.map((value) =>
ReadOnlyNodePropertiesComponent.renderSinglePropertyValue(value)
);
const stringifiedValues = propertyValues
.map(value => ReadOnlyNodePropertiesComponent.singlePropertyValueToString(value))
.map((value) => ReadOnlyNodePropertiesComponent.singlePropertyValueToString(value))
.join(", ");
return (
<tr key={key}>

View File

@@ -1,51 +1,51 @@
import * as ko from "knockout";
import { GraphStyleComponent, GraphStyleParams } from "./GraphStyleComponent";
import * as ViewModels from "../../../Contracts/ViewModels";
function buildComponent(buttonOptions: any) {
document.body.innerHTML = GraphStyleComponent.template as any;
const vm = new GraphStyleComponent.viewModel(buttonOptions);
ko.applyBindings(vm);
}
describe("Graph Style Component", () => {
let buildParams = (config: ViewModels.GraphConfigUiData): GraphStyleParams => {
return {
config: config
};
};
afterEach(() => {
ko.cleanNode(document);
});
describe("Rendering", () => {
it("should display proper list of choices passed in component parameters", () => {
const PROP2 = "prop2";
const PROPC = "prop3";
const params = buildParams({
nodeCaptionChoice: ko.observable(null),
nodeIconChoice: ko.observable(null),
nodeColorKeyChoice: ko.observable(null),
nodeIconSet: ko.observable(null),
nodeProperties: ko.observableArray(["prop1", PROP2]),
nodePropertiesWithNone: ko.observableArray(["propa", "propb", PROPC]),
showNeighborType: ko.observable(null)
});
buildComponent(params);
var e: any = document.querySelector(".graphStyle #nodeCaptionChoices");
expect(e.options.length).toBe(2);
expect(e.options[1].value).toBe(PROP2);
e = document.querySelector(".graphStyle #nodeColorKeyChoices");
expect(e.options.length).toBe(3);
expect(e.options[2].value).toBe(PROPC);
e = document.querySelector(".graphStyle #nodeIconChoices");
expect(e.options.length).toBe(3);
expect(e.options[2].value).toBe(PROPC);
});
});
});
import * as ko from "knockout";
import { GraphStyleComponent, GraphStyleParams } from "./GraphStyleComponent";
import * as ViewModels from "../../../Contracts/ViewModels";
function buildComponent(buttonOptions: any) {
document.body.innerHTML = GraphStyleComponent.template as any;
const vm = new GraphStyleComponent.viewModel(buttonOptions);
ko.applyBindings(vm);
}
describe("Graph Style Component", () => {
let buildParams = (config: ViewModels.GraphConfigUiData): GraphStyleParams => {
return {
config: config,
};
};
afterEach(() => {
ko.cleanNode(document);
});
describe("Rendering", () => {
it("should display proper list of choices passed in component parameters", () => {
const PROP2 = "prop2";
const PROPC = "prop3";
const params = buildParams({
nodeCaptionChoice: ko.observable(null),
nodeIconChoice: ko.observable(null),
nodeColorKeyChoice: ko.observable(null),
nodeIconSet: ko.observable(null),
nodeProperties: ko.observableArray(["prop1", PROP2]),
nodePropertiesWithNone: ko.observableArray(["propa", "propb", PROPC]),
showNeighborType: ko.observable(null),
});
buildComponent(params);
var e: any = document.querySelector(".graphStyle #nodeCaptionChoices");
expect(e.options.length).toBe(2);
expect(e.options[1].value).toBe(PROP2);
e = document.querySelector(".graphStyle #nodeColorKeyChoices");
expect(e.options.length).toBe(3);
expect(e.options[2].value).toBe(PROPC);
e = document.querySelector(".graphStyle #nodeIconChoices");
expect(e.options.length).toBe(3);
expect(e.options[2].value).toBe(PROPC);
});
});
});

View File

@@ -1,103 +1,103 @@
import * as Constants from "../../../Common/Constants";
import * as ViewModels from "../../../Contracts/ViewModels";
import { WaitsForTemplateViewModel } from "../../WaitsForTemplateViewModel";
/**
* Parameters for this component
*/
export interface GraphStyleParams {
config: ViewModels.GraphConfigUiData;
firstFieldHasFocus?: ko.Observable<boolean>;
/**
* Callback triggered when the template is bound to the component (for testing purposes)
*/
onTemplateReady?: () => void;
}
class GraphStyleViewModel extends WaitsForTemplateViewModel {
private params: GraphStyleParams;
public constructor(params: GraphStyleParams) {
super();
super.onTemplateReady((isTemplateReady: boolean) => {
if (isTemplateReady && params.onTemplateReady) {
params.onTemplateReady();
}
});
this.params = params;
}
public onAllNeighborsKeyPress = (source: any, event: KeyboardEvent): boolean => {
if (event.keyCode === Constants.KeyCodes.Space || event.keyCode === Constants.KeyCodes.Enter) {
this.params.config.showNeighborType(ViewModels.NeighborType.BOTH);
event.stopPropagation();
return false;
}
return true;
};
public onSourcesKeyPress = (source: any, event: KeyboardEvent): boolean => {
if (event.keyCode === Constants.KeyCodes.Space || event.keyCode === Constants.KeyCodes.Enter) {
this.params.config.showNeighborType(ViewModels.NeighborType.SOURCES_ONLY);
event.stopPropagation();
return false;
}
return true;
};
public onTargetsKeyPress = (source: any, event: KeyboardEvent): boolean => {
if (event.keyCode === Constants.KeyCodes.Space || event.keyCode === Constants.KeyCodes.Enter) {
this.params.config.showNeighborType(ViewModels.NeighborType.TARGETS_ONLY);
event.stopPropagation();
return false;
}
return true;
};
}
const template = `
<div id="graphStyle" class="graphStyle" data-bind="setTemplateReady: true, with:params.config">
<div class="seconddivpadding">
<p>Show vertex (node) as</p>
<select id="nodeCaptionChoices" class="formTree paneselect" required data-bind="options:nodeProperties,
value:nodeCaptionChoice, hasFocus: $parent.params.firstFieldHasFocus"></select>
</div>
<div class="seconddivpadding">
<p>Map this property to node color</p>
<select id="nodeColorKeyChoices" class="formTree paneselect" required data-bind="options:nodePropertiesWithNone,
value:nodeColorKeyChoice"></select>
</div>
<div class="seconddivpadding">
<p>Map this property to node icon</p>
<select id="nodeIconChoices" class="formTree paneselect" required data-bind="options:nodePropertiesWithNone,
value:nodeIconChoice"></select>
<input type="text" data-bind="value:nodeIconSet" placeholder="Icon set: blank for collection id" class="nodeIconSet" autocomplete="off" />
</div>
<p class="seconddivpadding">Show</p>
<div class="tabs">
<div class="tab">
<input type="radio" id="tab11" name="graphneighbortype" class="radio" data-bind="checkedValue:2, checked:showNeighborType" />
<label for="tab11" tabindex="0" data-bind="event: { keypress: $parent.onAllNeighborsKeyPress }">All neighbors</label>
</div>
<div class="tab">
<input type="radio" id="tab12" name="graphneighbortype" class="radio" data-bind="checkedValue:0, checked:showNeighborType" />
<label for="tab12" tabindex="0" data-bind="event: { keypress: $parent.onSourcesKeyPress }">Sources</label>
</div>
<div class="tab">
<input type="radio" id="tab13" name="graphneighbortype" class="radio" data-bind="checkedValue:1, checked:showNeighborType" />
<label for="tab13" tabindex="0" data-bind="event: { keypress: $parent.onTargetsKeyPress }">Targets</label>
</div>
</div>
</div>`;
export const GraphStyleComponent = {
viewModel: GraphStyleViewModel,
template
};
import * as Constants from "../../../Common/Constants";
import * as ViewModels from "../../../Contracts/ViewModels";
import { WaitsForTemplateViewModel } from "../../WaitsForTemplateViewModel";
/**
* Parameters for this component
*/
export interface GraphStyleParams {
config: ViewModels.GraphConfigUiData;
firstFieldHasFocus?: ko.Observable<boolean>;
/**
* Callback triggered when the template is bound to the component (for testing purposes)
*/
onTemplateReady?: () => void;
}
class GraphStyleViewModel extends WaitsForTemplateViewModel {
private params: GraphStyleParams;
public constructor(params: GraphStyleParams) {
super();
super.onTemplateReady((isTemplateReady: boolean) => {
if (isTemplateReady && params.onTemplateReady) {
params.onTemplateReady();
}
});
this.params = params;
}
public onAllNeighborsKeyPress = (source: any, event: KeyboardEvent): boolean => {
if (event.keyCode === Constants.KeyCodes.Space || event.keyCode === Constants.KeyCodes.Enter) {
this.params.config.showNeighborType(ViewModels.NeighborType.BOTH);
event.stopPropagation();
return false;
}
return true;
};
public onSourcesKeyPress = (source: any, event: KeyboardEvent): boolean => {
if (event.keyCode === Constants.KeyCodes.Space || event.keyCode === Constants.KeyCodes.Enter) {
this.params.config.showNeighborType(ViewModels.NeighborType.SOURCES_ONLY);
event.stopPropagation();
return false;
}
return true;
};
public onTargetsKeyPress = (source: any, event: KeyboardEvent): boolean => {
if (event.keyCode === Constants.KeyCodes.Space || event.keyCode === Constants.KeyCodes.Enter) {
this.params.config.showNeighborType(ViewModels.NeighborType.TARGETS_ONLY);
event.stopPropagation();
return false;
}
return true;
};
}
const template = `
<div id="graphStyle" class="graphStyle" data-bind="setTemplateReady: true, with:params.config">
<div class="seconddivpadding">
<p>Show vertex (node) as</p>
<select id="nodeCaptionChoices" class="formTree paneselect" required data-bind="options:nodeProperties,
value:nodeCaptionChoice, hasFocus: $parent.params.firstFieldHasFocus"></select>
</div>
<div class="seconddivpadding">
<p>Map this property to node color</p>
<select id="nodeColorKeyChoices" class="formTree paneselect" required data-bind="options:nodePropertiesWithNone,
value:nodeColorKeyChoice"></select>
</div>
<div class="seconddivpadding">
<p>Map this property to node icon</p>
<select id="nodeIconChoices" class="formTree paneselect" required data-bind="options:nodePropertiesWithNone,
value:nodeIconChoice"></select>
<input type="text" data-bind="value:nodeIconSet" placeholder="Icon set: blank for collection id" class="nodeIconSet" autocomplete="off" />
</div>
<p class="seconddivpadding">Show</p>
<div class="tabs">
<div class="tab">
<input type="radio" id="tab11" name="graphneighbortype" class="radio" data-bind="checkedValue:2, checked:showNeighborType" />
<label for="tab11" tabindex="0" data-bind="event: { keypress: $parent.onAllNeighborsKeyPress }">All neighbors</label>
</div>
<div class="tab">
<input type="radio" id="tab12" name="graphneighbortype" class="radio" data-bind="checkedValue:0, checked:showNeighborType" />
<label for="tab12" tabindex="0" data-bind="event: { keypress: $parent.onSourcesKeyPress }">Sources</label>
</div>
<div class="tab">
<input type="radio" id="tab13" name="graphneighbortype" class="radio" data-bind="checkedValue:1, checked:showNeighborType" />
<label for="tab13" tabindex="0" data-bind="event: { keypress: $parent.onTargetsKeyPress }">Targets</label>
</div>
</div>
</div>`;
export const GraphStyleComponent = {
viewModel: GraphStyleViewModel,
template,
};

View File

@@ -1,74 +1,74 @@
<div id="graphStyle" class="graphStyle" data-bind="setTemplateReady: true, with:params.config">
<div class="seconddivpadding">
<p>Show vertex (node) as</p>
<select
id="nodeCaptionChoices"
class="formTree paneselect"
required
data-bind="options:nodeProperties,
value:nodeCaptionChoice, hasFocus: $parent.params.firstFieldHasFocus"
></select>
</div>
<div class="seconddivpadding">
<p>Map this property to node color</p>
<select
id="nodeColorKeyChoices"
class="formTree paneselect"
required
data-bind="options:nodePropertiesWithNone,
value:nodeColorKeyChoice"
></select>
</div>
<div class="seconddivpadding">
<p>Map this property to node icon</p>
<select
id="nodeIconChoices"
class="formTree paneselect"
required
data-bind="options:nodePropertiesWithNone,
value:nodeIconChoice"
></select>
<input
type="text"
data-bind="value:nodeIconSet"
placeholder="Icon set: blank for collection id"
class="nodeIconSet"
autocomplete="off"
/>
</div>
<p class="seconddivpadding">Show</p>
<div class="tabs">
<div class="tab">
<input
type="radio"
id="tab11"
name="graphneighbortype"
class="radio"
data-bind="checkedValue:2, checked:showNeighborType"
/>
<label for="tab11">All neighbors</label>
</div>
<div class="tab">
<input
type="radio"
id="tab12"
name="graphneighbortype"
class="radio"
data-bind="checkedValue:0, checked:showNeighborType"
/>
<label for="tab12">Sources</label>
</div>
<div class="tab">
<input
type="radio"
id="tab13"
name="graphneighbortype"
class="radio"
data-bind="checkedValue:1, checked:showNeighborType"
/>
<label for="tab13">Targets</label>
</div>
</div>
</div>
<div id="graphStyle" class="graphStyle" data-bind="setTemplateReady: true, with:params.config">
<div class="seconddivpadding">
<p>Show vertex (node) as</p>
<select
id="nodeCaptionChoices"
class="formTree paneselect"
required
data-bind="options:nodeProperties,
value:nodeCaptionChoice, hasFocus: $parent.params.firstFieldHasFocus"
></select>
</div>
<div class="seconddivpadding">
<p>Map this property to node color</p>
<select
id="nodeColorKeyChoices"
class="formTree paneselect"
required
data-bind="options:nodePropertiesWithNone,
value:nodeColorKeyChoice"
></select>
</div>
<div class="seconddivpadding">
<p>Map this property to node icon</p>
<select
id="nodeIconChoices"
class="formTree paneselect"
required
data-bind="options:nodePropertiesWithNone,
value:nodeIconChoice"
></select>
<input
type="text"
data-bind="value:nodeIconSet"
placeholder="Icon set: blank for collection id"
class="nodeIconSet"
autocomplete="off"
/>
</div>
<p class="seconddivpadding">Show</p>
<div class="tabs">
<div class="tab">
<input
type="radio"
id="tab11"
name="graphneighbortype"
class="radio"
data-bind="checkedValue:2, checked:showNeighborType"
/>
<label for="tab11">All neighbors</label>
</div>
<div class="tab">
<input
type="radio"
id="tab12"
name="graphneighbortype"
class="radio"
data-bind="checkedValue:0, checked:showNeighborType"
/>
<label for="tab12">Sources</label>
</div>
<div class="tab">
<input
type="radio"
id="tab13"
name="graphneighbortype"
class="radio"
data-bind="checkedValue:1, checked:showNeighborType"
/>
<label for="tab13">Targets</label>
</div>
</div>
</div>

View File

@@ -1,75 +1,75 @@
import * as ko from "knockout";
import { NewVertexComponent, NewVertexViewModel } from "./NewVertexComponent";
const component = NewVertexComponent;
describe("New Vertex Component", () => {
let vm: NewVertexViewModel;
let partitionKeyProperty: ko.Observable<string>;
beforeEach(async () => {
document.body.innerHTML = component.template as any;
partitionKeyProperty = ko.observable(null);
vm = new component.viewModel({
newVertexData: null,
partitionKeyProperty
});
ko.applyBindings(vm);
});
afterEach(() => {
ko.cleanNode(document);
});
describe("Rendering", () => {
it("should display property list with input and +Add Property", () => {
expect(document.querySelector(".newVertexComponent .newVertexForm")).not.toBeNull();
expect(document.querySelector(".newVertexComponent .edgeInput")).not.toBeNull();
expect(document.querySelector(".newVertexComponent .rightPaneAddPropertyBtn")).not.toBeNull();
});
it("should display partition key property if set", () => {
partitionKeyProperty("testKey");
expect(
(document.querySelector(".newVertexComponent .newVertexForm .labelCol input") as HTMLInputElement).value
).toEqual("testKey");
});
it("should NOT display partition key property if NOT set", () => {
expect(document.getElementsByClassName("valueCol").length).toBe(0);
});
});
describe("Behavior", () => {
let clickSpy: jasmine.Spy;
beforeEach(() => {
clickSpy = jasmine.createSpy("Command button click spy");
});
it("should add new property row when +Add property button is pressed", () => {
document.querySelector(".newVertexComponent .rightPaneAddPropertyBtn").dispatchEvent(new Event("click"));
document.querySelector(".newVertexComponent .rightPaneAddPropertyBtn").dispatchEvent(new Event("click"));
document.querySelector(".newVertexComponent .rightPaneAddPropertyBtn").dispatchEvent(new Event("click"));
expect(document.getElementsByClassName("valueCol").length).toBe(3);
expect(document.getElementsByClassName("rightPaneTrashIcon").length).toBe(3);
});
it("should remove property row when trash button is pressed", () => {
document.querySelector(".newVertexComponent .rightPaneAddPropertyBtn").dispatchEvent(new Event("click"));
document.querySelector(".newVertexComponent .rightPaneAddPropertyBtn").dispatchEvent(new Event("click"));
// Mark this one to delete
const elts = document.querySelectorAll(".newVertexComponent .rightPaneTrashIconImg");
elts[elts.length - 1].className += " deleteme";
document.querySelector(".newVertexComponent .rightPaneAddPropertyBtn").dispatchEvent(new Event("click"));
document
.querySelector(".newVertexComponent .rightPaneTrashIconImg.deleteme")
.parentElement.dispatchEvent(new Event("click"));
expect(document.getElementsByClassName("valueCol").length).toBe(2);
expect(document.getElementsByClassName("rightPaneTrashIcon").length).toBe(2);
expect(document.querySelectorAll(".newVertexComponent .rightPaneTrashIconImg.deleteme").length).toBe(0);
});
});
});
import * as ko from "knockout";
import { NewVertexComponent, NewVertexViewModel } from "./NewVertexComponent";
const component = NewVertexComponent;
describe("New Vertex Component", () => {
let vm: NewVertexViewModel;
let partitionKeyProperty: ko.Observable<string>;
beforeEach(async () => {
document.body.innerHTML = component.template as any;
partitionKeyProperty = ko.observable(null);
vm = new component.viewModel({
newVertexData: null,
partitionKeyProperty,
});
ko.applyBindings(vm);
});
afterEach(() => {
ko.cleanNode(document);
});
describe("Rendering", () => {
it("should display property list with input and +Add Property", () => {
expect(document.querySelector(".newVertexComponent .newVertexForm")).not.toBeNull();
expect(document.querySelector(".newVertexComponent .edgeInput")).not.toBeNull();
expect(document.querySelector(".newVertexComponent .rightPaneAddPropertyBtn")).not.toBeNull();
});
it("should display partition key property if set", () => {
partitionKeyProperty("testKey");
expect(
(document.querySelector(".newVertexComponent .newVertexForm .labelCol input") as HTMLInputElement).value
).toEqual("testKey");
});
it("should NOT display partition key property if NOT set", () => {
expect(document.getElementsByClassName("valueCol").length).toBe(0);
});
});
describe("Behavior", () => {
let clickSpy: jasmine.Spy;
beforeEach(() => {
clickSpy = jasmine.createSpy("Command button click spy");
});
it("should add new property row when +Add property button is pressed", () => {
document.querySelector(".newVertexComponent .rightPaneAddPropertyBtn").dispatchEvent(new Event("click"));
document.querySelector(".newVertexComponent .rightPaneAddPropertyBtn").dispatchEvent(new Event("click"));
document.querySelector(".newVertexComponent .rightPaneAddPropertyBtn").dispatchEvent(new Event("click"));
expect(document.getElementsByClassName("valueCol").length).toBe(3);
expect(document.getElementsByClassName("rightPaneTrashIcon").length).toBe(3);
});
it("should remove property row when trash button is pressed", () => {
document.querySelector(".newVertexComponent .rightPaneAddPropertyBtn").dispatchEvent(new Event("click"));
document.querySelector(".newVertexComponent .rightPaneAddPropertyBtn").dispatchEvent(new Event("click"));
// Mark this one to delete
const elts = document.querySelectorAll(".newVertexComponent .rightPaneTrashIconImg");
elts[elts.length - 1].className += " deleteme";
document.querySelector(".newVertexComponent .rightPaneAddPropertyBtn").dispatchEvent(new Event("click"));
document
.querySelector(".newVertexComponent .rightPaneTrashIconImg.deleteme")
.parentElement.dispatchEvent(new Event("click"));
expect(document.getElementsByClassName("valueCol").length).toBe(2);
expect(document.getElementsByClassName("rightPaneTrashIcon").length).toBe(2);
expect(document.querySelectorAll(".newVertexComponent .rightPaneTrashIconImg.deleteme").length).toBe(0);
});
});
});

View File

@@ -1,99 +1,99 @@
import * as ko from "knockout";
import { EditorNodePropertiesComponent } from "../GraphExplorerComponent/EditorNodePropertiesComponent";
import { NewVertexData, InputProperty } from "../../../Contracts/ViewModels";
import { WaitsForTemplateViewModel } from "../../WaitsForTemplateViewModel";
import * as Constants from "../../../Common/Constants";
import template from "./NewVertexComponent.html";
/**
* Parameters for this component
*/
export interface NewVertexParams {
// Data to be edited by the component
newVertexData: ko.Observable<NewVertexData>;
partitionKeyProperty: ko.Observable<string>;
firstFieldHasFocus?: ko.Observable<boolean>;
/**
* Callback triggered when the template is bound to the component (for testing purposes)
*/
onTemplateReady?: () => void;
}
export class NewVertexViewModel extends WaitsForTemplateViewModel {
private static readonly DEFAULT_PROPERTY_TYPE = "string";
private newVertexData: ko.Observable<NewVertexData>;
private firstFieldHasFocus: ko.Observable<boolean>;
private propertyTypes: string[];
public constructor(params: NewVertexParams) {
super();
super.onTemplateReady((isTemplateReady: boolean) => {
if (isTemplateReady && params.onTemplateReady) {
params.onTemplateReady();
}
});
this.newVertexData =
params.newVertexData ||
ko.observable({
label: "",
properties: <InputProperty[]>[]
});
this.firstFieldHasFocus = params.firstFieldHasFocus || ko.observable(false);
this.propertyTypes = EditorNodePropertiesComponent.VERTEX_PROPERTY_TYPES;
if (params.partitionKeyProperty) {
params.partitionKeyProperty.subscribe((newKeyProp: string) => {
if (!newKeyProp) {
return;
}
this.addNewVertexProperty(newKeyProp);
});
}
}
public onAddNewProperty() {
this.addNewVertexProperty();
document.getElementById("propertyKeyNewVertexPane").focus();
}
public onAddNewPropertyKeyPress = (source: any, event: KeyboardEvent): boolean => {
if (event.keyCode === Constants.KeyCodes.Enter || event.keyCode === Constants.KeyCodes.Space) {
this.onAddNewProperty();
event.stopPropagation();
return false;
}
return true;
};
public addNewVertexProperty(key?: string) {
let ap = this.newVertexData().properties;
ap.push({ key: key || "", values: [{ value: "", type: NewVertexViewModel.DEFAULT_PROPERTY_TYPE }] });
this.newVertexData.valueHasMutated();
}
public removeNewVertexProperty(index: number) {
let ap = this.newVertexData().properties;
ap.splice(index, 1);
this.newVertexData.valueHasMutated();
document.getElementById("addProperyNewVertexBtn").focus();
}
public removeNewVertexPropertyKeyPress = (index: number, source: any, event: KeyboardEvent): boolean => {
if (event.keyCode === Constants.KeyCodes.Enter || event.keyCode === Constants.KeyCodes.Space) {
this.removeNewVertexProperty(index);
event.stopPropagation();
return false;
}
return true;
};
}
/**
* Helper class for ko component registration
*/
export const NewVertexComponent = {
viewModel: NewVertexViewModel,
template
};
import * as ko from "knockout";
import { EditorNodePropertiesComponent } from "../GraphExplorerComponent/EditorNodePropertiesComponent";
import { NewVertexData, InputProperty } from "../../../Contracts/ViewModels";
import { WaitsForTemplateViewModel } from "../../WaitsForTemplateViewModel";
import * as Constants from "../../../Common/Constants";
import template from "./NewVertexComponent.html";
/**
* Parameters for this component
*/
export interface NewVertexParams {
// Data to be edited by the component
newVertexData: ko.Observable<NewVertexData>;
partitionKeyProperty: ko.Observable<string>;
firstFieldHasFocus?: ko.Observable<boolean>;
/**
* Callback triggered when the template is bound to the component (for testing purposes)
*/
onTemplateReady?: () => void;
}
export class NewVertexViewModel extends WaitsForTemplateViewModel {
private static readonly DEFAULT_PROPERTY_TYPE = "string";
private newVertexData: ko.Observable<NewVertexData>;
private firstFieldHasFocus: ko.Observable<boolean>;
private propertyTypes: string[];
public constructor(params: NewVertexParams) {
super();
super.onTemplateReady((isTemplateReady: boolean) => {
if (isTemplateReady && params.onTemplateReady) {
params.onTemplateReady();
}
});
this.newVertexData =
params.newVertexData ||
ko.observable({
label: "",
properties: <InputProperty[]>[],
});
this.firstFieldHasFocus = params.firstFieldHasFocus || ko.observable(false);
this.propertyTypes = EditorNodePropertiesComponent.VERTEX_PROPERTY_TYPES;
if (params.partitionKeyProperty) {
params.partitionKeyProperty.subscribe((newKeyProp: string) => {
if (!newKeyProp) {
return;
}
this.addNewVertexProperty(newKeyProp);
});
}
}
public onAddNewProperty() {
this.addNewVertexProperty();
document.getElementById("propertyKeyNewVertexPane").focus();
}
public onAddNewPropertyKeyPress = (source: any, event: KeyboardEvent): boolean => {
if (event.keyCode === Constants.KeyCodes.Enter || event.keyCode === Constants.KeyCodes.Space) {
this.onAddNewProperty();
event.stopPropagation();
return false;
}
return true;
};
public addNewVertexProperty(key?: string) {
let ap = this.newVertexData().properties;
ap.push({ key: key || "", values: [{ value: "", type: NewVertexViewModel.DEFAULT_PROPERTY_TYPE }] });
this.newVertexData.valueHasMutated();
}
public removeNewVertexProperty(index: number) {
let ap = this.newVertexData().properties;
ap.splice(index, 1);
this.newVertexData.valueHasMutated();
document.getElementById("addProperyNewVertexBtn").focus();
}
public removeNewVertexPropertyKeyPress = (index: number, source: any, event: KeyboardEvent): boolean => {
if (event.keyCode === Constants.KeyCodes.Enter || event.keyCode === Constants.KeyCodes.Space) {
this.removeNewVertexProperty(index);
event.stopPropagation();
return false;
}
return true;
};
}
/**
* Helper class for ko component registration
*/
export const NewVertexComponent = {
viewModel: NewVertexViewModel,
template,
};