diff options
| author | Adam <[email protected]> | 2026-03-13 08:56:48 -0500 |
|---|---|---|
| committer | GitHub <[email protected]> | 2026-03-13 08:56:48 -0500 |
| commit | c9e9dbeee1ceb5af3d1b1ce292317390286fe7a0 (patch) | |
| tree | e679df64078db58eadb3cba2d1ac65b170175360 /packages/app/e2e | |
| parent | b88b323049ed7ad7bf41fbec6ce66247ec4047ab (diff) | |
| download | opencode-c9e9dbeee1ceb5af3d1b1ce292317390286fe7a0.tar.gz opencode-c9e9dbeee1ceb5af3d1b1ce292317390286fe7a0.zip | |
fix(app): terminal cloning without retry (#17354)
Diffstat (limited to 'packages/app/e2e')
| -rw-r--r-- | packages/app/e2e/actions.ts | 31 | ||||
| -rw-r--r-- | packages/app/e2e/terminal/terminal-reconnect.spec.ts | 46 | ||||
| -rw-r--r-- | packages/app/e2e/tsconfig.json | 3 |
3 files changed, 75 insertions, 5 deletions
diff --git a/packages/app/e2e/actions.ts b/packages/app/e2e/actions.ts index a56001248..8e21579e2 100644 --- a/packages/app/e2e/actions.ts +++ b/packages/app/e2e/actions.ts @@ -36,6 +36,22 @@ async function terminalID(term: Locator) { throw new Error(`Active terminal missing ${terminalAttr}`) } +export async function terminalConnects(page: Page, input?: { term?: Locator }) { + const term = input?.term ?? page.locator(terminalSelector).first() + const id = await terminalID(term) + return page.evaluate((id) => { + return (window as E2EWindow).__opencode_e2e?.terminal?.terminals?.[id]?.connects ?? 0 + }, id) +} + +export async function disconnectTerminal(page: Page, input?: { term?: Locator }) { + const term = input?.term ?? page.locator(terminalSelector).first() + const id = await terminalID(term) + await page.evaluate((id) => { + ;(window as E2EWindow).__opencode_e2e?.terminal?.controls?.[id]?.disconnect?.() + }, id) +} + async function terminalReady(page: Page, term?: Locator) { const next = term ?? page.locator(terminalSelector).first() const id = await terminalID(next) @@ -588,12 +604,19 @@ export async function seedSessionTask( .flatMap((message) => message.parts) .find((part) => { if (part.type !== "tool" || part.tool !== "task") return false - if (part.state.input?.description !== input.description) return false - return typeof part.state.metadata?.sessionId === "string" && part.state.metadata.sessionId.length > 0 + if (!("state" in part) || !part.state || typeof part.state !== "object") return false + if (!("input" in part.state) || !part.state.input || typeof part.state.input !== "object") return false + if (!("description" in part.state.input) || part.state.input.description !== input.description) return false + if (!("metadata" in part.state) || !part.state.metadata || typeof part.state.metadata !== "object") + return false + if (!("sessionId" in part.state.metadata)) return false + return typeof part.state.metadata.sessionId === "string" && part.state.metadata.sessionId.length > 0 }) - if (!part) return - const id = part.state.metadata?.sessionId + if (!part || !("state" in part) || !part.state || typeof part.state !== "object") return + if (!("metadata" in part.state) || !part.state.metadata || typeof part.state.metadata !== "object") return + if (!("sessionId" in part.state.metadata)) return + const id = part.state.metadata.sessionId if (typeof id !== "string" || !id) return const child = await sdk.session .get({ sessionID: id }) diff --git a/packages/app/e2e/terminal/terminal-reconnect.spec.ts b/packages/app/e2e/terminal/terminal-reconnect.spec.ts new file mode 100644 index 000000000..b03ed8956 --- /dev/null +++ b/packages/app/e2e/terminal/terminal-reconnect.spec.ts @@ -0,0 +1,46 @@ +import type { Page } from "@playwright/test" +import { disconnectTerminal, runTerminal, terminalConnects, waitTerminalReady } from "../actions" +import { test, expect } from "../fixtures" +import { terminalSelector } from "../selectors" +import { terminalToggleKey } from "../utils" + +async function open(page: Page) { + const term = page.locator(terminalSelector).first() + const visible = await term.isVisible().catch(() => false) + if (!visible) await page.keyboard.press(terminalToggleKey) + await waitTerminalReady(page, { term }) + return term +} + +test("terminal reconnects without replacing the pty", async ({ page, withProject }) => { + await withProject(async ({ gotoSession }) => { + const name = `OPENCODE_E2E_RECONNECT_${Date.now()}` + const token = `E2E_RECONNECT_${Date.now()}` + + await gotoSession() + + const term = await open(page) + const id = await term.getAttribute("data-pty-id") + if (!id) throw new Error("Active terminal missing data-pty-id") + + const prev = await terminalConnects(page, { term }) + + await runTerminal(page, { + term, + cmd: `export ${name}=${token}; echo ${token}`, + token, + }) + + await disconnectTerminal(page, { term }) + + await expect.poll(() => terminalConnects(page, { term }), { timeout: 15_000 }).toBeGreaterThan(prev) + await expect.poll(() => term.getAttribute("data-pty-id"), { timeout: 5_000 }).toBe(id) + + await runTerminal(page, { + term, + cmd: `echo $${name}`, + token, + timeout: 15_000, + }) + }) +}) diff --git a/packages/app/e2e/tsconfig.json b/packages/app/e2e/tsconfig.json index 18e88ddc9..a86352b83 100644 --- a/packages/app/e2e/tsconfig.json +++ b/packages/app/e2e/tsconfig.json @@ -2,7 +2,8 @@ "extends": "../tsconfig.json", "compilerOptions": { "noEmit": true, + "rootDir": "..", "types": ["node", "bun"] }, - "include": ["./**/*.ts"] + "include": ["./**/*.ts", "../src/testing/terminal.ts"] } |
