From ffbf35e0d2b6b3be276f9671c644f05fb9f0bf27 Mon Sep 17 00:00:00 2001 From: Neil <4138956+nwparker@users.noreply.github.com> Date: Sun, 6 Sep 2026 01:09:18 -0700 Subject: [PATCH 01/22] fix(source-control): stack Retry below the too-many-changes message (#19037) --- .../listing/too-many-changes-banner.tsx | 31 ++++++++++--------- .../source-control-large-file-count.spec.ts | 16 +++++++--- 2 files changed, 28 insertions(+), 19 deletions(-) diff --git a/src/renderer/src/components/right-sidebar/source-control/listing/too-many-changes-banner.tsx b/src/renderer/src/components/right-sidebar/source-control/listing/too-many-changes-banner.tsx index 3da7b3e58f1..468f919ce6b 100644 --- a/src/renderer/src/components/right-sidebar/source-control/listing/too-many-changes-banner.tsx +++ b/src/renderer/src/components/right-sidebar/source-control/listing/too-many-changes-banner.tsx @@ -68,9 +68,12 @@ export function TooManyChangesBanner({ } return ( -
-
- +
+
+ {translate( 'auto.components.right.sidebar.SourceControl.tooManyChanges', @@ -78,18 +81,18 @@ export function TooManyChangesBanner({ { value0: limit.toLocaleString() } )} -
+
) } diff --git a/tests/e2e/source-control-large-file-count.spec.ts b/tests/e2e/source-control-large-file-count.spec.ts index 85f3710b308..c8c699b2bd8 100644 --- a/tests/e2e/source-control-large-file-count.spec.ts +++ b/tests/e2e/source-control-large-file-count.spec.ts @@ -31,6 +31,7 @@ import { removeLargeFileCountUntrackedTree } from './large-file-count-fixtures' import { DEFAULT_GIT_STATUS_LIMIT } from '../../src/shared/git-status-limit' +import { RIGHT_SIDEBAR_MIN_WIDTH } from '../../src/renderer/src/components/right-sidebar/right-sidebar-width' // Matches the large-diff freeze budget: a blocking stall past 1s is the // "UI becomes unresponsive" symptom reported in #8013. @@ -416,12 +417,17 @@ test.describe('Source Control large file count (#8013)', () => { rendererWorkingSetMb: { before: workingSetBeforeMb, after: workingSetAfterMb } }) - const tooManyChangesBanner = orcaPage.getByText('Too many changes detected.', { - exact: false - }) + const tooManyChangesBanner = orcaPage.getByTestId('too-many-changes-banner') await expect(tooManyChangesBanner).toBeVisible() if (process.env.ORCA_LARGE_FILE_SCREENSHOT_PATH) { - await orcaPage.screenshot({ path: process.env.ORCA_LARGE_FILE_SCREENSHOT_PATH }) + // Narrowest supported sidebar is where the banner layout is worst. + await orcaPage.evaluate((minWidth) => { + window.__store?.getState().setRightSidebarWidth(minWidth) + document.documentElement.classList.add('dark') + }, RIGHT_SIDEBAR_MIN_WIDTH) + await tooManyChangesBanner.screenshot({ + path: process.env.ORCA_LARGE_FILE_SCREENSHOT_PATH + }) } expect(measurement.didHitLimit).toBe(true) @@ -439,7 +445,7 @@ test.describe('Source Control large file count (#8013)', () => { ) expect(hugeState).not.toBeNull() - const retryButton = tooManyChangesBanner.locator('..').getByRole('button', { name: 'Retry' }) + const retryButton = tooManyChangesBanner.getByRole('button', { name: 'Retry' }) await expect(retryButton).toBeVisible() // Keep automatic refreshes from removing Retry before its real request starts. await installGitStatusRetryBarrier(electronApp, fixture.repoPath) From 337433b39a91ae0ee20e899ab2eb03b7f3153d6b Mon Sep 17 00:00:00 2001 From: Neil <4138956+nwparker@users.noreply.github.com> Date: Sun, 6 Sep 2026 01:30:35 -0700 Subject: [PATCH 02/22] test: preserve Windows golden command failures (#19047) --- .github/workflows/golden-e2e-experiment.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/golden-e2e-experiment.yml b/.github/workflows/golden-e2e-experiment.yml index d46c80033fa..11cfa866c67 100644 --- a/.github/workflows/golden-e2e-experiment.yml +++ b/.github/workflows/golden-e2e-experiment.yml @@ -98,12 +98,17 @@ jobs: $env:SKIP_BUILD = '1' $env:ORCA_E2E_FORWARD_APP_LOGS = '1' pnpm run --if-present test:e2e:workspace-session-golden + if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } pnpm run --if-present test:e2e:windows-fresh-startup-golden + if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } pnpm run --if-present test:e2e:tab-bar-agent-launch-golden + if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } if (Test-Path tests/e2e/golden-fresh-profile-terminal.spec.ts) { pnpm run test:e2e -- tests/e2e/golden-fresh-profile-terminal.spec.ts tests/e2e/golden-shell-command.spec.ts + if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } } pnpm run --if-present test:e2e:source-control-golden + if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } - name: Upload Playwright traces if: failure() From 0b7837430ede7e41ac2570ba9eb23db6dfdc3f52 Mon Sep 17 00:00:00 2001 From: Neil <4138956+nwparker@users.noreply.github.com> Date: Sun, 6 Sep 2026 01:48:47 -0700 Subject: [PATCH 03/22] test: canonicalize Windows fresh-profile fixture path (#19049) --- tests/e2e/golden-fresh-profile-terminal.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/e2e/golden-fresh-profile-terminal.spec.ts b/tests/e2e/golden-fresh-profile-terminal.spec.ts index 5194868adb7..e987b2a20de 100644 --- a/tests/e2e/golden-fresh-profile-terminal.spec.ts +++ b/tests/e2e/golden-fresh-profile-terminal.spec.ts @@ -16,7 +16,7 @@ import { test.use({ dismissOnboarding: false, seedTestRepo: false }) async function createGitRepo(): Promise { - const root = realpathSync(await mkdtemp(path.join(os.tmpdir(), 'orca-e2e-golden-fresh-'))) + const root = realpathSync.native(await mkdtemp(path.join(os.tmpdir(), 'orca-e2e-golden-fresh-'))) const repoPath = path.join(root, 'golden-fresh-project') mkdirSync(repoPath) execFileSync('git', ['init'], { cwd: repoPath, stdio: 'pipe' }) From 5ae76afda6e24a91cc4d886214e44b2d5700ce67 Mon Sep 17 00:00:00 2001 From: Neil <4138956+nwparker@users.noreply.github.com> Date: Sun, 6 Sep 2026 01:53:16 -0700 Subject: [PATCH 04/22] test: repair Windows paste fixture setup and newline oracles (#19050) --- ...inal-windows-codex-multiline-paste.spec.ts | 30 ++----------------- ...inal-windows-shell-paste-ownership.spec.ts | 25 +++++++++------- 2 files changed, 18 insertions(+), 37 deletions(-) diff --git a/tests/e2e/terminal-windows-codex-multiline-paste.spec.ts b/tests/e2e/terminal-windows-codex-multiline-paste.spec.ts index 543f6072247..70d11602adc 100644 --- a/tests/e2e/terminal-windows-codex-multiline-paste.spec.ts +++ b/tests/e2e/terminal-windows-codex-multiline-paste.spec.ts @@ -2,6 +2,7 @@ import { createHash, randomUUID } from 'node:crypto' import { rmSync, writeFileSync } from 'node:fs' import path from 'node:path' import { test, expect } from './helpers/orca-app' +import { attachRepoAndOpenTerminal } from './helpers/orca-restart' import { focusActiveTerminalInput, getTerminalContent, @@ -54,33 +55,8 @@ async function activateTestRepository( page: Parameters[0], repoPath: string ): Promise { - await page.evaluate(async (targetRepoPath) => { - const normalizePath = (value: string): string => value.replaceAll('\\', '/').toLowerCase() - await window.api.repos.add({ path: targetRepoPath }) - const store = window.__store - if (!store) { - throw new Error('Orca store unavailable') - } - await store.getState().fetchRepos() - const repo = store - .getState() - .repos.find((candidate) => normalizePath(candidate.path) === normalizePath(targetRepoPath)) - if (!repo) { - throw new Error('Seeded repository unavailable') - } - await store.getState().updateRepo(repo.id, { externalWorktreeVisibility: 'show' }) - await store.getState().fetchWorktrees(repo.id) - const worktree = store - .getState() - .worktreesByRepo[repo.id]?.find( - (candidate) => normalizePath(candidate.path) === normalizePath(targetRepoPath) - ) - if (!worktree) { - throw new Error('Seeded worktree unavailable') - } - store.getState().setActiveWorktree(worktree.id) - store.getState().createTab(worktree.id) - }, repoPath) + const worktreeId = await attachRepoAndOpenTerminal(page, repoPath) + await page.evaluate((id) => window.__store!.getState().createTab(id), worktreeId) } function pasteCollectorScript( diff --git a/tests/e2e/terminal-windows-shell-paste-ownership.spec.ts b/tests/e2e/terminal-windows-shell-paste-ownership.spec.ts index 43fad9122a2..c94bf8a7f38 100644 --- a/tests/e2e/terminal-windows-shell-paste-ownership.spec.ts +++ b/tests/e2e/terminal-windows-shell-paste-ownership.spec.ts @@ -221,7 +221,8 @@ test.describe('Windows terminal shell paste ownership', () => { `mixed-newline-before\r\nlf-line\ncrlf-line\r\n${sentinel}` ].join('\n') const scriptPath = path.join(testRepoPath, `.orca-paste-powershell-shell-${runId}.mjs`) - writeFileSync(scriptPath, pasteCollectScript(runId, sentinel, payload)) + const expectedText = payload.replace(/\r?\n/g, '\r') + writeFileSync(scriptPath, pasteCollectScript(runId, sentinel, expectedText)) let scriptStarted = false try { @@ -237,7 +238,7 @@ test.describe('Windows terminal shell paste ownership', () => { await waitForTerminalOutput(orcaPage, `PASTE_COMPLETE_${runId}:MATCH`, 10_000, 12_000) const writes = (await readTerminalPtyWrites(electronApp)).join('') - expect(countOccurrences(writes, payload), 'PowerShell payload PTY write count').toBe(1) + expect(countOccurrences(writes, expectedText), 'PowerShell payload PTY write count').toBe(1) } finally { if (scriptStarted) { await sendToTerminal(orcaPage, ptyId, '\x03').catch(() => undefined) @@ -271,7 +272,8 @@ test.describe('Windows terminal shell paste ownership', () => { `mixed-newline-before\r\nlf-line\ncrlf-line\r\n${sentinel}` ].join('\n') const scriptPath = path.join(testRepoPath, `.orca-paste-cmd-shell-${runId}.mjs`) - writeFileSync(scriptPath, pasteCollectScript(runId, sentinel, payload)) + const expectedText = payload.replace(/\r?\n/g, '\r') + writeFileSync(scriptPath, pasteCollectScript(runId, sentinel, expectedText)) let scriptStarted = false try { @@ -287,7 +289,7 @@ test.describe('Windows terminal shell paste ownership', () => { await waitForTerminalOutput(orcaPage, `PASTE_COMPLETE_${runId}:MATCH`, 10_000, 12_000) const writes = (await readTerminalPtyWrites(electronApp)).join('') - expect(countOccurrences(writes, payload), 'cmd.exe payload PTY write count').toBe(1) + expect(countOccurrences(writes, expectedText), 'cmd.exe payload PTY write count').toBe(1) } finally { if (scriptStarted) { await sendToTerminal(orcaPage, ptyId, '\x03').catch(() => undefined) @@ -322,7 +324,8 @@ test.describe('Windows terminal shell paste ownership', () => { `mixed-newline-before\r\nlf-line\ncrlf-line\r\n${sentinel}` ].join('\n') const scriptPath = path.join(testRepoPath, `.orca-paste-git-bash-shell-${runId}.mjs`) - writeFileSync(scriptPath, pasteCollectScript(runId, sentinel, payload)) + const expectedText = payload.replace(/\r?\n/g, '\r') + writeFileSync(scriptPath, pasteCollectScript(runId, sentinel, expectedText)) let scriptStarted = false try { @@ -338,7 +341,7 @@ test.describe('Windows terminal shell paste ownership', () => { await waitForTerminalOutput(orcaPage, `PASTE_COMPLETE_${runId}:MATCH`, 10_000, 12_000) const writes = (await readTerminalPtyWrites(electronApp)).join('') - expect(countOccurrences(writes, payload), 'Git Bash payload PTY write count').toBe(1) + expect(countOccurrences(writes, expectedText), 'Git Bash payload PTY write count').toBe(1) } finally { if (scriptStarted) { await sendToTerminal(orcaPage, ptyId, '\x03').catch(() => undefined) @@ -376,7 +379,8 @@ test.describe('Windows terminal shell paste ownership', () => { `mixed-newline-before\r\nlf-line\ncrlf-line\r\n${sentinel}` ].join('\n') const scriptPath = path.join(testRepoPath, `.orca-paste-wsl-shell-${runId}.mjs`) - writeFileSync(scriptPath, pasteCollectScript(runId, sentinel, payload)) + const expectedText = payload.replace(/\r?\n/g, '\r') + writeFileSync(scriptPath, pasteCollectScript(runId, sentinel, expectedText)) let scriptStarted = false try { @@ -396,7 +400,7 @@ test.describe('Windows terminal shell paste ownership', () => { await waitForTerminalOutput(orcaPage, `PASTE_COMPLETE_${runId}:MATCH`, 10_000, 12_000) const writes = (await readTerminalPtyWrites(electronApp)).join('') - expect(countOccurrences(writes, payload), 'WSL payload PTY write count').toBe(1) + expect(countOccurrences(writes, expectedText), 'WSL payload PTY write count').toBe(1) } finally { if (scriptStarted) { await sendToTerminal(orcaPage, ptyId, '\x03').catch(() => undefined) @@ -437,7 +441,8 @@ test.describe('Windows terminal shell paste ownership', () => { `mixed-newline-before\r\nlf-line\ncrlf-line\r\n${sentinel}` ].join('\n') const scriptPath = path.join(testRepoPath, `.orca-paste-wsl-retention-${runId}.mjs`) - writeFileSync(scriptPath, pasteCollectScript(runId, sentinel, payload)) + const expectedText = payload.replace(/\r?\n/g, '\r') + writeFileSync(scriptPath, pasteCollectScript(runId, sentinel, expectedText)) let scriptStarted = false try { @@ -457,7 +462,7 @@ test.describe('Windows terminal shell paste ownership', () => { await waitForTerminalOutput(orcaPage, `PASTE_COMPLETE_${runId}:MATCH`, 10_000, 12_000) const writes = (await readTerminalPtyWrites(electronApp)).join('') - expect(countOccurrences(writes, payload), 'retained WSL payload PTY write count').toBe(1) + expect(countOccurrences(writes, expectedText), 'retained WSL payload PTY write count').toBe(1) } finally { if (scriptStarted) { await sendToTerminal(orcaPage, ptyId, '\x03').catch(() => undefined) From 3d48d3a481af0aa6a8738a12d0beffea7b3e0605 Mon Sep 17 00:00:00 2001 From: Neil <4138956+nwparker@users.noreply.github.com> Date: Sun, 6 Sep 2026 02:03:40 -0700 Subject: [PATCH 05/22] fix(source-control): stack the Create PR notice's settings link below its message (#19046) --- .../source-control/commit/commit-notices.tsx | 6 +- ...rol-create-pr-intent-notice-layout.spec.ts | 91 +++++++++++++++++++ 2 files changed, 94 insertions(+), 3 deletions(-) create mode 100644 tests/e2e/source-control-create-pr-intent-notice-layout.spec.ts diff --git a/src/renderer/src/components/right-sidebar/source-control/commit/commit-notices.tsx b/src/renderer/src/components/right-sidebar/source-control/commit/commit-notices.tsx index e46b1f6b3ea..666181a649a 100644 --- a/src/renderer/src/components/right-sidebar/source-control/commit/commit-notices.tsx +++ b/src/renderer/src/components/right-sidebar/source-control/commit/commit-notices.tsx @@ -113,20 +113,20 @@ export function CommitNotices({ role={createPrIntentNotice.tone === 'destructive' ? 'alert' : 'status'} aria-live="polite" className={cn( - 'mt-1 flex min-w-0 items-center gap-1.5 text-[11px]', + 'mt-1 flex min-w-0 flex-col items-start gap-1 text-[11px]', createPrIntentNotice.tone === 'destructive' ? 'text-destructive' : 'text-muted-foreground' )} > {/* Why: Create Review blockers carry recovery steps; truncating hides the action the user needs in a narrow sidebar. */} - + {createPrIntentNotice.message} {createPrIntentNotice.action === 'settings' && onOpenSourceControlAiSettings ? (