зеркало из https://github.com/microsoft/appium.git
Merge pull request #3625 from imurchie/isaac-hint
Handle hint text when setting text in Android
This commit is contained in:
Коммит
9cf909ec87
|
@ -1,18 +1,18 @@
|
|||
package io.appium.android.bootstrap.handler;
|
||||
|
||||
import com.android.uiautomator.core.UiObjectNotFoundException;
|
||||
import io.appium.android.bootstrap.*;
|
||||
import org.json.JSONException;
|
||||
import android.os.SystemClock;
|
||||
import android.view.KeyEvent;
|
||||
import java.lang.reflect.Method;
|
||||
import android.graphics.Rect;
|
||||
import com.android.uiautomator.common.ReflectionUtils;
|
||||
import com.android.uiautomator.core.UiObject;
|
||||
import android.os.SystemClock;
|
||||
import android.view.InputDevice;
|
||||
import android.view.KeyCharacterMap;
|
||||
import android.view.KeyEvent;
|
||||
import com.android.uiautomator.common.ReflectionUtils;
|
||||
import com.android.uiautomator.core.UiObject;
|
||||
import com.android.uiautomator.core.UiObjectNotFoundException;
|
||||
import com.android.uiautomator.core.UiSelector;
|
||||
|
||||
import io.appium.android.bootstrap.*;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import org.json.JSONException;
|
||||
|
||||
|
||||
/**
|
||||
|
@ -43,49 +43,39 @@ public class Clear extends CommandHandler {
|
|||
if (command.isElementCommand()) {
|
||||
try {
|
||||
final AndroidElement el = command.getElement();
|
||||
final ReflectionUtils utils = new ReflectionUtils();
|
||||
Rect rect = el.getVisibleBounds();
|
||||
// Trying to select entire text.
|
||||
TouchLongClick.correctLongClick(rect.left + 20, rect.centerY(),2000);
|
||||
UiObject selectAll = new UiObject(new UiSelector().descriptionContains("Select all"));
|
||||
if (selectAll.waitForExists(2000)) {
|
||||
selectAll.click();
|
||||
}
|
||||
// wait for the selection
|
||||
SystemClock.sleep(500);
|
||||
// delete it
|
||||
final Method sendKey = utils.getControllerMethod("sendKey", int.class,int.class);
|
||||
sendKey.invoke(utils.getController(), KeyEvent.KEYCODE_DEL, 0);
|
||||
|
||||
// first, try to do native clearing
|
||||
Logger.debug("Attempting to clear using UiObject.clearText().");
|
||||
el.clearText();
|
||||
if (el.getText().isEmpty()) {
|
||||
return getSuccessResult(true);
|
||||
}
|
||||
// If above strategy does not work then sending bunch of delete keys after clicking on element.
|
||||
Logger.debug("Clearing text not successful using selectAllDelete now trying to send delete keys.");
|
||||
String tempTextHolder = "";
|
||||
final Object bridgeObject = utils.getBridge();
|
||||
final Method injectInputEvent = utils.getMethodInjectInputEvent();
|
||||
// Preventing infinite while loop.
|
||||
while (!el.getText().isEmpty() && !tempTextHolder.equalsIgnoreCase(el.getText())) {
|
||||
tempTextHolder = el.getText();
|
||||
// Trying send delete keys after clicking in text box.
|
||||
el.click();
|
||||
// Sending 25 delete keys asynchronously
|
||||
final long eventTime = SystemClock.uptimeMillis();
|
||||
KeyEvent deleteEvent = new KeyEvent(eventTime, eventTime, KeyEvent.ACTION_DOWN,
|
||||
KeyEvent.KEYCODE_DEL, 0, 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0, 0,
|
||||
InputDevice.SOURCE_KEYBOARD);
|
||||
for (int count = 0; count < 25; count++) {
|
||||
injectInputEvent.invoke(bridgeObject, deleteEvent, false);
|
||||
}
|
||||
|
||||
final ReflectionUtils utils = new ReflectionUtils();
|
||||
|
||||
// next try to select everything and delete
|
||||
Logger.debug("Clearing text not successful. Attempting to clear " +
|
||||
"by selecting all and deleting.");
|
||||
if (selectAndDelete(el, utils)) {
|
||||
return getSuccessResult(true);
|
||||
}
|
||||
// If still text exist falling back on UIautomator clearText.
|
||||
if (!el.getText().isEmpty()) {
|
||||
Logger.debug("Clearing text not successful falling back to UiAutomator method clear");
|
||||
el.clearText();
|
||||
|
||||
// finally try to send delete keys
|
||||
Logger.debug("Clearing text not successful. Attempting to clear " +
|
||||
"by sending delete keys.");
|
||||
if (sendDeleteKeys(el, utils)) {
|
||||
return getSuccessResult(true);
|
||||
}
|
||||
// If clear text is still unsuccessful throwing error back
|
||||
|
||||
if (!el.getText().isEmpty()) {
|
||||
// either there was a failure, or there is hint text
|
||||
if (hasHintText(el, utils)) {
|
||||
Logger.debug("Text remains after clearing, " +
|
||||
"but it appears to be hint text.");
|
||||
return getSuccessResult(true);
|
||||
} else {
|
||||
return getErrorResult("Clear text not successful.");
|
||||
}
|
||||
}
|
||||
return getSuccessResult(true);
|
||||
} catch (final UiObjectNotFoundException e) {
|
||||
|
@ -94,7 +84,65 @@ public class Clear extends CommandHandler {
|
|||
} catch (final Exception e) { // handle NullPointerException
|
||||
return getErrorResult("Unknown error clearing text");
|
||||
}
|
||||
}
|
||||
return getErrorResult("Unknown error");
|
||||
}
|
||||
return getErrorResult("Unknown error");
|
||||
}
|
||||
|
||||
private boolean selectAndDelete(AndroidElement el, final ReflectionUtils utils)
|
||||
throws UiObjectNotFoundException, IllegalAccessException,
|
||||
InvocationTargetException, NoSuchMethodException {
|
||||
Rect rect = el.getVisibleBounds();
|
||||
// Trying to select entire text.
|
||||
TouchLongClick.correctLongClick(rect.left + 20, rect.centerY(), 2000);
|
||||
UiObject selectAll = new UiObject(new UiSelector().descriptionContains("Select all"));
|
||||
if (selectAll.waitForExists(2000)) {
|
||||
selectAll.click();
|
||||
}
|
||||
// wait for the selection
|
||||
SystemClock.sleep(500);
|
||||
// delete it
|
||||
final Method sendKey = utils.getControllerMethod("sendKey", int.class, int.class);
|
||||
sendKey.invoke(utils.getController(), KeyEvent.KEYCODE_DEL, 0);
|
||||
|
||||
return el.getText().isEmpty();
|
||||
}
|
||||
|
||||
private boolean sendDeleteKeys(AndroidElement el, final ReflectionUtils utils)
|
||||
throws UiObjectNotFoundException, IllegalAccessException,
|
||||
InvocationTargetException, NoSuchMethodException {
|
||||
String tempTextHolder = "";
|
||||
final Object bridgeObject = utils.getBridge();
|
||||
final Method injectInputEvent = utils.getMethodInjectInputEvent();
|
||||
// Preventing infinite while loop.
|
||||
while (!el.getText().isEmpty() && !tempTextHolder.equalsIgnoreCase(el.getText())) {
|
||||
tempTextHolder = el.getText();
|
||||
// Trying send delete keys after clicking in text box.
|
||||
el.click();
|
||||
// Sending 25 delete keys asynchronously
|
||||
final long eventTime = SystemClock.uptimeMillis();
|
||||
KeyEvent deleteEvent = new KeyEvent(eventTime, eventTime, KeyEvent.ACTION_DOWN,
|
||||
KeyEvent.KEYCODE_DEL, 0, 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0, 0,
|
||||
InputDevice.SOURCE_KEYBOARD);
|
||||
for (int count = 0; count < 25; count++) {
|
||||
injectInputEvent.invoke(bridgeObject, deleteEvent, false);
|
||||
}
|
||||
}
|
||||
|
||||
return el.getText().isEmpty();
|
||||
}
|
||||
|
||||
private boolean hasHintText(AndroidElement el, final ReflectionUtils utils)
|
||||
throws UiObjectNotFoundException, IllegalAccessException,
|
||||
InvocationTargetException, NoSuchMethodException {
|
||||
// to test if the remaining text is hint text, try sending a single
|
||||
// delete key and testing if there is any change.
|
||||
// ignore the off-chance that the delete silently fails and we get a false
|
||||
// positive.
|
||||
String currText = el.getText();
|
||||
|
||||
final Method sendKey = utils.getControllerMethod("sendKey", int.class, int.class);
|
||||
sendKey.invoke(utils.getController(), KeyEvent.KEYCODE_DEL, 0);
|
||||
|
||||
return currText.equals(el.getText());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -46,7 +46,10 @@ public class SetText extends CommandHandler {
|
|||
String currText = el.getText();
|
||||
new Clear().execute(command);
|
||||
if (!el.getText().isEmpty()) {
|
||||
Logger.debug("clearText not successful, continuing with setText anyway");
|
||||
// clear could have failed, or we could have a hint in the field
|
||||
// we'll assume it is the latter
|
||||
Logger.debug("Text not cleared. Assuming remainder is hint text.");
|
||||
currText = "";
|
||||
}
|
||||
if (!replace) {
|
||||
text = currText + text;
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 43889d79c5dc1ef4a0c90d0be136f506a2b19c3b
|
||||
Subproject commit c7973952c0a55e61e49de2110e04fa9da17f063b
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
var env = require('../../helpers/env')
|
||||
, setup = require("./setup-base")
|
||||
, safeClear = require('../../helpers/safe-clear')
|
||||
, _ = require('underscore')
|
||||
, getAppPath = require('../../helpers/app').getAppPath;
|
||||
|
||||
|
@ -24,12 +23,43 @@ module.exports = function () {
|
|||
driver
|
||||
.waitForElementByClassName('android.widget.EditText')
|
||||
.then(function (_el) { el = _el; })
|
||||
.then(function () { return safeClear(el); })
|
||||
.then(function () {
|
||||
if (env.SELENDROID) {
|
||||
return el.clear();
|
||||
}
|
||||
})
|
||||
.then(function () { return el.sendKeys(testText); })
|
||||
.then(function () { return el.text().should.become(testText); })
|
||||
.nodeify(done);
|
||||
};
|
||||
|
||||
var runEditAndClearTest = function (testText, done) {
|
||||
var el;
|
||||
driver
|
||||
.waitForElementByClassName('android.widget.EditText')
|
||||
.then(function (_el) { el = _el; })
|
||||
.then(function () {
|
||||
if (env.SELENDROID) {
|
||||
return el.clear();
|
||||
}
|
||||
})
|
||||
.then(function () { return el.sendKeys(testText).text().should.become(testText); })
|
||||
.then(function () {
|
||||
return el.clear().should.not.be.rejected;
|
||||
})
|
||||
.then(function () {
|
||||
// Selendroid and uiautomator have different ways of dealing with
|
||||
// hint text. In particular, Selendroid does not return it
|
||||
// and uiautomator does.
|
||||
var expectedText = "hint text";
|
||||
if (env.SELENDROID) {
|
||||
expectedText = "";
|
||||
}
|
||||
return el.text().should.become(expectedText);
|
||||
})
|
||||
.nodeify(done);
|
||||
};
|
||||
|
||||
describe('editing ascii text field', function () {
|
||||
setup(this, desired).then(function (d) { driver = d; });
|
||||
|
||||
|
@ -38,22 +68,12 @@ module.exports = function () {
|
|||
runTextEditTest(testText, done);
|
||||
});
|
||||
|
||||
// TODO: clear is not reliable
|
||||
it('should be able to edit and clear a text field', function (done) {
|
||||
var testText = "The answer is 42.", el;
|
||||
driver
|
||||
.waitForElementByClassName('android.widget.EditText')
|
||||
.then(function (_el) { el = _el; })
|
||||
.then(function () { return safeClear(el); })
|
||||
.then(function () { return el.sendKeys(testText).text().should.become(testText); })
|
||||
.then(function () { return safeClear(el); })
|
||||
// TODO: there is a bug here we should not need safeClear
|
||||
// workaround for now.
|
||||
.then(function () { return el.text().should.become(""); })
|
||||
.nodeify(done);
|
||||
it('should be able to edit and manually clear a text field', function (done) {
|
||||
var testText = "The answer is 42.";
|
||||
runEditAndClearTest(testText, done);
|
||||
});
|
||||
|
||||
it('as should be able to send &-', function (done) {
|
||||
it('should be able to send &-', function (done) {
|
||||
var testText = '&-';
|
||||
runTextEditTest(testText, done);
|
||||
});
|
||||
|
@ -80,19 +100,9 @@ module.exports = function () {
|
|||
runTextEditTest(testText, done);
|
||||
});
|
||||
|
||||
// TODO: clear is not reliable
|
||||
it('should be able to edit and clear a text field', function (done) {
|
||||
var testText = "The answer is 42.", el;
|
||||
driver
|
||||
.waitForElementByClassName('android.widget.EditText')
|
||||
.then(function (_el) { el = _el; })
|
||||
.then(function () { return safeClear(el); })
|
||||
.then(function () { return el.sendKeys(testText).text().should.become(testText); })
|
||||
.then(function () { return safeClear(el); })
|
||||
// TODO: there is a bug here we should not need safeClear
|
||||
// workaround for now.
|
||||
.then(function () { return el.text().should.become(""); })
|
||||
.nodeify(done);
|
||||
it('should be able to edit and manually clear a text field', function (done) {
|
||||
var testText = "The answer is 42.";
|
||||
runEditAndClearTest(testText, done);
|
||||
});
|
||||
|
||||
it('should be able to send &-', function (done) {
|
||||
|
|
Загрузка…
Ссылка в новой задаче