Skip to content

Commit

Permalink
Reimplement waitForElementAndTap... as a typed XCUIElement method
Browse files Browse the repository at this point in the history
"Typed" in that it expects an `enum` instead of a `String` to define the
expected element state.
  • Loading branch information
mokagio committed Jan 3, 2024
1 parent 6ba9000 commit b442842
Show file tree
Hide file tree
Showing 5 changed files with 99 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ public class EditorPostSettings: ScreenObject {
// To ensure that the day tap happens on the correct month
let nextMonth = monthLabel.value as! String
if nextMonth != currentMonth {
waitForElementAndTap(firstCalendarDayButton, untilConditionOn: firstCalendarDayButton, condition: "selected", errorMessage: "First Day button not selected!")
firstCalendarDayButton.tapUntil(.selected, failureMessage: "First Day button not selected!")
}

doneButton.tap()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,11 @@ public class NotificationsScreen: ScreenObject {
}

public func replyToComment(withText text: String) -> Self {
waitForElementAndTap(replyCommentButton, untilConditionOn: replyTextView, condition: "exists", errorMessage: "Reply Text View does not exists!")
replyCommentButton.tapUntil(
element: replyTextView,
matches: .exists,
failureMessage: "Reply Text View does not exists!"
)

replyTextView.typeText(text)
replyButton.tap()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ extension XCUIApplication {
// There should be no need to wait for this button to exist since it's part of the same
// alert where "Save Password" is.
let notNowButton = XCUIApplication().buttons["Not Now"]
waitForElementAndTap(notNowButton, untilConditionOn: notNowButton, condition: "dismissed", errorMessage: "Save Password Prompt not dismissed!")
notNowButton.tapUntil(.dismissed, failureMessage: "Save Password Prompt not dismissed!")
}
}
}
86 changes: 86 additions & 0 deletions WordPress/UITestsFoundation/XCUIElement+TapUntil.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import XCTest

