darabos commited on
Commit
177f432
·
1 Parent(s): 5401bd5

Work around ReactFlow issue. Try making tests more robust.

Browse files
.gitignore CHANGED
@@ -16,3 +16,9 @@ joblib-cache
16
  *.egg-info
17
 
18
  lynxkite_crdt_data
 
 
 
 
 
 
 
16
  *.egg-info
17
 
18
  lynxkite_crdt_data
19
+
20
+ # Playwright
21
+ /test-results/
22
+ /playwright-report/
23
+ /blob-report/
24
+ /playwright/.cache/
lynxkite-app/web/src/workspace/Workspace.tsx CHANGED
@@ -197,9 +197,10 @@ function LynxKiteFlow() {
197
  });
198
  }
199
  };
200
- document.addEventListener("keydown", handleKeyDown);
 
201
  return () => {
202
- document.removeEventListener("keydown", handleKeyDown);
203
  };
204
  }, [catalog.data, nodeSearchSettings, state.workspace.env]);
205
 
 
197
  });
198
  }
199
  };
200
+ // TODO: Switch to keydown once https://github.com/xyflow/xyflow/pull/5055 is merged.
201
+ document.addEventListener("keyup", handleKeyDown);
202
  return () => {
203
+ document.removeEventListener("keyup", handleKeyDown);
204
  };
205
  }, [catalog.data, nodeSearchSettings, state.workspace.env]);
206
 
