mirror of
https://github.com/stablyai/orca.git
synced 2026-11-04 11:07:42 +00:00
575 lines
17 KiB
TypeScript
575 lines
17 KiB
TypeScript
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
|
|
|
vi.mock('electron', async () =>
|
|
(await import('./createMainWindow-test-harness')).electronModuleMock()
|
|
)
|
|
vi.mock('./macos-tahoe-release', async () =>
|
|
(await import('./createMainWindow-test-harness')).macosTahoeReleaseMock()
|
|
)
|
|
vi.mock('../app-icon', async () => (await import('./createMainWindow-test-harness')).appIconMock())
|
|
vi.mock('../browser/browser-manager', async () =>
|
|
(await import('./createMainWindow-test-harness')).browserManagerMock()
|
|
)
|
|
|
|
import { createMainWindow } from './createMainWindow'
|
|
import { ipcMain } from 'electron'
|
|
import { resetExpectedTeardownStateForTest } from '../crash-reporting/expected-teardown-state'
|
|
import {
|
|
browserWindowMock,
|
|
buildFromTemplateMock,
|
|
menuPopupMock,
|
|
resetMainWindowMocks
|
|
} from './createMainWindow-test-harness'
|
|
|
|
function openMainWindowForContextMenu() {
|
|
const windowHandlers: Record<string, (...args: any[]) => void> = {}
|
|
const webContents = {
|
|
on: vi.fn((event, handler) => {
|
|
windowHandlers[event] = handler
|
|
}),
|
|
setZoomLevel: vi.fn(),
|
|
setBackgroundThrottling: vi.fn(),
|
|
invalidate: vi.fn(),
|
|
setWindowOpenHandler: vi.fn(),
|
|
send: vi.fn(),
|
|
isDevToolsOpened: vi.fn(),
|
|
openDevTools: vi.fn(),
|
|
closeDevTools: vi.fn(),
|
|
replaceMisspelling: vi.fn(),
|
|
session: { addWordToSpellCheckerDictionary: vi.fn() }
|
|
}
|
|
const browserWindowInstance = {
|
|
webContents,
|
|
on: vi.fn(),
|
|
isDestroyed: vi.fn(() => false),
|
|
isMaximized: vi.fn(() => true),
|
|
isFullScreen: vi.fn(() => false),
|
|
getSize: vi.fn(() => [1200, 800]),
|
|
setSize: vi.fn(),
|
|
maximize: vi.fn(),
|
|
show: vi.fn(),
|
|
loadFile: vi.fn(() => Promise.resolve()),
|
|
loadURL: vi.fn(() => Promise.resolve())
|
|
}
|
|
browserWindowMock.mockImplementation(function () {
|
|
return browserWindowInstance
|
|
})
|
|
|
|
createMainWindow(null)
|
|
return { webContents, windowHandlers, browserWindowInstance }
|
|
}
|
|
|
|
describe('createMainWindow', () => {
|
|
beforeEach(() => {
|
|
resetMainWindowMocks()
|
|
resetExpectedTeardownStateForTest()
|
|
vi.useRealTimers()
|
|
})
|
|
|
|
it('ignores traffic light sync IPC on non-macOS', () => {
|
|
const windowHandlers: Record<string, (...args: any[]) => void> = {}
|
|
const webContents = {
|
|
on: vi.fn((event, handler) => {
|
|
windowHandlers[event] = handler
|
|
}),
|
|
setZoomLevel: vi.fn(),
|
|
setBackgroundThrottling: vi.fn(),
|
|
invalidate: vi.fn(),
|
|
setWindowOpenHandler: vi.fn(),
|
|
send: vi.fn()
|
|
}
|
|
const browserWindowInstance = {
|
|
webContents,
|
|
on: vi.fn(),
|
|
isDestroyed: vi.fn(() => false),
|
|
isMaximized: vi.fn(() => true),
|
|
isFullScreen: vi.fn(() => false),
|
|
getSize: vi.fn(() => [1200, 800]),
|
|
setSize: vi.fn(),
|
|
setWindowButtonPosition: vi.fn(),
|
|
maximize: vi.fn(),
|
|
show: vi.fn(),
|
|
loadFile: vi.fn(() => Promise.resolve()),
|
|
loadURL: vi.fn(() => Promise.resolve())
|
|
}
|
|
browserWindowMock.mockImplementation(function () {
|
|
return browserWindowInstance
|
|
})
|
|
|
|
createMainWindow(null)
|
|
|
|
const syncListener = vi
|
|
.mocked(ipcMain.on)
|
|
.mock.calls.find(([channel]) => channel === 'ui:sync-traffic-lights')?.[1]
|
|
|
|
expect(syncListener).toBeTypeOf('function')
|
|
|
|
syncListener?.({} as never, 1.2)
|
|
|
|
if (process.platform === 'darwin') {
|
|
expect(browserWindowInstance.setWindowButtonPosition).toHaveBeenCalledWith({ x: 16, y: 16 })
|
|
return
|
|
}
|
|
|
|
expect(browserWindowInstance.setWindowButtonPosition).not.toHaveBeenCalled()
|
|
})
|
|
|
|
it('intercepts Cmd+B for sidebar when the markdown editor is not focused', () => {
|
|
const windowHandlers: Record<string, (...args: any[]) => void> = {}
|
|
const webContents = {
|
|
on: vi.fn((event, handler) => {
|
|
windowHandlers[event] = handler
|
|
}),
|
|
setZoomLevel: vi.fn(),
|
|
setBackgroundThrottling: vi.fn(),
|
|
invalidate: vi.fn(),
|
|
setWindowOpenHandler: vi.fn(),
|
|
send: vi.fn(),
|
|
isDevToolsOpened: vi.fn(),
|
|
openDevTools: vi.fn(),
|
|
closeDevTools: vi.fn()
|
|
}
|
|
const browserWindowInstance = {
|
|
webContents,
|
|
on: vi.fn(),
|
|
isDestroyed: vi.fn(() => false),
|
|
isMaximized: vi.fn(() => true),
|
|
isFullScreen: vi.fn(() => false),
|
|
getSize: vi.fn(() => [1200, 800]),
|
|
setSize: vi.fn(),
|
|
maximize: vi.fn(),
|
|
show: vi.fn(),
|
|
loadFile: vi.fn(() => Promise.resolve()),
|
|
loadURL: vi.fn(() => Promise.resolve())
|
|
}
|
|
browserWindowMock.mockImplementation(function () {
|
|
return browserWindowInstance
|
|
})
|
|
|
|
createMainWindow(null)
|
|
|
|
const preventDefault = vi.fn()
|
|
const isDarwin = process.platform === 'darwin'
|
|
windowHandlers['before-input-event'](
|
|
{ preventDefault } as never,
|
|
{
|
|
type: 'keyDown',
|
|
code: 'KeyB',
|
|
key: 'b',
|
|
meta: isDarwin,
|
|
control: !isDarwin,
|
|
alt: false,
|
|
shift: false
|
|
} as never
|
|
)
|
|
|
|
expect(preventDefault).toHaveBeenCalledTimes(1)
|
|
expect(webContents.send).toHaveBeenCalledWith('ui:toggleLeftSidebar')
|
|
})
|
|
|
|
it('skips Cmd+B interception when the markdown editor is focused', () => {
|
|
const windowHandlers: Record<string, (...args: any[]) => void> = {}
|
|
const webContents = {
|
|
on: vi.fn((event, handler) => {
|
|
windowHandlers[event] = handler
|
|
}),
|
|
setZoomLevel: vi.fn(),
|
|
setBackgroundThrottling: vi.fn(),
|
|
invalidate: vi.fn(),
|
|
setWindowOpenHandler: vi.fn(),
|
|
send: vi.fn(),
|
|
isDevToolsOpened: vi.fn(),
|
|
openDevTools: vi.fn(),
|
|
closeDevTools: vi.fn()
|
|
}
|
|
const browserWindowInstance = {
|
|
webContents,
|
|
on: vi.fn(),
|
|
isDestroyed: vi.fn(() => false),
|
|
isMaximized: vi.fn(() => true),
|
|
isFullScreen: vi.fn(() => false),
|
|
getSize: vi.fn(() => [1200, 800]),
|
|
setSize: vi.fn(),
|
|
maximize: vi.fn(),
|
|
show: vi.fn(),
|
|
loadFile: vi.fn(() => Promise.resolve()),
|
|
loadURL: vi.fn(() => Promise.resolve())
|
|
}
|
|
browserWindowMock.mockImplementation(function () {
|
|
return browserWindowInstance
|
|
})
|
|
|
|
createMainWindow(null)
|
|
|
|
const setFocusedListener = vi
|
|
.mocked(ipcMain.on)
|
|
.mock.calls.find(([channel]) => channel === 'ui:setMarkdownEditorFocused')?.[1]
|
|
expect(setFocusedListener).toBeTypeOf('function')
|
|
setFocusedListener?.({ sender: webContents } as never, true)
|
|
|
|
const preventDefault = vi.fn()
|
|
const isDarwin = process.platform === 'darwin'
|
|
windowHandlers['before-input-event'](
|
|
{ preventDefault } as never,
|
|
{
|
|
type: 'keyDown',
|
|
code: 'KeyB',
|
|
key: 'b',
|
|
meta: isDarwin,
|
|
control: !isDarwin,
|
|
alt: false,
|
|
shift: false
|
|
} as never
|
|
)
|
|
|
|
expect(preventDefault).not.toHaveBeenCalled()
|
|
expect(webContents.send).not.toHaveBeenCalledWith('ui:toggleLeftSidebar')
|
|
})
|
|
|
|
it('lets the shortcut recorder capture app shortcuts before main interception', () => {
|
|
const windowHandlers: Record<string, (...args: any[]) => void> = {}
|
|
const webContents = {
|
|
on: vi.fn((event, handler) => {
|
|
windowHandlers[event] = handler
|
|
}),
|
|
setZoomLevel: vi.fn(),
|
|
setBackgroundThrottling: vi.fn(),
|
|
invalidate: vi.fn(),
|
|
setWindowOpenHandler: vi.fn(),
|
|
send: vi.fn(),
|
|
isDevToolsOpened: vi.fn(),
|
|
openDevTools: vi.fn(),
|
|
closeDevTools: vi.fn()
|
|
}
|
|
const browserWindowInstance = {
|
|
webContents,
|
|
on: vi.fn(),
|
|
isDestroyed: vi.fn(() => false),
|
|
isMaximized: vi.fn(() => true),
|
|
isFullScreen: vi.fn(() => false),
|
|
getSize: vi.fn(() => [1200, 800]),
|
|
setSize: vi.fn(),
|
|
maximize: vi.fn(),
|
|
show: vi.fn(),
|
|
loadFile: vi.fn(() => Promise.resolve()),
|
|
loadURL: vi.fn(() => Promise.resolve())
|
|
}
|
|
browserWindowMock.mockImplementation(function () {
|
|
return browserWindowInstance
|
|
})
|
|
|
|
createMainWindow(null)
|
|
|
|
const setFocusedListener = vi
|
|
.mocked(ipcMain.on)
|
|
.mock.calls.find(([channel]) => channel === 'ui:setShortcutRecorderFocused')?.[1]
|
|
expect(setFocusedListener).toBeTypeOf('function')
|
|
setFocusedListener?.({ sender: webContents } as never, true)
|
|
|
|
const preventDefault = vi.fn()
|
|
const isDarwin = process.platform === 'darwin'
|
|
windowHandlers['before-input-event'](
|
|
{ preventDefault } as never,
|
|
{
|
|
type: 'keyDown',
|
|
code: 'KeyB',
|
|
key: 'b',
|
|
meta: isDarwin,
|
|
control: !isDarwin,
|
|
alt: false,
|
|
shift: false
|
|
} as never
|
|
)
|
|
|
|
expect(preventDefault).not.toHaveBeenCalled()
|
|
expect(webContents.send).not.toHaveBeenCalledWith('ui:toggleLeftSidebar')
|
|
})
|
|
|
|
it('skips Cmd+B interception when floating terminal input is focused', () => {
|
|
const windowHandlers: Record<string, (...args: any[]) => void> = {}
|
|
const webContents = {
|
|
on: vi.fn((event, handler) => {
|
|
windowHandlers[event] = handler
|
|
}),
|
|
setZoomLevel: vi.fn(),
|
|
setBackgroundThrottling: vi.fn(),
|
|
invalidate: vi.fn(),
|
|
setWindowOpenHandler: vi.fn(),
|
|
send: vi.fn(),
|
|
isDevToolsOpened: vi.fn(),
|
|
openDevTools: vi.fn(),
|
|
closeDevTools: vi.fn()
|
|
}
|
|
const browserWindowInstance = {
|
|
webContents,
|
|
on: vi.fn(),
|
|
isDestroyed: vi.fn(() => false),
|
|
isMaximized: vi.fn(() => true),
|
|
isFullScreen: vi.fn(() => false),
|
|
getSize: vi.fn(() => [1200, 800]),
|
|
setSize: vi.fn(),
|
|
maximize: vi.fn(),
|
|
show: vi.fn(),
|
|
loadFile: vi.fn(() => Promise.resolve()),
|
|
loadURL: vi.fn(() => Promise.resolve())
|
|
}
|
|
browserWindowMock.mockImplementation(function () {
|
|
return browserWindowInstance
|
|
})
|
|
|
|
createMainWindow(null)
|
|
|
|
const setFocusedListener = vi
|
|
.mocked(ipcMain.on)
|
|
.mock.calls.find(([channel]) => channel === 'ui:setFloatingFocus')?.[1]
|
|
expect(setFocusedListener).toBeTypeOf('function')
|
|
setFocusedListener?.(
|
|
{ sender: webContents } as never,
|
|
{ panelFocused: true, terminalFocused: true } as never
|
|
)
|
|
|
|
const preventDefault = vi.fn()
|
|
const isDarwin = process.platform === 'darwin'
|
|
windowHandlers['before-input-event'](
|
|
{ preventDefault } as never,
|
|
{
|
|
type: 'keyDown',
|
|
code: 'KeyB',
|
|
key: 'b',
|
|
meta: isDarwin,
|
|
control: !isDarwin,
|
|
alt: false,
|
|
shift: false
|
|
} as never
|
|
)
|
|
|
|
expect(preventDefault).not.toHaveBeenCalled()
|
|
expect(webContents.send).not.toHaveBeenCalledWith('ui:toggleLeftSidebar')
|
|
|
|
webContents.send.mockClear()
|
|
const newWorkspacePreventDefault = vi.fn()
|
|
windowHandlers['before-input-event'](
|
|
{ preventDefault: newWorkspacePreventDefault } as never,
|
|
{
|
|
type: 'keyDown',
|
|
code: 'KeyN',
|
|
key: 'n',
|
|
meta: isDarwin,
|
|
control: !isDarwin,
|
|
alt: false,
|
|
shift: false
|
|
} as never
|
|
)
|
|
|
|
expect(newWorkspacePreventDefault).toHaveBeenCalledTimes(1)
|
|
expect(webContents.send).toHaveBeenCalledWith('ui:openNewWorkspace')
|
|
})
|
|
|
|
it('still intercepts Cmd+Shift+B and Cmd+Alt+B when the markdown editor is focused', () => {
|
|
const windowHandlers: Record<string, (...args: any[]) => void> = {}
|
|
const webContents = {
|
|
on: vi.fn((event, handler) => {
|
|
windowHandlers[event] = handler
|
|
}),
|
|
setZoomLevel: vi.fn(),
|
|
setBackgroundThrottling: vi.fn(),
|
|
invalidate: vi.fn(),
|
|
setWindowOpenHandler: vi.fn(),
|
|
send: vi.fn(),
|
|
isDevToolsOpened: vi.fn(),
|
|
openDevTools: vi.fn(),
|
|
closeDevTools: vi.fn()
|
|
}
|
|
const browserWindowInstance = {
|
|
webContents,
|
|
on: vi.fn(),
|
|
isDestroyed: vi.fn(() => false),
|
|
isMaximized: vi.fn(() => true),
|
|
isFullScreen: vi.fn(() => false),
|
|
getSize: vi.fn(() => [1200, 800]),
|
|
setSize: vi.fn(),
|
|
maximize: vi.fn(),
|
|
show: vi.fn(),
|
|
loadFile: vi.fn(() => Promise.resolve()),
|
|
loadURL: vi.fn(() => Promise.resolve())
|
|
}
|
|
browserWindowMock.mockImplementation(function () {
|
|
return browserWindowInstance
|
|
})
|
|
|
|
createMainWindow(null)
|
|
|
|
const setFocusedListener = vi
|
|
.mocked(ipcMain.on)
|
|
.mock.calls.find(([channel]) => channel === 'ui:setMarkdownEditorFocused')?.[1]
|
|
setFocusedListener?.({ sender: webContents } as never, true)
|
|
|
|
const isDarwin = process.platform === 'darwin'
|
|
|
|
// Cmd+Shift+B is not in the policy allowlist, so no action resolves and no
|
|
// preventDefault fires — but the carve-out must not be what lets it through.
|
|
const shiftPreventDefault = vi.fn()
|
|
windowHandlers['before-input-event'](
|
|
{ preventDefault: shiftPreventDefault } as never,
|
|
{
|
|
type: 'keyDown',
|
|
code: 'KeyB',
|
|
key: 'B',
|
|
meta: isDarwin,
|
|
control: !isDarwin,
|
|
alt: false,
|
|
shift: true
|
|
} as never
|
|
)
|
|
expect(shiftPreventDefault).not.toHaveBeenCalled()
|
|
|
|
// Cmd+Alt+B is not a modifier chord in the policy (alt excluded), so the
|
|
// policy returns null and no preventDefault fires. Assert the carve-out
|
|
// is not what's short-circuiting this — it requires !alt.
|
|
const altPreventDefault = vi.fn()
|
|
windowHandlers['before-input-event'](
|
|
{ preventDefault: altPreventDefault } as never,
|
|
{
|
|
type: 'keyDown',
|
|
code: 'KeyB',
|
|
key: 'b',
|
|
meta: isDarwin,
|
|
control: !isDarwin,
|
|
alt: true,
|
|
shift: false
|
|
} as never
|
|
)
|
|
expect(altPreventDefault).not.toHaveBeenCalled()
|
|
})
|
|
|
|
it('coerces non-boolean setMarkdownEditorFocused payloads to false', () => {
|
|
const windowHandlers: Record<string, (...args: any[]) => void> = {}
|
|
const webContents = {
|
|
on: vi.fn((event, handler) => {
|
|
windowHandlers[event] = handler
|
|
}),
|
|
setZoomLevel: vi.fn(),
|
|
setBackgroundThrottling: vi.fn(),
|
|
invalidate: vi.fn(),
|
|
setWindowOpenHandler: vi.fn(),
|
|
send: vi.fn(),
|
|
isDevToolsOpened: vi.fn(),
|
|
openDevTools: vi.fn(),
|
|
closeDevTools: vi.fn()
|
|
}
|
|
const browserWindowInstance = {
|
|
webContents,
|
|
on: vi.fn(),
|
|
isDestroyed: vi.fn(() => false),
|
|
isMaximized: vi.fn(() => true),
|
|
isFullScreen: vi.fn(() => false),
|
|
getSize: vi.fn(() => [1200, 800]),
|
|
setSize: vi.fn(),
|
|
maximize: vi.fn(),
|
|
show: vi.fn(),
|
|
loadFile: vi.fn(() => Promise.resolve()),
|
|
loadURL: vi.fn(() => Promise.resolve())
|
|
}
|
|
browserWindowMock.mockImplementation(function () {
|
|
return browserWindowInstance
|
|
})
|
|
|
|
createMainWindow(null)
|
|
|
|
const setFocusedListener = vi
|
|
.mocked(ipcMain.on)
|
|
.mock.calls.find(([channel]) => channel === 'ui:setMarkdownEditorFocused')?.[1]
|
|
|
|
// Seed to true with a legitimate payload, then send a non-boolean and
|
|
// assert the flag returns to false by checking Cmd+B resumes interception.
|
|
setFocusedListener?.({ sender: webContents } as never, true)
|
|
setFocusedListener?.({ sender: webContents } as never, { malicious: true } as never)
|
|
|
|
const preventDefault = vi.fn()
|
|
const isDarwin = process.platform === 'darwin'
|
|
windowHandlers['before-input-event'](
|
|
{ preventDefault } as never,
|
|
{
|
|
type: 'keyDown',
|
|
code: 'KeyB',
|
|
key: 'b',
|
|
meta: isDarwin,
|
|
control: !isDarwin,
|
|
alt: false,
|
|
shift: false
|
|
} as never
|
|
)
|
|
|
|
expect(preventDefault).toHaveBeenCalledTimes(1)
|
|
expect(webContents.send).toHaveBeenCalledWith('ui:toggleLeftSidebar')
|
|
})
|
|
|
|
it('opens a table-aware context menu synchronously without a renderer query', () => {
|
|
const { webContents, windowHandlers, browserWindowInstance } = openMainWindowForContextMenu()
|
|
|
|
const tableTargetListener = vi
|
|
.mocked(ipcMain.on)
|
|
.mock.calls.find(([channel]) => channel === 'rich-markdown:context-target')?.[1]
|
|
tableTargetListener?.({ sender: webContents } as never, {
|
|
cellType: 'body',
|
|
targetId: 'table-target',
|
|
x: 42,
|
|
y: 84
|
|
})
|
|
windowHandlers['context-menu'](
|
|
{} as never,
|
|
{
|
|
x: 42,
|
|
y: 84,
|
|
isEditable: true,
|
|
formControlType: 'none',
|
|
spellcheckEnabled: true,
|
|
dictionarySuggestions: ['reference'],
|
|
misspelledWord: 'refrence'
|
|
} as Electron.ContextMenuParams
|
|
)
|
|
|
|
expect(buildFromTemplateMock).toHaveBeenCalledWith(
|
|
expect.arrayContaining([
|
|
expect.objectContaining({ label: 'reference' }),
|
|
expect.objectContaining({ label: 'Table' })
|
|
])
|
|
)
|
|
expect(menuPopupMock).toHaveBeenCalledWith({ window: browserWindowInstance, x: 42, y: 84 })
|
|
})
|
|
|
|
it('builds the plain edit menu for the one right-click the renderer marks as plain text', () => {
|
|
const { webContents, windowHandlers } = openMainWindowForContextMenu()
|
|
const params = {
|
|
x: 10,
|
|
y: 20,
|
|
isEditable: true,
|
|
formControlType: 'none',
|
|
dictionarySuggestions: ['reference'],
|
|
misspelledWord: 'refrence'
|
|
}
|
|
const plainTextListener: ((...args: any[]) => void) | undefined = vi
|
|
.mocked(ipcMain.on)
|
|
.mock.calls.find(([channel]) => channel === 'editable-context-menu:plain-text-target')?.[1]
|
|
|
|
plainTextListener?.({ sender: webContents })
|
|
windowHandlers['context-menu']({}, params)
|
|
windowHandlers['context-menu']({}, params)
|
|
|
|
expect(buildFromTemplateMock).toHaveBeenNthCalledWith(1, [
|
|
expect.objectContaining({ label: 'reference' }),
|
|
{ type: 'separator' },
|
|
expect.objectContaining({ label: 'Add to dictionary' }),
|
|
{ type: 'separator' },
|
|
{ role: 'cut' },
|
|
{ role: 'copy' },
|
|
expect.objectContaining({ label: 'Paste' }),
|
|
{ role: 'selectAll' }
|
|
])
|
|
expect(buildFromTemplateMock).toHaveBeenNthCalledWith(
|
|
2,
|
|
expect.arrayContaining([expect.objectContaining({ label: 'Format' })])
|
|
)
|
|
})
|
|
})
|