UI Testing Failure - Neither element nor any descendant has keyboard focus on secureTextField
Asked Answered
I

29

170

This is my case:

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

UI Testing Failure - Neither element nor any descendant has keyboard focus. Element:

What is wrong? This is working nice for normal textFields, but problem arise only with secureTextFields. Any workarounds?

Interspace answered 24/8, 2015 at 14:29 Comment(5)
I get the same error for a UIView that conforms to the UIKeyInput protocol.Brandy
and the weird part is if i try like this it works XCUIApplication().webViews.secureTextFields["Password"].tap() XCUIApplication().webViews.secureTextFields["Password"].typeText("Welcome")Meliamelic
It might be the case that you have set the accessibility identifier but you haven't set isAccessibilityElement = trueWimple
I've added an answer to a similar question here: https://mcmap.net/q/145044/-xcode-ui-tests-can-39-t-find-views-that-are-added-programmatically. My issue was that I had other UIWindows in existence with bad .windowLevel valuesHundley
An answer to a similar question: https://mcmap.net/q/143143/-ui-testing-failure-neither-element-nor-any-descendant-has-keyboard-focus-on-securetextfieldOsmunda
P
346

This issue caused me a world of pain, but I've managed to figure out a proper solution. In the Simulator, make sure I/O -> Keyboard -> Connect hardware keyboard is off.

Parmenter answered 4/12, 2015 at 18:34 Comment(13)
I believe this is the real answer. UI recording is very fussy, so there might be many reasons for a problem (typical Apple...), but I got the same error and this fixed it. What it means is that you have to poke the keys on the simulator, can't use the ones on your mac or recording will fail.Destructible
This has fixed the issue. But it is very poor workaround as we cannot apply it automatically for CI.Acclaim
This only fixes it if you only have one test. Soon as I wrote a second one, saw the same error.Unanswerable
For me, disabling only one option didn't help; what worked was this answer: https://mcmap.net/q/143143/-ui-testing-failure-neither-element-nor-any-descendant-has-keyboard-focus-on-securetextfieldDeakin
If you run your tests on CI (Jenkins etc.) You can set the following parameters in a script before running your tests. "defaults write com.apple.iphonesimulator ConnectHardwareKeyboard 0"Mirage
This doesn't solve the problem for me. I can clearly see the textfield obtaining focus, with the keyboard up, but the testing framework can never enter text using the typeText: methodCollete
Ran into this after upgrading to Xcode 11. This worked for me. Have not upgraded CI to Xcode 11 so don't know if the defaults disabling bit will work.Rathenau
Worked for me on Xcode 11, but unchecking Connect hardware keyboard may not be enough. One has additionally to ensure that the software keyboard is really shown (I had actually the situation where no hardware keyboard was connected, and no software keyboard was shown, even if the textField had focus, and the cursor was blinking).Addam
I ran into this on Xcode 11 as well. Fixes locally, but how can this be set in CI?Downtime
@seeya where would you say to place the script? I tried placing it in the scheme of my test case for pre-actions in test and build options but no luck.Maurizio
I have added a full script below which disables hardware keyboard for any selected simulator https://mcmap.net/q/143143/-ui-testing-failure-neither-element-nor-any-descendant-has-keyboard-focus-on-securetextfield.Weig
I managed to fix this by performing defaults write com.apple.iphonesimulator ConnectHardwareKeyboard -bool false before running tests on a CI, but this doesn't work for "parallel testing", it seems that the "clones" Simulators doesn't inherit the ConnectHardwareKeyboard setting. Did anyone manage to make it work with parallel testing?Pace
For some reason I need to toggle the I/O > Keyboard > Connect Hardware Keyboard option from off to on and then off again to make it work every time after restarting XCode and the simulator.Seddon
R
37

Recently we found a hack to make solution from accepted answer persistent. To disable Simulator setting: I/O -> Keyboard -> Connect hardware keyboard from command line one should write:

defaults write com.apple.iphonesimulator ConnectHardwareKeyboard 0

It will not affect a simulator which is running - you need to restart simulator or start a new one to make that setting have its effect.

