Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

UI Test deleting text in text field

In my test I have a text field with a pre-existing text. I want to delete the content and type a new string.

let textField = app.textFields
textField.tap()
// delete "Old value"
textField.typeText("New value")

When deleting string with hardware keyboard Recording generated for me nothing. After doing the same with software keyboard I got:

let key = app.keys["Usuń"] // Polish name for the key
key.tap()
key.tap() 
... // x times

or

app.keys["Usuń"].pressForDuration(1.5)

I was worried that my test is language-dependent so I have created something like this for my supported languages:

extension XCUIElementQuery {
    var deleteKey: XCUIElement {
        get {
            // Polish name for the key
            if self["Usuń"].exists {
                return self["Usuń"]
            } else {
                return self["Delete"]
            }
        }
    }
}

It looks nicer in code:

app.keys.deleteKey.pressForDuration(1.5)

but it is very fragile. After quitting from Simulator Toggle software keyboard was reset and I've got a failing test. My solution doesn't work well with CI testing. How can this be solved to be more universal?

like image 254
Tomasz Bąk Avatar asked Sep 28 '15 11:09

Tomasz Bąk


3 Answers

I wrote an extension method to do this for me and it's pretty fast:

extension XCUIElement {
    /**
     Removes any current text in the field before typing in the new value
     - Parameter text: the text to enter into the field
     */
    func clearAndEnterText(text: String) {
        guard let stringValue = self.value as? String else {
            XCTFail("Tried to clear and enter text into a non string value")
            return
        }

        self.tap()

        let deleteString = String(repeating: XCUIKeyboardKey.delete.rawValue, count: stringValue.count)

        self.typeText(deleteString)
        self.typeText(text)
    }
}

This is then used pretty easily: app.textFields["Email"].clearAndEnterText("[email protected]")

like image 172
Bay Phillips Avatar answered Oct 17 '22 23:10

Bay Phillips


Since you fixed your localized delete key name problem in the comments of your questions, I'll assume you can access the delete key by just calling it "Delete".

The code below will allow you to reliably delete the contents of your field:

while (textField.value as! String).characters.count > 0 {
    app.keys["Delete"].tap()
}

or Swift 4+:

while !(textView.value as! String).isEmpty {
    app.keys["Delete"].tap()
}

But at the same time, your issue might indicate the need to solve this more elegantly to improve the usability of your app. On the text field you can also add a Clear button with which a user can immediately empty the text field;

Open the storyboard and select the text field, under the attributes inspector find "Clear button" and set it to the desired option (e.g. is always visible).

Clear button selection

Now users can clear the field with a simple tap on the cross at the right of the text field:

Clear button

Or in your UI test:

textField.buttons["Clear text"].tap()
like image 41
Martijn Hols Avatar answered Oct 17 '22 23:10

Martijn Hols


this will work for textfield and textview

for SWIFT 3

extension XCUIElement {
    func clearText() {
        guard let stringValue = self.value as? String else {
            return
        }

        var deleteString = String()
        for _ in stringValue {
            deleteString += XCUIKeyboardKeyDelete
        }
        self.typeText(deleteString)
    }
}

for SWIFT 4, SWIFT 5

extension XCUIElement {
    func clearText() {
        guard let stringValue = self.value as? String else {
            return
        }

        var deleteString = String()
        for _ in stringValue {
            deleteString += XCUIKeyboardKey.delete.rawValue
        }
        typeText(deleteString)
    }
}

UPDATE XCODE 9

There is an apple bug where if the textfield is empty, value and placeholderValue are equal

extension XCUIElement {
    func clearText() {
        guard let stringValue = self.value as? String else {
            return
        }
        // workaround for apple bug
        if let placeholderString = self.placeholderValue, placeholderString == stringValue {
            return
        }

        var deleteString = String()
        for _ in stringValue {
            deleteString += XCUIKeyboardKey.delete.rawValue
        }
        typeText(deleteString)
    }
}
like image 22
Ted Avatar answered Oct 18 '22 00:10

Ted