mirror of
https://github.com/stablyai/orca.git
synced 2026-10-24 16:03:45 +00:00
test(e2e): harden triaged failures
This commit is contained in:
@@ -69,7 +69,7 @@ function launchLegacyCloseClient(options: {
|
||||
}): {
|
||||
child: ChildProcess
|
||||
ready: Promise<LegacyCloseReport>
|
||||
finish(): void
|
||||
finish(): Promise<void>
|
||||
output(): string
|
||||
} {
|
||||
const { runtime, generations, capableCanaries, legacyCanaries } = options
|
||||
@@ -139,27 +139,80 @@ function launchLegacyCloseClient(options: {
|
||||
return {
|
||||
child,
|
||||
ready,
|
||||
finish: () => {
|
||||
if (child.connected) {
|
||||
child.send?.({ type: 'finish' }, () => {})
|
||||
finish: async () => {
|
||||
if (!child.connected) {
|
||||
throw new Error('Legacy close client IPC channel disconnected before finish handshake')
|
||||
}
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
child.send({ type: 'finish' }, (error) => {
|
||||
if (error) {
|
||||
reject(error)
|
||||
return
|
||||
}
|
||||
resolve()
|
||||
})
|
||||
})
|
||||
},
|
||||
output: () => output
|
||||
}
|
||||
}
|
||||
|
||||
async function terminateLegacyCloseClient(
|
||||
client: ReturnType<typeof launchLegacyCloseClient>,
|
||||
identity: Awaited<ReturnType<typeof recordProcessIdentity>> | null
|
||||
): Promise<void> {
|
||||
if (legacyCloseClientExited(client)) {
|
||||
return
|
||||
}
|
||||
if (identity) {
|
||||
await terminateRecordedTree(await recordProcessTree(identity))
|
||||
return
|
||||
}
|
||||
// Why: identity capture can race with teardown; the direct child handle is the last cleanup path.
|
||||
client.child.kill('SIGKILL')
|
||||
await waitForCondition(
|
||||
'forced legacy close client exit',
|
||||
() => legacyCloseClientExited(client),
|
||||
5_000
|
||||
)
|
||||
}
|
||||
|
||||
function legacyCloseClientExited(client: ReturnType<typeof launchLegacyCloseClient>): boolean {
|
||||
return client.child.exitCode !== null || client.child.signalCode !== null
|
||||
}
|
||||
|
||||
async function finishLegacyCloseClient(
|
||||
client: ReturnType<typeof launchLegacyCloseClient>
|
||||
): Promise<void> {
|
||||
if (!client.child.pid || client.child.exitCode !== null) {
|
||||
if (!client.child.pid || legacyCloseClientExited(client)) {
|
||||
return
|
||||
}
|
||||
const identity = await recordProcessIdentity(client.child.pid)
|
||||
client.finish()
|
||||
const identity = await recordProcessIdentity(client.child.pid).catch(() => null)
|
||||
let finishError: unknown
|
||||
let finishFailed = false
|
||||
try {
|
||||
await waitForCondition('legacy close client exit', () => client.child.exitCode !== null, 2_000)
|
||||
await client.finish()
|
||||
} catch (error) {
|
||||
finishError = error
|
||||
finishFailed = true
|
||||
}
|
||||
try {
|
||||
await waitForCondition('legacy close client exit', () => legacyCloseClientExited(client), 2_000)
|
||||
} catch {
|
||||
await terminateRecordedTree(await recordProcessTree(identity))
|
||||
try {
|
||||
await terminateLegacyCloseClient(client, identity)
|
||||
} catch (cleanupError) {
|
||||
if (finishFailed) {
|
||||
throw new AggregateError(
|
||||
[finishError, cleanupError],
|
||||
'Legacy close client finish handshake and forced cleanup both failed'
|
||||
)
|
||||
}
|
||||
throw cleanupError
|
||||
}
|
||||
}
|
||||
if (finishFailed) {
|
||||
throw finishError
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,13 +19,18 @@ test('new-tab file results prioritize the filename and reveal the full path on h
|
||||
await ensureTerminalVisible(orcaPage)
|
||||
|
||||
const newTab = orcaPage.getByRole('button', { name: 'New tab' })
|
||||
// Keyboard activation avoids the animated tab bar's pointer stability gate in CI.
|
||||
await newTab.press('Space')
|
||||
const searchInput = orcaPage.getByRole('combobox')
|
||||
await searchInput.fill('s')
|
||||
// Not the placeholder/aria-label: that copy is translated and already drifted
|
||||
// once. aria-controls points at the results listbox id, which is structural.
|
||||
const input = orcaPage.locator('input[role="combobox"][aria-controls="tab-create-entry-results"]')
|
||||
// Keyboard activation avoids the animated tab bar's pointer stability gate in CI.
|
||||
await expect(async () => {
|
||||
if ((await newTab.getAttribute('aria-expanded')) !== 'true') {
|
||||
await newTab.press('Space')
|
||||
}
|
||||
await expect(searchInput).toBeVisible({ timeout: 1_000 })
|
||||
}).toPass({ timeout: 10_000 })
|
||||
await searchInput.fill('s')
|
||||
await input.fill('secondaryNav')
|
||||
|
||||
const row = orcaPage.locator('[role="option"]').filter({ hasText: 'Open file' }).first()
|
||||
|
||||
@@ -165,10 +165,8 @@ test.describe('Voice microphone selection', () => {
|
||||
state.dispatchDeviceChange()
|
||||
})
|
||||
|
||||
await prepareVoiceSettings(orcaPage, 'stale-airpods-id', 'AirPods')
|
||||
const refreshedMicrophone = orcaPage.getByRole('combobox', { name: 'Microphone' })
|
||||
await expect(refreshedMicrophone).toHaveText('AirPods')
|
||||
await refreshedMicrophone.press('Space')
|
||||
await expect(microphone).toHaveText('AirPods')
|
||||
await microphone.press('Space')
|
||||
await expect(orcaPage.getByRole('option', { name: 'AirPods' })).toBeVisible()
|
||||
await expect(orcaPage.getByRole('option', { name: 'AirPods (unavailable)' })).toHaveCount(0)
|
||||
await orcaPage.keyboard.press('Escape')
|
||||
|
||||
@@ -4,7 +4,7 @@ import { waitForActiveWorktree, waitForSessionReady } from './helpers/store'
|
||||
import { worktreeRow } from './worktree-row-locators'
|
||||
|
||||
const MAX_CLICK_TASK_DURATION_MS = 32
|
||||
const MAX_CLICK_BACK_TIMER_DRIFT_MS = 32
|
||||
const MAX_CLICK_BACK_TIMER_DRIFT_MS = 50
|
||||
|
||||
async function prepareSidebarForSwitchTest(page: Page): Promise<[string, string]> {
|
||||
return page.evaluate(async () => {
|
||||
|
||||
Reference in New Issue
Block a user