fix(menu): restore paste in macOS native dialogs (#12645)
* fix(menu): restore paste in macOS native dialogs * test(menu): pin platform for paste routing coverage CI runs unit shards on ubuntu-latest only, so the unpinned paste-routing test asserted "no native paste" vacuously: a double-route regression passed green on Linux. Cover darwin/linux/win32 explicitly so the exactly-once contract holds on every platform, and assert the item exists in the negative-only cases so a rename cannot pass them silently. Also document why the native first-responder fallback exists. --------- Co-authored-by: Jinwoo-H <Jinwoo-H@users.noreply.github.com>
This commit is contained in:
parent
a30e3b9f61
commit
ca187c7307
|
|
@ -1,9 +1,15 @@
|
|||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
|
||||
const { buildFromTemplateMock, setApplicationMenuMock, getFocusedWindowMock } = vi.hoisted(() => ({
|
||||
const {
|
||||
buildFromTemplateMock,
|
||||
setApplicationMenuMock,
|
||||
getFocusedWindowMock,
|
||||
sendActionToFirstResponderMock
|
||||
} = vi.hoisted(() => ({
|
||||
buildFromTemplateMock: vi.fn(),
|
||||
setApplicationMenuMock: vi.fn(),
|
||||
getFocusedWindowMock: vi.fn()
|
||||
getFocusedWindowMock: vi.fn(),
|
||||
sendActionToFirstResponderMock: vi.fn()
|
||||
}))
|
||||
|
||||
vi.mock('electron', () => ({
|
||||
|
|
@ -12,7 +18,8 @@ vi.mock('electron', () => ({
|
|||
},
|
||||
Menu: {
|
||||
buildFromTemplate: buildFromTemplateMock,
|
||||
setApplicationMenu: setApplicationMenuMock
|
||||
setApplicationMenu: setApplicationMenuMock,
|
||||
sendActionToFirstResponder: sendActionToFirstResponderMock
|
||||
},
|
||||
app: {
|
||||
name: 'Orca'
|
||||
|
|
@ -70,9 +77,14 @@ describe('registerAppMenu', () => {
|
|||
buildFromTemplateMock.mockReset()
|
||||
setApplicationMenuMock.mockReset()
|
||||
getFocusedWindowMock.mockReset()
|
||||
sendActionToFirstResponderMock.mockReset()
|
||||
buildFromTemplateMock.mockImplementation((template) => ({ template }))
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
vi.restoreAllMocks()
|
||||
})
|
||||
|
||||
it('shows reload shortcuts as policy-routed menu hints', () => {
|
||||
registerAppMenu(buildMenuOptions())
|
||||
|
||||
|
|
@ -214,23 +226,59 @@ describe('registerAppMenu', () => {
|
|||
expect(paletteItem?.accelerator).toBeUndefined()
|
||||
})
|
||||
|
||||
it('routes Edit > Paste through Orca coordinated paste ownership', () => {
|
||||
const send = vi.fn()
|
||||
getFocusedWindowMock.mockReturnValue({ webContents: { send } })
|
||||
// Why: pin the platform on every case — CI runs this suite on Linux only, so an
|
||||
// unpinned test leaves the other platforms' branches entirely uncovered.
|
||||
it.each(['darwin', 'linux', 'win32'] as const)(
|
||||
'routes Edit > Paste through Orca coordinated paste ownership on %s',
|
||||
(platform) => {
|
||||
vi.spyOn(process, 'platform', 'get').mockReturnValue(platform)
|
||||
const send = vi.fn()
|
||||
getFocusedWindowMock.mockReturnValue({ webContents: { send } })
|
||||
registerAppMenu(buildMenuOptions())
|
||||
|
||||
const editSubmenu = getSubmenu(getTemplate(), 'Edit')
|
||||
const pasteItem = editSubmenu.find((item) => item.label === 'Paste')
|
||||
|
||||
expect(pasteItem).toBeDefined()
|
||||
expect(pasteItem?.role).toBeUndefined()
|
||||
expect(pasteItem?.accelerator).toBe('CmdOrCtrl+V')
|
||||
|
||||
pasteItem?.click?.({} as never, {} as never, {} as never)
|
||||
|
||||
expect(send).toHaveBeenCalledOnce()
|
||||
expect(send).toHaveBeenCalledWith('ui:appMenuPaste')
|
||||
expect(sendActionToFirstResponderMock).not.toHaveBeenCalled()
|
||||
}
|
||||
)
|
||||
|
||||
it('routes Edit > Paste to the native first responder once on macOS without a focused window', () => {
|
||||
vi.spyOn(process, 'platform', 'get').mockReturnValue('darwin')
|
||||
getFocusedWindowMock.mockReturnValue(null)
|
||||
registerAppMenu(buildMenuOptions())
|
||||
|
||||
const editSubmenu = getSubmenu(getTemplate(), 'Edit')
|
||||
const pasteItem = editSubmenu.find((item) => item.label === 'Paste')
|
||||
|
||||
expect(pasteItem).toBeDefined()
|
||||
expect(pasteItem?.role).toBeUndefined()
|
||||
expect(pasteItem?.accelerator).toBe('CmdOrCtrl+V')
|
||||
|
||||
const pasteItem = getSubmenu(getTemplate(), 'Edit').find((item) => item.label === 'Paste')
|
||||
pasteItem?.click?.({} as never, {} as never, {} as never)
|
||||
|
||||
expect(send).toHaveBeenCalledWith('ui:appMenuPaste')
|
||||
expect(sendActionToFirstResponderMock).toHaveBeenCalledOnce()
|
||||
expect(sendActionToFirstResponderMock).toHaveBeenCalledWith('paste:')
|
||||
})
|
||||
|
||||
it.each(['linux', 'win32'] as const)(
|
||||
'does not invoke the native paste responder on %s without a focused window',
|
||||
(platform) => {
|
||||
vi.spyOn(process, 'platform', 'get').mockReturnValue(platform)
|
||||
getFocusedWindowMock.mockReturnValue(null)
|
||||
registerAppMenu(buildMenuOptions())
|
||||
|
||||
const pasteItem = getSubmenu(getTemplate(), 'Edit').find((item) => item.label === 'Paste')
|
||||
// Why: this case asserts only a negative, so it would pass green if the item vanished.
|
||||
expect(pasteItem).toBeDefined()
|
||||
pasteItem?.click?.({} as never, {} as never, {} as never)
|
||||
|
||||
expect(sendActionToFirstResponderMock).not.toHaveBeenCalled()
|
||||
}
|
||||
)
|
||||
|
||||
it.runIf(!isMac)('puts Settings and Exit under File on Windows/Linux', () => {
|
||||
registerAppMenu(buildMenuOptions())
|
||||
|
||||
|
|
|
|||
|
|
@ -180,7 +180,17 @@ function buildAndApplyMenu(options: RegisterAppMenuOptions): void {
|
|||
click: () => {
|
||||
// Why: a focused terminal/native-chat pane is not a native editable
|
||||
// control, so raw Electron paste cannot know which Orca surface owns it.
|
||||
BrowserWindow.getFocusedWindow()?.webContents.send('ui:appMenuPaste')
|
||||
const focusedWindow = BrowserWindow.getFocusedWindow()
|
||||
if (focusedWindow) {
|
||||
focusedWindow.webContents.send('ui:appMenuPaste')
|
||||
return
|
||||
}
|
||||
|
||||
// Why: a macOS native panel (open/save, Go to Folder) leaves no focused
|
||||
// BrowserWindow, so overriding the paste role would strand Cmd+V as a no-op.
|
||||
if (isMac) {
|
||||
Menu.sendActionToFirstResponder('paste:')
|
||||
}
|
||||
}
|
||||
},
|
||||
{ role: 'selectAll' }
|
||||
|
|
|
|||
Loading…
Reference in New Issue