test(mobile): centralize renderer setup (#13408)
This commit is contained in:
parent
954de38507
commit
c56ff58dd7
|
|
@ -157,28 +157,12 @@ const RESET_SNAPSHOT = {
|
|||
}
|
||||
} as const
|
||||
|
||||
function suppressReactTestRendererDeprecationWarning(): () => void {
|
||||
const originalConsoleError = console.error
|
||||
const spy = vi.spyOn(console, 'error').mockImplementation((...args) => {
|
||||
if (typeof args[0] === 'string' && args[0].includes('react-test-renderer is deprecated')) {
|
||||
return
|
||||
}
|
||||
originalConsoleError(...args)
|
||||
})
|
||||
return () => spy.mockRestore()
|
||||
}
|
||||
|
||||
async function renderAccountsRoute(): Promise<ReactTestRenderer> {
|
||||
let renderer: ReactTestRenderer | null = null
|
||||
const restoreConsoleError = suppressReactTestRendererDeprecationWarning()
|
||||
try {
|
||||
await act(async () => {
|
||||
renderer = create(createElement(AccountsScreen))
|
||||
await Promise.resolve()
|
||||
})
|
||||
} finally {
|
||||
restoreConsoleError()
|
||||
}
|
||||
await act(async () => {
|
||||
renderer = create(createElement(AccountsScreen))
|
||||
await Promise.resolve()
|
||||
})
|
||||
if (!renderer) {
|
||||
throw new Error('Accounts route did not render')
|
||||
}
|
||||
|
|
@ -229,7 +213,6 @@ describe('accounts route Codex reset credit', () => {
|
|||
let storedValues: Map<string, string>
|
||||
|
||||
beforeEach(() => {
|
||||
globalThis.IS_REACT_ACT_ENVIRONMENT = true
|
||||
resetCodexResetAttemptJournalForTests()
|
||||
storedValues = new Map()
|
||||
dependencies.alert.mockReset()
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { createElement } from 'react'
|
||||
import { act, create, type ReactTestRenderer } from 'react-test-renderer'
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest'
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
import { CodexResetCreditAction } from './CodexResetCreditAction'
|
||||
|
||||
vi.mock('react-native', () => ({
|
||||
|
|
@ -19,35 +19,19 @@ const summary = {
|
|||
expiryLabel: 'Expires in 5d'
|
||||
}
|
||||
|
||||
function suppressRendererWarning(): () => void {
|
||||
const original = console.error
|
||||
const spy = vi.spyOn(console, 'error').mockImplementation((...args) => {
|
||||
if (typeof args[0] === 'string' && args[0].includes('react-test-renderer is deprecated')) {
|
||||
return
|
||||
}
|
||||
original(...args)
|
||||
})
|
||||
return () => spy.mockRestore()
|
||||
}
|
||||
|
||||
function renderAction(busy: boolean, disabled: boolean): ReactTestRenderer {
|
||||
let renderer: ReactTestRenderer | null = null
|
||||
const restore = suppressRendererWarning()
|
||||
try {
|
||||
act(() => {
|
||||
renderer = create(
|
||||
createElement(CodexResetCreditAction, {
|
||||
summary,
|
||||
scopeLabel: 'dev@example.com on the host',
|
||||
busy,
|
||||
disabled,
|
||||
onPress: vi.fn()
|
||||
})
|
||||
)
|
||||
})
|
||||
} finally {
|
||||
restore()
|
||||
}
|
||||
act(() => {
|
||||
renderer = create(
|
||||
createElement(CodexResetCreditAction, {
|
||||
summary,
|
||||
scopeLabel: 'dev@example.com on the host',
|
||||
busy,
|
||||
disabled,
|
||||
onPress: vi.fn()
|
||||
})
|
||||
)
|
||||
})
|
||||
if (!renderer) {
|
||||
throw new Error('Reset action did not render')
|
||||
}
|
||||
|
|
@ -55,10 +39,6 @@ function renderAction(busy: boolean, disabled: boolean): ReactTestRenderer {
|
|||
}
|
||||
|
||||
describe('CodexResetCreditAction', () => {
|
||||
afterEach(() => {
|
||||
vi.restoreAllMocks()
|
||||
})
|
||||
|
||||
it('exposes a 44pt touch target and enabled accessibility state', () => {
|
||||
const renderer = renderAction(false, false)
|
||||
const button = renderer.root.findByType('Pressable')
|
||||
|
|
|
|||
|
|
@ -78,7 +78,6 @@ describe('HostProtocolGate', () => {
|
|||
let renderer: ReactTestRenderer | null = null
|
||||
|
||||
beforeEach(() => {
|
||||
globalThis.IS_REACT_ACT_ENVIRONMENT = true
|
||||
nativeTestState.openUrl.mockClear()
|
||||
nativeTestState.platform.OS = 'ios'
|
||||
probeMounts.count = 0
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { createElement } from 'react'
|
||||
import { act, create, type ReactTestRenderer } from 'react-test-renderer'
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest'
|
||||
import type { HostProfile } from '../transport/types'
|
||||
import { MobileHomeQuickActions } from './MobileHomeQuickActions'
|
||||
|
||||
|
|
@ -37,10 +37,6 @@ function host(id: string, name: string, endpoint: string): HostProfile {
|
|||
describe('MobileHomeQuickActions', () => {
|
||||
let renderer: ReactTestRenderer | null = null
|
||||
|
||||
beforeEach(() => {
|
||||
globalThis.IS_REACT_ACT_ENVIRONMENT = true
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
act(() => renderer?.unmount())
|
||||
renderer = null
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { createElement } from 'react'
|
||||
import { act, create, type ReactTestRenderer } from 'react-test-renderer'
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest'
|
||||
import { MobileHostCard } from './MobileHostCard'
|
||||
|
||||
vi.mock('react-native', () => ({
|
||||
|
|
@ -30,10 +30,6 @@ function suppressRendererDeprecation() {
|
|||
describe('MobileHostCard', () => {
|
||||
let renderer: ReactTestRenderer | null = null
|
||||
|
||||
beforeEach(() => {
|
||||
globalThis.IS_REACT_ACT_ENVIRONMENT = true
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
act(() => renderer?.unmount())
|
||||
renderer = null
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { createElement } from 'react'
|
||||
import { act, create, type ReactTestRenderer } from 'react-test-renderer'
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest'
|
||||
import type { ConnectionVerdict } from '../transport/connection-health'
|
||||
import type { MobileConnectionPath } from '../transport/stable-logical-rpc-client'
|
||||
import type { ConnectionState, HostCredentialStatus, HostProfile } from '../transport/types'
|
||||
|
|
@ -39,10 +39,6 @@ const loaded: HostWorktreeInfo = {
|
|||
describe('MobileHostCard', () => {
|
||||
let renderer: ReactTestRenderer | null = null
|
||||
|
||||
beforeEach(() => {
|
||||
globalThis.IS_REACT_ACT_ENVIRONMENT = true
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
act(() => renderer?.unmount())
|
||||
renderer = null
|
||||
|
|
|
|||
|
|
@ -38,7 +38,6 @@ describe('MobileMarkdown file links', () => {
|
|||
const onOpenFile = vi.fn()
|
||||
|
||||
beforeEach(() => {
|
||||
globalThis.IS_REACT_ACT_ENVIRONMENT = true
|
||||
onOpenFile.mockClear()
|
||||
openURL.mockClear()
|
||||
})
|
||||
|
|
|
|||
|
|
@ -58,7 +58,6 @@ describe('NewWorktreeModal repo list', () => {
|
|||
let renderer: ReactTestRenderer | null = null
|
||||
|
||||
beforeEach(() => {
|
||||
globalThis.IS_REACT_ACT_ENVIRONMENT = true
|
||||
setCachedRepos('host-1', repos)
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -24,7 +24,6 @@ describe('PickerModal accessibility', () => {
|
|||
let renderer: ReactTestRenderer | null = null
|
||||
|
||||
beforeEach(() => {
|
||||
globalThis.IS_REACT_ACT_ENVIRONMENT = true
|
||||
vi.spyOn(console, 'error').mockImplementation((...args) => {
|
||||
if (typeof args[0] !== 'string' || !args[0].includes('react-test-renderer is deprecated')) {
|
||||
throw new Error(String(args[0]))
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { createElement } from 'react'
|
||||
import { act, create, type ReactTestRenderer } from 'react-test-renderer'
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest'
|
||||
import type { RuntimeWorktreeAgentRow } from '../../../src/shared/runtime-types'
|
||||
import { WorktreeAgentList } from './WorktreeAgentList'
|
||||
|
||||
|
|
@ -39,10 +39,6 @@ function agent(paneKey: string, parentPaneKey: string | null = null): RuntimeWor
|
|||
describe('WorktreeAgentList', () => {
|
||||
let renderer: ReactTestRenderer | null = null
|
||||
|
||||
beforeEach(() => {
|
||||
globalThis.IS_REACT_ACT_ENVIRONMENT = true
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
act(() => renderer?.unmount())
|
||||
renderer = null
|
||||
|
|
|
|||
|
|
@ -127,7 +127,6 @@ describe('memoized worktree rows', () => {
|
|||
let renderer: ReactTestRenderer | null = null
|
||||
|
||||
beforeEach(() => {
|
||||
;(globalThis as { IS_REACT_ACT_ENVIRONMENT?: boolean }).IS_REACT_ACT_ENVIRONMENT = true
|
||||
agentSpinnerRender.mockClear()
|
||||
agentStateDotRender.mockClear()
|
||||
})
|
||||
|
|
|
|||
|
|
@ -51,14 +51,12 @@ function mountedDrawer(renderer: ReactTestRenderer) {
|
|||
|
||||
describe('BottomDrawer close lifecycle', () => {
|
||||
beforeEach(() => {
|
||||
globalThis.IS_REACT_ACT_ENVIRONMENT = true
|
||||
const originalConsoleError = console.error
|
||||
vi.spyOn(console, 'error').mockImplementation((...args) => {
|
||||
const message = args[0]
|
||||
if (
|
||||
typeof message === 'string' &&
|
||||
(message.includes('react-test-renderer is deprecated') ||
|
||||
message.includes('The current testing environment is not configured to support act'))
|
||||
message.includes('The current testing environment is not configured to support act')
|
||||
) {
|
||||
return
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { createElement } from 'react'
|
||||
import { act, create, type ReactTestRenderer } from 'react-test-renderer'
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest'
|
||||
import { MobileAgentIcon } from './MobileAgentIcon'
|
||||
|
||||
vi.mock('react-native', () => ({
|
||||
|
|
@ -35,10 +35,6 @@ vi.mock('./AgentIcons', () => ({
|
|||
describe('MobileAgentIcon OMP gradient', () => {
|
||||
let renderer: ReactTestRenderer | null = null
|
||||
|
||||
beforeEach(() => {
|
||||
globalThis.IS_REACT_ACT_ENVIRONMENT = true
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
act(() => renderer?.unmount())
|
||||
renderer = null
|
||||
|
|
|
|||
|
|
@ -32,18 +32,6 @@ vi.mock('./pr-comments-styles', () => ({ prCommentsStyles: {} }))
|
|||
vi.mock('./mobile-pr-sidebar-styles', () => ({ mobilePrSidebarStyles: {} }))
|
||||
vi.mock('../../theme/mobile-theme', () => ({ colors: { textSecondary: '#999' } }))
|
||||
|
||||
function suppressReactTestRendererDeprecationWarning(): () => void {
|
||||
const originalConsoleError = console.error
|
||||
const consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation((...args) => {
|
||||
const firstArg = args[0]
|
||||
if (typeof firstArg === 'string' && firstArg.includes('react-test-renderer is deprecated')) {
|
||||
return
|
||||
}
|
||||
originalConsoleError(...args)
|
||||
})
|
||||
return () => consoleErrorSpy.mockRestore()
|
||||
}
|
||||
|
||||
function comment(id: number): PRComment {
|
||||
return {
|
||||
id,
|
||||
|
|
@ -65,14 +53,9 @@ function detailsWithComments(count: number): GitHubWorkItemDetails {
|
|||
|
||||
async function renderComments(details: GitHubWorkItemDetails): Promise<ReactTestRenderer> {
|
||||
let renderer: ReactTestRenderer | null = null
|
||||
const restoreConsoleError = suppressReactTestRendererDeprecationWarning()
|
||||
try {
|
||||
await act(async () => {
|
||||
renderer = create(createElement(PRCommentsSection, { details, prState: 'open' }))
|
||||
})
|
||||
} finally {
|
||||
restoreConsoleError()
|
||||
}
|
||||
await act(async () => {
|
||||
renderer = create(createElement(PRCommentsSection, { details, prState: 'open' }))
|
||||
})
|
||||
if (!renderer) {
|
||||
throw new Error('PRCommentsSection did not render')
|
||||
}
|
||||
|
|
@ -107,7 +90,6 @@ describe('PRCommentsSection', () => {
|
|||
afterEach(() => {
|
||||
renderer?.unmount()
|
||||
renderer = null
|
||||
vi.restoreAllMocks()
|
||||
})
|
||||
|
||||
it('resets pagination only when the user chooses a different audience filter', async () => {
|
||||
|
|
|
|||
|
|
@ -83,18 +83,6 @@ vi.mock('../transport/client-context', () => ({
|
|||
})
|
||||
}))
|
||||
|
||||
function suppressReactTestRendererDeprecationWarning(): () => void {
|
||||
const originalConsoleError = console.error
|
||||
const consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation((...args) => {
|
||||
const firstArg = args[0]
|
||||
if (typeof firstArg === 'string' && firstArg.includes('react-test-renderer is deprecated')) {
|
||||
return
|
||||
}
|
||||
originalConsoleError(...args)
|
||||
})
|
||||
return () => consoleErrorSpy.mockRestore()
|
||||
}
|
||||
|
||||
function entry(name: string, isDirectory = false): MobileDirEntry {
|
||||
return { name, isDirectory }
|
||||
}
|
||||
|
|
@ -113,21 +101,16 @@ function createMockClient(entriesByPath: Record<string, MobileDirEntry[]>): Mock
|
|||
|
||||
async function renderExplorer(): Promise<ReactTestRenderer> {
|
||||
let renderer: ReactTestRenderer | null = null
|
||||
const restoreConsoleError = suppressReactTestRendererDeprecationWarning()
|
||||
try {
|
||||
await act(async () => {
|
||||
renderer = create(
|
||||
createElement(MobileFileExplorerPanel, {
|
||||
hostId: 'host-a',
|
||||
worktreeId: 'worktree-a',
|
||||
name: 'Example Worktree',
|
||||
embedded: true
|
||||
})
|
||||
)
|
||||
})
|
||||
} finally {
|
||||
restoreConsoleError()
|
||||
}
|
||||
await act(async () => {
|
||||
renderer = create(
|
||||
createElement(MobileFileExplorerPanel, {
|
||||
hostId: 'host-a',
|
||||
worktreeId: 'worktree-a',
|
||||
name: 'Example Worktree',
|
||||
embedded: true
|
||||
})
|
||||
)
|
||||
})
|
||||
if (!renderer) {
|
||||
throw new Error('MobileFileExplorerPanel did not render')
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,28 +33,11 @@ vi.mock('./mobile-file-preview-styles', () => ({
|
|||
|
||||
type PreviewProps = Parameters<typeof MobileFileMarkdownPreview>[0]
|
||||
|
||||
function suppressReactTestRendererDeprecationWarning(): () => void {
|
||||
const originalConsoleError = console.error
|
||||
const consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation((...args) => {
|
||||
const firstArg = args[0]
|
||||
if (typeof firstArg === 'string' && firstArg.includes('react-test-renderer is deprecated')) {
|
||||
return
|
||||
}
|
||||
originalConsoleError(...args)
|
||||
})
|
||||
return () => consoleErrorSpy.mockRestore()
|
||||
}
|
||||
|
||||
async function renderPreview(props: PreviewProps): Promise<ReactTestRenderer> {
|
||||
let renderer: ReactTestRenderer | null = null
|
||||
const restoreConsoleError = suppressReactTestRendererDeprecationWarning()
|
||||
try {
|
||||
await act(async () => {
|
||||
renderer = create(createElement(MobileFileMarkdownPreview, props))
|
||||
})
|
||||
} finally {
|
||||
restoreConsoleError()
|
||||
}
|
||||
await act(async () => {
|
||||
renderer = create(createElement(MobileFileMarkdownPreview, props))
|
||||
})
|
||||
if (!renderer) {
|
||||
throw new Error('MobileFileMarkdownPreview did not render')
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { createElement } from 'react'
|
||||
import { act, create, type ReactTestRenderer } from 'react-test-renderer'
|
||||
import { afterEach, beforeEach, describe, expect, it, vi, type MockInstance } from 'vitest'
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
|
||||
const appState = vi.hoisted(() => ({
|
||||
current: 'active',
|
||||
|
|
@ -25,7 +25,6 @@ import { useNow } from './use-now'
|
|||
describe('useNow', () => {
|
||||
let renderer: ReactTestRenderer | null = null
|
||||
let latest = 0
|
||||
let consoleSpy: MockInstance
|
||||
|
||||
function Harness({ enabled = true }: { enabled?: boolean }): null {
|
||||
latest = useNow(1_000, enabled)
|
||||
|
|
@ -42,18 +41,10 @@ describe('useNow', () => {
|
|||
beforeEach(() => {
|
||||
vi.useFakeTimers()
|
||||
vi.setSystemTime(1_000)
|
||||
globalThis.IS_REACT_ACT_ENVIRONMENT = true
|
||||
appState.current = 'active'
|
||||
appState.listener = null
|
||||
appState.remove.mockClear()
|
||||
latest = 0
|
||||
const original = console.error
|
||||
consoleSpy = vi.spyOn(console, 'error').mockImplementation((...args) => {
|
||||
if (typeof args[0] === 'string' && args[0].includes('react-test-renderer is deprecated')) {
|
||||
return
|
||||
}
|
||||
original(...args)
|
||||
})
|
||||
act(() => {
|
||||
renderer = create(createElement(Harness))
|
||||
})
|
||||
|
|
@ -63,7 +54,6 @@ describe('useNow', () => {
|
|||
act(() => renderer?.unmount())
|
||||
renderer = null
|
||||
vi.useRealTimers()
|
||||
consoleSpy.mockRestore()
|
||||
})
|
||||
|
||||
it('ticks while active, pauses in the background, and refreshes immediately on resume', () => {
|
||||
|
|
|
|||
|
|
@ -46,29 +46,12 @@ vi.mock('./transport/client-context', () => ({
|
|||
usePrimeHosts: () => dependencies.primeHosts
|
||||
}))
|
||||
|
||||
function suppressReactTestRendererDeprecationWarning(): () => void {
|
||||
const originalConsoleError = console.error
|
||||
const consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation((...args) => {
|
||||
const firstArg = args[0]
|
||||
if (typeof firstArg === 'string' && firstArg.includes('react-test-renderer is deprecated')) {
|
||||
return
|
||||
}
|
||||
originalConsoleError(...args)
|
||||
})
|
||||
return () => consoleErrorSpy.mockRestore()
|
||||
}
|
||||
|
||||
async function renderEditHostRoute(): Promise<ReactTestRenderer> {
|
||||
let renderer: ReactTestRenderer | null = null
|
||||
const restoreConsoleError = suppressReactTestRendererDeprecationWarning()
|
||||
try {
|
||||
await act(async () => {
|
||||
renderer = create(createElement(EditHostScreen))
|
||||
await Promise.resolve()
|
||||
})
|
||||
} finally {
|
||||
restoreConsoleError()
|
||||
}
|
||||
await act(async () => {
|
||||
renderer = create(createElement(EditHostScreen))
|
||||
await Promise.resolve()
|
||||
})
|
||||
if (!renderer) {
|
||||
throw new Error('Edit host route did not render')
|
||||
}
|
||||
|
|
@ -77,7 +60,6 @@ async function renderEditHostRoute(): Promise<ReactTestRenderer> {
|
|||
|
||||
describe('edit host route accessibility', () => {
|
||||
beforeEach(() => {
|
||||
globalThis.IS_REACT_ACT_ENVIRONMENT = true
|
||||
dependencies.loadHosts.mockReset().mockResolvedValue([
|
||||
{
|
||||
id: 'host-1',
|
||||
|
|
|
|||
|
|
@ -56,29 +56,12 @@ const HOST_FIXTURE = {
|
|||
lastConnected: 1
|
||||
}
|
||||
|
||||
function suppressReactTestRendererDeprecationWarning(): () => void {
|
||||
const originalConsoleError = console.error
|
||||
const consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation((...args) => {
|
||||
const firstArg = args[0]
|
||||
if (typeof firstArg === 'string' && firstArg.includes('react-test-renderer is deprecated')) {
|
||||
return
|
||||
}
|
||||
originalConsoleError(...args)
|
||||
})
|
||||
return () => consoleErrorSpy.mockRestore()
|
||||
}
|
||||
|
||||
async function renderEditHostRoute(): Promise<ReactTestRenderer> {
|
||||
let renderer: ReactTestRenderer | null = null
|
||||
const restoreConsoleError = suppressReactTestRendererDeprecationWarning()
|
||||
try {
|
||||
await act(async () => {
|
||||
renderer = create(createElement(EditHostScreen))
|
||||
await Promise.resolve()
|
||||
})
|
||||
} finally {
|
||||
restoreConsoleError()
|
||||
}
|
||||
await act(async () => {
|
||||
renderer = create(createElement(EditHostScreen))
|
||||
await Promise.resolve()
|
||||
})
|
||||
if (!renderer) {
|
||||
throw new Error('Edit host route did not render')
|
||||
}
|
||||
|
|
@ -135,7 +118,6 @@ function findText(renderer: ReactTestRenderer, match: string): boolean {
|
|||
|
||||
describe('edit host handleSave', () => {
|
||||
beforeEach(() => {
|
||||
globalThis.IS_REACT_ACT_ENVIRONMENT = true
|
||||
dependencies.hostId = 'host-1'
|
||||
dependencies.back.mockReset()
|
||||
dependencies.forceReconnectHost.mockReset().mockResolvedValue(undefined)
|
||||
|
|
@ -285,7 +267,6 @@ describe('edit host handleSave', () => {
|
|||
|
||||
describe('edit host load() error states', () => {
|
||||
beforeEach(() => {
|
||||
globalThis.IS_REACT_ACT_ENVIRONMENT = true
|
||||
dependencies.hostId = 'host-1'
|
||||
dependencies.back.mockReset()
|
||||
dependencies.forceReconnectHost.mockReset().mockResolvedValue(undefined)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { createElement } from 'react'
|
||||
import { act, create, type ReactTestRenderer } from 'react-test-renderer'
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest'
|
||||
import { MobileOnboardingPage } from './MobileOnboardingPage'
|
||||
|
||||
vi.mock('react-native', async () => {
|
||||
|
|
@ -24,10 +24,6 @@ vi.mock('lucide-react-native', () => ({
|
|||
describe('MobileOnboardingPage', () => {
|
||||
let renderer: ReactTestRenderer | null = null
|
||||
|
||||
beforeEach(() => {
|
||||
globalThis.IS_REACT_ACT_ENVIRONMENT = true
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
act(() => renderer?.unmount())
|
||||
renderer = null
|
||||
|
|
|
|||
|
|
@ -56,7 +56,6 @@ describe('MobileOnboardingScreen', () => {
|
|||
let renderer: ReactTestRenderer | null = null
|
||||
|
||||
beforeEach(() => {
|
||||
globalThis.IS_REACT_ACT_ENVIRONMENT = true
|
||||
mocks.params = { hostId: 'paired-host', steps: 'session-view,notifications' }
|
||||
mocks.replace.mockReset()
|
||||
mocks.reducedMotionEnabled = false
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { createElement } from 'react'
|
||||
import { act, create, type ReactTestRenderer } from 'react-test-renderer'
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest'
|
||||
import { radii, spacing } from '../theme/mobile-theme'
|
||||
import { MobileNativeChatComposer } from './MobileNativeChatComposer'
|
||||
|
||||
|
|
@ -43,24 +43,9 @@ vi.mock('../components/BottomDrawer', async () => {
|
|||
}
|
||||
})
|
||||
|
||||
function suppressRendererWarning(): () => void {
|
||||
const original = console.error
|
||||
const spy = vi.spyOn(console, 'error').mockImplementation((...args) => {
|
||||
if (typeof args[0] === 'string' && args[0].includes('react-test-renderer is deprecated')) {
|
||||
return
|
||||
}
|
||||
original(...args)
|
||||
})
|
||||
return () => spy.mockRestore()
|
||||
}
|
||||
|
||||
describe('MobileNativeChatComposer', () => {
|
||||
let renderer: ReactTestRenderer | null = null
|
||||
|
||||
beforeEach(() => {
|
||||
globalThis.IS_REACT_ACT_ENVIRONMENT = true
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
act(() => renderer?.unmount())
|
||||
renderer = null
|
||||
|
|
@ -71,21 +56,16 @@ describe('MobileNativeChatComposer', () => {
|
|||
onChangeText: () => void,
|
||||
isAttaching = false
|
||||
) {
|
||||
const restore = suppressRendererWarning()
|
||||
try {
|
||||
await act(async () => {
|
||||
renderer = create(
|
||||
createElement(MobileNativeChatComposer, {
|
||||
value: ' hello ',
|
||||
onChangeText,
|
||||
onSend,
|
||||
isAttaching
|
||||
})
|
||||
)
|
||||
})
|
||||
} finally {
|
||||
restore()
|
||||
}
|
||||
await act(async () => {
|
||||
renderer = create(
|
||||
createElement(MobileNativeChatComposer, {
|
||||
value: ' hello ',
|
||||
onChangeText,
|
||||
onSend,
|
||||
isAttaching
|
||||
})
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
function sendButton(): { props: { onPress: () => Promise<void> } } {
|
||||
|
|
@ -130,20 +110,15 @@ describe('MobileNativeChatComposer', () => {
|
|||
|
||||
it('preserves leading whitespace so prose is not turned into a slash command', async () => {
|
||||
const onSend = vi.fn().mockResolvedValue(true)
|
||||
const restore = suppressRendererWarning()
|
||||
try {
|
||||
await act(async () => {
|
||||
renderer = create(
|
||||
createElement(MobileNativeChatComposer, {
|
||||
value: ' /clear is prose ',
|
||||
onChangeText: vi.fn(),
|
||||
onSend
|
||||
})
|
||||
)
|
||||
})
|
||||
} finally {
|
||||
restore()
|
||||
}
|
||||
await act(async () => {
|
||||
renderer = create(
|
||||
createElement(MobileNativeChatComposer, {
|
||||
value: ' /clear is prose ',
|
||||
onChangeText: vi.fn(),
|
||||
onSend
|
||||
})
|
||||
)
|
||||
})
|
||||
await act(async () => sendButton().props.onPress())
|
||||
expect(onSend).toHaveBeenCalledWith(' /clear is prose')
|
||||
})
|
||||
|
|
@ -181,67 +156,57 @@ describe('MobileNativeChatComposer', () => {
|
|||
invokeAction: vi.fn(),
|
||||
recordCommand: vi.fn()
|
||||
}
|
||||
const restore = suppressRendererWarning()
|
||||
try {
|
||||
await act(async () => {
|
||||
renderer = create(
|
||||
createElement(MobileNativeChatComposer, {
|
||||
value: 'run the tests',
|
||||
onChangeText: vi.fn(),
|
||||
onSend,
|
||||
sessionOptions: { isWorking: false, controller }
|
||||
})
|
||||
)
|
||||
})
|
||||
const modelPill = (): { props: { accessibilityState: { disabled: boolean } } } =>
|
||||
renderer!.root.find(
|
||||
(node) => node.type === 'Pressable' && node.props.accessibilityLabel === 'Model, Model'
|
||||
) as { props: { accessibilityState: { disabled: boolean } } }
|
||||
expect(modelPill().props.accessibilityState).toMatchObject({ disabled: false })
|
||||
// Start the send but don't await it — it stays in flight on purpose.
|
||||
let pressed!: Promise<void>
|
||||
await act(async () => {
|
||||
pressed = sendButton().props.onPress()
|
||||
await Promise.resolve()
|
||||
})
|
||||
expect(onSend).toHaveBeenCalled()
|
||||
expect(modelPill().props.accessibilityState).toMatchObject({ disabled: true })
|
||||
await act(async () => {
|
||||
releaseSend?.(true)
|
||||
await pressed
|
||||
})
|
||||
expect(modelPill().props.accessibilityState).toMatchObject({ disabled: false })
|
||||
} finally {
|
||||
restore()
|
||||
}
|
||||
await act(async () => {
|
||||
renderer = create(
|
||||
createElement(MobileNativeChatComposer, {
|
||||
value: 'run the tests',
|
||||
onChangeText: vi.fn(),
|
||||
onSend,
|
||||
sessionOptions: { isWorking: false, controller }
|
||||
})
|
||||
)
|
||||
})
|
||||
const modelPill = (): { props: { accessibilityState: { disabled: boolean } } } =>
|
||||
renderer!.root.find(
|
||||
(node) => node.type === 'Pressable' && node.props.accessibilityLabel === 'Model, Model'
|
||||
) as { props: { accessibilityState: { disabled: boolean } } }
|
||||
expect(modelPill().props.accessibilityState).toMatchObject({ disabled: false })
|
||||
// Start the send but don't await it — it stays in flight on purpose.
|
||||
let pressed!: Promise<void>
|
||||
await act(async () => {
|
||||
pressed = sendButton().props.onPress()
|
||||
await Promise.resolve()
|
||||
})
|
||||
expect(onSend).toHaveBeenCalled()
|
||||
expect(modelPill().props.accessibilityState).toMatchObject({ disabled: true })
|
||||
await act(async () => {
|
||||
releaseSend?.(true)
|
||||
await pressed
|
||||
})
|
||||
expect(modelPill().props.accessibilityState).toMatchObject({ disabled: false })
|
||||
})
|
||||
|
||||
it('blocks composer submission while a session-option command is pending', async () => {
|
||||
const onSend = vi.fn().mockResolvedValue(true)
|
||||
const restore = suppressRendererWarning()
|
||||
try {
|
||||
await act(async () => {
|
||||
renderer = create(
|
||||
createElement(MobileNativeChatComposer, {
|
||||
value: 'hello',
|
||||
onChangeText: vi.fn(),
|
||||
onSend,
|
||||
sessionOptions: {
|
||||
isWorking: false,
|
||||
controller: {
|
||||
snapshot: [],
|
||||
pendingId: 'model',
|
||||
setOption: vi.fn(),
|
||||
invokeAction: vi.fn(),
|
||||
recordCommand: vi.fn()
|
||||
}
|
||||
await act(async () => {
|
||||
renderer = create(
|
||||
createElement(MobileNativeChatComposer, {
|
||||
value: 'hello',
|
||||
onChangeText: vi.fn(),
|
||||
onSend,
|
||||
sessionOptions: {
|
||||
isWorking: false,
|
||||
controller: {
|
||||
snapshot: [],
|
||||
pendingId: 'model',
|
||||
setOption: vi.fn(),
|
||||
invokeAction: vi.fn(),
|
||||
recordCommand: vi.fn()
|
||||
}
|
||||
})
|
||||
)
|
||||
})
|
||||
} finally {
|
||||
restore()
|
||||
}
|
||||
}
|
||||
})
|
||||
)
|
||||
})
|
||||
expect(sendButton().props).toMatchObject({ disabled: true })
|
||||
await act(async () => sendButton().props.onPress())
|
||||
expect(onSend).not.toHaveBeenCalled()
|
||||
|
|
@ -268,21 +233,16 @@ describe('MobileNativeChatComposer', () => {
|
|||
})
|
||||
|
||||
it('keeps the text input editable while the send is locked', async () => {
|
||||
const restore = suppressRendererWarning()
|
||||
try {
|
||||
await act(async () => {
|
||||
renderer = create(
|
||||
createElement(MobileNativeChatComposer, {
|
||||
value: 'half-typed',
|
||||
onChangeText: vi.fn(),
|
||||
onSend: vi.fn().mockResolvedValue(true),
|
||||
disabled: true
|
||||
})
|
||||
)
|
||||
})
|
||||
} finally {
|
||||
restore()
|
||||
}
|
||||
await act(async () => {
|
||||
renderer = create(
|
||||
createElement(MobileNativeChatComposer, {
|
||||
value: 'half-typed',
|
||||
onChangeText: vi.fn(),
|
||||
onSend: vi.fn().mockResolvedValue(true),
|
||||
disabled: true
|
||||
})
|
||||
)
|
||||
})
|
||||
// Revoking `editable` on a focused field resigns first responder on iOS and
|
||||
// yanks the keyboard mid-typing (#10681) — the lock may only gate sending.
|
||||
const input = renderer!.root.find((node) => node.type === 'TextInput') as {
|
||||
|
|
@ -294,25 +254,20 @@ describe('MobileNativeChatComposer', () => {
|
|||
|
||||
it('renders a removable thumbnail for each pending image attachment', async () => {
|
||||
const onRemoveAttachment = vi.fn()
|
||||
const restore = suppressRendererWarning()
|
||||
try {
|
||||
await act(async () => {
|
||||
renderer = create(
|
||||
createElement(MobileNativeChatComposer, {
|
||||
value: '',
|
||||
onChangeText: vi.fn(),
|
||||
onSend: vi.fn().mockResolvedValue(true),
|
||||
attachments: [
|
||||
{ id: 'img-1', path: '/tmp/a.png', previewUri: 'file:///a.png' },
|
||||
{ id: 'img-2', path: '/tmp/b.png', previewUri: 'file:///b.png' }
|
||||
],
|
||||
onRemoveAttachment
|
||||
})
|
||||
)
|
||||
})
|
||||
} finally {
|
||||
restore()
|
||||
}
|
||||
await act(async () => {
|
||||
renderer = create(
|
||||
createElement(MobileNativeChatComposer, {
|
||||
value: '',
|
||||
onChangeText: vi.fn(),
|
||||
onSend: vi.fn().mockResolvedValue(true),
|
||||
attachments: [
|
||||
{ id: 'img-1', path: '/tmp/a.png', previewUri: 'file:///a.png' },
|
||||
{ id: 'img-2', path: '/tmp/b.png', previewUri: 'file:///b.png' }
|
||||
],
|
||||
onRemoveAttachment
|
||||
})
|
||||
)
|
||||
})
|
||||
const thumbs = renderer!.root.findAll((node) => node.type === 'Image') as Array<{
|
||||
props: { source: { uri: string } }
|
||||
}>
|
||||
|
|
@ -327,42 +282,32 @@ describe('MobileNativeChatComposer', () => {
|
|||
|
||||
it('enables send with an attached image even when the text is empty', async () => {
|
||||
const onSend = vi.fn().mockResolvedValue(true)
|
||||
const restore = suppressRendererWarning()
|
||||
try {
|
||||
await act(async () => {
|
||||
renderer = create(
|
||||
createElement(MobileNativeChatComposer, {
|
||||
value: '',
|
||||
onChangeText: vi.fn(),
|
||||
onSend,
|
||||
attachments: [{ id: 'img-1', path: '/tmp/a.png', previewUri: 'file:///a.png' }]
|
||||
})
|
||||
)
|
||||
})
|
||||
} finally {
|
||||
restore()
|
||||
}
|
||||
await act(async () => {
|
||||
renderer = create(
|
||||
createElement(MobileNativeChatComposer, {
|
||||
value: '',
|
||||
onChangeText: vi.fn(),
|
||||
onSend,
|
||||
attachments: [{ id: 'img-1', path: '/tmp/a.png', previewUri: 'file:///a.png' }]
|
||||
})
|
||||
)
|
||||
})
|
||||
expect(sendButton().props).toMatchObject({ disabled: false })
|
||||
await act(async () => sendButton().props.onPress())
|
||||
expect(onSend).toHaveBeenCalledWith('')
|
||||
})
|
||||
|
||||
it('moves the caret to the insert point after an autocomplete pick, then releases control', async () => {
|
||||
const restore = suppressRendererWarning()
|
||||
try {
|
||||
await act(async () => {
|
||||
renderer = create(
|
||||
createElement(MobileNativeChatComposer, {
|
||||
value: '/c',
|
||||
onChangeText: vi.fn(),
|
||||
onSend: vi.fn().mockResolvedValue(true),
|
||||
agent: 'claude'
|
||||
})
|
||||
)
|
||||
})
|
||||
} finally {
|
||||
restore()
|
||||
}
|
||||
await act(async () => {
|
||||
renderer = create(
|
||||
createElement(MobileNativeChatComposer, {
|
||||
value: '/c',
|
||||
onChangeText: vi.fn(),
|
||||
onSend: vi.fn().mockResolvedValue(true),
|
||||
agent: 'claude'
|
||||
})
|
||||
)
|
||||
})
|
||||
const input = () =>
|
||||
renderer!.root.find((node) => node.type === 'TextInput') as {
|
||||
props: {
|
||||
|
|
@ -390,21 +335,16 @@ describe('MobileNativeChatComposer', () => {
|
|||
})
|
||||
|
||||
it('serves the active agent’s shared command catalog with descriptions', async () => {
|
||||
const restore = suppressRendererWarning()
|
||||
try {
|
||||
await act(async () => {
|
||||
renderer = create(
|
||||
createElement(MobileNativeChatComposer, {
|
||||
value: '/',
|
||||
onChangeText: vi.fn(),
|
||||
onSend: vi.fn().mockResolvedValue(true),
|
||||
agent: 'codex'
|
||||
})
|
||||
)
|
||||
})
|
||||
} finally {
|
||||
restore()
|
||||
}
|
||||
await act(async () => {
|
||||
renderer = create(
|
||||
createElement(MobileNativeChatComposer, {
|
||||
value: '/',
|
||||
onChangeText: vi.fn(),
|
||||
onSend: vi.fn().mockResolvedValue(true),
|
||||
agent: 'codex'
|
||||
})
|
||||
)
|
||||
})
|
||||
const input = renderer!.root.find((node) => node.type === 'TextInput') as {
|
||||
props: { onSelectionChange: (e: { nativeEvent: { selection: { end: number } } }) => void }
|
||||
}
|
||||
|
|
@ -428,45 +368,40 @@ describe('MobileNativeChatComposer', () => {
|
|||
(node) => node.type === 'Pressable' && node.props.accessibilityLabel === 'Dictate'
|
||||
) as { props: { onPress?: unknown; onPressIn?: unknown; onPressOut?: unknown } }
|
||||
|
||||
const restore = suppressRendererWarning()
|
||||
try {
|
||||
await act(async () => {
|
||||
renderer = create(
|
||||
createElement(MobileNativeChatComposer, {
|
||||
value: '',
|
||||
onChangeText: vi.fn(),
|
||||
onSend: vi.fn().mockResolvedValue(true),
|
||||
onMicPress,
|
||||
dictationMode: 'hold',
|
||||
onMicPressIn,
|
||||
onMicPressOut
|
||||
})
|
||||
)
|
||||
})
|
||||
// Hold mode is walkie-talkie: press-in/out drive dictation, tap is inert.
|
||||
expect(mic().props.onPress).toBeUndefined()
|
||||
expect(mic().props.onPressIn).toBe(onMicPressIn)
|
||||
expect(mic().props.onPressOut).toBe(onMicPressOut)
|
||||
await act(async () => {
|
||||
renderer = create(
|
||||
createElement(MobileNativeChatComposer, {
|
||||
value: '',
|
||||
onChangeText: vi.fn(),
|
||||
onSend: vi.fn().mockResolvedValue(true),
|
||||
onMicPress,
|
||||
dictationMode: 'hold',
|
||||
onMicPressIn,
|
||||
onMicPressOut
|
||||
})
|
||||
)
|
||||
})
|
||||
// Hold mode is walkie-talkie: press-in/out drive dictation, tap is inert.
|
||||
expect(mic().props.onPress).toBeUndefined()
|
||||
expect(mic().props.onPressIn).toBe(onMicPressIn)
|
||||
expect(mic().props.onPressOut).toBe(onMicPressOut)
|
||||
|
||||
await act(async () => {
|
||||
renderer!.update(
|
||||
createElement(MobileNativeChatComposer, {
|
||||
value: '',
|
||||
onChangeText: vi.fn(),
|
||||
onSend: vi.fn().mockResolvedValue(true),
|
||||
onMicPress,
|
||||
dictationMode: 'toggle',
|
||||
onMicPressIn,
|
||||
onMicPressOut
|
||||
})
|
||||
)
|
||||
})
|
||||
// Toggle mode: tap drives dictation, press-in/out inert.
|
||||
expect(mic().props.onPress).toBe(onMicPress)
|
||||
expect(mic().props.onPressIn).toBeUndefined()
|
||||
expect(mic().props.onPressOut).toBeUndefined()
|
||||
} finally {
|
||||
restore()
|
||||
}
|
||||
await act(async () => {
|
||||
renderer!.update(
|
||||
createElement(MobileNativeChatComposer, {
|
||||
value: '',
|
||||
onChangeText: vi.fn(),
|
||||
onSend: vi.fn().mockResolvedValue(true),
|
||||
onMicPress,
|
||||
dictationMode: 'toggle',
|
||||
onMicPressIn,
|
||||
onMicPressOut
|
||||
})
|
||||
)
|
||||
})
|
||||
// Toggle mode: tap drives dictation, press-in/out inert.
|
||||
expect(mic().props.onPress).toBe(onMicPress)
|
||||
expect(mic().props.onPressIn).toBeUndefined()
|
||||
expect(mic().props.onPressOut).toBeUndefined()
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { createElement } from 'react'
|
||||
import { act, create, type ReactTestInstance, type ReactTestRenderer } from 'react-test-renderer'
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest'
|
||||
import type { NativeChatMessage } from '../../../src/shared/native-chat-types'
|
||||
import { MAX_TOOL_DETAIL_LENGTH } from './mobile-native-chat-tool-summary'
|
||||
|
||||
|
|
@ -38,9 +38,6 @@ function toolMessage(blocks: NativeChatMessage['blocks']): NativeChatMessage {
|
|||
describe('MobileNativeChatMessage', () => {
|
||||
let renderer: ReactTestRenderer | null = null
|
||||
|
||||
beforeEach(() => {
|
||||
globalThis.IS_REACT_ACT_ENVIRONMENT = true
|
||||
})
|
||||
afterEach(() => {
|
||||
act(() => renderer?.unmount())
|
||||
renderer = null
|
||||
|
|
@ -50,20 +47,9 @@ describe('MobileNativeChatMessage', () => {
|
|||
message: NativeChatMessage,
|
||||
props: { toolsExpanded?: boolean } = {}
|
||||
): ReactTestRenderer {
|
||||
const original = console.error
|
||||
const spy = vi.spyOn(console, 'error').mockImplementation((...a) => {
|
||||
if (typeof a[0] === 'string' && a[0].includes('react-test-renderer is deprecated')) {
|
||||
return
|
||||
}
|
||||
original(...a)
|
||||
act(() => {
|
||||
renderer = create(createElement(MobileNativeChatMessage, { message, ...props }))
|
||||
})
|
||||
try {
|
||||
act(() => {
|
||||
renderer = create(createElement(MobileNativeChatMessage, { message, ...props }))
|
||||
})
|
||||
} finally {
|
||||
spy.mockRestore()
|
||||
}
|
||||
return renderer!
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { createElement } from 'react'
|
||||
import { act, create, type ReactTestRenderer } from 'react-test-renderer'
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest'
|
||||
import type { NativeChatMessage } from '../../../src/shared/native-chat-types'
|
||||
import { MobileNativeChatOverlay } from './MobileNativeChatOverlay'
|
||||
import type { MobileNativeChatController } from './use-mobile-native-chat-controller'
|
||||
|
|
@ -16,17 +16,6 @@ function assistantTurn(id: string, text: string): NativeChatMessage {
|
|||
return { id, role: 'assistant', blocks: [{ type: 'text', text }], timestamp: 0, source: 'hook' }
|
||||
}
|
||||
|
||||
function suppressRendererWarning(): () => void {
|
||||
const original = console.error
|
||||
const spy = vi.spyOn(console, 'error').mockImplementation((...args) => {
|
||||
if (typeof args[0] === 'string' && args[0].includes('react-test-renderer is deprecated')) {
|
||||
return
|
||||
}
|
||||
original(...args)
|
||||
})
|
||||
return () => spy.mockRestore()
|
||||
}
|
||||
|
||||
/** One render of the route: chat visible or not, the transcript it currently
|
||||
* holds, and the agent-status stream behind it. */
|
||||
type Tick = {
|
||||
|
|
@ -69,24 +58,15 @@ function overlayElement(tick: Tick): ReturnType<typeof createElement> {
|
|||
describe('MobileNativeChatOverlay streaming gate', () => {
|
||||
let renderer: ReactTestRenderer | null = null
|
||||
|
||||
beforeEach(() => {
|
||||
globalThis.IS_REACT_ACT_ENVIRONMENT = true
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
act(() => renderer?.unmount())
|
||||
renderer = null
|
||||
})
|
||||
|
||||
async function render(tick: Tick): Promise<void> {
|
||||
const restore = suppressRendererWarning()
|
||||
try {
|
||||
await act(async () => {
|
||||
renderer = create(overlayElement(tick))
|
||||
})
|
||||
} finally {
|
||||
restore()
|
||||
}
|
||||
await act(async () => {
|
||||
renderer = create(overlayElement(tick))
|
||||
})
|
||||
}
|
||||
|
||||
async function update(tick: Tick): Promise<void> {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { createElement } from 'react'
|
||||
import { act, create, type ReactTestRenderer } from 'react-test-renderer'
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest'
|
||||
import { MobileNativeChatPermission } from './MobileNativeChatPermission'
|
||||
|
||||
vi.mock('react-native', () => ({
|
||||
|
|
@ -15,10 +15,6 @@ vi.mock('lucide-react-native', () => ({ ShieldQuestion: 'ShieldQuestion' }))
|
|||
describe('MobileNativeChatPermission', () => {
|
||||
let renderer: ReactTestRenderer | null = null
|
||||
|
||||
beforeEach(() => {
|
||||
globalThis.IS_REACT_ACT_ENVIRONMENT = true
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
act(() => renderer?.unmount())
|
||||
renderer = null
|
||||
|
|
@ -28,25 +24,14 @@ describe('MobileNativeChatPermission', () => {
|
|||
let resolveResponse: (accepted: boolean) => void = () => {}
|
||||
const response = new Promise<boolean>((resolve) => (resolveResponse = resolve))
|
||||
const onRespond = vi.fn(() => response)
|
||||
const original = console.error
|
||||
const consoleSpy = vi.spyOn(console, 'error').mockImplementation((...args) => {
|
||||
if (typeof args[0] === 'string' && args[0].includes('react-test-renderer is deprecated')) {
|
||||
return
|
||||
}
|
||||
original(...args)
|
||||
await act(async () => {
|
||||
renderer = create(
|
||||
createElement(MobileNativeChatPermission, {
|
||||
permission: { title: 'Approve?', options: [{ label: 'Allow', send: '1' }] },
|
||||
onRespond
|
||||
})
|
||||
)
|
||||
})
|
||||
try {
|
||||
await act(async () => {
|
||||
renderer = create(
|
||||
createElement(MobileNativeChatPermission, {
|
||||
permission: { title: 'Approve?', options: [{ label: 'Allow', send: '1' }] },
|
||||
onRespond
|
||||
})
|
||||
)
|
||||
})
|
||||
} finally {
|
||||
consoleSpy.mockRestore()
|
||||
}
|
||||
const button = renderer.root.findByType('Pressable')
|
||||
|
||||
act(() => {
|
||||
|
|
|
|||
|
|
@ -149,7 +149,6 @@ describe('MobileNativeChatSessionOptionPickers', () => {
|
|||
}
|
||||
|
||||
beforeEach(() => {
|
||||
globalThis.IS_REACT_ACT_ENVIRONMENT = true
|
||||
setOption.mockReset()
|
||||
setOption.mockResolvedValue(true)
|
||||
invokeAction.mockReset()
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { createElement } from 'react'
|
||||
import { act, create, type ReactTestInstance, type ReactTestRenderer } from 'react-test-renderer'
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest'
|
||||
import type { NativeChatMessage } from '../../../src/shared/native-chat-types'
|
||||
import { MobileNativeChatView } from './MobileNativeChatView'
|
||||
|
||||
|
|
@ -73,17 +73,6 @@ type Overrides = {
|
|||
onSend?: (text: string) => Promise<boolean>
|
||||
}
|
||||
|
||||
function suppressRendererWarning(): () => void {
|
||||
const original = console.error
|
||||
const spy = vi.spyOn(console, 'error').mockImplementation((...args) => {
|
||||
if (typeof args[0] === 'string' && args[0].includes('react-test-renderer is deprecated')) {
|
||||
return
|
||||
}
|
||||
original(...args)
|
||||
})
|
||||
return () => spy.mockRestore()
|
||||
}
|
||||
|
||||
function assistantTurn(id: string, text: string): NativeChatMessage {
|
||||
return { id, role: 'assistant', blocks: [{ type: 'text', text }], timestamp: 0, source: 'hook' }
|
||||
}
|
||||
|
|
@ -105,24 +94,15 @@ function chatViewElement(overrides: Overrides): ReturnType<typeof createElement>
|
|||
describe('MobileNativeChatView', () => {
|
||||
let renderer: ReactTestRenderer | null = null
|
||||
|
||||
beforeEach(() => {
|
||||
globalThis.IS_REACT_ACT_ENVIRONMENT = true
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
act(() => renderer?.unmount())
|
||||
renderer = null
|
||||
})
|
||||
|
||||
async function render(overrides: Overrides = {}): Promise<void> {
|
||||
const restore = suppressRendererWarning()
|
||||
try {
|
||||
await act(async () => {
|
||||
renderer = create(chatViewElement(overrides))
|
||||
})
|
||||
} finally {
|
||||
restore()
|
||||
}
|
||||
await act(async () => {
|
||||
renderer = create(chatViewElement(overrides))
|
||||
})
|
||||
}
|
||||
|
||||
async function update(overrides: Overrides = {}): Promise<void> {
|
||||
|
|
|
|||
|
|
@ -29,9 +29,7 @@ vi.mock('../components/MobileAgentIcon', () => ({ MobileAgentIcon: 'MobileAgentI
|
|||
describe('QuickCommandsList search', () => {
|
||||
let renderer: ReactTestRenderer | null = null
|
||||
|
||||
beforeEach(() => {
|
||||
globalThis.IS_REACT_ACT_ENVIRONMENT = true
|
||||
})
|
||||
beforeEach(() => {})
|
||||
|
||||
afterEach(() => {
|
||||
act(() => renderer?.unmount())
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { createElement, type ReactNode } from 'react'
|
||||
import { act, create, type ReactTestRenderer } from 'react-test-renderer'
|
||||
import { afterEach, beforeEach, describe, expect, it, vi, type MockInstance } from 'vitest'
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import type { TerminalQuickCommand } from '../../../src/shared/types'
|
||||
import type { RpcClient } from '../transport/rpc-client'
|
||||
import { MAX_QUICK_COMMANDS } from '../terminal/quick-commands'
|
||||
|
|
@ -65,26 +65,15 @@ function deferred<T>() {
|
|||
|
||||
describe('QuickCommandsSheet', () => {
|
||||
let renderer: ReactTestRenderer | null = null
|
||||
let consoleSpy: MockInstance
|
||||
|
||||
beforeEach(() => {
|
||||
globalThis.IS_REACT_ACT_ENVIRONMENT = true
|
||||
mocks.alert.mockReset()
|
||||
mocks.commands = []
|
||||
mocks.persist.mockReset()
|
||||
const originalConsoleError = console.error
|
||||
consoleSpy = vi.spyOn(console, 'error').mockImplementation((...args) => {
|
||||
if (typeof args[0] === 'string' && args[0].includes('react-test-renderer is deprecated')) {
|
||||
return
|
||||
}
|
||||
originalConsoleError(...args)
|
||||
})
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
act(() => renderer?.unmount())
|
||||
renderer = null
|
||||
consoleSpy.mockRestore()
|
||||
})
|
||||
|
||||
it('keeps the sheet open when a launch is rejected', async () => {
|
||||
|
|
|
|||
|
|
@ -67,7 +67,6 @@ describe('useMobileNativeChatPermissionSend', () => {
|
|||
let respond: ((text: string) => Promise<boolean>) | null = null
|
||||
|
||||
beforeEach(() => {
|
||||
globalThis.IS_REACT_ACT_ENVIRONMENT = true
|
||||
resetMobileNativeChatStaleInputForTests()
|
||||
resetMobileNativeChatTerminalWritesForTests()
|
||||
})
|
||||
|
|
|
|||
|
|
@ -52,7 +52,6 @@ describe('useInitialSessionTerminalAutoCreate', () => {
|
|||
}
|
||||
|
||||
beforeEach(() => {
|
||||
globalThis.IS_REACT_ACT_ENVIRONMENT = true
|
||||
stateRef = {
|
||||
current: { autoCreatedForWorktree: null, sawSessionTabs: false }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,18 +13,6 @@ vi.mock('expo-router', async () => {
|
|||
}
|
||||
})
|
||||
|
||||
function suppressReactTestRendererDeprecationWarning(): () => void {
|
||||
const originalConsoleError = console.error
|
||||
const consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation((...args) => {
|
||||
const firstArg = args[0]
|
||||
if (typeof firstArg === 'string' && firstArg.includes('react-test-renderer is deprecated')) {
|
||||
return
|
||||
}
|
||||
originalConsoleError(...args)
|
||||
})
|
||||
return () => consoleErrorSpy.mockRestore()
|
||||
}
|
||||
|
||||
describe('useLiveWorktreeName request volume', () => {
|
||||
let renderer: ReactTestRenderer | null = null
|
||||
let eventListener: ((payload: unknown) => void) | null = null
|
||||
|
|
@ -54,15 +42,10 @@ describe('useLiveWorktreeName request volume', () => {
|
|||
return null
|
||||
}
|
||||
|
||||
const restoreConsoleError = suppressReactTestRendererDeprecationWarning()
|
||||
try {
|
||||
await act(async () => {
|
||||
renderer = create(createElement(Harness))
|
||||
await Promise.resolve()
|
||||
})
|
||||
} finally {
|
||||
restoreConsoleError()
|
||||
}
|
||||
await act(async () => {
|
||||
renderer = create(createElement(Harness))
|
||||
await Promise.resolve()
|
||||
})
|
||||
}
|
||||
|
||||
async function emitEvent(payload: unknown): Promise<void> {
|
||||
|
|
@ -75,7 +58,6 @@ describe('useLiveWorktreeName request volume', () => {
|
|||
|
||||
beforeEach(() => {
|
||||
vi.useFakeTimers()
|
||||
globalThis.IS_REACT_ACT_ENVIRONMENT = true
|
||||
eventListener = null
|
||||
sendRequest.mockClear().mockResolvedValue({
|
||||
id: 'worktree-show',
|
||||
|
|
@ -179,15 +161,10 @@ describe('useLiveWorktreeName request volume', () => {
|
|||
return null
|
||||
}
|
||||
|
||||
const restoreConsoleError = suppressReactTestRendererDeprecationWarning()
|
||||
try {
|
||||
await act(async () => {
|
||||
renderer = create(createElement(FloatingHarness))
|
||||
await Promise.resolve()
|
||||
})
|
||||
} finally {
|
||||
restoreConsoleError()
|
||||
}
|
||||
await act(async () => {
|
||||
renderer = create(createElement(FloatingHarness))
|
||||
await Promise.resolve()
|
||||
})
|
||||
await act(async () => {
|
||||
await vi.advanceTimersByTimeAsync(30_000)
|
||||
})
|
||||
|
|
@ -214,7 +191,6 @@ describe('useLiveWorktreeName request volume', () => {
|
|||
return null
|
||||
}
|
||||
|
||||
const restoreConsoleError = suppressReactTestRendererDeprecationWarning()
|
||||
try {
|
||||
await act(async () => {
|
||||
renderer = create(
|
||||
|
|
@ -241,7 +217,6 @@ describe('useLiveWorktreeName request volume', () => {
|
|||
)
|
||||
})
|
||||
} finally {
|
||||
restoreConsoleError()
|
||||
act(() => renderer?.unmount())
|
||||
}
|
||||
|
||||
|
|
@ -263,15 +238,10 @@ describe('useLiveWorktreeName request volume', () => {
|
|||
return null
|
||||
}
|
||||
const mount = async (): Promise<void> => {
|
||||
const restoreConsoleError = suppressReactTestRendererDeprecationWarning()
|
||||
try {
|
||||
await act(async () => {
|
||||
renderer = create(createElement(VerdictHarness))
|
||||
await Promise.resolve()
|
||||
})
|
||||
} finally {
|
||||
restoreConsoleError()
|
||||
}
|
||||
await act(async () => {
|
||||
renderer = create(createElement(VerdictHarness))
|
||||
await Promise.resolve()
|
||||
})
|
||||
}
|
||||
|
||||
await mount()
|
||||
|
|
|
|||
|
|
@ -27,7 +27,6 @@ describe('useMissingWorktreeBounce', () => {
|
|||
const bounce = vi.fn()
|
||||
|
||||
beforeEach(() => {
|
||||
globalThis.IS_REACT_ACT_ENVIRONMENT = true
|
||||
bounce.mockReset()
|
||||
renderer = null
|
||||
})
|
||||
|
|
|
|||
|
|
@ -7,16 +7,7 @@ type Gate = (targetHandle: string) => Promise<boolean>
|
|||
|
||||
describe('useMobileAttachmentInputLeaseGate', () => {
|
||||
let renderer: ReactTestRenderer | null = null
|
||||
let errorSpy: ReturnType<typeof vi.spyOn> | null = null
|
||||
|
||||
beforeEach(() => {
|
||||
const original = console.error
|
||||
errorSpy = vi.spyOn(console, 'error').mockImplementation((...args: unknown[]) => {
|
||||
if (typeof args[0] === 'string' && args[0].includes('react-test-renderer is deprecated')) {
|
||||
return
|
||||
}
|
||||
original(...(args as Parameters<typeof console.error>))
|
||||
})
|
||||
vi.useFakeTimers()
|
||||
})
|
||||
|
||||
|
|
@ -24,7 +15,6 @@ describe('useMobileAttachmentInputLeaseGate', () => {
|
|||
act(() => renderer?.unmount())
|
||||
renderer = null
|
||||
vi.useRealTimers()
|
||||
errorSpy?.mockRestore()
|
||||
})
|
||||
|
||||
function renderGate(args: {
|
||||
|
|
|
|||
|
|
@ -30,7 +30,6 @@ describe('useMobileDefaultSessionViewPreference', () => {
|
|||
let preference: MobileDefaultSessionViewPreference | null = null
|
||||
|
||||
beforeEach(() => {
|
||||
globalThis.IS_REACT_ACT_ENVIRONMENT = true
|
||||
vi.mocked(loadDefaultSessionView).mockReset().mockResolvedValue('terminal')
|
||||
vi.mocked(saveDefaultSessionView).mockReset().mockResolvedValue(undefined)
|
||||
})
|
||||
|
|
|
|||
|
|
@ -63,7 +63,6 @@ describe('useMobileDiffReviewController', () => {
|
|||
}
|
||||
|
||||
beforeEach(() => {
|
||||
globalThis.IS_REACT_ACT_ENVIRONMENT = true
|
||||
loadSnapshot.mockReset()
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -57,7 +57,6 @@ describe('useMobileDiffReviewDiffLoading', () => {
|
|||
}
|
||||
|
||||
beforeEach(() => {
|
||||
globalThis.IS_REACT_ACT_ENVIRONMENT = true
|
||||
loadDiff.mockReset()
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -52,7 +52,6 @@ describe('useMobileDiffReviewSendActions', () => {
|
|||
let saveCommentsAndReviewState: ReturnType<typeof vi.fn>
|
||||
|
||||
beforeEach(() => {
|
||||
globalThis.IS_REACT_ACT_ENVIRONMENT = true
|
||||
resetMobileNativeChatStaleInputForTests()
|
||||
setActionError = vi.fn()
|
||||
setSendSheet = vi.fn()
|
||||
|
|
@ -81,20 +80,9 @@ describe('useMobileDiffReviewSendActions', () => {
|
|||
|
||||
async function mount(client: RpcClient): Promise<void> {
|
||||
mountedClient = client
|
||||
const original = console.error
|
||||
const consoleSpy = vi.spyOn(console, 'error').mockImplementation((...args) => {
|
||||
if (typeof args[0] === 'string' && args[0].includes('react-test-renderer is deprecated')) {
|
||||
return
|
||||
}
|
||||
original(...args)
|
||||
await act(async () => {
|
||||
renderer = create(createElement(Harness))
|
||||
})
|
||||
try {
|
||||
await act(async () => {
|
||||
renderer = create(createElement(Harness))
|
||||
})
|
||||
} finally {
|
||||
consoleSpy.mockRestore()
|
||||
}
|
||||
}
|
||||
|
||||
it('heals a marked terminal BEFORE submitting the notes', async () => {
|
||||
|
|
|
|||
|
|
@ -19,7 +19,6 @@ describe('useMobileFileTapHandlers', () => {
|
|||
let handlers: Handlers | null = null
|
||||
|
||||
beforeEach(() => {
|
||||
globalThis.IS_REACT_ACT_ENVIRONMENT = true
|
||||
push.mockClear()
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -54,7 +54,6 @@ describe('useMobileNativeChatAnswerSend', () => {
|
|||
beforeEach(() => {
|
||||
onAccepted = vi.fn()
|
||||
vi.useFakeTimers()
|
||||
globalThis.IS_REACT_ACT_ENVIRONMENT = true
|
||||
resetMobileNativeChatStaleInputForTests()
|
||||
resetMobileNativeChatTerminalWritesForTests()
|
||||
})
|
||||
|
|
@ -93,20 +92,9 @@ describe('useMobileNativeChatAnswerSend', () => {
|
|||
mountedClient = client
|
||||
mountedOnSendError = onSendError
|
||||
mountedAgent = agent
|
||||
const original = console.error
|
||||
const consoleSpy = vi.spyOn(console, 'error').mockImplementation((...args) => {
|
||||
if (typeof args[0] === 'string' && args[0].includes('react-test-renderer is deprecated')) {
|
||||
return
|
||||
}
|
||||
original(...args)
|
||||
await act(async () => {
|
||||
renderer = create(createElement(Harness, { enabled: true }))
|
||||
})
|
||||
try {
|
||||
await act(async () => {
|
||||
renderer = create(createElement(Harness, { enabled: true }))
|
||||
})
|
||||
} finally {
|
||||
consoleSpy.mockRestore()
|
||||
}
|
||||
}
|
||||
|
||||
async function setEnabled(enabled: boolean): Promise<void> {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { createElement } from 'react'
|
||||
import { act, create, type ReactTestRenderer } from 'react-test-renderer'
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import { afterEach, beforeEach, describe, expect, it } from 'vitest'
|
||||
import type { AskPrompt } from './mobile-native-chat-ask'
|
||||
import { useMobileNativeChatAskDismiss } from './use-mobile-native-chat-ask-dismiss'
|
||||
|
||||
|
|
@ -10,7 +10,6 @@ describe('useMobileNativeChatAskDismiss', () => {
|
|||
let renders = 0
|
||||
|
||||
beforeEach(() => {
|
||||
globalThis.IS_REACT_ACT_ENVIRONMENT = true
|
||||
renders = 0
|
||||
})
|
||||
|
||||
|
|
@ -45,20 +44,9 @@ describe('useMobileNativeChatAskDismiss', () => {
|
|||
}
|
||||
|
||||
async function mount(props: Parameters<typeof Harness>[0]): Promise<void> {
|
||||
const original = console.error
|
||||
const consoleSpy = vi.spyOn(console, 'error').mockImplementation((...args) => {
|
||||
if (typeof args[0] === 'string' && args[0].includes('react-test-renderer is deprecated')) {
|
||||
return
|
||||
}
|
||||
original(...args)
|
||||
await act(async () => {
|
||||
renderer = create(createElement(Harness, props))
|
||||
})
|
||||
try {
|
||||
await act(async () => {
|
||||
renderer = create(createElement(Harness, props))
|
||||
})
|
||||
} finally {
|
||||
consoleSpy.mockRestore()
|
||||
}
|
||||
}
|
||||
|
||||
async function update(props: Parameters<typeof Harness>[0]): Promise<void> {
|
||||
|
|
|
|||
|
|
@ -127,24 +127,12 @@ describe('useMobileNativeChatController handleNativeChatSend', () => {
|
|||
}
|
||||
|
||||
beforeEach(() => {
|
||||
globalThis.IS_REACT_ACT_ENVIRONMENT = true
|
||||
vi.clearAllMocks()
|
||||
resetMobileNativeChatStaleInputForTests()
|
||||
captureSendOrigin.mockReturnValue(ORIGIN)
|
||||
const original = console.error
|
||||
const spy = vi.spyOn(console, 'error').mockImplementation((...a) => {
|
||||
if (typeof a[0] === 'string' && a[0].includes('react-test-renderer is deprecated')) {
|
||||
return
|
||||
}
|
||||
original(...a)
|
||||
act(() => {
|
||||
renderer = create(createElement(Harness))
|
||||
})
|
||||
try {
|
||||
act(() => {
|
||||
renderer = create(createElement(Harness))
|
||||
})
|
||||
} finally {
|
||||
spy.mockRestore()
|
||||
}
|
||||
})
|
||||
afterEach(() => {
|
||||
act(() => renderer?.unmount())
|
||||
|
|
@ -364,24 +352,12 @@ describe('useMobileNativeChatController launch-draft wiring', () => {
|
|||
}
|
||||
|
||||
function render(tab: unknown): void {
|
||||
const original = console.error
|
||||
const spy = vi.spyOn(console, 'error').mockImplementation((...a) => {
|
||||
if (typeof a[0] === 'string' && a[0].includes('react-test-renderer is deprecated')) {
|
||||
return
|
||||
}
|
||||
original(...a)
|
||||
act(() => {
|
||||
renderer = create(createElement(Harness, { tab }))
|
||||
})
|
||||
try {
|
||||
act(() => {
|
||||
renderer = create(createElement(Harness, { tab }))
|
||||
})
|
||||
} finally {
|
||||
spy.mockRestore()
|
||||
}
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
globalThis.IS_REACT_ACT_ENVIRONMENT = true
|
||||
draftsArgs.length = 0
|
||||
viewMode.isTabChatView = () => true
|
||||
sessionState.messages = []
|
||||
|
|
@ -487,25 +463,13 @@ describe('useMobileNativeChatController ask dismissal across a transcript reload
|
|||
}
|
||||
|
||||
beforeEach(() => {
|
||||
globalThis.IS_REACT_ACT_ENVIRONMENT = true
|
||||
viewMode.isTabChatView = () => true
|
||||
setTranscript('ready')
|
||||
promptsState.ask = PROMPT
|
||||
promptsState.detectedAsk = PROMPT
|
||||
const original = console.error
|
||||
const spy = vi.spyOn(console, 'error').mockImplementation((...a) => {
|
||||
if (typeof a[0] === 'string' && a[0].includes('react-test-renderer is deprecated')) {
|
||||
return
|
||||
}
|
||||
original(...a)
|
||||
act(() => {
|
||||
renderer = create(createElement(Harness))
|
||||
})
|
||||
try {
|
||||
act(() => {
|
||||
renderer = create(createElement(Harness))
|
||||
})
|
||||
} finally {
|
||||
spy.mockRestore()
|
||||
}
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
|
|
@ -757,22 +721,10 @@ describe('useMobileNativeChatController streaming scope', () => {
|
|||
}
|
||||
|
||||
beforeEach(() => {
|
||||
globalThis.IS_REACT_ACT_ENVIRONMENT = true
|
||||
viewMode.isTabChatView = () => true
|
||||
const original = console.error
|
||||
const spy = vi.spyOn(console, 'error').mockImplementation((...a) => {
|
||||
if (typeof a[0] === 'string' && a[0].includes('react-test-renderer is deprecated')) {
|
||||
return
|
||||
}
|
||||
original(...a)
|
||||
act(() => {
|
||||
renderer = create(createElement(Harness))
|
||||
})
|
||||
try {
|
||||
act(() => {
|
||||
renderer = create(createElement(Harness))
|
||||
})
|
||||
} finally {
|
||||
spy.mockRestore()
|
||||
}
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { createElement } from 'react'
|
||||
import { act, create, type ReactTestRenderer } from 'react-test-renderer'
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import { afterEach, describe, expect, it } from 'vitest'
|
||||
import type { NativeChatMessage } from '../../../src/shared/native-chat-types'
|
||||
import { useMobileNativeChatDrafts } from './use-mobile-native-chat-drafts'
|
||||
|
||||
|
|
@ -23,10 +23,6 @@ describe('useMobileNativeChatDrafts launch draft', () => {
|
|||
let renderer: ReactTestRenderer | null = null
|
||||
let state: DraftState | null = null
|
||||
|
||||
beforeEach(() => {
|
||||
globalThis.IS_REACT_ACT_ENVIRONMENT = true
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
act(() => renderer?.unmount())
|
||||
renderer = null
|
||||
|
|
@ -65,20 +61,9 @@ describe('useMobileNativeChatDrafts launch draft', () => {
|
|||
}
|
||||
|
||||
async function mount(tabId: string): Promise<void> {
|
||||
const original = console.error
|
||||
const consoleSpy = vi.spyOn(console, 'error').mockImplementation((...args) => {
|
||||
if (typeof args[0] === 'string' && args[0].includes('react-test-renderer is deprecated')) {
|
||||
return
|
||||
}
|
||||
original(...args)
|
||||
await act(async () => {
|
||||
renderer = create(createElement(Harness, { tabId }))
|
||||
})
|
||||
try {
|
||||
await act(async () => {
|
||||
renderer = create(createElement(Harness, { tabId }))
|
||||
})
|
||||
} finally {
|
||||
consoleSpy.mockRestore()
|
||||
}
|
||||
}
|
||||
|
||||
it('prefills the composer from a host launch draft exactly once', async () => {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { createElement } from 'react'
|
||||
import { act, create, type ReactTestRenderer } from 'react-test-renderer'
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest'
|
||||
import type { NativeChatMessage } from '../../../src/shared/native-chat-types'
|
||||
import { useMobileNativeChatDrafts } from './use-mobile-native-chat-drafts'
|
||||
|
||||
|
|
@ -30,10 +30,6 @@ describe('useMobileNativeChatDrafts', () => {
|
|||
let renderer: ReactTestRenderer | null = null
|
||||
let state: DraftState | null = null
|
||||
|
||||
beforeEach(() => {
|
||||
globalThis.IS_REACT_ACT_ENVIRONMENT = true
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
act(() => renderer?.unmount())
|
||||
renderer = null
|
||||
|
|
@ -69,20 +65,9 @@ describe('useMobileNativeChatDrafts', () => {
|
|||
}
|
||||
|
||||
async function mount(tabId: string): Promise<void> {
|
||||
const original = console.error
|
||||
const consoleSpy = vi.spyOn(console, 'error').mockImplementation((...args) => {
|
||||
if (typeof args[0] === 'string' && args[0].includes('react-test-renderer is deprecated')) {
|
||||
return
|
||||
}
|
||||
original(...args)
|
||||
await act(async () => {
|
||||
renderer = create(createElement(Harness, { tabId }))
|
||||
})
|
||||
try {
|
||||
await act(async () => {
|
||||
renderer = create(createElement(Harness, { tabId }))
|
||||
})
|
||||
} finally {
|
||||
consoleSpy.mockRestore()
|
||||
}
|
||||
}
|
||||
|
||||
async function switchTo(tabId: string): Promise<void> {
|
||||
|
|
|
|||
|
|
@ -15,17 +15,6 @@ function rpcSuccess(files: string[]): Awaited<ReturnType<RpcClient['sendRequest'
|
|||
}
|
||||
}
|
||||
|
||||
function suppressRendererWarning(): () => void {
|
||||
const original = console.error
|
||||
const spy = vi.spyOn(console, 'error').mockImplementation((...args) => {
|
||||
if (typeof args[0] === 'string' && args[0].includes('react-test-renderer is deprecated')) {
|
||||
return
|
||||
}
|
||||
original(...args)
|
||||
})
|
||||
return () => spy.mockRestore()
|
||||
}
|
||||
|
||||
describe('useMobileNativeChatFileSearch', () => {
|
||||
let renderer: ReactTestRenderer | null = null
|
||||
let state: SearchState | null = null
|
||||
|
|
@ -35,19 +24,13 @@ describe('useMobileNativeChatFileSearch', () => {
|
|||
state = useMobileNativeChatFileSearch({ client, worktreeId: 'wt-1' })
|
||||
return null
|
||||
}
|
||||
const restore = suppressRendererWarning()
|
||||
try {
|
||||
await act(async () => {
|
||||
renderer = create(createElement(Harness))
|
||||
})
|
||||
} finally {
|
||||
restore()
|
||||
}
|
||||
await act(async () => {
|
||||
renderer = create(createElement(Harness))
|
||||
})
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
vi.useFakeTimers()
|
||||
globalThis.IS_REACT_ACT_ENVIRONMENT = true
|
||||
state = null
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -87,7 +87,6 @@ describe('useMobileNativeChatImageAttachments', () => {
|
|||
}
|
||||
|
||||
beforeEach(() => {
|
||||
globalThis.IS_REACT_ACT_ENVIRONMENT = true
|
||||
pick.mockReset()
|
||||
// Stale markers and write locks live at module scope (they outlive the
|
||||
// screen), so they also outlive a test.
|
||||
|
|
@ -101,20 +100,9 @@ describe('useMobileNativeChatImageAttachments', () => {
|
|||
})
|
||||
|
||||
function mount(args: HookArgs): void {
|
||||
const original = console.error
|
||||
const spy = vi.spyOn(console, 'error').mockImplementation((...a) => {
|
||||
if (typeof a[0] === 'string' && a[0].includes('react-test-renderer is deprecated')) {
|
||||
return
|
||||
}
|
||||
original(...a)
|
||||
act(() => {
|
||||
renderer = create(createElement(Harness, { args }))
|
||||
})
|
||||
try {
|
||||
act(() => {
|
||||
renderer = create(createElement(Harness, { args }))
|
||||
})
|
||||
} finally {
|
||||
spy.mockRestore()
|
||||
}
|
||||
}
|
||||
|
||||
function update(args: HookArgs): void {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { createElement } from 'react'
|
||||
import { act, create, type ReactTestRenderer } from 'react-test-renderer'
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import { afterEach, describe, expect, it } from 'vitest'
|
||||
import { useMobileNativeChatInputLease } from './use-mobile-native-chat-input-lease'
|
||||
|
||||
type Lease = ReturnType<typeof useMobileNativeChatInputLease>
|
||||
|
|
@ -9,10 +9,6 @@ describe('useMobileNativeChatInputLease', () => {
|
|||
let renderer: ReactTestRenderer | null = null
|
||||
let lease: Lease | null = null
|
||||
|
||||
beforeEach(() => {
|
||||
globalThis.IS_REACT_ACT_ENVIRONMENT = true
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
act(() => renderer?.unmount())
|
||||
renderer = null
|
||||
|
|
@ -25,65 +21,43 @@ describe('useMobileNativeChatInputLease', () => {
|
|||
}
|
||||
|
||||
it('unlocks only after acknowledgement and clears on disconnect', async () => {
|
||||
const original = console.error
|
||||
const consoleSpy = vi.spyOn(console, 'error').mockImplementation((...args) => {
|
||||
if (typeof args[0] === 'string' && args[0].includes('react-test-renderer is deprecated')) {
|
||||
return
|
||||
}
|
||||
original(...args)
|
||||
await act(async () => {
|
||||
renderer = create(createElement(Harness, { connected: true }))
|
||||
})
|
||||
try {
|
||||
await act(async () => {
|
||||
renderer = create(createElement(Harness, { connected: true }))
|
||||
})
|
||||
expect(lease?.ready).toBe(false)
|
||||
expect(lease?.lockReason).toBe('waiting')
|
||||
act(() => lease?.markReady('terminal'))
|
||||
expect(lease?.ready).toBe(true)
|
||||
expect(lease?.lockReason).toBeNull()
|
||||
expect(lease?.ready).toBe(false)
|
||||
expect(lease?.lockReason).toBe('waiting')
|
||||
act(() => lease?.markReady('terminal'))
|
||||
expect(lease?.ready).toBe(true)
|
||||
expect(lease?.lockReason).toBeNull()
|
||||
|
||||
act(() => lease?.clear())
|
||||
expect(lease?.ready).toBe(false)
|
||||
act(() => lease?.markReady('terminal'))
|
||||
expect(lease?.ready).toBe(true)
|
||||
act(() => lease?.clear())
|
||||
expect(lease?.ready).toBe(false)
|
||||
act(() => lease?.markReady('terminal'))
|
||||
expect(lease?.ready).toBe(true)
|
||||
|
||||
await act(async () => renderer?.update(createElement(Harness, { connected: false })))
|
||||
expect(lease?.ready).toBe(false)
|
||||
expect(lease?.lockReason).toBe('disconnected')
|
||||
} finally {
|
||||
consoleSpy.mockRestore()
|
||||
}
|
||||
await act(async () => renderer?.update(createElement(Harness, { connected: false })))
|
||||
expect(lease?.ready).toBe(false)
|
||||
expect(lease?.lockReason).toBe('disconnected')
|
||||
})
|
||||
|
||||
it('reports whether a clear actually dropped a lease', async () => {
|
||||
const original = console.error
|
||||
const consoleSpy = vi.spyOn(console, 'error').mockImplementation((...args) => {
|
||||
if (typeof args[0] === 'string' && args[0].includes('react-test-renderer is deprecated')) {
|
||||
return
|
||||
}
|
||||
original(...args)
|
||||
await act(async () => {
|
||||
renderer = create(createElement(Harness, { connected: true }))
|
||||
})
|
||||
try {
|
||||
await act(async () => {
|
||||
renderer = create(createElement(Harness, { connected: true }))
|
||||
})
|
||||
// The route reads this to tell a real teardown from one React never sees.
|
||||
expect(lease?.clear('terminal')).toBe(false)
|
||||
expect(lease?.clear()).toBe(false)
|
||||
// The route reads this to tell a real teardown from one React never sees.
|
||||
expect(lease?.clear('terminal')).toBe(false)
|
||||
expect(lease?.clear()).toBe(false)
|
||||
|
||||
act(() => lease?.markReady('terminal'))
|
||||
let dropped: boolean | undefined
|
||||
act(() => {
|
||||
dropped = lease?.clear('terminal')
|
||||
})
|
||||
expect(dropped).toBe(true)
|
||||
expect(lease?.ready).toBe(false)
|
||||
expect(lease?.clear('terminal')).toBe(false)
|
||||
act(() => lease?.markReady('terminal'))
|
||||
let dropped: boolean | undefined
|
||||
act(() => {
|
||||
dropped = lease?.clear('terminal')
|
||||
})
|
||||
expect(dropped).toBe(true)
|
||||
expect(lease?.ready).toBe(false)
|
||||
expect(lease?.clear('terminal')).toBe(false)
|
||||
|
||||
act(() => lease?.markReady('other'))
|
||||
expect(lease?.clear()).toBe(true)
|
||||
} finally {
|
||||
consoleSpy.mockRestore()
|
||||
}
|
||||
act(() => lease?.markReady('other'))
|
||||
expect(lease?.clear()).toBe(true)
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -80,7 +80,6 @@ describe('useMobileNativeChatMessageSend', () => {
|
|||
(clearInputWrite.mock.calls[0]?.[0] ?? {}) as { clearInput?: string }
|
||||
|
||||
beforeEach(() => {
|
||||
globalThis.IS_REACT_ACT_ENVIRONMENT = true
|
||||
sendWithOutcome.mockReset()
|
||||
sendWithOutcome.mockResolvedValue('accepted')
|
||||
clearInputWrite.mockReset()
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ describe('useMobileNativeChatReadability', () => {
|
|||
let readable = false
|
||||
|
||||
beforeEach(() => {
|
||||
globalThis.IS_REACT_ACT_ENVIRONMENT = true
|
||||
readable = false
|
||||
})
|
||||
|
||||
|
|
@ -34,21 +33,10 @@ describe('useMobileNativeChatReadability', () => {
|
|||
readable = useMobileNativeChatReadability(client, worktreeId)
|
||||
return null
|
||||
}
|
||||
const original = console.error
|
||||
const consoleSpy = vi.spyOn(console, 'error').mockImplementation((...args) => {
|
||||
if (typeof args[0] === 'string' && args[0].includes('react-test-renderer is deprecated')) {
|
||||
return
|
||||
}
|
||||
original(...args)
|
||||
await act(async () => {
|
||||
renderer = create(createElement(Harness))
|
||||
await Promise.resolve()
|
||||
})
|
||||
try {
|
||||
await act(async () => {
|
||||
renderer = create(createElement(Harness))
|
||||
await Promise.resolve()
|
||||
})
|
||||
} finally {
|
||||
consoleSpy.mockRestore()
|
||||
}
|
||||
return sendRequest
|
||||
}
|
||||
|
||||
|
|
@ -89,32 +77,21 @@ describe('useMobileNativeChatReadability', () => {
|
|||
readable = useMobileNativeChatReadability(client, worktreeId)
|
||||
return null
|
||||
}
|
||||
const original = console.error
|
||||
const consoleSpy = vi.spyOn(console, 'error').mockImplementation((...args) => {
|
||||
if (typeof args[0] === 'string' && args[0].includes('react-test-renderer is deprecated')) {
|
||||
return
|
||||
}
|
||||
original(...args)
|
||||
await act(async () => {
|
||||
renderer = create(createElement(Harness, { worktreeId: 'local-repo::/one' }))
|
||||
await Promise.resolve()
|
||||
})
|
||||
try {
|
||||
await act(async () => {
|
||||
renderer = create(createElement(Harness, { worktreeId: 'local-repo::/one' }))
|
||||
await Promise.resolve()
|
||||
})
|
||||
expect(readable).toBe(true)
|
||||
expect(readable).toBe(true)
|
||||
|
||||
act(() => renderer?.update(createElement(Harness, { worktreeId: 'ssh-repo::/two' })))
|
||||
expect(readable).toBe(false)
|
||||
await act(async () => {
|
||||
resolveNext({
|
||||
ok: true,
|
||||
result: { repos: [{ id: 'ssh-repo', connectionId: 'model-a-ssh' }] }
|
||||
})
|
||||
await Promise.resolve()
|
||||
act(() => renderer?.update(createElement(Harness, { worktreeId: 'ssh-repo::/two' })))
|
||||
expect(readable).toBe(false)
|
||||
await act(async () => {
|
||||
resolveNext({
|
||||
ok: true,
|
||||
result: { repos: [{ id: 'ssh-repo', connectionId: 'model-a-ssh' }] }
|
||||
})
|
||||
expect(readable).toBe(false)
|
||||
} finally {
|
||||
consoleSpy.mockRestore()
|
||||
}
|
||||
await Promise.resolve()
|
||||
})
|
||||
expect(readable).toBe(false)
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -31,31 +31,13 @@ describe('useMobileNativeChatSendError', () => {
|
|||
return apiRef.current
|
||||
}
|
||||
|
||||
/** react-test-renderer logs a deprecation notice on every render; keep real errors. */
|
||||
function suppressRendererWarning(): () => void {
|
||||
const original = console.error
|
||||
const spy = vi.spyOn(console, 'error').mockImplementation((...args) => {
|
||||
if (typeof args[0] === 'string' && args[0].includes('react-test-renderer is deprecated')) {
|
||||
return
|
||||
}
|
||||
original(...args)
|
||||
})
|
||||
return () => spy.mockRestore()
|
||||
}
|
||||
|
||||
async function render(scopeKey: string | null = 'terminal-1'): Promise<void> {
|
||||
const restore = suppressRendererWarning()
|
||||
try {
|
||||
await act(async () => {
|
||||
renderer = create(createElement(Harness, { scopeKey }))
|
||||
})
|
||||
} finally {
|
||||
restore()
|
||||
}
|
||||
await act(async () => {
|
||||
renderer = create(createElement(Harness, { scopeKey }))
|
||||
})
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
globalThis.IS_REACT_ACT_ENVIRONMENT = true
|
||||
apiRef.current = null
|
||||
showToast.mockClear()
|
||||
vi.useFakeTimers()
|
||||
|
|
@ -117,26 +99,16 @@ describe('useMobileNativeChatSendError', () => {
|
|||
await act(async () => api().show('a'))
|
||||
expect(api().message).toBe('a')
|
||||
|
||||
const restore = suppressRendererWarning()
|
||||
try {
|
||||
await act(async () => {
|
||||
renderer?.update(createElement(Harness, { scopeKey: 'terminal-2' }))
|
||||
})
|
||||
} finally {
|
||||
restore()
|
||||
}
|
||||
await act(async () => {
|
||||
renderer?.update(createElement(Harness, { scopeKey: 'terminal-2' }))
|
||||
})
|
||||
expect(api().message).toBeNull()
|
||||
})
|
||||
|
||||
it('falls back to the toast when the banner is not mounted', async () => {
|
||||
const restore = suppressRendererWarning()
|
||||
try {
|
||||
await act(async () => {
|
||||
renderer = create(createElement(Harness, { scopeKey: 'terminal-1', bannerMounted: false }))
|
||||
})
|
||||
} finally {
|
||||
restore()
|
||||
}
|
||||
await act(async () => {
|
||||
renderer = create(createElement(Harness, { scopeKey: 'terminal-1', bannerMounted: false }))
|
||||
})
|
||||
// A deferred failure landing after the user left chat must still be seen.
|
||||
await act(async () => api().show('Delivery unconfirmed'))
|
||||
|
||||
|
|
@ -149,14 +121,9 @@ describe('useMobileNativeChatSendError', () => {
|
|||
// Captured while tab A was live; a 20s unconfirmed send resolves much later.
|
||||
const showFromTabA = api().show
|
||||
|
||||
const restore = suppressRendererWarning()
|
||||
try {
|
||||
await act(async () => {
|
||||
renderer?.update(createElement(Harness, { scopeKey: 'terminal-2' }))
|
||||
})
|
||||
} finally {
|
||||
restore()
|
||||
}
|
||||
await act(async () => {
|
||||
renderer?.update(createElement(Harness, { scopeKey: 'terminal-2' }))
|
||||
})
|
||||
await act(async () => showFromTabA('Message not sent'))
|
||||
|
||||
// The banner belongs to terminal-2 now, so A's failure must not paint there.
|
||||
|
|
@ -168,14 +135,9 @@ describe('useMobileNativeChatSendError', () => {
|
|||
await render('terminal-1')
|
||||
const clearFromTabA = api().clear
|
||||
|
||||
const restore = suppressRendererWarning()
|
||||
try {
|
||||
await act(async () => {
|
||||
renderer?.update(createElement(Harness, { scopeKey: 'terminal-2' }))
|
||||
})
|
||||
} finally {
|
||||
restore()
|
||||
}
|
||||
await act(async () => {
|
||||
renderer?.update(createElement(Harness, { scopeKey: 'terminal-2' }))
|
||||
})
|
||||
await act(async () => api().show('b'))
|
||||
// An accepted card action from tab A resolving late must not retire B's warning.
|
||||
await act(async () => clearFromTabA())
|
||||
|
|
|
|||
|
|
@ -44,7 +44,6 @@ describe('useMobileNativeChatSessionOptions', () => {
|
|||
}
|
||||
|
||||
beforeEach(() => {
|
||||
globalThis.IS_REACT_ACT_ENVIRONMENT = true
|
||||
clearMobileSessionOptionRecordsForTests()
|
||||
dispatchCommand.mockReset()
|
||||
dispatchCommand.mockResolvedValue('accepted')
|
||||
|
|
|
|||
|
|
@ -24,7 +24,6 @@ describe('useMobileNativeChatSession', () => {
|
|||
let emit: (frame: unknown) => void = () => {}
|
||||
|
||||
beforeEach(() => {
|
||||
globalThis.IS_REACT_ACT_ENVIRONMENT = true
|
||||
state = null
|
||||
})
|
||||
|
||||
|
|
@ -45,20 +44,9 @@ describe('useMobileNativeChatSession', () => {
|
|||
}
|
||||
|
||||
async function mount(client: RpcClient): Promise<void> {
|
||||
const original = console.error
|
||||
const consoleSpy = vi.spyOn(console, 'error').mockImplementation((...args) => {
|
||||
if (typeof args[0] === 'string' && args[0].includes('react-test-renderer is deprecated')) {
|
||||
return
|
||||
}
|
||||
original(...args)
|
||||
await act(async () => {
|
||||
renderer = create(createElement(Harness, { client }))
|
||||
})
|
||||
try {
|
||||
await act(async () => {
|
||||
renderer = create(createElement(Harness, { client }))
|
||||
})
|
||||
} finally {
|
||||
consoleSpy.mockRestore()
|
||||
}
|
||||
}
|
||||
|
||||
it('drops an older-page response captured before transcript replacement', async () => {
|
||||
|
|
@ -445,7 +433,6 @@ describe('useMobileNativeChatSession transcriptLoading', () => {
|
|||
}[] = []
|
||||
|
||||
beforeEach(() => {
|
||||
globalThis.IS_REACT_ACT_ENVIRONMENT = true
|
||||
renders.length = 0
|
||||
})
|
||||
|
||||
|
|
@ -482,20 +469,9 @@ describe('useMobileNativeChatSession transcriptLoading', () => {
|
|||
}
|
||||
|
||||
async function mountAt(client: RpcClient | null, sessionId: string | null): Promise<void> {
|
||||
const original = console.error
|
||||
const consoleSpy = vi.spyOn(console, 'error').mockImplementation((...args) => {
|
||||
if (typeof args[0] === 'string' && args[0].includes('react-test-renderer is deprecated')) {
|
||||
return
|
||||
}
|
||||
original(...args)
|
||||
await act(async () => {
|
||||
renderer = create(createElement(Harness, { client, sessionId }))
|
||||
})
|
||||
try {
|
||||
await act(async () => {
|
||||
renderer = create(createElement(Harness, { client, sessionId }))
|
||||
})
|
||||
} finally {
|
||||
consoleSpy.mockRestore()
|
||||
}
|
||||
}
|
||||
|
||||
it('reports loading on the very first render, before the subscription effect runs', async () => {
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@ describe('useMobileNativeChatStop', () => {
|
|||
|
||||
beforeEach(() => {
|
||||
vi.useFakeTimers()
|
||||
globalThis.IS_REACT_ACT_ENVIRONMENT = true
|
||||
sendRequest.mockReset().mockResolvedValue({
|
||||
ok: true,
|
||||
result: { send: { accepted: true } }
|
||||
|
|
|
|||
|
|
@ -17,7 +17,6 @@ describe('useMobileNativeChatTerminalStream', () => {
|
|||
const hasTabsRecoveryNeedRef = { current: (): boolean => false }
|
||||
|
||||
beforeEach(() => {
|
||||
globalThis.IS_REACT_ACT_ENVIRONMENT = true
|
||||
subscriptionsRef.current = new Map([['terminal-1', () => {}]])
|
||||
subscribingRef.current = new Set()
|
||||
webReadyRef.current = new Set(['terminal-1'])
|
||||
|
|
@ -85,113 +84,73 @@ describe('useMobileNativeChatTerminalStream', () => {
|
|||
}
|
||||
|
||||
it('replaces output with a lease-only stream while covered, then restores output', async () => {
|
||||
const original = console.error
|
||||
const consoleSpy = vi.spyOn(console, 'error').mockImplementation((...args) => {
|
||||
if (typeof args[0] === 'string' && args[0].includes('react-test-renderer is deprecated')) {
|
||||
return
|
||||
}
|
||||
original(...args)
|
||||
await act(async () => {
|
||||
renderer = create(createElement(Harness, { showNativeChat: false }))
|
||||
})
|
||||
await act(async () => {
|
||||
notifyWebReadyRef.current('terminal-1', false)
|
||||
})
|
||||
expect(harnessRenderCount).toBe(1)
|
||||
await act(async () => {
|
||||
renderer?.update(createElement(Harness, { showNativeChat: true }))
|
||||
})
|
||||
try {
|
||||
await act(async () => {
|
||||
renderer = create(createElement(Harness, { showNativeChat: false }))
|
||||
})
|
||||
await act(async () => {
|
||||
notifyWebReadyRef.current('terminal-1', false)
|
||||
})
|
||||
expect(harnessRenderCount).toBe(1)
|
||||
await act(async () => {
|
||||
renderer?.update(createElement(Harness, { showNativeChat: true }))
|
||||
})
|
||||
|
||||
expect(unsubscribe).toHaveBeenNthCalledWith(1, 'terminal-1')
|
||||
expect(subscribe).toHaveBeenNthCalledWith(1, 'terminal-1')
|
||||
expect(initializedRef.current.has('terminal-1')).toBe(false)
|
||||
expect(unsubscribe).toHaveBeenNthCalledWith(1, 'terminal-1')
|
||||
expect(subscribe).toHaveBeenNthCalledWith(1, 'terminal-1')
|
||||
expect(initializedRef.current.has('terminal-1')).toBe(false)
|
||||
|
||||
await act(async () => {
|
||||
renderer?.update(createElement(Harness, { showNativeChat: false }))
|
||||
})
|
||||
await act(async () => {
|
||||
renderer?.update(createElement(Harness, { showNativeChat: false }))
|
||||
})
|
||||
|
||||
expect(unsubscribe).toHaveBeenNthCalledWith(2, 'terminal-1')
|
||||
expect(subscribe).toHaveBeenNthCalledWith(2, 'terminal-1')
|
||||
} finally {
|
||||
consoleSpy.mockRestore()
|
||||
}
|
||||
expect(unsubscribe).toHaveBeenNthCalledWith(2, 'terminal-1')
|
||||
expect(subscribe).toHaveBeenNthCalledWith(2, 'terminal-1')
|
||||
})
|
||||
|
||||
it('re-subscribes a covered stream torn down under chat (#10681)', async () => {
|
||||
const original = console.error
|
||||
const consoleSpy = vi.spyOn(console, 'error').mockImplementation((...args) => {
|
||||
if (typeof args[0] === 'string' && args[0].includes('react-test-renderer is deprecated')) {
|
||||
return
|
||||
}
|
||||
original(...args)
|
||||
await act(async () => {
|
||||
renderer = create(createElement(Harness, { showNativeChat: true }))
|
||||
})
|
||||
subscribe.mockClear()
|
||||
unsubscribe.mockClear()
|
||||
// What a terminal.list prune / `end` frame does to the lease-only stream:
|
||||
// the subscription is gone and the lease drops with it.
|
||||
subscriptionsRef.current.delete('terminal-1')
|
||||
await act(async () => {
|
||||
renderer?.update(createElement(Harness, { showNativeChat: true, leaseReady: false }))
|
||||
})
|
||||
try {
|
||||
await act(async () => {
|
||||
renderer = create(createElement(Harness, { showNativeChat: true }))
|
||||
})
|
||||
subscribe.mockClear()
|
||||
unsubscribe.mockClear()
|
||||
// What a terminal.list prune / `end` frame does to the lease-only stream:
|
||||
// the subscription is gone and the lease drops with it.
|
||||
subscriptionsRef.current.delete('terminal-1')
|
||||
await act(async () => {
|
||||
renderer?.update(createElement(Harness, { showNativeChat: true, leaseReady: false }))
|
||||
})
|
||||
|
||||
expect(subscribe).toHaveBeenCalledOnce()
|
||||
expect(subscribe).toHaveBeenCalledWith('terminal-1')
|
||||
// Pins the rearm branch specifically: without it the action falls through to
|
||||
// the resume tail, which also subscribes — but drops coverage on the way.
|
||||
expect(unsubscribe).not.toHaveBeenCalled()
|
||||
} finally {
|
||||
consoleSpy.mockRestore()
|
||||
}
|
||||
expect(subscribe).toHaveBeenCalledOnce()
|
||||
expect(subscribe).toHaveBeenCalledWith('terminal-1')
|
||||
// Pins the rearm branch specifically: without it the action falls through to
|
||||
// the resume tail, which also subscribes — but drops coverage on the way.
|
||||
expect(unsubscribe).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('stops rearming a handle whose stream never comes back (#10681)', async () => {
|
||||
const original = console.error
|
||||
const consoleSpy = vi.spyOn(console, 'error').mockImplementation((...args) => {
|
||||
if (typeof args[0] === 'string' && args[0].includes('react-test-renderer is deprecated')) {
|
||||
return
|
||||
}
|
||||
original(...args)
|
||||
await act(async () => {
|
||||
renderer = create(createElement(Harness, { showNativeChat: true }))
|
||||
})
|
||||
try {
|
||||
subscribe.mockClear()
|
||||
// A dead PTY answers every subscribe with `subscribed`+`end`, so the stream is
|
||||
// gone again on each pass. Unbounded, that is a ~10s resubscribe loop.
|
||||
for (let revision = 1; revision <= 6; revision += 1) {
|
||||
subscriptionsRef.current.delete('terminal-1')
|
||||
await act(async () => {
|
||||
renderer = create(createElement(Harness, { showNativeChat: true }))
|
||||
renderer?.update(
|
||||
createElement(Harness, {
|
||||
showNativeChat: true,
|
||||
leaseReady: false,
|
||||
streamRevision: revision
|
||||
})
|
||||
)
|
||||
})
|
||||
subscribe.mockClear()
|
||||
// A dead PTY answers every subscribe with `subscribed`+`end`, so the stream is
|
||||
// gone again on each pass. Unbounded, that is a ~10s resubscribe loop.
|
||||
for (let revision = 1; revision <= 6; revision += 1) {
|
||||
subscriptionsRef.current.delete('terminal-1')
|
||||
await act(async () => {
|
||||
renderer?.update(
|
||||
createElement(Harness, {
|
||||
showNativeChat: true,
|
||||
leaseReady: false,
|
||||
streamRevision: revision
|
||||
})
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
expect(subscribe.mock.calls.length).toBeLessThanOrEqual(3)
|
||||
} finally {
|
||||
consoleSpy.mockRestore()
|
||||
}
|
||||
|
||||
expect(subscribe.mock.calls.length).toBeLessThanOrEqual(3)
|
||||
})
|
||||
|
||||
it('does not charge the rearm budget for a subscribe its own gates turned away', async () => {
|
||||
const original = console.error
|
||||
const consoleSpy = vi.spyOn(console, 'error').mockImplementation((...args) => {
|
||||
if (typeof args[0] === 'string' && args[0].includes('react-test-renderer is deprecated')) {
|
||||
return
|
||||
}
|
||||
original(...args)
|
||||
})
|
||||
try {
|
||||
await act(async () => {
|
||||
renderer = create(createElement(Harness, { showNativeChat: true }))
|
||||
|
|
@ -218,89 +177,66 @@ describe('useMobileNativeChatTerminalStream', () => {
|
|||
subscribe.mockImplementation((handle: string) =>
|
||||
subscriptionsRef.current.set(handle, () => {})
|
||||
)
|
||||
consoleSpy.mockRestore()
|
||||
}
|
||||
})
|
||||
|
||||
it('refills the rearm budget when terminal.list reports the handle again (#10681)', async () => {
|
||||
const original = console.error
|
||||
const consoleSpy = vi.spyOn(console, 'error').mockImplementation((...args) => {
|
||||
if (typeof args[0] === 'string' && args[0].includes('react-test-renderer is deprecated')) {
|
||||
return
|
||||
}
|
||||
original(...args)
|
||||
await act(async () => {
|
||||
renderer = create(createElement(Harness, { showNativeChat: true }))
|
||||
})
|
||||
try {
|
||||
await act(async () => {
|
||||
renderer = create(createElement(Harness, { showNativeChat: true }))
|
||||
})
|
||||
for (let revision = 1; revision <= 5; revision += 1) {
|
||||
subscriptionsRef.current.delete('terminal-1')
|
||||
await act(async () => {
|
||||
renderer?.update(
|
||||
createElement(Harness, {
|
||||
showNativeChat: true,
|
||||
leaseReady: false,
|
||||
streamRevision: revision
|
||||
})
|
||||
)
|
||||
})
|
||||
}
|
||||
subscribe.mockClear()
|
||||
// Budget is spent, so a further teardown signal alone changes nothing.
|
||||
await act(async () => {
|
||||
renderer?.update(
|
||||
createElement(Harness, { showNativeChat: true, leaseReady: false, streamRevision: 6 })
|
||||
)
|
||||
})
|
||||
expect(subscribe).not.toHaveBeenCalled()
|
||||
|
||||
// A dead-but-listed handle must not buy a new budget on every list refresh,
|
||||
// or the resubscribe loop this bound exists to stop comes straight back.
|
||||
await act(async () => {
|
||||
notifyListedHandlesRef.current(new Set(['terminal-1']))
|
||||
notifyListedHandlesRef.current(new Set(['terminal-1']))
|
||||
})
|
||||
expect(subscribe).not.toHaveBeenCalled()
|
||||
|
||||
// The handle actually went away and came back: it may have a live PTY once more.
|
||||
await act(async () => {
|
||||
notifyListedHandlesRef.current(new Set())
|
||||
notifyListedHandlesRef.current(new Set(['terminal-1']))
|
||||
})
|
||||
|
||||
expect(subscribe).toHaveBeenCalledWith('terminal-1')
|
||||
} finally {
|
||||
consoleSpy.mockRestore()
|
||||
}
|
||||
})
|
||||
|
||||
it('rearms on a teardown the lease cannot report (#10681)', async () => {
|
||||
const original = console.error
|
||||
const consoleSpy = vi.spyOn(console, 'error').mockImplementation((...args) => {
|
||||
if (typeof args[0] === 'string' && args[0].includes('react-test-renderer is deprecated')) {
|
||||
return
|
||||
}
|
||||
original(...args)
|
||||
})
|
||||
try {
|
||||
await act(async () => {
|
||||
renderer = create(createElement(Harness, { showNativeChat: true }))
|
||||
})
|
||||
subscribe.mockClear()
|
||||
// `end` with no preceding `subscribed` leaves the lease untouched, so
|
||||
// `leaseReady` holds its value and only the revision bump can re-run us.
|
||||
for (let revision = 1; revision <= 5; revision += 1) {
|
||||
subscriptionsRef.current.delete('terminal-1')
|
||||
await act(async () => {
|
||||
renderer?.update(
|
||||
createElement(Harness, { showNativeChat: true, leaseReady: true, streamRevision: 1 })
|
||||
createElement(Harness, {
|
||||
showNativeChat: true,
|
||||
leaseReady: false,
|
||||
streamRevision: revision
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
expect(subscribe).toHaveBeenCalledWith('terminal-1')
|
||||
} finally {
|
||||
consoleSpy.mockRestore()
|
||||
}
|
||||
subscribe.mockClear()
|
||||
// Budget is spent, so a further teardown signal alone changes nothing.
|
||||
await act(async () => {
|
||||
renderer?.update(
|
||||
createElement(Harness, { showNativeChat: true, leaseReady: false, streamRevision: 6 })
|
||||
)
|
||||
})
|
||||
expect(subscribe).not.toHaveBeenCalled()
|
||||
|
||||
// A dead-but-listed handle must not buy a new budget on every list refresh,
|
||||
// or the resubscribe loop this bound exists to stop comes straight back.
|
||||
await act(async () => {
|
||||
notifyListedHandlesRef.current(new Set(['terminal-1']))
|
||||
notifyListedHandlesRef.current(new Set(['terminal-1']))
|
||||
})
|
||||
expect(subscribe).not.toHaveBeenCalled()
|
||||
|
||||
// The handle actually went away and came back: it may have a live PTY once more.
|
||||
await act(async () => {
|
||||
notifyListedHandlesRef.current(new Set())
|
||||
notifyListedHandlesRef.current(new Set(['terminal-1']))
|
||||
})
|
||||
|
||||
expect(subscribe).toHaveBeenCalledWith('terminal-1')
|
||||
})
|
||||
|
||||
it('rearms on a teardown the lease cannot report (#10681)', async () => {
|
||||
await act(async () => {
|
||||
renderer = create(createElement(Harness, { showNativeChat: true }))
|
||||
})
|
||||
subscribe.mockClear()
|
||||
// `end` with no preceding `subscribed` leaves the lease untouched, so
|
||||
// `leaseReady` holds its value and only the revision bump can re-run us.
|
||||
subscriptionsRef.current.delete('terminal-1')
|
||||
await act(async () => {
|
||||
renderer?.update(
|
||||
createElement(Harness, { showNativeChat: true, leaseReady: true, streamRevision: 1 })
|
||||
)
|
||||
})
|
||||
|
||||
expect(subscribe).toHaveBeenCalledWith('terminal-1')
|
||||
})
|
||||
|
||||
it('does not let a dead PTY buy a new budget with its own `subscribed` ack', async () => {
|
||||
|
|
@ -411,34 +347,23 @@ describe('useMobileNativeChatTerminalStream', () => {
|
|||
})
|
||||
|
||||
it('resumes a cold-start lease-only stream when WebView readiness arrives late', async () => {
|
||||
const original = console.error
|
||||
const consoleSpy = vi.spyOn(console, 'error').mockImplementation((...args) => {
|
||||
if (typeof args[0] === 'string' && args[0].includes('react-test-renderer is deprecated')) {
|
||||
return
|
||||
}
|
||||
original(...args)
|
||||
webReadyRef.current.clear()
|
||||
await act(async () => {
|
||||
renderer = create(createElement(Harness, { showNativeChat: true }))
|
||||
})
|
||||
await act(async () => {
|
||||
renderer?.update(createElement(Harness, { showNativeChat: false }))
|
||||
})
|
||||
try {
|
||||
webReadyRef.current.clear()
|
||||
await act(async () => {
|
||||
renderer = create(createElement(Harness, { showNativeChat: true }))
|
||||
})
|
||||
await act(async () => {
|
||||
renderer?.update(createElement(Harness, { showNativeChat: false }))
|
||||
})
|
||||
|
||||
expect(unsubscribe).toHaveBeenCalledOnce()
|
||||
expect(subscribe).toHaveBeenCalledOnce()
|
||||
expect(unsubscribe).toHaveBeenCalledOnce()
|
||||
expect(subscribe).toHaveBeenCalledOnce()
|
||||
|
||||
webReadyRef.current.add('terminal-1')
|
||||
await act(async () => {
|
||||
notifyWebReadyRef.current('terminal-1', false)
|
||||
})
|
||||
webReadyRef.current.add('terminal-1')
|
||||
await act(async () => {
|
||||
notifyWebReadyRef.current('terminal-1', false)
|
||||
})
|
||||
|
||||
expect(unsubscribe).toHaveBeenNthCalledWith(2, 'terminal-1')
|
||||
expect(subscribe).toHaveBeenNthCalledWith(2, 'terminal-1')
|
||||
} finally {
|
||||
consoleSpy.mockRestore()
|
||||
}
|
||||
expect(unsubscribe).toHaveBeenNthCalledWith(2, 'terminal-1')
|
||||
expect(subscribe).toHaveBeenNthCalledWith(2, 'terminal-1')
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -112,8 +112,6 @@ async function setAppState(state: string): Promise<void> {
|
|||
|
||||
describe('useMobileSessionTabsReconciliation', () => {
|
||||
let renderer: ReactTestRenderer | null = null
|
||||
let consoleErrorSpy: ReturnType<typeof vi.spyOn>
|
||||
|
||||
async function mount(): Promise<void> {
|
||||
await act(async () => {
|
||||
renderer = create(createElement(Harness))
|
||||
|
|
@ -124,14 +122,6 @@ describe('useMobileSessionTabsReconciliation', () => {
|
|||
beforeEach(() => {
|
||||
vi.useFakeTimers()
|
||||
vi.setSystemTime(0)
|
||||
globalThis.IS_REACT_ACT_ENVIRONMENT = true
|
||||
const originalConsoleError = console.error
|
||||
consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation((...args) => {
|
||||
if (typeof args[0] === 'string' && args[0].includes('react-test-renderer is deprecated')) {
|
||||
return
|
||||
}
|
||||
originalConsoleError(...args)
|
||||
})
|
||||
lifecycle.appState = 'active'
|
||||
lifecycle.focused = true
|
||||
lifecycle.listeners.clear()
|
||||
|
|
@ -157,7 +147,6 @@ describe('useMobileSessionTabsReconciliation', () => {
|
|||
act(() => renderer?.unmount())
|
||||
renderer = null
|
||||
streamListener = null
|
||||
consoleErrorSpy.mockRestore()
|
||||
vi.useRealTimers()
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -52,7 +52,6 @@ describe('useMobileSessionViewMode', () => {
|
|||
let controller: MobileSessionViewModeController | null = null
|
||||
|
||||
beforeEach(() => {
|
||||
globalThis.IS_REACT_ACT_ENVIRONMENT = true
|
||||
vi.mocked(loadDefaultSessionView).mockReset().mockResolvedValue('terminal')
|
||||
vi.mocked(readSessionViewOverridesPreference)
|
||||
.mockReset()
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { createElement } from 'react'
|
||||
import { act, create, type ReactTestRenderer } from 'react-test-renderer'
|
||||
import { afterEach, beforeEach, describe, expect, it, vi, type MockInstance } from 'vitest'
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest'
|
||||
import type { TerminalQuickCommand } from '../../../src/shared/types'
|
||||
import type { RpcClient } from '../transport/rpc-client'
|
||||
import { LogicalClientCutoverError } from '../transport/stable-logical-rpc-client'
|
||||
|
|
@ -49,24 +49,10 @@ function deferred<T>() {
|
|||
describe('useQuickCommands', () => {
|
||||
let renderer: ReactTestRenderer | null = null
|
||||
let state: ReturnType<typeof useQuickCommands> | null = null
|
||||
let consoleSpy: MockInstance
|
||||
|
||||
beforeEach(() => {
|
||||
globalThis.IS_REACT_ACT_ENVIRONMENT = true
|
||||
const original = console.error
|
||||
consoleSpy = vi.spyOn(console, 'error').mockImplementation((...args) => {
|
||||
if (typeof args[0] === 'string' && args[0].includes('react-test-renderer is deprecated')) {
|
||||
return
|
||||
}
|
||||
original(...args)
|
||||
})
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
act(() => renderer?.unmount())
|
||||
renderer = null
|
||||
state = null
|
||||
consoleSpy.mockRestore()
|
||||
})
|
||||
|
||||
async function mount(client: RpcClient, enabled = true): Promise<void> {
|
||||
|
|
|
|||
|
|
@ -34,18 +34,6 @@ function createDeferred<T>(): Deferred<T> {
|
|||
return { promise, resolve }
|
||||
}
|
||||
|
||||
function suppressReactTestRendererDeprecationWarning(): () => void {
|
||||
const originalConsoleError = console.error
|
||||
const consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation((...args) => {
|
||||
const firstArg = args[0]
|
||||
if (typeof firstArg === 'string' && firstArg.includes('react-test-renderer is deprecated')) {
|
||||
return
|
||||
}
|
||||
originalConsoleError(...args)
|
||||
})
|
||||
return () => consoleErrorSpy.mockRestore()
|
||||
}
|
||||
|
||||
function createTerminalLiveInputModePreferenceHarness(): TerminalLiveInputModePreferenceHarness {
|
||||
let current: ReturnType<typeof useTerminalLiveInputModePreference> | null = null
|
||||
let renderer: ReactTestRenderer | null = null
|
||||
|
|
@ -58,14 +46,9 @@ function createTerminalLiveInputModePreferenceHarness(): TerminalLiveInputModePr
|
|||
return null
|
||||
}
|
||||
|
||||
const restoreConsoleError = suppressReactTestRendererDeprecationWarning()
|
||||
try {
|
||||
act(() => {
|
||||
renderer = create(createElement(Harness))
|
||||
})
|
||||
} finally {
|
||||
restoreConsoleError()
|
||||
}
|
||||
act(() => {
|
||||
renderer = create(createElement(Harness))
|
||||
})
|
||||
if (!current || !renderer) {
|
||||
throw new Error('terminal live input mode preference hook did not render')
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,11 @@
|
|||
import { createElement } from 'react'
|
||||
import { act, create, type ReactTestRenderer } from 'react-test-renderer'
|
||||
import { afterEach, beforeEach, describe, expect, it, vi, type MockInstance } from 'vitest'
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import { useThrottledLatestValue } from './use-throttled-latest-value'
|
||||
|
||||
describe('useThrottledLatestValue', () => {
|
||||
let renderer: ReactTestRenderer | null = null
|
||||
let latest: string | undefined
|
||||
let consoleSpy: MockInstance
|
||||
|
||||
function Harness({ value }: { value: string | undefined }): null {
|
||||
latest = useThrottledLatestValue(value, 50)
|
||||
|
|
@ -25,22 +24,13 @@ describe('useThrottledLatestValue', () => {
|
|||
|
||||
beforeEach(() => {
|
||||
vi.useFakeTimers()
|
||||
globalThis.IS_REACT_ACT_ENVIRONMENT = true
|
||||
latest = undefined
|
||||
const original = console.error
|
||||
consoleSpy = vi.spyOn(console, 'error').mockImplementation((...args) => {
|
||||
if (typeof args[0] === 'string' && args[0].includes('react-test-renderer is deprecated')) {
|
||||
return
|
||||
}
|
||||
original(...args)
|
||||
})
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
act(() => renderer?.unmount())
|
||||
renderer = null
|
||||
vi.useRealTimers()
|
||||
consoleSpy.mockRestore()
|
||||
})
|
||||
|
||||
it('emits the first frame immediately', () => {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { createElement, type ReactElement } from 'react'
|
||||
import { act, create, type ReactTestRenderer } from 'react-test-renderer'
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest'
|
||||
import type { RpcClient } from '../transport/rpc-client'
|
||||
import type { ConnectionState } from '../transport/types'
|
||||
import { MobileGitHistoryList } from './MobileGitHistoryList'
|
||||
|
|
@ -44,10 +44,6 @@ const compareResponse = {
|
|||
describe('MobileGitHistoryList', () => {
|
||||
let renderer: ReactTestRenderer | null = null
|
||||
|
||||
beforeEach(() => {
|
||||
globalThis.IS_REACT_ACT_ENVIRONMENT = true
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
act(() => renderer?.unmount())
|
||||
renderer = null
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { createElement } from 'react'
|
||||
import { act, create, type ReactTestRenderer } from 'react-test-renderer'
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest'
|
||||
import type { HostedReviewCreationEligibility } from '../../../src/shared/hosted-review'
|
||||
import {
|
||||
buildMobileHostedReviewEligibilityLoadKey,
|
||||
|
|
@ -206,10 +206,6 @@ describe('eligibility request ordering', () => {
|
|||
let renderer: ReactTestRenderer | null = null
|
||||
let renderedState: MobileCreatePrEligibilityState = { kind: 'idle' }
|
||||
|
||||
beforeEach(() => {
|
||||
globalThis.IS_REACT_ACT_ENVIRONMENT = true
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
act(() => renderer?.unmount())
|
||||
renderer = null
|
||||
|
|
|
|||
|
|
@ -41,18 +41,6 @@ vi.mock('lucide-react-native', () => ({
|
|||
RefreshCw: 'RefreshCw'
|
||||
}))
|
||||
|
||||
function suppressReactTestRendererDeprecationWarning(): () => void {
|
||||
const originalConsoleError = console.error
|
||||
const consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation((...args) => {
|
||||
const firstArg = args[0]
|
||||
if (typeof firstArg === 'string' && firstArg.includes('react-test-renderer is deprecated')) {
|
||||
return
|
||||
}
|
||||
originalConsoleError(...args)
|
||||
})
|
||||
return () => consoleErrorSpy.mockRestore()
|
||||
}
|
||||
|
||||
// Why: mounting TerminalWebView arms the web-ready watchdog; tests must
|
||||
// unmount so the timer can't survive into later tests and fire in teardown.
|
||||
let activeRenderer: ReactTestRenderer | null = null
|
||||
|
|
@ -62,14 +50,9 @@ function createTerminalWebViewRenderer(
|
|||
props: Record<string, unknown> = {}
|
||||
) {
|
||||
let renderer: ReactTestRenderer | null = null
|
||||
const restoreConsoleError = suppressReactTestRendererDeprecationWarning()
|
||||
try {
|
||||
act(() => {
|
||||
renderer = create(createElement(TerminalWebView, { onEngineError, ...props }))
|
||||
})
|
||||
} finally {
|
||||
restoreConsoleError()
|
||||
}
|
||||
act(() => {
|
||||
renderer = create(createElement(TerminalWebView, { onEngineError, ...props }))
|
||||
})
|
||||
if (!renderer) {
|
||||
throw new Error('TerminalWebView did not render')
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,18 +25,6 @@ function createDeferredBoolean(): DeferredBoolean {
|
|||
return { promise, resolve: resolvePromise }
|
||||
}
|
||||
|
||||
function suppressReactTestRendererDeprecationWarning(): () => void {
|
||||
const originalConsoleError = console.error
|
||||
const consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation((...args) => {
|
||||
const firstArg = args[0]
|
||||
if (typeof firstArg === 'string' && firstArg.includes('react-test-renderer is deprecated')) {
|
||||
return
|
||||
}
|
||||
originalConsoleError(...args)
|
||||
})
|
||||
return () => consoleErrorSpy.mockRestore()
|
||||
}
|
||||
|
||||
type AccessoryInputCommitHarnessOptions = {
|
||||
readonly heldText?: string
|
||||
readonly sentText?: string
|
||||
|
|
@ -105,14 +93,9 @@ function createAccessoryInputCommitHarness({
|
|||
return null
|
||||
}
|
||||
|
||||
const restoreConsoleError = suppressReactTestRendererDeprecationWarning()
|
||||
try {
|
||||
act(() => {
|
||||
renderer = create(createElement(Harness))
|
||||
})
|
||||
} finally {
|
||||
restoreConsoleError()
|
||||
}
|
||||
act(() => {
|
||||
renderer = create(createElement(Harness))
|
||||
})
|
||||
if (!commit || !renderer) {
|
||||
throw new Error('terminal live accessory input hook did not render')
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,18 +20,6 @@ type TerminalLiveInputCommitHarnessOptions = {
|
|||
readonly sendResult?: boolean
|
||||
}
|
||||
|
||||
function suppressReactTestRendererDeprecationWarning(): () => void {
|
||||
const originalConsoleError = console.error
|
||||
const consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation((...args) => {
|
||||
const firstArg = args[0]
|
||||
if (typeof firstArg === 'string' && firstArg.includes('react-test-renderer is deprecated')) {
|
||||
return
|
||||
}
|
||||
originalConsoleError(...args)
|
||||
})
|
||||
return () => consoleErrorSpy.mockRestore()
|
||||
}
|
||||
|
||||
function createTerminalLiveInputCommitHarness({
|
||||
sendResult = true
|
||||
}: TerminalLiveInputCommitHarnessOptions = {}): TerminalLiveInputCommitHarness {
|
||||
|
|
@ -77,14 +65,9 @@ function createTerminalLiveInputCommitHarness({
|
|||
return null
|
||||
}
|
||||
|
||||
const restoreConsoleError = suppressReactTestRendererDeprecationWarning()
|
||||
try {
|
||||
act(() => {
|
||||
renderer = create(createElement(Harness))
|
||||
})
|
||||
} finally {
|
||||
restoreConsoleError()
|
||||
}
|
||||
act(() => {
|
||||
renderer = create(createElement(Harness))
|
||||
})
|
||||
if (!handlers || !renderer) {
|
||||
throw new Error('terminal live input hook did not render')
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { createElement, type RefObject } from 'react'
|
||||
import { act, create, type ReactTestRenderer } from 'react-test-renderer'
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest'
|
||||
import type {
|
||||
TerminalLiveInputFocusTarget,
|
||||
TerminalLiveInputFocusTimerRef
|
||||
|
|
@ -20,17 +20,6 @@ type HarnessProps = {
|
|||
|
||||
type FocusHandlers = ReturnType<typeof useTerminalLiveInputFocus>
|
||||
|
||||
function suppressReactTestRendererWarning(): () => void {
|
||||
const originalConsoleError = console.error
|
||||
const spy = vi.spyOn(console, 'error').mockImplementation((...args) => {
|
||||
if (typeof args[0] === 'string' && args[0].includes('react-test-renderer is deprecated')) {
|
||||
return
|
||||
}
|
||||
originalConsoleError(...args)
|
||||
})
|
||||
return () => spy.mockRestore()
|
||||
}
|
||||
|
||||
function createFocusTarget(initiallyFocused = false): TerminalLiveInputFocusTarget & {
|
||||
readonly blur: ReturnType<typeof vi.fn>
|
||||
readonly focus: ReturnType<typeof vi.fn>
|
||||
|
|
@ -67,14 +56,9 @@ function createHarness(initialProps: HarnessProps): {
|
|||
return null
|
||||
}
|
||||
|
||||
const restoreWarning = suppressReactTestRendererWarning()
|
||||
try {
|
||||
act(() => {
|
||||
renderer = create(createElement(Harness, initialProps))
|
||||
})
|
||||
} finally {
|
||||
restoreWarning()
|
||||
}
|
||||
act(() => {
|
||||
renderer = create(createElement(Harness, initialProps))
|
||||
})
|
||||
if (!handlers || !renderer) {
|
||||
throw new Error('terminal live input focus harness did not render')
|
||||
}
|
||||
|
|
@ -113,10 +97,6 @@ function connectedProps(
|
|||
}
|
||||
|
||||
describe('terminal live input focus hook', () => {
|
||||
beforeEach(() => {
|
||||
globalThis.IS_REACT_ACT_ENVIRONMENT = true
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
vi.useRealTimers()
|
||||
})
|
||||
|
|
|
|||
|
|
@ -71,18 +71,6 @@ type Harness = {
|
|||
readonly unmount: () => void
|
||||
}
|
||||
|
||||
function suppressReactTestRendererDeprecationWarning(): () => void {
|
||||
const originalConsoleError = console.error
|
||||
const spy = vi.spyOn(console, 'error').mockImplementation((...args) => {
|
||||
const firstArg = args[0]
|
||||
if (typeof firstArg === 'string' && firstArg.includes('react-test-renderer is deprecated')) {
|
||||
return
|
||||
}
|
||||
originalConsoleError(...args)
|
||||
})
|
||||
return () => spy.mockRestore()
|
||||
}
|
||||
|
||||
async function renderHarness(hostId: string): Promise<Harness> {
|
||||
let hook: ReturnType<typeof useHostClient> | null = null
|
||||
let closeHost: ((hostId: string) => void) | null = null
|
||||
|
|
@ -94,14 +82,9 @@ async function renderHarness(hostId: string): Promise<Harness> {
|
|||
return null
|
||||
}
|
||||
|
||||
const restore = suppressReactTestRendererDeprecationWarning()
|
||||
try {
|
||||
await act(async () => {
|
||||
renderer = create(createElement(RpcClientProvider, null, createElement(Probe)))
|
||||
})
|
||||
} finally {
|
||||
restore()
|
||||
}
|
||||
await act(async () => {
|
||||
renderer = create(createElement(RpcClientProvider, null, createElement(Probe)))
|
||||
})
|
||||
if (!hook || !closeHost || !renderer) {
|
||||
throw new Error('harness did not render')
|
||||
}
|
||||
|
|
@ -124,7 +107,6 @@ async function renderHarness(hostId: string): Promise<Harness> {
|
|||
}
|
||||
|
||||
beforeEach(() => {
|
||||
globalThis.IS_REACT_ACT_ENVIRONMENT = true
|
||||
connectMock.mockReset()
|
||||
loadHostsMock.mockReset()
|
||||
})
|
||||
|
|
@ -149,7 +131,6 @@ describe('useHostClient', () => {
|
|||
return null
|
||||
}
|
||||
|
||||
const restore = suppressReactTestRendererDeprecationWarning()
|
||||
try {
|
||||
await act(async () => {
|
||||
renderer = create(createElement(RpcClientProvider, null, createElement(Probe)))
|
||||
|
|
@ -169,7 +150,6 @@ describe('useHostClient', () => {
|
|||
expect(selectedState).toBe('disconnected')
|
||||
expect(connectMock).toHaveBeenCalledTimes(2)
|
||||
} finally {
|
||||
restore()
|
||||
act(() => renderer?.unmount())
|
||||
}
|
||||
})
|
||||
|
|
@ -188,7 +168,6 @@ describe('useHostClient', () => {
|
|||
return null
|
||||
}
|
||||
|
||||
const restore = suppressReactTestRendererDeprecationWarning()
|
||||
try {
|
||||
await act(async () => {
|
||||
renderer = create(createElement(RpcClientProvider, null, createElement(Probe)))
|
||||
|
|
@ -211,7 +190,6 @@ describe('useHostClient', () => {
|
|||
})
|
||||
expect(stateByRenderTick.get(2)).toBe('connecting')
|
||||
} finally {
|
||||
restore()
|
||||
act(() => renderer?.unmount())
|
||||
}
|
||||
})
|
||||
|
|
@ -263,7 +241,6 @@ describe('useHostClient', () => {
|
|||
states.push(useHostClient(HOST.id).state)
|
||||
return null
|
||||
}
|
||||
const restore = suppressReactTestRendererDeprecationWarning()
|
||||
try {
|
||||
act(() => {
|
||||
renderer = create(createElement(RpcClientProvider, null, createElement(Probe)))
|
||||
|
|
@ -277,7 +254,6 @@ describe('useHostClient', () => {
|
|||
expect(states.at(-1)).toBe('connecting')
|
||||
expect(states).not.toContain('disconnected')
|
||||
} finally {
|
||||
restore()
|
||||
act(() => renderer?.unmount())
|
||||
}
|
||||
})
|
||||
|
|
@ -296,7 +272,6 @@ describe('useHostClient', () => {
|
|||
states.push(useHostClient(HOST.id).state)
|
||||
return null
|
||||
}
|
||||
const restore = suppressReactTestRendererDeprecationWarning()
|
||||
try {
|
||||
await act(async () => {
|
||||
renderer = create(createElement(RpcClientProvider, null, createElement(Probe)))
|
||||
|
|
@ -311,7 +286,6 @@ describe('useHostClient', () => {
|
|||
expect(states.at(-1)).toBe('connecting')
|
||||
expect(states).not.toContain('disconnected')
|
||||
} finally {
|
||||
restore()
|
||||
act(() => renderer?.unmount())
|
||||
}
|
||||
})
|
||||
|
|
@ -333,14 +307,9 @@ describe('useHostClient', () => {
|
|||
return null
|
||||
}
|
||||
|
||||
const restore = suppressReactTestRendererDeprecationWarning()
|
||||
try {
|
||||
act(() => {
|
||||
renderer = create(createElement(RpcClientProvider, null, createElement(Probe)))
|
||||
})
|
||||
} finally {
|
||||
restore()
|
||||
}
|
||||
act(() => {
|
||||
renderer = create(createElement(RpcClientProvider, null, createElement(Probe)))
|
||||
})
|
||||
expect(loadHostsMock).toHaveBeenCalledOnce()
|
||||
if (!closeHost || !resolveHosts || !renderer) {
|
||||
throw new Error('pending-open harness did not initialize')
|
||||
|
|
@ -370,14 +339,9 @@ describe('useHostClient', () => {
|
|||
useHostClient(HOST.id)
|
||||
return null
|
||||
}
|
||||
const restore = suppressReactTestRendererDeprecationWarning()
|
||||
try {
|
||||
act(() => {
|
||||
renderer = create(createElement(RpcClientProvider, null, createElement(Probe)))
|
||||
})
|
||||
} finally {
|
||||
restore()
|
||||
}
|
||||
act(() => {
|
||||
renderer = create(createElement(RpcClientProvider, null, createElement(Probe)))
|
||||
})
|
||||
expect(loadHostsMock).toHaveBeenCalledOnce()
|
||||
act(() => renderer?.unmount())
|
||||
await act(async () => {
|
||||
|
|
@ -401,7 +365,6 @@ describe('useAllHostClients', () => {
|
|||
return null
|
||||
}
|
||||
|
||||
const restore = suppressReactTestRendererDeprecationWarning()
|
||||
try {
|
||||
await act(async () => {
|
||||
renderer = create(createElement(RpcClientProvider, null, createElement(Probe)))
|
||||
|
|
@ -410,7 +373,6 @@ describe('useAllHostClients', () => {
|
|||
expect(connectMock).toHaveBeenCalledOnce()
|
||||
expect(connectMock).toHaveBeenCalledWith(host2, expect.any(Function))
|
||||
} finally {
|
||||
restore()
|
||||
act(() => renderer?.unmount())
|
||||
}
|
||||
})
|
||||
|
|
@ -433,7 +395,6 @@ describe('useAllHostClients', () => {
|
|||
return null
|
||||
}
|
||||
|
||||
const restore = suppressReactTestRendererDeprecationWarning()
|
||||
try {
|
||||
await act(async () => {
|
||||
renderer = create(createElement(RpcClientProvider, null, createElement(Probe)))
|
||||
|
|
@ -446,7 +407,6 @@ describe('useAllHostClients', () => {
|
|||
'host-997'
|
||||
])
|
||||
} finally {
|
||||
restore()
|
||||
act(() => renderer?.unmount())
|
||||
}
|
||||
})
|
||||
|
|
@ -477,7 +437,6 @@ describe('useAllHostClients', () => {
|
|||
return null
|
||||
}
|
||||
|
||||
const restore = suppressReactTestRendererDeprecationWarning()
|
||||
try {
|
||||
await act(async () => {
|
||||
renderer = create(
|
||||
|
|
@ -504,7 +463,6 @@ describe('useAllHostClients', () => {
|
|||
expect(clients.get('host-c')?.closeMock).toHaveBeenCalledOnce()
|
||||
expect(clients.get('host-d')?.closeMock).not.toHaveBeenCalled()
|
||||
} finally {
|
||||
restore()
|
||||
act(() => renderer?.unmount())
|
||||
}
|
||||
})
|
||||
|
|
@ -520,7 +478,6 @@ describe('useAllHostClients', () => {
|
|||
return null
|
||||
}
|
||||
|
||||
const restore = suppressReactTestRendererDeprecationWarning()
|
||||
try {
|
||||
await act(async () => {
|
||||
renderer = create(createElement(RpcClientProvider, null, createElement(Probe)))
|
||||
|
|
@ -528,7 +485,6 @@ describe('useAllHostClients', () => {
|
|||
})
|
||||
expect(connectMock).toHaveBeenCalledTimes(2)
|
||||
} finally {
|
||||
restore()
|
||||
act(() => renderer?.unmount())
|
||||
}
|
||||
})
|
||||
|
|
@ -545,7 +501,6 @@ describe('useAllHostClients', () => {
|
|||
return null
|
||||
}
|
||||
|
||||
const restore = suppressReactTestRendererDeprecationWarning()
|
||||
try {
|
||||
await act(async () => {
|
||||
renderer = create(createElement(RpcClientProvider, null, createElement(Probe)))
|
||||
|
|
@ -559,7 +514,6 @@ describe('useAllHostClients', () => {
|
|||
})
|
||||
expect(connectMock).toHaveBeenCalledOnce()
|
||||
} finally {
|
||||
restore()
|
||||
act(() => renderer?.unmount())
|
||||
}
|
||||
})
|
||||
|
|
|
|||
|
|
@ -4,17 +4,6 @@ import { describe, expect, it, vi } from 'vitest'
|
|||
import type { RpcClient } from './rpc-client'
|
||||
import { useHostStatusGates, type HostStatusGates } from './host-status-gates'
|
||||
|
||||
function suppressReactTestRendererDeprecationWarning(): () => void {
|
||||
const originalConsoleError = console.error
|
||||
const spy = vi.spyOn(console, 'error').mockImplementation((...args) => {
|
||||
if (typeof args[0] === 'string' && args[0].includes('react-test-renderer is deprecated')) {
|
||||
return
|
||||
}
|
||||
originalConsoleError(...args)
|
||||
})
|
||||
return () => spy.mockRestore()
|
||||
}
|
||||
|
||||
describe('useHostStatusGates', () => {
|
||||
it('clears every prior-host gate and ignores its late response while the client is replaced', async () => {
|
||||
let resolveOldStatus: ((response: unknown) => void) | null = null
|
||||
|
|
@ -43,7 +32,6 @@ describe('useHostStatusGates', () => {
|
|||
return null
|
||||
}
|
||||
|
||||
const restore = suppressReactTestRendererDeprecationWarning()
|
||||
try {
|
||||
await act(async () => {
|
||||
renderer = create(createElement(Probe, { hostId: 'host-1', client: oldClient }))
|
||||
|
|
@ -80,7 +68,6 @@ describe('useHostStatusGates', () => {
|
|||
expect(oldSendRequest).toHaveBeenCalledOnce()
|
||||
expect(newSendRequest).toHaveBeenCalledOnce()
|
||||
} finally {
|
||||
restore()
|
||||
renderer?.unmount()
|
||||
}
|
||||
})
|
||||
|
|
@ -102,7 +89,6 @@ describe('useHostStatusGates', () => {
|
|||
return null
|
||||
}
|
||||
|
||||
const restore = suppressReactTestRendererDeprecationWarning()
|
||||
try {
|
||||
await act(async () => {
|
||||
renderer = create(createElement(Probe, { hostId: 'host-1' }))
|
||||
|
|
@ -115,7 +101,6 @@ describe('useHostStatusGates', () => {
|
|||
|
||||
expect(sendRequest).toHaveBeenCalledOnce()
|
||||
} finally {
|
||||
restore()
|
||||
renderer?.unmount()
|
||||
}
|
||||
})
|
||||
|
|
@ -141,7 +126,6 @@ describe('useHostStatusGates', () => {
|
|||
return null
|
||||
}
|
||||
|
||||
const restore = suppressReactTestRendererDeprecationWarning()
|
||||
try {
|
||||
await act(async () => {
|
||||
renderer = create(createElement(Probe, { connState: 'connected' }))
|
||||
|
|
@ -181,7 +165,6 @@ describe('useHostStatusGates', () => {
|
|||
statusPending: false
|
||||
})
|
||||
} finally {
|
||||
restore()
|
||||
renderer?.unmount()
|
||||
}
|
||||
})
|
||||
|
|
@ -204,7 +187,6 @@ describe('useHostStatusGates', () => {
|
|||
return null
|
||||
}
|
||||
|
||||
const restore = suppressReactTestRendererDeprecationWarning()
|
||||
try {
|
||||
await act(async () => {
|
||||
renderer = create(createElement(Probe, { client: firstClient }))
|
||||
|
|
@ -217,7 +199,6 @@ describe('useHostStatusGates', () => {
|
|||
})
|
||||
expect(gates).toMatchObject({ hostCapabilities: [], statusPending: true })
|
||||
} finally {
|
||||
restore()
|
||||
renderer?.unmount()
|
||||
}
|
||||
})
|
||||
|
|
|
|||
|
|
@ -151,29 +151,13 @@ function TestApp({
|
|||
)
|
||||
}
|
||||
|
||||
function suppressRendererWarning(): () => void {
|
||||
const originalConsoleError = console.error
|
||||
const spy = vi.spyOn(console, 'error').mockImplementation((...args) => {
|
||||
if (typeof args[0] === 'string' && args[0].includes('react-test-renderer is deprecated')) {
|
||||
return
|
||||
}
|
||||
originalConsoleError(...args)
|
||||
})
|
||||
return () => spy.mockRestore()
|
||||
}
|
||||
|
||||
async function renderScreen(screen: Screen, detailHostId?: string): Promise<ReactTestRenderer> {
|
||||
let renderer: ReactTestRenderer | null = null
|
||||
const restore = suppressRendererWarning()
|
||||
try {
|
||||
await act(async () => {
|
||||
renderer = create(createElement(TestApp, { screen, detailHostId }))
|
||||
await Promise.resolve()
|
||||
await Promise.resolve()
|
||||
})
|
||||
} finally {
|
||||
restore()
|
||||
}
|
||||
await act(async () => {
|
||||
renderer = create(createElement(TestApp, { screen, detailHostId }))
|
||||
await Promise.resolve()
|
||||
await Promise.resolve()
|
||||
})
|
||||
if (!renderer) {
|
||||
throw new Error('settings lifecycle harness did not render')
|
||||
}
|
||||
|
|
@ -203,7 +187,6 @@ function activeHostIds(): string[] {
|
|||
}
|
||||
|
||||
beforeEach(() => {
|
||||
globalThis.IS_REACT_ACT_ENVIRONMENT = true
|
||||
context = null
|
||||
routeFocus.effect = null
|
||||
connectMock.mockReset()
|
||||
|
|
@ -235,16 +218,11 @@ describe('settings host client lifecycle', () => {
|
|||
}
|
||||
|
||||
let renderer: ReactTestRenderer | null = null
|
||||
const restore = suppressRendererWarning()
|
||||
try {
|
||||
await act(async () => {
|
||||
renderer = create(createElement(NavigationStack, { settingsVisible: false }))
|
||||
await Promise.resolve()
|
||||
await Promise.resolve()
|
||||
})
|
||||
} finally {
|
||||
restore()
|
||||
}
|
||||
await act(async () => {
|
||||
renderer = create(createElement(NavigationStack, { settingsVisible: false }))
|
||||
await Promise.resolve()
|
||||
await Promise.resolve()
|
||||
})
|
||||
if (!renderer) {
|
||||
throw new Error('navigation stack harness did not render')
|
||||
}
|
||||
|
|
@ -354,20 +332,15 @@ describe('settings host client lifecycle', () => {
|
|||
}
|
||||
|
||||
let renderer: ReactTestRenderer | null = null
|
||||
const restore = suppressRendererWarning()
|
||||
try {
|
||||
await act(async () => {
|
||||
renderer = create(
|
||||
createElement(HostListApp, {
|
||||
settingsHostIds: [retainedHostId, sharedHostId, removedHostId]
|
||||
})
|
||||
)
|
||||
await Promise.resolve()
|
||||
await Promise.resolve()
|
||||
})
|
||||
} finally {
|
||||
restore()
|
||||
}
|
||||
await act(async () => {
|
||||
renderer = create(
|
||||
createElement(HostListApp, {
|
||||
settingsHostIds: [retainedHostId, sharedHostId, removedHostId]
|
||||
})
|
||||
)
|
||||
await Promise.resolve()
|
||||
await Promise.resolve()
|
||||
})
|
||||
if (!renderer) {
|
||||
throw new Error('host-list lifecycle harness did not render')
|
||||
}
|
||||
|
|
@ -451,14 +424,9 @@ describe('settings host client lifecycle', () => {
|
|||
}
|
||||
|
||||
let renderer: ReactTestRenderer | null = null
|
||||
const restore = suppressRendererWarning()
|
||||
try {
|
||||
act(() => {
|
||||
renderer = create(createElement(RetryApp, { settingsVisible: true }))
|
||||
})
|
||||
} finally {
|
||||
restore()
|
||||
}
|
||||
act(() => {
|
||||
renderer = create(createElement(RetryApp, { settingsVisible: true }))
|
||||
})
|
||||
if (!renderer || !resolveInitial || !resolveRetry) {
|
||||
throw new Error('retry lifecycle harness did not initialize')
|
||||
}
|
||||
|
|
@ -517,14 +485,9 @@ describe('settings host client lifecycle', () => {
|
|||
}
|
||||
|
||||
let renderer: ReactTestRenderer | null = null
|
||||
const restore = suppressRendererWarning()
|
||||
try {
|
||||
act(() => {
|
||||
renderer = create(createElement(PendingApp, { visible: true }))
|
||||
})
|
||||
} finally {
|
||||
restore()
|
||||
}
|
||||
act(() => {
|
||||
renderer = create(createElement(PendingApp, { visible: true }))
|
||||
})
|
||||
if (!renderer || !resolveFirst || !resolveSecond) {
|
||||
throw new Error('pending settings harness did not initialize')
|
||||
}
|
||||
|
|
@ -580,17 +543,12 @@ describe('settings host client lifecycle', () => {
|
|||
}
|
||||
|
||||
let renderer: ReactTestRenderer | null = null
|
||||
const restore = suppressRendererWarning()
|
||||
try {
|
||||
await act(async () => {
|
||||
renderer = create(
|
||||
createElement(ReplacementApp, { detailVisible: false, settingsVisible: false })
|
||||
)
|
||||
await Promise.resolve()
|
||||
})
|
||||
} finally {
|
||||
restore()
|
||||
}
|
||||
await act(async () => {
|
||||
renderer = create(
|
||||
createElement(ReplacementApp, { detailVisible: false, settingsVisible: false })
|
||||
)
|
||||
await Promise.resolve()
|
||||
})
|
||||
if (!renderer || !context) {
|
||||
throw new Error('replacement lifecycle harness did not initialize')
|
||||
}
|
||||
|
|
@ -665,14 +623,9 @@ describe('settings host client lifecycle', () => {
|
|||
}
|
||||
|
||||
let renderer: ReactTestRenderer | null = null
|
||||
const restore = suppressRendererWarning()
|
||||
try {
|
||||
act(() => {
|
||||
renderer = create(createElement(ManualApp, { selected: false }))
|
||||
})
|
||||
} finally {
|
||||
restore()
|
||||
}
|
||||
act(() => {
|
||||
renderer = create(createElement(ManualApp, { selected: false }))
|
||||
})
|
||||
if (!renderer || !context) {
|
||||
throw new Error('manual connection harness did not initialize')
|
||||
}
|
||||
|
|
@ -735,16 +688,11 @@ describe('settings host client lifecycle', () => {
|
|||
}
|
||||
|
||||
let renderer: ReactTestRenderer | null = null
|
||||
const restore = suppressRendererWarning()
|
||||
try {
|
||||
await act(async () => {
|
||||
renderer = create(createElement(FocusStack))
|
||||
await Promise.resolve()
|
||||
await Promise.resolve()
|
||||
})
|
||||
} finally {
|
||||
restore()
|
||||
}
|
||||
await act(async () => {
|
||||
renderer = create(createElement(FocusStack))
|
||||
await Promise.resolve()
|
||||
await Promise.resolve()
|
||||
})
|
||||
if (!renderer || !routeFocus.effect) {
|
||||
throw new Error('settings focus harness did not initialize')
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,8 @@ export default defineConfig({
|
|||
oxc: vitestOxcConfig,
|
||||
test: {
|
||||
environment: 'node',
|
||||
setupFiles: ['./vitest.setup.ts'],
|
||||
onConsoleLog: (log) => !log.includes('react-test-renderer is deprecated'),
|
||||
// .tsx too: component tests exist (react-test-renderer + mocked react-native) and were
|
||||
// silently never collected, so render-level regressions shipped untested.
|
||||
include: ['src/**/*.test.ts', 'src/**/*.test.tsx']
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
Object.assign(globalThis, { IS_REACT_ACT_ENVIRONMENT: true })
|
||||
Loading…
Reference in New Issue