I have password and password conf textFields that I have applied the "Secure Text Entry", to mask the password. However, when I run the UI tests, those fields can not be found getting
UI Testing Failure - No matches found for "password" TextField
I'm attempting to select the fields like so:
let passwordTextField = app.textFields["password"]
but then it fails when I try to tap:
passwordTextField.tap()
Any ideas on how I can access the field?
As discussed in the comments, when accessing a secure text field use the secureTextFields selector.
let passwordTextField = app.secureTextFields["password"]
passwordTextField.tap()
passwordTextField.typeText("my secure password")
Also if you need to type to secureTextField
If menu I/O Hardware Keyboard is ON typeText not working for secureTextField.
Use below to type to secure field
password.tap()
app.keys["t"].tap()
app.keys["e"].tap()
app.keys["s"].tap()
app.keys["t"].tap()
Joe's answer is good, but it was insufficient for my use case. I had to find all text fields by index, no matter if it's a normal text field or a secure text field. I couldn't find any information on the web about this.
That's what I came up with:
// elementType must be specified as integer
//
// See:
// * https://developer.apple.com/documentation/xctest/xcuielementtype/xcuielementtypetextfield
// * https://developer.apple.com/documentation/xctest/xcuielementtype/xcuielementtypesecuretextfield
let textFieldPredicate = NSPredicate(format: "elementType == 49")
let secureTextFieldPredicate = NSPredicate(format: "elementType == 50")
let predicate = NSCompoundPredicate(
orPredicateWithSubpredicates: [
textFieldPredicate,
secureTextFieldPredicate
]
)
let textFields = app.descendants(matching: .any).matching(predicate)
let textField = textFields.firstMatch
textField.tap()
textField.typeText('yeah boi')
Related
I need to set UITextField for nickname. It need to be english only, lowercase, underscore and numbers. Really don't know how to do that and can't find any information. Thanks for your time!
you can create a regex for this & comparison textfield value with your regex pattern.
func isValidName(name:String)-> Bool {
let nameRegEx = "^[a-z0-9_]+$" // this mean you can only use lower case a-z, 0-9 and underscore
let namelTest = NSPredicate(format:"SELF MATCHES %#", nameRegEx)
return namelTest.evaluate(with: name)
}
use this method and vaidate textfield value by pasting as parameter function.
if return true it means username passed validation.
if return false you can show an error to user or shake textfiled and set red color for textfield.text or placeholder color or any thing you want.
hope to this help you.
I have an empty text field on my UI, though it has a placeholder text (whose value is foo) set in the storyboard. In my UI test, I am trying to check that its text value starts out empty, but when I query it's value, it seems to be giving me the placeholder value instead:
func testTextFieldInitiallyEmpty {
let input = XCUIApplication().textFields["My Text Field"]
XCTAssertEqual(input.value as! String, "")
}
as the test fails with this message:
XCTAssertEqual failed: ("foo") is not equal to ("")
Of course, foo is the placeholder value, but it's not the text value of that text field. I would have expected that error message if I had written:
XCTAssertEqual(input.placeholderValue as! String, "")
input is a XCUIElement, which implements XCUIElementAttributes, so I don't see anything else that would do the trick here.
How do I check (assert) that the text field is empty?
Edit
After doing some further research and trying out the suggestions below for using the input's properties of accessibilityValue, label, and title, I have not found any solution that will give me the text field's text value when there is text, and an empty string when only the placeholder is visible.
This seems like either (a) a bug, or (b) a questionable design decision from the test framework to not provide that ability. At a minimum, the documentation for XCUIElementAttributes#value seems inadequate to me, as the only detail is:
The exact type of value varies based on the type of the element.
Still looking for a better solution...
You can compare to the XCUIElementAttributes's placeholderValue variable in addition to checking for a blank string
extension XCUIElement {
func noTextEntered() -> Bool {
return self.value as? String != "" && self.value as? String != placeholderValue
}
}
Then you can run XCAssert(input.noTextEntered(), "Unexpected text entered into field")
Just make sure your placeholder is not something a user would type in. This way you don't have to hardcode placeholder values to check against
Kind of ridiculous that this is actually the case it works and that it needs a workaround.
Anyway, my solution to get the value w/o the placeholder interfering, based on #Jason's answer.
extension XCUIElement {
var valueWithoutPlaceholder: String {
if let v = value as? String, v != placeholderValue {
return v
}
return ""
}
}
Be aware, if the input is actually the placeholder this would break!
Try using accessibilityValue property of input.
func testTextFieldInitiallyEmpty {
let input = XCUIApplication().textFields["My Text Field"]
XCTAssertEqual(input.accessibilityValue, "")
}
If you command+click the property, you can see the following..
/*
Returns a localized string that represents the value of the element, such as the value
of a slider or the text in a text field. Use only when the label of the element
differs from a value. For example: A volume slider has a label of "Volume", but a value of "60%".
default == nil
default on UIKit controls == values for appropriate controls
Setting the property will change the value that is returned to the accessibility client.
*/
public var accessibilityValue: String?
I'm trying to verify that every character in a text box is limited to the language's keyboard options. This means that in English, you would only be able to type characters which are accessible through the iOS keyboard. If there is a string containing all characters, or a code solution - either will work.
Thanks for the help.
Try something like this. Let me know if it helped :) Good luck!
let letters = CharacterSet.letters
let text = textField.text
let range = text.rangeOfCharacters(from: letters)
if range != nil {
// Yay it's correct
}else{
// Oh no
}
The CharacterSet documentation has multiple options for the characters you want to restrict the field to. You can even make your own character sets! Check out the link: https://developer.apple.com/documentation/foundation/characterset
You could check it like that (is Swift):
let charactersSet = CharacterSet(charactersIn: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")
if yourString.rangeOfCharacter(from: charactersSet.inverted) != nil {
print("string contains special characters")
}
You can try some thing like:
NSString *inputString = #"abc0129yourcontent";
BOOL success = [inputString isMatchedByRegex:#"^[0-9a-zA-Z]+$"];
You can convert in Swift if required.
I am developing a IOS custom keyboard. I was wondering if there was a way to fetch the current text inside of the text field and how it would work.
For example, we can use textDocumentProxy.hasText() to see if the textfield has text inside but I want to know the exact string that is inside the textfield.
The closest things would be textDocumentProxy.documentContextBeforeInput and textDocumentProxy.documentContextAfterInput. These will respect sentences and such, which means if the value is a paragraph, you will only get the current sentence. Users have been known to retrieve the entire string by repositioning the cursor multiple times until everything is retrieved.
Of course, you generally do not have to worry about this if the field expects a single value like a username, email, id number, etc. Combining the values of both before and after input contexts should suffice.
Sample Code
For the single phrase value, you would do:
let value = (textDocumentProxy.documentContextBeforeInput ?? "") + (textDocumentProxy.documentContextAfterInput ?? "")
For values that might contain sentence ending punctuation, it will be a little more complicated as you need to run it on a separate thread. Because of this, and the fact that you have to move the input cursor to get the full text, the cursor will visibly move. It is also unknown whether this will be accepted into the AppStore (after all, Apple probably did not add an easy way to get the full text on purpose in order to prevent official custom keyboards from invading a user's privacy).
Note: the below code is based off of this Stack Overflow answer except modified for Swift, removed unnecessary sleeps, uses strings with no custom categories, and uses a more efficient movement process.
func foo() {
dispatch_async(dispatch_queue_create("com.example.test", DISPATCH_QUEUE_SERIAL)) { () -> Void in
let string = self.fullDocumentContext()
}
}
func fullDocumentContext() {
let textDocumentProxy = self.textDocumentProxy
var before = textDocumentProxy.documentContextBeforeInput
var completePriorString = "";
// Grab everything before the cursor
while (before != nil && !before!.isEmpty) {
completePriorString = before! + completePriorString
let length = before!.lengthOfBytesUsingEncoding(NSUTF8StringEncoding)
textDocumentProxy.adjustTextPositionByCharacterOffset(-length)
NSThread.sleepForTimeInterval(0.01)
before = textDocumentProxy.documentContextBeforeInput
}
// Move the cursor back to the original position
self.textDocumentProxy.adjustTextPositionByCharacterOffset(completePriorString.characters.count)
NSThread.sleepForTimeInterval(0.01)
var after = textDocumentProxy.documentContextAfterInput
var completeAfterString = "";
// Grab everything after the cursor
while (after != nil && !after!.isEmpty) {
completeAfterString += after!
let length = after!.lengthOfBytesUsingEncoding(NSUTF8StringEncoding)
textDocumentProxy.adjustTextPositionByCharacterOffset(length)
NSThread.sleepForTimeInterval(0.01)
after = textDocumentProxy.documentContextAfterInput
}
// Go back to the original cursor position
self.textDocumentProxy.adjustTextPositionByCharacterOffset(-(completeAfterString.characters.count))
let completeString = completePriorString + completeAfterString
print(completeString)
return completeString
}
I'm replacing the selected text in a textView with the new one. To accomplish this, I'm using this code based on this answer of beyowulf. All works well, the replaced text becomes selected, the problem arises when in the text there is one ore more special characters (like emoji etc). In this case the selected text misses one ore more characters at the end of the selection.
mainTextField.replaceRange((theRange), withText: newStr) // replace old text with the new one
selectNewText(theRange, newStr: newStr) // select the new text
func selectNewText(theRange: UITextRange, newStr: String) {
let newStrLength = newStr.characters.count // let's see how long is the string
mainTextField.selectedTextRange = mainTextField.textRangeFromPosition(theRange.start, toPosition: mainTextField.positionFromPosition(theRange.start, offset: newStrLength)!)
mainTextField.becomeFirstResponder()
}
OK, after I read the answers and comments to this question, I fixed this problem by replacing this statement (which returns the "human-perceptible" number of characters):
let newStrLength = newStr.characters.count
With this one:
let newStrLength = newStr.utf16.count
PS
By the way, here is some test I done with different implementations:
let str = "Abc😬"
let count = str.characters.count
print(count) // 4
let count2 = str.utf16.count
print(count2) // 5
let count3 = str.utf8.count
print(count3) // 7