5

I have a picker view with a date picker created in Interface Builder. In the Attributes Inspector is selected: Mode "Date and Time". In my UI test I need to select a specific Date and Time from it. I am using the XCTest UI testing APIs that were intruduced with Xcode 7.

My testing code for a localized united states date and time picker looks like that:

app.tables.pickerWheels.elementBoundByIndex(0).adjustToPickerWheelValue("Jun 29")
app.tables.pickerWheels.elementBoundByIndex(1).adjustToPickerWheelValue("12")
app.tables.pickerWheels.elementBoundByIndex(2).adjustToPickerWheelValue("10")
app.tables.pickerWheels.elementBoundByIndex(3).adjustToPickerWheelValue("PM")

which will work perfect!

My testing code for a localized german date and time picker looks like that:

app.tables.pickerWheels.elementBoundByIndex(0).adjustToPickerWheelValue("29. Juni")
app.tables.pickerWheels.elementBoundByIndex(1).adjustToPickerWheelValue("07")
app.tables.pickerWheels.elementBoundByIndex(2).adjustToPickerWheelValue("10")

in this case I got following exception:

Assertion Failure: UI Testing Failure - Internal error: unable to find current value '(null)' in possible values 11. Mai, 12. Mai, 13. Mai, 14. Mai, 15. Mai, 16. Mai, 17. Mai, 18. Mai, 19. Mai, 20. Mai, 21. Mai, 22. Mai, 23. Mai, 24. Mai, 25. Mai, 26. Mai, 27. Mai, 28. Mai, 29. Mai, 30. Mai, 31. Mai, 1. Juni, 2. Juni, 3. Juni, 4. Juni, 5. Juni, 6. Juni, 7. Juni, 8. Juni, 9. Juni, 10. Juni, 11. Juni, 12. Juni, 13. Juni, 14. Juni, 15. Juni, 16. Juni, 17. Juni, 18. Juni, 19. Juni, 20. Juni, 21. Juni, 22. Juni, 23. Juni, 24. Juni, 25. Juni, 26. Juni, 27. Juni, 28. Juni, 29. Juni, 30. Juni, 1. Juli, 2. Juli, 3. Juli, 4. Juli, 5. Juli, 6. Juli, 7. Juli, 8. Juli, 9. Juli, 10. Juli, 11. Juli, 12. Juli, 13. Juli, 14. Juli, 15. Juli, 16. Juli, 17. Juli, 18. Juli, 19. Juli, 20. Juli, 21. Juli, 22. Juli, 23. Juli, 24. Juli, 25. Juli, 26. Juli, 27. Juli, 28. Juli, 29. Juli, 30. Juli, 31. Juli, 1. Aug., 2. Aug., 3. Aug., 4. Aug., 5. Aug., 6. Aug., 7. Aug., 8. Aug., 9. Aug., 10. Aug., 11. Aug., 12. Aug., 13. Aug., 14. Aug., 15. Aug., 16. Aug., 17. Aug., 18. Aug. for the picker wheel "Heute" PickerWheel

I presume, that the '(null)' says that there is no search string set for the UITest query:

app.tables.pickerWheels.elementBoundByIndex(0).adjustToPickerWheelValue("29. Juni")

and this is the reason for rising this exception.

The described problem appears only when the simulators preferences "General / Language & Region" are not set to default "United States".

When I switch to other language settings in the simulator, I got this the same results with my own swift code above as well as the Objective-C code referred in following posting:

How to select a picker view item in an iOS UI test in Xcode?

I definitely have to perform my UITestings with a localized date and time picker! Does anybody have an idea, what´s going wrong here and if there is a solution for it ?

Community
  • 1
  • 1
Herwart
  • 51
  • 3

2 Answers2

2

Probably too late, but this helped me out

 let dateComponent = Calendar.current.dateComponents([.day, .month, .year], from: Date())
 let formatter = DateFormatter()
 formatter.dateFormat = "MMMM"
 let monthText = formatter.string(from: Date())

 datePicker.pickerWheels[String(dateComponent.year!)].adjust(toPickerWheelValue: "2017")
 datePicker.pickerWheels[monthText].adjust(toPickerWheelValue: "December")
 datePicker.pickerWheels[String(dateComponent.day!)].adjust(toPickerWheelValue: "21")
Andrew
  • 3,166
  • 21
  • 32
-1

The previous solutions assumes that the picker starts in the current date. We can have problems if we want to change the date two or more times, e.g., to test that the date validation is working ok.

A more secure approach is use a regex to infer which wheel corresponds to each date component. I mean, days satisfies the regex \d{1,2}, years satisfies the regex \d{4}, and months satisfies the regex [A-Za-z]*. No matter which localization are we using.

So, using this XCUIElementQuery extension:

extension XCUIElementQuery {
  func regex(_ regexPattern: String) -> XCUIElementQuery {
    let satisfiesRegex = NSPredicate { (evaluatedObject, _) in
      guard let element = evaluatedObject as? XCUIElementAttributes else { return false }
      guard let text = element.value as? String else { return false }
      guard let regex = try? NSRegularExpression(pattern: regexPattern) else { return false }
      let range = NSRange(location: 0, length: text.count)
      return regex.firstMatch(in: text, options: [], range: range) != nil
    }
    return self.containing(satisfiesRegex)
  }
}

We can do the following:

  func datePickerWheelFor(_ wheelType: DatePickerWheelType) -> XCUIElement {
    let datePicker = XCUIApplication().datePickers
    switch wheelType {
    case .day:
      return datePicker.pickerWheels.regex2("^\\d{1,2}$").firstMatch
    case .month:
      return datePicker.pickerWheels.regex2("[A-Za-z]*").firstMatch
    case .year:
      return datePicker.pickerWheels.regex2("^\\d{4}$").firstMatch
    }
  }
kanobius
  • 756
  • 9
  • 12