it-roy-ru.com

UI Test удаление текста в текстовом поле

В моем тесте у меня есть текстовое поле с уже существующим текстом. Я хочу удалить содержимое и ввести новую строку.

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

При удалении строки с аппаратной клавиатуры запись для меня ничего не генерирует. Сделав то же самое с программной клавиатурой, я получил:

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

или же

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

Я беспокоился о том, что мой тест зависит от языка, поэтому я создал что-то вроде этого для поддерживаемых языков:

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

Это выглядит лучше в коде:

app.keys.deleteKey.pressForDuration(1.5)

но это очень хрупкий После выхода из симулятора Toggle software keyboard был сброшен, и у меня не пройден тест. Мое решение плохо работает с тестированием CI. Как это можно решить, чтобы стать более универсальным?

50
Tomasz Bąk

Я написал метод расширения, чтобы сделать это для меня, и это довольно быстро:

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

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

Затем это используется довольно легко: app.textFields["Email"].clearAndEnterText("[email protected]")

121
Bay Phillips

Поскольку вы исправили проблему локализованного имени ключа удаления в комментариях к своим вопросам, я предполагаю, что вы можете получить доступ к ключу удаления, просто назвав его «Удалить».

Код ниже позволит вам надежно удалить содержимое вашего поля:

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

Но в то же время ваша проблема может указывать на необходимость более элегантного решения этой проблемы для повышения удобства использования вашего приложения. В текстовое поле вы также можете добавить Clear button, с помощью которого пользователь может сразу очистить текстовое поле;

Откройте раскадровку и выберите текстовое поле, в инспекторе атрибутов найдите «Кнопка очистки» и установите для него нужный параметр (например, всегда видимый).

 Clear button selection

Теперь пользователи могут очистить поле с помощью простого нажатия на крестик справа от текстового поля:

 Clear button

Или в вашем тесте пользовательского интерфейса:

textField.buttons["Clear text"].tap()
18
Martijn Hols

Я нашел следующее решение:

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

Когда вы нажимаете и удерживаете текстовое поле, открывается меню, в котором вы можете нажать кнопку «Выбрать все». После этого все, что вам нужно, это удалить этот текст с помощью кнопки «удалить» на клавиатуре или просто ввести новый текст. Он перезапишет старый.

10
oliverfrost

это будет работать для textfield и textview

для 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)
    }
}

для Swift 4 до Swift 99

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

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

ОБНОВЛЕНИЕ XCODE 9

Существует ошибка Apple где, если текстовое поле пусто, значение и placeholderValue равны

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
        }
        self.typeText(deleteString)
    }
}
7
Ted

Итак, я не нашел никакого хорошего решения пока: /

И мне не нравятся решения, зависящие от локали, как выше с явным поиском «Чистый текст».

Итак, я делаю проверку типа, затем пытаюсь найти кнопку очистки в текстовом поле Это работает хорошо, если у вас нет настраиваемого текстового поля с более чем одной кнопкой

Мой лучший сейчас (у меня нет пользовательских текстовых полей с большим количеством кнопок):

    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
    }
4
Oleg Shanyuk

Xcode 9, Swift 4

Пробовал решения выше, но ни одно не работало из-за некоторого странного поведения при нажатии - он перемещал курсор либо в начало текстового поля, либо в какую-то произвольную точку в тексте. Подход, который я использовал, описал @oliverfrost здесь , но я добавил несколько штрихов, чтобы обойти проблемы и объединить их в аккуратное расширение. Я надеюсь, что это может быть полезно для кого-то. 

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

Использование:

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

Для тех, кто все еще использует Objective-C

@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
1
Skander Fathallah

Теперь в Swift 4.2, возможно, вам следует попробовать следующий код:

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)
    }
}
0
Alfredo Luco G

У меня были некоторые трудности с тем, чтобы описанные выше решения работали для решения аналогичной проблемы, с которой я столкнулся: курсор помещался перед текстом, а затем работал в обратном направлении. Кроме того, я хотел проверить, что текстовое поле содержало текст перед удалением. Вот мое решение, вдохновленное расширением https://stackoverflow.com/users/482361/bay-phillips wrote. Я должен отметить, что нажатие клавиши удаления может занять много времени, и его можно заменить на .pressForDuration

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)
    }
0
Phillip English

Я использовал то, что описал @oliverfrost, но он не работал на IPhone XR, я немного изменил его для собственного использования, на этот

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

и, как сказал @zysoft, вы можете использовать его так:

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

Сделайте это, чтобы удалить текущее строковое значение в текстовом поле, не полагаясь на виртуальную клавиатуру.

// читаем значение вашего текстового поля в этой переменной let textInTextField: String = 

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

хорошая вещь об этом решении - то, что оно работает независимо от того, имеет симулятор виртуальную клавиатуру или нет.

0
Dilip Agheda

Я новичок в тестировании пользовательского интерфейса с iOS, но мне удалось очистить текстовые поля с помощью этого простого обходного пути. Работаем с Xcode8 и планируем рефакторинг в ближайшее время:

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...
}
0
Jonathan Sweeney