预初始化各 Tab 路由容器,修正 iOS 17 ContentUnavailableView 调用,提升自定义 TabBar 与扫码流程的 UI Test 稳定性。 Co-authored-by: Cursor <cursoragent@cursor.com>
102 lines
3.3 KiB
Swift
102 lines
3.3 KiB
Swift
//
|
||
// SuixinkanUIApplication.swift
|
||
// suixinkanUITests
|
||
//
|
||
// Created by Codex on 2026/6/26.
|
||
//
|
||
|
||
import XCTest
|
||
|
||
/// 封装 XCUIApplication 的常用等待、权限弹窗和全局 Loading 处理。
|
||
final class SuixinkanApp {
|
||
let application: XCUIApplication
|
||
private let resetState: Bool
|
||
private var hasLaunched = false
|
||
|
||
/// 创建并配置 UI Test 启动参数的应用实例。
|
||
init(
|
||
resetState: Bool = false,
|
||
openMenuTitle: String? = nil,
|
||
openProfileRoute: String? = nil
|
||
) {
|
||
self.resetState = resetState
|
||
application = XCUIApplication()
|
||
UITestLaunchConfiguration.apply(
|
||
to: application,
|
||
resetState: resetState,
|
||
openMenu: openMenuTitle,
|
||
openProfile: openProfileRoute
|
||
)
|
||
}
|
||
|
||
/// 启动 App 并等待首屏稳定。
|
||
@discardableResult
|
||
func launch(waitForLoading: Bool = true) -> Self {
|
||
if hasLaunched, application.state == .runningForeground {
|
||
application.activate()
|
||
} else {
|
||
if application.state != .notRunning {
|
||
application.terminate()
|
||
Thread.sleep(forTimeInterval: 0.5)
|
||
}
|
||
application.launch()
|
||
hasLaunched = true
|
||
}
|
||
dismissSystemAlertsIfNeeded()
|
||
if waitForLoading {
|
||
waitForGlobalLoadingToDisappear()
|
||
}
|
||
return self
|
||
}
|
||
|
||
/// 等待全局 Loading 层消失。
|
||
func waitForGlobalLoadingToDisappear(timeout: TimeInterval = 45) {
|
||
let loading = application.staticTexts["加载中"]
|
||
guard loading.waitForExistence(timeout: 2) else { return }
|
||
|
||
let predicate = NSPredicate(format: "exists == false")
|
||
let expectation = XCTNSPredicateExpectation(predicate: predicate, object: loading)
|
||
_ = XCTWaiter.wait(for: [expectation], timeout: timeout)
|
||
}
|
||
|
||
/// 处理系统权限弹窗,尽量点允许以便页面继续加载。
|
||
func dismissSystemAlertsIfNeeded() {
|
||
let springboard = XCUIApplication(bundleIdentifier: "com.apple.springboard")
|
||
let allowButtons = ["允许", "好", "使用App时允许", "Allow", "Allow While Using App", "OK"]
|
||
for title in allowButtons {
|
||
let button = springboard.buttons[title]
|
||
if button.waitForExistence(timeout: 1) {
|
||
button.tap()
|
||
}
|
||
}
|
||
}
|
||
|
||
/// 判断当前是否仍在登录页。
|
||
var isOnLoginScreen: Bool {
|
||
application.staticTexts["login.title"].exists
|
||
}
|
||
|
||
/// 判断主 Tab 是否已出现。
|
||
var isOnMainTabs: Bool {
|
||
application.buttons[TabBarNavigator.Tab.home.identifier].exists
|
||
|| application.buttons[TabBarNavigator.Tab.orders.identifier].exists
|
||
|| application.buttons[TabBarNavigator.Tab.statistics.identifier].exists
|
||
|| application.buttons[TabBarNavigator.Tab.profile.identifier].exists
|
||
}
|
||
|
||
/// 等待冷启动后进入登录页或主 Tab。
|
||
func waitForInitialScreen(timeout: TimeInterval = 45) {
|
||
if application.staticTexts["login.title"].waitForExistence(timeout: 2) {
|
||
return
|
||
}
|
||
|
||
let deadline = Date().addingTimeInterval(timeout)
|
||
while Date() < deadline {
|
||
if isOnMainTabs {
|
||
return
|
||
}
|
||
RunLoop.current.run(until: Date().addingTimeInterval(0.2))
|
||
}
|
||
}
|
||
}
|