Retortion answered 15/1, 2016 at 14:14 Comment(4)
@netshark1000 that may be changed with the newer version of Xcode, I will check it.Retortion
extremely helpful, I needed the opposite where keyboard is always enabled, but yeah this is great thanks. I ended up going with defaults write com.apple.iphonesimulator ConnectHardwareKeyboard -bool true , but its the same concept.Gilges
I created a script on build phases to do it. But I needed kill all simulators, so I did that: killall "Simulator"; defaults write com.apple.iphonesimulator ConnectHardwareKeyboard 0;Andizhan
@WagnerSales doesn't seem to work anymore, do you know has the key changed?Reddick
I
20

I have written a small extension (Swift) which works perfect for me. Here is the 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))
        }
    }
}

The main idea is to keep tapping an element (text field) before the keyboard is presented.

Insincere answered 15/1, 2016 at 10:29 Comment(4)
This is not working for me right now. I am having Xcode 7.2 (7C68), what version do you have? This solution that we found for CI works for us: https://mcmap.net/q/143143/-ui-testing-failure-neither-element-nor-any-descendant-has-keyboard-focus-on-securetextfield.Acclaim
To confirm it one more time: this helper works for you for secure password text field not just regular text field?Acclaim
Yes, it works for both types of text field: regular and secure.Insincere
Problem with this answer is that it uses a hardcoded delay. Instead, use the wait for expectations model with the keyboard element.Borecole
S
15

Stanislav has the right idea.

In a team environment, you need something that will automatically work. I've come up with a fix here on my blog.

Basically you just paste:

UIPasteboard.generalPasteboard().string = "Their password"
let passwordSecureTextField = app.secureTextFields["password"]
passwordSecureTextField.pressForDuration(1.1)
app.menuItems["Paste"].tap()
Supernaturalism answered 13/1, 2016 at 16:40 Comment(4)
Thanks for sharing your solution. Unfortunately it is not working for me right now. I am having Xcode 7.2 (7C68), what you? See other solution that we found for CI: #32185337.Acclaim
Excellent. Tried 'app.secureTextFields["PasswordTextField"].press(forDuration: 1.1)' instead of 'app.secureTextFields["PasswordTextField"].doubleTap()' This problem occurs when editing multiple tetxfieldsForerun
For Xcode 15 on CI machine (Xcode 14 was fine) I have to use this hack again, it seems like the simulator typing the password too "fast" and some characters are skipped due to the CI machine is slow(?).Keikokeil
This is a great hack, as it's hard to tell the DevOps to turn off I/O -> Keyboard -> Connect hardware keyboard. Saved my day.Amarelle
W
11

Another cause of this error is if there is a parent view of the text field in which you are trying to enter text that is set as an accessibility element (view.isAccessibilityElement = true). In this case, XCTest is not able to get a handle on the subview to enter the text and returns the error.

UI Testing Failure - Neither element nor any descendant has keyboard focus.

It isn't that no element has focus (as you can often see the keyboard up and blinking cursor in the UITextField), it is just that no element it can reach has focus. I ran into this when attempting to enter text in a UISearchBar. The search bar itself is not the text field, when setting it as an accessibility element, access to the underlying UITextField was blocked. To resolve this, searchBar.accessibilityIdentifier = "My Identifier" was set on the UISearchBar however the isAccessibilityElement was not set to true. After this, test code of the form:

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

Works

Whitewood answered 29/11, 2017 at 20:37 Comment(0)
V
7

It occurred with me for many times. You have to disable Keyboard Hardware and Same Layout as OSX in your Simulator

Hardware/Keyboard (disable all)

After that keyboard software won't dismiss and your tests can type text

Disable Hardware

Vicarial answered 5/5, 2016 at 13:36 Comment(0)
P
7

In my case this Hardware -> Keyboard -> Connect Hardware Keyboard -> Disable didn't worked for me.

But when I followed

1) Hardware -> Keyboard -> Connect Hardware Keyboard -> Enabled and run the app
2) Hardware -> Keyboard -> Connect Hardware Keyboard -> Disable.

It worked for me

Parsons answered 2/4, 2020 at 13:5 Comment(0)
C
5
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()
}
Chumley answered 5/11, 2015 at 5:8 Comment(0)
E
5

Use a sleep between launching the app and typing in data in textfields like this:

sleep(2)

In my case I was keeping getting this error every time and only this solution helped my out.

Expendable answered 21/8, 2016 at 2:54 Comment(1)
The problem is if you have a large number of UI Tests that perform something like an initial login, this will add a huge overhead to your CI test run.Melmon
N
4

This maybe help: I just add a "tap" action before the error; that´s all :)

