From 328c6de80d51704c09bdd962df2ddf5b9d7c82ea Mon Sep 17 00:00:00 2001 From: Luke Parker <10430890+Hona@users.noreply.github.com> Date: Thu, 12 Mar 2026 17:35:26 +1000 Subject: Fix terminal e2e flakiness with a real terminal driver (#17144) --- packages/app/e2e/actions.ts | 53 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) (limited to 'packages/app/e2e/actions.ts') diff --git a/packages/app/e2e/actions.ts b/packages/app/e2e/actions.ts index 86147dc65..f721e0a7c 100644 --- a/packages/app/e2e/actions.ts +++ b/packages/app/e2e/actions.ts @@ -3,6 +3,7 @@ import fs from "node:fs/promises" import os from "node:os" import path from "node:path" import { execSync } from "node:child_process" +import { terminalAttr, type E2EWindow } from "../src/testing/terminal" import { createSdk, modKey, resolveDirectory, serverUrl } from "./utils" import { dropdownMenuTriggerSelector, @@ -15,6 +16,7 @@ import { listItemSelector, listItemKeySelector, listItemKeyStartsWithSelector, + terminalSelector, workspaceItemSelector, workspaceMenuTriggerSelector, } from "./selectors" @@ -28,6 +30,57 @@ export async function defocus(page: Page) { .catch(() => undefined) } +async function terminalID(term: Locator) { + const id = await term.getAttribute(terminalAttr) + if (id) return id + throw new Error(`Active terminal missing ${terminalAttr}`) +} + +async function terminalReady(page: Page, term?: Locator) { + const next = term ?? page.locator(terminalSelector).first() + const id = await terminalID(next) + return page.evaluate((id) => { + const state = (window as E2EWindow).__opencode_e2e?.terminal?.terminals?.[id] + return !!state?.connected && (state.settled ?? 0) > 0 + }, id) +} + +async function terminalHas(page: Page, input: { term?: Locator; token: string }) { + const next = input.term ?? page.locator(terminalSelector).first() + const id = await terminalID(next) + return page.evaluate( + (input) => { + const state = (window as E2EWindow).__opencode_e2e?.terminal?.terminals?.[input.id] + return state?.rendered.includes(input.token) ?? false + }, + { id, token: input.token }, + ) +} + +export async function waitTerminalReady(page: Page, input?: { term?: Locator; timeout?: number }) { + const term = input?.term ?? page.locator(terminalSelector).first() + const timeout = input?.timeout ?? 10_000 + await expect(term).toBeVisible() + await expect(term.locator("textarea")).toHaveCount(1) + await expect + .poll(() => terminalReady(page, term), { timeout }) + .toBe(true) +} + +export async function runTerminal(page: Page, input: { cmd: string; token: string; term?: Locator; timeout?: number }) { + const term = input.term ?? page.locator(terminalSelector).first() + const timeout = input.timeout ?? 10_000 + await waitTerminalReady(page, { term, timeout }) + const textarea = term.locator("textarea") + await term.click() + await expect(textarea).toBeFocused() + await page.keyboard.type(input.cmd) + await page.keyboard.press("Enter") + await expect + .poll(() => terminalHas(page, { term, token: input.token }), { timeout }) + .toBe(true) +} + export async function openPalette(page: Page) { await defocus(page) await page.keyboard.press(`${modKey}+P`) -- cgit v1.2.3