@@ -23,11 +23,8 @@ final class EditorTabReorderUITests: UITestCase {
2323
2424 drag ( tab ( named: before [ 0 ] , in: window) , onto: tab ( named: before [ 2 ] , in: window) )
2525
26- let after = tabLabels ( in: window)
27-
28- XCTAssertNotEqual (
29- before,
30- after,
26+ XCTAssertTrue (
27+ waitForTabOrder ( toChangeFrom: before, in: window) ,
3128 " Dragging the first tab across the strip must change the tab order, was \( before) "
3229 )
3330 }
@@ -53,9 +50,8 @@ final class EditorTabReorderUITests: UITestCase {
5350
5451 drag ( tab ( named: unselected, in: window) , onto: tab ( named: before [ before. count - 1 ] , in: window) )
5552
56- XCTAssertNotEqual (
57- before,
58- tabLabels ( in: window) ,
53+ XCTAssertTrue (
54+ waitForTabOrder ( toChangeFrom: before, in: window) ,
5955 " Dragging an unselected tab must change the tab order, was \( before) "
6056 )
6157 }
@@ -81,9 +77,8 @@ final class EditorTabReorderUITests: UITestCase {
8177
8278 drag ( tab ( named: selected, in: window) , onto: tab ( named: target, in: window) )
8379
84- XCTAssertNotEqual (
85- before,
86- tabLabels ( in: window) ,
80+ XCTAssertTrue (
81+ waitForTabOrder ( toChangeFrom: before, in: window) ,
8782 " Dragging the selected tab must change the tab order, was \( before) "
8883 )
8984 }
@@ -107,11 +102,22 @@ final class EditorTabReorderUITests: UITestCase {
107102
108103 let before = tabLabels ( in: window)
109104
110- drag ( tab ( named: before [ 0 ] , in: window) , onto: tab ( named: before [ 2 ] , in: window) )
105+ // Not `before[0]`: once the track scrolls, the leading tabs stay in the accessibility tree
106+ // at frames outside the viewport, and the pointer cannot land on one.
107+ let reachable = onScreenTabs ( in: window) . map { $0. label }
108+ XCTAssertGreaterThanOrEqual (
109+ reachable. count,
110+ 4 ,
111+ " The viewport must hold four tabs to drag between, showed \( reachable) of \( before) "
112+ )
111113
112- XCTAssertNotEqual (
113- before,
114- tabLabels ( in: window) ,
114+ // Interior tabs only. A tab at either edge of a scrolled track is half outside the
115+ // viewport, and a click at its centre lands on the chrome beside the track rather than on
116+ // the tab, so the drag never reaches the strip at all.
117+ drag ( tab ( named: reachable [ 1 ] , in: window) , onto: tab ( named: reachable [ reachable. count - 2 ] , in: window) )
118+
119+ XCTAssertTrue (
120+ waitForTabOrder ( toChangeFrom: before, in: window) ,
115121 " Dragging a tab in an overflowing strip must change the tab order, was \( before) "
116122 )
117123 }
@@ -171,6 +177,23 @@ final class EditorTabReorderUITests: UITestCase {
171177 forDuration: 0.6 ,
172178 thenDragTo: destination. coordinate ( withNormalizedOffset: CGVector ( dx: 0.5 , dy: 0.5 ) )
173179 )
174- Thread . sleep ( forTimeInterval: 1.0 )
180+ }
181+
182+ /// The strip animates the move and commits it on release, so the new order arrives some time
183+ /// after the gesture returns. Waiting for it rather than sleeping a fixed amount is what keeps
184+ /// this readable on a loaded machine: a sleep long enough for CI is dead time on every local
185+ /// run, and one short enough for a local run reads the pre-drag order on CI and reports the
186+ /// reorder as broken.
187+ private func waitForTabOrder( toChangeFrom before: [ String ] , in window: XCUIElement ) -> Bool {
188+ waitForPredicate ( timeout: 15 ) { self . tabLabels ( in: window) != before }
189+ }
190+
191+ /// The tabs the pointer can actually reach.
192+ ///
193+ /// Once the strip overflows, the track scrolls and the tabs outside the viewport stay in the
194+ /// accessibility tree with frames the pointer cannot land on. Dragging one of those is not a
195+ /// weaker version of the gesture, it is no gesture at all.
196+ private func onScreenTabs( in window: XCUIElement ) -> [ XCUIElement ] {
197+ tabElements ( in: window) . filter { $0. exists && $0. isHittable }
175198 }
176199}
0 commit comments