test: 增强 UI 测试基础设施与阅读器自动化测试

- 新增 BookmarkTests/PageNavigationTests/TableOfContentsTests 等阅读器功能测试
- 扩展 AccessibilityIdentifiers 支持更多 UI 元素定位
- XCUIApplication+Launch: 增加启动参数支持(reset state、自定义书籍)
- ReaderAnnotationTests: 完善标注测试覆盖
- ViewController: 支持 --demo-reset-state 参数清理持久化状态
- 新增凡人修仙传测试样本

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
shenlei
2026-06-02 21:17:04 +08:00
co-authored by Claude Opus 4.7
parent c76beed03c
commit 9801af05d3
11 changed files with 907 additions and 14 deletions
@@ -12,15 +12,30 @@ enum IDs {
static let readerBottomToolbar = "epub.reader.bottomToolbar"
static let readerContent = "epub.reader.content"
static let readerPaging = "epub.reader.paging"
static let readerBookmark = "epub.reader.bookmark"
static let readerToc = "epub.reader.toc"
static let readerBookmarks = "epub.reader.bookmarks"
static let readerHighlights = "epub.reader.highlights"
static let readerAddHighlight = "epub.reader.add-highlight"
static let readerSettings = "epub.reader.settings"
static let readerTocPanel = "epub.reader.toc.panel"
static let readerTocTable = "epub.reader.toc.table"
static let readerTocNavBar = "epub.reader.toc.navbar"
static let readerBookmarksPanel = "epub.reader.bookmarks.panel"
static let readerBookmarksTable = "epub.reader.bookmarks.table"
static let readerBookmarksNavBar = "epub.reader.bookmarks.navbar"
static let readerSelectionText = "epub.reader.selection.text"
static let readerSelectionHighlight = "epub.reader.selection.高亮"
static let settingsScroll = "epub.reader.settings.scroll"
static let settingsBrightness = "epub.reader.settings.brightness"
static let settingsFontIncrease = "epub.reader.settings.font.increase"
static let settingsFontDecrease = "epub.reader.settings.font.decrease"
static let settingsFontValue = "epub.reader.settings.font.value"
static let settingsFontChoice = "epub.reader.settings.font.choice"
static let settingsLineHeight = "epub.reader.settings.lineHeight"
static let settingsColumns = "epub.reader.settings.columns"
static let settingsDisplayType = "epub.reader.settings.displayType"
static let settingsDone = "epub.reader.settings.done"
static func settingsTheme(_ index: Int) -> String { "epub.reader.settings.theme.\(index)" }
}
@@ -1,8 +1,16 @@
import XCTest
extension XCUIApplication {
func launchAndOpenSampleBook(displayType: String? = nil, pageNumber: Int? = nil) {
var args = ["--demo-book-title", "回归验证样本"]
func launchAndOpenSampleBook(
bookTitleQuery: String = "回归验证样本",
displayType: String? = nil,
pageNumber: Int? = nil,
resetsReaderState: Bool = true
) {
var args = ["--demo-book-title", bookTitleQuery]
if resetsReaderState {
args.append("--demo-reset-state")
}
if let displayType {
args += ["--demo-display-type", displayType]
}
@@ -21,6 +29,10 @@ extension XCUIApplication {
return state
}
func waitForReaderPage(_ pageNumber: Int, timeout: TimeInterval = 12) {
waitForReaderState(containing: "page=\(pageNumber)", timeout: timeout)
}
func showReaderChromeIfNeeded() {
if buttons[IDs.readerBack].exists && buttons[IDs.readerSettings].exists {
return
@@ -34,18 +46,33 @@ extension XCUIApplication {
}
}
func hideReaderChromeIfNeeded() {
for _ in 0..<3 {
guard buttons[IDs.readerBack].exists else { return }
let content = otherElements[IDs.readerContent]
if content.waitForExistence(timeout: 2) {
content.coordinate(withNormalizedOffset: CGVector(dx: 0.5, dy: 0.6)).tap()
}
RunLoop.current.run(until: Date().addingTimeInterval(0.5))
}
}
func waitForReaderState(containing expectedText: String, timeout: TimeInterval = 8) {
let state = staticTexts[IDs.demoReaderState]
let deadline = Date().addingTimeInterval(timeout)
var lastLabel = "<missing>"
while Date() < deadline {
if state.exists && state.label.contains(expectedText) {
return
if state.exists {
lastLabel = state.label
if lastLabel.contains(expectedText) {
return
}
}
RunLoop.current.run(until: Date().addingTimeInterval(0.1))
}
let currentLabel = state.exists ? state.label : "<missing>"
XCTFail("阅读器状态未包含 \(expectedText),当前:\(currentLabel)")
XCTFail("阅读器状态未包含 \(expectedText),当前:\(lastLabel)")
}
}