Skip to content

Commit 66552e8

Browse files
Merge branch 'release-1.10.0' into LE-1080
2 parents ed88516 + 9c6839c commit 66552e8

7 files changed

Lines changed: 128 additions & 39 deletions

File tree

src/frontend/tests/core/features/tweaksTest.spec.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,16 @@ test(
1111

1212
await page.getByTestId("side_nav_options_all-templates").click();
1313
await page.getByRole("heading", { name: "Basic Prompting" }).click();
14+
// Wait for the new-flow Loading state to clear before checking the
15+
// publish button — the canvas mounts only after the flow finishes
16+
// loading, which can outlast a 20s action timeout on Windows CI.
17+
await page.waitForSelector('text="Loading"', {
18+
state: "hidden",
19+
timeout: 60000,
20+
});
21+
await page.waitForSelector('[data-testid="publish-button"]', {
22+
timeout: 30000,
23+
});
1424
await page.getByTestId("publish-button").click();
1525
await page.getByTestId("api-access-item").click();
1626
await page.getByTestId("api_tab_curl").click();

src/frontend/tests/core/features/user-flow-state-cleanup.spec.ts

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,13 @@ test(
4646
});
4747
await page.getByRole("button", { name: "Sign In" }).click();
4848

49-
// Create User A
49+
// Create User A — wait for the homepage Loading state to clear before
50+
// checking mainpage_title (mainpage_title only renders after data load,
51+
// which can outlast a 30s wait on slower runners like Windows CI).
52+
await page.waitForSelector('text="Loading"', {
53+
state: "hidden",
54+
timeout: 60000,
55+
});
5056
await page.waitForSelector('[data-testid="mainpage_title"]', {
5157
timeout: 30000,
5258
});
@@ -106,11 +112,15 @@ test(
106112
}
107113

108114
await page.waitForSelector('[data-testid="modal-title"]', {
109-
timeout: 3000,
115+
timeout: 30000,
110116
});
117+
await page.getByTestId("side_nav_options_all-templates").click();
111118
await page.getByRole("heading", { name: "Basic Prompting" }).click();
119+
// Match the canvas_controls_dropdown wait used elsewhere in the suite
120+
// (tests/core/features/token-usage.spec.ts, chatInputOutput.spec.ts):
121+
// the new-flow canvas can take well over 30s to mount on slower runners.
112122
await page.waitForSelector('[data-testid="canvas_controls_dropdown"]', {
113-
timeout: 30000,
123+
timeout: 100000,
114124
});
115125

116126
await renameFlow(page, { flowName: userAFlowName });

