test(mobile): model capability client lifecycle in modal fixtures

This commit is contained in:
Neil
2026-10-21 01:52:49 -07:00
parent 68ef27653e
commit 554649e20a
3 changed files with 31 additions and 34 deletions
+25 -30
View File
@@ -84,8 +84,11 @@ function sourceInputs(renderer: ReactTestRenderer) {
)
}
// oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: the modal only sends requests.
const requestsOnly = (sendRequest: unknown) => ({ sendRequest }) as unknown as RpcClient
function requestsOnly(sendRequest: RpcClient['sendRequest']): RpcClient {
const client = new FakeSession('connected')
client.sendRequest.mockImplementation(sendRequest)
return client
}
function runTargetPicker(renderer: ReactTestRenderer) {
return renderer.root
@@ -122,7 +125,7 @@ describe('NewWorktreeModal project targets', () => {
}
return new Promise(() => {})
})
const client = { sendRequest } as unknown as RpcClient
const client = requestsOnly(sendRequest)
await act(async () => {
renderer = create(
@@ -161,8 +164,8 @@ describe('NewWorktreeModal project targets', () => {
upstream: { owner: 'stablyai', repo: 'orca' }
}
]
const client = {
sendRequest: vi.fn().mockImplementation((method: string) => {
const client = requestsOnly(
vi.fn().mockImplementation((method: string) => {
if (method === 'repo.list') {
return Promise.resolve({ ok: true, result: { repos: listedRepos } })
}
@@ -171,7 +174,7 @@ describe('NewWorktreeModal project targets', () => {
}
return new Promise(() => {})
})
} as unknown as RpcClient
)
await act(async () => {
renderer = create(
@@ -248,7 +251,7 @@ describe('NewWorktreeModal project targets', () => {
}
return Promise.resolve({ ok: true, result: {} })
})
const client = { sendRequest } as unknown as RpcClient
const client = requestsOnly(sendRequest)
await act(async () => {
renderer = create(
@@ -304,7 +307,7 @@ describe('NewWorktreeModal project targets', () => {
}
return Promise.resolve({ ok: true, result: {} })
})
const client = { sendRequest } as unknown as RpcClient
const client = requestsOnly(sendRequest)
await act(async () => {
renderer = create(
@@ -556,16 +559,16 @@ describe('NewWorktreeModal project targets', () => {
resolveOldList = resolve
})
const freshRepo = { ...repos[0]!, id: 'repo-fresh', displayName: 'fresh' }
const oldClient = {
sendRequest: vi.fn().mockImplementation((method: string) => {
const oldClient = requestsOnly(
vi.fn().mockImplementation((method: string) => {
if (method === 'repo.list') {
return oldList
}
return new Promise(() => {})
})
} as unknown as RpcClient
const freshClient = {
sendRequest: vi.fn().mockImplementation((method: string) => {
)
const freshClient = requestsOnly(
vi.fn().mockImplementation((method: string) => {
if (method === 'repo.list') {
return Promise.resolve({ ok: true, result: { repos: [freshRepo] } })
}
@@ -574,7 +577,7 @@ describe('NewWorktreeModal project targets', () => {
}
return new Promise(() => {})
})
} as unknown as RpcClient
)
const modalProps = {
visible: true,
hostId: 'host-1',
@@ -605,7 +608,7 @@ describe('NewWorktreeModal project targets', () => {
it('remounts before the reopened session renders, so no stale instance sees visible', async () => {
const sendRequest = vi.fn().mockImplementation(() => new Promise(() => {}))
const client = { sendRequest } as unknown as RpcClient
const client = requestsOnly(sendRequest)
const modalProps = {
client,
hostId: 'host-1',
@@ -630,8 +633,8 @@ describe('NewWorktreeModal project targets', () => {
// remounted the modal mid-edit and silently threw away the picked source.
it('keeps the picked source when a reconnect swaps the client for the same host', async () => {
const makeClient = () =>
({
sendRequest: vi.fn().mockImplementation((method: string) => {
requestsOnly(
vi.fn().mockImplementation((method: string) => {
if (method === 'repo.list') {
return Promise.resolve({ ok: true, result: { repos } })
}
@@ -640,7 +643,7 @@ describe('NewWorktreeModal project targets', () => {
}
return new Promise(() => {})
})
}) as unknown as RpcClient
)
const modalProps = {
visible: true,
hostId: 'host-1',
@@ -681,9 +684,7 @@ describe('NewWorktreeModal project targets', () => {
it('keeps the form when a host switch renders but never commits', async () => {
setCachedRepos('host-2', repos)
const client = {
sendRequest: vi.fn().mockImplementation(() => new Promise(() => {}))
} as unknown as RpcClient
const client = requestsOnly(vi.fn().mockImplementation(() => new Promise(() => {})))
const modalProps = { visible: true, client, onCreated: () => {}, onClose: () => {} }
let frozen = false
const tree = (hostId: string) =>
@@ -708,9 +709,7 @@ describe('NewWorktreeModal project targets', () => {
})
it('keeps the form when a close renders but never commits', async () => {
const client = {
sendRequest: vi.fn().mockImplementation(() => new Promise(() => {}))
} as unknown as RpcClient
const client = requestsOnly(vi.fn().mockImplementation(() => new Promise(() => {})))
const modalProps = {
client,
hostId: 'host-1',
@@ -744,9 +743,7 @@ describe('NewWorktreeModal project targets', () => {
it('starts a fresh form session when the modal switches hosts', async () => {
setCachedRepos('host-2', repos)
const client = {
sendRequest: vi.fn().mockImplementation(() => new Promise(() => {}))
} as unknown as RpcClient
const client = requestsOnly(vi.fn().mockImplementation(() => new Promise(() => {})))
const modalProps = { visible: true, client, onCreated: () => {}, onClose: () => {} }
await act(async () => {
@@ -762,9 +759,7 @@ describe('NewWorktreeModal project targets', () => {
})
it('starts with fresh form state after closing and reopening', async () => {
const client = {
sendRequest: vi.fn().mockImplementation(() => new Promise(() => {}))
} as unknown as RpcClient
const client = requestsOnly(vi.fn().mockImplementation(() => new Promise(() => {})))
const modalProps = {
client,
hostId: 'host-1',
@@ -64,7 +64,9 @@ describe('capability probe recovery', () => {
const host = connection()
host.sendRequest.mockRejectedValueOnce(new Error('timeout'))
await render(host.client)
await act(async () => vi.advanceTimersByTimeAsync(1_000))
await act(async () => {
await vi.advanceTimersByTimeAsync(1_000)
})
expect(current?.tasksSupported).toBe(true)
})
@@ -97,7 +97,7 @@ type ClientVisibleOutcome = {
probedCapabilities: readonly string[] | null
answeredProbe: boolean
quickCommandsSupported: boolean
worktreeCreateSupport: NewWorktreeRuntimeCapabilities
worktreeCreateSupport: NewWorktreeRuntimeCapabilities | null
}
/** Every released path that reads status.get, plus the states derived from what it published. */
@@ -160,8 +160,8 @@ describe('mobileWeb.bundle.v1 on a released client', () => {
expect(advertised.gateStatus).not.toBeNull()
expect(advertised.probedCapabilities).toContain(MOBILE_WEB_BUNDLE_CAPABILITY)
expect(advertised.quickCommandsSupported).toBe(true)
expect(advertised.worktreeCreateSupport.tasksSupported).toBe(true)
expect(advertised.worktreeCreateSupport.worktreeCreateIdempotency).not.toBe(false)
expect(advertised.worktreeCreateSupport?.tasksSupported).toBe(true)
expect(advertised.worktreeCreateSupport?.worktreeCreateIdempotency).not.toBe(false)
expect(oldDesktop.gates.hostCapabilities).not.toContain(MOBILE_WEB_BUNDLE_CAPABILITY)
expect(asIfNeverAdvertised(advertised)).toEqual(oldDesktop)