mirror of
https://github.com/Aminark/legado-ios.git
synced 2026-10-20 19:21:22 +08:00
188 lines
8.7 KiB
Swift
188 lines
8.7 KiB
Swift
import XCTest
|
|
|
|
@MainActor
|
|
final class LiveReadingFlowUITests: XCTestCase {
|
|
private let app = XCUIApplication()
|
|
|
|
override func setUp() {
|
|
super.setUp()
|
|
// Xcode's runtime-issue callback can crash on Network.framework's log
|
|
// quarantine with thousands of failing hosts. Keep server testing active.
|
|
app.launchEnvironment["OS_ACTIVITY_MODE"] = "disable"
|
|
}
|
|
|
|
func testSearchLatencyAndCachedQuery() throws {
|
|
guard ProcessInfo.processInfo.environment["LEGADO_SEARCH_UI_BENCHMARK"] == "1" else {
|
|
throw XCTSkip("Use the LegadoSearchPerformanceUITests scheme with the imported catalog")
|
|
}
|
|
continueAfterFailure = false
|
|
app.launchArguments = ["-uiDemoTab", "search", "-uiAutomationSearch", "极道天魔", "-themeMode", "1"]
|
|
let started = Date()
|
|
app.launch()
|
|
let exact = app.buttons.matching(NSPredicate(format: "label BEGINSWITH %@", "极道天魔, 滚开")).firstMatch
|
|
XCTAssertTrue(exact.waitForExistence(timeout: 90))
|
|
print("[SearchPerformance] launch_to_exact_seconds=\(Date().timeIntervalSince(started))")
|
|
attach("all-sources-search-latency")
|
|
app.buttons["搜索选项"].tap()
|
|
if app.buttons["停止搜索"].exists { app.buttons["停止搜索"].tap() }
|
|
let search = app.searchFields["书名或作者"]
|
|
search.tap()
|
|
search.typeText(String(repeating: XCUIKeyboardKey.delete.rawValue, count: 4) + "极道天")
|
|
let repeated = Date()
|
|
search.typeText("\n")
|
|
XCTAssertTrue(app.staticTexts["“极道天”"].waitForExistence(timeout: 10))
|
|
XCTAssertTrue(exact.exists)
|
|
print("[SearchPerformance] cached_query_to_result_seconds=\(Date().timeIntervalSince(repeated))")
|
|
attach("cached-search-result")
|
|
exact.tap()
|
|
XCTAssertTrue(app.navigationBars["书籍详情"].waitForExistence(timeout: 10))
|
|
attach("detail-from-running-search")
|
|
}
|
|
|
|
func testImportSearchDetailReadSwitchAndResume() throws {
|
|
guard ProcessInfo.processInfo.environment["LEGADO_LIVE_UI_TESTS"] == "1" else {
|
|
throw XCTSkip("Use the LegadoLiveReadingUITests scheme for real-server testing")
|
|
}
|
|
continueAfterFailure = false
|
|
defer {
|
|
if (testRun?.failureCount ?? 0) > 0 {
|
|
attach("failure")
|
|
let tree = XCTAttachment(string: app.debugDescription)
|
|
tree.name = "failure-accessibility-tree"
|
|
tree.lifetime = .keepAlways
|
|
add(tree)
|
|
}
|
|
}
|
|
app.launchArguments = ["-uiDemoTab", "search", "-uiAutomationSearch", "极道天魔", "-pageAnim", "0", "-themeMode", "1"]
|
|
app.launch()
|
|
XCTAssertTrue(app.navigationBars["搜索"].waitForExistence(timeout: 30))
|
|
app.buttons["搜索选项"].tap()
|
|
app.buttons["书源管理"].tap()
|
|
XCTAssertTrue(app.navigationBars["书源"].waitForExistence(timeout: 10))
|
|
app.buttons["更多"].firstMatch.tap()
|
|
app.buttons["URL 或粘贴 JSON"].tap()
|
|
let url = app.textFields["https://example.com/sources.json"]
|
|
XCTAssertTrue(url.waitForExistence(timeout: 10))
|
|
url.tap()
|
|
url.typeText("https://legado.aoaostar.com/sources/b778fe6b.json")
|
|
app.navigationBars["导入书源"].buttons["导入"].tap()
|
|
XCTAssertTrue(app.navigationBars["书源导入预检"].waitForExistence(timeout: 120))
|
|
app.navigationBars["书源导入预检"].buttons["选择"].tap()
|
|
app.buttons["全选"].tap()
|
|
attach("01-import-preview")
|
|
XCTAssertTrue(app.navigationBars["书源导入预检"].buttons["导入"].isEnabled)
|
|
app.navigationBars["书源导入预检"].buttons["导入"].tap()
|
|
XCTAssertTrue(app.alerts["书源导入完成"].waitForExistence(timeout: 90))
|
|
attach("02-import-completed")
|
|
app.alerts.buttons["好"].tap()
|
|
app.navigationBars["书源"].buttons.element(boundBy: 0).tap()
|
|
selectSearchSource("DragonQuestQBall")
|
|
let search = app.searchFields["书名或作者"]
|
|
search.tap()
|
|
search.typeText("\n")
|
|
let title = app.buttons.matching(NSPredicate(format: "label BEGINSWITH %@", "极道天魔, 滚开")).firstMatch
|
|
XCTAssertTrue(title.waitForExistence(timeout: 90))
|
|
attach("03-search")
|
|
title.tap()
|
|
XCTAssertTrue(app.navigationBars["书籍详情"].waitForExistence(timeout: 10))
|
|
let start = app.buttons.matching(NSPredicate(format: "label IN %@", ["开始阅读", "继续阅读"])).firstMatch
|
|
waitUntil("Readable directory", timeout: 90) { start.exists && start.isEnabled }
|
|
XCTAssertTrue(app.staticTexts["滚开"].exists)
|
|
attach("04-detail")
|
|
if app.buttons["继续阅读"].exists {
|
|
let firstChapter = app.staticTexts.matching(NSPredicate(format: "label CONTAINS %@", "道法无情")).firstMatch
|
|
XCTAssertTrue(firstChapter.exists)
|
|
firstChapter.tap()
|
|
} else {
|
|
start.tap()
|
|
}
|
|
waitForBody(containing: "道法无情")
|
|
attach("05-reading")
|
|
showReaderMenu()
|
|
let currentSourceName = app.buttons["reader.changeSource"].value as? String
|
|
let replacementID = currentSourceName == "💰QQ阅读[浏览器]" ? "DragonQuestQBall" : "https://bookshelf.html5.qq.com/"
|
|
let replacementName = currentSourceName == "💰QQ阅读[浏览器]" ? "●QB男生<qq♛" : "💰QQ阅读[浏览器]"
|
|
app.buttons["下一章"].tap()
|
|
waitForBody(containing: "作弊器")
|
|
attach("06-next-chapter")
|
|
showReaderMenu()
|
|
app.buttons["换源"].tap()
|
|
XCTAssertTrue(app.navigationBars["为《极道天魔》换源"].waitForExistence(timeout: 10))
|
|
let replacement = app.buttons["sourceSwitch.candidate." + replacementID]
|
|
XCTAssertTrue(replacement.waitForExistence(timeout: 240))
|
|
attach("07-source-candidates")
|
|
replacement.tap()
|
|
waitUntil("Source switch sheet dismissed", timeout: 90) {
|
|
!self.app.navigationBars["为《极道天魔》换源"].exists
|
|
}
|
|
waitForBody(containing: "作弊器")
|
|
showReaderMenu()
|
|
waitUntil("Replacement source active", timeout: 20) {
|
|
self.app.buttons["reader.changeSource"].value as? String == replacementName
|
|
}
|
|
attach("08-switched-reading")
|
|
app.buttons["返回"].tap()
|
|
app.terminate()
|
|
app.launchArguments = ["-pageAnim", "0", "-themeMode", "1"]
|
|
app.launch()
|
|
let resume = app.buttons["bookshelf.continue"]
|
|
XCTAssertTrue(resume.waitForExistence(timeout: 30))
|
|
resume.tap()
|
|
waitForBody(containing: "作弊器")
|
|
showReaderMenu()
|
|
XCTAssertEqual(app.buttons["reader.changeSource"].value as? String, replacementName)
|
|
attach("09-resumed-reading")
|
|
}
|
|
|
|
private func selectSearchSource(_ sourceID: String) {
|
|
app.buttons["搜索选项"].tap()
|
|
if app.buttons["停止搜索"].exists {
|
|
app.buttons["停止搜索"].tap()
|
|
app.buttons["搜索选项"].tap()
|
|
}
|
|
app.buttons["分组或书源"].tap()
|
|
app.segmentedControls.buttons["书源"].tap()
|
|
let filter = app.searchFields["筛选书源"]
|
|
filter.tap()
|
|
filter.typeText(sourceID)
|
|
let source = app.staticTexts[sourceID].firstMatch
|
|
XCTAssertTrue(source.waitForExistence(timeout: 10))
|
|
source.tap()
|
|
app.buttons["确定"].tap()
|
|
}
|
|
|
|
private func waitForBody(containing chapter: String) {
|
|
let body = app.textViews["reader.text"]
|
|
waitUntil("Body of \(chapter)", timeout: 90) {
|
|
guard body.exists, body.isHittable else { return false }
|
|
let value = body.value as? String ?? ""
|
|
return value.contains(chapter) && value.count > 500
|
|
}
|
|
let value = body.value as? String ?? ""
|
|
XCTAssertGreaterThan(value.components(separatedBy: "\n").count, 5)
|
|
XCTAssertFalse(value.contains("<p>"))
|
|
XCTAssertFalse(value.contains(#"\r\n"#))
|
|
}
|
|
|
|
private func showReaderMenu() {
|
|
if !app.buttons["换源"].exists {
|
|
app.textViews["reader.text"].coordinate(withNormalizedOffset: CGVector(dx: 0.5, dy: 0.5)).tap()
|
|
}
|
|
XCTAssertTrue(app.buttons["换源"].waitForExistence(timeout: 10))
|
|
}
|
|
|
|
private func waitUntil(_ name: String, timeout: TimeInterval, condition: @escaping () -> Bool) {
|
|
let ready = expectation(for: NSPredicate { _, _ in condition() }, evaluatedWith: nil)
|
|
ready.expectationDescription = name
|
|
wait(for: [ready], timeout: timeout)
|
|
}
|
|
|
|
private func attach(_ name: String) {
|
|
let screenshot = XCTAttachment(screenshot: app.screenshot())
|
|
screenshot.name = name
|
|
screenshot.lifetime = .keepAlways
|
|
add(screenshot)
|
|
}
|
|
|
|
}
|