Échec du test de l'interface utilisateur - Aucun élément ni aucun descendant n'a le focus clavier sur secureTextField

139

C'est mon cas:

let passwordSecureTextField = app.secureTextFields["password"]
passwordSecureTextField.tap()
passwordSecureTextField.typeText("wrong_password") //here is an error

Échec du test de l'interface utilisateur - Aucun élément ni aucun descendant n'a le focus clavier. Élément:

Qu'est-ce qui ne va pas? Cela fonctionne bien pour la normale textFields, mais le problème ne survient qu'avec secureTextFields. Des solutions de contournement?

Bartłomiej Semańczyk
la source
et la partie étrange est que si j'essaye comme ça, cela fonctionne XCUIApplication (). webViews.secureTextFields ["Password"]. tap () XCUIApplication (). webViews.secureTextFields ["Password"]. typeText ("Welcome")
aurilio
Il se peut que vous ayez défini l'identifiant d'accessibilité, mais que vous n'avez pas défini isAccessibilityElement = true
soumil
J'ai ajouté une réponse à une question similaire ici: stackoverflow.com/a/59637897/2585413 . Mon problème était que j'avais d'autres UIWindows avec de mauvaises valeurs
.windowLevel

Réponses:

264

Ce problème m'a causé un monde de douleur, mais j'ai réussi à trouver une solution appropriée. Dans le simulateur, assurez-vous que «Matériel -> Clavier -> Connecter le clavier matériel» est désactivé.

Ted Kaminski
la source
4
Cela a résolu le problème. Mais c'est une solution de contournement très médiocre car nous ne pouvons pas l'appliquer automatiquement pour CI.
Stanislav Pankevich
25
Si vous exécutez vos tests sur CI (Jenkins etc.) Vous pouvez définir les paramètres suivants dans un script avant d'exécuter vos tests. "Par défaut, écrivez com.apple.iphonesimulator ConnectHardwareKeyboard 0"
charlyatwork
7
Cela ne résout pas le problème pour moi. Je peux clairement voir le champ de texte obtenir le focus, avec le clavier relevé, mais le cadre de test ne peut jamais entrer de texte à l'aide de la méthode typeText:
Michael
2
A travaillé pour moi sur Xcode 11, mais décocher le clavier matériel Connect peut ne pas suffire. Il faut en outre s'assurer que le clavier logiciel est vraiment affiché (j'avais en fait la situation où aucun clavier matériel n'était connecté et aucun clavier logiciel n'était affiché, même si textField avait le focus et le curseur clignotait).
Reinhard Männer
1
J'ai également rencontré cela sur Xcode 11. Corrige localement, mais comment cela peut-il être défini dans CI?
jherg
26

Récemment, nous avons trouvé un hack pour rendre la solution à partir de la réponse acceptée persistante. Pour désactiver les paramètres du simulateur: 'Matériel -> Clavier -> Connecter le clavier matériel' à partir de la ligne de commande, il faut écrire:

defaults write com.apple.iphonesimulator ConnectHardwareKeyboard 0

Cela n'affectera pas un simulateur en cours d'exécution - vous devez redémarrer le simulateur ou en démarrer un nouveau pour que ce paramètre ait son effet.

AlexDenisov
la source
cela n'a rien changé.
netshark1000
@ netshark1000 qui peut être modifié avec la nouvelle version de Xcode, je vais le vérifier.
AlexDenisov
1
extrêmement utile, j'avais besoin du contraire où le clavier est toujours activé, mais oui, c'est super merci. J'ai fini par y aller defaults write com.apple.iphonesimulator ConnectHardwareKeyboard -bool true, mais c'est le même concept.
Laser Hawk
2
J'ai créé un script sur les phases de construction pour le faire. Mais j'avais besoin de tuer tous les simulateurs, alors j'ai fait ça:killall "Simulator"; defaults write com.apple.iphonesimulator ConnectHardwareKeyboard 0;
Wagner Sales
Cela ne semble plus fonctionner, la clé a-t-elle changé ?? @AlexDenisov
Simon McLoughlin le
15

J'ai écrit une petite extension (Swift) qui fonctionne parfaitement pour moi. Voici le code:

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))
        }
    }
}

