UI-Test Löschen von Text im Textfeld

73

In meinem Test habe ich ein Textfeld mit einem bereits vorhandenen Text. Ich möchte den Inhalt löschen und eine neue Zeichenfolge eingeben.

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

Beim Löschen von Zeichenfolgen mit Hardwaretastatur wird für mich keine Aufzeichnung generiert. Nachdem ich dasselbe mit der Software-Tastatur gemacht hatte, bekam ich:

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

oder

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

Ich habe mir Sorgen gemacht, dass mein Test sprachabhängig ist, deshalb habe ich für meine unterstützten Sprachen Folgendes erstellt:

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

Im Code sieht es besser aus:

app.keys.deleteKey.pressForDuration(1.5)

aber es ist sehr zerbrechlich. Nach dem Beenden von Simulator Toggle software keyboardwurde zurückgesetzt und ich habe einen fehlgeschlagenen Test. Meine Lösung funktioniert nicht gut mit CI-Tests. Wie kann dies gelöst werden, um universeller zu sein?

Tomasz Bąk
quelle
Ich kann den aufgetretenen Fehler nicht reproduzieren. Ich habe dieselbe Erweiterung hinzugefügt, die Sprache meines Simulators auf Polnisch umgestellt und überprüft, ob die Taste "Usún" gedrückt wird. Das Neustarten / Zurücksetzen des Simulators scheint keine Auswirkungen auf die Toggle software keyboardEinstellung zu haben. Gibt es noch etwas in der Textansicht, das die Tastatur verbergen / schließen könnte?
Joe Masilotti
Vielleicht habe ich nach dem Neustart des Systems Toggle software keyboardzurückgesetzt. Es ist nicht die Standardeinstellung für den Simulator (und ich weiß nicht, ob dies geändert werden kann). Auf die eine oder andere Weise ist meine Methode erst dann zuverlässig, wenn Sie die Sprach- und Softwaretastatureinstellungen auf der Ebene des Tests (oder des Testschemas) korrigieren können.
Tomasz Bąk
Mein eigener Kommentar brachte mich auf eine Idee und ich habe die Spracheinstellung unter Schema> Optionen> Anwendungssprache gefunden. Das Tastaturproblem bleibt weiterhin ungelöst.
Tomasz Bąk
Nett! Behebt das den Fehler?
Joe Masilotti
Es wurde nur ein Problem mit lokalisierten Löschschlüsselnamen behoben.
Tomasz Bąk

Antworten:

150

Ich habe eine Erweiterungsmethode geschrieben, um dies für mich zu tun, und sie ist ziemlich schnell:

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

Dies ist dann ziemlich einfach zu verwenden: app.textFields["Email"].clearAndEnterText("[email protected]")

Bay Phillips
quelle
20
Sie können "Zeichenfolge löschen" auf funktionalere Weise erstellen mit:let deleteString = stringValue.characters.map { _ in "\u{8}" }.joinWithSeparator("")
Tomasz Bąk
1
Oh toll! Ich wusste nicht, dass sie joinWithSeparatorin Swift 2 hinzugefügt hatten . Danke!
Bay Phillips
8
Verwenden Sie für swift4 XCUIKeyboardKey.delete.rawValue
Daniel
18
Für Swift 4 können Sie einfachlet deleteString = String(repeating: XCUIKeyboardKey.delete.rawValue, count: stringValue.characters.count)
Stasel
13
Für Swift 4.2:let deleteString = String(repeating: XCUIKeyboardKey.delete.rawValue, count: stringValue.count)
Rody Davis
20

Da Sie Ihr lokalisiertes Problem mit dem Namen des Löschschlüssels in den Kommentaren Ihrer Fragen behoben haben, gehe ich davon aus, dass Sie auf den Löschschlüssel zugreifen können, indem Sie ihn einfach "Löschen" nennen.

Mit dem folgenden Code können Sie den Inhalt Ihres Feldes zuverlässig löschen:

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

oder Swift 4+:

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

Gleichzeitig weist Ihr Problem möglicherweise darauf hin, dass dies eleganter gelöst werden muss, um die Benutzerfreundlichkeit Ihrer App zu verbessern. Im Textfeld können Sie auch ein hinzufügen, Clear buttonmit dem ein Benutzer das Textfeld sofort leeren kann.

