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', [