L'idée principale est de continuer à taper sur un élément (champ de texte) avant que le clavier ne soit présenté.

berezhnyi oleksandr
la source
Cela ne fonctionne pas pour moi en ce moment. J'ai Xcode 7.2 (7C68), quelle version avez-vous? Cette solution que nous avons trouvée pour CI fonctionne pour nous: stackoverflow.com/a/34812979/598057 .
Stanislav Pankevich
Pour le confirmer une fois de plus: cet assistant fonctionne pour vous pour un champ de texte de mot de passe sécurisé, pas seulement un champ de texte normal?
Stanislav Pankevich
Oui, cela fonctionne pour les deux types de champ de texte: régulier et sécurisé.
berezhnyi oleksandr
1
Le problème avec cette réponse est qu'elle utilise un délai codé en dur. À la place, utilisez le modèle d'attente des attentes avec l'élément clavier.
user1122069
11

Stanislav a la bonne idée.

Dans un environnement d'équipe, vous avez besoin de quelque chose qui fonctionnera automatiquement. J'ai trouvé une solution ici sur mon blog.

En gros, vous venez de coller:

UIPasteboard.generalPasteboard().string = "Their password"
let passwordSecureTextField = app.secureTextFields["password"]
passwordSecureTextField.pressForDuration(1.1)
app.menuItems["Paste"].tap()
RyanPliske
la source
Merci d'avoir partagé votre solution. Malheureusement, cela ne fonctionne pas pour moi en ce moment. J'ai Xcode 7.2 (7C68), qu'est-ce que vous? Voir l'autre solution que nous avons trouvée pour CI: stackoverflow.com/questions/32184837/… .
Stanislav Pankevich
8

Une autre cause de cette erreur est s'il existe une vue parent du champ de texte dans lequel vous essayez de saisir du texte défini comme élément d'accessibilité ( view.isAccessibilityElement = true). Dans ce cas, XCTest n'est pas en mesure d'obtenir une poignée sur la sous-vue pour entrer le texte et renvoie l'erreur.

Échec du test de l'interface utilisateur - Aucun élément ni aucun descendant n'a le focus clavier.

Ce n'est pas qu'aucun élément n'a le focus (comme vous pouvez souvent voir le clavier vers le haut et le curseur clignotant dans UITextField), c'est juste qu'aucun élément qu'il peut atteindre n'a le focus. J'ai rencontré cela en essayant de saisir du texte dans un UISearchBar. La barre de recherche elle-même n'est pas le champ de texte, lors de sa définition en tant qu'élément d'accessibilité, l'accès à l'UITextField sous-jacent a été bloqué. Pour résoudre ce problème, a searchBar.accessibilityIdentifier = "My Identifier"été défini sur UISearchBarmais isAccessibilityElementn'a pas été défini sur true. Après cela, testez le code du formulaire:

app.otherElements["My Identifier"].tap()
app.otherElements["My Identifier"].typeText("sample text")

Travaux

user3847320
la source
6

Cela s'est produit avec moi plusieurs fois. Vous devez désactiver le matériel du clavier et la même disposition que OSX dans votre simulateur

Matériel / clavier (tout désactiver)

Après cela, le logiciel de clavier ne rejettera pas et vos tests pourront taper du texte

Désactiver le matériel

Fernando Martínez
la source
5

Utilisez un sommeil entre le lancement de l'application et la saisie de données dans des champs de texte comme celui-ci:

sleep(2)

Dans mon cas, je continuais à recevoir cette erreur à chaque fois et seule cette solution m'a aidé.

Kingalione
la source
1
Le problème est que si vous avez un grand nombre de tests d'interface utilisateur qui effectuent quelque chose comme une connexion initiale, cela ajoutera une surcharge énorme à votre exécution de test CI.
delta2flat
4

Cela peut peut-être aider: j'ajoute simplement une action "tap" avant l'erreur; c'est tout :)

[app.textFields[@"theTitle"] tap];
[app.textFields[@"theTitle"] typeText:@"kk"];
Carlos Peralta
la source
2
Cela ne fonctionne certainement pas pour le champ de texte de mot de passe sécurisé.
Stanislav Pankevich
Cela a très bien fonctionné pour mon champ de texte de mot de passe sécurisé. Bonne solution.
Travis M.
4
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()
}
Anthony
la source
4

