import XCTest /// 画笔面板打开时,工具未选中应保留手机横屏单页的连续竖滑; /// 选中任一绘图工具后,单指手势只能用于绘制,不能触发阅读器滚动。 final class PDFDrawingModeTests: XCTestCase { private let app = XCUIApplication() override func setUpWithError() throws { continueAfterFailure = false XCUIDevice.shared.orientation = .portrait } override func tearDownWithError() throws { XCUIDevice.shared.orientation = .portrait } func testLandscapeSinglePageScrollsOnlyWhenDrawingToolIsDeselected() { app.launchAndOpenSampleBook(bookTitleQuery: "英汉大词典", displayType: "pagecurl") app.waitForReader() XCUIDevice.shared.orientation = .landscapeLeft let content = app.otherElements[IDs.readerContent].firstMatch XCTAssertTrue(content.waitForExistence(timeout: 8), "PDF 横屏阅读页面未出现") XCTAssertTrue(waitForLandscapeLayout(), "PDF 未进入横屏单页布局") app.showReaderChromeIfNeeded(requiredButtonIDs: [IDs.pdfDrawing]) let drawing = app.buttons[IDs.pdfDrawing] XCTAssertTrue(drawing.waitForExistence(timeout: 5), "画笔入口未出现") drawing.tap() let pen = app.buttons[IDs.pdfDrawingPen] let eraser = app.buttons[IDs.pdfDrawingEraser] XCTAssertTrue(pen.waitForExistence(timeout: 5), "画笔工具未出现") XCTAssertTrue(eraser.exists, "橡皮擦工具未出现") let firstScrollablePage = app.currentDemoReaderState()?.page XCTAssertNotNil(firstScrollablePage, "未取得初始 PDF 页码") guard let firstScrollablePage else { return } XCTAssertTrue( scrollToAnotherPage(content, from: firstScrollablePage), "未选中工具时,手机横屏单页无法连续竖滑" ) let lockedPage = app.currentDemoReaderState()?.page XCTAssertNotNil(lockedPage, "滚动后未取得 PDF 页码") guard let lockedPage else { return } pen.tap() assertPageRemains(lockedPage, afterSwiping: content, message: "选中画笔后仍触发了页面滚动") eraser.tap() assertPageRemains(lockedPage, afterSwiping: content, message: "切换至橡皮擦后仍触发了页面滚动") eraser.tap() XCTAssertTrue( scrollToAnotherPage(content, from: lockedPage), "取消橡皮擦选中后,手机横屏单页没有恢复连续竖滑" ) } /// 放大状态下同一规则:未选中工具单指拖动画面,选中画笔后单指只用于绘制。 func testZoomedPageDragsOnlyWhenDrawingToolIsDeselected() { app.launchAndOpenSampleBook(bookTitleQuery: "英汉大词典", displayType: "pagecurl") app.waitForReader() let content = app.otherElements[IDs.readerContent].firstMatch XCTAssertTrue(content.waitForExistence(timeout: 8), "PDF 阅读页面未出现") app.showReaderChromeIfNeeded(requiredButtonIDs: [IDs.pdfDrawing]) let drawing = app.buttons[IDs.pdfDrawing] XCTAssertTrue(drawing.waitForExistence(timeout: 5), "画笔入口未出现") drawing.tap() let pen = app.buttons[IDs.pdfDrawingPen] XCTAssertTrue(pen.waitForExistence(timeout: 5), "画笔工具未出现") // 未选中工具 → 双指放大 content.pinch(withScale: 2.5, velocity: 1) RunLoop.current.run(until: Date().addingTimeInterval(1.0)) let zoomedFrame = content.frame XCTAssertGreaterThan(zoomedFrame.width, app.windows.firstMatch.frame.width * 1.2, "画笔面板打开时无法双指放大页面") // 未选中工具 → 单指拖动应移动页面 let start = content.coordinate(withNormalizedOffset: CGVector(dx: 0.5, dy: 0.5)) let end = content.coordinate(withNormalizedOffset: CGVector(dx: 0.3, dy: 0.3)) start.press(forDuration: 0.05, thenDragTo: end) RunLoop.current.run(until: Date().addingTimeInterval(0.8)) let draggedFrame = content.frame XCTAssertNotEqual(draggedFrame.origin, zoomedFrame.origin, "未选中工具时,放大页面无法单指拖动") // 选中画笔 → 单指应绘制而非拖动 pen.tap() let frameBeforeStroke = content.frame let strokeStart = content.coordinate(withNormalizedOffset: CGVector(dx: 0.45, dy: 0.45)) let strokeEnd = content.coordinate(withNormalizedOffset: CGVector(dx: 0.6, dy: 0.6)) strokeStart.press(forDuration: 0.05, thenDragTo: strokeEnd) RunLoop.current.run(until: Date().addingTimeInterval(0.8)) XCTAssertEqual(content.frame.origin, frameBeforeStroke.origin, "选中画笔后,单指仍拖动了放大页面而不是绘制") } private func waitForLandscapeLayout() -> Bool { let deadline = Date().addingTimeInterval(8) while Date() < deadline { let frame = app.windows.firstMatch.frame if frame.width > frame.height { return true } RunLoop.current.run(until: Date().addingTimeInterval(0.1)) } return false } private func scrollToAnotherPage(_ content: XCUIElement, from page: Int) -> Bool { let window = app.windows.firstMatch for _ in 0..<5 { // 从屏幕上半部拖动,避开底部画笔工具条区域。 let start = window.coordinate(withNormalizedOffset: CGVector(dx: 0.5, dy: 0.35)) let end = window.coordinate(withNormalizedOffset: CGVector(dx: 0.5, dy: 0.05)) start.press(forDuration: 0.05, thenDragTo: end, withVelocity: .fast, thenHoldForDuration: 0) let deadline = Date().addingTimeInterval(1.5) while Date() < deadline { if let current = app.currentDemoReaderState()?.page, current != page { return true } RunLoop.current.run(until: Date().addingTimeInterval(0.1)) } } return false } private func assertPageRemains(_ page: Int, afterSwiping content: XCUIElement, message: String) { for _ in 0..<3 { content.swipeUp() } RunLoop.current.run(until: Date().addingTimeInterval(0.8)) XCTAssertEqual(app.currentDemoReaderState()?.page, page, message) } }