src/frontend/tests/core/unit/customEdgesComponent.spec.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ test(
1616
await page.getByTestId("sidebar-search-input").click();
1717
await page.getByTestId("sidebar-search-input").fill("search api");
1818
await page.waitForSelector('[data-testid="searchapiSearchApi"]', {
19-
timeout: 1000,
19+
timeout: 10000,
2020
});
2121

2222
await zoomOut(page, 3);
@@ -32,7 +32,7 @@ test(
3232
await page.waitForSelector(
3333
'[data-testid="langchain_utilitiesTool Calling Agent"]',
3434
{
35-
timeout: 1000,
35+
timeout: 10000,
3636
},
3737
);
3838

@@ -75,7 +75,7 @@ test(
7575
await page.getByTestId("sidebar-search-input").click();
7676
await page.getByTestId("sidebar-search-input").fill("search api");
7777
await page.waitForSelector('[data-testid="searchapiSearchApi"]', {
78-
timeout: 1000,
78+
timeout: 10000,
7979
});
8080

8181
await zoomOut(page, 3);
@@ -91,7 +91,7 @@ test(
9191
await page.waitForSelector(
9292
'[data-testid="langchain_utilitiesTool Calling Agent"]',
9393
{
94-
timeout: 1000,
94+
timeout: 10000,
9595
},
9696
);
9797

src/frontend/tests/core/unit/fileUploadComponent.spec.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -355,7 +355,7 @@ test(
355355
},
356356
);
357357
await expect(page.getByText(`${newTxtFile}.txt`).first()).toBeVisible({
358-
timeout: 10000,
358+
timeout: 30000,
359359
});
360360
await page.getByTestId(`remove-file-button-${renamedTxtFile}`).click();
361361

@@ -826,7 +826,7 @@ test(
826826
await page.getByTestId("sidebar-search-input").click();
827827
await page.getByTestId("sidebar-search-input").fill("Read File");
828828
await page.waitForSelector('[data-testid="files_and_knowledgeRead File"]', {
829-
timeout: 1000,
829+
timeout: 10000,
830830
});
831831

832832
// Get flow ID from URL
@@ -902,7 +902,7 @@ test(
902902
await page.getByTestId("sidebar-search-input").click();
903903
await page.getByTestId("sidebar-search-input").fill("Text Input");
904904
await page.waitForSelector('[data-testid="input_outputText Input"]', {
905-
timeout: 1000,
905+
timeout: 10000,
906906
});
907907

908908
await adjustScreenView(page, { numberOfZoomOut: 3 });
@@ -932,7 +932,7 @@ test(
932932
await page.getByTestId("sidebar-search-input").click();
933933
await page.getByTestId("sidebar-search-input").fill("Text Input");
934934
await page.waitForSelector('[data-testid="input_outputText Input"]', {
935-
timeout: 1000,
935+
timeout: 10000,
936936
});
937937

938938
await adjustScreenView(page, { numberOfZoomOut: 3 });
@@ -958,7 +958,7 @@ test(
958958
await page.getByTestId("sidebar-search-input").click();
959959
await page.getByTestId("sidebar-search-input").fill("Chat Output");
960960
await page.waitForSelector('[data-testid="input_outputChat Output"]', {
961-
timeout: 1000,
961+
timeout: 10000,
962962
});
963963
await page
964964
.getByTestId("input_outputChat Output")

src/frontend/tests/fixtures.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,29 @@
22
import { test as base, expect, Page } from "@playwright/test";
33
import "./playwrightCoverage";
44

5+
// Optional CPU throttling for reproducing race conditions seen on slower
6+
// runners (Windows CI). Enable with LF_CPU_THROTTLE=<rate>, e.g. 4.
7+
const CPU_THROTTLE_RATE = (() => {
8+
const raw = process.env.LF_CPU_THROTTLE;
9+
if (!raw) return 0;
10+
const n = Number.parseFloat(raw);
11+
return Number.isFinite(n) && n > 1 ? n : 0;
12+
})();
13+
514
// Extend test to log backend errors
615
export const test = base.extend({
716
page: async ({ page }, use) => {
17+
if (CPU_THROTTLE_RATE > 0) {
18+
try {
19+
const client = await page.context().newCDPSession(page);
20+
await client.send("Emulation.setCPUThrottlingRate", {
21+
rate: CPU_THROTTLE_RATE,
22+
});
23+
} catch {
24+
// Throttling is best-effort and only supported on Chromium.
25+
}
26+
}
27+
828
const errors: Array<{
929
url: string;
1030
status: number;

src/frontend/tests/globalTeardown.ts

Lines changed: 68 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3,31 +3,77 @@
33
import fs from "fs";
44
import path from "path";
55

6+
const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms));
7+
8+
// On Windows, the uvicorn process can still hold SQLite file handles when
9+
// teardown runs. POSIX allows unlinking files with open handles; Win32 does
10+
// not, surfacing as EBUSY/EPERM. Retry with backoff, fall back to walking the
11+
// tree and removing children individually, and never throw out of teardown.
12+
async function removeWithRetry(target: string): Promise<boolean> {
13+
const attempts = 5;
14+
for (let i = 0; i < attempts; i++) {
15+
try {
16+
fs.rmSync(target, { recursive: true, force: true });
17+
if (!fs.existsSync(target)) return true;
18+
} catch (err) {
19+
const code = (err as NodeJS.ErrnoException).code;
20+
if (code !== "EBUSY" && code !== "EPERM" && code !== "ENOTEMPTY") {
21+
throw err;
22+
}
23+
}
24+
await sleep(200 * 2 ** i);
25+
}
26+
return !fs.existsSync(target);
27+
}
28+
29+
function removeChildrenBestEffort(target: string): string[] {
30+
const failed: string[] = [];
31+
let entries: fs.Dirent[];
32+
try {
33+
entries = fs.readdirSync(target, { withFileTypes: true });
34+
} catch {
35+
return failed;
36+
}
37+
for (const entry of entries) {
38+
const childPath = path.join(target, entry.name);
39+
try {
40+
fs.rmSync(childPath, { recursive: true, force: true });
41+
} catch {
42+
failed.push(childPath);
43+
}
44+
}
45+
return failed;
46+
}
47+
648
export default async () => {
49+
console.warn("Removing the temp database");
50+
// this file is in src/frontend/tests/globalTeardown.ts
51+
// temp is in src/frontend/temp
52+
const tempDbPath = path.join(__dirname, "..", "temp");
53+
console.warn("tempDbPath", tempDbPath);
54+
55+
if (!fs.existsSync(tempDbPath)) {
56+
console.warn("Temp database directory does not exist, skipping removal");
57+
return;
58+
}
59+
760
try {
8-
console.warn("Removing the temp database");
9-
// Check if the file exists in the path
10-
// this file is in src/frontend/tests/globalTeardown.ts
11-
// temp is in src/frontend/temp
12-
const tempDbPath = path.join(__dirname, "..", "temp");
13-
console.warn("tempDbPath", tempDbPath);
14-
15-
// Check if the directory exists before attempting to remove it
16-
if (fs.existsSync(tempDbPath)) {
17-
// Remove the temp database
18-
fs.rmSync(tempDbPath, { recursive: true, force: true });
19-
20-
// Check if the file is removed
21-
if (!fs.existsSync(tempDbPath)) {
22-
console.warn("Successfully removed the temp database");
23-
} else {
24-
console.error(
25-
"Error: temp database still exists after removal attempt",
26-
);
27-
}
28-
} else {
29-
console.warn("Temp database directory does not exist, skipping removal");
61+
if (await removeWithRetry(tempDbPath)) {
62+
console.warn("Successfully removed the temp database");
63+
return;
64+
}
65+
66+
const stragglers = removeChildrenBestEffort(tempDbPath);
67+
if (await removeWithRetry(tempDbPath)) {
68+
console.warn(
69+
"Successfully removed the temp database after per-file fallback",
70+
);
71+
return;
3072
}
73+
74+
console.warn(
75+
`Temp database directory still present after retries; leaving it for the runner workspace cleanup. Files that resisted removal: ${stragglers.length}`,
76+
);
3177
} catch (error) {
3278
console.error("Error while removing the temp database:", error);
3379
}

src/frontend/tests/utils/add-new-user-and-loggin.ts

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -42,13 +42,16 @@ export const addNewUserAndLogin = async (page: Page) => {
4242

4343
await page.getByRole("button", { name: "Sign In" }).click();
4444

45-
await page.waitForSelector('[data-testid="mainpage_title"]', {
46-
timeout: 30000,
47-
});
48-
49-
// Wait for any loading text to disappear
45+
// Wait for any loading text to disappear before checking the homepage:
46+
// mainpage_title only renders after the homepage data finishes loading,
47+
// and on slower runners (Windows CI) the Loading state can outlast a
48+
// 30s mainpage_title wait.
5049
await page.waitForSelector('text="Loading"', {
5150
state: "hidden",
51+
timeout: 60000,
52+
});
53+
54+
await page.waitForSelector('[data-testid="mainpage_title"]', {
5255
timeout: 30000,
5356
});
5457

0 commit comments

Comments
 (0)