fix(mobile): read an empty iOS text field as empty, not as its placeholder
On iOS, an empty text field read as if it held its placeholder text. That broke the ios expo agent test in the nightly examples run on both attempts.
-
agent-device reports an empty React Native
TextInputon iOS withvalueequal toplaceholderand nohintShowingflag. Only UIAutomator sets that flag; the iOS runner has no such field:{"type":"TextField","label":"Name","value":"Ada Lovelace","placeholder":"Ada Lovelace","identifier":"name","editable":true} -
packages/mobile/src/nodes.tsblanked the value only onhintShowing, so the field becametextbox "Name" value="Ada Lovelace". The agent's view hidesplaceholderwhen a field has a name (packages/e2e/src/agent/observation.ts:448), so the model saw only a field it thought was already filled. -
Asked to "press Greet without typing a name", the agent spent its 11 turns trying to delete "Ada Lovelace" and never pressed Greet. The run before it read the field the same way and passed by luck.
-
The reader now treats an editable iOS field whose value equals its placeholder (trimmed) as empty. agent-device's runner uses the same check during text entry (
textMatchesPlaceholder). Android still relies onhintShowing. -
History: #499 hit this on the CI Mac and added the
hintShowingcheck, believing the iOS bridge set the flag. No agent-device version does that on iOS: 0.21.13, the version #499 used, and 0.21.28 both read it from Android sources only. The test that check came with fed iOS a flag that never comes. agent-device later addedplaceholderto iOS nodes so consumers could tell an empty field from a filled one (callstack/agent-device#2961). The reader never used it for that until now. -
Trade-off: on iOS, text typed to exactly match the placeholder also reads as empty. The snapshot cannot tell the two apart.
docs/reference/mobile.mdxnow says so. -
The iOS goldens in
packages/mobile/tests/fixtures/snapshots/ios/do not change. In those captures of the mobile benchmark, empty fields come with novalueat all. The unit test pins the shape captured fromexamples/with-expo.
Verified
Ran it locally: yes
examples/with-expobuilt for the iOS simulator as the nightly builds it (expo prebuild, Releasexcodebuild), on an iPhone 17 Pro with iOS 26.5, Xcode 27.1, and agent-device 0.21.28, the same agent-device version the nightly used.- A probe test dumped the screen the agent reads in three states. main used the npm packages; this branch used
e2e,@e2e-dev/web, and@e2e-dev/mobilepacked from the branch, the way the nightly swaps them in:
| state | main | this branch |
|---|---|---|
empty, after app.open() | textbox "Name" testid="name" value="Ada Lovelace" | textbox "Name" testid="name" |
| empty, focused | textbox "Name" testid="name" value="Ada Lovelace" [focused] | textbox "Name" testid="name" [focused] |
after fill('Ada') | value="Ada" | value="Ada" |
| after Greet clears it | textbox "Name" testid="name" value="Ada Lovelace" | textbox "Name" testid="name" |
- The example's
tests/deterministic.e2e.tspasses on the branch build. - Not run: the example's agent test. No model key was set locally.