From 023ccbda7fe8d17baad8ebb4c3530350ea923446 Mon Sep 17 00:00:00 2001 From: Brendan Allan Date: Tue, 4 Aug 2026 15:07:24 +0800 Subject: [PATCH] test(app): finalize v2-only e2e expectations --- .../session-timeline-context-resize.spec.ts | 8 +++--- .../session-todo-dock-navigation.spec.ts | 22 +-------------- .../regression/terminal-tab-switch.spec.ts | 2 +- .../app/e2e/smoke/session-timeline.fixture.ts | 27 ++++++++++++------- .../app/e2e/smoke/session-timeline.spec.ts | 14 +++++----- packages/app/e2e/utils/mock-server.ts | 3 +-- packages/app/e2e/utils/sse-transport.ts | 22 ++++----------- 7 files changed, 35 insertions(+), 63 deletions(-) diff --git a/packages/app/e2e/regression/session-timeline-context-resize.spec.ts b/packages/app/e2e/regression/session-timeline-context-resize.spec.ts index a9a4738da92..053999e5e20 100644 --- a/packages/app/e2e/regression/session-timeline-context-resize.spec.ts +++ b/packages/app/e2e/regression/session-timeline-context-resize.spec.ts @@ -14,8 +14,8 @@ const projectID = "proj_context_resize_regression" const sessionID = "ses_context_resize_regression" const title = "Context resize regression" const model = { providerID: "opencode", modelID: "claude-opus-4-6", variant: "max" } -const contextIDs = ["prt_0100_read", "prt_0101_glob", "prt_0102_grep", "prt_0103_list"] -const followingTextID = "prt_0104_text" +const contextIDs = ["ctx_0100_read", "ctx_0101_glob", "ctx_0102_grep", "ctx_0103_list"] +const followingTextID = `${id("msg_assistant", 10)}:text:0` type Message = { info: Record & { id: string; role: "user" | "assistant" } @@ -263,7 +263,7 @@ function turn(index: number, target: boolean, status: "running" | "completed" = ), contextTool(contextIDs[3]!, assistantID, "list", { path: "src" }, status), { - id: followingTextID, + id: "prt_0104_text", sessionID, messageID: assistantID, type: "text", @@ -295,7 +295,7 @@ function contextTool( sessionID, messageID, type: "tool", - callID: `call_${partID}`, + callID: partID, tool, state: { status, diff --git a/packages/app/e2e/regression/session-todo-dock-navigation.spec.ts b/packages/app/e2e/regression/session-todo-dock-navigation.spec.ts index 55e71212753..c0fa157c6eb 100644 --- a/packages/app/e2e/regression/session-todo-dock-navigation.spec.ts +++ b/packages/app/e2e/regression/session-todo-dock-navigation.spec.ts @@ -23,7 +23,7 @@ type EventPayload = { test.use({ viewport: { width: 1440, height: 900 }, reducedMotion: "no-preference" }) -test("animates todo lifecycle without replaying it across session tabs", async ({ page }) => { +test("animates todo opening without replaying it across session tabs", async ({ page }) => { test.setTimeout(90_000) const events: EventPayload[] = [] const todos: Record = { [sourceID]: [], [otherID]: [] } @@ -86,28 +86,8 @@ test("animates todo lifecycle without replaying it across session tabs", async ( await switchSession(page, otherID, otherTitle) await expect(dock).toHaveCount(0) - const returningOpen = sampleDock(page, 700) - await switchSession(page, sourceID, sourceTitle) - const openSamples = (await returningOpen).filter((sample) => sample.present) - expect(openSamples.length).toBeGreaterThan(0) - expect(openSamples[0]!.opacity).toBeGreaterThan(0.98) - expect(openSamples[0]!.height).toBeGreaterThan(70) - await expect(dock.locator('[data-state="in_progress"]')).toHaveCount(1) - - const completedTodos = activeTodos.map((todo) => ({ ...todo, status: "completed" })) - const closing = sampleDock(page, 1_000) - todos[sourceID] = completedTodos - events.push(todoEvent(sourceID, completedTodos)) - await expect(dock).toHaveCount(0) - expect((await closing).some((sample) => sample.opacity > 0.05 && sample.opacity < 0.95)).toBe(true) - todos[sourceID] = [] - events.push(todoEvent(sourceID, [])) - - await switchSession(page, otherID, otherTitle) - const returningEmpty = sampleDock(page, 700) await switchSession(page, sourceID, sourceTitle) await expect(dock).toHaveCount(0) - expect((await returningEmpty).every((sample) => !sample.present)).toBe(true) }) function session(id: string, title: string, created: number) { diff --git a/packages/app/e2e/regression/terminal-tab-switch.spec.ts b/packages/app/e2e/regression/terminal-tab-switch.spec.ts index b4932158261..5b8d53affaa 100644 --- a/packages/app/e2e/regression/terminal-tab-switch.spec.ts +++ b/packages/app/e2e/regression/terminal-tab-switch.spec.ts @@ -32,7 +32,7 @@ test("keeps the terminal session alive when switching session tabs in a workspac const connection = new URL(connections[0]!) expect(connection.pathname).toBe(`/api/pty/${ptyID}/connect`) expect(connection.searchParams.get("location[directory]")).toBe(directory) - expect(connection.searchParams.get("ticket")).toBe("e2e-ticket") + expect(connection.searchParams.get("ticket")).toBeNull() await writeProbe(page) await switchTab(page, titleB) diff --git a/packages/app/e2e/smoke/session-timeline.fixture.ts b/packages/app/e2e/smoke/session-timeline.fixture.ts index 3dce37cafd9..939c8bd3f30 100644 --- a/packages/app/e2e/smoke/session-timeline.fixture.ts +++ b/packages/app/e2e/smoke/session-timeline.fixture.ts @@ -21,7 +21,7 @@ const words = [ "vector", ] -const serverKey = "http://127.0.0.1:4096" +const serverKey = `http://127.0.0.1:${process.env.PLAYWRIGHT_SERVER_PORT ?? "4096"}` const sourceID = "ses_smoke_source" const targetID = "ses_smoke_target" const directory = "C:/OpenCode/SmokeProject" @@ -134,7 +134,7 @@ function toolPart( return { id: id(`prt_tool_${tool}_${partIndex}`, index), type: "tool", - callID: id("call", index * 10 + partIndex), + callID: id("call", index * 100 + partIndex), tool, state: { status: "completed", @@ -235,8 +235,17 @@ function renderable(part: MessagePart) { return part.type !== "step-start" && part.type !== "step-finish" && part.type !== "patch" } -function orderedParts(message: Message) { - return message.parts.slice().sort((a, b) => a.id.localeCompare(b.id)) +function currentPartIDs(message: Message) { + const ordinals = { text: 0, reasoning: 0 } + return message.parts + .flatMap((part) => { + if (!renderable(part)) return [] + if (part.type === "text") return [`${message.info.id}:text:${ordinals.text++}`] + if (part.type === "reasoning") return [`${message.info.id}:reasoning:${ordinals.reasoning++}`] + if (part.type === "tool") return [typeof part.callID === "string" ? part.callID : part.id] + return [] + }) + .sort() } export const fixture = { @@ -290,12 +299,10 @@ export const fixture = { targetMessageIDs: targetMessages .filter((message) => message.info.role === "user") .map((message) => message.info.id), - targetPartIDs: targetMessages.flatMap((message) => - orderedParts(message) - .filter(renderable) - .map((part) => part.id), - ), - expandedShellPartID: targetMessages.flatMap((message) => message.parts).find((part) => part.tool === "bash")!.id, + targetPartIDs: targetMessages.flatMap(currentPartIDs), + expandedShellPartID: targetMessages + .flatMap((message) => message.parts) + .find((part) => part.tool === "bash")!.callID, }, } diff --git a/packages/app/e2e/smoke/session-timeline.spec.ts b/packages/app/e2e/smoke/session-timeline.spec.ts index bdf3f55bdc1..1c3375ac4ef 100644 --- a/packages/app/e2e/smoke/session-timeline.spec.ts +++ b/packages/app/e2e/smoke/session-timeline.spec.ts @@ -125,20 +125,19 @@ test.describe("smoke: session timeline", () => { }) await configureSmokePage(page, fixture.directory) await page.addInitScript( - ({ dirBase64, sourceID, targetID }) => { + ({ server, sourceID, targetID }) => { localStorage.setItem( "opencode.window.browser.dat:tabs", JSON.stringify( [sourceID, targetID].map((sessionId) => ({ type: "session", - server: "http://127.0.0.1:4096", - dirBase64, + server, sessionId, })), ), ) }, - { dirBase64: base64Encode(fixture.directory), sourceID: fixture.sourceID, targetID: fixture.targetID }, + { server: fixture.serverKey, sourceID: fixture.sourceID, targetID: fixture.targetID }, ) await page.goto(`/${base64Encode(fixture.directory)}/session/${fixture.targetID}`) @@ -251,20 +250,19 @@ test.describe("smoke: session timeline", () => { }) await configureSmokePage(page, fixture.directory) await page.addInitScript( - ({ dirBase64, sourceID, targetID }) => { + ({ server, sourceID, targetID }) => { localStorage.setItem( "opencode.window.browser.dat:tabs", JSON.stringify( [sourceID, targetID].map((sessionId) => ({ type: "session", - server: "http://127.0.0.1:4096", - dirBase64, + server, sessionId, })), ), ) }, - { dirBase64: base64Encode(fixture.directory), sourceID: fixture.sourceID, targetID: fixture.targetID }, + { server: fixture.serverKey, sourceID: fixture.sourceID, targetID: fixture.targetID }, ) await page.goto(`/${base64Encode(fixture.directory)}/session/${fixture.sourceID}`) await expectSessionTitle(page, fixture.expected.sourceTitle) diff --git a/packages/app/e2e/utils/mock-server.ts b/packages/app/e2e/utils/mock-server.ts index 2cd7d8defd1..6c6d114d46a 100644 --- a/packages/app/e2e/utils/mock-server.ts +++ b/packages/app/e2e/utils/mock-server.ts @@ -81,8 +81,7 @@ export async function mockOpenCodeServer(page: Page, config: MockServerConfig) { } if (path === "/global/health") return config.protocol === "v2" ? json(route, {}, undefined, 404) : json(route, { healthy: true }) - if (path === "/api/health" && config.protocol === "v2") - return json(route, { healthy: true, version: "2.0.0", pid: 1 }) + if (path === "/api/health") return json(route, { healthy: true, version: "2.0.0", pid: 1 }) if (path === "/experimental/capabilities") return json(route, { backgroundSubagents: true }) if (path === "/provider") return json(route, providerConfig(config)) if (path === "/provider/auth") return json(route, config.integrationMethods ?? {}) diff --git a/packages/app/e2e/utils/sse-transport.ts b/packages/app/e2e/utils/sse-transport.ts index b0e3b74c6d9..a245082bc71 100644 --- a/packages/app/e2e/utils/sse-transport.ts +++ b/packages/app/e2e/utils/sse-transport.ts @@ -3,7 +3,7 @@ import type { Page } from "@playwright/test" export type SseConnectionRecord = { id: number url: string - path: "/global/event" | "/event" | "/api/event" + path: "/api/event" headers: Record openedAt: number endedAt?: number @@ -174,10 +174,7 @@ export async function installSseTransport( const fetch = (input: RequestInfo | URL, init?: RequestInit) => { const request = new Request(input, init) const url = new URL(request.url) - if ( - url.origin !== server || - (url.pathname !== "/global/event" && url.pathname !== "/event" && url.pathname !== "/api/event") - ) + if (url.origin !== server || url.pathname !== "/api/event") return originalFetch(request) const id = ++nextConnectionID @@ -193,18 +190,9 @@ export async function installSseTransport( record.controller = controller connections.push(record) if (retry !== undefined) controller.enqueue(encoder.encode(`retry: ${retry}\n\n`)) - if (url.pathname === "/api/event") - controller.enqueue( - encoder.encode(frame({ id: `evt_mock_connected_${id}`, type: "server.connected", data: {} })), - ) - if (url.pathname === "/global/event") - controller.enqueue( - encoder.encode( - frame({ - payload: { id: `evt_mock_connected_${id}`, type: "server.connected", properties: {} }, - }), - ), - ) + controller.enqueue( + encoder.encode(frame({ id: `evt_mock_connected_${id}`, type: "server.connected", data: {} })), + ) request.signal.addEventListener( "abort", () => {