From cf1dfc66051c44925dab5afd3091d2e1be941235 Mon Sep 17 00:00:00 2001 From: Brennan Benson <79079362+brennanb2025@users.noreply.github.com> Date: Tue, 28 Jul 2026 21:44:45 -0700 Subject: [PATCH] test(mobile): scope the IME source guards and drop a circular assertion MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Review follow-up on the IME input props. - Removed an assertion that compared getTerminalImeInputProps' output against isTerminalAutocorrectEnabled, which the implementation calls internally — it reduced to `enabled === enabled` and could not fail. The literal expected objects already cover the mapping. - The `autoCorrect={false}` guard was file-scoped on a 5387-line route with non-terminal inputs of its own, so the next unrelated field would have failed it with a misleading message. Both guards are now scoped to the two terminal elements via anchored slices, and a missing anchor fails loudly instead of slicing to EOF and passing vacuously. - Dropped two negative guards that could never fire: one named an identifier that no longer exists, the other asserted the exact formatting of a deleted line. Settings copy no longer hardcodes the per-platform default that the input resolves through, and it stops claiming direct input is immune to suggestions — on Android, clearing NO_SUGGESTIONS is what lets the strip reappear. --- mobile/app/terminal-settings.tsx | 9 ++- .../terminal/terminal-ime-input-props.test.ts | 73 +++++++++++++++---- 2 files changed, 63 insertions(+), 19 deletions(-) diff --git a/mobile/app/terminal-settings.tsx b/mobile/app/terminal-settings.tsx index f113825ef86..66c22c520ea 100644 --- a/mobile/app/terminal-settings.tsx +++ b/mobile/app/terminal-settings.tsx @@ -334,9 +334,12 @@ export default function TerminalSettingsScreen() { KEYBOARD INPUT Enable phone-style autocomplete, autocorrect, and spelling suggestions in the terminal - command bar. {Platform.OS === 'ios' ? 'On' : 'Off'} by default. Direct keyboard input - (when keys go straight to the terminal) always sends raw keystrokes, so suggestions - don't apply there. + command bar.{' '} + {isTerminalAutocorrectEnabled('command', Platform.OS, 'unset') ? 'On' : 'Off'} by default. + Direct keyboard input (when keys go straight to the terminal) never autocorrects. + {Platform.OS === 'android' + ? ' On Android the keyboard may still offer suggestions you can tap — suppressing them entirely is the same setting that stops Korean and Chinese from composing, so composition wins.' + : ''} diff --git a/mobile/src/terminal/terminal-ime-input-props.test.ts b/mobile/src/terminal/terminal-ime-input-props.test.ts index 7ada8cc1e93..95da6c79bee 100644 --- a/mobile/src/terminal/terminal-ime-input-props.test.ts +++ b/mobile/src/terminal/terminal-ime-input-props.test.ts @@ -64,9 +64,6 @@ describe('getTerminalImeInputProps', () => { for (const preference of PREFERENCES) { const props = getTerminalImeInputProps(entryMode, 'android', preference) expect(props.autoCorrect).not.toBe(false) - expect(Object.hasOwn(props, 'autoCorrect')).toBe( - isTerminalAutocorrectEnabled(entryMode, 'android', preference) - ) } } }) @@ -101,28 +98,72 @@ describe('getTerminalImeInputProps', () => { }) }) +// Why: a slice between anchors silently runs to EOF if an anchor rots, which turns +// every assertion below it vacuous — so a missing anchor must fail loudly instead. +function sliceBetween(source: string, startAnchor: string, endAnchor: string): string { + const start = source.indexOf(startAnchor) + expect(start, `start anchor not found: ${startAnchor}`).toBeGreaterThan(-1) + const end = source.indexOf(endAnchor, start) + expect(end, `end anchor not found after start: ${endAnchor}`).toBeGreaterThan(-1) + return source.slice(start, end) +} + describe('session route IME wiring', () => { + // Element-scoped, not file-scoped: ~20 other mobile fields legitimately hardcode + // autoCorrect={false}, and this route has non-terminal inputs of its own. + const liveInput = sliceBetween( + sessionRouteSource, + 'ref={liveInputRef}', + 'importantForAutofill="no"' + ) + const commandInput = sliceBetween( + sessionRouteSource, + 'ref={commandInputRef}', + 'onSubmitEditing={() => void handleSend()}' + ) + // Guard the call sites: the pure module is only a fix if the route actually uses it. it('routes both terminal inputs through the IME prop builder', () => { - expect(sessionRouteSource).toContain( - "{...getTerminalImeInputProps('live', Platform.OS, autocompletePref)}" - ) - expect(sessionRouteSource).toContain( - "{...getTerminalImeInputProps('command', Platform.OS, autocompletePref)}" + expect(liveInput).toContain("getTerminalImeInputProps('live', Platform.OS, autocompletePref)") + expect(commandInput).toContain( + "getTerminalImeInputProps('command', Platform.OS, autocompletePref)" ) }) - it('hardcodes neither autoCorrect nor spellCheck on the terminal inputs', () => { - expect(sessionRouteSource).not.toContain('autoCorrect={false}') - expect(sessionRouteSource).not.toContain('spellCheck={false}') - expect(sessionRouteSource).not.toContain('autoCorrect={autocompleteEnabled}') + // #6995: a literal false on either terminal input reintroduces NO_SUGGESTIONS on Android. + it('hardcodes neither autoCorrect nor spellCheck on either terminal input', () => { + for (const [name, element] of [ + ['live', liveInput], + ['command', commandInput] + ] as const) { + expect(element, `${name} input`).not.toMatch(/autoCorrect=\{(false|true)\}/) + expect(element, `${name} input`).not.toMatch(/spellCheck=\{(false|true)\}/) + } + }) + + // Android caches inputType at mount, so the prop shape and the remount key must flip together. + it('keys the Android command-input remount off the same resolved value it emits', () => { + expect(commandInput).toContain('commandAutocorrect') + expect(sessionRouteSource).toContain( + "isTerminalAutocorrectEnabled('command', Platform.OS, autocompletePref)" + ) }) // Otherwise the toggle reads "Off" on iOS while the command bar autocorrects (#4606). - it('resolves the settings toggle through the same per-platform default', () => { - expect(terminalSettingsSource).toContain("isTerminalAutocorrectEnabled('command', Platform.OS,") - expect(terminalSettingsSource).not.toContain( - 'const [autocompleteEnabled, setAutocompleteEnabled] = useState(false)' + it('resolves the settings toggle and its copy through the same per-platform default', () => { + const autocompleteState = sliceBetween( + terminalSettingsSource, + 'const [autocompleteEnabled, setAutocompleteEnabled]', + 'const toggleAutocomplete' ) + // Both the initial value and the post-load value must go through the resolver, + // or the row renders a default the command bar does not actually use. + expect(autocompleteState).not.toContain('useState(false)') + expect( + autocompleteState.match(/isTerminalAutocorrectEnabled\('command', Platform\.OS,/g)?.length + ).toBe(2) + // ...and so must the "On/Off by default" sentence in the group description. + const groupDescription = sliceBetween(terminalSettingsSource, 'KEYBOARD INPUT', 'by default') + expect(groupDescription).toContain("isTerminalAutocorrectEnabled('command', Platform.OS,") }) })