fix: read Android get text from target field (#1561)

This commit is contained in:
Michał Pierzchała
2026-08-03 12:09:31 +02:00
committed by GitHub
parent c18636315a
commit f8617a2db9
3 changed files with 20 additions and 14 deletions
@@ -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 = `<?xml version="1.0" encoding="UTF-8"?><hierarchy>
<node package="com.example" class="android.widget.EditText" text="Ada Lovelace" resource-id="com.example:id/field-name" focused="false" bounds="[0,0][200,100]"/>
<node package="com.example" class="android.widget.EditText" text="fallback@example.com" resource-id="com.example:id/field-email" focused="true" bounds="[0,200][200,300]"/>
</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 `<?xml version="1.0" encoding="UTF-8"?><hierarchy><node package="com.example" class="android.widget.EditText" text="${options.text}" focused="true" bounds="[0,0][200,100]"/></hierarchy>`;
}
function focusedEditHierarchy(): string {
return `<?xml version="1.0" encoding="UTF-8"?><hierarchy>
<node package="com.example" class="android.widget.TextView" text="point fallback" focused="false" bounds="[0,0][200,100]"/>
<node package="com.example" class="android.widget.EditText" text="focused value" focused="true" bounds="[300,300][500,400]"/>
</hierarchy>`;
}
function appPackageWithInputMethodSubstringHierarchy(): string {
return `<?xml version="1.0" encoding="UTF-8"?><hierarchy>
<node package="com.example.shop" class="android.widget.EditText" text="Search Products" resource-id="com.example.shop:id/search" focused="false" bounds="[0,0][300,100]"/>
+3 -1
View File
@@ -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 {
@@ -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<void> {
'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', [