Öffnen Sie das Storyboard und wählen Sie das Textfeld aus. Suchen Sie unter dem Attributinspektor die Schaltfläche "Löschen" und stellen Sie die gewünschte Option ein (z. B. ist immer sichtbar).

Tastenauswahl löschen

Jetzt können Benutzer das Feld durch einfaches Tippen auf das Kreuz rechts neben dem Textfeld löschen:

Schaltfläche Löschen

Oder in Ihrem UI-Test:

textField.buttons["Clear text"].tap()
Martijn Hols
quelle
Ich mag die Idee mit der Schaltfläche "Löschen", aber ich suche immer noch nach einer Möglichkeit, die Software-Tastatur in Tests zu erzwingen. Als ob es sich um eine while-Schleife handelt - jedes Tippen hat eine Verzögerung, sodass die Tests für lange Zeichenfolgen langsam werden.
Tomasz Bąk
1
+1 Ja! Verwenden Sie den Prozess des Schreibens der Tests, um die Software zu verbessern. Vielleicht hat es die Anforderungen des OP nicht ganz erfüllt, aber es ist genau der Zeiger, den ich brauchte, als ich auf ein ähnliches Problem stieß.
Andy Mortimer
3
Dies gibt mir "UI Testing Failure - Keine Übereinstimmungen für" Delete "Key" in Xcode 7.2 gefunden
Oded
1
Das Ändern des App-Verhaltens im Geschmack zum Testen ist meiner Meinung nach ein Nein-Nein. @ bay.philips Antwort scheint für diesen Zweck viel legitimer.
hris.to
2
Ich habe dies unter der Annahme geschrieben, dass Sie Tests für tatsächliche Anwendungsfälle schreiben, anstatt nur zufällige Dinge in Ihren Tests zu tun. Wenn Sie jedes Feedback verwenden, um die Benutzerfreundlichkeit zu verbessern, erhalten Sie bessere Apps für Benutzer.
Martijn Hols
15

Dies funktioniert für Textfeld und Textansicht

für 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)
    }
}

für 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

Es gibt einen Apple-Fehler, bei dem Wert und Platzhalterwert gleich sind, wenn das Textfeld leer ist

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)
    }
}
Ted
quelle
4
Wow, können Sie bis Swift 99 vorhersagen?
Daniel
@ Daniel haha ​​war nur genervt, dass wir es für schnelle 2, 3, 4 ändern mussten.
Ted
@ SujitBaranwal ok wird überprüfen, was geändert wurde
Ted
Dieser Code funktioniert auf meinem Simulator und meinem Gerät einwandfrei. Wenn ich ihn jedoch auf einem Browserstack teste, einer Cloud-basierten Plattform zum Testen auf realen Geräten aus der Ferne, wird die erste Guard-Anweisung nur von dort zurückgegeben. Das bedeutet, dass der Text in meinem Textfeld nicht als Zeichenfolge erkannt wird. Ich bin sicher, dass der Text vorhanden ist, da auch die Testfälle aufgezeichnet werden. Irgendeine Hilfe ?
Nr. 5
13

Ich habe folgende Lösung gefunden:

let myTextView = app.textViews["some_selector"]
myTextView.pressForDuration(1.2)
app.menuItems["Select All"].tap()
app.typeText("New text you want to enter") 
// or use app.keys["delete"].tap() if you have keyboard enabled

Wenn Sie auf das Textfeld tippen und es gedrückt halten, wird ein Menü geöffnet, in dem Sie auf die Schaltfläche "Alle auswählen" tippen können. Danach müssen Sie nur noch diesen Text mit der Schaltfläche "Löschen" auf der Tastatur entfernen oder einfach neuen Text eingeben. Es wird das alte überschreiben.

oliverfrost
quelle
2
Sie können auch verwenden myTextView.doubleTap(), um das Menü
Hudson
2
Dies funktioniert nicht immer, da manchmal langes Drücken ein Wort im Textfeld hervorheben kann und dies dazu führt, dass die Schaltfläche "Alle
auswählen
7

Xcode 9, Swift 4

