test(m9): drive scroller + state-driven triggers in UI suite (#135) #136

Merged
gronod merged 1 commits from feat/135-uitest-scroller-trigger into milestone/m9-monterey 2026-09-12 14:04:31 +01:00
2 changed files with 38 additions and 24 deletions
+20 -9
View File
@@ -198,15 +198,26 @@ final class Milestone3UITests: XCTestCase {
} }
XCTAssertTrue(app.buttons["btnPrintAll"].isEnabled) XCTAssertTrue(app.buttons["btnPrintAll"].isEnabled)
// The gallery cell's Print button can sit at the window's bottom // The gallery cell's Print button sits at the window's bottom
// edge where XCUI's automatic scroll-to-visible is inert (#132) // edge where synthesized scroll-wheel events are inert on the
// scroll stage-2 explicitly until the hit point is onscreen. // LazyVGrid (#132). Drag the NSScrollView's vertical AXScrollBar
let printPage = app.buttons["btnPrintPage-0"] // thumb instead a real scroll that re-renders the cell onscreen.
let stage2 = app.scrollViews["stage-2"] var printPage = app.buttons["btnPrintPage-0"]
let scrollDeadline = Date().addingTimeInterval(10) let scrollDeadline = Date().addingTimeInterval(15)
while Date() < scrollDeadline, !printPage.isHittable { while !printPage.isHittable, Date() < scrollDeadline {
stage2.scroll(byDeltaX: 0, deltaY: -1) let scroller = app.scrollBars.allElementsBoundByIndex
RunLoop.current.run(until: Date().addingTimeInterval(0.2)) .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 { if printPage.isHittable {
printPage.click() printPage.click()
+18 -15
View File
@@ -111,16 +111,9 @@ final class Milestone4UITests: XCTestCase {
} }
app.buttons["btnCalibrate"].click() app.buttons["btnCalibrate"].click()
// Trigger strip A. // Trigger each strip until all are read Done & Save appears.
_ = waitFor("btnTrigger", timeout: 20) driveStripsUntilDone()
app.buttons["btnTrigger"].click() XCTAssertTrue(element("btnDoneRead").exists)
// Trigger strip B.
_ = waitFor("btnTrigger", timeout: 20)
app.buttons["btnTrigger"].click()
// All strips read Done & Save appears.
_ = waitFor("btnDoneRead", timeout: 20)
app.buttons["btnDoneRead"].firstMatch.click() app.buttons["btnDoneRead"].firstMatch.click()
// Averaging panel appears with one pass snapshot. // Averaging panel appears with one pass snapshot.
@@ -186,11 +179,21 @@ final class Milestone4UITests: XCTestCase {
start.click() start.click()
_ = waitFor("btnCalibrate", timeout: 25) _ = waitFor("btnCalibrate", timeout: 25)
app.buttons["btnCalibrate"].click() app.buttons["btnCalibrate"].click()
_ = waitFor("btnTrigger", timeout: 20) driveStripsUntilDone()
app.buttons["btnTrigger"].click() XCTAssertTrue(element("btnDoneRead").exists)
_ = waitFor("btnTrigger", timeout: 20)
app.buttons["btnTrigger"].click()
_ = waitFor("btnDoneRead", timeout: 20)
app.buttons["btnDoneRead"].firstMatch.click() 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))
}
}
} }