From f624d4c2a5838f80e88dd1e441997f137b2f8e71 Mon Sep 17 00:00:00 2001 From: Jinjing <6427696+AmethystLiang@users.noreply.github.com> Date: Thu, 30 Jul 2026 11:35:34 -0700 Subject: [PATCH] test(e2e): fix flaky PR check failures from recent main drift - Seed Jira composer e2e with taskSourceContext required since #11296 - Assert new-tab outcome instead of Radix menu teardown timing - Use durable WIDTH marker after shell prompt redraw clobbers RESTORE - Re-probe discoverActivePtyId when ConPTY drops the first write --- tests/e2e/helpers/terminal.ts | 35 ++++++++++++------- ...rkspace-linked-item-project-switch.spec.ts | 3 ++ tests/e2e/tabs.spec.ts | 7 ++-- ...terminal-long-table-scroll-restore.spec.ts | 11 +++--- 4 files changed, 36 insertions(+), 20 deletions(-) diff --git a/tests/e2e/helpers/terminal.ts b/tests/e2e/helpers/terminal.ts index ec55b702c39..0d09aa01c5a 100644 --- a/tests/e2e/helpers/terminal.ts +++ b/tests/e2e/helpers/terminal.ts @@ -262,20 +262,25 @@ export async function discoverActivePtyId(page: Page): Promise { buildFreshShellProbeInputSequence(`echo ${marker}_${index}\r`) ) - await page.evaluate( - ({ candidateIds, candidateInputs }) => { - // Why: daemon PTY IDs can contain path separators and shell metacharacters. - // Echo a numeric probe index, then map it back to the opaque ID in Node. - for (const [index, id] of candidateIds.entries()) { - for (const input of candidateInputs[index] ?? []) { - window.api.pty.write(String(id), input) + const writeProbes = async (): Promise => { + await page.evaluate( + ({ candidateIds, candidateInputs }) => { + // Why: daemon PTY IDs can contain path separators and shell metacharacters. + // Echo a numeric probe index, then map it back to the opaque ID in Node. + for (const [index, id] of candidateIds.entries()) { + for (const input of candidateInputs[index] ?? []) { + window.api.pty.write(String(id), input) + } } - } - }, - { candidateIds, candidateInputs } - ) + }, + { candidateIds, candidateInputs } + ) + } + + await writeProbes() let foundPtyId: string | null = null + let probeAttempts = 0 await expect .poll( async () => { @@ -287,9 +292,15 @@ export async function discoverActivePtyId(page: Page): Promise { foundPtyId = Number.isInteger(index) ? (candidateIds[index] ?? null) : null return true } + // Why: Windows ConPTY can drop the first write before the shell is ready. + // Re-probe every few polls instead of failing after a single lost write. + probeAttempts += 1 + if (probeAttempts % 4 === 0) { + await writeProbes() + } return false }, - { timeout: 10_000, message: 'PTY marker did not appear in terminal buffer' } + { timeout: 15_000, message: 'PTY marker did not appear in terminal buffer' } ) .toBe(true) diff --git a/tests/e2e/new-workspace-linked-item-project-switch.spec.ts b/tests/e2e/new-workspace-linked-item-project-switch.spec.ts index 9970f3696f0..047fcf85313 100644 --- a/tests/e2e/new-workspace-linked-item-project-switch.spec.ts +++ b/tests/e2e/new-workspace-linked-item-project-switch.spec.ts @@ -73,6 +73,9 @@ async function openComposerWithLinkedWorkItem( ) } +// Why: useComposerState drops Jira seeds that lack a matching taskSourceContext +// (site + project identity). Derive host/project from the active setup so the +// seed mirrors real onSmartJiraIssueSelect metadata. async function getJiraSourceContext(page: Page): Promise { return page.evaluate(() => { const state = window.__store?.getState() diff --git a/tests/e2e/tabs.spec.ts b/tests/e2e/tabs.spec.ts index 369d11ac81c..3bba9c7233d 100644 --- a/tests/e2e/tabs.spec.ts +++ b/tests/e2e/tabs.spec.ts @@ -91,11 +91,12 @@ test.describe('Tabs', () => { // Why: the "+" dropdown uses Radix , which exposes the // label text as the accessible name once the menu is open. const newTerminalMenuItem = orcaPage.getByRole('menuitem', { name: /New Terminal/i }).first() + await expect(newTerminalMenuItem).toBeVisible() await newTerminalMenuItem.click({ force: true }) - await expect(newTerminalMenuItem).toBeHidden({ timeout: 3_000 }) - // Final assertion is on the rendered tab count — the tab bar itself must - // gain an element, not just the store. + // Why: assert the product outcome (new tab) rather than menu teardown. + // Radix can leave the menuitem in the a11y tree briefly after a forced + // click even when the terminal tab was already created. await expect .poll(() => countRenderedTabs(orcaPage), { timeout: 5_000, diff --git a/tests/e2e/terminal-long-table-scroll-restore.spec.ts b/tests/e2e/terminal-long-table-scroll-restore.spec.ts index 6452207980f..a032a099ff7 100644 --- a/tests/e2e/terminal-long-table-scroll-restore.spec.ts +++ b/tests/e2e/terminal-long-table-scroll-restore.spec.ts @@ -503,7 +503,10 @@ test.describe('Terminal long table scroll restore repro', () => { const ptyId = await waitForActivePanePtyId(orcaPage) await waitForPtyColumnsAtMost(orcaPage, ptyId, renderedTableTerminalCols) const runId = randomUUID() - const marker = `EMOJI_FIXTURE_TABLE_RESTORE_${runId}` + // Why: the trailing RESTORE line is often clobbered by shell prompt redraw + // (\x1b[1A). The WIDTH line is one line earlier and is the durable survival + // signal for this fixture; the right-edge assertions below use it too. + const widthMarker = emojiFixtureTableWidthMarker(runId) const scriptPath = path.join(testRepoPath, `.orca-emoji-fixture-table-${runId}.mjs`) writeFileSync(scriptPath, emojiFixtureMarkdownTableScript(EMOJI_TABLE_FIXTURE, runId)) @@ -525,11 +528,9 @@ test.describe('Terminal long table scroll restore repro', () => { timeout: 10_000, message: 'real emoji table marker did not survive workspace switch' }) - .toContain(marker) + .toContain(widthMarker) const generatedWidthContent = await getTerminalContent(orcaPage, 30_000) - const generatedWidthMatch = generatedWidthContent.match( - new RegExp(`${emojiFixtureTableWidthMarker(runId)}(\\d+)`) - ) + const generatedWidthMatch = generatedWidthContent.match(new RegExp(`${widthMarker}(\\d+)`)) expect(generatedWidthMatch).not.toBeNull() const generatedTableWidth = Number(generatedWidthMatch?.[1] ?? 0)