test(mobile): scope the IME source guards and drop a circular assertion

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.
This commit is contained in:
Brennan Benson
2026-07-28 21:44:45 -07:00
parent aa9a3163ea
commit cf1dfc6605
2 changed files with 63 additions and 19 deletions
+6 -3
View File
@@ -334,9 +334,12 @@ export default function TerminalSettingsScreen() {
<Text style={[styles.groupHeading, styles.inputGroupGap]}>KEYBOARD INPUT</Text>
<Text style={styles.groupDescription}>
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&apos;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.'
: ''}
</Text>
<View style={[styles.section, styles.sectionTopGap]}>
<View style={styles.row}>
@@ -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,")
})
})