diff --git a/Tests/ICCeryUITests/Milestone3UITests.swift b/Tests/ICCeryUITests/Milestone3UITests.swift index b95584b..8ea1f86 100644 --- a/Tests/ICCeryUITests/Milestone3UITests.swift +++ b/Tests/ICCeryUITests/Milestone3UITests.swift @@ -198,15 +198,26 @@ final class Milestone3UITests: XCTestCase { } XCTAssertTrue(app.buttons["btnPrintAll"].isEnabled) - // The gallery cell's Print button can sit at the window's bottom - // edge where XCUI's automatic scroll-to-visible is inert (#132) - // — scroll stage-2 explicitly until the hit point is onscreen. - let printPage = app.buttons["btnPrintPage-0"] - let stage2 = app.scrollViews["stage-2"] - let scrollDeadline = Date().addingTimeInterval(10) - while Date() < scrollDeadline, !printPage.isHittable { - stage2.scroll(byDeltaX: 0, deltaY: -1) - RunLoop.current.run(until: Date().addingTimeInterval(0.2)) + // The gallery cell's Print button sits at the window's bottom + // edge where synthesized scroll-wheel events are inert on the + // LazyVGrid (#132). Drag the NSScrollView's vertical AXScrollBar + // thumb instead — a real scroll that re-renders the cell onscreen. + var printPage = app.buttons["btnPrintPage-0"] + let scrollDeadline = Date().addingTimeInterval(15) + while !printPage.isHittable, Date() < scrollDeadline { + let scroller = app.scrollBars.allElementsBoundByIndex + .first { $0.frame.height > $0.frame.width } + if let scroller { + scroller.coordinate(withNormalizedOffset: + CGVector(dx: 0.5, dy: 0.1)) + .press(forDuration: 0.1, thenDragTo: + scroller.coordinate(withNormalizedOffset: + CGVector(dx: 0.5, dy: 0.6))) + } else { + app.scrollViews["stage-2"].scroll(byDeltaX: 0, deltaY: -1) + } + RunLoop.current.run(until: Date().addingTimeInterval(0.5)) + printPage = app.buttons["btnPrintPage-0"] } if printPage.isHittable { printPage.click() diff --git a/Tests/ICCeryUITests/Milestone4UITests.swift b/Tests/ICCeryUITests/Milestone4UITests.swift index 66fda1e..6d4bc15 100644 --- a/Tests/ICCeryUITests/Milestone4UITests.swift +++ b/Tests/ICCeryUITests/Milestone4UITests.swift @@ -111,16 +111,9 @@ final class Milestone4UITests: XCTestCase { } app.buttons["btnCalibrate"].click() - // Trigger strip A. - _ = waitFor("btnTrigger", timeout: 20) - app.buttons["btnTrigger"].click() - - // Trigger strip B. - _ = waitFor("btnTrigger", timeout: 20) - app.buttons["btnTrigger"].click() - - // All strips read → Done & Save appears. - _ = waitFor("btnDoneRead", timeout: 20) + // Trigger each strip until all are read → Done & Save appears. + driveStripsUntilDone() + XCTAssertTrue(element("btnDoneRead").exists) app.buttons["btnDoneRead"].firstMatch.click() // Averaging panel appears with one pass snapshot. @@ -186,11 +179,21 @@ final class Milestone4UITests: XCTestCase { start.click() _ = waitFor("btnCalibrate", timeout: 25) app.buttons["btnCalibrate"].click() - _ = waitFor("btnTrigger", timeout: 20) - app.buttons["btnTrigger"].click() - _ = waitFor("btnTrigger", timeout: 20) - app.buttons["btnTrigger"].click() - _ = waitFor("btnDoneRead", timeout: 20) + driveStripsUntilDone() + XCTAssertTrue(element("btnDoneRead").exists) app.buttons["btnDoneRead"].firstMatch.click() } + + /// Clicks Trigger for each remaining strip until `btnDoneRead` + /// appears — the button is re-polled each pass so a click that + /// races a state transition isn't lost. + private func driveStripsUntilDone() { + let deadline = Date().addingTimeInterval(40) + while !element("btnDoneRead").exists, Date() < deadline { + if app.buttons["btnTrigger"].waitForExistence(timeout: 10) { + app.buttons["btnTrigger"].click() + } + RunLoop.current.run(until: Date().addingTimeInterval(0.5)) + } + } }