Spaces:
Running
Running
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 |
-
|
|
|
201 |
return () => {
|
202 |
-
document.removeEventListener("
|
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 |
-
|
8 |
});
|
9 |
|
10 |
test("Pytorch example", async ({ page }) => {
|
11 |
const ws = await Workspace.open(page, "PyTorch demo");
|
12 |
-
|
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 |
-
|
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 |
-
|
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 |
-
|
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 |
-
|
37 |
});
|
38 |
|
39 |
test("Airlines demo", async ({ page }) => {
|
40 |
const ws = await Workspace.open(page, "Airlines demo");
|
41 |
-
|
42 |
});
|
43 |
|
44 |
test("Pillow example", async ({ page }) => {
|
45 |
const ws = await Workspace.open(page, "Image processing");
|
46 |
-
|
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.
|
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
|
82 |
return catalog;
|
83 |
}
|
84 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
85 |
async deleteBoxes(boxIds: string[]) {
|
86 |
for (const boxId of boxIds) {
|
87 |
-
await this.
|
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")
|
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
|
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 |
-
|
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() {
|