Ошибка тестирования пользовательского интерфейса - ни элемент, ни какой-либо его потомок не фокусируются на клавиатуре secureTextField
Это мой случай:
let passwordSecureTextField = app.secureTextFields["password"]
passwordSecureTextField.tap()
passwordSecureTextField.typeText("wrong_password") //here is an error
UI Testing Failure - Ни элемент, ни потомок не имеют фокусировки на клавиатуре. Элемент:
Что случилось? Это нормально работает нормально textFields
, но проблемы возникают только с secureTextFields
, Есть обходные пути?
29 ответов
Эта проблема причинила мне боль, но мне удалось найти правильное решение. В симуляторе убедитесь, что "Оборудование -> Клавиатура -> Подключить аппаратную клавиатуру" выключено.
Недавно мы нашли хак, чтобы сделать решение из принятого ответа постоянным. Чтобы отключить настройку симулятора: "Оборудование -> Клавиатура -> Подключить аппаратную клавиатуру" из командной строки, необходимо написать:
defaults write com.apple.iphonesimulator ConnectHardwareKeyboard 0
Это не повлияет на работающий симулятор - вам нужно перезапустить симулятор или запустить новый, чтобы настройки вступили в силу.
Я написал небольшое расширение (Swift), которое идеально подходит для меня. Вот код:
extension XCTestCase {
func tapElementAndWaitForKeyboardToAppear(element: XCUIElement) {
let keyboard = XCUIApplication().keyboards.element
while (true) {
element.tap()
if keyboard.exists {
break;
}
NSRunLoop.currentRunLoop().runUntilDate(NSDate(timeIntervalSinceNow: 0.5))
}
}
}
Основная идея состоит в том, чтобы продолжать нажимать на элемент (текстовое поле) до того, как появится клавиатура.
У Станислава правильная идея.
В командной среде вам нужно что-то, что будет работать автоматически. Я нашел исправление в моем блоге.
В основном вы просто вставляете:
UIPasteboard.generalPasteboard().string = "Their password"
let passwordSecureTextField = app.secureTextFields["password"]
passwordSecureTextField.pressForDuration(1.1)
app.menuItems["Paste"].tap()
Другая причина этой ошибки - если есть родительское представление текстового поля, в которое вы пытаетесь ввести текст, который установлен как элемент доступности (view.isAccessibilityElement = true
). В этом случае XCTest не может получить дескриптор подпредставления для ввода текста и возвращает ошибку.
UI Testing Failure - Ни элемент, ни потомок не имеют фокусировки на клавиатуре.
Дело не в том, что ни один элемент не имеет фокуса (как вы часто можете видеть клавиатуру вверх и мигающий курсор в UITextField), просто ни один элемент не может достичь фокуса. Я столкнулся с этим при попытке ввести текст в UISearchBar. Сама панель поиска не является текстовым полем, при установке его в качестве элемента доступности доступ к базовому UITextField был заблокирован. Чтобы решить это, searchBar.accessibilityIdentifier = "My Identifier"
был установлен на UISearchBar
Тем не менее isAccessibilityElement
не был установлен в true
, После этого протестируйте код вида:
app.otherElements["My Identifier"].tap()
app.otherElements["My Identifier"].typeText("sample text")
Работает
В моем случае это Hardware -> Keyboard -> Connect Hardware Keyboard
-> Отключение у меня не сработало.
Но когда я последовал
1) Hardware -> Keyboard -> Connect Hardware Keyboard
-> Включено и запустите приложение
2)Hardware -> Keyboard -> Connect Hardware Keyboard
-> Отключить.
Это сработало для меня
Это происходило со мной много раз. Вы должны отключить аппаратное обеспечение клавиатуры и ту же раскладку, что и в OSX в вашем симуляторе
Оборудование / Клавиатура (отключить все)
После этого программное обеспечение клавиатуры не исчезнет, и ваши тесты смогут набирать текст
Это может помочь: я просто добавляю "касание" перед ошибкой; это все:)
[app.textFields[@"theTitle"] tap];
[app.textFields[@"theTitle"] typeText:@"kk"];
Используйте режим сна между запуском приложения и вводом данных в текстовые поля, например так:
sleep(2)
В моем случае я продолжал получать эту ошибку каждый раз, и только это решение помогло мне.
func pasteTextFieldText(app:XCUIApplication, element:XCUIElement, value:String, clearText:Bool) {
// Get the password into the pasteboard buffer
UIPasteboard.generalPasteboard().string = value
// Bring up the popup menu on the password field
element.tap()
if clearText {
element.buttons["Clear text"].tap()
}
element.doubleTap()
// Tap the Paste button to input the password
app.menuItems["Paste"].tap()
}
Запишите случай, как хотите, с клавиатурой или без клавиатуры. Но перед игрой сделайте следующее.
Этот следующий параметр (подключить аппаратную клавиатуру) должен быть отключен во время воспроизведения теста.
Работает для меня:
extension XCUIElement {
func typeTextAlt(_ text: String) {
// Solution for `Neither element nor any descendant has keyboard focus.`
if !(self.value(forKey: "hasKeyboardFocus") as? Bool ?? false) {
XCUIDevice.shared.press(XCUIDevice.Button.home)
XCUIApplication().activate()
}
self.typeText(text)
}
}
[Повторно Bartłomiej Semańczyk комментарий Бартломея Bartłomiej Semańczyk, потому что он решил проблему для меня]
Мне нужно было выполнить Simulator > Reset Contents and Settings в строке меню симулятора, чтобы это начало работать для меня.
Я столкнулся с этой проблемой и смог исправить ее в своем сценарии, взяв решение, опубликованное @AlexDenisov, и добавив его в свои предварительные действия для запуска и тестирования.
ht tps://stackru.com/images/39ee0150a669bc8db86ce894caafd51ca5886fe0.png
Приведенные выше ответы решили это для меня при запуске тестов пользовательского интерфейса, когда пользовательский интерфейс симулятора находится на переднем плане (когда вы могли видеть изменения в окне симулятора). Однако это не сработало, когда они запускались в фоновом режиме (например, как их запускал fastlane).
Я должен был пойти в
Simulator > Device > Erase All Content and Settings...
Для ясности, я вручную отключил-включено-отключил аппаратную клавиатуру в настройках симулятора, установил
Следующее - из сценария приложения Swift 5.4, SwiftUI, которое не прошло бы тестирование пользовательского интерфейса.
app.cells.firstMatch.tap()
// transition from list view to detail view for item
app.textFields
.matching(identifier: "balance_textedit")
.element.tap()
app.textFields
.matching(identifier: "balance_textedit")
.element.typeText("7620") // :FAIL:
Тест: не удалось синтезировать событие: ни у элемента, ни у потомка нет фокуса клавиатуры.
Решение: нажмите еще раз
Второй
tap()
было обнаружено, что прошло.
app.cells.firstMatch.tap()
// transition from list view to detail view for item
app.textFields
.matching(identifier: "balance_textedit")
.element.tap()
app.textFields
.matching(identifier: "balance_textedit")
.element.tap()
app.textFields
.matching(identifier: "balance_textedit")
.element.typeText("7620") // :PASS:
Тем не мение,
.doubleTap()
а также
tap(withNumberOfTaps: 2, numberOfTouches: 1)
все равно не получится.
Решение: дождитесь текстового поля, затем коснитесь
Иногда для перехода к просмотру необходимо явно дождаться прибытия TextEdit. Приведенный ниже подход ожидает прибытия целевого поля TextEdit.
Расширение XCTestCase.swift:
extension XCTestCase {
func awaitArrival(element: XCUIElement, timeout: TimeInterval) {
let predicate = NSPredicate(format: "exists == 1")
expectation(for: predicate, evaluatedWith: element)
waitForExpectations(timeout: timeout)
}
func awaitDeparture(element: XCUIElement, timeout: TimeInterval) {
let predicate = NSPredicate(format: "exists == 0")
expectation(for: predicate, evaluatedWith: element)
waitForExpectations(timeout: timeout)
}
}
Пример использования:
app.cells.firstMatch.tap()
// transition from list view to detail view for item
awaitArrival(
element: app.textFields.matching(identifier: "balance_textedit").element,
timeout: 5)
app.textFields
.matching(identifier: "balance_textedit")
.element.tap()
app.textFields
.matching(identifier: "balance_textedit")
.element.typeText("7620") // :PASS:
Иногда текстовые поля не реализуются как текстовые поля, или они заключены в другой элемент пользовательского интерфейса и не легко доступны. Вот работа вокруг:
//XCUIApplication().scrollViews.otherElements.staticTexts["Email"] the locator for the element
RegistrationScreenStep1of2.emailTextField.tap()
let keys = app.keys
keys["p"].tap() //type the keys that you need
//If you stored your data somewhere and need to access that string //you can cats your string to an array and then pass the index //number to key[]
let newUserEmail = Array(newPatient.email())
let password = Array(newPatient.password)
//When you cast your string to an array the elements of the array //are Character so you would need to cast them into string otherwise //Xcode will compain.
let keys = app.keys
keys[String(newUserEmail[0])].tap()
keys[String(newUserEmail[1])].tap()
keys[String(newUserEmail[2])].tap()
keys[String(newUserEmail[3])].tap()
keys[String(newUserEmail[4])].tap()
keys[String(newUserEmail[5])].tap()
emailTextField.tap() // add this line before typeText
emailTextField.typeText("your text")
Другой ответ, но для нас проблема заключалась в том, что представление было слишком близко к другому представлению, что распознаватель жестов на нем. Мы обнаружили, что нам нужно, чтобы изображение было на расстоянии не менее 20 пикселей (в нашем случае ниже). Буквально 15 не сработало, а 20 и более сработало. Это странно, я признаю, но у нас было несколько UITextViews, которые работали, и некоторые, которые не работали, и все находились под одним и тем же родителем и имели идентичное другое позиционирование (и, конечно, имена переменных). Клавиатура включена или выключена или что-то не имеет значения. Доступность показала поля. Мы перезапустили наши компьютеры. Мы сделали чистые сборки. Свежий источник проверки.
Нет необходимости включать / выключать клавиатуру при вводе / выводе. Не используйте.typeText для secureTextField, просто используйте
app.keys["p"].tap()
app.keys["a"].tap()
app.keys["s"].tap()
app.keys["s"].tap()
Бонус: слышен щелчок клавиатуры:)
Проблема для меня была такой же, как и для Теда. На самом деле, если поле пароля вводится после того, как поле входа в систему и аппаратное КБ включены, программная клавиатура будет отключаться при втором нажатии поля, и это не относится к тестам пользовательского интерфейса.
После некоторого времени возни с AppleScript вот что я придумал (улучшения приветствуются):
tell application "Simulator"
activate
tell application "System Events"
try
tell process "Simulator"
tell menu bar 1
tell menu bar item "Hardware"
tell menu "Hardware"
tell menu item "Keyboard"
tell menu "Keyboard"
set menuItem to menu item "Connect Hardware Keyboard"
tell menu item "Connect Hardware Keyboard"
set checkboxStatus to value of attribute "AXMenuItemMarkChar" of menuItem
if checkboxStatus is equal to "✓" then
click
end if
end tell
end tell
end tell
end tell
end tell
end tell
end tell
on error
tell application "System Preferences"
activate
set securityPane to pane id "com.apple.preference.security"
tell securityPane to reveal anchor "Privacy_Accessibility"
display dialog "Xcode needs Universal access to disable hardware keyboard during tests(otherwise tests may fail because of focus issues)"
end tell
end try
end tell
end tell
Создайте файл сценария с приведенным выше кодом и добавьте его к необходимым целям (возможно, цель - только для тестов пользовательского интерфейса, вы можете добавить аналогичный сценарий к целям разработки, чтобы повторно включить HW-клавиатуру во время разработки). Вы должны добавить Run Script
этап на этапе сборки и использовать его так:osascript Path/To/Script/script_name.applescript
Мы столкнулись с той же ошибкой при установке accessibilityIdentifier
значение для пользовательского представления (UIStackView
подкласс), содержащий UIControl
подвиды. В этом случае XCTest не удалось получить фокус клавиатуры для дочерних элементов.
Наше решение было просто удалить accessibilityIdentifier
от нашего родительского взгляда и установить accessibilityIdentifier
для подпредставлений через выделенные свойства.
Не перепутайте, проблема возникла из-за того, что вы записали время тестирования, в котором ваше приложение будет подключаться к аппаратной клавиатуре, а симулятор времени автоматического тестирования использует только программную клавиатуру. так что как решить эту проблему. Просто используйте программную клавиатуру на время записи. Вы можете увидеть магию.
Ваша первая строка - это просто определение запроса, что не означает, что passwordSecureTextField
на самом деле существует.
Ваша вторая строка будет динамически выполнять запрос и попытаться (повторно) связать запрос с элементом пользовательского интерфейса. Вы должны установить точку останова и убедиться, что найден один и только один элемент. Или просто используйте assert:
XCTAssertFalse(passwordSecureTextField.exists);
В противном случае это выглядит хорошо, tap
должен заставить клавиатуру быть видимой typeText
должен просто работать. Журнал ошибок должен рассказать вам больше информации.
Короче говоря: ввод с аппаратной клавиатуры в iOS Simulator уже давно не работает.
Дело в том, что
-[UIApplicationSupport _accessibilityHardwareKeyboardActive]
частный метод от
com.apple.UIKit.axbundle
иногда возвращается
NO
хотя он должен вернуться
YES
(когда вид ввода текста
firstResponder
, а курсор мигает, что буквально означает, что аппаратная клавиатура активна).
Поместите этот код в любом месте приложения, чтобы симулятор iOS считал, что если программная клавиатура неактивна, значит, активна аппаратная.
#if TARGET_OS_SIMULATOR
__attribute__((constructor))
static void Fix_UIApplicationAccessibility__accessibilityHardwareKeyboardActive(void) {
{
static id observer = nil;
observer = [[NSNotificationCenter defaultCenter] addObserverForName:NSBundleDidLoadNotification
object:nil
queue:nil
usingBlock:^(NSNotification * _Nonnull note) {
NSBundle *bundle = note.object;
if ([bundle.bundleIdentifier isEqualToString:@"com.apple.UIKit.axbundle"]) {
Class cls = objc_lookUpClass("UIApplicationAccessibility");
SEL sel = sel_getUid("_accessibilityHardwareKeyboardActive");
Method m = class_getInstanceMethod(cls, sel);
method_setImplementation(m, imp_implementationWithBlock(^BOOL(UIApplication *app) {
return ![[app valueForKey:@"_accessibilitySoftwareKeyboardActive"] boolValue];
}));
}
}];
}
}
#endif
Наконец, я написал сценарий, который редактирует файл.plist симулятора и устанавливает ConnectHardwareKeyboard
значение false для выбранного симулятора. Вы правильно поняли, он изменяет свойство для специально выбранного симулятора в словаре DevicePreferences, а не редактирует глобальное свойство.
Сначала создайте сценарий оболочки с именем disable-hardware-keyboard.sh со следующим содержимым. Вы можете разместить его в "YourProject/xyzUITests/Scripts/":
echo "Script: Set ConnectHardwareKeyboard to false for given Simulator UDID"
if [[ $1 != *-*-*-*-* ]]; then
echo "Pass device udid as first argument."
exit 1
else
DEVICE_ID=$1
fi
DEVICE_PREFERENCES_VALUE='<dict><key>ConnectHardwareKeyboard</key><false/></dict>'
killall Simulator # kill restart the simulator to make the plist changes picked up
defaults write com.apple.iphonesimulator DevicePreferences -dict-add $DEVICE_ID $DEVICE_PREFERENCES_VALUE
open -a Simulator # IMPORTANT
Теперь выполните следующие действия, чтобы вызвать его с передачей udid выбранного симулятора в качестве аргумента:
- Отредактируйте схему Xcode (или определенную схему тестов пользовательского интерфейса, если она у вас есть)
- Перейдите в: Тест> Предварительные действия
- Добавьте новый сценарий, нажав символ "+"> "Новое действие сценария выполнения".
- Важно: в раскрывающемся списке "Предоставить настройки сборки из" выберите цель основного приложения, а не цель тестов пользовательского интерфейса.
- Теперь добавьте следующий скрипт в текстовую область ниже.
Скрипт внутри Test>Pre-actions:
#!/bin/sh
# $PROJECT_DIR is path to your source project. This is provided when we select "Provide build settings from" to "AppTarget"
# $TARGET_DEVICE_IDENTIFIER is the UDID of the selected simulator
sh $PROJECT_DIR/xyzUITests/Scripts/disable-hardware-keyboard.sh $TARGET_DEVICE_IDENTIFIER
# In order to see output of above script, append following with it:
# | tee ~/Desktop/ui-test-scheme-prescript.txt
Пора это проверить:
- Симулятор запуска
- Включите для него аппаратную клавиатуру
- Запустите любой тест пользовательского интерфейса с помощью клавиатуры. Наблюдайте, как симулятор перезапускается и аппаратная клавиатура отключена. И взаимодействие теста с клавиатурой работает нормально.:)
Что исправило эту проблему для меня, так это добавив 1 секунду сна:
let textField = app.textFields["identifier"]
textField.tap()
sleep(1)
textField.typeText(text)
Была такая же проблема с Securetextfields. Вариант подключения оборудования в моем симуляторе был, но все же столкнулся с проблемой. Наконец, это сработало для меня (Swift 3):
let enterPasswordSecureTextField = app.secureTextFields["Enter Password"]
enterPasswordSecureTextField.tap()
enterPasswordSecureTextField.typeText("12345678")
Убедитесь, что вы отключили «Подключить аппаратную клавиатуру» из ввода-вывода в симуляторе для XCode 12.