Enregistrez le boîtier comme vous le souhaitez, clavier ou sans clavier attaché. Mais faites ce qui suit avant de jouer au test.

Cette option suivante (connecter le clavier matériel) doit être décochée pendant la lecture du test.

entrez la description de l'image ici

umairhhhs
la source
4

Dans mon cas ceci Hardware -> Keyboard -> Connect Hardware Keyboard-> Désactiver n'a pas fonctionné pour moi.

Mais quand j'ai suivi

1) Hardware -> Keyboard -> Connect Hardware Keyboard-> Activé et exécutez l'application
2) Hardware -> Keyboard -> Connect Hardware Keyboard-> Désactiver .

Ça a marché pour moi

BharathRao
la source
A travaillé pour moi, MAN Je déteste cet aspect de XCode!
bwobbones
3

[Republier le commentaire de Bartłomiej Semańczyk comme réponse car il a résolu le problème pour moi]

J'avais besoin de faire Simulateur> Réinitialiser le contenu et les paramètres dans la barre de menus du simulateur pour que cela commence à fonctionner pour moi.

rogueleaderr
la source
La réinitialisation du simulateur m'a également aidé. Fait intéressant, le problème ne s'est produit que dans le simulateur alors que le même test (appuyez sur texfield suivi de la saisie de caractères) s'est bien déroulé sur un appareil.
Christian
1

Parfois, les champs de texte ne sont pas implémentés en tant que champs de texte, ou ils sont enveloppés dans un autre élément de l'interface utilisateur et ne sont pas facilement accessibles. Voici un travail autour:

//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()       

Eugène Berezin
la source
0

Votre première ligne n'est qu'une définition de requête , ce qui ne veut pas dire qu'elle passwordSecureTextFieldexisterait réellement.

Votre deuxième ligne exécutera dynamiquement la requête et tentera de (re) lier la requête à l'élément d'interface utilisateur. Vous devez y mettre un point d'arrêt et vérifier qu'un et un seul élément est trouvé. Ou utilisez simplement une assertion:

XCTAssertFalse(passwordSecureTextField.exists);

Sinon, cela semble correct, tapdevrait forcer le clavier visible et ensuite typeTextfonctionner. Le journal des erreurs devrait vous donner plus d'informations.

JOM
la source
1
passwordSecureTextFieldexiste. J'ai résolu le problème, mais en nettoyant la mémoire et en réécrivant ces lignes à nouveau. Bizarre, mais ça a marché.
Bartłomiej Semańczyk
C'est bon d'entendre que c'est réparé! J'ai rencontré un problème similaire hier avec la bêta 6 :)
JOM
beta 6? :-) vraiment? Je n'en ai que 5 :)
Bartłomiej Semańczyk
Vous avez été occupé à coder :) La bêta 6 n'a pas enregistré tappour moi, il a fallu du temps pour comprendre. Bonne pratique de débogage!
JOM
Peut-être connaissez-vous la réponse à cela: stackoverflow.com/questions/32219015/... ?
Bartłomiej Semańczyk
0

Ne vous trompez pas, le problème est que vous avez enregistré votre temps de test.Votre application connectera le clavier matériel tandis que votre simulateur de temps de test automatique ne prend que le clavier logiciel. donc pour savoir comment résoudre ces problèmes. Utilisez simplement le clavier logiciel sur votre temps d'enregistrement. vous pouvez voir la magie.

codercat
la source
0

Le problème pour moi était le même que pour Ted. En fait, si le champ du mot de passe est tapé après le champ de connexion et que la base de connaissances matérielle est activée, le clavier logiciel se fermera lors du deuxième appui sur le champ, et ce n'est pas spécifique aux tests d'interface utilisateur.

Après un certain temps à déconner avec AppleScript, voici ce que j'ai proposé (les améliorations sont les bienvenues):

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

