feat: complete large-book pagination runtime and UI coverage
This commit is contained in:
@@ -0,0 +1,71 @@
|
||||
import XCTest
|
||||
|
||||
struct DemoReaderState {
|
||||
let rawValue: String
|
||||
let fields: [String: String]
|
||||
|
||||
init(rawValue: String) {
|
||||
self.rawValue = rawValue
|
||||
var parsed: [String: String] = [:]
|
||||
let separators = CharacterSet.whitespacesAndNewlines.union(CharacterSet(charactersIn: ";"))
|
||||
for token in rawValue.components(separatedBy: separators) where !token.isEmpty {
|
||||
guard let delimiterIndex = token.firstIndex(of: "=") else { continue }
|
||||
let key = String(token[..<delimiterIndex])
|
||||
let value = String(token[token.index(after: delimiterIndex)...])
|
||||
parsed[key] = value
|
||||
}
|
||||
fields = parsed
|
||||
}
|
||||
|
||||
subscript(key: String) -> String? {
|
||||
fields[key]
|
||||
}
|
||||
|
||||
var isOpened: Bool { fields["reader"] == "opened" }
|
||||
var page: Int? { fields["page"].flatMap(Int.init) }
|
||||
var display: String? { fields["display"] }
|
||||
var toolbar: String? { fields["toolbar"] }
|
||||
var highlights: Int? { fields["highlights"].flatMap(Int.init) }
|
||||
var selection: Int? { fields["selection"].flatMap(Int.init) }
|
||||
var href: String? { fields["href"] }
|
||||
var progression: Double? { fields["progression"].flatMap(Double.init) }
|
||||
var mode: String? { fields["mode"] }
|
||||
var pagination: String? { fields["pagination"] }
|
||||
var knownPages: Int? { fields["knownPages"].flatMap(Int.init) }
|
||||
var knownChapters: Int? { fields["knownChapters"].flatMap(Int.init) }
|
||||
var buildableChapters: Int? { fields["buildableChapters"].flatMap(Int.init) }
|
||||
var avoidWidows: Int? { fields["avoidWidows"].flatMap(Int.init) }
|
||||
var avoidOrphans: Int? { fields["avoidOrphans"].flatMap(Int.init) }
|
||||
}
|
||||
|
||||
extension XCUIApplication {
|
||||
func currentDemoReaderState() -> DemoReaderState? {
|
||||
let state = staticTexts[IDs.demoReaderState]
|
||||
guard state.exists else { return nil }
|
||||
return DemoReaderState(rawValue: state.label)
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
func waitForDemoReaderState(
|
||||
timeout: TimeInterval = 8,
|
||||
description: String,
|
||||
where predicate: (DemoReaderState) -> Bool
|
||||
) -> DemoReaderState {
|
||||
let stateElement = staticTexts[IDs.demoReaderState]
|
||||
let deadline = Date().addingTimeInterval(timeout)
|
||||
var lastState = DemoReaderState(rawValue: "<missing>")
|
||||
|
||||
while Date() < deadline {
|
||||
if stateElement.exists {
|
||||
lastState = DemoReaderState(rawValue: stateElement.label)
|
||||
if predicate(lastState) {
|
||||
return lastState
|
||||
}
|
||||
}
|
||||
RunLoop.current.run(until: Date().addingTimeInterval(0.1))
|
||||
}
|
||||
|
||||
XCTFail("阅读器状态未满足条件: \(description),当前:\(lastState.rawValue)")
|
||||
return lastState
|
||||
}
|
||||
}
|
||||
@@ -25,12 +25,15 @@ extension XCUIApplication {
|
||||
func waitForReader(timeout: TimeInterval = 12) -> XCUIElement {
|
||||
let state = staticTexts[IDs.demoReaderState]
|
||||
XCTAssertTrue(state.waitForExistence(timeout: timeout), "阅读器状态标签未出现")
|
||||
XCTAssertTrue(state.label.contains("reader=opened"), "阅读器未进入 opened 状态:\(state.label)")
|
||||
let parsed = DemoReaderState(rawValue: state.label)
|
||||
XCTAssertTrue(parsed.isOpened, "阅读器未进入 opened 状态:\(state.label)")
|
||||
return state
|
||||
}
|
||||
|
||||
func waitForReaderPage(_ pageNumber: Int, timeout: TimeInterval = 12) {
|
||||
waitForReaderState(containing: "page=\(pageNumber)", timeout: timeout)
|
||||
_ = waitForDemoReaderState(timeout: timeout, description: "page=\(pageNumber)") { state in
|
||||
state.page == pageNumber
|
||||
}
|
||||
}
|
||||
|
||||
func showReaderChromeIfNeeded() {
|
||||
|
||||
Reference in New Issue
Block a user