test: remove low-value host caller and UI tests (#42465)

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
This commit is contained in:
OrcaWin
2026-11-14 11:30:47 -08:00
committed by GitHub
co-authored by OrcaWin
parent aee2f8abe9
commit 7348dca919
11 changed files with 7 additions and 745 deletions
File diff suppressed because one or more lines are too long
@@ -4097,8 +4097,6 @@
"src/renderer/src/components/NewWorkspaceComposerCard.set-location.test.tsx": 1301,
"src/renderer/src/components/SelectedTextCopyMenu.test.tsx": 329,
"src/renderer/src/components/UpdateCard.error-card.test.tsx": 2851,
"src/renderer/src/components/UpdateCard.test.ts": 694,
"src/renderer/src/components/WorktreeBaseFallbackDialog.test.tsx": 2104,
"src/renderer/src/components/activity/activity-auto-mark-read-loop.react185.test.tsx": 5607,
"src/renderer/src/components/activity/activity-clear-completed-hmr.test.ts": 814,
"src/renderer/src/components/activity/activity-clear-completed-state-lifetime.test.ts": 4609,
@@ -47,14 +47,6 @@ describe('orchestration dispatch coordinator handle', () => {
json
} as never)
const invokeRun = (flags: Map<string, string | boolean>) =>
ORCHESTRATION_HANDLERS['orchestration coordinator-start']({
flags,
client: { call: callMock },
cwd: '/tmp/repo',
json: true
} as never)
it('rejects stale coordinator env handles when the caller pane cannot be proven', async () => {
process.env.ORCA_TERMINAL_HANDLE = 'term_stale_coord'
callMock.mockRejectedValueOnce(staleHandleError())
@@ -74,51 +66,4 @@ describe('orchestration dispatch coordinator handle', () => {
expect(callMock).toHaveBeenCalledTimes(1)
expect(getTerminalHandleMock).not.toHaveBeenCalled()
})
it('retires the legacy coordinator command without runtime effects', async () => {
await expect(
invokeRun(new Map<string, string | boolean>([['spec', 'run the plan']]))
).rejects.toMatchObject({
code: 'orchestration_migration_required',
data: {
reason: 'command_retired',
effectsApplied: false,
nextCommandArgs: ['skills', 'get', 'orchestration', '--full']
}
})
expect(callMock).not.toHaveBeenCalled()
})
})
describe('orchestration task-create caller handle', () => {
beforeEach(() => {
callMock.mockReset()
getTerminalHandleMock.mockReset()
delete process.env.ORCA_TERMINAL_HANDLE
delete process.env.ORCA_PANE_KEY
})
const invokeTaskCreate = (flags: Map<string, string | boolean>) =>
ORCHESTRATION_HANDLERS['orchestration task-create']({
flags,
client: { call: callMock },
cwd: '/tmp/repo',
json: true
} as never)
it('fails closed when a stale task creator handle cannot be reminted', async () => {
process.env.ORCA_TERMINAL_HANDLE = 'term_stale'
callMock.mockRejectedValueOnce(staleHandleError())
getTerminalHandleMock.mockResolvedValue('term_wrong_active')
await expect(
invokeTaskCreate(new Map<string, string | boolean>([['spec', 'do work']]))
).rejects.toMatchObject({ code: 'no_active_sender_terminal' })
expect(callMock).toHaveBeenNthCalledWith(1, 'terminal.resolveIdentity', {
terminal: 'term_stale'
})
expect(getTerminalHandleMock).not.toHaveBeenCalled()
expect(callMock).toHaveBeenCalledTimes(1)
})
})
@@ -1,139 +0,0 @@
// Inherited pane identity and an available sibling must never override the injected session caller.
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
const callMock = vi.hoisted(() => vi.fn())
const getTerminalHandleMock = vi.hoisted(() => vi.fn())
vi.mock('../../output/command-result-output', () => ({ printResult: vi.fn() }))
vi.mock('../../terminals/terminal-selector', () => ({ getTerminalHandle: getTerminalHandleMock }))
import { ORCHESTRATION_HANDLERS } from '../orchestration-handlers'
import { findCommandSpec } from '../../arguments/args'
import { COMMAND_SPECS } from '../../specs/index'
import { refuseConflictingSessionCallerFlags } from './session-caller-flags'
import { createOrchestrationCompatibilityEnvelope } from './orchestration-compatibility-envelope'
const SESSION = 'f7a1c0de-1111-4222-8333-444455556666'
const IDENTITY_ENV = [
'ORCA_AGENT_SESSION_ID',
'ORCA_TERMINAL_HANDLE',
'ORCA_PANE_KEY',
'ORCA_STRUCTURED_SESSION'
] as const
const originalEnv = Object.fromEntries(IDENTITY_ENV.map((name) => [name, process.env[name]]))
const RESULT = {
result: {
run: { id: 'run_1', objective: 'o', consumer_generation: 1 },
runs: [],
nextCursor: null,
messages: [],
count: 0,
message: { id: 'msg_1' },
lifecycle: { action: 'completed' },
dispatch: { id: 'dispatch_1', task_id: 'task_1', status: 'dispatched' },
gate: { id: 'gate_1', task_id: 'task_1', status: 'pending', resolution: 'r' },
gates: [],
task: { id: 'task_1', status: 'pending' },
tasks: [],
answer: 'yes',
messageId: 'msg_1',
threadId: 'thread_1',
timedOut: false,
state: 'ready',
runId: 'run_1',
taskId: 'task_1',
dispatchId: 'dispatch_1',
effects: [],
residualResources: [],
workers: [],
counts: {}
}
}
/** dispatch-show is excluded: its --from fills preview text rather than naming the caller. */
function flagMap(flags: Record<string, string | true>): Map<string, string | boolean> {
return new Map(Object.entries(flags))
}
async function invoke(
command: string,
flags: Map<string, string | boolean>,
json = true
): Promise<void> {
const handler = ORCHESTRATION_HANDLERS[`orchestration ${command}`]
if (!handler) {
throw new Error(`no handler for ${command}`)
}
refuseConflictingSessionCallerFlags(
findCommandSpec(COMMAND_SPECS, ['orchestration', command]),
flags
)
await handler({
flags,
// oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: these handlers read only `call`; RuntimeClient is a class, so a structural double cannot satisfy it without the cast.
client: { call: callMock } as never,
cwd: '/tmp/repo',
json
})
}
function setEnv(env: Partial<Record<(typeof IDENTITY_ENV)[number], string>>): void {
for (const name of IDENTITY_ENV) {
const value = env[name]
if (value === undefined) {
delete process.env[name]
} else {
process.env[name] = value
}
}
}
beforeEach(() => {
callMock.mockReset().mockResolvedValue(RESULT)
getTerminalHandleMock.mockReset().mockResolvedValue('term_sibling')
vi.spyOn(console, 'log').mockImplementation(() => {})
vi.spyOn(console, 'error').mockImplementation(() => {})
})
afterEach(() => {
vi.restoreAllMocks()
setEnv(originalEnv)
process.exitCode = undefined
})
describe('the identity a session presents', () => {
it('keeps the identity-less refusal, without --from advice, for a child that has no id', async () => {
setEnv({ ORCA_STRUCTURED_SESSION: '1' })
await expect(invoke('reply', flagMap({ id: 'msg_1', body: 'b' }))).rejects.toMatchObject({
code: 'no_active_sender_terminal',
message: expect.not.stringContaining('Pass --from')
})
expect(getTerminalHandleMock).not.toHaveBeenCalled()
expect(callMock).not.toHaveBeenCalled()
})
})
describe('the orchestration envelope', () => {
it('carries the injected id beside whatever terminal evidence the process also has', () => {
const envelope = createOrchestrationCompatibilityEnvelope({
ORCA_AGENT_SESSION_ID: ` ${SESSION} `,
ORCA_TERMINAL_HANDLE: 'term_inherited_pane'
})
expect(envelope.orchestrationCompatibilityEvidence).toEqual({
terminalHandle: 'term_inherited_pane',
agentSessionId: SESSION
})
})
it('claims no session without an injected id', () => {
expect(
createOrchestrationCompatibilityEnvelope({ ORCA_AGENT_SESSION_ID: ' ' })
.orchestrationCompatibilityEvidence
).toBeUndefined()
})
})
@@ -1,43 +0,0 @@
import { mkdtempSync } from 'node:fs'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import { afterEach, describe, expect, it, vi } from 'vitest'
import type * as BrowserClientHostIdModule from './browser-client-host-id'
function profileDirectory(): string {
return mkdtempSync(join(tmpdir(), 'orca-host-id-'))
}
async function freshModule(): Promise<typeof BrowserClientHostIdModule> {
vi.resetModules()
return await import('./browser-client-host-id')
}
afterEach(() => {
vi.restoreAllMocks()
vi.resetModules()
})
describe('browser client host id', () => {
it('answers the same durable id to every launch of one profile', async () => {
const directory = profileDirectory()
const first = await freshModule()
first.initializeBrowserClientHostId(directory)
const second = await freshModule()
second.initializeBrowserClientHostId(directory)
expect(first.getBrowserClientHostId()).toBe(second.getBrowserClientHostId())
})
it('keeps the id a renderer already holds when startup resolves the profile too late', async () => {
const warn = vi.spyOn(console, 'warn').mockImplementation(() => {})
const module = await freshModule()
const stamped = module.getBrowserClientHostId()
module.initializeBrowserClientHostId(profileDirectory())
expect(module.getBrowserClientHostId()).toBe(stamped)
expect(warn).toHaveBeenCalled()
})
})
@@ -84,37 +84,6 @@ describe('PairedRuntimeBrowserClientHostComposition', () => {
expect(rig.routes.reconnect).toHaveBeenCalledTimes(2)
expect(rig.executor.handle).toHaveBeenCalledOnce()
})
it('fails closed without racing page cleanup when handlers do not settle', async () => {
const rig = createRig({ hostSettled: false })
const composition = rig.createComposition()
await composition.start()
await expect(composition.close()).resolves.toBe(false)
expect(rig.order).toEqual([
'activate-routes',
'closing',
'suspend-routes',
'fence-navigation',
'close-host',
'close-routes'
])
expect(rig.executor.close).not.toHaveBeenCalled()
rig.settleHandlers()
await composition.whenClosed()
expect(rig.order).toEqual([
'activate-routes',
'closing',
'suspend-routes',
'fence-navigation',
'close-host',
'close-routes',
'close-executor'
])
})
})
function createRig(
@@ -33,41 +33,6 @@ afterEach(() => {
})
describe('PairedRuntimeBrowserClientHost reconnect', () => {
it('reattaches once with the refreshed unavailable-page inventory', async () => {
const attempts = mockAttempts()
let state: 'active' | 'outcomeUnknown' = 'active'
const host = new PairedRuntimeBrowserClientHost({
connection: pairedRuntimeConnectionFromOffer(pairing),
authorityRuntimeId: 'runtime-a',
browserHostClientId: 'host-a',
hostCapabilities: ['webview'],
handler: vi.fn(() => ({ status: 'completed' as const })),
getPageInventory: () => [inventoryPage(state)],
pageReconciliationProtocolVersion: 1,
reconnectRetryDelayMs: 1
})
const starting = host.start()
await vi.waitFor(() => expect(attempts).toHaveLength(1))
expect(subscribeRemoteRuntimeRequestMock.mock.calls[0]?.[2]).toMatchObject({
pageInventory: [expect.objectContaining({ state: 'active' })]
})
attempts[0]!.callbacks.onResponse(readyResponse({ reconciliation: true }))
await starting
state = 'outcomeUnknown'
const refreshing = host.refreshPageInventory()
await vi.waitFor(() => expect(attempts).toHaveLength(2))
expect(attempts[0]!.close).toHaveBeenCalledOnce()
expect(subscribeRemoteRuntimeRequestMock.mock.calls[1]?.[2]).toMatchObject({
pageInventory: [expect.objectContaining({ state: 'outcomeUnknown' })]
})
attempts[1]!.callbacks.onResponse(readyResponse({ reconciliation: true }))
await expect(refreshing).resolves.toBeUndefined()
expect(attempts).toHaveLength(2)
await host.close()
})
it('replays a completed mutation without executing its handler twice', async () => {
const attempts = mockAttempts()
const handler = vi.fn(() => ({ status: 'completed' as const }))
@@ -139,7 +104,7 @@ function mockAttempts(): {
return attempts
}
function readyResponse(options: { reconciliation?: boolean } = {}) {
function readyResponse() {
return {
id: 'browser-host',
ok: true as const,
@@ -149,27 +114,12 @@ function readyResponse(options: { reconciliation?: boolean } = {}) {
browserHostGeneration: 4,
pageCommandProtocolVersion: 1 as const,
pageInventoryProtocolVersion: 1 as const,
leaseReconnectProtocolVersion: 1 as const,
...(options.reconciliation ? { pageReconciliationProtocolVersion: 1 as const } : {})
leaseReconnectProtocolVersion: 1 as const
},
_meta: { runtimeId: 'runtime-a' }
}
}
function inventoryPage(state: 'active' | 'outcomeUnknown') {
return {
authorityRuntimeId: 'runtime-a',
authorityEpoch: 'epoch-a',
browserHostClientId: 'host-a',
browserHostGeneration: 4,
browserPageId: 'page-a',
pageHostGeneration: 1,
browserProfileId: 'default',
executionHostKey: 'native:runtime-a:1',
state
} as const
}
function commandResponse() {
return {
id: 'browser-host',
@@ -1,232 +0,0 @@
import type { subscribeRemoteRuntimeTransport } from '../../../../shared/remote-runtime/requests/remote-runtime-subscription-transport'
import { pairedRuntimeConnectionFromOffer } from '../../../../shared/remote-runtime/pairing/paired-runtime-connection'
import { afterEach, describe, expect, it, vi } from 'vitest'
import type { PairingOffer } from '../../../../shared/remote-runtime/pairing/mobile-relay-pairing-offer'
import type {
RemoteRuntimeTransportSubscription,
RemoteRuntimeTransportSubscriptionCallbacks
} from '../../../../shared/remote-runtime/requests/remote-runtime-subscription-contract'
const { subscribeRemoteRuntimeRequestMock } = vi.hoisted(() => ({
subscribeRemoteRuntimeRequestMock: vi.fn()
}))
vi.mock('../../../../shared/remote-runtime/requests/remote-runtime-subscription-transport', () => ({
subscribeRemoteRuntimeTransport: subscribeRemoteRuntimeRequestMock
}))
import { PairedRuntimeBrowserHostLease } from '../leases/paired-runtime-browser-host-lease'
import type { PairedRuntimeBrowserHostLeaseOptions } from '../leases/paired-runtime-browser-host-lease-options'
const pairing = {
v: 2,
endpoint: 'ws://127.0.0.1:6768',
deviceToken: 'device-token',
publicKeyB64: 'public-key',
pairedDeviceId: 'device-a',
scope: 'runtime'
} as PairingOffer
afterEach(() => {
subscribeRemoteRuntimeRequestMock.mockReset()
vi.restoreAllMocks()
})
describe('paired runtime browser host command admission', () => {
it('rejects page commands when an old host did not negotiate them', async () => {
const { callbacks, close } = await subscribeLease({
sendRequest: undefined
})
const onError = vi.fn()
const onPageCommand = vi.fn()
const lease = createLease({
pageCommandProtocolVersion: 1,
onPageCommand,
onError
})
const starting = lease.start()
await vi.waitFor(() => expect(callbacks.current).toBeDefined())
callbacks.current!.onResponse({
id: 'browser-host',
ok: true,
result: {
type: 'ready',
authorityEpoch: 'epoch-a',
browserHostGeneration: 4
},
_meta: { runtimeId: 'runtime-a' }
})
await starting
callbacks.current!.onResponse({
id: 'browser-host',
ok: true,
result: {
type: 'command',
pageCommandProtocolVersion: 1,
authorityRuntimeId: 'runtime-a',
authorityEpoch: 'epoch-a',
browserHostClientId: 'host-a',
browserHostGeneration: 4,
browserPageId: 'page-a',
pageHostGeneration: 1,
commandSequence: 1,
commandId: 'command-a',
command: {
type: 'createPage',
browserProfileId: 'default',
executionHostKey: 'native:runtime-a:1'
}
},
_meta: { runtimeId: 'runtime-a' }
})
await vi.waitFor(() => expect(close).toHaveBeenCalledOnce())
expect(onPageCommand).not.toHaveBeenCalled()
expect(onError).toHaveBeenCalledWith(
expect.objectContaining({
message: 'Unnegotiated browser host page command'
})
)
})
it.each([
['authorityEpoch', { authorityEpoch: 'epoch-b' }],
['authorityRuntimeId', { authorityRuntimeId: 'runtime-b' }],
['browserHostClientId', { browserHostClientId: 'host-b' }],
['browserHostGeneration', { browserHostGeneration: 5 }]
])(
'rejects a negotiated command whose %s is stale before delivery',
async (_field, staleness) => {
const { callbacks, close } = await subscribeLease()
const onError = vi.fn()
const onPageCommand = vi.fn()
const lease = createLease({
pageCommandProtocolVersion: 1,
onPageCommand,
onError
})
const starting = lease.start()
await vi.waitFor(() => expect(callbacks.current).toBeDefined())
callbacks.current!.onResponse({
id: 'browser-host',
ok: true,
result: {
type: 'ready',
authorityEpoch: 'epoch-a',
browserHostGeneration: 4,
pageCommandProtocolVersion: 1
},
_meta: { runtimeId: 'runtime-a' }
})
await starting
callbacks.current!.onResponse({
id: 'browser-host',
ok: true,
result: {
type: 'command',
pageCommandProtocolVersion: 1,
authorityRuntimeId: 'runtime-a',
authorityEpoch: 'epoch-a',
browserHostClientId: 'host-a',
browserHostGeneration: 4,
browserPageId: 'page-a',
pageHostGeneration: 1,
commandSequence: 1,
commandId: 'command-a',
command: {
type: 'createPage',
browserProfileId: 'default',
executionHostKey: 'native:runtime-a:1'
},
...staleness
},
_meta: { runtimeId: 'runtime-a' }
})
await vi.waitFor(() => expect(close).toHaveBeenCalledOnce())
expect(onPageCommand).not.toHaveBeenCalled()
expect(onError).toHaveBeenCalledWith(
expect.objectContaining({ message: 'Stale browser host page command' })
)
}
)
it('refuses an inventory refresh on a lease that never negotiated reconnect', async () => {
const { callbacks, close } = await subscribeLease()
const lease = createLease({
pageInventoryProtocolVersion: 1,
getPageInventory: () => []
})
const starting = lease.start()
await vi.waitFor(() => expect(callbacks.current).toBeDefined())
callbacks.current!.onResponse({
id: 'browser-host',
ok: true,
result: {
type: 'ready',
authorityEpoch: 'epoch-a',
browserHostGeneration: 4,
pageInventoryProtocolVersion: 1
},
_meta: { runtimeId: 'runtime-a' }
})
await starting
await expect(lease.refreshPageInventory()).rejects.toThrow(
'Browser host inventory refresh is unavailable'
)
expect(close).not.toHaveBeenCalled()
await lease.close()
})
})
function createLease(
overrides: Partial<PairedRuntimeBrowserHostLeaseOptions> = {}
): PairedRuntimeBrowserHostLease {
return new PairedRuntimeBrowserHostLease({
connection: pairedRuntimeConnectionFromOffer(pairing),
authorityRuntimeId: 'runtime-a',
browserHostClientId: 'host-a',
hostCapabilities: ['webview'],
...overrides
})
}
async function subscribeLease(options?: {
sendRequest?: RemoteRuntimeTransportSubscription['sendRequest']
}): Promise<{
callbacks: { current?: RemoteRuntimeTransportSubscriptionCallbacks }
close: ReturnType<typeof vi.fn>
}> {
const callbacks: { current?: RemoteRuntimeTransportSubscriptionCallbacks } = {}
const close = vi.fn()
const sendRequest =
options && 'sendRequest' in options
? options.sendRequest
: vi.fn().mockResolvedValue({
id: 'command-result',
ok: true,
result: { accepted: true },
_meta: { runtimeId: 'runtime-a' }
})
subscribeRemoteRuntimeRequestMock.mockImplementationOnce(
async (
...args: Parameters<typeof subscribeRemoteRuntimeTransport>
): Promise<RemoteRuntimeTransportSubscription> => {
callbacks.current = args[4]
return {
requestId: 'browser-host',
close,
sendBinary: () => false,
...(sendRequest
? {
sendRequest: sendRequest
}
: {})
}
}
)
return { callbacks, close }
}
@@ -1,120 +0,0 @@
import { createStore, type StoreApi } from 'zustand/vanilla'
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import type { UpdateStatus } from '../../../shared/update-status-types'
import { createUISlice } from '../store/slices/ui/create-ui-slice'
import type { AppState } from '../store/types'
import { isUpdateCardVisible } from './maintenance/update-card/update-card-visibility'
function createTestStore(): StoreApi<AppState> {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
return createStore<any>()((...args: any[]) => ({
repos: [],
rightSidebarWidth: 280,
...createUISlice(...(args as Parameters<typeof createUISlice>))
})) as unknown as StoreApi<AppState>
}
function setState(store: StoreApi<AppState>, status: UpdateStatus): void {
store.getState().setUpdateStatus(status)
}
beforeEach(() => {
vi.stubGlobal('window', {
api: {
ui: { set: vi.fn().mockResolvedValue(undefined) },
shell: { openUrl: vi.fn() },
updater: {
download: vi.fn().mockResolvedValue(undefined),
quitAndInstall: vi.fn().mockResolvedValue(undefined),
dismissNudge: vi.fn().mockResolvedValue(undefined),
dismissAvailableUpdate: vi.fn().mockResolvedValue(undefined)
}
},
matchMedia: vi.fn().mockReturnValue({
matches: false,
addEventListener: vi.fn(),
removeEventListener: vi.fn()
})
})
})
afterEach(() => {
vi.unstubAllGlobals()
})
describe('updateCardCollapsed', () => {
it('resets to false on every state transition so new phases re-surface', () => {
const store = createTestStore()
setState(store, { state: 'downloading', percent: 20, version: '1.2.0' })
store.getState().setUpdateCardCollapsed(true)
expect(store.getState().updateCardCollapsed).toBe(true)
// Why: percent-only updates are not transitions and must not reset.
setState(store, { state: 'downloading', percent: 50, version: '1.2.0' })
expect(store.getState().updateCardCollapsed).toBe(true)
setState(store, { state: 'downloaded', version: '1.2.0' })
expect(store.getState().updateCardCollapsed).toBe(false)
})
})
// Exercise visibility gates without a full DOM so irrelevant cards stay hidden and real updates remain visible.
type VisibilityInput = {
status: UpdateStatus
dismissedVersion: string | null
cachedVersion: string | null
updateUserInitiatedCycle?: boolean
collapsed?: boolean
}
type VisibilityResult = 'hidden' | 'visible'
function computeVisibility(input: VisibilityInput): VisibilityResult {
return isUpdateCardVisible({
...input,
updateUserInitiatedCycle: input.updateUserInitiatedCycle ?? false
})
? 'visible'
: 'hidden'
}
describe('UpdateCard visibility gates', () => {
it('hides check errors once a new checking cycle cleared the cached version', () => {
const store = createTestStore()
setState(store, { state: 'available', version: '1.2.0', changelog: null })
setState(store, { state: 'checking' })
setState(store, { state: 'error', message: 'network timeout' })
expect(
computeVisibility({
status: store.getState().updateStatus,
collapsed: store.getState().updateCardCollapsed,
dismissedVersion: '1.2.0',
cachedVersion: null
})
).toBe('hidden')
})
})
describe('full update lifecycle through setUpdateStatus', () => {
it('dismiss → new version cycle → card visible again', () => {
const store = createTestStore()
setState(store, { state: 'available', version: '1.2.0', changelog: null })
store.getState().dismissUpdate()
expect(store.getState().dismissedUpdateVersion).toBe('1.2.0')
setState(store, { state: 'checking' })
setState(store, { state: 'available', version: '1.3.0', changelog: null })
expect(
computeVisibility({
status: store.getState().updateStatus,
dismissedVersion: store.getState().dismissedUpdateVersion,
cachedVersion: '1.3.0'
})
).toBe('visible')
})
})
@@ -1,61 +0,0 @@
// @vitest-environment happy-dom
import { act } from 'react'
import { createRoot, type Root } from 'react-dom/client'
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import { useAppStore } from '@/store'
import type { AppState } from '@/store/types'
import WorktreeBaseFallbackDialog from './WorktreeBaseFallbackDialog'
import {
requestWorktreeBaseFallbackNotice,
resetWorktreeBaseFallbackNoticesForTests
} from './worktree-base-fallback-notice'
const initialState = useAppStore.getInitialState()
let root: Root | null = null
async function renderDialog(activeModal: AppState['activeModal'] = 'none'): Promise<void> {
useAppStore.setState({
activeModal,
setContextualToursBlockingSurfaceVisible: vi.fn()
})
const container = document.createElement('div')
document.body.appendChild(container)
root = createRoot(container)
await act(async () => {
root?.render(<WorktreeBaseFallbackDialog />)
})
}
describe('WorktreeBaseFallbackDialog', () => {
beforeEach(() => {
useAppStore.setState(initialState, true)
resetWorktreeBaseFallbackNoticesForTests()
})
afterEach(async () => {
await act(async () => {
root?.unmount()
})
root = null
document.body.innerHTML = ''
resetWorktreeBaseFallbackNoticesForTests()
useAppStore.setState(initialState, true)
})
it('waits until another app modal closes', async () => {
requestWorktreeBaseFallbackNotice({
requestedRef: 'origin/main',
localRef: 'main'
})
await renderDialog('new-workspace-composer')
expect(document.body.textContent).not.toContain('Workspace created from a local base')
await act(async () => {
useAppStore.setState({ activeModal: 'none' })
})
expect(document.body.textContent).toContain('Workspace created from a local base')
})
})
@@ -28,8 +28,3 @@ export function subscribeWorktreeBaseFallbackNotice(listener: () => void): () =>
listeners.add(listener)
return () => listeners.delete(listener)
}
export function resetWorktreeBaseFallbackNoticesForTests(): void {
notices.length = 0
notifyListeners()
}