From 993ebd9ef7b465cd64978c1081302ff8b34a7767 Mon Sep 17 00:00:00 2001 From: Roomote Date: Sat, 29 Aug 2026 02:14:51 +0000 Subject: [PATCH 1/2] test(e2e): await subtask approval restoration --- apps/vscode-e2e/src/suite/subtasks.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/vscode-e2e/src/suite/subtasks.test.ts b/apps/vscode-e2e/src/suite/subtasks.test.ts index 4a0e209895..497ae64484 100644 --- a/apps/vscode-e2e/src/suite/subtasks.test.ts +++ b/apps/vscode-e2e/src/suite/subtasks.test.ts @@ -213,7 +213,7 @@ suite("Roo Code Subtasks", function () { const parentAskCount = asks[parentTaskId]?.length ?? 0 await api.clearCurrentTask() - api.resumeTask(parentTaskId) + await api.resumeTask(parentTaskId) await waitFor(() => hasToolAsk(parentTaskId, "newTask", parentAskCount)) assert.ok( !asks[parentTaskId]?.slice(parentAskCount).some(({ ask }) => ask === "resume_task"), @@ -235,7 +235,7 @@ suite("Roo Code Subtasks", function () { const childAskCount = asks[childTaskId!]?.length ?? 0 await api.clearCurrentTask() - api.resumeTask(childTaskId!) + await api.resumeTask(childTaskId!) await waitFor(() => hasToolAsk(childTaskId!, "finishTask", childAskCount)) assert.ok( !asks[childTaskId!] From 165ea602875407adb85c21112e3d5aee2cb757ae Mon Sep 17 00:00:00 2001 From: Roomote Date: Sat, 29 Aug 2026 13:22:50 +0000 Subject: [PATCH 2/2] lint(e2e): reject floating promises --- apps/vscode-e2e/eslint.config.mjs | 16 ++++++- apps/vscode-e2e/src/runTest.ts | 5 +- apps/vscode-e2e/src/suite/index.ts | 4 +- apps/vscode-e2e/src/suite/utils.ts | 47 +++++++++++-------- .../src/theme-fixtures/fixtures.test.ts | 10 ++-- 5 files changed, 54 insertions(+), 28 deletions(-) diff --git a/apps/vscode-e2e/eslint.config.mjs b/apps/vscode-e2e/eslint.config.mjs index 694bf73664..222e2069b0 100644 --- a/apps/vscode-e2e/eslint.config.mjs +++ b/apps/vscode-e2e/eslint.config.mjs @@ -1,4 +1,18 @@ import { config } from "@roo-code/config-eslint/base" /** @type {import("eslint").Linter.Config} */ -export default [...config] +export default [ + ...config, + { + files: ["src/**/*.ts"], + languageOptions: { + parserOptions: { + project: true, + tsconfigRootDir: import.meta.dirname, + }, + }, + rules: { + "@typescript-eslint/no-floating-promises": "error", + }, + }, +] diff --git a/apps/vscode-e2e/src/runTest.ts b/apps/vscode-e2e/src/runTest.ts index 8162f34068..88c687bc76 100644 --- a/apps/vscode-e2e/src/runTest.ts +++ b/apps/vscode-e2e/src/runTest.ts @@ -231,4 +231,7 @@ async function main() { } } -main() +main().catch((error) => { + console.error("Failed to initialize or clean up tests", error) + process.exitCode = 1 +}) diff --git a/apps/vscode-e2e/src/suite/index.ts b/apps/vscode-e2e/src/suite/index.ts index e93d73bd37..d9fd0683c0 100644 --- a/apps/vscode-e2e/src/suite/index.ts +++ b/apps/vscode-e2e/src/suite/index.ts @@ -35,7 +35,9 @@ export async function run() { // for a button that the webview routes to "start new task" rather than "yes". api.on(RooCodeEventName.Message, ({ message }) => { if (message.type === "ask" && message.ask === "completion_result") { - api.approveCurrentAsk() + void api.approveCurrentAsk().catch((error) => { + console.error("Failed to approve completion result", error) + }) } }) diff --git a/apps/vscode-e2e/src/suite/utils.ts b/apps/vscode-e2e/src/suite/utils.ts index 7203f87681..a11cb0cab0 100644 --- a/apps/vscode-e2e/src/suite/utils.ts +++ b/apps/vscode-e2e/src/suite/utils.ts @@ -9,34 +9,41 @@ export const waitFor = ( condition: (() => Promise) | (() => boolean), { timeout = 30_000, interval = 250 }: WaitForOptions = {}, ) => { - let timeoutId: NodeJS.Timeout | undefined = undefined + return new Promise((resolve, reject) => { + let settled = false + let intervalId: NodeJS.Timeout | undefined + const timeoutId = setTimeout(() => { + settled = true + if (intervalId) clearTimeout(intervalId) + reject(new Error(`Timeout after ${Math.floor(timeout / 1000)}s`)) + }, timeout) - return Promise.race([ - new Promise((resolve) => { - const check = async () => { - const result = condition() - const isSatisfied = result instanceof Promise ? await result : result + const cleanup = () => { + clearTimeout(timeoutId) + if (intervalId) clearTimeout(intervalId) + } + const check = async () => { + try { + const isSatisfied = await condition() + if (settled) return if (isSatisfied) { - if (timeoutId) { - clearTimeout(timeoutId) - timeoutId = undefined - } - + settled = true + cleanup() resolve() } else { - setTimeout(check, interval) + intervalId = setTimeout(() => void check(), interval) } + } catch (error) { + if (settled) return + settled = true + cleanup() + reject(error) } + } - check() - }), - new Promise((_, reject) => { - timeoutId = setTimeout(() => { - reject(new Error(`Timeout after ${Math.floor(timeout / 1000)}s`)) - }, timeout) - }), - ]) + void check() + }) } type WaitUntilAbortedOptions = WaitForOptions & { diff --git a/apps/vscode-e2e/src/theme-fixtures/fixtures.test.ts b/apps/vscode-e2e/src/theme-fixtures/fixtures.test.ts index 16a66153f9..6f0e3f1a8a 100644 --- a/apps/vscode-e2e/src/theme-fixtures/fixtures.test.ts +++ b/apps/vscode-e2e/src/theme-fixtures/fixtures.test.ts @@ -20,7 +20,7 @@ const validFixture: WebviewThemeFixture = { }, } -test("serializeThemeFixture sorts variables and emits stable metadata", () => { +void test("serializeThemeFixture sorts variables and emits stable metadata", () => { const fixture: WebviewThemeFixture = { themeId: "Default Dark Modern", bodyClass: "vscode-dark", @@ -45,7 +45,7 @@ test("serializeThemeFixture sorts variables and emits stable metadata", () => { ) }) -test("findDriftedFixtures reports missing and changed files in sorted order", () => { +void test("findDriftedFixtures reports missing and changed files in sorted order", () => { const expected = new Map([ ["vscode-theme-light.css", "light"], ["vscode-theme-dark.css", "dark"], @@ -55,7 +55,7 @@ test("findDriftedFixtures reports missing and changed files in sorted order", () assert.deepEqual(findDriftedFixtures(expected, actual), ["vscode-theme-dark.css", "vscode-theme-light.css"]) }) -test("createSerializedFixtures rejects incomplete captures", () => { +void test("createSerializedFixtures rejects incomplete captures", () => { const fixture: WebviewThemeFixture = { ...validFixture, variables: { @@ -71,7 +71,7 @@ test("createSerializedFixtures rejects incomplete captures", () => { ) }) -test("createSerializedFixtures rejects an empty capture", () => { +void test("createSerializedFixtures rejects an empty capture", () => { assert.throws( () => createSerializedFixtures(new Map([["dark", { ...validFixture, variables: {} }]]), "1.100.0", [ @@ -81,7 +81,7 @@ test("createSerializedFixtures rejects an empty capture", () => { ) }) -test("createSerializedFixtures rejects the wrong theme identity", () => { +void test("createSerializedFixtures rejects the wrong theme identity", () => { assert.throws( () => createSerializedFixtures(