新增完整 UI Test 回归套件,并完善启动隔离逻辑。
按模块拆分 XCUITest 用例与 Support 基础设施,UI Test 运行时跳过推送与排队 WebSocket,并将 Podfile 最低版本对齐 iOS 16。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
78
suixinkanUITests/Support/SuixinkanUIApplication.swift
Normal file
78
suixinkanUITests/Support/SuixinkanUIApplication.swift
Normal file
@ -0,0 +1,78 @@
|
||||
//
|
||||
// SuixinkanUIApplication.swift
|
||||
// suixinkanUITests
|
||||
//
|
||||
// Created by Codex on 2026/6/26.
|
||||
//
|
||||
|
||||
import XCTest
|
||||
|
||||
/// 封装 XCUIApplication 的常用等待、权限弹窗和全局 Loading 处理。
|
||||
final class SuixinkanApp {
|
||||
let application: XCUIApplication
|
||||
private let resetState: Bool
|
||||
|
||||
/// 创建并配置 UI Test 启动参数的应用实例。
|
||||
init(resetState: Bool = false) {
|
||||
self.resetState = resetState
|
||||
application = XCUIApplication()
|
||||
UITestLaunchConfiguration.apply(to: application, resetState: resetState)
|
||||
}
|
||||
|
||||
/// 启动 App 并等待首屏稳定。
|
||||
@discardableResult
|
||||
func launch(waitForLoading: Bool = true) -> Self {
|
||||
if application.state == .runningForeground {
|
||||
application.activate()
|
||||
} else {
|
||||
application.launch()
|
||||
}
|
||||
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["首页"].waitForExistence(timeout: 1)
|
||||
|| application.buttons["我的"].waitForExistence(timeout: 1)
|
||||
}
|
||||
|
||||
/// 等待冷启动后进入登录页或主 Tab。
|
||||
func waitForInitialScreen(timeout: TimeInterval = 45) {
|
||||
if application.staticTexts["login.title"].waitForExistence(timeout: 2) {
|
||||
return
|
||||
}
|
||||
_ = application.buttons["首页"].waitForExistence(timeout: timeout)
|
||||
|| application.buttons["我的"].waitForExistence(timeout: 2)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user