[app.textFields[@"theTitle"] tap];
[app.textFields[@"theTitle"] typeText:@"kk"];
Nalepka answered 6/10, 2015 at 19:39 Comment(1)
This definitely does not work for secure password text field.Acclaim
B
4

Record case however you want, keyboard or without keyboard attached. But do the following before playing test.

This following option (connect hardware keyboard) should be unchecked while playing test.

enter image description here

Borreri answered 29/3, 2016 at 9:37 Comment(0)
O
4

Works for me:

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)
    }
}
Otiose answered 8/12, 2020 at 23:31 Comment(1)
Would break tests that require not putting the app in the background. But otherwise, works like a charm.Detailed
W
3

[Reposting Bartłomiej Semańczyk's comment as an answer because it solved the problem for me]

I needed to do Simulator > Reset Contents and Settings in the simulator menu bar to make this start working for me.

Workingwoman answered 28/11, 2015 at 23:19 Comment(1)
Resetting the simulator helped me, too. Interestingly the problem did only occur in the simulator while the same test (tap texfield followed by typing characters) ran fine on a device.Warmhearted
G
2

I ran into this issue and was able to fix it in my scenario by taking the solution posted by @AlexDenisov and adding it to my pre-actions for run & test.

Pre-Action Run Script

Gentleness answered 9/6, 2020 at 19:24 Comment(0)
H
2

The above answers solved it for me when running UI tests while the simulator UI is in the foreground (when you could see the changes in the simulator window). However, it did not work when running them in the background (which is how fastlane ran them, for example).

I had to go to Simulator > Device > Erase All Content and Settings...

To be clear, I manually disabled-enabled-disabled the Hardware Keyboard in the Simulator Settings, set defaults write com.apple.iphonesimulator ConnectHardwareKeyboard 0, and then erased content and settings.

Hallel answered 22/6, 2021 at 20:34 Comment(0)
V
1

Sometime text fields are not implemented as text fields, or they're wrapped into another UI element and not easily accessible. Here's a work around:

//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()       
Vibrio answered 22/6, 2018 at 18:48 Comment(0)
A
1

The following is from a Swift 5.4, SwiftUI app scenario which would fail UI Testing.

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:

Test: Failed to synthesize event: Neither element nor any descendant has keyboard focus.

Solution: Tap Again

A second tap() was found to pass.

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:

However, .doubleTap() and tap(withNumberOfTaps: 2, numberOfTouches: 1) would still fail.

Solution: Await TextField Arrival, Then Tap

Sometimes a view transition needs to expressly await the TextEdit arrival. The approach below awaits the arrival of the targeted TextEdit field.

XCTestCase.swift extension:

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

Example use:

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:

Solution: waitForExistence, Then Tap

Use the waitForExistence(timeout:) method provided by XCUIElement.

app.cells.firstMatch.tap()
// transition from list view to detail view for item
app.textFields.matching(identifier: "balance_textedit")
    .element.waitForExistence(timeout: 5)
app.textFields
    .matching(identifier: "balance_textedit")
    .element.tap()
app.textFields
    .matching(identifier: "balance_textedit")
    .element.typeText("7620") // :PASS:
Attributive answered 5/9, 2021 at 18:11 Comment(0)
I
0

Your first line is just a query definition, which doesn't mean that passwordSecureTextField would actually exist.

Your second line will dynamically execute the query and try to (re)bind the query to UI element. You should put a breakpoint on it and verify that one and only one element is found. Or just use an assert:

XCTAssertFalse(passwordSecureTextField.exists);

Otherwise it looks ok, tap should force keyboard visible and then typeText should just work. Error log should tell you more info.

Indiaindiaman answered 26/8, 2015 at 6:20 Comment(2)
passwordSecureTextField exists. I fixed the problem, but cleaning memory, and rewrite those lines again. Weird, but it worked.Allegraallegretto
Good to hear it's fixed! I run into similar problem yesterday with beta 6 :)Indiaindiaman
P
0

Don't messed up, There problem caught the reason is you are recorded your testing time your app will connection hardware keyboard while your automatic testing time simulator takes only software keyboard. so for how to fix this issues. Just use software keyboard on your recording time. you can see the magic.

Peltz answered 4/2, 2016 at 6:34 Comment(0)
C
0

The problem for me was the same as for Ted. Actually if password field gets tapped after login field and hardware KB is on, software keyboard will dismiss itself on second field tap, and it's not specific to UI tests.

After some time messing around with AppleScript, here's what I came up with(improvements are welcome):

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

Create a script file with code above and add it to necessary targets(probably UI tests target only, you may want to add similar script to your development targets to re-enable HW keyboard during development). You should add Run Script phase in build phases and use it like this: osascript Path/To/Script/script_name.applescript

Cothurnus answered 14/4, 2016 at 3:37 Comment(0)
L
0

We encountered the same error when setting the accessibilityIdentifier value for a custom view (UIStackView subclass) containing UIControl subviews. In that case XCTest was unable to get the keyboard focus for the descendent elements.

Our solution was simply to remove the accessibilityIdentifier from our parent view and set the accessibilityIdentifier for the subviews through dedicated properties.

Liquefacient answered 25/7, 2017 at 14:29 Comment(0)
W
0

Another answer, but for us the problem was the view was too close to another view that a Gesture recognizer on it. We found we needed the view to be at least 20 pixels away (in our case below). Literally 15 didn't work and 20 or more did. This is strange I'll admit, but we had some UITextViews that were working and some that were not and all were under the same parent and identical other positioning (and variable names of course). The keyboard on or off or whatever made no difference. Accessibility showed the fields. We restarted our computers. We did clean builds. Fresh source checkouts.

Walkthrough answered 10/11, 2018 at 13:16 Comment(0)
P
0

No Need to turn on/off keyboard in I/O. Don't use .typeText for secureTextField, just use

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

Bonus: You get keyboard sound click :)

