fix(settings): reenable terminal link Actions after an old opt-out (#26853)

An explicit Actions, Open URL, or Leave to terminal choice now also writes the
legacy terminalLinkActionPopoverEnabled switch, so an older opt-out no longer
snaps the Actions selection back to Leave to terminal.

Fixes #25576

Co-authored-by: drakeo338 <paranoyouz@gmail.com>
This commit is contained in:
Neil
2026-10-10 20:47:45 -07:00
committed by GitHub
co-authored by drakeo338
parent 666d990bc0
commit 56d25b00bd
3 changed files with 225 additions and 2 deletions
@@ -0,0 +1,108 @@
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import { mkdtempSync, rmSync } from 'node:fs'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import {
closeTestStores,
createStore,
testState,
writeDataFile
} from '../../persistence-test-harness'
import { getDefaultPersistedState } from '../../../shared/constants'
import { updateSettings, type SettingsMutationOperations } from './settings-update'
function makeOperations(): SettingsMutationOperations {
const state = getDefaultPersistedState(tmpdir())
state.settings.terminalLinkActionPopoverEnabled = false
return {
state,
bumpLocalWorktreeScanGeneration: vi.fn(),
removeRetainedBlob: vi.fn(),
scheduleSave: vi.fn(),
notifySettingsChanged: vi.fn()
}
}
describe('terminal URL click preference writes', () => {
it.each(['actions', 'open', 'none'] as const)(
'publishes the explicit %s choice with a consistent legacy switch',
(behavior) => {
const operations = makeOperations()
operations.state.settings.terminalLinkActionPopoverEnabled = behavior !== 'actions'
operations.state.settings.terminalLinkClickBehavior =
behavior === 'actions' ? 'none' : 'actions'
updateSettings(
operations,
{
terminalLinkClickBehavior: behavior,
terminalLinkActionPopoverEnabled: behavior !== 'actions'
},
{ notifyListeners: true, originWebContentsId: 7 }
)
expect(operations.state.settings.terminalLinkClickBehavior).toBe(behavior)
expect(operations.state.settings.terminalLinkActionPopoverEnabled).toBe(
behavior === 'actions'
)
expect(operations.notifySettingsChanged).toHaveBeenCalledWith(
expect.objectContaining({
terminalLinkClickBehavior: behavior,
terminalLinkActionPopoverEnabled: behavior === 'actions'
}),
7
)
}
)
it('preserves a legacy-only switch update', () => {
const operations = makeOperations()
updateSettings(operations, { terminalLinkActionPopoverEnabled: true })
expect(operations.state.settings.terminalLinkActionPopoverEnabled).toBe(true)
updateSettings(operations, { terminalLinkActionPopoverEnabled: false })
expect(operations.state.settings.terminalLinkActionPopoverEnabled).toBe(false)
})
})
vi.mock('electron', () => ({
app: { getPath: () => testState.dir },
safeStorage: { isEncryptionAvailable: () => false }
}))
vi.mock('../../telemetry/client', () => ({ track: vi.fn() }))
describe('terminal URL click preference persistence', () => {
beforeEach(() => {
testState.dir = mkdtempSync(join(tmpdir(), 'orca-terminal-link-preference-'))
writeDataFile({ version: 1, settings: { terminalLinkActionPopoverEnabled: false } })
})
afterEach(async () => {
await closeTestStores()
rmSync(testState.dir, { recursive: true, force: true })
})
it('keeps an old opt-out after saving unrelated settings and reopening the profile', async () => {
const store = createStore()
store.updateSettings({ terminalFontSize: 15 })
store.flush()
await closeTestStores()
const reopened = createStore()
expect(reopened.getSettings()).toMatchObject({
terminalLinkClickBehavior: 'actions',
terminalLinkActionPopoverEnabled: false,
terminalFontSize: 15
})
})
it('keeps a newly selected Actions preference after reopening the profile', async () => {
const store = createStore()
store.updateSettings({ terminalLinkClickBehavior: 'actions' })
store.flush()
await closeTestStores()
const reopened = createStore()
expect(reopened.getSettings()).toMatchObject({
terminalLinkClickBehavior: 'actions',
terminalLinkActionPopoverEnabled: true
})
})
})
@@ -58,6 +58,11 @@ export function updateSettings(
options: { notifyListeners?: boolean; originWebContentsId?: number } = {}
): GlobalSettings {
const sanitizedUpdates = stripRetiredGlobalSettings(updates)
const linkBehavior = updates.terminalLinkClickBehavior
if (linkBehavior === 'actions' || linkBehavior === 'open' || linkBehavior === 'none') {
// Loaded defaults are ambiguous; synchronize the legacy switch only on an explicit write.
sanitizedUpdates.terminalLinkActionPopoverEnabled = linkBehavior === 'actions'
}
if ('opencodeSessionCookie' in updates && !updates.opencodeSessionCookie) {
operations.removeRetainedBlob(PROTECTED_SECRET_SLOT.opencodeSessionCookie)
}
+112 -2
View File
@@ -12,6 +12,11 @@ import {
} from './helpers/terminal'
import { ensureTerminalVisible, waitForActiveWorktree, waitForSessionReady } from './helpers/store'
import {
readPanelNavigationObserver,
startPanelNavigationObserver
} from './helpers/plugin-panel-navigation-observer'
const FIXTURE_PATH = path.join(
process.cwd(),
'tests/e2e/fixtures/terminal-link-mouse-owner-fixture.cjs'
@@ -92,12 +97,117 @@ async function expectChildMouseReports(mouseLogPath: string): Promise<void> {
.toBeGreaterThan(0)
}
async function expectOrcaOwnedMouseOutcome(mouseLogPath: string): Promise<void> {
async function expectOrcaOwnedMouseOutcome(
mouseLogPath: string,
reportsBeforeClick = 0
): Promise<void> {
await new Promise<void>((resolve) => setTimeout(resolve, 1_000))
expect(childMouseReportCount(mouseLogPath)).toBe(0)
expect(childMouseReportCount(mouseLogPath)).toBe(reportsBeforeClick)
}
async function openLegacyDisabledLinkSettings(orcaPage: Page): Promise<void> {
await orcaPage.evaluate(() => {
void window.__store?.getState().updateSettings({ terminalLinkActionPopoverEnabled: false })
})
await expect
.poll(() =>
orcaPage.evaluate(() => window.__store?.getState().settings?.terminalLinkActionPopoverEnabled)
)
.toBe(false)
await orcaPage.evaluate(() => {
const state = window.__store?.getState()
state?.openSettingsPage()
state?.openSettingsTarget({ pane: 'browser', repoId: null })
})
}
test.describe('terminal link click ownership', () => {
test('selecting Actions reenables a legacy-disabled popover and survives renderer reload', async ({
orcaPage
}, testInfo) => {
const { mouseLogPath, ptyId, target } = await startMouseAwareLinkFixture(orcaPage, testInfo)
await openLegacyDisabledLinkSettings(orcaPage)
const choices = orcaPage.getByRole('radiogroup', { name: 'Plain click URL behavior' })
await expect(choices.getByRole('radio', { name: 'Leave to terminal' })).toHaveAttribute(
'aria-checked',
'true'
)
await choices.getByRole('radio', { name: 'Actions', exact: true }).click()
await expect(choices.getByRole('radio', { name: 'Actions', exact: true })).toHaveAttribute(
'aria-checked',
'true'
)
await orcaPage.reload()
await waitForSessionReady(orcaPage)
await orcaPage.evaluate(() => {
const state = window.__store?.getState()
state?.openSettingsPage()
state?.openSettingsTarget({ pane: 'browser', repoId: null })
})
await expect(choices.getByRole('radio', { name: 'Actions', exact: true })).toHaveAttribute(
'aria-checked',
'true'
)
await orcaPage.screenshot({ path: testInfo.outputPath('actions-selected-after.png') })
await orcaPage.evaluate(() => window.__store?.getState().closeSettingsPage())
await waitForActiveTerminalManager(orcaPage)
await orcaPage.mouse.click(target.x, target.y)
await expect(orcaPage.locator('[data-terminal-link-action-popover]')).toBeVisible()
await expect(orcaPage.locator('[data-terminal-link-destination]')).toHaveText(LINK)
await expectOrcaOwnedMouseOutcome(mouseLogPath)
await orcaPage.screenshot({ path: testInfo.outputPath('actions-after.png') })
await sendToTerminal(orcaPage, ptyId, 'q')
})
test('explicit Open URL and Leave to terminal choices preserve direct-open modifiers', async ({
orcaPage,
electronApp
}, testInfo) => {
const { mouseLogPath, ptyId, target } = await startMouseAwareLinkFixture(orcaPage, testInfo)
await startPanelNavigationObserver(electronApp, orcaPage.url())
await openLegacyDisabledLinkSettings(orcaPage)
const choices = orcaPage.getByRole('radiogroup', { name: 'Plain click URL behavior' })
await choices.getByRole('radio', { name: 'Open URL', exact: true }).click()
await expect(choices.getByRole('radio', { name: 'Open URL', exact: true })).toHaveAttribute(
'aria-checked',
'true'
)
await orcaPage.evaluate(() => window.__store?.getState().closeSettingsPage())
await orcaPage.mouse.click(target.x, target.y)
await expect
.poll(async () => (await readPanelNavigationObserver(electronApp)).externalUrls)
.toEqual([LINK])
await expectOrcaOwnedMouseOutcome(mouseLogPath)
await orcaPage.evaluate(() => {
const state = window.__store?.getState()
state?.openSettingsPage()
state?.openSettingsTarget({ pane: 'browser', repoId: null })
})
await choices.getByRole('radio', { name: 'Leave to terminal', exact: true }).click()
await expect(
choices.getByRole('radio', { name: 'Leave to terminal', exact: true })
).toHaveAttribute('aria-checked', 'true')
await orcaPage.evaluate(() => window.__store?.getState().closeSettingsPage())
await orcaPage.mouse.click(target.x, target.y)
await expectChildMouseReports(mouseLogPath)
expect((await readPanelNavigationObserver(electronApp)).externalUrls).toEqual([LINK])
const reportsBeforeModifiedClick = childMouseReportCount(mouseLogPath)
const modifier = process.platform === 'darwin' ? 'Meta' : 'Control'
await orcaPage.keyboard.down(modifier)
await orcaPage.mouse.click(target.x, target.y)
await orcaPage.keyboard.up(modifier)
await expect
.poll(async () => (await readPanelNavigationObserver(electronApp)).externalUrls)
.toEqual([LINK, LINK])
await expectOrcaOwnedMouseOutcome(mouseLogPath, reportsBeforeModifiedClick)
await expect(orcaPage.locator('[data-terminal-link-action-popover]')).toHaveCount(0)
await sendToTerminal(orcaPage, ptyId, 'q')
})
test('an Orca-owned plain link click emits no child PTY mouse frames', async ({
orcaPage
}, testInfo) => {