163 lines
6.7 KiB
Swift
163 lines
6.7 KiB
Swift
import XCTest
|
||
|
||
/// 选区批注动作和高亮持久化测试
|
||
/// 覆盖:批注菜单动作创建高亮、高亮跨重启存活、重排后高亮跳转页码正确
|
||
final class SelectionAnnotateTests: XCTestCase {
|
||
|
||
private let app = XCUIApplication()
|
||
|
||
override func setUpWithError() throws {
|
||
continueAfterFailure = false
|
||
}
|
||
|
||
/// 点击批注菜单项应创建高亮
|
||
func testAnnotateMenuActionCreatesHighlight() throws {
|
||
app.launchAndOpenSampleBook()
|
||
app.waitForReader()
|
||
|
||
// 长按并拖拽选中文本
|
||
let selectionText = app.otherElements[IDs.readerSelectionText].firstMatch
|
||
guard selectionText.waitForExistence(timeout: 5) else {
|
||
throw XCTSkip("文本选区元素不存在")
|
||
}
|
||
|
||
let start = selectionText.coordinate(withNormalizedOffset: CGVector(dx: 0.3, dy: 0.5))
|
||
let end = selectionText.coordinate(withNormalizedOffset: CGVector(dx: 0.6, dy: 0.5))
|
||
start.press(forDuration: 0.8, thenDragTo: end)
|
||
|
||
// 等待选区状态
|
||
Thread.sleep(forTimeInterval: 0.5)
|
||
|
||
// 点击批注菜单项
|
||
let annotateItem = app.menuItems["注释"]
|
||
guard annotateItem.waitForExistence(timeout: 3) else {
|
||
throw XCTSkip("批注菜单项不存在")
|
||
}
|
||
annotateItem.tap()
|
||
|
||
// 验证高亮数量变为 1
|
||
app.waitForDemoReaderState(timeout: 5, description: "highlights=1") { $0.highlights == 1 }
|
||
}
|
||
|
||
/// 高亮跨重启存活
|
||
func testHighlightSurvivesRestart() throws {
|
||
app.launchAndOpenSampleBook()
|
||
app.waitForReader()
|
||
|
||
// 创建高亮
|
||
let selectionText = app.otherElements[IDs.readerSelectionText].firstMatch
|
||
guard selectionText.waitForExistence(timeout: 5) else {
|
||
throw XCTSkip("文本选区元素不存在")
|
||
}
|
||
|
||
let start = selectionText.coordinate(withNormalizedOffset: CGVector(dx: 0.3, dy: 0.5))
|
||
let end = selectionText.coordinate(withNormalizedOffset: CGVector(dx: 0.6, dy: 0.5))
|
||
start.press(forDuration: 0.8, thenDragTo: end)
|
||
|
||
Thread.sleep(forTimeInterval: 0.5)
|
||
|
||
let annotateItem = app.menuItems["注释"]
|
||
guard annotateItem.waitForExistence(timeout: 3) else {
|
||
throw XCTSkip("批注菜单项不存在")
|
||
}
|
||
annotateItem.tap()
|
||
|
||
// 等待高亮创建完成
|
||
app.waitForDemoReaderState(timeout: 5, description: "highlights=1") { $0.highlights == 1 }
|
||
|
||
// 关闭阅读器
|
||
app.buttons[IDs.readerBack].tap()
|
||
|
||
// 重新打开(不重置状态)
|
||
app.launchAndOpenSampleBook(resetsReaderState: false)
|
||
app.waitForReader()
|
||
|
||
// 验证高亮仍然存在
|
||
app.waitForDemoReaderState(timeout: 8, description: "highlights=1 after restart") { $0.highlights == 1 }
|
||
}
|
||
|
||
/// 重排后高亮跳转页码应正确(多页覆盖)
|
||
/// 回归:chapterOffset(for:) 曾优先使用 row/column 查表而非存储的 chapterOffset,
|
||
/// 导致重新排版后跳转到错误的页面
|
||
func testHighlightNavigationAfterRepagination() throws {
|
||
// 测试多页:在不同页面创建高亮,改变字体后跳转,验证页码正确
|
||
let testPages = [2, 3, 5]
|
||
|
||
for page in testPages {
|
||
// 每轮重新启动以确保干净状态
|
||
app.launchAndOpenSampleBook(pageNumber: page)
|
||
app.waitForReader()
|
||
|
||
let stateBefore = app.waitForDemoReaderState(timeout: 5, description: "at page \(page)") { $0.page == page }
|
||
XCTAssertEqual(stateBefore.page, page, "应成功打开第 \(page) 页")
|
||
|
||
// 创建高亮
|
||
let selectionText = app.otherElements[IDs.readerSelectionText].firstMatch
|
||
guard selectionText.waitForExistence(timeout: 5) else {
|
||
throw XCTSkip("文本选区元素不存在")
|
||
}
|
||
let start = selectionText.coordinate(withNormalizedOffset: CGVector(dx: 0.2, dy: 0.5))
|
||
let end = selectionText.coordinate(withNormalizedOffset: CGVector(dx: 0.5, dy: 0.5))
|
||
start.press(forDuration: 0.8, thenDragTo: end)
|
||
Thread.sleep(forTimeInterval: 0.5)
|
||
|
||
let highlightItem = app.menuItems["划线"]
|
||
guard highlightItem.waitForExistence(timeout: 3) else {
|
||
throw XCTSkip("高亮菜单项不存在")
|
||
}
|
||
highlightItem.tap()
|
||
app.waitForDemoReaderState(timeout: 5, description: "highlights=1 on page \(page)") { $0.highlights == 1 }
|
||
|
||
// 改变字体大小触发重新排版
|
||
app.showReaderChromeIfNeeded()
|
||
let settingsButton = app.buttons[IDs.readerSettings]
|
||
XCTAssertTrue(settingsButton.waitForExistence(timeout: 3), "设置按钮应存在")
|
||
settingsButton.tap()
|
||
|
||
let fontIncrease = app.buttons[IDs.settingsFontIncrease]
|
||
XCTAssertTrue(fontIncrease.waitForExistence(timeout: 3), "字号增大按钮应存在")
|
||
fontIncrease.tap()
|
||
fontIncrease.tap()
|
||
Thread.sleep(forTimeInterval: 0.5)
|
||
|
||
let doneButton = app.buttons[IDs.settingsDone]
|
||
if doneButton.waitForExistence(timeout: 3) {
|
||
doneButton.tap()
|
||
}
|
||
Thread.sleep(forTimeInterval: 1)
|
||
|
||
// 翻到其他页
|
||
let content = app.otherElements[IDs.readerContent].firstMatch
|
||
if content.waitForExistence(timeout: 3) {
|
||
for _ in 0..<3 {
|
||
content.swipeLeft()
|
||
Thread.sleep(forTimeInterval: 0.3)
|
||
}
|
||
Thread.sleep(forTimeInterval: 0.5)
|
||
}
|
||
|
||
// 通过高亮面板跳转回高亮位置
|
||
app.showReaderChromeIfNeeded()
|
||
let highlightsButton = app.buttons[IDs.readerHighlights]
|
||
XCTAssertTrue(highlightsButton.waitForExistence(timeout: 3), "高亮列表按钮应存在")
|
||
highlightsButton.tap()
|
||
|
||
let highlightsTable = app.tables[IDs.readerHighlightsTable]
|
||
XCTAssertTrue(highlightsTable.waitForExistence(timeout: 5), "高亮列表应出现")
|
||
|
||
let firstCell = highlightsTable.cells.firstMatch
|
||
XCTAssertTrue(firstCell.waitForExistence(timeout: 3), "高亮列表应有至少一个条目")
|
||
firstCell.tap()
|
||
|
||
// 等待跳转完成
|
||
Thread.sleep(forTimeInterval: 1)
|
||
|
||
// 验证跳转后的页码应为创建高亮时的页码
|
||
let stateAfter = app.waitForDemoReaderState(timeout: 5, description: "back at page \(page) after repagination") {
|
||
$0.page == page
|
||
}
|
||
XCTAssertEqual(stateAfter.page, page, "重排后高亮跳转应回到第 \(page) 页,实际:\(stateAfter.page ?? -1)")
|
||
}
|
||
}
|
||
}
|