You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
458 lines
17 KiB
458 lines
17 KiB
// Copyright © 2015 Abhishek Banthia |
|
|
|
import XCTest |
|
|
|
class PreferencesTest: XCTestCase { |
|
var app: XCUIApplication! |
|
|
|
override func setUp() { |
|
super.setUp() |
|
continueAfterFailure = false |
|
app = XCUIApplication() |
|
app.launchArguments.append(CLUITestingLaunchArgument) |
|
app.launch() |
|
if app.tables["FloatingTableView"].exists { |
|
app.tapMenubarIcon() |
|
app.buttons["FloatingPin"].click() |
|
} |
|
} |
|
|
|
func testRemovingButtonVisibility() { |
|
app.tapMenubarIcon() |
|
app.tables["mainTableView"].typeKey(",", modifierFlags: .command) |
|
|
|
let predicate = NSPredicate(format: "identifier BEGINSWITH 'DeleteTimezone'", "") |
|
let beforeTimezoneSelected = app.windows["Clocker"].checkBoxes.matching(predicate).firstMatch |
|
|
|
XCTAssertFalse(beforeTimezoneSelected.isEnabled) |
|
|
|
if app.tables["TimezoneTableView"].tableRows.count <= 0 { |
|
XCTFail("There are no timezones.") |
|
return |
|
} |
|
|
|
app.windows["Clocker"].tables["TimezoneTableView"].tableRows.firstMatch.coordinate(withNormalizedOffset: CGVector(dx: 0, dy: 0)).click() |
|
|
|
XCTAssertTrue(app.checkBoxes["DeleteTimezone"].isEnabled) |
|
} |
|
|
|
func testAddingATimezone() { |
|
app.tapMenubarIcon() |
|
app.tables["mainTableView"].typeKey(",", modifierFlags: .command) |
|
|
|
if app.sheets.count == 0 { |
|
app.windows["Clocker"].checkBoxes["AddTimezone"].click() |
|
} |
|
|
|
addAPlace(place: "UTC", to: app) |
|
|
|
let matchPredicate = NSPredicate(format: "value contains %@", "UTC") |
|
let matchingFields = app.tables["TimezoneTableView"].textFields.matching(matchPredicate) |
|
XCTAssertTrue(matchingFields.count > 0, "Matching Fields count was zero") |
|
|
|
deleteAPlace(place: "UTC", for: app) |
|
} |
|
|
|
func testSortingCitiesByTimezoneDifference() { |
|
app.tapMenubarIcon() |
|
app.tables["mainTableView"].typeKey(",", modifierFlags: .command) |
|
|
|
deleteAllPlaces(app: app) |
|
|
|
addAPlace(place: "New Zealand", to: app) |
|
addAPlace(place: "San Francisco", to: app) |
|
addAPlace(place: "Florida", to: app, shouldSleep: false) // Last elements don't need to sleep |
|
|
|
XCTAssertTrue(app.windows["Clocker"].checkBoxes["Sort by Time Difference".localizedString()].exists) |
|
|
|
app.windows["Clocker"].checkBoxes["Sort by Time Difference".localizedString()].click() |
|
|
|
var actualLabels: [String] = [] |
|
let newFormattedAddressQuery = app.windows["Clocker"].textFields |
|
|
|
for elementIndex in 0 ..< newFormattedAddressQuery.count { |
|
if let currentValue = newFormattedAddressQuery.element(boundBy: elementIndex).value as? String, elementIndex % 2 == 0 { |
|
actualLabels.append(currentValue) |
|
} |
|
} |
|
|
|
XCTAssertEqual(actualLabels, |
|
["New Zealand".localizedString(), |
|
"Florida".localizedString(), |
|
"San Francisco".localizedString()]) |
|
|
|
app.windows["Clocker"].checkBoxes["Sort by Time Difference".localizedString()].click() |
|
|
|
var actualReversedLabels: [String] = [] |
|
let newReversedQuery = app.windows["Clocker"].textFields |
|
|
|
for elementIndex in 0 ..< newReversedQuery.count { |
|
if let currentValue = newReversedQuery.element(boundBy: elementIndex).value as? String, elementIndex % 2 == 0 { |
|
actualReversedLabels.append(currentValue) |
|
} |
|
} |
|
|
|
XCTAssertEqual(actualReversedLabels, ["San Francisco".localizedString(), |
|
"Florida".localizedString(), |
|
"New Zealand".localizedString()]) |
|
|
|
addAPlace(place: "Omaha", to: app) |
|
addAPlace(place: "Mumbai", to: app) |
|
} |
|
|
|
func testSortingCitiesByTimezoneName() { |
|
app.tapMenubarIcon() |
|
app.tables["mainTableView"].typeKey(",", modifierFlags: .command) |
|
|
|
XCTAssertTrue(app.windows["Clocker"].checkBoxes["Sort by Time Difference".localizedString()].exists) |
|
XCTAssertTrue(app.windows["Clocker"].checkBoxes["Sort by Label".localizedString()].exists) |
|
XCTAssertTrue(app.windows["Clocker"].checkBoxes["Sort by Name".localizedString()].exists) |
|
|
|
var formattedAddress: [String] = [] |
|
|
|
let formattedAddressQuery = app.windows["Clocker"].textFields |
|
|
|
for elementIndex in 0 ..< formattedAddressQuery.count { |
|
if let currentValue = formattedAddressQuery.element(boundBy: elementIndex).value as? String, elementIndex % 2 == 0 { |
|
formattedAddress.append(currentValue) |
|
} |
|
} |
|
|
|
formattedAddress.sort() |
|
|
|
if let value = app.windows["Clocker"].checkBoxes["Sort by Name".localizedString()].value as? Int, value == 0 { |
|
app.windows["Clocker"].checkBoxes["Sort by Name".localizedString()].click() |
|
} |
|
|
|
var newformattedAddress: [String] = [] |
|
let newFormattedAddressQuery = app.windows["Clocker"].textFields |
|
|
|
for elementIndex in 0 ..< newFormattedAddressQuery.count { |
|
if let currentValue = newFormattedAddressQuery.element(boundBy: elementIndex).value as? String, elementIndex % 2 == 0 { |
|
newformattedAddress.append(currentValue) |
|
} |
|
} |
|
|
|
XCTAssertEqual(newformattedAddress, formattedAddress) |
|
} |
|
|
|
func testSortingCitiesByCustomLabel() { |
|
app.tapMenubarIcon() |
|
app.tables["mainTableView"].typeKey(",", modifierFlags: .command) |
|
|
|
addAPlace(place: "Aurangabad", to: app) |
|
addAPlace(place: "Zimbabwe", to: app) |
|
addAPlace(place: "Portland", to: app, shouldSleep: false) |
|
addAPlace(place: "Asia/Calcutta", to: app) |
|
addAPlace(place: "Anywhere on Earth", to: app, shouldSleep: false) |
|
|
|
XCTAssertTrue(app.windows["Clocker"].checkBoxes["Sort by Label".localizedString()].exists) |
|
|
|
var expectedLabels: [String] = [] |
|
|
|
let formattedAddressQuery = app.windows["Clocker"].textFields |
|
|
|
for elementIndex in 0 ..< formattedAddressQuery.count { |
|
if let currentValue = formattedAddressQuery.element(boundBy: elementIndex).value as? String, elementIndex % 2 == 1 { |
|
expectedLabels.append(currentValue) |
|
} |
|
} |
|
|
|
expectedLabels.sort() |
|
|
|
if let value = app.windows["Clocker"].checkBoxes["Sort by Label".localizedString()].value as? Int, value == 0 { |
|
app.windows["Clocker"].checkBoxes["Sort by Label".localizedString()].click() |
|
} |
|
|
|
var actualLabels: [String] = [] |
|
let newFormattedAddressQuery = app.windows["Clocker"].textFields |
|
|
|
for elementIndex in 0 ..< newFormattedAddressQuery.count { |
|
if let currentValue = newFormattedAddressQuery.element(boundBy: elementIndex).value as? String, elementIndex % 2 == 1 { |
|
actualLabels.append(currentValue) |
|
} |
|
} |
|
|
|
XCTAssertEqual(actualLabels, expectedLabels) |
|
|
|
deleteAPlace(place: "Aurangabad", for: app) |
|
deleteAPlace(place: "Zimbabwe", for: app) |
|
deleteAPlace(place: "Portland", for: app) |
|
deleteAPlace(place: "Asia/Calcutta", for: app) |
|
deleteAPlace(place: "Anywhere on Earth", for: app, shouldSleep: false) |
|
} |
|
|
|
func testSortingTimezonesByCustomLabel() { |
|
app.tapMenubarIcon() |
|
app.tables["mainTableView"].typeKey(",", modifierFlags: .command) |
|
|
|
addAPlace(place: "Europe/Lisbon", to: app) |
|
addAPlace(place: "Asia/Calcutta", to: app) |
|
addAPlace(place: "Anywhere on Earth", to: app, shouldSleep: false) |
|
|
|
XCTAssertTrue(app.windows["Clocker"].checkBoxes["Sort by Label".localizedString()].exists) |
|
|
|
var expectedLabels: [String] = [] |
|
|
|
let formattedAddressQuery = app.windows["Clocker"].textFields |
|
|
|
for elementIndex in 0 ..< formattedAddressQuery.count { |
|
if let currentValue = formattedAddressQuery.element(boundBy: elementIndex).value as? String, elementIndex % 2 == 1 { |
|
expectedLabels.append(currentValue) |
|
} |
|
} |
|
|
|
expectedLabels.sort() |
|
|
|
if let value = app.windows["Clocker"].checkBoxes["Sort by Label".localizedString()].value as? Int, value == 0 { |
|
app.windows["Clocker"].checkBoxes["Sort by Label".localizedString()].click() |
|
} |
|
|
|
var actualLabels: [String] = [] |
|
let newFormattedAddressQuery = app.windows["Clocker"].textFields |
|
|
|
for elementIndex in 0 ..< newFormattedAddressQuery.count { |
|
if let currentValue = newFormattedAddressQuery.element(boundBy: elementIndex).value as? String, elementIndex % 2 == 1 { |
|
actualLabels.append(currentValue) |
|
} |
|
} |
|
|
|
XCTAssertEqual(actualLabels, expectedLabels) |
|
|
|
deleteAPlace(place: "Europe/Lisbon", for: app) |
|
deleteAPlace(place: "Asia/Calcutta", for: app) |
|
deleteAPlace(place: "Anywhere on Earth", for: app, shouldSleep: false) |
|
} |
|
|
|
func searchingWithMisspelledName() { |
|
app.tapMenubarIcon() |
|
app.tables["mainTableView"].typeKey(",", modifierFlags: .command) |
|
|
|
if app.sheets.count == 0 { |
|
app.windows["Clocker"].checkBoxes["AddTimezone"].click() |
|
} |
|
|
|
let searchField = app.searchFields["AvailableSearchField"] |
|
searchField.reset(text: "StuJjlqh7AcJFnBuOdgNa2dQ4WrIajP9Mo8R83FV7fIZ3B8zE2n") |
|
|
|
sleep(2) |
|
|
|
let maxCharacterCountPredicate = NSPredicate(format: "value like %@", "Max Search Characters".localizedString()) |
|
let currentSheets = app.sheets.firstMatch.staticTexts |
|
let maxCharacterQuery = currentSheets.matching(maxCharacterCountPredicate) |
|
|
|
XCTAssertTrue(maxCharacterQuery.count > 0) |
|
|
|
addAPlace(place: "asdakjhdasdahsdasd", to: app, shouldSleep: false) |
|
XCTAssertTrue(app.sheets.staticTexts["No Timezone Selected".localizedString()].exists) |
|
|
|
let informativeLabelPredicate = NSPredicate(format: "placeholderValue like %@", "No results! 😔 Try entering the exact name.") |
|
let sheets = app.sheets.firstMatch.staticTexts |
|
let query = sheets.matching(informativeLabelPredicate) |
|
|
|
XCTAssertTrue(query.count > 0) |
|
|
|
addAPlace(place: "Cambodia", to: app) |
|
|
|
let newInformativeLabelPredicate = NSPredicate(format: "placeholderValue like %@", "No results! 😔 Try entering the exact name.") |
|
let newSheets = app.sheets.firstMatch.staticTexts |
|
let newQuery = newSheets.matching(newInformativeLabelPredicate) |
|
XCTAssertTrue(newQuery.count == 0, "New Query returned \(newQuery.count)") |
|
XCTAssertFalse(app.sheets.staticTexts["Please select a timezone!"].exists) |
|
|
|
deleteAPlace(place: "Cambodia", for: app, shouldSleep: false) |
|
} |
|
|
|
func testNoTimezone() { |
|
app.tapMenubarIcon() |
|
app.buttons["Preferences"].click() |
|
|
|
deleteAllTimezones() |
|
|
|
XCTAssertTrue(app.staticTexts["NoTimezoneEmoji"].exists) |
|
XCTAssertTrue(app.staticTexts["NoTimezoneMessage"].exists) |
|
|
|
app.tapMenubarIcon() |
|
XCTAssertTrue(app.buttons["EmptyAddTimezone"].exists) |
|
|
|
addAPlace(place: "Omaha", to: app) |
|
addAPlace(place: "Mumbai", to: app) |
|
|
|
deleteAllTimezones() |
|
|
|
XCTAssertTrue(app.staticTexts["NoTimezoneEmoji"].exists) |
|
XCTAssertTrue(app.staticTexts["NoTimezoneMessage"].exists) |
|
|
|
addAPlace(place: "Omaha", to: app) |
|
addAPlace(place: "Mumbai", to: app) |
|
} |
|
|
|
func testWarningIfMoreThanOneMenubarIsSelected() { |
|
app.tapMenubarIcon() |
|
app.buttons["Preferences"].click() |
|
|
|
let preferencesTable = app.tables["TimezoneTableView"] |
|
XCTAssertTrue(preferencesTable.exists) |
|
|
|
// Let's reset all checkboxes |
|
let favouritedMenubarsQuery = preferencesTable.checkBoxes.matching(NSPredicate(format: "value == 1", "")) |
|
|
|
if favouritedMenubarsQuery.count > 1 { |
|
for _ in 0 ..< favouritedMenubarsQuery.count { |
|
let checkbox = favouritedMenubarsQuery.element(boundBy: 0) |
|
checkbox.click() |
|
} |
|
} |
|
|
|
// Let's make sure we have > 1 timezones first |
|
let favourites = preferencesTable.tableRows |
|
|
|
if favourites.count < 2 { |
|
addAPlace(place: "UTC", to: app) |
|
} |
|
|
|
XCTAssertTrue(favourites.count > 1) |
|
sleep(2) |
|
|
|
// Select two timezones |
|
let unfavouritedMenubarsQuery = preferencesTable.checkBoxes.matching(NSPredicate(format: "value == 0", "")) |
|
|
|
if unfavouritedMenubarsQuery.count > 1 { |
|
for _ in 0 ..< unfavouritedMenubarsQuery.count { |
|
let checkbox = unfavouritedMenubarsQuery.element(boundBy: 0) |
|
checkbox.click() |
|
sleep(2) |
|
} |
|
} |
|
|
|
XCTAssertTrue(app.dialogs.count > 0) |
|
|
|
let compactModeButton = app.dialogs.buttons["Enable Compact Mode"] |
|
|
|
if compactModeButton.isHittable { |
|
compactModeButton.click() |
|
XCTAssertTrue(app.dialogs.count == 0) |
|
} |
|
} |
|
|
|
private func deleteAllTimezones() { |
|
let clockerWindow = app.windows["Clocker"] |
|
let rowQueryCount = clockerWindow.tables["TimezoneTableView"].tableRows.count |
|
|
|
if rowQueryCount > 0 { |
|
// Table Rows aren't hittable in Xcode 12.0 (10/7/20) and so we need to find a closer co-ordinate and perform click() |
|
let currentElement = clockerWindow.tables["TimezoneTableView"].tableRows.firstMatch.coordinate(withNormalizedOffset: CGVector(dx: 0, dy: 0)) |
|
currentElement.click() |
|
|
|
for _ in 0 ..< rowQueryCount { |
|
clockerWindow.typeKey(XCUIKeyboardKey.delete, |
|
modifierFlags: XCUIElement.KeyModifierFlags()) |
|
} |
|
} |
|
} |
|
} |
|
|
|
extension XCUIApplication { |
|
func tapMenubarIcon() { |
|
if menuBars.count < 2 { |
|
XCTFail("Unable to find menubar options") |
|
return |
|
} |
|
|
|
statusItems.firstMatch.click() |
|
} |
|
} |
|
|
|
extension XCTestCase { |
|
func inverseWaiterFor(element: XCUIElement, time: TimeInterval = 25) { |
|
let spinnerPredicate = NSPredicate(format: "exists == false") |
|
let spinnerExpectation = expectation(for: spinnerPredicate, evaluatedWith: element, handler: nil) |
|
let spinnerResult = XCTWaiter().wait(for: [spinnerExpectation], timeout: time) |
|
|
|
if spinnerResult != .completed { |
|
XCTFail("Still seeing Spinner after 25 seconds. Something's wrong") |
|
return |
|
} |
|
} |
|
|
|
func addAPlace(place: String, to app: XCUIApplication, shouldSleep: Bool = true) { |
|
// Let's first check if the place is already present in the list |
|
|
|
let matchPredicate = NSPredicate(format: "value contains %@", place) |
|
let matchingFields = app.windows["Clocker"].tables["TimezoneTableView"].textFields.matching(matchPredicate) |
|
if matchingFields.count > 0 { |
|
return |
|
} |
|
|
|
if app.sheets.count == 0 { |
|
app.windows["Clocker"].checkBoxes["AddTimezone"].click() |
|
} |
|
|
|
let searchField = app.searchFields["AvailableSearchField"] |
|
if searchField.isHittable { |
|
searchField.reset(text: place) |
|
} |
|
|
|
let results = app.tables["AvailableTimezoneTableView"].cells.staticTexts.matching(matchPredicate) |
|
|
|
let waiter = XCTWaiter() |
|
let isHittable = NSPredicate(format: "exists == true", "") |
|
let addExpectation = expectation(for: isHittable, |
|
evaluatedWith: results.firstMatch) { () -> Bool in |
|
true |
|
} |
|
|
|
waiter.wait(for: [addExpectation], timeout: 5) |
|
|
|
if results.count > 0 { |
|
results.firstMatch.click() |
|
} |
|
|
|
if app.buttons["AddAvailableTimezone"].exists { |
|
app.buttons["AddAvailableTimezone"].click() |
|
} |
|
|
|
if shouldSleep { |
|
sleep(2) |
|
} |
|
} |
|
|
|
func deleteAllPlaces(app: XCUIApplication) { |
|
var rowQueryCount = app.windows["Clocker"].tables["TimezoneTableView"].tableRows.count |
|
if rowQueryCount == 0 { |
|
return |
|
} |
|
|
|
let currentElement = app.windows["Clocker"].tableRows.firstMatch.coordinate(withNormalizedOffset: CGVector(dx: 0, dy: 0)) |
|
currentElement.click() |
|
|
|
while rowQueryCount > 0 { |
|
app.windows["Clocker"].typeKey(XCUIKeyboardKey.delete, modifierFlags: XCUIElement.KeyModifierFlags()) |
|
rowQueryCount -= 1 |
|
} |
|
} |
|
|
|
func deleteAPlace(place: String, for app: XCUIApplication, shouldSleep: Bool = true) { |
|
let userPrefferedLanguage = Locale.preferredLanguages.first ?? "en-US" |
|
if !userPrefferedLanguage.lowercased().contains("en") { |
|
// We're testing in a different user language. We can't do string matching here. |
|
// Delete the last row |
|
let rowCount = app.tables["TimezoneTableView"].tableRows.count |
|
let rowToDelete = app.tables["TimezoneTableView"].tableRows.element(boundBy: rowCount - 1) |
|
deleteAtRow(rowToDelete, for: app, shouldSleep: shouldSleep) |
|
return |
|
} |
|
|
|
let matchPredicate = NSPredicate(format: "value contains %@", place) |
|
let row = app.tables["TimezoneTableView"].textFields.matching(matchPredicate).firstMatch |
|
deleteAtRow(row, for: app, shouldSleep: shouldSleep) |
|
} |
|
|
|
private func deleteAtRow(_ rowToDelete: XCUIElement, for _: XCUIApplication, shouldSleep: Bool) { |
|
rowToDelete.click() |
|
rowToDelete.typeKey(XCUIKeyboardKey.delete, modifierFlags: XCUIElement.KeyModifierFlags()) |
|
if shouldSleep { |
|
sleep(2) |
|
} |
|
} |
|
}
|
|
|