Skip to content

Commit

Permalink
Minor tweaks
Browse files Browse the repository at this point in the history
  • Loading branch information
eonist committed Oct 20, 2023
1 parent e3eba02 commit 8c119c5
Show file tree
Hide file tree
Showing 13 changed files with 64 additions and 53 deletions.
6 changes: 3 additions & 3 deletions Sources/UITestSugar/query/QueryAsserter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ public class QueryAsserter {
* - Fixme: ⚠️️ write example
*/
public static func waitFor(app: XCUIApplication, testCase: XCTestCase, labelString: String, timeOut: Double = 5) {
let label = app.staticTexts[labelString] // Get the XCUIElement instance of the label with the provided string
let exists = NSPredicate(format: "exists == true") // Create a predicate to check if the label exists
let label: XCUIElement = app.staticTexts[labelString] // Get the XCUIElement instance of the label with the provided string
let exists: NSPredicate = .init(format: "exists == true") // Create a predicate to check if the label exists
testCase.expectation(
for: exists, // The expectation to evaluate
evaluatedWith: label, // The object to evaluate the expectation against
Expand Down Expand Up @@ -51,7 +51,7 @@ public class QueryAsserter {
predicate: existsPredicate, // The predicate to evaluate
object: element // The element to evaluate the predicate against
) // Create an expectation for the element to exist
let waiter = XCTWaiter() // Create a new XCTWaiter instance
let waiter: XCTWaiter = .init() // Create a new XCTWaiter instance
let result: XCTWaiter.Result = waiter.wait(
for: [expectation], // The expectations to wait for
timeout: timeOut // The maximum amount of time to wait for the expectations to be fulfilled
Expand Down
15 changes: 9 additions & 6 deletions Sources/UITestSugar/query/QueryParser.swift
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,8 @@ public class QueryParser {
* ```
*/
public static func firstElement(query: XCUIElementQuery, labels: [String]) -> XCUIElement? {
labels.map { label in
// ⚠️️ used to be CONTAINS, but thats partial and not exact etc
labels.map { (label: String) in
// ⚠️️ Used to be CONTAINS, but thats partial and not exact etc
let predicate: NSPredicate = .init(format: "label MATCHES %@", label)
return query.containing(predicate)
// Filter out any nil values from the array
Expand All @@ -78,7 +78,8 @@ public class QueryParser {
*/
public static func firstElement(_ query: XCUIElementQuery, label: String) -> XCUIElement {
// ⚠️️ Used to be CONTAINS, but thats partial and not exact etc
let elementQuery:XCUIElementQuery = query.containing(NSPredicate(format: "label MATCHES %@", label))
let predicate: NSPredicate = .init(format: "label MATCHES %@", label)
let elementQuery: XCUIElementQuery = query.containing(predicate)
return elementQuery.firstMatch
}
/**
Expand All @@ -88,7 +89,8 @@ public class QueryParser {
* - Returns: The first matching element, or nil if no matches were found.
*/
public static func firstElement(_ query: XCUIElementQuery, title: String) -> XCUIElement {
let elementQuery:XCUIElementQuery = query.containing(NSPredicate(format: "title MATCHES %@", title))
let predicate: NSPredicate = .init(format: "title MATCHES %@", title)
let elementQuery: XCUIElementQuery = query.containing(predicate)
return elementQuery.firstMatch
}
/**
Expand All @@ -98,7 +100,8 @@ public class QueryParser {
* - Returns: The first matching element, or nil if no matches were found.
*/
public static func firstElement(_ query: XCUIElementQuery, value: String) -> XCUIElement {
let elementQuery:XCUIElementQuery = query.containing(NSPredicate(format: "value MATCHES %@", value))
let predicate: NSPredicate = .init(format: "value MATCHES %@", value)
let elementQuery: XCUIElementQuery = query.containing(predicate)
return elementQuery.firstMatch
}
/**
Expand Down Expand Up @@ -141,7 +144,7 @@ public class QueryParser {
*/
public static func children(query: XCUIElementQuery, strings: [String], type: XCUIElement.ElementType = .any) -> [XCUIElement] {
let result: [[XCUIElement]] = strings.map { (string: String) in // Map over each string in the array
let predicate:NSPredicate = .init(format: "label MATCHES %@", string) // Create a predicate to match the string
let predicate: NSPredicate = .init(format: "label MATCHES %@", string) // Create a predicate to match the string
let elementQuery: XCUIElementQuery = query.containing(predicate) // Create an element query with the predicate
let elements: [XCUIElement] = QueryParser.elements(query: elementQuery) // Get the elements matching the query
return elements // Return the elements
Expand Down
1 change: 0 additions & 1 deletion Sources/UITestSugar/ui/XCUIApplication+Ext.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ extension XCUIApplication {
* - Fixme: ⚠️️ This function should be added to UITestSugar.
* - Note: ref: https://stackoverflow.com/questions/43904798/how-to-dismiss-a-popover-in-a-ui-test (The selected link provides a solution for dismissing a popover in UI testing by using the XCUIApplication class and tapping on the "Dismiss" button.)
*/

public func dismissPopup() {
// Dismiss the popup by tapping the "Cancel" button
otherElements["Cancel"].tap()
Expand Down
4 changes: 2 additions & 2 deletions Sources/UITestSugar/ui/element/ElementAsserter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@ public class ElementAsserter {
*/
public static func existsAndVisible(element: XCUIElement, timeout: Double) -> Bool {
// Wait for the specified element to exist in the UI hierarchy
let exists = element.waitForExistence(timeout: timeout)
let exists: Bool = element.waitForExistence(timeout: timeout)
// Check if the element is visible with the window frame
let visible = isVisibleInWindow(element: element)
let visible: Bool = isVisibleInWindow(element: element)
// Return true if the element exists and is visible, false otherwise
return exists && visible
}
Expand Down
54 changes: 29 additions & 25 deletions Sources/UITestSugar/ui/element/extension/Element+Parsing.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ extension XCUIElement {
* - Parameter map: - Fixme: ⚠️️
*/
public func firstDescendant(_ map: [Any]) -> XCUIElement {
if map.count == 1, let query = map.first { // if map is only 1 level deep
if map.count == 1, let query: Any = map.first { // if map is only 1 level deep
return self.firstDescendant(query: query)
} else if map.count > 1, let query: Any = map.first { // if map is more than 1 level deep
let element: XCUIElement = self.firstDescendant(query: query)
Expand Down Expand Up @@ -59,11 +59,11 @@ extension XCUIElement {
* - Returns: The first descendant of the element that matches the specified query.
*/
public func firstDescendant(_ map: [SearchType]) -> XCUIElement {
if map.count == 1, let query = map.first { // if map is only 1 level deep
if map.count == 1, let query: XCUIElement.SearchType = map.first { // if map is only 1 level deep
return self.firstDescendant(type: query.type, id: query.id)
} else if map.count > 1, let query: SearchType = map.first { // if map is more than 1 level deep
let element = self.firstDescendant(type: query.type, id: query.id)
let newMap = Array(map[1..<map.count]) // substract first element
let element: XCUIElement = self.firstDescendant(type: query.type, id: query.id)
let newMap: [XCUIElement.SearchType] = Array(map[1..<map.count]) // substract first element
return element.firstDescendant(newMap) // recursive call
} else { // map is empty, might never be called
Swift.print("🚫 map is an empty array 🚫")
Expand All @@ -78,11 +78,11 @@ extension XCUIElement {
* - Returns: A query for all descendants of the element that match the specified query.
*/
public func descendants(_ map: [SearchType]) -> XCUIElementQuery {
if map.count == 1, let search = map.first { // if map is only 1 level deep
if map.count == 1, let search: XCUIElement.SearchType = map.first { // if map is only 1 level deep
return self.descendants(type: search.type, id: search.id)
} else if map.count > 1, let search: SearchType = map.first { // if map is more than 1 level deep
let element = self.firstDescendant(type: search.type, id: search.id)
let newMap = Array(map[1..<map.count])
let element: XCUIElement = self.firstDescendant(type: search.type, id: search.id)
let newMap: [XCUIElement.SearchType] = Array(map[1..<map.count])
return element.descendants(newMap) // recursive call
} else { // map is empty, might never be called
Swift.print("🚫 map is an empty array 🚫")
Expand All @@ -103,7 +103,7 @@ extension XCUIElement {
* app.firstDescendant(type: .button, id: "someBtn").waitToAppear(5)?.tap(wait: 2)
*/
public func firstDescendant(type: XCUIElement.ElementType = .any, id: String? = nil) -> XCUIElement {
if let id = id { // If an ID is provided
if let id: String = id { // If an ID is provided
return self.descendants(type: type, id: id).firstMatch // Find the first descendant element with the specified type and ID
} else { // If no ID is provided
return self.descendants(matching: type).firstMatch // Find the first descendant element with the specified type
Expand All @@ -118,7 +118,7 @@ extension XCUIElement {
* - Returns: A query for all descendants of the element that match the specified type and ID.
*/
public func descendants(type: XCUIElement.ElementType = .any, id: String? = nil) -> XCUIElementQuery {
if let id = id { // Check if the `id` parameter is not `nil`
if let id: String = id { // Check if the `id` parameter is not `nil`
return self.descendants(matching: type).matching(identifier: id) // If so, find all descendants with the specified type and then filter the results to include only those with the specified ID
} else { // If the `id` parameter is `nil`
return self.descendants(matching: type) // Find all descendants with the specified type
Expand All @@ -134,7 +134,7 @@ extension XCUIElement {
*/
public func firstDescendant(label: String?, type: XCUIElement.ElementType = .any) -> XCUIElement {
if let lbl: String = label { // Check if the `label` parameter is not `nil`
let query = self.descendants(matching: type) // Find all descendants with the specified type
let query: XCUIElementQuery = self.descendants(matching: type) // Find all descendants with the specified type
return QueryParser.firstElement(query, label: lbl) // If so, find the first element with the specified label
} else { // If the `label` parameter is `nil`
return self.descendants(matching: type).firstMatch // Find the first descendant with the specified type
Expand All @@ -144,13 +144,14 @@ extension XCUIElement {
* Returns the first descendant of the element that matches the specified title and type.
* If the `title` parameter is not `nil`, the function calls the `descendants(matching:)` method to find all descendants with the specified type and then filters the results to include only those with the specified title.
* If the `title` parameter is `nil`, the function calls the `descendants(matching:)` method to find the first descendant with the specified type.
* - Parameter title: The title of the UI element being searched for.
* - Parameter type: The type of UI element being searched for.
* - Parameters:
* - title: The title of the UI element being searched for.
* - type: The type of UI element being searched for.
* - Returns: The first descendant of the element that matches the specified title and type.
*/
public func firstDescendant(title: String?, type: XCUIElement.ElementType = .any) -> XCUIElement {
if let title = title { // Check if the `title` parameter is not `nil`
let query = self.descendants(matching: type) // Find all descendants with the specified type
if let title: String = title { // Check if the `title` parameter is not `nil`
let query: XCUIElementQuery = self.descendants(matching: type) // Find all descendants with the specified type
return QueryParser.firstElement(query, title: title) // If so, find the first element with the specified title
} else { // If the `title` parameter is `nil`
return self.descendants(matching: type).firstMatch // Find the first descendant with the specified type
Expand All @@ -160,13 +161,14 @@ extension XCUIElement {
* Returns the first descendant of the element that matches the specified value and type.
* If the `value` parameter is not `nil`, the function calls the `descendants(matching:)` method to find all descendants with the specified type and then filters the results to include only those with the specified value.
* If the `value` parameter is `nil`, the function calls the `descendants(matching:)` method to find the first descendant with the specified type.
* - Parameter value: The value of the UI element being searched for.
* - Parameter type: The type of UI element being searched for.
* - Parameters:
* - value: The value of the UI element being searched for.
* - type: The type of UI element being searched for.
* - Returns: The first descendant of the element that matches the specified value and type.
*/
public func firstDescendant(value: String?, type: XCUIElement.ElementType = .any) -> XCUIElement {
if let value = value { // Check if the `value` parameter is not `nil`
let query = self.descendants(matching: type) // Find all descendants with the specified type
if let value: String = value { // Check if the `value` parameter is not `nil`
let query: XCUIElementQuery = self.descendants(matching: type) // Find all descendants with the specified type
return QueryParser.firstElement(query, value: value) // If so, find the first element with the specified value
} else { // If the `value` parameter is `nil`
return self.descendants(matching: type).firstMatch // Find the first descendant with the specified type
Expand All @@ -175,8 +177,9 @@ extension XCUIElement {
/**
* Finds the first element of the specified type that has a sub-element that matches the provided condition.
* - Remark: This method can be a bit slow, to speed it up, try to narrow down the element it is called on.
* - Parameter type: The type of UI element being searched for.
* - Parameter condition: A closure that takes an `XCUIElement` and returns a `Bool`. The closure is used to check if a sub-element of the element being searched has a certain condition.
* - Parameters:
* - type: The type of UI element being searched for.
* - condition: A closure that takes an `XCUIElement` and returns a `Bool`. The closure is used to check if a sub-element of the element being searched has a certain condition.
* - Returns: The first element of the specified type that has a sub-element that matches the provided condition.
* ## Example:
* app.firstDescendant(type: .cell) {
Expand All @@ -185,9 +188,9 @@ extension XCUIElement {
*/
public func firstDescendant(type: XCUIElementType, condition: (XCUIElement) -> Bool) -> XCUIElement? {
// Find all descendant elements with the specified type
let descendants = self.descendants(matching: type)
let descendants: XCUIElementQuery = self.descendants(matching: type)
// Find the first element that has a descendant that satisfies the given condition
let firstMatch = descendants.allElementsBoundByIndex.first {
let firstMatch: XCUIElement? = descendants.allElementsBoundByIndex.first {
$0.firstDescendant(condition) != nil // Check if the descendant satisfies the given condition
}
return firstMatch // Return the first matching element, or nil if no match is found
Expand Down Expand Up @@ -228,12 +231,13 @@ extension XCUIElement {
* Returns the first child of the element that matches the specified type and ID.
* If the `id` parameter is not `nil`, the function calls the `children(id:type:)` method to find all children with the specified type and ID and then returns the first match.
* If the `id` parameter is `nil`, the function calls the `children(matching:)` method to find the first child with the specified type.
* - Parameter type: The type of UI element being searched for.
* - Parameter id: The ID of the UI element being searched for.
* - Parameters:
* - type: The type of UI element being searched for.
* - id: The ID of the UI element being searched for.
* - Returns: The first child of the element that matches the specified type and ID.
*/
public func firstChild(type: XCUIElement.ElementType = .any, id: String? = nil) -> XCUIElement {
if let id = id { // Check if the `id` parameter is not `nil`
if let id: String = id { // Check if the `id` parameter is not `nil`
return self.children(id: id, type: type).firstMatch // If so, find all children with the specified type and ID and return the first match
} else { // If the `id` parameter is `nil`
return self.children(matching: type).firstMatch // This line of code is incomplete and will cause a compilation error
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,15 @@ extension XCUIElement {
* - Returns: The current XCUIElement instance.
*/
@discardableResult public func wait(after sleepSecs: Double) -> XCUIElement {
sleep(sec: sleepSecs) // Sleep for the specified number of seconds
sleep(sec: sleepSecs) // Sleep for the specified number of seconds
return self // Return the modified element
}
/**
* Checks if an item exists and is hittable. If it's not hittable, then the app is tapped so that the tooltip goes away.
* - Returns: The current XCUIElement instance.
*/
@discardableResult public func disregardToolTip() -> XCUIElement {
let elementExists = self.waitForExistence(timeout: 10)
let elementExists: Bool = self.waitForExistence(timeout: 10)
if elementExists && self.isHittable == false { // Most likely tooltip is being shown and this needs to be dismissed.
XCUIApplication().tap(waitForExistence: 5, waitAfter: 2)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ extension XCUIElement {
*/
@discardableResult public func typeString(_ text: String) -> XCUIElement {
self.typeText(text) // Type the specified text into the element
return self// Return the modified element
return self // Return the modified element
}
/**
* Clears the given search field and types the provided text.
Expand Down Expand Up @@ -44,7 +44,7 @@ extension XCUIElement {
*/
public func clearAndEnterText(text: String) {
// Check if the element's value is a string
guard let stringValue = self.value as? String else {
guard let stringValue: String = self.value as? String else {
XCTFail("⚠️️ Tried to clear and enter text into a non string value")
return
}
Expand All @@ -58,7 +58,7 @@ extension XCUIElement {
self.doubleTap() // ⚠️️ We need 3 taps to select all, 2 taps sometimes fail to select all if there are special characters etc
#endif
// Delete the old text by typing the delete key repeatedly
let deleteString = String(
let deleteString: String = .init(
repeating: XCUIKeyboardKey.delete.rawValue, // The string to repeat
count: stringValue.count // The number of times to repeat the string
)
Expand Down Expand Up @@ -96,12 +96,12 @@ extension XCUIElement {
// Repeatedly delete text as long as there is something in the text field.
// This is required to clear text that does not fit in to the textfield and is partially hidden initially.
// It's important to check for the placeholder value, otherwise it gets into an infinite loop.
while let stringValue = self.value as? String, !stringValue.isEmpty, stringValue != self.placeholderValue {
while let stringValue: String = self.value as? String, !stringValue.isEmpty, stringValue != self.placeholderValue {
// Move the cursor to the end of the text field
let lowerRightCorner = self.coordinate(withNormalizedOffset: CGVector(dx: 0.9, dy: 0.9))
let lowerRightCorner: XCUICoordinate = self.coordinate(withNormalizedOffset: CGVector(dx: 0.9, dy: 0.9))
lowerRightCorner.tap()
// Delete the current text by typing the delete key repeatedly
let delete = String(
let delete: String = .init(
repeating: XCUIKeyboardKey.delete.rawValue, // The string to repeat
count: stringValue.count // The number of times to repeat the string
)
Expand Down
Loading

0 comments on commit 8c119c5

Please sign in to comment.