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 src/main/claude/claude-structured-control-actions.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,8 @@ describe('cancelClaudeTurn', () => {
{
clientMessageId: 'client-1',
state: 'rejected',
reason: 'provider_cancelled_before_start'
reason: 'provider_cancelled_before_start',
rejection: { kind: 'cancelled' }
}
]
])
Expand Down
3 changes: 2 additions & 1 deletion src/main/claude/claude-structured-queued-stop.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,8 @@ const WITHDRAWN = [
sessionId: 'session-1',
clientMessageId: 'client-b',
state: 'rejected',
reason: DISPATCH_REJECTED_CANCELLED
reason: DISPATCH_REJECTED_CANCELLED,
rejection: { kind: 'cancelled' }
}
]

Expand Down
3 changes: 2 additions & 1 deletion src/main/claude/claude-structured-real-cli.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -375,7 +375,8 @@ describe.skipIf(!realClaudeAvailable)('Claude structured real CLI handshake', ()
sessionId: 'real-cli-handshake',
clientMessageId: 'real-cli-queued-stop-b',
state: 'rejected',
reason: 'provider_cancelled_before_start'
reason: 'provider_cancelled_before_start',
rejection: { kind: 'cancelled' }
})
} finally {
await adapter.closeAll()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ export function requestTerminalLinkAction(
!event ||
!context ||
!(
isTerminalLinkActionActivation(event) ||
(isTerminalLinkActionActivation(event) && context.plainClickBehavior !== 'none') ||
(isTerminalMiddleClickActivation(event) && context.middleClickBehavior !== 'none')
) ||
!context.pointerGesture.canRequestAction(event)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,26 @@ describe('terminal link action routing', () => {
expect(run).toHaveBeenCalledOnce()
})

it('leaves disabled plain clicks with the terminal while middle click remains enabled', () => {
const context = actionContext()
context.plainClickBehavior = 'none'
context.middleClickBehavior = 'open'
const event = plainEvent()
const run = vi.fn()

expect(
requestTerminalLinkAction(event, context, {
destination: 'https://example.com',
kind: 'url',
primary: { label: 'Open', run }
})
).toBe(false)
expect(context.claimPtyMouse).not.toHaveBeenCalled()
expect(event.preventDefault).not.toHaveBeenCalled()
expect(context.request).not.toHaveBeenCalled()
expect(run).not.toHaveBeenCalled()
})

it('keeps middle click available when plain clicks stay with the terminal', () => {
const run = vi.fn()
const context = actionContext()
Expand Down
7 changes: 6 additions & 1 deletion tests/e2e/fixtures/daemon-generation-legacy-close-client.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { readFileSync } from 'node:fs'
import process from 'node:process'
import type { Store } from '../../../src/main/persistence'
import { OrcaRuntimeService } from '../../../src/main/runtime/orca-runtime'
import { RpcDispatcher } from '../../../src/main/runtime/rpc/dispatcher'
import { SESSION_TAB_METHODS } from '../../../src/main/runtime/rpc/methods/session-tabs'
import { SESSION_TAB_CLOSE_INTENT_RUNTIME_CAPABILITY } from '../../../src/shared/protocol-version'
import type { RuntimeMobileSessionTabsSnapshot } from '../../../src/shared/runtime-types'
import { createDesktopDiscoveredDaemonRouter } from './daemon-generation-desktop-discovery'
import { createDaemonGenerationProfileStore } from './daemon-generation-profile-store'

type FixtureSession = {
protocolVersion: number
Expand Down Expand Up @@ -126,6 +128,7 @@ async function waitForFinish(): Promise<void> {
async function main(): Promise<void> {
const config = readConfig()
const { router } = await createDesktopDiscoveredDaemonRouter(config)
let store: Store | null = null
try {
const outputBySessionId = new Map<string, string>()
router.onData((event) => {
Expand All @@ -149,7 +152,8 @@ async function main(): Promise<void> {
}
}

const runtime = new OrcaRuntimeService()
store = createDaemonGenerationProfileStore(config.cwd)
const runtime = new OrcaRuntimeService(store)
const calls: Record<string, unknown>[] = []
const sessionByTabId = new Map(config.sessions.map((session) => [session.tabId, session]))
runtime.setPtyController({
Expand Down Expand Up @@ -296,6 +300,7 @@ async function main(): Promise<void> {
} finally {
await router.disconnectOnly().catch(() => {})
router.dispose()
await store?.freezeWritesAsync()
}
}

Expand Down
14 changes: 14 additions & 0 deletions tests/e2e/fixtures/daemon-generation-profile-store.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import path from 'node:path'
import { installOrcadHostAdapters } from '../../../src/main/orcad/orcad-entry'
import type { Store } from '../../../src/main/persistence'
import { createProfileStateStore } from '../../../src/main/persistence/profile-state/profile-state-store-factory'

export function createDaemonGenerationProfileStore(directory: string): Store {
installOrcadHostAdapters()
// The bundled fixture has no profile writer worker; use the synchronous SQLite authority.
return createProfileStateStore({
dataFile: path.join(directory, 'orca-data.json'),
databaseFile: path.join(directory, 'profile-state.sqlite'),
profileId: 'legacy-close-fixture'
}).store
}
37 changes: 20 additions & 17 deletions tests/e2e/paired-client-hosted-browser-restart-survival.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,24 +112,22 @@ test('keeps a client-hosted browser tab across a paired runtime restart', async
})
.toContain(opened.remotePageId)

await expect
.poll(() => findMirroredBrowserPage(client!.page, restartedWorktreeId, fixture.origin), {
timeout: 180_000,
message: 'the client lost its client-hosted row across the runtime restart'
})
.not.toBeNull()

// Counted across the whole fixture origin: a recovery that also replays the create URL leaves
// two rows, and matching only the moved one would call that a pass.
const rows = await readClientBrowserRows(client.page, restartedWorktreeId)
const survivorRows = rows.filter((row) => row.url.startsWith(fixture.origin))
expect(survivorRows, 'the tab must survive the restart exactly once').toHaveLength(1)
let survivor: Awaited<ReturnType<typeof findMirroredBrowserPage>> = null
// Reconnection can replace the retained row between reads; wait for the whole contract.
await expect(async () => {
const rows = await readClientBrowserRows(client!.page, restartedWorktreeId)
const survivorRows = rows.filter((row) => row.url.startsWith(fixture.origin))
expect(survivorRows, 'the tab must survive the restart exactly once').toHaveLength(1)

const survivor = await findMirroredBrowserPage(client.page, restartedWorktreeId, fixture.origin)
expect(survivor?.remotePageId, 'recovery must keep the page identity it was created with').toBe(
opened.remotePageId
)
expect(survivor?.placementKind, 'the surviving tab must still be client-hosted').toBe('client')
survivor = await findMirroredBrowserPage(client!.page, restartedWorktreeId, fixture.origin)
expect(
survivor?.remotePageId,
'recovery must keep the page identity it was created with'
).toBe(opened.remotePageId)
expect(survivor?.placementKind, 'the surviving tab must still be client-hosted').toBe(
'client'
)
}).toPass({ timeout: 180_000 })

await focusClientBrowserRow(client.page, restartedWorktreeId, survivor!.localPageId)
expect(
Expand All @@ -140,6 +138,11 @@ test('keeps a client-hosted browser tab across a paired runtime restart', async
),
'the tab must come back where the user left it, not on its create URL'
).toBe('moved-on')
const renderedRows = await readClientBrowserRows(client.page, restartedWorktreeId)
expect(
renderedRows.filter((row) => row.url.startsWith(fixture.origin)),
'rendering the recovered guest must leave exactly one surviving tab'
).toHaveLength(1)
} finally {
if (client) {
await cleanupE2EDaemons(client.userDataDir).catch(() => undefined)
Expand Down
Loading