diff --git a/mobile/src/terminal/quick-commands/QuickCommandsSheet.test.ts b/mobile/src/terminal/quick-commands/QuickCommandsSheet.test.ts index 2afda96b32c..cf82027d5f2 100644 --- a/mobile/src/terminal/quick-commands/QuickCommandsSheet.test.ts +++ b/mobile/src/terminal/quick-commands/QuickCommandsSheet.test.ts @@ -160,10 +160,7 @@ describe('QuickCommandsSheet', () => { saved.resolve(true) await saved.promise }) - expect(editor().props.draft).toMatchObject({ - label: 'Newer', - id: mocks.persist.mock.calls[0]?.[0].command.id - }) + expect(editor().props.draft.label).toBe('Newer') expect(editor().props.saving).toBe(false) client.close() }) diff --git a/mobile/src/terminal/session/creation/use-initial-session-terminal-autocreate.test.ts b/mobile/src/terminal/session/creation/use-initial-session-terminal-autocreate.test.ts deleted file mode 100644 index e8755754ad8..00000000000 --- a/mobile/src/terminal/session/creation/use-initial-session-terminal-autocreate.test.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { createElement } from 'react' -import { act, create, type ReactTestRenderer } from 'react-test-renderer' -import { afterEach, describe, expect, it } from 'vitest' -import { useWorktreeSessionTabsLoaded } from './use-initial-session-terminal-autocreate' - -describe('useWorktreeSessionTabsLoaded', () => { - let renderer: ReactTestRenderer | null = null - let current: readonly [boolean, (loaded: boolean) => void] | null = null - - function Harness({ worktreeId }: { worktreeId: string }): null { - current = useWorktreeSessionTabsLoaded(worktreeId) - return null - } - - afterEach(() => { - act(() => renderer?.unmount()) - renderer = null - current = null - }) - - it('does not carry a loaded snapshot across a reused route', async () => { - await act(async () => { - renderer = create(createElement(Harness, { worktreeId: 'existing' })) - }) - act(() => current?.[1](true)) - expect(current?.[0]).toBe(true) - - await act(async () => { - renderer?.update(createElement(Harness, { worktreeId: 'new' })) - }) - expect(current?.[0]).toBe(false) - }) -}) diff --git a/mobile/src/terminal/session/inventory/mobile-terminal-inventory-request.test.ts b/mobile/src/terminal/session/inventory/mobile-terminal-inventory-request.test.ts deleted file mode 100644 index 9b22756d32f..00000000000 --- a/mobile/src/terminal/session/inventory/mobile-terminal-inventory-request.test.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { afterEach, describe, expect, it, vi } from 'vitest' -import { MobileTerminalInventoryRequest } from './mobile-terminal-inventory-request' - -describe('MobileTerminalInventoryRequest', () => { - afterEach(() => { - vi.restoreAllMocks() - }) - - it('fences a response after its route activation is replaced', async () => { - const oldResponse = Promise.withResolvers() - const applied: string[] = [] - const requests = new MobileTerminalInventoryRequest() - const deactivateOld = requests.activate() - const oldResult = requests.run(true, async (_allowsEmpty, isCurrent) => { - await oldResponse.promise - if (!isCurrent()) { - return false - } - applied.push('old') - return true - }) - await Promise.resolve() - - requests.activate() - deactivateOld() - oldResponse.resolve() - await expect(oldResult).resolves.toBe(false) - await expect( - requests.run(true, async (_allowsEmpty, isCurrent) => { - if (!isCurrent()) { - return false - } - applied.push('next') - return true - }) - ).resolves.toBe(true) - expect(applied).toEqual(['next']) - }) -}) diff --git a/mobile/src/terminal/terminal-webview-html/document-style.test.ts b/mobile/src/terminal/terminal-webview-html/document-style.test.ts deleted file mode 100644 index 7537423d714..00000000000 --- a/mobile/src/terminal/terminal-webview-html/document-style.test.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { describe, expect, it } from 'vitest' -import { scopeStyleToHost } from '../../style-scoping/document-style-scoping' -import { XTERM_ENGINE_CSS } from '../document/terminal-webview-engine-css.generated' -import { TERMINAL_DOCUMENT_ELEMENT_STYLE } from './document-style' - -// Verify native root rules remain separate while page-injected rules can reach only terminal-owned elements. -const PREFIX = '.orca-terminal-document-host' - -function selectorsOf(css: string): string[] { - return [...css.matchAll(/(?:^|\})\s*([^{}]+)\{/g)].flatMap((match) => - match[1]!.split(',').map((one) => one.trim()) - ) -} - -describe('the terminal document stylesheet', () => { - it('holds every selector of both injected sheets under the host', () => { - for (const sheet of [TERMINAL_DOCUMENT_ELEMENT_STYLE, XTERM_ENGINE_CSS]) { - const scoped = scopeStyleToHost(sheet, PREFIX) - const selectors = selectorsOf(scoped) - expect(selectors.length).toBeGreaterThan(0) - expect(selectors.filter((one) => !one.startsWith(`${PREFIX} `))).toEqual([]) - } - }) -}) diff --git a/mobile/src/terminal/viewport/terminal-auto-restore-fit-state.test.ts b/mobile/src/terminal/viewport/terminal-auto-restore-fit-state.test.ts deleted file mode 100644 index 08e4fdf466c..00000000000 --- a/mobile/src/terminal/viewport/terminal-auto-restore-fit-state.test.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { describe, expect, it, vi } from 'vitest' -import { - readTerminalAutoRestoreFitMs, - saveTerminalAutoRestoreFitMs -} from './terminal-auto-restore-fit-state' - -describe('terminal auto restore fit state', () => { - it('reads a host without the setting as the default', async () => { - const sendRequest = vi.fn().mockResolvedValue({ - id: '1', - ok: false, - error: { code: 'method_not_found', message: 'Unknown method' } - }) - - await expect(readTerminalAutoRestoreFitMs({ sendRequest })).resolves.toBeNull() - await expect(saveTerminalAutoRestoreFitMs({ sendRequest }, 60_000)).resolves.toBeNull() - }) -}) diff --git a/mobile/src/terminal/viewport/terminal-cell-box.test.ts b/mobile/src/terminal/viewport/terminal-cell-box.test.ts deleted file mode 100644 index 06569b2af30..00000000000 --- a/mobile/src/terminal/viewport/terminal-cell-box.test.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { describe, expect, it } from 'vitest' -import { readTerminalCellBox } from './terminal-cell-box' - -const CELL_1X = { fontScale: 1, cellWidth: 23 / 3, cellHeight: 15 } - -describe('readTerminalCellBox', () => { - it('reads a well-formed box and rejects a malformed one', () => { - expect(readTerminalCellBox({ cellBox: CELL_1X })).toEqual(CELL_1X) - expect(readTerminalCellBox({ cellBox: { ...CELL_1X, cellWidth: 0 } })).toBeNull() - expect(readTerminalCellBox({ cellBox: [CELL_1X] })).toBeNull() - }) -}) diff --git a/mobile/src/terminal/webview/terminal-webview-contract.test.ts b/mobile/src/terminal/webview/terminal-webview-contract.test.ts index cde678336f8..1e8a70e5cc1 100644 --- a/mobile/src/terminal/webview/terminal-webview-contract.test.ts +++ b/mobile/src/terminal/webview/terminal-webview-contract.test.ts @@ -3,15 +3,6 @@ import { describe, expect, it } from 'vitest' import { parseTerminalKeyboardAvoidanceMetrics } from './terminal-webview-contract' describe('parseTerminalKeyboardAvoidanceMetrics', () => { - it('defaults contentBottomRow to cursorY when absent (older WebView bundles)', () => { - expect(parseTerminalKeyboardAvoidanceMetrics({ cursorY: 12, rows: 40 })).toEqual({ - cursorY: 12, - contentBottomRow: 12, - rows: 40, - altScreen: false - }) - }) - it('bounds untrusted numeric fields to the reported viewport', () => { expect( parseTerminalKeyboardAvoidanceMetrics({ diff --git a/mobile/src/test-support/rpc-recording/golden-recorder-failure-absence.test.ts b/mobile/src/test-support/rpc-recording/golden-recorder-failure-absence.test.ts deleted file mode 100644 index 572796b842d..00000000000 --- a/mobile/src/test-support/rpc-recording/golden-recorder-failure-absence.test.ts +++ /dev/null @@ -1,72 +0,0 @@ -import { readdirSync } from 'node:fs' -import { resolve } from 'node:path' -import { describe, expect, it } from 'vitest' -import { readGolden } from './golden-recording' -import { OBSERVATION_FIELDS } from './golden-value-pool' -import type { Observation } from './recording-scenario' -import type { RecordedValue } from './recording-values' - -const root = resolve(import.meta.dirname, '../../../..') -const directory = - process.env.RPC_FOUNDATION_GOLDENS ?? resolve(root, 'mobile/rpc-foundation/goldens') - -/** `captureValue`'s two refusals. Neither is ever product behaviour. */ -const PROJECTION_REFUSALS = [ - 'Unsupported observation', - 'Observation requires an explicit projection' -] - -function refusalText(value: RecordedValue): boolean { - if (typeof value === 'string') { - return PROJECTION_REFUSALS.some((refusal) => value.includes(refusal)) - } - if (Array.isArray(value)) { - return value.some(refusalText) - } - return typeof value === 'object' && value !== null && Object.values(value).some(refusalText) -} - -function failures(at: string, observation: Observation): string[] { - const found: string[] = [] - for (const field of OBSERVATION_FIELDS) { - if (refusalText(observation[field])) { - found.push(`${at}.${field}: recorder refused to project a value`) - } - } - return found -} - -// Projection refusals can be captured as data and re-recorded green; reject those while allowing real detached product failures. -describe('recorder failures never reach a golden', () => { - it('records no refused projection in any observation field', () => { - const ids = readdirSync(directory) - .filter((file) => file.endsWith('.json')) - .map((file) => file.replace(/\.json$/, '')) - - // Seed a captured refusal to prove absence checking detects the real failure shape. - const seeded: Observation = { - sender: [], - payloads: [], - settlements: { fetch: { status: 'pending' } }, - state: { fetched: 'Observation requires an explicit projection for non-plain objects' }, - effects: [ - { name: 'unhandled-rejection', value: { message: 'Unsupported observation: function' } } - ] - } - expect(failures('seeded', seeded)).toEqual([ - 'seeded.state: recorder refused to project a value', - 'seeded.effects: recorder refused to project a value' - ]) - - let checkpoints = 0 - const found = ids.flatMap((id) => - readGolden(directory, id).recording.checkpoints.flatMap((checkpoint) => { - checkpoints++ - return failures(`${id}/${checkpoint.id}`, checkpoint.observation) - }) - ) - expect(found).toEqual([]) - expect(ids.length).toBeGreaterThan(0) - expect(checkpoints).toBeGreaterThan(ids.length) - }) -}) diff --git a/mobile/src/test-support/rpc-recording/vitest-recording-scheduler.test.ts b/mobile/src/test-support/rpc-recording/vitest-recording-scheduler.test.ts deleted file mode 100644 index d0cf2fed5f7..00000000000 --- a/mobile/src/test-support/rpc-recording/vitest-recording-scheduler.test.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { describe, expect, it } from 'vitest' -import { runRecording } from './run-recording' -import { reactActQueuePrimed, vitestRecordingScheduler } from './vitest-recording-scheduler' -import type { MountAdapter, RecordingScenario } from './recording-scenario' - -/** The seeded generator's first value: `seed = 1`, one LCG step, divided by 2^32. */ -const SEEDED_FIRST_DRAW = 0.23645552527159452 - -const drawing: MountAdapter = ({ effect }) => ({ - action: (name) => { - if (name !== 'draw') { - throw new Error(`Unexpected action: ${name}`) - } - effect('draw', Math.random()) - return undefined - }, - state: () => null, - dispose: () => {} -}) - -/** `flushFirst` puts a zero-time drain — one `await act` — ahead of the draw. */ -function scenario(id: string, flushFirst: boolean): RecordingScenario { - const draw: RecordingScenario['steps'] = [{ action: 'draw', id: 'd' }, { checkpoint: 'drawn' }] - return { - id, - operation: 'scheduler-determinism', - family: 'scheduler-determinism', - sites: [], - schedules: [], - steps: flushFirst ? [{ checkpoint: 'mounted' }, ...draw] : draw - } -} - -async function record(id: string, flushFirst: boolean): Promise { - const recording = await runRecording( - scenario(id, flushFirst), - drawing, - vitestRecordingScheduler() - ) - const drawn = recording.checkpoints.at(-1)?.observation.effects - return JSON.parse(JSON.stringify(drawn)) -} - -describe('recording scheduler randomness', () => { - // Keep this test first: only a process’s first act can expose an unprimed React random draw. - it('draws the same seeded value first in the process as afterwards', async () => { - expect(reactActQueuePrimed()).toBe(false) - const first = await record('first-in-process', true) - const later = await record('later-in-process', true) - const expected = [{ name: 'draw', ordinal: 1, value: SEEDED_FIRST_DRAW }] - expect([first, later]).toEqual([expected, expected]) - }) -}) diff --git a/mobile/src/theme/themed-styles.test.ts b/mobile/src/theme/themed-styles.test.ts deleted file mode 100644 index 8d89343ecdc..00000000000 --- a/mobile/src/theme/themed-styles.test.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { afterEach, expect, it, vi } from 'vitest' -import { applyMobileColorScheme, colors, paletteForScheme } from './mobile-theme' -import { themedStyles } from './themed-styles' - -vi.mock('react-native', () => ({ StyleSheet: { create: (styles: T): T => styles } })) - -afterEach(() => { - applyMobileColorScheme('dark') -}) - -it('serves each scheme its own stylesheet, including ones built before the switch', () => { - const styles = themedStyles(() => ({ row: { backgroundColor: colors.bgBase } })) - const merged = themedStyles(() => ({ ...styles })) - expect(merged.row.backgroundColor).toBe(paletteForScheme('dark').bgBase) - - applyMobileColorScheme('light') - expect(styles.row.backgroundColor).toBe(paletteForScheme('light').bgBase) - expect(merged.row.backgroundColor).toBe(paletteForScheme('light').bgBase) - - applyMobileColorScheme('dark') - expect(merged.row.backgroundColor).toBe(paletteForScheme('dark').bgBase) -}) diff --git a/mobile/src/transport/connection/client-context.test.ts b/mobile/src/transport/connection/client-context.test.ts index 4d0097b6f53..29c2853f5a7 100644 --- a/mobile/src/transport/connection/client-context.test.ts +++ b/mobile/src/transport/connection/client-context.test.ts @@ -190,30 +190,6 @@ describe('useHostClient', () => { }) describe('useAllHostClients', () => { - it('rerenders when Relay becomes pending without a transport-state change', async () => { - const client = makeFakeClient('reconnecting') - connectMock.mockReturnValue(client) - loadHostsMock.mockResolvedValue([HOST]) - let pendingPath: MobileConnectionPath | null | undefined - let renderer!: ReturnType - - function Probe(): null { - pendingPath = useAllHostClients([HOST.id])[0]?.pendingPath - return null - } - - await act(async () => { - renderer = create(createElement(RpcClientProvider, null, createElement(Probe))) - await Promise.resolve() - }) - expect(pendingPath).toBeNull() - - act(() => client.emitPendingPath('relay')) - expect(pendingPath).toBe('relay') - - act(() => renderer.unmount()) - }) - it('publishes a latched pairing rejection to the screens', async () => { const client = makeFakeClient('reconnecting') connectMock.mockReturnValue(client) diff --git a/mobile/src/transport/encryption/paired-e2ee-native-codec.test.ts b/mobile/src/transport/encryption/paired-e2ee-native-codec.test.ts index 41e233f03a6..d9ecfafda52 100644 --- a/mobile/src/transport/encryption/paired-e2ee-native-codec.test.ts +++ b/mobile/src/transport/encryption/paired-e2ee-native-codec.test.ts @@ -83,13 +83,11 @@ describe('the native paired E2EE codec', () => { await sodium.ready }) - // UTF-8 fallback must preserve an already-opened frame whose counter advanced. - // libsodium sizes its output as the box length minus the MAC, which underflows and crashes. it('never hands libsodium a box shorter than its MAC', async () => { const { native } = await load(true) secretboxOpenCalls.mockClear() - for (let length = 0; length < 16; length++) { + for (const length of [0, 15]) { expect( native!.openSecretbox(new Uint8Array(length), randomBytes(24), randomBytes(32)) ).toBeNull()