mirror of
https://github.com/stablyai/orca.git
synced 2026-10-30 16:47:34 +00:00
test: strengthen watcher and mobile async assertions (#33641)
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
This commit is contained in:
@@ -429,7 +429,6 @@
|
||||
1 test-no-real-time-wait:src/renderer/src/components/dashboard-popout/useDashboardSnapshot.test.tsx
|
||||
1 test-no-real-time-wait:src/renderer/src/components/editor/combined-diff/load-sections/combined-diff-stale-analysis-work-budget.test.tsx
|
||||
1 test-no-real-time-wait:src/renderer/src/components/editor/DiagramViewerModal.test.tsx
|
||||
2 test-no-real-time-wait:src/renderer/src/components/mobile/MobilePage.test.tsx
|
||||
1 test-no-real-time-wait:src/renderer/src/components/native-chat/native-chat-composer-draft-lifecycle.test.tsx
|
||||
6 test-no-real-time-wait:src/renderer/src/components/native-chat/native-chat-composer-draft-store.test.ts
|
||||
1 test-no-real-time-wait:src/renderer/src/components/native-chat/NativeChatModelCombobox.test.tsx
|
||||
|
||||
@@ -116,6 +116,15 @@ describe('WatcherProcessCrashFuse', () => {
|
||||
expect(fuse.isOpen(crashAt)).toBe(false)
|
||||
fuse.recordCrash(crashAt)
|
||||
expect(fuse.isOpen(crashAt + 2 * CRASH_FUSE_COOLDOWN_INITIAL_MS - 1)).toBe(true)
|
||||
|
||||
const nextTrialAt = crashAt + 2 * CRASH_FUSE_COOLDOWN_INITIAL_MS
|
||||
expect(fuse.isOpen(nextTrialAt)).toBe(false)
|
||||
fuse.recordDeliveryHealth('healthy', nextTrialAt)
|
||||
fuse.recordDeliveryHealth('unknown', nextTrialAt + CRASH_FUSE_TRIAL_STABLE_MS - 1)
|
||||
const stableAt = nextTrialAt + CRASH_FUSE_TRIAL_STABLE_MS
|
||||
expect(fuse.isOpen(stableAt)).toBe(false)
|
||||
fuse.recordCrash(stableAt + 1)
|
||||
expect(fuse.isOpen(stableAt + 1)).toBe(false)
|
||||
})
|
||||
|
||||
it('closes immediately when explicitly reset', () => {
|
||||
|
||||
@@ -37,7 +37,7 @@ it('joins disposal and does not confuse disconnect/error with physical exit', as
|
||||
const result = first.then(disposed)
|
||||
c.events.emit('disconnect')
|
||||
c.events.emit('error', new Error('spawn or IPC failure'))
|
||||
await Promise.resolve()
|
||||
await new Promise<void>((resolve) => setImmediate(resolve))
|
||||
expect(disposed).not.toHaveBeenCalled()
|
||||
expect(logical).toHaveBeenCalledOnce()
|
||||
c.close()
|
||||
@@ -55,7 +55,7 @@ it('includes children already signaled by synchronous or retired-owner disposal'
|
||||
const disposed = vi.fn()
|
||||
const result = owner.disposeAndWait(() => {}).then(disposed)
|
||||
replacement.close()
|
||||
await Promise.resolve()
|
||||
await new Promise<void>((resolve) => setImmediate(resolve))
|
||||
expect(disposed).not.toHaveBeenCalled()
|
||||
old.close()
|
||||
await result
|
||||
@@ -71,7 +71,7 @@ it('retains a child after termination deadline failure and supports explicit ret
|
||||
expect(await result).toMatchObject({ message: 'watcher_owned_children_shutdown_incomplete' })
|
||||
const completed = vi.fn()
|
||||
const retry = owner.disposeAndWait(() => {}).then(completed)
|
||||
await Promise.resolve()
|
||||
await vi.advanceTimersByTimeAsync(0)
|
||||
expect(completed).not.toHaveBeenCalled()
|
||||
c.close()
|
||||
await retry
|
||||
@@ -96,7 +96,7 @@ it('waits for other children even when logical disposal and one kill fail', asyn
|
||||
finished()
|
||||
return error
|
||||
})
|
||||
await Promise.resolve()
|
||||
await new Promise<void>((resolve) => setImmediate(resolve))
|
||||
expect(finished).not.toHaveBeenCalled()
|
||||
pending.close()
|
||||
expect(await result).toMatchObject({ errors: [logicalFailure, expect.any(Error)] })
|
||||
|
||||
@@ -32,7 +32,7 @@ describe('disposeWatcherProcessAndWait', () => {
|
||||
settled()
|
||||
throw error
|
||||
})
|
||||
await Promise.resolve()
|
||||
await new Promise<void>((resolve) => setImmediate(resolve))
|
||||
expect(settled).not.toHaveBeenCalled()
|
||||
finishPool()
|
||||
await expect(disposal).rejects.toMatchObject({ message: 'watcher_process_shutdown_incomplete' })
|
||||
|
||||
@@ -86,21 +86,6 @@ describe('subscribeViaWatcherProcess', () => {
|
||||
expect(callback).toHaveBeenCalledWith(null, events)
|
||||
})
|
||||
|
||||
it('retires the failed native subscription before reporting its error', async () => {
|
||||
const callback = vi.fn()
|
||||
const promise = subscribeViaWatcherProcess('/repo', callback, {})
|
||||
const child = currentChild()
|
||||
const id = ackSubscribe(child)
|
||||
await promise
|
||||
|
||||
child.emit('message', { op: 'watch-error', id, message: 'boom' })
|
||||
expect(callback).not.toHaveBeenCalled()
|
||||
expect(child.kill).toHaveBeenCalledTimes(1)
|
||||
child.emit('exit', 0, null)
|
||||
await vi.waitFor(() => expect(callback).toHaveBeenCalledTimes(1))
|
||||
expect(callback).toHaveBeenCalledWith(expect.objectContaining({ message: 'boom' }), [])
|
||||
})
|
||||
|
||||
it('rejects and physically cancels a watcher error before subscription readiness', async () => {
|
||||
const callback = vi.fn()
|
||||
const promise = subscribeViaWatcherProcess('/repo', callback, {})
|
||||
@@ -115,8 +100,10 @@ describe('subscribeViaWatcherProcess', () => {
|
||||
settled = true
|
||||
})
|
||||
expect(child.sent.at(-1)).toEqual({ op: 'cancel-subscribe', id })
|
||||
await new Promise<void>((resolve) => setImmediate(resolve))
|
||||
expect(settled).toBe(false)
|
||||
child.emit('message', { op: 'cancel-requires-restart', id })
|
||||
await new Promise<void>((resolve) => setImmediate(resolve))
|
||||
expect(settled).toBe(false)
|
||||
expect(callback).not.toHaveBeenCalled()
|
||||
expect(child.kill).toHaveBeenCalledTimes(1)
|
||||
@@ -365,6 +352,7 @@ describe('subscribeViaWatcherProcess', () => {
|
||||
const isSettled = trackPromiseSettlement(pending)
|
||||
expect(child.kill).not.toHaveBeenCalled()
|
||||
expect(child.sent.at(-1)).toEqual({ op: 'cancel-subscribe', id: queuedId })
|
||||
await new Promise<void>((resolve) => setImmediate(resolve))
|
||||
expect(isSettled()).toBe(false)
|
||||
child.emit('message', { op: 'unsubscribed', id: queuedId })
|
||||
await expect(pending).rejects.toThrow('aborted')
|
||||
@@ -443,6 +431,7 @@ describe('subscribeViaWatcherProcess', () => {
|
||||
expect(first.sent.at(-1)).toEqual({ op: 'cancel-subscribe', id: pendingId })
|
||||
first.emit('message', { op: 'cancel-requires-restart', id: pendingId })
|
||||
expect(first.kill).toHaveBeenCalledTimes(1)
|
||||
await new Promise<void>((resolve) => setImmediate(resolve))
|
||||
expect(isSettled()).toBe(false)
|
||||
expect(forkMock).toHaveBeenCalledTimes(1)
|
||||
first.emit('exit', 0, null)
|
||||
@@ -695,6 +684,7 @@ describe('subscribeViaWatcherProcess', () => {
|
||||
const unsubscribe = subscription.unsubscribe()
|
||||
const isSettled = trackPromiseSettlement(unsubscribe)
|
||||
expect(first.kill).toHaveBeenCalledTimes(1)
|
||||
await new Promise<void>((resolve) => setImmediate(resolve))
|
||||
expect(isSettled()).toBe(false)
|
||||
expect(rmSyncMock).toHaveBeenCalledWith('/tmp/orca-watcher-canary-supervisor-test', {
|
||||
recursive: true,
|
||||
@@ -757,12 +747,17 @@ describe('subscribeViaWatcherProcess', () => {
|
||||
const second = await secondPromise
|
||||
|
||||
const firstUnsub = first.unsubscribe()
|
||||
const firstSettled = trackPromiseSettlement(firstUnsub)
|
||||
expect(child.kill).not.toHaveBeenCalled()
|
||||
expect(child.sent.at(-1)).toMatchObject({ op: 'unsubscribe' })
|
||||
|
||||
// Both callers may gate destructive cleanup, so both must await physical exit.
|
||||
const secondUnsub = second.unsubscribe()
|
||||
const secondSettled = trackPromiseSettlement(secondUnsub)
|
||||
expect(child.kill).toHaveBeenCalledTimes(1)
|
||||
await new Promise<void>((resolve) => setImmediate(resolve))
|
||||
expect(firstSettled()).toBe(false)
|
||||
expect(secondSettled()).toBe(false)
|
||||
child.emit('exit', 0, null)
|
||||
await expect(firstUnsub).resolves.toBeUndefined()
|
||||
await expect(secondUnsub).resolves.toBeUndefined()
|
||||
@@ -801,7 +796,7 @@ describe('subscribeViaWatcherProcess', () => {
|
||||
expect(unsubscribe).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('rejects a pre-aborted in-process subscribe before loading Parcel', async () => {
|
||||
it('rejects a pre-aborted in-process subscribe before invoking Parcel subscribe', async () => {
|
||||
vi.stubEnv('VITEST', 'true')
|
||||
const controller = new AbortController()
|
||||
controller.abort()
|
||||
|
||||
@@ -77,6 +77,8 @@ describe('shallow watcher subscription', () => {
|
||||
}
|
||||
)
|
||||
|
||||
emit(root, 'unrelated')
|
||||
emit(join(root, 'logs'), 'unrelated')
|
||||
emit(root, 'config')
|
||||
emit(join(root, 'logs'), 'HEAD')
|
||||
await promise
|
||||
@@ -84,6 +86,7 @@ describe('shallow watcher subscription', () => {
|
||||
expect(events).toContain(join(root, 'config'))
|
||||
expect(events).toContain(join(root, 'logs', 'HEAD'))
|
||||
expect(events).not.toContain(join(root, 'unrelated'))
|
||||
expect(events).not.toContain(join(root, 'logs', 'unrelated'))
|
||||
await subscription.unsubscribe()
|
||||
} finally {
|
||||
await rm(root, { recursive: true, force: true })
|
||||
|
||||
@@ -130,9 +130,11 @@ describe('WatcherProcessSupervisor capacity wait', () => {
|
||||
)
|
||||
await Promise.resolve()
|
||||
expect(forkMock).toHaveBeenCalledTimes(MAX_PHYSICAL_WATCHER_CHILDREN + 1)
|
||||
const secondBlockedSettled = trackPromiseSettlement(secondBlocked)
|
||||
firstController.abort()
|
||||
await expect(firstBlocked).rejects.toMatchObject({ code: 'subscribe_aborted' })
|
||||
expect(trackPromiseSettlement(secondBlocked)()).toBe(false)
|
||||
await vi.advanceTimersByTimeAsync(0)
|
||||
expect(secondBlockedSettled()).toBe(false)
|
||||
secondController.abort()
|
||||
await expect(secondBlocked).rejects.toMatchObject({ code: 'subscribe_aborted' })
|
||||
|
||||
|
||||
@@ -163,8 +163,8 @@ describe('watcher native unsubscribe timeout', () => {
|
||||
await siblingPromise
|
||||
|
||||
const result = firstSubscription.unsubscribe().catch((error: unknown) => error)
|
||||
await vi.advanceTimersByTimeAsync(WATCHER_PROCESS_UNSUBSCRIBE_TIMEOUT_MS)
|
||||
const isSettled = trackPromiseSettlement(result)
|
||||
await vi.advanceTimersByTimeAsync(WATCHER_PROCESS_UNSUBSCRIBE_TIMEOUT_MS)
|
||||
expect(isSettled()).toBe(false)
|
||||
|
||||
await vi.advanceTimersByTimeAsync(WATCHER_PROCESS_EXIT_DEADLINE_MS)
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
import { afterEach, describe, expect, it } from 'vitest'
|
||||
import {
|
||||
MACOS_FSEVENTS_EXCLUSION_PATH_LIMIT,
|
||||
WATCHER_IGNORE_DIRS,
|
||||
buildParcelWatcherIgnoreOptions
|
||||
} from './filesystem-watcher-ignore'
|
||||
import { WATCHER_IGNORE_DIRS, buildParcelWatcherIgnoreOptions } from './filesystem-watcher-ignore'
|
||||
|
||||
const realPlatform = process.platform
|
||||
|
||||
@@ -22,8 +18,8 @@ describe('buildParcelWatcherIgnoreOptions', () => {
|
||||
const option = options.ignore ?? []
|
||||
// FSEvents rejects all plain exclusions above eight paths, silently restoring node_modules/.git churn.
|
||||
const plainPaths = option.filter((entry) => !entry.includes('*'))
|
||||
expect(plainPaths.length).toBeLessThanOrEqual(MACOS_FSEVENTS_EXCLUSION_PATH_LIMIT)
|
||||
expect(option).toEqual(WATCHER_IGNORE_DIRS.slice(0, MACOS_FSEVENTS_EXCLUSION_PATH_LIMIT))
|
||||
expect(plainPaths.length).toBeLessThanOrEqual(8)
|
||||
expect(option).toEqual(WATCHER_IGNORE_DIRS.slice(0, 8))
|
||||
const fallbackRegex = new RegExp(options.ignoreGlobs?.[0] ?? '(?!)')
|
||||
for (const dir of WATCHER_IGNORE_DIRS) {
|
||||
expect(fallbackRegex.test(dir)).toBe(true)
|
||||
|
||||
@@ -17,7 +17,7 @@ afterEach(() => {
|
||||
})
|
||||
|
||||
describe('showMobileEmulatorHiddenToast', () => {
|
||||
it('auto-dismisses after 30s while staying dismissible', () => {
|
||||
it('requests a dismissible toast with a 30-second duration', () => {
|
||||
showMobileEmulatorHiddenToast({
|
||||
openSettingsPage: vi.fn() as unknown as AppState['openSettingsPage'],
|
||||
openSettingsTarget: vi.fn() as unknown as AppState['openSettingsTarget']
|
||||
|
||||
@@ -206,7 +206,7 @@ describe('HeroFlow height', () => {
|
||||
expect(screen.getByText('Sign in to create a Relay pairing code')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('disables Relay recovery immediately and delays visible retry feedback', async () => {
|
||||
it('disables Relay recovery while busy and eventually shows retry feedback', async () => {
|
||||
renderFlow(1, {
|
||||
pairLoading: true,
|
||||
relayMintFailure: {
|
||||
@@ -220,7 +220,7 @@ describe('HeroFlow height', () => {
|
||||
await waitFor(() => expect(screen.getByText(/Creating a new pairing code/)).toBeVisible())
|
||||
})
|
||||
|
||||
it('offers Retry Relay when the desktop provider is missing, since a retry installs it', () => {
|
||||
it('offers Retry Relay when the desktop provider is missing', () => {
|
||||
renderFlow(1, {
|
||||
relayMintFailure: {
|
||||
code: 'relay_provider_unavailable',
|
||||
@@ -257,12 +257,12 @@ describe('HeroFlow height', () => {
|
||||
expect(screen.queryByRole('button', { name: 'Retry Relay' })).toBeNull()
|
||||
})
|
||||
|
||||
it('hides the mint-failure notice when a Relay QR is shown', () => {
|
||||
it('shows a Relay QR without a failure notice when no failure is supplied', () => {
|
||||
renderFlow(1, { pairQrDataUrl: 'data:image/png;base64,qr' })
|
||||
expect(screen.queryByTestId('relay-mint-failure-notice')).not.toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('renders a pairing QR at its natural integer-scaled bitmap size', () => {
|
||||
it('uses the supplied QR bitmap size and adds frame padding', () => {
|
||||
renderFlow(1, { pairQrDataUrl: 'data:image/png;base64,qr', pairQrSize: 218 })
|
||||
|
||||
const image = screen.getByRole('img', { name: 'Pairing QR' })
|
||||
@@ -368,7 +368,7 @@ describe('HeroFlow height', () => {
|
||||
expect(refresh).toHaveFocus()
|
||||
})
|
||||
|
||||
it('lets the user name this computer in the pairing step, ahead of the code', () => {
|
||||
it('places the machine-name field slot ahead of the pairing code', () => {
|
||||
const props: React.ComponentProps<typeof MobileHeroPairingStep> = {
|
||||
pairQrDataUrl: 'data:image/png;base64,AAAA',
|
||||
pairingUrl: 'orca://pair?code=abc',
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
import '@testing-library/jest-dom/vitest'
|
||||
|
||||
import { cleanup, render, screen, waitFor } from '@testing-library/react'
|
||||
import { act, cleanup, render, screen, waitFor } from '@testing-library/react'
|
||||
import userEvent from '@testing-library/user-event'
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import type { MobilePairingConnectionMode } from '../../../../shared/mobile-pairing-connection-mode'
|
||||
@@ -283,7 +283,7 @@ describe('MobilePage pairing connection mode', () => {
|
||||
await user.click(screen.getByRole('button', { name: 'Orca Relay' }))
|
||||
await waitFor(() => expect(screen.getByTestId('mode')).toHaveTextContent('automatic'))
|
||||
await waitFor(() => expect(screen.getByTestId('pairing-qr')).toHaveTextContent('none'))
|
||||
await new Promise((resolve) => setTimeout(resolve, 20))
|
||||
await act(async () => {})
|
||||
expect(getPairingQR).not.toHaveBeenCalled()
|
||||
expect(screen.getByTestId('can-generate')).toHaveTextContent('false')
|
||||
})
|
||||
@@ -297,7 +297,7 @@ describe('MobilePage pairing connection mode', () => {
|
||||
getPairingQR.mockClear()
|
||||
|
||||
await user.click(screen.getByRole('button', { name: 'Change address' }))
|
||||
await new Promise((resolve) => setTimeout(resolve, 20))
|
||||
await act(async () => {})
|
||||
expect(getPairingQR).not.toHaveBeenCalled()
|
||||
expect(screen.getByTestId('pairing-qr')).toHaveTextContent('none')
|
||||
})
|
||||
@@ -350,7 +350,7 @@ describe('MobilePage pairing connection mode', () => {
|
||||
expect(screen.getByTestId('pairing-url')).toHaveTextContent('none')
|
||||
})
|
||||
|
||||
it('keeps the copy fallback when the real encoder cannot render the offer', async () => {
|
||||
it('keeps the copy fallback when the host reports QR encoding failure', async () => {
|
||||
getPairingQR.mockResolvedValue({
|
||||
available: true,
|
||||
qrDataUrl: null,
|
||||
@@ -367,7 +367,7 @@ describe('MobilePage pairing connection mode', () => {
|
||||
expect(screen.getByTestId('pairing-url')).toHaveTextContent('copy-fallback')
|
||||
})
|
||||
|
||||
it('surfaces Relay failure and retries with a rotated credential', async () => {
|
||||
it('surfaces Relay failure and requests credential rotation on retry', async () => {
|
||||
getPairingQR.mockResolvedValueOnce({
|
||||
available: false,
|
||||
reason: 'relay_mint_failed',
|
||||
@@ -439,12 +439,14 @@ describe('MobilePage pairing connection mode', () => {
|
||||
expect(getPairingQR).toHaveBeenLastCalledWith({ connectionMode: 'local-only' })
|
||||
)
|
||||
await waitFor(() => expect(screen.getByTestId('pairing-qr')).toHaveTextContent('base64,local'))
|
||||
resolveRetry?.({
|
||||
available: true,
|
||||
qrDataUrl: 'data:image/png;base64,stale-relay',
|
||||
pairingUrl: 'orca://pair#stale-relay',
|
||||
endpoint: 'ws://relay',
|
||||
connectionMode: 'automatic'
|
||||
await act(async () => {
|
||||
resolveRetry?.({
|
||||
available: true,
|
||||
qrDataUrl: 'data:image/png;base64,stale-relay',
|
||||
pairingUrl: 'orca://pair#stale-relay',
|
||||
endpoint: 'ws://relay',
|
||||
connectionMode: 'automatic'
|
||||
})
|
||||
})
|
||||
await waitFor(() => expect(screen.getByTestId('pairing-qr')).toHaveTextContent('base64,local'))
|
||||
expect(screen.getByTestId('mode')).toHaveTextContent('local-only')
|
||||
@@ -587,7 +589,11 @@ describe('MobilePage pairing connection mode', () => {
|
||||
await user.click(screen.getByRole('button', { name: 'Continue' }))
|
||||
await waitFor(() => expect(getPairingQR).toHaveBeenCalledTimes(2))
|
||||
|
||||
resolveAbandonedMint?.({ available: true, qrDataUrl: 'abandoned' })
|
||||
await act(async () => {
|
||||
resolveAbandonedMint?.({ available: true, qrDataUrl: 'abandoned' })
|
||||
})
|
||||
expect(screen.getByTestId('pairing-qr')).toHaveTextContent('none')
|
||||
expect(screen.getByTestId('pair-loading')).toHaveTextContent('true')
|
||||
|
||||
resolveCurrentMint?.({ available: true, qrDataUrl: 'current' })
|
||||
await waitFor(() => expect(screen.getByTestId('pairing-qr')).toHaveTextContent('current'))
|
||||
@@ -668,7 +674,9 @@ describe('MobilePage pairing connection mode', () => {
|
||||
await user.click(screen.getByRole('button', { name: 'Continue' }))
|
||||
await waitFor(() => expect(listNetworkInterfaces).toHaveBeenCalledTimes(2))
|
||||
|
||||
resolveFirstLookup?.({ interfaces: [{ name: 'Wi-Fi', address: '10.0.0.5' }] })
|
||||
await act(async () => {
|
||||
resolveFirstLookup?.({ interfaces: [{ name: 'Wi-Fi', address: '10.0.0.5' }] })
|
||||
})
|
||||
await waitFor(() => expect(screen.getByTestId('pair-loading')).toHaveTextContent('true'))
|
||||
expect(getPairingQR).not.toHaveBeenCalled()
|
||||
expect(screen.getByTestId('selected-address')).toHaveTextContent('none')
|
||||
@@ -711,7 +719,9 @@ describe('MobilePage pairing connection mode', () => {
|
||||
await waitFor(() => expect(listNetworkInterfaces).toHaveBeenCalledTimes(2))
|
||||
|
||||
// The old lookup cannot release minting before its replacement chooses the address.
|
||||
resolveEntryLookup?.({ interfaces: [{ name: 'Wi-Fi', address: '10.0.0.5' }] })
|
||||
await act(async () => {
|
||||
resolveEntryLookup?.({ interfaces: [{ name: 'Wi-Fi', address: '10.0.0.5' }] })
|
||||
})
|
||||
await waitFor(() => expect(screen.getByTestId('pair-loading')).toHaveTextContent('true'))
|
||||
expect(getPairingQR).not.toHaveBeenCalled()
|
||||
expect(screen.getByTestId('selected-address')).toHaveTextContent('none')
|
||||
@@ -758,10 +768,10 @@ describe('MobilePage pairing connection mode', () => {
|
||||
await waitFor(() => expect(screen.getByTestId('pair-loading')).toHaveTextContent('false'))
|
||||
|
||||
// A late abandoned visit must not roll back the settled marker and strand Step 2.
|
||||
resolveAbandonedLookup?.({ interfaces: [{ name: 'Wi-Fi', address: '10.0.0.5' }] })
|
||||
await waitFor(() =>
|
||||
expect(screen.getByTestId('refreshing-addresses')).toHaveTextContent('false')
|
||||
)
|
||||
await act(async () => {
|
||||
resolveAbandonedLookup?.({ interfaces: [{ name: 'Wi-Fi', address: '10.0.0.5' }] })
|
||||
})
|
||||
expect(screen.getByTestId('refreshing-addresses')).toHaveTextContent('false')
|
||||
expect(screen.getByTestId('pair-loading')).toHaveTextContent('false')
|
||||
expect(screen.getByTestId('selected-address')).toHaveTextContent('10.0.0.9')
|
||||
expect(getPairingQR).toHaveBeenCalledTimes(1)
|
||||
@@ -797,10 +807,10 @@ describe('MobilePage pairing connection mode', () => {
|
||||
rotate: true
|
||||
})
|
||||
|
||||
resolveStaleRefresh?.({ interfaces: [{ name: 'Wi-Fi', address: '10.0.0.1' }] })
|
||||
await waitFor(() =>
|
||||
expect(screen.getByTestId('refreshing-addresses')).toHaveTextContent('false')
|
||||
)
|
||||
await act(async () => {
|
||||
resolveStaleRefresh?.({ interfaces: [{ name: 'Wi-Fi', address: '10.0.0.1' }] })
|
||||
})
|
||||
expect(screen.getByTestId('refreshing-addresses')).toHaveTextContent('false')
|
||||
|
||||
expect(screen.getByTestId('selected-address')).toHaveTextContent('10.0.0.7')
|
||||
expect(getPairingQR).toHaveBeenCalledTimes(2)
|
||||
@@ -879,11 +889,11 @@ describe('MobilePage pairing connection mode', () => {
|
||||
)
|
||||
expect(listNetworkInterfaces).toHaveBeenCalledOnce()
|
||||
|
||||
resolveRefresh?.({ interfaces: [{ name: 'Ethernet', address: '10.0.0.2' }] })
|
||||
await act(async () => {
|
||||
resolveRefresh?.({ interfaces: [{ name: 'Ethernet', address: '10.0.0.2' }] })
|
||||
})
|
||||
|
||||
await waitFor(() =>
|
||||
expect(screen.getByTestId('refreshing-addresses')).toHaveTextContent('false')
|
||||
)
|
||||
expect(screen.getByTestId('refreshing-addresses')).toHaveTextContent('false')
|
||||
expect(screen.getByTestId('selected-address')).toHaveTextContent('wss://custom.example/large')
|
||||
expect(getPairingQR).not.toHaveBeenCalledWith({
|
||||
address: '10.0.0.2',
|
||||
|
||||
@@ -94,7 +94,9 @@ describe('WindowsFirewallNotice', () => {
|
||||
const user = userEvent.setup()
|
||||
render(<WindowsFirewallNotice pairingReady address="192.168.0.108" />)
|
||||
|
||||
await user.click(await screen.findByRole('button', { name: /allow phone connections/i }))
|
||||
const repairButton = await screen.findByRole('button', { name: /allow phone connections/i })
|
||||
expect(repairWindowsFirewall).not.toHaveBeenCalled()
|
||||
await user.click(repairButton)
|
||||
expect(repairWindowsFirewall).toHaveBeenCalledTimes(1)
|
||||
await waitFor(() => expect(getWindowsFirewallStatus).toHaveBeenCalledTimes(2))
|
||||
await waitFor(() =>
|
||||
@@ -205,9 +207,11 @@ describe('WindowsFirewallNotice', () => {
|
||||
getWindowsFirewallStatus
|
||||
})
|
||||
|
||||
render(<WindowsFirewallNotice pairingReady address="10.0.0.4" />)
|
||||
await act(async () => {
|
||||
render(<WindowsFirewallNotice pairingReady address="10.0.0.4" />)
|
||||
})
|
||||
|
||||
await waitFor(() => expect(getWindowsFirewallStatus).toHaveBeenCalledTimes(1))
|
||||
expect(getWindowsFirewallStatus).toHaveBeenCalledTimes(1)
|
||||
expect(screen.queryByRole('button', { name: /allow phone connections/i })).toBeNull()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -4,17 +4,6 @@ import { getMobilePaneSearchEntries, shouldOpenMobilePairingAddress } from './mo
|
||||
import { matchesSettingsSearch } from './settings-search'
|
||||
|
||||
describe('getMobilePaneSearchEntries', () => {
|
||||
// Catalog order breaks search-score ties; preserve Network Interface ranking after catalog splits.
|
||||
it('keeps Network Interface in its original catalog position', () => {
|
||||
expect(getMobilePaneSearchEntries().map((entry) => entry.title)).toEqual([
|
||||
'Mobile Pairing',
|
||||
'Connected Devices',
|
||||
'Network Interface',
|
||||
'When you leave the mobile app',
|
||||
'Machine name'
|
||||
])
|
||||
})
|
||||
|
||||
it('keeps the shared machine-name entry searchable from the Mobile pane', () => {
|
||||
const entries = getMobilePaneSearchEntries()
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import {
|
||||
MAX_MOBILE_PAIRING_CUSTOM_ADDRESSES,
|
||||
addMobilePairingCustomAddress,
|
||||
normalizeMobilePairingCustomAddress,
|
||||
normalizeMobilePairingCustomAddresses,
|
||||
@@ -32,13 +31,10 @@ describe('mobile pairing custom address collection', () => {
|
||||
).toEqual(['first.example:6768', 'second.example:6768'])
|
||||
})
|
||||
|
||||
it('bounds persisted entries', () => {
|
||||
const addresses = Array.from(
|
||||
{ length: MAX_MOBILE_PAIRING_CUSTOM_ADDRESSES + 5 },
|
||||
(_, index) => `host-${index}.example:6768`
|
||||
)
|
||||
expect(normalizeMobilePairingCustomAddresses(addresses)).toHaveLength(
|
||||
MAX_MOBILE_PAIRING_CUSTOM_ADDRESSES
|
||||
it('keeps the first 50 valid persisted addresses', () => {
|
||||
const addresses = Array.from({ length: 55 }, (_, index) => `host-${index}.example:6768`)
|
||||
expect(normalizeMobilePairingCustomAddresses(addresses)).toEqual(
|
||||
Array.from({ length: 50 }, (_, index) => `host-${index}.example:6768`)
|
||||
)
|
||||
})
|
||||
|
||||
|
||||
Reference in New Issue
Block a user