Versuchte die obigen Lösungen, aber keine funktionierte aufgrund eines seltsamen Verhaltens beim Tippen - es bewegte den Cursor entweder zum Anfang des Textfelds oder an einem zufälligen Punkt im Text. Der Ansatz, den ich verwendet habe, ist der von @oliverfrost hier beschriebene , aber ich habe einige Details hinzugefügt, um die Probleme zu umgehen und sie in einer ordentlichen Erweiterung zu kombinieren. Ich hoffe, es kann für jemanden nützlich sein.

extension XCUIElement {
    func clearText(andReplaceWith newText:String? = nil) {
        tap()
        tap() //When there is some text, its parts can be selected on the first tap, the second tap clears the selection
        press(forDuration: 1.0)
        let selectAll = XCUIApplication().menuItems["Select All"]
        //For empty fields there will be no "Select All", so we need to check
        if selectAll.waitForExistence(timeout: 0.5), selectAll.exists {
            selectAll.tap()
            typeText(String(XCUIKeyboardKey.delete.rawValue))
        }
        if let newVal = newText { typeText(newVal) }
    }
}

Verwendung:

let app = XCUIApplication()
//Just clear text
app.textFields["field1"].clearText() 
//Replace text    
app.secureTextFields["field2"].clearText(andReplaceWith: "Some Other Text")
zysoft
quelle
4

Also habe ich noch keine gute Lösung gefunden: /

Und ich mag keine länderabhängigen Lösungen wie oben mit expliziter "Klartext" -Suche.

Also mache ich eine Typprüfung und versuche dann, eine klare Schaltfläche im Textfeld zu finden. Es funktioniert gut, es sei denn, Sie haben ein benutzerdefiniertes Textfeld mit mehr als einer Schaltfläche

Mein Bestes ist jetzt (ich habe keine benutzerdefinierten Textfelder mit mehr Schaltflächen):

    class func clearTextField(textField : XCUIElement!) -> Bool {

        guard textField.elementType != .TextField else {
            return false
        }

        let TextFieldClearButton = textField.buttons.elementBoundByIndex(0)

        guard TextFieldClearButton.exists else {
            return false
        }

        TextFieldClearButton.tap()

        return true
    }
Oleg Shanyuk
quelle
3

Ich habe eine Lösung gefunden, die eine iOS-Funktion verwendet, mit der die gesamten Textfelder durch mehrmaliges Tippen ausgewählt werden. Anschließend löschen wir das Textfeld durch Eingabe eines beliebigen Zeichens oder durch Drücken von Löschen (wenn die Tastatur aktiviert ist).

let myTextView = app.textViews["some_selector"]
myTextView.tap(withNumberOfTaps: 2, numberOfTouches: 1)
app.typeText("New text you want to enter") 
// or use app.keys["delete"].tap() if you have keyboard enabled
Tadej Magajna
quelle
Es sieht so aus, als wäre ein Vierfach-Tipp die magische Zahl pro Apfel
Mark Gerrior
Manchmal muss ich jedoch vorher .tap () ausführen, um den Fokus festzulegen. Hier ist mein Code-Snippet.
Mark Gerrior
let toSelectWord = 2 // Select a word: Double-tap the word with one finger. let toSelectSentence = 3 // Select a sentence: Triple-tap the sentence with one finger. let toSelectParagraph = 4 // Select a paragraph: Quadruple-tap with one finger. app.textFields["sd.Title"].tap() app.textFields["sd.Title"].tap(withNumberOfTaps: toSelectParagraph, numberOfTouches: 1) app.textFields["sd.Title"].typeText("Old Task")
Mark Gerrior
1
Nur withNumberOfTaps: 2ist ausreichend.
Boris Y.
2

Führen Sie dies aus, um den aktuellen Zeichenfolgenwert in einem Textfeld zu löschen, ohne auf die virtuelle Tastatur angewiesen zu sein.

// Lies den Wert deines Textfeldes in dieser Variablen let textInTextField: String =

  let characterCount: Int = textInTextField.count
  for _ in 0..<characterCount {
    textFields[0].typeText(XCUIKeyboardKey.delete.rawValue)
  }

