orca/tests/e2e/terminal-typing-latency.spe...

137 lines
4.6 KiB
TypeScript

import type { Page } from '@stablyai/playwright-test'
import { randomUUID } from 'node:crypto'
import { rmSync, writeFileSync } from 'node:fs'
import path from 'node:path'
import { test, expect } from './helpers/orca-app'
import {
getTerminalContent,
waitForActivePanePtyId,
waitForActiveTerminalManager,
waitForTerminalOutput,
sendToTerminal
} from './helpers/terminal'
import { ensureTerminalVisible, waitForActiveWorktree, waitForSessionReady } from './helpers/store'
const KEY_LATENCY_SAMPLES = 'abcdefghijklmnop'
const MAX_MEDIAN_KEY_LATENCY_MS = 250
const MAX_WORST_KEY_LATENCY_MS = 1_000
async function focusActiveTerminalInput(page: Page): Promise<void> {
await page.evaluate(() => {
const state = window.__store?.getState()
const worktreeId = state?.activeWorktreeId
const tabId =
state?.activeTabType === 'terminal'
? state.activeTabId
: worktreeId
? (state?.activeTabIdByWorktree?.[worktreeId] ?? null)
: null
const manager = tabId ? window.__paneManagers?.get(tabId) : null
const pane = manager?.getActivePane?.() ?? manager?.getPanes?.()[0] ?? null
if (!pane) {
throw new Error('No active terminal pane to focus')
}
pane.terminal.focus()
const textarea = pane.container.querySelector(
'.xterm-helper-textarea'
) as HTMLTextAreaElement | null
if (!textarea) {
throw new Error('Active terminal has no xterm helper textarea')
}
textarea.focus()
})
}
function interactivePromptScript(runId: string): string {
return `
process.stdin.setEncoding('utf8')
if (process.stdin.isTTY) process.stdin.setRawMode(true)
process.stdin.resume()
let seq = 0
const interrupt = String.fromCharCode(3)
process.stdout.write('\\x1b]0;Terminal typing benchmark\\x07')
process.stdout.write('TYPING_READY_${runId}\\n')
process.stdin.on('data', (chunk) => {
if (chunk.includes(interrupt)) {
process.exit(0)
}
for (const char of chunk) {
if (char === '\\r' || char === '\\n') continue
seq += 1
process.stdout.write('\\r\\x1b[2KInteractive prompt ' + seq + ': ' + char + ' TYPING_KEY_${runId}_' + seq + '\\n')
}
})
`
}
async function waitForMarkerLatency(
page: Page,
marker: string,
timeoutMs: number
): Promise<number> {
const start = performance.now()
while (performance.now() - start < timeoutMs) {
if ((await getTerminalContent(page, 12_000)).includes(marker)) {
return performance.now() - start
}
await page.waitForTimeout(5)
}
throw new Error(`Timed out waiting for terminal marker ${marker}`)
}
function median(values: number[]): number {
const sorted = [...values].sort((a, b) => a - b)
return sorted[Math.floor(sorted.length / 2)] ?? 0
}
test.describe('Terminal typing latency', () => {
test('interactive prompt echoes typed keys without visible lag', async ({
orcaPage,
testRepoPath
}, testInfo) => {
await waitForSessionReady(orcaPage)
await waitForActiveWorktree(orcaPage)
await ensureTerminalVisible(orcaPage)
await waitForActiveTerminalManager(orcaPage, 30_000)
const ptyId = await waitForActivePanePtyId(orcaPage)
const runId = randomUUID()
const scriptPath = path.join(testRepoPath, `.orca-typing-benchmark-${runId}.mjs`)
writeFileSync(scriptPath, interactivePromptScript(runId))
let commandSent = false
try {
await sendToTerminal(orcaPage, ptyId, `node ${JSON.stringify(scriptPath)}\r`)
commandSent = true
await waitForTerminalOutput(orcaPage, `TYPING_READY_${runId}`, 10_000)
await focusActiveTerminalInput(orcaPage)
const latencies: number[] = []
for (const [index, char] of [...KEY_LATENCY_SAMPLES].entries()) {
const seq = index + 1
const marker = `TYPING_KEY_${runId}_${seq}`
const start = performance.now()
await orcaPage.keyboard.type(char)
await waitForMarkerLatency(orcaPage, marker, MAX_WORST_KEY_LATENCY_MS)
latencies.push(performance.now() - start)
}
const medianLatency = median(latencies)
const worstLatency = Math.max(...latencies)
testInfo.annotations.push({
type: 'terminal-typing-latency',
description: `median=${medianLatency.toFixed(1)}ms worst=${worstLatency.toFixed(1)}ms samples=${latencies
.map((value) => value.toFixed(1))
.join(',')}`
})
expect(medianLatency).toBeLessThan(MAX_MEDIAN_KEY_LATENCY_MS)
expect(worstLatency).toBeLessThan(MAX_WORST_KEY_LATENCY_MS)
} finally {
if (commandSent) {
await sendToTerminal(orcaPage, ptyId, '\x03').catch(() => undefined)
}
rmSync(scriptPath, { force: true })
}
})
})