按模块拆分 XCUITest 用例与 Support 基础设施,UI Test 运行时跳过推送与排队 WebSocket,并将 Podfile 最低版本对齐 iOS 16。 Co-authored-by: Cursor <cursoragent@cursor.com>
105 lines
3.3 KiB
Swift
105 lines
3.3 KiB
Swift
//
|
||
// LoginFlow.swift
|
||
// suixinkanUITests
|
||
//
|
||
// Created by Codex on 2026/6/26.
|
||
//
|
||
|
||
import XCTest
|
||
|
||
/// 登录与多账号选择流程封装。
|
||
enum LoginFlow {
|
||
/// 如当前在登录页则执行完整登录,否则直接返回。
|
||
static func loginIfNeeded(
|
||
app: SuixinkanApp,
|
||
credentials: UITestCredentials,
|
||
file: StaticString = #filePath,
|
||
line: UInt = #line
|
||
) {
|
||
guard app.isOnLoginScreen else { return }
|
||
|
||
fillLoginForm(app: app, credentials: credentials)
|
||
submitLogin(app: app)
|
||
handleAccountSelectionIfNeeded(app: app, credentials: credentials)
|
||
app.waitForGlobalLoadingToDisappear()
|
||
|
||
XCTAssertTrue(
|
||
app.isOnMainTabs,
|
||
"登录后应进入主 Tab。",
|
||
file: file,
|
||
line: line
|
||
)
|
||
}
|
||
|
||
/// 填写登录表单并勾选协议。
|
||
static func fillLoginForm(app: SuixinkanApp, credentials: UITestCredentials) {
|
||
let usernameField = app.application.textFields["login.username"]
|
||
XCTAssertTrue(usernameField.waitForExistence(timeout: 8))
|
||
usernameField.tap()
|
||
usernameField.clearAndType(credentials.phone)
|
||
|
||
let passwordField = preferredPasswordField(in: app.application)
|
||
XCTAssertTrue(passwordField.waitForExistence(timeout: 4))
|
||
passwordField.tap()
|
||
passwordField.clearAndType(credentials.password)
|
||
|
||
let privacyToggle = app.application.buttons["login.privacy"]
|
||
if privacyToggle.exists {
|
||
privacyToggle.tap()
|
||
}
|
||
}
|
||
|
||
/// 点击登录按钮。
|
||
static func submitLogin(app: SuixinkanApp) {
|
||
let submitButton = app.application.buttons["login.submit"]
|
||
XCTAssertTrue(submitButton.waitForExistence(timeout: 4))
|
||
submitButton.tap()
|
||
app.waitForGlobalLoadingToDisappear()
|
||
}
|
||
|
||
/// 处理 v9 多账号选择页。
|
||
static func handleAccountSelectionIfNeeded(
|
||
app: SuixinkanApp,
|
||
credentials: UITestCredentials
|
||
) {
|
||
let accountSelectionTitle = app.application.navigationBars["选择账号"]
|
||
guard accountSelectionTitle.waitForExistence(timeout: 6) else { return }
|
||
|
||
if let accountName = credentials.accountName {
|
||
let accountCell = app.application.staticTexts[accountName]
|
||
if accountCell.waitForExistence(timeout: 4) {
|
||
accountCell.tap()
|
||
}
|
||
}
|
||
|
||
let confirmButton = app.application.buttons["进入系统"]
|
||
XCTAssertTrue(confirmButton.waitForExistence(timeout: 4))
|
||
confirmButton.tap()
|
||
app.waitForGlobalLoadingToDisappear()
|
||
}
|
||
|
||
/// 优先读取安全输入框,兼容调试构建下的普通文本框。
|
||
private static func preferredPasswordField(in app: XCUIApplication) -> XCUIElement {
|
||
let secureField = app.secureTextFields["login.password"]
|
||
if secureField.exists {
|
||
return secureField
|
||
}
|
||
return app.textFields["login.password"]
|
||
}
|
||
}
|
||
|
||
private extension XCUIElement {
|
||
/// 清空并重新输入文本。
|
||
func clearAndType(_ text: String) {
|
||
guard let currentValue = value as? String else {
|
||
typeText(text)
|
||
return
|
||
}
|
||
|
||
tap()
|
||
let deleteString = String(repeating: XCUIKeyboardKey.delete.rawValue, count: currentValue.count)
|
||
typeText(deleteString)
|
||
typeText(text)
|
||
}
|
||
}
|