Das Gute an dieser Lösung ist, dass sie unabhängig davon funktioniert, ob der Simulator über eine virtuelle Tastatur verfügt oder nicht.

Dilip Agheda
quelle
1

swift 4.2Vielleicht sollten Sie jetzt den folgenden Code ausprobieren:

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()
        for _ in 0..<stringValue.count {
            self.typeText(XCUIKeyboardKey.delete.rawValue)
        }

        self.typeText(text)
    }
}
Alfredo Luco G.
quelle
@eonist - Deaktiviere die Hardware-Tastatur und ich denke, es wird funktionieren. Siehe: stackoverflow.com/a/34095158/238166
Inti
1

Für diejenigen, die noch Objective-C verwenden

@implementation XCUIElement (Extensions)

-(void)clearText{
    if (!self){
        return;
    }
    if (![self.value isKindOfClass:[NSString class]]){
        return;
    }
    NSString* stringValue = (NSString*)self.value;
    for (int i=0; i<stringValue.length ; i++) {
        [self typeText:XCUIKeyboardKeyDelete];
    }
}

@end
Skander Fathallah
quelle
0

Ich hatte einige Schwierigkeiten, die oben genannten Lösungen für ein ähnliches Problem zum Laufen zu bringen: Der Cursor stellte sich vor den Text und arbeitete dann von dort aus rückwärts. Außerdem wollte ich vor dem Löschen überprüfen, ob das Textfeld Text enthält. Hier ist meine Lösung, inspiriert von der Erweiterung, die https://stackoverflow.com/users/482361/bay-phillips geschrieben hat. Ich sollte beachten, dass das Tippen auf die Löschtaste lange dauern kann und durch .pressForDuration ersetzt werden kann

func clearAndEnterText(element: XCUIElement, text: String) -> Void
    {
        guard let stringValue = element.value as? String else {
            XCTFail("Tried to clear and enter text into a non string value")
            return
        }

        element.tap()

        guard stringValue.characters.count > 0 else
        {
            app.typeText(text)
            return
        }

       for _ in stringValue.characters
        {
            app.keys["delete"].tap()
        }
        app.typeText(text)
    }
Phillip Englisch
quelle
0

Ich bin neu im Testen von Benutzeroberflächen mit iOS, konnte jedoch Textfelder mit dieser einfachen Problemumgehung löschen. Arbeiten Sie mit Xcode8 und planen Sie, dies bald zu überarbeiten:

func testLoginWithCorrectUsernamePassword() {
      //Usually this will be completed by Xcode
    let app = XCUIApplication()
      //Set the text field as a constant
    let usernameTextField = app.textFields["User name"]
      //Set the delete key to a constant
    let deleteKey = app.keys["delete"]
      //Tap the username text field to toggle the keyboard
    usernameTextField.tap()
      //Set the time to clear the field.  generally 4 seconds works
    deleteKey.press(forDuration: 4.0);
      //Enter your code below...
}
Jonathan Sweeney
quelle
0

Ich habe das verwendet, was @oliverfrost beschrieben hat, aber es funktionierte nicht auf IPhone XR. Ich habe es ein wenig für meinen eigenen Gebrauch geändert

extension XCUIElement {
func clearText(andReplaceWith newText:String? = nil) {
    tap()
    tap() //When there is some text, its parts can be selected on the first tap, the second tap clears the selection
    press(forDuration: 1.0)
    let select = XCUIApplication().menuItems["Select"]
    //For empty fields there will be no "Select All", so we need to check
    if select.waitForExistence(timeout: 0.5), select.exists {
        select.tap()
        typeText(String(XCUIKeyboardKey.delete.rawValue))
    }
    if let newVal = newText { typeText(newVal) }
}
}

und wie @zysoft sagte, können Sie es wie folgt verwenden:

let app = XCUIApplication()
//Just clear text
app.textFields["field1"].clearText() 
//Replace text    
app.secureTextFields["field2"].clearText(andReplaceWith: "Some Other Text")
Hamid Shahsavari
quelle
0

Swift 5

basierend auf @ Bay Phillips Antwort,

extension XCUIElement {

    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 = stringValue.map { _ in "\u{8}" }.joined(separator: "")

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

}
Hatim
quelle