lynxkite-app/web/tests/basic.spec.ts CHANGED
@@ -21,6 +21,9 @@ test("Box creation & deletion per env", async () => {
21
  const envs = await workspace.getEnvs();
22
  for (const env of envs) {
23
  await workspace.setEnv(env);
 
 
 
24
  const catalog = await workspace.getCatalog();
25
  expect(catalog).not.toHaveLength(0);
26
  const op = catalog[0];
 
21
  const envs = await workspace.getEnvs();
22
  for (const env of envs) {
23
  await workspace.setEnv(env);
24
+ // TODO: Opening the catalog immediately after setting the env can fail.
25
+ // Let's fix this!
26
+ await new Promise((resolve) => setTimeout(resolve, 500));
27
  const catalog = await workspace.getCatalog();
28
  expect(catalog).not.toHaveLength(0);
29
  const op = catalog[0];
lynxkite-app/web/tests/examples.spec.ts CHANGED
@@ -4,44 +4,44 @@ import { Workspace } from "./lynxkite";
4
 
5
  test("LynxKite Graph Analytics example", async ({ page }) => {
6
  const ws = await Workspace.open(page, "NetworkX demo");
7
- expect(await ws.isErrorFree(process.env.CI ? 2000 : 1000)).toBeTruthy();
8
  });
9
 
10
  test("Pytorch example", async ({ page }) => {
11
  const ws = await Workspace.open(page, "PyTorch demo");
12
- expect(await ws.isErrorFree()).toBeTruthy();
13
  });
14
 
15
  test.fail("AIMO example", async ({ page }) => {
16
  // Fails because of missing OPENAI_API_KEY
17
  const ws = await Workspace.open(page, "AIMO");
18
- expect(await ws.isErrorFree()).toBeTruthy();
19
  });
20
 
21
  test.fail("LynxScribe example", async ({ page }) => {
22
  // Fails because of missing OPENAI_API_KEY
23
  const ws = await Workspace.open(page, "LynxScribe demo");
24
- expect(await ws.isErrorFree()).toBeTruthy();
25
  });
26
 
27
  test.fail("Graph RAG", async ({ page }) => {
28
  // Fails due to some issue with ChromaDB
29
  const ws = await Workspace.open(page, "Graph RAG");
30
- expect(await ws.isErrorFree(process.env.CI ? 2000 : 500)).toBeTruthy();
31
  });
32
 
33
  test.fail("RAG chatbot app", async ({ page }) => {
34
  // Fail due to all operation being unknown
35
  const ws = await Workspace.open(page, "RAG chatbot app");
36
- expect(await ws.isErrorFree()).toBeTruthy();
37
  });
38
 
39
  test("Airlines demo", async ({ page }) => {
40
  const ws = await Workspace.open(page, "Airlines demo");
41
- expect(await ws.isErrorFree(process.env.CI ? 10000 : 500)).toBeTruthy();
42
  });
43
 
44
  test("Pillow example", async ({ page }) => {
45
  const ws = await Workspace.open(page, "Image processing");
46
- expect(await ws.isErrorFree()).toBeTruthy();
47
  });
 
4
 
5
  test("LynxKite Graph Analytics example", async ({ page }) => {
6
  const ws = await Workspace.open(page, "NetworkX demo");
7
+ await ws.expectErrorFree(process.env.CI ? 2000 : 1000);
8
  });
9
 
10
  test("Pytorch example", async ({ page }) => {
11
  const ws = await Workspace.open(page, "PyTorch demo");
12
+ await ws.expectErrorFree();
13
  });
14
 
15
  test.fail("AIMO example", async ({ page }) => {
16
  // Fails because of missing OPENAI_API_KEY
17
  const ws = await Workspace.open(page, "AIMO");
18
+ await ws.expectErrorFree();
19
  });
20
 
21
  test.fail("LynxScribe example", async ({ page }) => {
22
  // Fails because of missing OPENAI_API_KEY
23
  const ws = await Workspace.open(page, "LynxScribe demo");
24
+ await ws.expectErrorFree();
25
  });
26
 
27
  test.fail("Graph RAG", async ({ page }) => {
28
  // Fails due to some issue with ChromaDB
29
  const ws = await Workspace.open(page, "Graph RAG");
30
+ await ws.expectErrorFree(process.env.CI ? 2000 : 500);
31
  });
32
 
33
  test.fail("RAG chatbot app", async ({ page }) => {
34
  // Fail due to all operation being unknown
35
  const ws = await Workspace.open(page, "RAG chatbot app");
36
+ await ws.expectErrorFree();
37
  });
38
 
39
  test("Airlines demo", async ({ page }) => {
40
  const ws = await Workspace.open(page, "Airlines demo");
41
+ await ws.expectErrorFree(process.env.CI ? 10000 : 500);
42
  });
43
 
44
  test("Pillow example", async ({ page }) => {
45
  const ws = await Workspace.open(page, "Image processing");
46
+ await ws.expectErrorFree();
47
  });
lynxkite-app/web/tests/lynxkite.ts CHANGED
@@ -54,7 +54,7 @@ export class Workspace {
54
 
55
  async addBox(boxName) {
56
  //TODO: Support passing box parameters (id, position, etc.)
57
- const allBoxes = await this.getBoxes();
58
  if (allBoxes) {
59
  // Avoid overlapping with existing nodes
60
  const numNodes = allBoxes.length || 1;
@@ -66,9 +66,7 @@ export class Workspace {
66
  await this.page.locator(".ws-name").click();
67
  await this.page.keyboard.press("/");
68
  await this.page.locator(".node-search").getByText(boxName).click();
69
- await this.page.keyboard.press("Escape");
70
- // Workaround to wait for the deselection animation after choosing a box. Otherwise, the next box will not be added.
71
- await new Promise((resolve) => setTimeout(resolve, 200));
72
  }
73
 
74
  async getCatalog() {
@@ -78,14 +76,22 @@ export class Workspace {
78
  .allInnerTexts();
79
  // Dismiss the catalog menu
80
  await this.page.keyboard.press("Escape");
81
- await new Promise((resolve) => setTimeout(resolve, 200));
82
  return catalog;
83
  }
84
 
 
 
 
 
 
 
 
85
  async deleteBoxes(boxIds: string[]) {
86
  for (const boxId of boxIds) {
87
- await this.getBoxHandle(boxId).first().click();
88
  await this.page.keyboard.press("Backspace");
 
89
  }
90
  }
91
 
@@ -94,7 +100,7 @@ export class Workspace {
94
  }
95
 
96
  getBoxes() {
97
- return this.page.locator(".react-flow__node").all();
98
  }
99
 
100
  getBoxHandle(boxId: string, pos?: string) {
@@ -143,19 +149,13 @@ export class Workspace {
143
  await this.page.mouse.up();
144
  }
145
 
146
- async isErrorFree(executionWaitTime?): Promise<boolean> {
147
  // TODO: Workaround, to account for workspace execution. Once
148
  // we have a load indicator we can use that instead.
149
  await new Promise((resolve) =>
150
  setTimeout(resolve, executionWaitTime ? executionWaitTime : 500),
151
  );
152
- const boxes = await this.getBoxes();
153
- for (const box of boxes) {
154
- if (await box.locator(".error").isVisible()) {
155
- return false;
156
- }
157
- }
158
- return true;
159
  }
160
 
161
  async close() {
 
54
 
55
  async addBox(boxName) {
56
  //TODO: Support passing box parameters (id, position, etc.)
57
+ const allBoxes = await this.getBoxes().all();
58
  if (allBoxes) {
59
  // Avoid overlapping with existing nodes
60
  const numNodes = allBoxes.length || 1;
 
66
  await this.page.locator(".ws-name").click();
67
  await this.page.keyboard.press("/");
68
  await this.page.locator(".node-search").getByText(boxName).click();
69
+ await expect(this.getBoxes()).toHaveCount(allBoxes.length + 1);
 
 
70
  }
71
 
72
  async getCatalog() {
 
76
  .allInnerTexts();
77
  // Dismiss the catalog menu
78
  await this.page.keyboard.press("Escape");
79
+ await expect(this.page.locator(".node-search")).not.toBeVisible();
80
  return catalog;
81
  }
82
 
83
+ async selectBox(boxId: string) {
84
+ const box = this.getBox(boxId);
85
+ // Click on the resizer, so we don't click on any parameters by accident.
86
+ await box.locator(".react-flow__resize-control").click();
87
+ await expect(box).toHaveClass(/selected/);
88
+ }
89
+
90
  async deleteBoxes(boxIds: string[]) {
91
  for (const boxId of boxIds) {
92
+ await this.selectBox(boxId);
93
  await this.page.keyboard.press("Backspace");
94
+ await expect(this.getBox(boxId)).not.toBeVisible();
95
  }
96
  }
97
 
 
100
  }
101
 
102
  getBoxes() {
103
+ return this.page.locator(".react-flow__node");
104
  }
105
 
106
  getBoxHandle(boxId: string, pos?: string) {
 
149
  await this.page.mouse.up();
150
  }
151
 
152
+ async expectErrorFree(executionWaitTime?) {
153
  // TODO: Workaround, to account for workspace execution. Once
154
  // we have a load indicator we can use that instead.
155
  await new Promise((resolve) =>
156
  setTimeout(resolve, executionWaitTime ? executionWaitTime : 500),
157
  );
158
+ await expect(this.getBoxes().locator(".error")).not.toBeVisible();
 
 
 
 
 
 
159
  }
160
 
161
  async close() {