Skip to content

fix(android): carry the field's hint text on the snapshot node as placeholder - #2927

Merged
thymikee merged 1 commit into
callstack:mainfrom
okwasniewski:oskar/android-placeholder
Sep 24, 2026
Merged

thymikee merged 1 commit into
callstack:mainfrom
okwasniewski:oskar/android-placeholder

Conversation

@okwasniewski

@okwasniewski okwasniewski commented Sep 24, 2026 •

Copy link
Copy Markdown
Contributor

Summary

The helper wrote hint-showing but never the hint itself, and text holds the hint only while a field is empty, so a filled field's placeholder was readable nowhere: a placeholder query or attribute read had nothing to match on Android.

The helper now writes hint (getHintText(), API 26 or later) whenever the field has one, showing or not. The parser, the Android node, and the published snapshot node carry it as placeholder to get attrs, the unchanged-snapshot comparison, and the selector digest. label, value, and hintShowing are unchanged: an empty field still reports the hint as its text with hintShowing: true, and the new field is the one place that says what the hint is once the field is filled. A field without a hint omits it.

10 files. No HELPER_API_VERSION bump, as with selected (#2515) and checked (#2913): an older helper omits the attribute, which means unavailable.

Validation

Tested at the pushed head. pnpm check:affected --run --base dc9ab863a passed: 854 test files, 6,677 tests, fallow and oxfmt clean.

Rebuilt helper on an API 36 emulator, React Native form, snapshot --json and get attrs:

name-input        value "Ada Lovelace"  hintShowing false  placeholder "Type your name"
passphrase-input  value "Passphrase"    hintShowing true   placeholder "Passphrase"
key-input         value "Key echo"      hintShowing true   placeholder "Key echo"

The filled field's placeholder appears nowhere else in the tree. A WebView <input> named by <label for> carries no hint (Chrome writes none), so it stays without one.

Risk: an unchanged comparison now notices a placeholder change.

…ceholder

The helper wrote hint-showing but never the hint itself, and text holds the
hint only while the field is empty, so a filled field's placeholder was
readable nowhere. The helper now writes hint (getHintText, API 26+), the
node carries it as placeholder whether or not it is showing, and the fact
joins the unchanged map and the find/get digest. Value and label are
unchanged.
Copilot AI lite review requested due to automatic review settings September 24, 2026 17:00

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🔵 Needs a closer look

The advertised placeholder selector query remains unsupported.

Review effort: Lite
Findings: None

What changed in this PR

Adds Android field hint text as placeholder metadata through snapshots and attribute responses.

Changes:

  • Serializes and parses Android hints.
  • Propagates placeholders through snapshots, digests, and unchanged comparisons.
  • Adds documentation and regression tests.
File Summary
website/​docs/​docs/​snapshots.md Documents Android placeholder metadata.
src/​daemon/​response-views.ts Includes placeholders in selector digests.
src/​daemon/​__tests__/​response-views.test.ts Tests digest behavior.
src/​commands/​capture/​runtime/​snapshot-unchanged.ts Compares placeholder changes.
src/​commands/​capture/​runtime/​snapshot-unchanged.test.ts Tests comparison behavior.
packages/​platform-android/​src/​ui-hierarchy.ts Parses and maps hints.
packages/​platform-android/​src/​ui-hierarchy-node.ts Adds placeholder metadata.
packages/​platform-android/​src/​ui-hierarchy-builder.ts Publishes placeholder data.
packages/​platform-android/​src/​__tests__/​ui-hierarchy-placeholder.test.ts Tests placeholder propagation.
packages/​kernel/​src/​snapshot.ts Defines the snapshot field.
android/​snapshot-helper/​src/​main/​java/​com/​callstack/​agentdevice/​snapshothelper/​AccessibilityTreeXml.java Serializes native hint text.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@thymikee

Copy link
Copy Markdown
Member

Reviewed at c6463bd. The code looks correct: the Android snapshot helper carries the field's hint text through as placeholder, and the TS side parses and plumbs the new attribute cleanly. Smoke Tests are still queued, so there's nothing to attribute yet; if a later Android failure shows up, it would land on the same route this PR touches. I didn't run the Android helper or an emulator myself, so the live-device evidence here is the name-input/passphrase-input/key-input triplet the author pasted, not something I reproduced. Nothing here blocks merge on the current evidence.

@thymikee thymikee added the ready-for-human Valid work that needs human implementation, judgment, or maintainer merge label Sep 24, 2026
@thymikee
thymikee merged commit 14bf77b into callstack:main Sep 24, 2026
15 checks passed
thymikee pushed a commit that referenced this pull request Sep 25, 2026
* feat(ios): the snapshot node carries the field's placeholder

Android nodes carry the field's hint as placeholder since #2927; iOS nodes carried
nothing, so a consumer could neither query a field by its placeholder nor tell an
empty field (XCTest reports the placeholder as its value) from one holding that
text. XCTest exposes placeholderValue on every element and snapshot, and the AX
server exposes XC_kAXXCAttributePlaceholderValue.

Every iOS producer now publishes placeholder on RawAXNode and PresentedNode: the
XCTest tree and query sweeps read placeholderValue off the snapshot or element,
the runner's private-AX reader asks for the keypath beside value, and the
Simulator AX bridge requests the attribute (source v1.8.0, one recompile per
host). An empty placeholder reads as none. The docs name the fact for both
platforms.

* fix(ios): count a placeholder as content, keep value and placeholder apart in the tests

Review follow-ups: the collapsed-tab producer read the placeholder only after
its content check, so a placeholder-only field inside a collapsed container was
dropped; the placeholder now counts as content. The bridge test used one string
for value and placeholder, so it could not tell the two attributes apart; it
uses distinct strings and asserts both. A wire-shape test pins that a node
without a placeholder encodes no key. The docs stop calling a value equal to the
placeholder an empty field, since a user can type that text.

* fix(ios): make the placeholder attribute optional in the bridge vocabulary, read it on text entry only

Review follow-ups. The Simulator AX bridge asked for the placeholder attribute
in its required set, so a runtime whose vocabulary lacks it would have failed
every capture; a mismatch now retries the mapping without the attribute and the
capture serves without placeholders. The bridge trims the placeholder the way
the runner does, so whitespace reads as none. The element sweeps read a live
XCUIElement attribute per element inside their deadline, so they read
placeholderValue on text-entry types only; the snapshot producer keeps reading
it off the snapshot it already holds.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ready-for-human Valid work that needs human implementation, judgment, or maintainer merge

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants