mirror of
https://github.com/stablyai/orca.git
synced 2026-10-30 08:37:49 +00:00
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:
+108
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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) => {
|
||||
|
||||
Reference in New Issue
Block a user