Skip to content

Commit 147147c

Browse files
test(e2e): ignore partial asks in completion waits (#1449)
Co-authored-by: Roomote <roomote@roomote.dev>
1 parent 97265fd commit 147147c

7 files changed

Lines changed: 42 additions & 18 deletions

File tree

apps/vscode-e2e/AGENTS.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,10 @@ Background API calls from the extension (usage collection, initialization) hit a
158158
| `OPENROUTER_API_KEY=<key> pnpm --filter @roo-code/vscode-e2e test:record` | Record mode — proxies to real API, writes `openai-*.json` |
159159
| `OPENROUTER_API_KEY=<key> pnpm --filter @roo-code/vscode-e2e test:ci` | Real-API mode — runs against live OpenRouter (for drift detection) |
160160
161+
## Waiting for actionable messages
162+
163+
Tool and completion asks can be emitted first as partial streaming previews. Tests that approve an ask, navigate away, or assert a durable action must wait for `message.partial !== true`; observing a matching tool name is not sufficient. Use `isCompletedAsk` from `src/suite/utils.ts` for completion-oriented message collectors. Only consume partial asks when the test explicitly covers streaming behavior.
164+
161165
## Tests that use a fetch interceptor instead of aimock
162166
163167
Some suites can't redirect their provider through aimock. These suites patch `globalThis.fetch` directly — the OpenAI SDK resolves `fetch` at API client construction time (which happens lazily at task start), so installing the interceptor before `api.startNewTask()` is sufficient. Installing it before `api.setConfiguration()` (as done below) is the conservative, recommended order.

apps/vscode-e2e/src/suite/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import * as vscode from "vscode"
55

66
import { RooCodeEventName, type RooCodeAPI } from "@roo-code/types"
77

8-
import { waitFor } from "./utils"
8+
import { isCompletedAsk, waitFor } from "./utils"
99

1010
export async function run() {
1111
const extension = vscode.extensions.getExtension<RooCodeAPI>("ZooCodeOrganization.zoo-code")
@@ -34,7 +34,7 @@ export async function run() {
3434
// Automatically approve completion_result asks so tests don't stall waiting
3535
// for a button that the webview routes to "start new task" rather than "yes".
3636
api.on(RooCodeEventName.Message, ({ message }) => {
37-
if (message.type === "ask" && message.ask === "completion_result") {
37+
if (isCompletedAsk(message) && message.ask === "completion_result") {
3838
void api.approveCurrentAsk().catch((error) => {
3939
console.error("Failed to approve completion result", error)
4040
})

apps/vscode-e2e/src/suite/subtasks.test.ts

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import * as assert from "assert"
33
import { RooCodeEventName, type ClineMessage } from "@roo-code/types"
44

55
import { setDefaultSuiteTimeout } from "./test-utils"
6-
import { sleep, waitFor, waitUntilCompleted } from "./utils"
6+
import { isCompletedAsk, sleep, waitFor, waitUntilCompleted } from "./utils"
77
import {
88
SCHED_COMPLETED_PROMPT,
99
SCHED_COMPLETED_RESULT,
@@ -180,7 +180,7 @@ suite("Roo Code Subtasks", function () {
180180
const asks: Record<string, ClineMessage[]> = {}
181181

182182
const messageHandler = ({ taskId, message }: { taskId: string; message: ClineMessage }) => {
183-
if (message.type === "ask") {
183+
if (isCompletedAsk(message)) {
184184
asks[taskId] = asks[taskId] || []
185185
asks[taskId].push(message)
186186
}
@@ -266,7 +266,7 @@ suite("Roo Code Subtasks", function () {
266266
const says: Record<string, ClineMessage[]> = {}
267267

268268
const messageHandler = ({ taskId, message }: { taskId: string; message: ClineMessage }) => {
269-
if (message.type === "ask") {
269+
if (isCompletedAsk(message)) {
270270
asks[taskId] = asks[taskId] || []
271271
asks[taskId].push(message)
272272
}
@@ -347,7 +347,7 @@ suite("Roo Code Subtasks", function () {
347347
let delegationCompletedSummary: string | undefined
348348

349349
const messageHandler = ({ taskId, message }: { taskId: string; message: ClineMessage }) => {
350-
if (message.type === "ask") {
350+
if (isCompletedAsk(message)) {
351351
asks[taskId] = asks[taskId] || []
352352
asks[taskId].push(message)
353353
}
@@ -452,7 +452,7 @@ suite("Roo Code Subtasks", function () {
452452
const messages: Record<string, ClineMessage[]> = {}
453453

454454
const messageHandler = ({ taskId, message }: { taskId: string; message: ClineMessage }) => {
455-
if (message.type === "ask") {
455+
if (isCompletedAsk(message)) {
456456
asks[taskId] = asks[taskId] || []
457457
asks[taskId].push(message)
458458
}
@@ -532,7 +532,7 @@ suite("Roo Code Subtasks", function () {
532532
const says: Record<string, ClineMessage[]> = {}
533533

534534
const messageHandler = ({ taskId, message }: { taskId: string; message: ClineMessage }) => {
535-
if (message.type === "ask") {
535+
if (isCompletedAsk(message)) {
536536
asks[taskId] = asks[taskId] || []
537537
asks[taskId].push(message)
538538
}
@@ -615,7 +615,7 @@ suite("Roo Code Subtasks", function () {
615615
let delayedChildRequestStartedAt: number | undefined
616616

617617
const messageHandler = ({ taskId, message }: { taskId: string; message: ClineMessage }) => {
618-
if (message.type === "ask") {
618+
if (isCompletedAsk(message)) {
619619
asks[taskId] = asks[taskId] || []
620620
asks[taskId].push(message)
621621
}
@@ -842,7 +842,7 @@ suite("Roo Code Subtasks", function () {
842842
const says: Record<string, ClineMessage[]> = {}
843843

844844
const messageHandler = ({ taskId, message }: { taskId: string; message: ClineMessage }) => {
845-
if (message.type === "ask") {
845+
if (isCompletedAsk(message)) {
846846
asks[taskId] = asks[taskId] || []
847847
asks[taskId].push(message)
848848
}
@@ -940,7 +940,7 @@ suite("Roo Code Subtasks", function () {
940940
const says: Record<string, ClineMessage[]> = {}
941941

942942
const messageHandler = ({ taskId, message }: { taskId: string; message: ClineMessage }) => {
943-
if (message.type === "ask") {
943+
if (isCompletedAsk(message)) {
944944
asks[taskId] = asks[taskId] || []
945945
asks[taskId].push(message)
946946
}
@@ -1101,7 +1101,7 @@ suite("Roo Code Subtasks", function () {
11011101
const says: Record<string, ClineMessage[]> = {}
11021102

11031103
const messageHandler = ({ taskId, message }: { taskId: string; message: ClineMessage }) => {
1104-
if (message.type === "ask") {
1104+
if (isCompletedAsk(message)) {
11051105
asks[taskId] = asks[taskId] || []
11061106
asks[taskId].push(message)
11071107
}
@@ -1157,7 +1157,7 @@ suite("Roo Code Subtasks", function () {
11571157
const says: Record<string, ClineMessage[]> = {}
11581158

11591159
const messageHandler = ({ taskId, message }: { taskId: string; message: ClineMessage }) => {
1160-
if (message.type === "ask") {
1160+
if (isCompletedAsk(message)) {
11611161
asks[taskId] = asks[taskId] || []
11621162
asks[taskId].push(message)
11631163
}

apps/vscode-e2e/src/suite/tools/write-to-file.test.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import * as vscode from "vscode"
55

66
import { RooCodeEventName, type ClineMessage } from "@roo-code/types"
77

8-
import { waitUntilCompleted, sleep } from "../utils"
8+
import { isCompletedAsk, waitUntilCompleted, sleep } from "../utils"
99
import { setDefaultSuiteTimeout } from "../test-utils"
1010

1111
const TEST_DIR_NAME = "write-to-file-tool-fixture"
@@ -120,7 +120,7 @@ suite("Roo Code write_to_file Tool", function () {
120120

121121
const toolApprovalMessage = messages.find(
122122
(message) =>
123-
message.type === "ask" &&
123+
isCompletedAsk(message) &&
124124
message.ask === "tool" &&
125125
message.text?.includes("write-to-file-smoke.txt"),
126126
)
@@ -168,7 +168,7 @@ suite("Roo Code write_to_file Tool", function () {
168168

169169
const toolApprovalMessage = messages.find(
170170
(message) =>
171-
message.type === "ask" &&
171+
isCompletedAsk(message) &&
172172
message.ask === "tool" &&
173173
message.text?.includes("write-to-file-nested-smoke.txt"),
174174
)
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import * as assert from "assert"
2+
3+
import type { ClineMessage } from "@roo-code/types"
4+
5+
import { isCompletedAsk } from "./utils"
6+
7+
suite("E2E message guards", () => {
8+
test("distinguishes completed asks from streaming previews", () => {
9+
const message = { ts: 1, type: "ask", ask: "tool" } satisfies ClineMessage
10+
11+
assert.strictEqual(isCompletedAsk({ ...message, partial: true }), false)
12+
assert.strictEqual(isCompletedAsk({ ...message, partial: false }), true)
13+
assert.strictEqual(isCompletedAsk(message), true)
14+
assert.strictEqual(isCompletedAsk({ ts: 1, type: "say", say: "text" }), false)
15+
})
16+
})

apps/vscode-e2e/src/suite/utils.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
import { RooCodeEventName, type RooCodeAPI } from "@roo-code/types"
1+
import { RooCodeEventName, type ClineMessage, type RooCodeAPI } from "@roo-code/types"
2+
3+
export const isCompletedAsk = (message: ClineMessage) => message.type === "ask" && message.partial !== true
24

35
type WaitForOptions = {
46
timeout?: number

apps/vscode-e2e/src/visual/sceneController.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import * as vscode from "vscode"
44

55
import { RooCodeEventName, type ClineMessage, type RooCodeTestAPI, type WebviewThemeFixture } from "@roo-code/types"
66

7+
import { isCompletedAsk } from "../suite/utils"
8+
79
const POLL_INTERVAL_MS = 100
810
const SCENE_TIMEOUT_MS = 60_000
911

@@ -103,7 +105,7 @@ export async function run(): Promise<void> {
103105
)
104106
if (
105107
(message.type === "say" && message.say === "completion_result" && message.text?.includes(result)) ||
106-
(message.type === "ask" && message.ask === "completion_result" && message.text?.includes(result))
108+
(isCompletedAsk(message) && message.ask === "completion_result" && message.text?.includes(result))
107109
) {
108110
resolve()
109111
}

0 commit comments

Comments
 (0)