Skip to content

Commit 14c09c1

Browse files
authored
Merge pull request #285 from FalkorDB/canvasTests
codeGraph/canvas tests
2 parents bef86d8 + 62dea82 commit 14c09c1

File tree

14 files changed

+466
-56
lines changed

14 files changed

+466
-56
lines changed

app/components/chat.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -528,7 +528,7 @@ export function Chat({ repo, path, setPath, graph, chartRef, selectedPathId, isP
528528
</button>
529529
<form className="grow flex items-center border rounded-md px-2" onSubmit={sendQuery}>
530530
<DropdownMenuTrigger asChild>
531-
<button className="bg-gray-200 p-2 rounded-md hover:bg-gray-300">
531+
<button data-name="questionOptionsMenu" className="bg-gray-200 p-2 rounded-md hover:bg-gray-300">
532532
<ArrowDown color="white" />
533533
</button>
534534
</DropdownMenuTrigger>

app/components/code-graph.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -435,8 +435,8 @@ export function CodeGraph({
435435
</button>
436436
}
437437
</div>
438-
<div className="w-full absolute bottom-0 left-0 flex justify-between items-center p-4 z-10 pointer-events-none">
439-
<div className="flex gap-4 text-gray-500">
438+
<div data-name="canvas-info-panel" className="w-full absolute bottom-0 left-0 flex justify-between items-center p-4 z-10 pointer-events-none">
439+
<div data-name="metrics-panel" className="flex gap-4 text-gray-500">
440440
<p>{nodesCount} Nodes</p>
441441
<p>{edgesCount} Edges</p>
442442
</div>

app/components/dataPanel.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ export default function DataPanel({ obj, setObj, url }: Props) {
3636
const object = Object.entries(obj).filter(([k]) => !excludedProperties.includes(k))
3737

3838
return (
39-
<div className="z-20 absolute -top-10 left-20 bg-[#343434] text-white shadow-lg rounded-lg flex flex-col max-h-[88%] max-w-[56%] overflow-hidden" >
39+
<div data-name="node-details-panel" className="z-20 absolute -top-10 left-20 bg-[#343434] text-white shadow-lg rounded-lg flex flex-col max-h-[88%] max-w-[56%] overflow-hidden" >
4040
<header className="bg-[#191919] flex items-center gap-8 justify-between p-8">
4141
<p title={label} className="truncate font-bold">{label.toUpperCase()}</p>
4242
<button onClick={() => setObj(undefined)}>

e2e/config/constants.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
export const GRAPH_ID = "1";
1+
export const GRAPH_ID = "GraphRAG-SDK";
22
export const PROJECT_NAME = "GraphRAG-SDK";
33
export const CHAT_OPTTIONS_COUNT = 1;
44
export const Node_Question = "how many nodes do we have?";

e2e/config/testData.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,8 @@ export const specialCharacters: { character: string; expectedRes: boolean }[] =
1313
...categorizeCharacters(['%', '*', '(', ')', '-', '[', ']', '{', '}', ';', ':', '"', '|', '~'], false),
1414
...categorizeCharacters(['!', '@', '$', '^', '_', '=', '+', "'", ',', '.', '<', '>', '/', '?', '\\', '`', '&', '#'], true)
1515
];
16+
17+
export const nodesPath: { firstNode: string; secondNode: string }[] = [
18+
{ firstNode: "import_data", secondNode: "add_edge" },
19+
{ firstNode: "test_kg_delete", secondNode: "list_graphs" },
20+
];

e2e/infra/ui/browserWrapper.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@ export default class BrowserWrapper {
1313
if (!this.browser) {
1414
this.browser = await chromium.launch();
1515
}
16-
if (!this.context) {
17-
this.context = await this.browser.newContext();
18-
}
16+
this.context = await this.browser.newContext({
17+
permissions: ['clipboard-read', 'clipboard-write'],
18+
});
1919
if (!this.page) {
2020
this.page = await this.context.newPage();
2121
}

e2e/logic/POM/codeGraph.ts

Lines changed: 171 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { Locator, Page } from "playwright";
22
import BasePage from "../../infra/ui/basePage";
33
import { delay, waitToBeEnabled } from "../utils";
4-
import { analyzeCanvasWithLocator, CanvasAnalysisResult } from "../canvasAnalysis";
4+
import { analyzeCanvasNodes, CanvasAnalysisResult } from "../canvasAnalysis";
55

66
export default class CodeGraph extends BasePage {
77
/* NavBar Locators*/
@@ -21,17 +21,33 @@ export default class CodeGraph extends BasePage {
2121
return this.page.locator("//div[@role='dialog']")
2222
}
2323

24+
private get tipBtn(): Locator {
25+
return this.page.locator("//button[@title='Tip']")
26+
}
27+
28+
private get genericMenu(): Locator {
29+
return this.page.locator("//div[contains(@role, 'menu')]")
30+
}
31+
32+
private get tipMenuCloseBtn(): Locator {
33+
return this.page.locator("//div[@role='menu']//button[@title='Close']")
34+
}
35+
2436
/* CodeGraph Locators*/
2537
private get comboBoxbtn(): Locator {
2638
return this.page.locator("//button[@role='combobox']")
2739
}
2840

29-
private get selectGraphInComboBox(): (graph: string) => Locator {
30-
return (graph: string) => this.page.locator(`//div[@role='presentation']//div[@role='option'][${graph}]`);
41+
private get selectGraphInComboBoxByName(): (graph: string) => Locator {
42+
return (graph: string) => this.page.locator(`//div[@role='presentation']//div//span[contains(text(), '${graph}')]`);
43+
}
44+
45+
private get selectGraphInComboBoxById(): (graph: string) => Locator {
46+
return (graph: string) => this.page.locator(`//div[@role='presentation']//div[${graph}]`);
3147
}
3248

3349
private get lastElementInChat(): Locator {
34-
return this.page.locator("//main[@data-name='main-chat']/*[last()]/p");
50+
return this.page.locator("//main[@data-name='main-chat']/*[last()]/span");
3551
}
3652

3753
private get typeUrlInput(): Locator {
@@ -115,6 +131,22 @@ export default class CodeGraph extends BasePage {
115131
return this.page.locator("//div[@role='region']//ol//li");
116132
}
117133

134+
private get notificationErrorCloseBtn(): Locator {
135+
return this.page.locator("//div[@role='region']//ol//li/button");
136+
}
137+
138+
private get questionOptionsMenu(): Locator {
139+
return this.page.locator("//button[@data-name='questionOptionsMenu']");
140+
}
141+
142+
private get selectQuestionInMenu(): (questionNumber: string) => Locator {
143+
return (questionNumber: string) => this.page.locator(`//div[contains(@role, 'menu')]/button[${questionNumber}]`);
144+
}
145+
146+
private get lastQuestionInChat(): Locator {
147+
return this.page.locator("//main[@data-name='main-chat']/*[last()-1]/p");
148+
}
149+
118150
/* Canvas Locators*/
119151

120152
private get canvasElement(): Locator {
@@ -133,10 +165,37 @@ export default class CodeGraph extends BasePage {
133165
return this.page.locator("//button[@title='Center']");
134166
}
135167

136-
private get removeNodeViaElementMenu(): Locator {
137-
return this.page.locator("//button[@title='Remove']");
168+
private get codeGraphCheckbox(): (checkbox: string) => Locator {
169+
return (checkbox: string) => this.page.locator(`(//button[@role='checkbox'])[${checkbox}]`);
170+
}
171+
172+
private get clearGraphBtn(): Locator {
173+
return this.page.locator("//button[p[text()='Clear Graph']]");
174+
}
175+
176+
private get elementMenuButton(): (buttonID: string) => Locator {
177+
return (buttonID: string) => this.page.locator(`//button[@title='${buttonID}']`);
138178
}
139179

180+
private get nodedetailsPanelHeader(): Locator {
181+
return this.page.locator("//div[@data-name='node-details-panel']/header/p");
182+
}
183+
184+
private get nodedetailsPanelcloseBtn(): Locator {
185+
return this.page.locator("//div[@data-name='node-details-panel']/header/button");
186+
}
187+
private get canvasMetricsPanel(): (itemId: string) => Locator {
188+
return (itemId: string) => this.page.locator(`//div[@data-name='metrics-panel']/p[${itemId}]`);
189+
}
190+
191+
private get nodedetailsPanelID(): Locator {
192+
return this.page.locator("//div[@data-name='node-details-panel']/main/div[1]/p[2]");
193+
}
194+
195+
private get nodedetailsPanelElements(): Locator {
196+
return this.page.locator("//div[@data-name='node-details-panel']/main/div/p[1]");
197+
}
198+
140199
/* NavBar functionality */
141200
async clickOnFalkorDbLogo(): Promise<Page> {
142201
await this.page.waitForLoadState('networkidle');
@@ -164,6 +223,19 @@ export default class CodeGraph extends BasePage {
164223
return await this.createNewProjectDialog.isVisible();
165224
}
166225

226+
async clickonTipBtn(): Promise<void> {
227+
await this.tipBtn.click();
228+
}
229+
230+
async isTipMenuVisible(): Promise<boolean> {
231+
await delay(500);
232+
return await this.genericMenu.isVisible();
233+
}
234+
235+
async clickonTipMenuCloseBtn(): Promise<void> {
236+
await this.tipMenuCloseBtn.click();
237+
}
238+
167239
/* Chat functionality */
168240
async clickOnshowPathBtn(): Promise<void> {
169241
await this.showPathBtn.click();
@@ -183,8 +255,9 @@ export default class CodeGraph extends BasePage {
183255
await this.lightbulbBtn.click();
184256
}
185257

186-
async getTextInLastChatElement(): Promise<string | null>{
187-
return await this.lastElementInChat.textContent();
258+
async getTextInLastChatElement(): Promise<string>{
259+
await delay(2500);
260+
return (await this.lastElementInChat.textContent())!;
188261
}
189262

190263
async getLastChatElementButtonCount(): Promise<number | null>{
@@ -224,14 +297,37 @@ export default class CodeGraph extends BasePage {
224297
}
225298

226299
async isNotificationError(): Promise<boolean> {
300+
await delay(500);
227301
return await this.notificationError.isVisible();
228302
}
229303

304+
async clickOnNotificationErrorCloseBtn(): Promise<void> {
305+
await this.notificationErrorCloseBtn.click();
306+
}
307+
308+
async clickOnQuestionOptionsMenu(): Promise<void> {
309+
await this.questionOptionsMenu.click();
310+
}
311+
312+
async selectAndGetQuestionInOptionsMenu(questionNumber: string): Promise<string> {
313+
await this.selectQuestionInMenu(questionNumber).click();
314+
return await this.selectQuestionInMenu(questionNumber).innerHTML();
315+
}
316+
317+
async getLastQuestionInChat(): Promise<string> {
318+
return await this.lastQuestionInChat.innerText();
319+
}
320+
230321
/* CodeGraph functionality */
231-
async selectGraph(graph: string): Promise<void> {
322+
async selectGraph(graph: string | number): Promise<void> {
232323
await this.comboBoxbtn.click();
233-
await this.selectGraphInComboBox(graph).waitFor({ state : 'visible'})
234-
await this.selectGraphInComboBox(graph).click();
324+
if(typeof graph === 'number'){
325+
await this.selectGraphInComboBoxById(graph.toString()).waitFor({ state : 'visible'})
326+
await this.selectGraphInComboBoxById(graph.toString()).click();
327+
} else {
328+
await this.selectGraphInComboBoxByName(graph).waitFor({ state : 'visible'})
329+
await this.selectGraphInComboBoxByName(graph).click();
330+
}
235331
}
236332

237333
async createProject(url : string): Promise<void> {
@@ -251,6 +347,7 @@ export default class CodeGraph extends BasePage {
251347
}
252348

253349
async getSearchAutoCompleteCount(): Promise<number> {
350+
await this.searchBarAutoCompleteOptions.first().waitFor({ state: 'visible' });
254351
return await this.searchBarAutoCompleteOptions.count();
255352
}
256353

@@ -259,14 +356,14 @@ export default class CodeGraph extends BasePage {
259356
}
260357

261358
async selectSearchBarOptionBtn(buttonNum: string): Promise<void> {
359+
await delay(1000);
262360
await this.searchBarOptionBtn(buttonNum).click();
263361
}
264362

265363
async getSearchBarInputValue(): Promise<string> {
266364
return await this.searchBarInput.inputValue();
267365
}
268366

269-
270367
async scrollToBottomInSearchBarList(): Promise<void> {
271368
await this.searchBarList.evaluate((element) => {
272369
element.scrollTop = element.scrollHeight;
@@ -279,10 +376,10 @@ export default class CodeGraph extends BasePage {
279376
}
280377

281378
/* Canvas functionality */
282-
379+
283380
async getCanvasAnalysis(): Promise<CanvasAnalysisResult> {
284381
await delay(2000);
285-
return await analyzeCanvasWithLocator(this.canvasElement);
382+
return await analyzeCanvasNodes(this.canvasElement);
286383
}
287384

288385
async clickZoomIn(): Promise<void> {
@@ -298,14 +395,70 @@ export default class CodeGraph extends BasePage {
298395
}
299396

300397
async clickOnRemoveNodeViaElementMenu(): Promise<void> {
301-
await this.removeNodeViaElementMenu.click();
398+
await this.elementMenuButton("Remove").click();
302399
}
303400

304-
async rightClickOnNode(x : number, y: number): Promise<void> {
401+
async rightClickOnNode(x: number, y: number): Promise<void> {
305402
const boundingBox = (await this.canvasElement.boundingBox())!;
306-
const adjustedX = boundingBox.x + Math.round(x);
307-
const adjustedY = boundingBox.y + Math.round(y);
403+
const devicePixelRatio = await this.page.evaluate(() => window.devicePixelRatio || 1);
404+
const adjustedX = boundingBox.x + Math.round(x * devicePixelRatio);
405+
const adjustedY = boundingBox.y + Math.round(y * devicePixelRatio);
308406
await this.page.mouse.click(adjustedX, adjustedY, { button: 'right' });
309407
}
310408

409+
410+
async selectCodeGraphCheckbox(checkbox: string): Promise<void> {
411+
await this.codeGraphCheckbox(checkbox).click();
412+
}
413+
414+
async clickOnClearGraphBtn(): Promise<void> {
415+
await this.clearGraphBtn.click();
416+
}
417+
418+
async changeNodePosition(x: number, y: number): Promise<void> {
419+
const box = (await this.canvasElement.boundingBox())!;
420+
const targetX = x + 100;
421+
const targetY = y + 50;
422+
const absStartX = box.x + x;
423+
const absStartY = box.y + y;
424+
const absEndX = box.x + targetX;
425+
const absEndY = box.y + targetY;
426+
await this.page.mouse.move(absStartX, absStartY);
427+
await this.page.mouse.down();
428+
await this.page.mouse.move(absEndX, absEndY);
429+
await this.page.mouse.up();
430+
}
431+
432+
async getNodeDetailsHeader(): Promise<string> {
433+
await this.elementMenuButton("View Node").click();
434+
const text = await this.nodedetailsPanelHeader.innerHTML();
435+
return text;
436+
}
437+
438+
async clickOnNodeDetailsCloseBtn(): Promise<void>{
439+
await this.nodedetailsPanelcloseBtn.click();
440+
}
441+
442+
async getMetricsPanelInfo(): Promise<{nodes: string, edges: string}> {
443+
const nodes = await this.canvasMetricsPanel("1").innerHTML();
444+
const edges = await this.canvasMetricsPanel("2").innerHTML();
445+
return { nodes, edges }
446+
}
447+
448+
async clickOnCopySrcOnNode(): Promise<string> {
449+
await this.elementMenuButton("Copy src to clipboard").click();
450+
await delay(1000)
451+
return await this.page.evaluate(() => navigator.clipboard.readText());
452+
}
453+
454+
async getNodedetailsPanelID(): Promise<string> {
455+
return await this.nodedetailsPanelID.innerHTML();
456+
}
457+
458+
async getNodeDetailsPanelElements(): Promise<string[]> {
459+
await this.elementMenuButton("View Node").click();
460+
await delay(500)
461+
const elements = await this.nodedetailsPanelElements.all();
462+
return Promise.all(elements.map(element => element.innerHTML()));
463+
}
311464
}

e2e/logic/api/apiCalls.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ export class ApiCalls {
1414
return await result.json();
1515
}
1616

17-
async fetchLatestRepoInfo(projectName: string): Promise<fetchLatestRepoInfo>{
18-
const result = await postRequest(urls.baseUrl + "api/repo/" + projectName);
17+
async projectInfo(projectName: string): Promise<fetchLatestRepoInfo>{
18+
const result = await getRequest(urls.baseUrl + "api/repo/" + projectName + "/info");
1919
return await result.json();
2020
}
2121

e2e/logic/api/apiResponse.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ export interface getProjectResponse {
2121
ext?: string;
2222
name: string;
2323
path: string;
24+
src: string;
2425
doc?: string;
2526
src_end?: number;
2627
src_start?: number;

0 commit comments

Comments
 (0)