Paving answered 20/5, 2020 at 9:15 Comment(0)
W
0

Finally, I wrote a script which edits the Simulator's .plist file and sets the ConnectHardwareKeyboard property to false for the selected simulator. You heard it right, it changes the property for the specifically selected simulator inside "DevicePreferences" dictionary rather than editing the global property.

First, create a shell script named disable-hardware-keyboard.sh with the following contents. You can place it within "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

Now follow these steps to call it with passing the selected simulator's udid as an argument:

  1. Edit your Xcode scheme (or UI tests specific scheme if you have one)
  2. Go to: Test > Pre-actions
  3. Add new script by tapping "+" symbol > "New Run Script Action".
  4. Important: Inside "Provide build settings from" dropdown choose your main app target, not the UI tests target.
  5. Now add the following script in the text area below.

Script inside 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

Time to test it:

  1. Launch simulator
  2. Enable hardware keyboard for it
  3. Run any UI test with keyboard interaction. Observe the simulator restarts and the hardware keyboard is disabled. And the test's keyboard interaction is working fine. :)
Weig answered 16/7, 2020 at 7:49 Comment(0)
O
0

Long story short: hardware keyboard input in iOS Simulator seems to broken for a long time.

The thing is that -[UIApplicationSupport _accessibilityHardwareKeyboardActive] private method from com.apple.UIKit.axbundle sometimes returns NO even though it should return YES (when a text input view is firstResponder, and the cursor is blinking, which literally means that the hardware keyboard is active).

Put this code anywhere in the app to make iOS Simulator believe that if Software Keyboard is inactive then the hardware one is active.

    
#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
Osmunda answered 17/12, 2020 at 5:37 Comment(0)
C
0
    emailTextField.tap() // add this line before typeText
    
    emailTextField.typeText("your text")
Centro answered 2/5, 2022 at 8:58 Comment(1)
You need to add some explanation to your answerSihun
I
0

I ran into the same error, and this solution fixed it. I only needed to call waitUntilElementHasFocus(). On my machine, it took about 2 mins for the textField to actually be in focus.

Interknit answered 30/1 at 23:0 Comment(0)
V
-1

Had the same issue with Securetextfields. The connect hardware option in my Simulator was of, but still ran into the issue. Finally, this worked for me (Swift 3):

 let enterPasswordSecureTextField = app.secureTextFields["Enter Password"]
    enterPasswordSecureTextField.tap()
    enterPasswordSecureTextField.typeText("12345678")
Veach answered 25/9, 2017 at 16:33 Comment(1)
What is the difference between your code and the code asked in the question.Quadrennium
N
-1

What fixed this problem for me was adding a 1 second sleep:

let textField = app.textFields["identifier"]
textField.tap()
sleep(1)
textField.typeText(text)
Neva answered 28/3, 2019 at 15:42 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.