public extension XCUIElement {

/// Abstraction do describe possible "states" an `XCUIElement` can be in.
///
/// The goal of this `enum` is to make checking against the possible states a safe operation thanks to the compiler enforcing all and only the states represented by the `enum` `case`s are handled.
enum State {
case exists
case dismissed
case selected
}

/// Attempt to tap `self` until the given `XCUIElement` is in the given `State` or the `maxRetries` number of retries has been reached.
///
/// Useful to make tests robusts against UI changes that may have some lag.
func tapUntil(
element: XCUIElement,
matches state: State,
failureMessage: String,
maxRetries: Int = 10,
retryInterval: TimeInterval = 1
) {
tapUntil(
Condition(element: element, state: state),
retriedCount: 0,
failureMessage: failureMessage,
maxRetries: maxRetries,
retryInterval: retryInterval
)
}

/// Attempt to tap `self` until its "state" matches `Condition.State` or the `maxRetries` number of retries has been reached.
///
/// Useful to make tests robusts against UI changes that may have some lag.
func tapUntil(
_ state: State,
failureMessage: String,
maxRetries: Int = 10,
retryInterval: TimeInterval = 1
) {
tapUntil(
Condition(element: self, state: state),
retriedCount: 0,
failureMessage: failureMessage,
maxRetries: maxRetries,
retryInterval: retryInterval
)
}

/// Describe the expectation for a given `XCUIElement` to be in a certain `Condition.State`.
///
/// Example: `Condition(element: myButton, state: .selected)`.
struct Condition {

let element: XCUIElement
let state: XCUIElement.State

fileprivate func isMet() -> Bool {
switch state {
case .exists: return element.exists
case .dismissed: return element.isHittable
case .selected: return element.isSelected
}
}
}

private func tapUntil(
_ condition: Condition,
retriedCount: Int,
failureMessage: String,
maxRetries: Int,
retryInterval: TimeInterval
) {
guard retriedCount < maxRetries else {
return XCTFail("\(failureMessage) after \(retriedCount) tries.")
}

tap()

guard condition.isMet() else {
sleep(UInt32(retryInterval))
return tapUntil(condition, retriedCount: retriedCount + 1, failureMessage: failureMessage, maxRetries: maxRetries, retryInterval: retryInterval)
}
}
}
8 changes: 6 additions & 2 deletions WordPress/WordPress.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -879,6 +879,7 @@
37022D931981C19000F322B7 /* VerticallyStackedButton.m in Sources */ = {isa = PBXBuildFile; fileRef = 37022D901981BF9200F322B7 /* VerticallyStackedButton.m */; };
374CB16215B93C0800DD0EBC /* AudioToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 374CB16115B93C0800DD0EBC /* AudioToolbox.framework */; };
37EAAF4D1A11799A006D6306 /* CircularImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37EAAF4C1A11799A006D6306 /* CircularImageView.swift */; };
3F03F2BD2B45041E00A9CE99 /* XCUIElement+TapUntil.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3F03F2BC2B45041E00A9CE99 /* XCUIElement+TapUntil.swift */; };
3F09CCA82428FF3300D00A8C /* ReaderTabViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3F09CCA72428FF3300D00A8C /* ReaderTabViewController.swift */; };
3F09CCAA2428FF8300D00A8C /* ReaderTabView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3F09CCA92428FF8300D00A8C /* ReaderTabView.swift */; };
3F09CCAE24292EFD00D00A8C /* ReaderTabItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3F09CCAD24292EFD00D00A8C /* ReaderTabItem.swift */; };
Expand Down Expand Up @@ -6583,6 +6584,7 @@
37EAAF4C1A11799A006D6306 /* CircularImageView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CircularImageView.swift; sourceTree = "<group>"; };
3AB6A3B516053EA8D0BC3B17 /* Pods-JetpackStatsWidgets.release-alpha.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-JetpackStatsWidgets.release-alpha.xcconfig"; path = "../Pods/Target Support Files/Pods-JetpackStatsWidgets/Pods-JetpackStatsWidgets.release-alpha.xcconfig"; sourceTree = "<group>"; };
3C8DE270EF0498A2129349B0 /* Pods-JetpackNotificationServiceExtension.release-alpha.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-JetpackNotificationServiceExtension.release-alpha.xcconfig"; path = "../Pods/Target Support Files/Pods-JetpackNotificationServiceExtension/Pods-JetpackNotificationServiceExtension.release-alpha.xcconfig"; sourceTree = "<group>"; };
3F03F2BC2B45041E00A9CE99 /* XCUIElement+TapUntil.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "XCUIElement+TapUntil.swift"; sourceTree = "<group>"; };
3F09CCA72428FF3300D00A8C /* ReaderTabViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReaderTabViewController.swift; sourceTree = "<group>"; };
3F09CCA92428FF8300D00A8C /* ReaderTabView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReaderTabView.swift; sourceTree = "<group>"; };
3F09CCAD24292EFD00D00A8C /* ReaderTabItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReaderTabItem.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -11801,15 +11803,16 @@
3FA6405A2670CCD40064401E /* Info.plist */,
3F762E9226784A950088CD45 /* Logger.swift */,
3FE39A4326F8391D006E2B3A /* Screens */,
3FA640592670CCD40064401E /* UITestsFoundation.h */,
EA85B7A92A6860370096E097 /* TestObserver.swift */,
3FA640592670CCD40064401E /* UITestsFoundation.h */,
3F762E9426784B540088CD45 /* WireMock.swift */,
3F107B1829B6F7E0009B3658 /* XCTestCase+Utils.swift */,
3F6A8CDF2A246357009DBC2B /* XCUIApplication+SavePassword.swift */,
D8E7529A2A29DC4C00E73B2D /* XCUIApplication+ScrollDownToElement.swift */,
3FB5C2B227059AC8007D0ECE /* XCUIElement+Scroll.swift */,
3F03F2BC2B45041E00A9CE99 /* XCUIElement+TapUntil.swift */,
3F762E9A26784D2A0088CD45 /* XCUIElement+Utils.swift */,
3F762E9826784CC90088CD45 /* XCUIElementQuery+Utils.swift */,
D8E7529A2A29DC4C00E73B2D /* XCUIApplication+ScrollDownToElement.swift */,
);
path = UITestsFoundation;
sourceTree = "<group>";
Expand Down Expand Up @@ -22942,6 +22945,7 @@
3F2F855A26FAF227000FCDA5 /* EditorNoticeComponent.swift in Sources */,
01281E9A2A0456CB00464F8F /* DomainsSelectionScreen.swift in Sources */,
3F2F855D26FAF227000FCDA5 /* LoginCheckMagicLinkScreen.swift in Sources */,
3F03F2BD2B45041E00A9CE99 /* XCUIElement+TapUntil.swift in Sources */,
EA78189427596B2F00554DFA /* ContactUsScreen.swift in Sources */,
D82E087829EEB7AF0098F500 /* DomainsScreen.swift in Sources */,
3F6A8CE02A246357009DBC2B /* XCUIApplication+SavePassword.swift in Sources */,
Expand Down

0 comments on commit b442842

Please sign in to comment.