mirror of
https://github.com/stablyai/orca.git
synced 2026-09-25 16:02:38 +00:00
162 lines
6.0 KiB
TypeScript
162 lines
6.0 KiB
TypeScript
import { existsSync, readFileSync } from 'node:fs'
|
|
import path from 'node:path'
|
|
import type { Page, TestInfo } from '@playwright/test'
|
|
import { test, expect } from './helpers/orca-app'
|
|
import {
|
|
execInTerminal,
|
|
sendToTerminal,
|
|
waitForActivePanePtyId,
|
|
waitForActiveTerminalManager,
|
|
waitForPaneCount,
|
|
waitForTerminalOutput
|
|
} from './helpers/terminal'
|
|
import { ensureTerminalVisible, waitForActiveWorktree, waitForSessionReady } from './helpers/store'
|
|
|
|
const FIXTURE_PATH = path.join(
|
|
process.cwd(),
|
|
'tests/e2e/fixtures/terminal-link-mouse-owner-fixture.cjs'
|
|
)
|
|
const LINK = 'https://example.com/sta-3888'
|
|
const OSC_LINK_TEXT = 'STA_3888_OSC_LINK'
|
|
|
|
type LinkTarget = { x: number; y: number; mouseTrackingMode: string }
|
|
type LinkMode = 'http' | 'osc'
|
|
|
|
async function startMouseAwareLinkFixture(
|
|
orcaPage: Page,
|
|
testInfo: TestInfo,
|
|
linkMode: LinkMode = 'http'
|
|
): Promise<{ mouseLogPath: string; ptyId: string; target: LinkTarget }> {
|
|
await waitForSessionReady(orcaPage)
|
|
await waitForActiveWorktree(orcaPage)
|
|
await ensureTerminalVisible(orcaPage)
|
|
await waitForActiveTerminalManager(orcaPage)
|
|
await waitForPaneCount(orcaPage, 1)
|
|
|
|
const ptyId = await waitForActivePanePtyId(orcaPage)
|
|
const mouseLogPath = testInfo.outputPath('child-mouse-reports.log')
|
|
await execInTerminal(
|
|
orcaPage,
|
|
ptyId,
|
|
`node ${JSON.stringify(FIXTURE_PATH)} ${JSON.stringify(mouseLogPath)} ${linkMode}`
|
|
)
|
|
const renderedLinkText = linkMode === 'osc' ? OSC_LINK_TEXT : LINK
|
|
await waitForTerminalOutput(orcaPage, 'LINK_MOUSE_READY')
|
|
|
|
const target = await orcaPage.evaluate((linkText) => {
|
|
const state = window.__store?.getState()
|
|
const worktreeId = state?.activeWorktreeId
|
|
const tabId = worktreeId ? state?.activeTabIdByWorktree?.[worktreeId] : null
|
|
const manager = tabId ? window.__paneManagers?.get(tabId) : null
|
|
const pane = manager?.getActivePane?.() ?? manager?.getPanes?.()[0] ?? null
|
|
const screen = pane?.terminal.element?.querySelector<HTMLElement>('.xterm-screen') ?? null
|
|
if (!pane || !screen) {
|
|
throw new Error('Active terminal screen unavailable')
|
|
}
|
|
|
|
const buffer = pane.terminal.buffer.active
|
|
for (let viewportRow = 0; viewportRow < pane.terminal.rows; viewportRow += 1) {
|
|
const text = buffer.getLine(buffer.viewportY + viewportRow)?.translateToString(false)
|
|
const column = text?.indexOf(linkText) ?? -1
|
|
if (column < 0) {
|
|
continue
|
|
}
|
|
const rect = screen.getBoundingClientRect()
|
|
const cell = pane.terminal.dimensions?.css.cell
|
|
if (!cell?.width || !cell.height) {
|
|
throw new Error('Active terminal cell dimensions unavailable')
|
|
}
|
|
return {
|
|
x: rect.left + (column + linkText.length / 2) * cell.width,
|
|
y: rect.top + (viewportRow + 0.5) * cell.height,
|
|
mouseTrackingMode: pane.terminal.modes.mouseTrackingMode
|
|
}
|
|
}
|
|
throw new Error('Rendered fixture link unavailable')
|
|
}, renderedLinkText)
|
|
|
|
expect(target.mouseTrackingMode).not.toBe('none')
|
|
return { mouseLogPath, ptyId, target }
|
|
}
|
|
|
|
function childMouseReportCount(mouseLogPath: string): number {
|
|
if (!existsSync(mouseLogPath)) {
|
|
return 0
|
|
}
|
|
return readFileSync(mouseLogPath, 'utf8').trim().split(/\s+/).filter(Boolean).length
|
|
}
|
|
|
|
async function expectChildMouseReports(mouseLogPath: string): Promise<void> {
|
|
await expect
|
|
.poll(() => childMouseReportCount(mouseLogPath), { timeout: 5_000 })
|
|
.toBeGreaterThan(0)
|
|
}
|
|
|
|
async function expectOrcaOwnedMouseOutcome(mouseLogPath: string): Promise<void> {
|
|
await new Promise<void>((resolve) => setTimeout(resolve, 1_000))
|
|
expect(childMouseReportCount(mouseLogPath)).toBe(0)
|
|
}
|
|
|
|
test.describe('terminal link click ownership', () => {
|
|
test('an Orca-owned plain link click emits no child PTY mouse frames', async ({
|
|
orcaPage
|
|
}, testInfo) => {
|
|
const { mouseLogPath, ptyId, target } = await startMouseAwareLinkFixture(orcaPage, testInfo)
|
|
await orcaPage.mouse.click(target.x, target.y)
|
|
|
|
await expect(orcaPage.locator('[data-terminal-link-action-popover]')).toBeVisible()
|
|
await expect(orcaPage.locator('[data-terminal-link-destination]')).toHaveText(LINK)
|
|
|
|
await expectOrcaOwnedMouseOutcome(mouseLogPath)
|
|
|
|
await sendToTerminal(orcaPage, ptyId, 'q')
|
|
})
|
|
|
|
test('an Orca-owned OSC link click emits no child PTY mouse frames', async ({
|
|
orcaPage
|
|
}, testInfo) => {
|
|
const { mouseLogPath, ptyId, target } = await startMouseAwareLinkFixture(
|
|
orcaPage,
|
|
testInfo,
|
|
'osc'
|
|
)
|
|
await orcaPage.mouse.move(target.x, target.y)
|
|
await expect(orcaPage.locator('.xterm-hover')).toHaveCount(1)
|
|
await orcaPage.mouse.click(target.x, target.y)
|
|
|
|
await expect(orcaPage.locator('[data-terminal-link-action-popover]')).toBeVisible()
|
|
await expect(orcaPage.locator('[data-terminal-link-destination]')).toHaveText(LINK)
|
|
await expectOrcaOwnedMouseOutcome(mouseLogPath)
|
|
|
|
await sendToTerminal(orcaPage, ptyId, 'q')
|
|
})
|
|
|
|
test('a plain click stays child-owned when link actions are disabled', async ({
|
|
orcaPage
|
|
}, testInfo) => {
|
|
const { mouseLogPath, ptyId, target } = await startMouseAwareLinkFixture(orcaPage, testInfo)
|
|
await orcaPage.evaluate(async () => {
|
|
await window.__store?.getState().updateSettings({ terminalLinkActionPopoverEnabled: false })
|
|
})
|
|
|
|
await orcaPage.mouse.click(target.x, target.y)
|
|
|
|
await expect(orcaPage.locator('[data-terminal-link-action-popover]')).toHaveCount(0)
|
|
await expectChildMouseReports(mouseLogPath)
|
|
await sendToTerminal(orcaPage, ptyId, 'q')
|
|
})
|
|
|
|
test('a drag across a link stays child-owned', async ({ orcaPage }, testInfo) => {
|
|
const { mouseLogPath, ptyId, target } = await startMouseAwareLinkFixture(orcaPage, testInfo)
|
|
|
|
await orcaPage.mouse.move(target.x, target.y)
|
|
await orcaPage.mouse.down()
|
|
await orcaPage.mouse.move(target.x + 12, target.y + 12, { steps: 3 })
|
|
await orcaPage.mouse.up()
|
|
|
|
await expect(orcaPage.locator('[data-terminal-link-action-popover]')).toHaveCount(0)
|
|
await expectChildMouseReports(mouseLogPath)
|
|
await sendToTerminal(orcaPage, ptyId, 'q')
|
|
})
|
|
})
|