mirror of
https://github.com/stablyai/orca.git
synced 2026-09-23 08:02:31 +00:00
* Revert "test(ime): restore coverage the composition-ownership change removed (#13168)" This reverts commit25a8c517e1. * Revert "refactor(terminal): return IME composition ownership to xterm (#13128)" This reverts commit17b3dff3c4. * test(ime): keep the architecture-neutral Korean trace coverage The recorded IBus/fcitx5 and Windows MS-Korean traces from #13168 assert PTY byte order, not composition ownership, so they still hold once the terminal composition layer is restored. The mobile accessory-order test pinned the new handleLiveInputChange signature and does not. Co-authored-by: Orca <help@stably.ai> * fix(terminal): keep the macOS Backslash bypass through the revert The restored native-text forwarder only claims keys for input sources in its hardcoded CJK allowlist, so third-party IMEs off that list (Qingg, #10896) still get a raw backslash. #13128 added this bypass as a partial replacement; keep it rather than trade the open issue back. Scoped to the bare backslash key. The rest of shouldBypassXtermForMacNativeText bypassed all unmodified non-ASCII text, which would race the restored forwarder. Co-authored-by: Orca <help@stably.ai> * fix(mobile): move the mirror-step ref write out of render The restored hook assigned runMirrorStepRef during render, which is not replay-safe — React can discard render work, so the mutation can leak from UI that never commits. Its only read is inside the held-commit timer, which fires long after commit, and the ref has a safe default, so an effect is soon enough. Surfaced by the changed-lines React Doctor gate: the rule postdates this code, so restoring the file re-introduced it as a new violation. Co-authored-by: Orca <help@stably.ai> --------- Co-authored-by: Orca <help@stably.ai>
116 lines
4.7 KiB
TypeScript
116 lines
4.7 KiB
TypeScript
import type { TestInfo } from '@stablyai/playwright-test'
|
||
import { test, expect } from './helpers/orca-app'
|
||
import { waitForActiveWorktree, waitForSessionReady, getActiveTabId } from './helpers/store'
|
||
import {
|
||
getTerminalContent,
|
||
sendToTerminal,
|
||
waitForActivePanePtyId,
|
||
waitForActiveTerminalManager,
|
||
waitForPaneIdentitySnapshot
|
||
} from './helpers/terminal'
|
||
import { parkHiddenTabBehindDecoy } from './helpers/terminal-hidden-parking'
|
||
import {
|
||
cleanupDockerSshRelayTarget,
|
||
startDockerSshRelayTarget,
|
||
type DockerSshRelayTarget
|
||
} from './helpers/docker-ssh-relay-target'
|
||
import { connectDockerSshRelayTarget } from './helpers/docker-ssh-relay-connection'
|
||
|
||
const RUN_DOCKER_SSH = process.env.ORCA_E2E_SSH_DOCKER === '1'
|
||
const PARKING_DELAY_MS = Number(process.env.ORCA_E2E_TERMINAL_PARKING_DELAY_MS) || 500
|
||
|
||
test.use({
|
||
seedTestRepo: false,
|
||
orcaAppExtraEnv: { ORCA_E2E_TERMINAL_PARKING_DELAY_MS: String(PARKING_DELAY_MS) }
|
||
})
|
||
|
||
// C1 slice A: SSH tabs park like local ones and reveal restores content from
|
||
// main's headless model (relay replay is the fallback). This is the SSH
|
||
// park+reveal round-trip fidelity check the design gate required.
|
||
test.describe('SSH terminal hidden view parking', () => {
|
||
test.skip(!RUN_DOCKER_SSH, 'Set ORCA_E2E_SSH_DOCKER=1 to run Docker-backed SSH tests.')
|
||
test.skip(process.platform === 'win32', 'Docker SSH parking uses POSIX SSH tooling.')
|
||
|
||
test('parks a hidden SSH tab and restores its scrollback on reveal', async ({
|
||
orcaPage
|
||
}, testInfo: TestInfo) => {
|
||
test.setTimeout(240_000)
|
||
let target: DockerSshRelayTarget | null = null
|
||
try {
|
||
target = startDockerSshRelayTarget(testInfo)
|
||
await waitForSessionReady(orcaPage)
|
||
const remote = await connectDockerSshRelayTarget(orcaPage, target)
|
||
await expect
|
||
.poll(() => waitForActiveWorktree(orcaPage), { timeout: 30_000 })
|
||
.toBe(remote.worktreeId)
|
||
await waitForActiveTerminalManager(orcaPage, 60_000)
|
||
const sshPtyId = await waitForActivePanePtyId(orcaPage, 60_000)
|
||
const sshTabId = await getActiveTabId(orcaPage)
|
||
if (!sshTabId) {
|
||
throw new Error('SSH terminal tab did not become active')
|
||
}
|
||
const snapshot = await waitForPaneIdentitySnapshot(orcaPage, 1)
|
||
expect(snapshot.panes[0]?.ptyId).toBe(sshPtyId)
|
||
|
||
// Why the ':' terminator: `${marker}_1:` must not substring-match _10/_100.
|
||
const marker = `SSH_PARK_MARKER_${Date.now()}`
|
||
await sendToTerminal(
|
||
orcaPage,
|
||
sshPtyId,
|
||
`for i in $(seq 1 200); do echo "${marker}_$i:"; done\r`
|
||
)
|
||
await expect
|
||
.poll(() => getTerminalContent(orcaPage, 20_000), {
|
||
timeout: 30_000,
|
||
message: 'SSH marker output did not render before parking'
|
||
})
|
||
.toContain(`${marker}_200:`)
|
||
// Why the pad: ~3000 × ~60B ≈ 180KB pushes the early markers past the
|
||
// relay's 100KiB rolling replay buffer while staying inside main's
|
||
// ~5k-row headless model — so a revealed `${marker}_1:` can only have
|
||
// come from the model paint, never the relay fallback.
|
||
await sendToTerminal(
|
||
orcaPage,
|
||
sshPtyId,
|
||
`for i in $(seq 1 3000); do echo "PAD_$i:0123456789012345678901234567890123456789"; done; echo "${marker}_PAD_DONE:"\r`
|
||
)
|
||
await expect
|
||
.poll(() => getTerminalContent(orcaPage, 20_000), {
|
||
timeout: 60_000,
|
||
message: 'SSH pad output did not finish before parking'
|
||
})
|
||
.toContain(`${marker}_PAD_DONE:`)
|
||
|
||
await parkHiddenTabBehindDecoy(orcaPage, remote.worktreeId, sshTabId, {
|
||
parkDelayMs: PARKING_DELAY_MS
|
||
})
|
||
|
||
// Reveal: reattach must paint from main's headless model (or relay
|
||
// replay when the model is unavailable) — never a blank pane.
|
||
await orcaPage.evaluate((tabId) => {
|
||
const state = window.__store?.getState()
|
||
state?.setActiveTab(tabId)
|
||
state?.setActiveTabType('terminal')
|
||
}, sshTabId)
|
||
await waitForActiveTerminalManager(orcaPage, 60_000)
|
||
await expect
|
||
.poll(() => getTerminalContent(orcaPage, 20_000), {
|
||
timeout: 60_000,
|
||
message: 'revealed SSH tab did not restore the final pad line'
|
||
})
|
||
.toContain(`${marker}_PAD_DONE:`)
|
||
// Depth proof: `${marker}_1:` predates >100KiB of later output, so its
|
||
// presence after reveal proves the headless-model paint restored
|
||
// scrollback the relay replay cannot hold.
|
||
await expect
|
||
.poll(() => getTerminalContent(orcaPage, 2_000_000), {
|
||
timeout: 15_000,
|
||
message: 'revealed SSH tab lost the pre-pad scrollback only the model paint restores'
|
||
})
|
||
.toContain(`${marker}_1:`)
|
||
} finally {
|
||
cleanupDockerSshRelayTarget(target)
|
||
}
|
||
})
|
||
})
|