diff --git a/src/platforms/android/__tests__/input-actions-fill.test.ts b/src/platforms/android/__tests__/input-actions-fill.test.ts
index 936c2d7a1..be9376500 100644
--- a/src/platforms/android/__tests__/input-actions-fill.test.ts
+++ b/src/platforms/android/__tests__/input-actions-fill.test.ts
@@ -354,8 +354,14 @@ test('verifyAndroidFilledTextInHierarchy redacts masked password values on wrong
);
});
-test('readAndroidTextAtPointInHierarchy prefers focused edit text over point fallback', () => {
- assert.equal(readAndroidTextAtPointInHierarchy(focusedEditHierarchy(), 10, 10), 'focused value');
+test('readAndroidTextAtPointInHierarchy reads the EditText under the requested point', () => {
+ const hierarchy = `
+
+
+`;
+
+ assert.equal(readAndroidTextAtPointInHierarchy(hierarchy, 100, 50), 'Ada Lovelace');
+ assert.equal(readAndroidTextAtPointInHierarchy(hierarchy, 100, 250), 'fallback@example.com');
});
const IME_RESOURCE_ID = 'com.google.android.inputmethod.latin:id/0_resource_name_obfuscated';
@@ -467,13 +473,6 @@ function androidInputXml(options: { text: string }): string {
return ``;
}
-function focusedEditHierarchy(): string {
- return `
-
-
-`;
-}
-
function appPackageWithInputMethodSubstringHierarchy(): string {
return `
diff --git a/src/platforms/android/fill-verification.ts b/src/platforms/android/fill-verification.ts
index 9a6682ac1..c980af6d0 100644
--- a/src/platforms/android/fill-verification.ts
+++ b/src/platforms/android/fill-verification.ts
@@ -111,7 +111,9 @@ export function readAndroidTextAtPointInHierarchy(
x: number,
y: number,
): string | null {
- return inspectAndroidTextAtPointInHierarchy(xml, x, y).actualInput?.text ?? null;
+ // Reads are point-targeted: a focused sibling may be a different app field or an IME
+ // composing surface, so it must not override the node that contains the requested point.
+ return inspectAndroidTextAtPointInHierarchy(xml, x, y).targetInput?.text ?? null;
}
export function androidFillFailureMessage(verification: AndroidFillVerification | null): string {
diff --git a/test/integration/android-emulator-e2e/live-form-scenario.ts b/test/integration/android-emulator-e2e/live-form-scenario.ts
index c3f84c153..25201347f 100644
--- a/test/integration/android-emulator-e2e/live-form-scenario.ts
+++ b/test/integration/android-emulator-e2e/live-form-scenario.ts
@@ -7,7 +7,6 @@ import type { CliJsonResult } from '../cli-json.ts';
import {
assertElementText,
assertFilesDiffer,
- assertJsonContains,
assertWaitText,
capturePng,
requireAndroidResourceId,
@@ -36,16 +35,22 @@ export async function assertFormInput(context: LiveContext): Promise {
'id="field-name"',
'Ada Łovelace',
]);
+ await runStep(context, 'fill email field', ['fill', 'id="field-email"', 'ada@example']);
const name = await runStep(context, 'read filled full name', ['get', 'text', 'id="field-name"']);
- assertJsonContains(name, 'Ada Łovelace', 'Unicode name should be observable in Android UI');
+ assert.equal(name.json?.data?.text, 'Ada Łovelace', JSON.stringify(name.json));
+ const emailText = await runStep(context, 'read filled email', [
+ 'get',
+ 'text',
+ 'id="field-email"',
+ ]);
+ assert.equal(emailText.json?.data?.text, 'ada@example', JSON.stringify(emailText.json));
verifyCommand(context, C.fill, 'Unicode replacement text is read back from Android UI');
verifyBehavior(
context,
'test-ime-unicode-input',
- `active ${ANDROID_TEST_IME_PACKAGE} committed and read back Ada Łovelace`,
+ `active ${ANDROID_TEST_IME_PACKAGE} committed and both filled values were read from their own Android fields`,
);
- await runStep(context, 'fill email field', ['fill', 'id="field-email"', 'ada@example']);
const snapshot = await runStep(context, 'locate email Android resource-id', ['snapshot', '-i']);
const email = requireAndroidResourceId(snapshot, 'field-email');
await runStep(context, 'focus email by snapshot-derived point', [