import XCTest /// 固定布局(pre-paginated)书籍的旋转回归: /// 每次旋转会使预加载缓存签名失效并批量丢弃 WKWebView 页面, /// 历史上曾因 script message handler 保留环导致 WebView 泄漏累积, /// 多次旋转后在 CA commit 阶段 abort(CA::Render::Encoder::grow)。 final class FixedLayoutRotationTests: XCTestCase { private let app = XCUIApplication() override func setUpWithError() throws { continueAfterFailure = false XCUIDevice.shared.orientation = .portrait } override func tearDownWithError() throws { XCUIDevice.shared.orientation = .portrait } func testFixedLayoutBookSurvivesRepeatedRotation() { app.launchAndOpenSampleBook(bookTitleQuery: "熊爷爷") app.waitForReader() for _ in 0..<3 { XCUIDevice.shared.orientation = .landscapeLeft RunLoop.current.run(until: Date().addingTimeInterval(2)) let landscapeState = app.waitForDemoReaderState( timeout: 5, description: "固定版式横屏保持单页" ) { $0.pagesPerScreen == 1 } XCTAssertEqual(landscapeState.pagesPerScreen, 1, "交互型固定版式书横屏不应启用双页") XCUIDevice.shared.orientation = .portrait RunLoop.current.run(until: Date().addingTimeInterval(2)) } XCTAssertEqual(app.state, .runningForeground, "反复旋转后 app 已退出(疑似崩溃)") app.waitForReader() } /// PDF 手机横屏必须在旋转完成的第一轮布局中立即成为全宽单页。 /// 过去 flow layout 继续使用竖屏 itemSize,导致横屏首帧并排露出两页, /// 只有点击触发下一轮布局后才恢复。 func testPDFLandscapeImmediatelyUsesSingleFullWidthPage() { app.launchAndOpenSampleBook( bookTitleQuery: "英汉大词典", displayType: "pagecurl" ) app.waitForReader() XCUIDevice.shared.orientation = .landscapeLeft let content = app.otherElements[IDs.readerContent].firstMatch XCTAssertTrue(content.waitForExistence(timeout: 8), "PDF 阅读区域未出现") let deadline = Date().addingTimeInterval(8) while Date() < deadline { let windowWidth = app.windows.firstMatch.frame.width if windowWidth > 0, abs(content.frame.width - windowWidth) <= 1 { break } RunLoop.current.run(until: Date().addingTimeInterval(0.1)) } // 等待旋转动画和 PDF 页异步配置完成,避免只验证到过渡中的短暂正确状态。 RunLoop.current.run(until: Date().addingTimeInterval(1.5)) let windowFrame = app.windows.firstMatch.frame XCTAssertGreaterThan(windowFrame.width, windowFrame.height, "设备没有进入横屏") XCTAssertEqual(content.frame.width, windowFrame.width, accuracy: 1, "横屏首帧仍在使用竖屏页面宽度") // 横屏单页按宽度适配:cell 高度是纸张按全宽换算的实际高度,竖版纸张 // 高于一屏,纵向超出部分由阅读器连续竖滑承接,因此不再等于窗口高度。 XCTAssertGreaterThanOrEqual( content.frame.height, windowFrame.height - 1, "横屏单页高度应不小于一屏(宽度适配后的纸张高度)" ) } func testFixedLayoutCachedPageCanReturnAfterPageTurn() { app.launchAndOpenSampleBook(bookTitleQuery: "熊爷爷") app.waitForReader() let content = app.otherElements[IDs.readerContent] XCTAssertTrue(content.waitForExistence(timeout: 5), "固定版式阅读区域未出现") let initialState = app.waitForDemoReaderState(timeout: 5, description: "固定版式初始页") { $0.page != nil } let initialPage = initialState.page ?? 0 content.swipeLeft() let nextState = app.waitForDemoReaderState(timeout: 8, description: "固定版式向后翻页") { state in guard let page = state.page else { return false } return page != initialPage } content.swipeRight() let returnedState = app.waitForDemoReaderState(timeout: 8, description: "固定版式返回缓存页") { $0.page == initialPage } XCTAssertNotEqual(nextState.page, returnedState.page, "固定版式返回缓存页后页码未恢复") XCTAssertTrue(content.exists, "返回缓存页后阅读区域不应消失") } }