Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion config/scripts/ci-e2e-job-selection.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ import { selectPrE2eSpecs } from './pr-e2e-source-routing.mjs'
it.each([
'src/main/runtime/runtime-browser-commands-factory.ts',
'src/main/runtime/orca-runtime-get-status.ts',
'src/main/orcad/orcad-browser-startup.ts'
'src/main/orcad/orcad-browser-startup.ts',
'src/main/runtime/browser/browser-client-page-opening.ts'
])('routes the managed browser capability oracle from %s', (path) => {
expect(selectPrE2eSpecs([path])).toContain('tests/e2e/ssh-orcad-browser-capabilities.spec.ts')
})
Expand Down
2 changes: 1 addition & 1 deletion config/scripts/pr-e2e-source-routing.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ export const PR_E2E_SOURCE_ROUTES = [
specs: ['tests/e2e/ssh-orcad-browser-capabilities.spec.ts'],
matches: (file) =>
isProductSource(file) &&
/^src\/main\/(?:runtime\/(?:runtime-browser-commands-factory|orca-runtime-get-status)|host\/electron-browser-commands|orcad\/orcad-browser-)/.test(
/^src\/main\/(?:runtime\/(?:runtime-browser-commands-factory|orca-runtime-get-status|browser\/browser-client-page-opening)|host\/electron-browser-commands|orcad\/orcad-browser-)/.test(
file
)
},
Expand Down
7 changes: 6 additions & 1 deletion src/main/orcad/electron-serve-browser-process.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { z } from 'zod'
import type * as RunProcessModule from '@orca/process-host'
import type * as FsPromisesModule from 'node:fs/promises'
import type { RuntimeBrowserCommandHost } from '../runtime/browser/browser-command-contracts'
import { RuntimeBrowserPageRegistry } from '../runtime/runtime-browser-page-registry'

const { spawnProcessMock, lockedRemovalPaths } = vi.hoisted(() => ({
spawnProcessMock:
Expand Down Expand Up @@ -496,7 +497,11 @@ it('forwards tab commands through startup to the Electron sidecar', async () =>
const startup = startOrcadBrowserProvider({ userDataPath: harnessRoot })
await startup.ready
try {
const commands = createRuntimeBrowserCommands(host)
// The runtime that composes the provider owns a page registry, which startup consults.
const commands = createRuntimeBrowserCommands({
...host,
getRuntimeBrowserPageRegistry: () => new RuntimeBrowserPageRegistry()
})
await commands.browserTabCreate({ worktree: 'wt-a' })
await commands.browserTabList({ worktree: 'wt-a' })
expect((await browserRequests()).map((request) => request.method)).toEqual([
Expand Down
6 changes: 5 additions & 1 deletion src/main/orcad/external-chromium-browser-process.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import {
setRuntimeBrowserCommandsFactory
} from '../runtime/runtime-browser-commands-factory'
import type { RuntimeBrowserCommandHost } from '../runtime/browser/browser-command-contracts'
import { RuntimeBrowserPageRegistry } from '../runtime/runtime-browser-page-registry'

vi.mock('./orcad-browser-provider', () => ({ resolveOrcadBrowserProvider: vi.fn() }))
afterEach(() => setRuntimeBrowserCommandsFactory(null))
Expand Down Expand Up @@ -75,7 +76,10 @@ it('forwards tab commands through startup to the Chromium provider', async () =>
const startup = startOrcadBrowserProvider({ userDataPath: '/state' })
await startup.ready
try {
const host: RuntimeBrowserCommandHost = Object.create(null)
// The runtime that composes the provider owns a page registry, which startup consults.
const host: RuntimeBrowserCommandHost = Object.assign(Object.create(null), {
getRuntimeBrowserPageRegistry: () => new RuntimeBrowserPageRegistry()
})
const commands = createRuntimeBrowserCommands(host)
const params = { worktree: 'wt-a' }
createTab.mockResolvedValueOnce({ browserPageId: 'page-a' })
Expand Down
10 changes: 9 additions & 1 deletion src/main/orcad/orcad-browser-startup.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,15 @@ afterEach(() => {
setRuntimeBrowserUnavailableCause(null)
})

const host: RuntimeBrowserCommandHost = Object.create(null)
// No desktop hosts pages for this runtime, so only the engine can answer.
const host: RuntimeBrowserCommandHost = Object.assign(Object.create(null), {
getRuntimeBrowserPageRegistry: () => new RuntimeBrowserPageRegistry(),
getBrowserHostLeaseRegistry: () => ({
select: () => {
throw new Error('browser_host_unavailable')
}
})
})
function delayedProvider() {
const pending = Promise.withResolvers<OrcadBrowserProvider | null>()
vi.mocked(resolveOrcadBrowserProvider).mockReturnValueOnce(pending.promise)
Expand Down
38 changes: 15 additions & 23 deletions src/main/orcad/orcad-browser-startup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,12 @@ import {
browserUnavailableError,
setRuntimeBrowserCommandsFactory
} from '../runtime/runtime-browser-commands-factory'
import {
closeBrowserPageWithoutEngine,
closeRegisteredBrowserClientPage
} from '../runtime/browser/browser-client-page-close'
import {
resolveOrcadBrowserProvider,
type OrcadBrowserProvider,
type OrcadBrowserProviderOptions
} from './orcad-browser-provider'
import { orcadClientPageCommand } from './orcad-client-page-commands'

/** Browser discovery must not hold core RPC readiness hostage to a desktop authorization UI. */
export function startOrcadBrowserProvider(options: OrcadBrowserProviderOptions): {
Expand All @@ -24,6 +21,7 @@ export function startOrcadBrowserProvider(options: OrcadBrowserProviderOptions):
let provider: OrcadBrowserProvider | null = null
let startupError: unknown
let stopping: Promise<void> | undefined
const engineAvailable = (): boolean => !controller.signal.aborted && !!provider?.isAvailable()
setRuntimeBrowserCommandsFactory(
(host) => {
let commands: RuntimeBrowserCommands | undefined
Expand All @@ -34,42 +32,36 @@ export function startOrcadBrowserProvider(options: OrcadBrowserProviderOptions):
if (property === 'then' || typeof property !== 'string') {
return undefined
}
const forward = (args: unknown[]): unknown => {
if (controller.signal.aborted || !provider?.isAvailable()) {
const forward = (args: unknown[], name: string = property): unknown => {
if (!engineAvailable() || !provider) {
throw browserUnavailableError()
}
commands ??= provider.factory(host)
return callBrowserCommand(commands, property, args)
}
if (property === 'browserTabClose') {
// Why: client-hosted pages live in this runtime's registry, never in the provider.
return async (...args: Parameters<RuntimeBrowserCommands['browserTabClose']>) => {
const params = args[0] ?? {}
if (controller.signal.aborted || !provider?.isAvailable()) {
return closeBrowserPageWithoutEngine(host, params, browserUnavailableError)
}
return (await closeRegisteredBrowserClientPage(host, params)) ?? forward(args)
}
return callBrowserCommand(commands, name, args)
}
return (...args: unknown[]) => forward(args)
return (
orcadClientPageCommand(host, property, forward, engineAvailable) ??
((...args: unknown[]) => forward(args))
)
}
})
},
{ headless: true, isAvailable: () => !controller.signal.aborted && !!provider?.isAvailable() }
{
headless: true,
clientHostingWithoutEngine: true,
isAvailable: engineAvailable
}
)
const ready = Promise.resolve()
.then(() => resolveOrcadBrowserProvider({ ...options, signal: controller.signal }))
.then(
// Why the factory stays installed without a provider: it still serves desktop-hosted pages.
(resolved) => {
provider = resolved
if (!resolved && !controller.signal.aborted) {
setRuntimeBrowserCommandsFactory(null)
}
},
(error: unknown) => {
startupError = error
if (!controller.signal.aborted) {
setRuntimeBrowserCommandsFactory(null)
console.warn('[orcad] Browser startup failed:', error)
}
}
Expand Down
Loading
Loading