Créez un fichier de script avec le code ci-dessus et ajoutez-le aux cibles nécessaires (probablement la cible des tests d'interface utilisateur uniquement, vous pouvez ajouter un script similaire à vos cibles de développement pour réactiver le clavier matériel pendant le développement). Vous devez ajouter une Run Scriptphase dans les phases de construction et l'utiliser comme ceci: osascript Path/To/Script/script_name.applescript

Timur Kuchkarov
la source
0

Nous avons rencontré la même erreur lors de la définition de la accessibilityIdentifiervaleur d'une vue personnalisée ( UIStackViewsous-classe) contenant des sous- UIControlvues. Dans ce cas, XCTest n'a pas pu obtenir le focus clavier pour les éléments descendants.

Notre solution consistait simplement à supprimer le accessibilityIdentifierde notre vue parent et à définir le accessibilityIdentifierpour les sous-vues via des propriétés dédiées.

Shadowhorst
la source
0

Une autre réponse, mais pour nous, le problème était que la vue était trop proche d'une autre vue qu'un outil de reconnaissance de geste dessus. Nous avons constaté que la vue devait être distante d'au moins 20 pixels (dans notre cas ci-dessous). Littéralement, 15 n'ont pas fonctionné et 20 ou plus l'ont fait. C'est étrange, je l'admets, mais nous avions quelques UITextViews qui fonctionnaient et d'autres qui n'étaient pas et tous étaient sous le même parent et d'autres positionnements identiques (et des noms de variables bien sûr). Le clavier allumé ou éteint ou quoi que ce soit ne faisait aucune différence. L'accessibilité a montré les champs. Nous avons redémarré nos ordinateurs. Nous avons fait des constructions propres. Paiements à la source fraîche.

David J
la source
0

Ce qui a résolu ce problème pour moi a été d'ajouter une seconde de sommeil:

let textField = app.textFields["identifier"]
textField.tap()
sleep(1)
textField.typeText(text)
Przemysław Wrzesiński
la source
0

J'ai rencontré ce problème et j'ai pu le résoudre dans mon scénario en prenant la solution publiée par @AlexDenisov et en l'ajoutant à mes pré-actions pour l' exécution et le test .

entrez la description de l'image ici

CodeBender
la source
0

Pas besoin d'activer / désactiver le clavier dans les E / S. N'utilisez pas .typeText pour secureTextField, utilisez simplement

app.keys["p"].tap()
app.keys["a"].tap()
app.keys["s"].tap()
app.keys["s"].tap()

Bonus: vous obtenez le son du clavier :)

zdravko zdravkin
la source
0

Enfin, j'ai écrit un script qui édite le fichier .plist du simulateur et définit le ConnectHardwareKeyboard propriété sur false pour le simulateur sélectionné. Vous avez bien entendu, cela change la propriété du simulateur spécifiquement sélectionné dans le dictionnaire "DevicePreferences" plutôt que de modifier la propriété globale.

Commencez par créer un script shell nommé disable-hardware-keyboard.sh avec le contenu suivant. Vous pouvez le placer dans "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

Suivez maintenant ces étapes pour l'appeler en passant l'udid du simulateur sélectionné comme argument:

  1. Modifiez votre schéma Xcode (ou votre schéma spécifique de tests UI si vous en avez un)
  2. Allez dans: Test> Pré-actions
  3. Ajoutez un nouveau script en appuyant sur le symbole «+»> «Nouvelle action de script d'exécution».
  4. Important: dans le menu déroulant "Fournir les paramètres de build à partir de", choisissez votre cible d'application principale, pas la cible des tests d'interface utilisateur.
  5. Ajoutez maintenant le script suivant dans la zone de texte ci-dessous.

Script dans Test> Pré-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

Il est temps de le tester:

  1. Simulateur de lancement
  2. Activer le clavier matériel pour cela
  3. Exécutez n'importe quel test d'interface utilisateur avec une interaction au clavier. Observez que le simulateur redémarre et que le clavier matériel est désactivé. Et l'interaction clavier du test fonctionne correctement. :)
Hasaan Ali
la source
-1

Eu le même problème avec Securetextfields. L'option de connexion matérielle dans mon simulateur était de, mais rencontrait toujours le problème. Enfin, cela a fonctionné pour moi (Swift 3):

 let enterPasswordSecureTextField = app.secureTextFields["Enter Password"]
    enterPasswordSecureTextField.tap()
    enterPasswordSecureTextField.typeText("12345678")
Rads
la source
Quelle est la différence entre votre code et le code posé dans la question.
Shivam Pokhriyal