Rebrand from Casera/MyCrib to honeyDue
Total rebrand across KMM project: - Kotlin package: com.example.casera -> com.tt.honeyDue (dirs + declarations) - Gradle: rootProject.name, namespace, applicationId - Android: manifest, strings.xml (all languages), widget resources - iOS: pbxproj bundle IDs, Info.plist, entitlements, xcconfig - iOS directories: Casera/ -> HoneyDue/, CaseraTests/ -> HoneyDueTests/, etc. - Swift source: all class/struct/enum renames - Deep links: casera:// -> honeydue://, .casera -> .honeydue - App icons replaced with honeyDue honeycomb icon - Domains: casera.treytartt.com -> honeyDue.treytartt.com - Bundle IDs: com.tt.casera -> com.tt.honeyDue - Database table names preserved Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
97
iosApp/HoneyDueUITests/PageObjects/BaseScreen.swift
Normal file
97
iosApp/HoneyDueUITests/PageObjects/BaseScreen.swift
Normal file
@@ -0,0 +1,97 @@
|
||||
import XCTest
|
||||
|
||||
/// Base class for all page objects providing common waiting and assertion utilities.
|
||||
///
|
||||
/// Replaces ad-hoc `sleep()` calls with condition-based waits for reliable,
|
||||
/// non-flaky UI tests. All screen page objects should inherit from this class.
|
||||
class BaseScreen {
|
||||
let app: XCUIApplication
|
||||
let timeout: TimeInterval
|
||||
|
||||
init(app: XCUIApplication, timeout: TimeInterval = 10) {
|
||||
self.app = app
|
||||
self.timeout = timeout
|
||||
}
|
||||
|
||||
// MARK: - Wait Helpers (replaces fixed sleeps)
|
||||
|
||||
/// Waits for an element to exist within the timeout period.
|
||||
/// Fails the test with a descriptive message if the element does not appear.
|
||||
@discardableResult
|
||||
func waitForElement(_ element: XCUIElement, timeout: TimeInterval? = nil) -> XCUIElement {
|
||||
let t = timeout ?? self.timeout
|
||||
XCTAssertTrue(element.waitForExistence(timeout: t), "Element \(element) did not appear within \(t)s")
|
||||
return element
|
||||
}
|
||||
|
||||
/// Waits for an element to disappear within the timeout period.
|
||||
/// Fails the test if the element is still present after the timeout.
|
||||
func waitForElementToDisappear(_ element: XCUIElement, timeout: TimeInterval? = nil) {
|
||||
let t = timeout ?? self.timeout
|
||||
let predicate = NSPredicate(format: "exists == false")
|
||||
let expectation = XCTNSPredicateExpectation(predicate: predicate, object: element)
|
||||
let result = XCTWaiter().wait(for: [expectation], timeout: t)
|
||||
XCTAssertEqual(result, .completed, "Element \(element) did not disappear within \(t)s")
|
||||
}
|
||||
|
||||
/// Waits for an element to become hittable (visible and interactable).
|
||||
/// Returns the element for chaining.
|
||||
@discardableResult
|
||||
func waitForHittable(_ element: XCUIElement, timeout: TimeInterval? = nil) -> XCUIElement {
|
||||
let t = timeout ?? self.timeout
|
||||
let predicate = NSPredicate(format: "isHittable == true")
|
||||
let expectation = XCTNSPredicateExpectation(predicate: predicate, object: element)
|
||||
_ = XCTWaiter().wait(for: [expectation], timeout: t)
|
||||
return element
|
||||
}
|
||||
|
||||
/// Waits until a condition evaluates to true, polling every 0.5s.
|
||||
/// More flexible than element-based waits for complex state checks.
|
||||
func waitForCondition(
|
||||
_ description: String,
|
||||
timeout: TimeInterval? = nil,
|
||||
condition: () -> Bool
|
||||
) -> Bool {
|
||||
let t = timeout ?? self.timeout
|
||||
let deadline = Date().addingTimeInterval(t)
|
||||
while Date() < deadline {
|
||||
if condition() { return true }
|
||||
RunLoop.current.run(until: Date().addingTimeInterval(0.5))
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
/// Waits for an element to exist, then taps it. Convenience for the common wait+tap pattern.
|
||||
@discardableResult
|
||||
func tapElement(_ element: XCUIElement, timeout: TimeInterval? = nil) -> XCUIElement {
|
||||
waitForElement(element, timeout: timeout)
|
||||
element.tap()
|
||||
return element
|
||||
}
|
||||
|
||||
// MARK: - State Assertions
|
||||
|
||||
/// Asserts that an element with the given accessibility identifier exists.
|
||||
func assertExists(_ identifier: String, file: StaticString = #file, line: UInt = #line) {
|
||||
let element = app.descendants(matching: .any)[identifier]
|
||||
XCTAssertTrue(element.waitForExistence(timeout: timeout), "Element '\(identifier)' not found", file: file, line: line)
|
||||
}
|
||||
|
||||
/// Asserts that an element with the given accessibility identifier does not exist.
|
||||
func assertNotExists(_ identifier: String, file: StaticString = #file, line: UInt = #line) {
|
||||
let element = app.descendants(matching: .any)[identifier]
|
||||
XCTAssertFalse(element.exists, "Element '\(identifier)' should not exist", file: file, line: line)
|
||||
}
|
||||
|
||||
// MARK: - Navigation
|
||||
|
||||
/// Taps the first button in the navigation bar (typically the back button).
|
||||
func tapBackButton() {
|
||||
app.navigationBars.buttons.element(boundBy: 0).tap()
|
||||
}
|
||||
|
||||
/// Subclasses must override this property to indicate whether the screen is currently displayed.
|
||||
var isDisplayed: Bool {
|
||||
fatalError("Subclasses must override isDisplayed")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user