@@ -75,13 +75,16 @@ - (void)testPointerButtons {
7575 XCTAssertTrue (
7676 [app.textFields[@" 2,PointerChange.up,device=0,buttons=0" ] waitForExistenceWithTimeout: 1 ],
7777 @" PointerChange.up event did not occur for a normal tap" );
78+ XCTAssertTrue (
79+ [app.textFields[@" 3,PointerChange.remove,device=0,buttons=0" ] waitForExistenceWithTimeout: 1 ],
80+ @" PointerChange.remove event did not occur for a normal tap" );
7881 SEL rightClick = @selector (rightClick );
7982 XCTAssertTrue ([flutterView respondsToSelector: rightClick],
8083 @" If supportsPointerInteraction is true, this should be true too." );
8184 [flutterView performSelector: rightClick];
8285 // On simulated right click, a hover also occurs, so the hover pointer is added
8386 XCTAssertTrue (
84- [app.textFields[@" 3 ,PointerChange.add,device=1,buttons=0" ] waitForExistenceWithTimeout: 1 ],
87+ [app.textFields[@" 4 ,PointerChange.add,device=1,buttons=0" ] waitForExistenceWithTimeout: 1 ],
8588 @" PointerChange.add event did not occur for a right-click's hover pointer" );
8689
8790 // The hover pointer is removed after ~3.5 seconds, this ensures that all events are received
@@ -137,7 +140,7 @@ - (void)testPointerButtons {
137140 @" Right-click pointer was pressed before it was added" );
138141 XCTAssertGreaterThan (rightClickUpSequenceNumber, rightClickDownSequenceNumber,
139142 @" Right-click pointer was released before it was pressed" );
140- XCTAssertGreaterThan ([[hoverSequenceNumbers firstObject ] intValue ], 3 ,
143+ XCTAssertGreaterThan ([[hoverSequenceNumbers firstObject ] intValue ], 4 ,
141144 @" Hover occured before hover pointer was added" );
142145 XCTAssertGreaterThan (hoverRemovedSequenceNumber, [[hoverSequenceNumbers lastObject ] intValue ],
143146 @" Hover occured after hover pointer was removed" );
@@ -196,6 +199,88 @@ - (void)testPointerHover {
196199 XCTAssertTrue ([app.textFields[removeMessage] waitForExistenceWithTimeout: 1 ],
197200 @" PointerChange.remove event did not occur for a hover" );
198201}
202+
203+ - (void )testPointerScroll {
204+ BOOL supportsPointerInteraction = NO ;
205+ SEL supportsPointerInteractionSelector = @selector (supportsPointerInteraction );
206+ if ([XCUIDevice.sharedDevice respondsToSelector: supportsPointerInteractionSelector]) {
207+ supportsPointerInteraction =
208+ performBoolSelector (XCUIDevice.sharedDevice , supportsPointerInteractionSelector);
209+ }
210+ XCTSkipUnless (supportsPointerInteraction, " Device does not support pointer interaction." );
211+ XCUIApplication* app = [[XCUIApplication alloc ] init ];
212+ app.launchArguments = @[ @" --pointer-events" ];
213+ [app launch ];
214+
215+ NSPredicate * predicateToFindFlutterView =
216+ [NSPredicate predicateWithFormat: @" identifier BEGINSWITH 'flutter_view'" ];
217+ XCUIElement* flutterView = [[app descendantsMatchingType: XCUIElementTypeAny]
218+ elementMatchingPredicate: predicateToFindFlutterView];
219+ if (![flutterView waitForExistenceWithTimeout: kSecondsToWaitForFlutterView ]) {
220+ NSLog (@" %@ " , app.debugDescription );
221+ XCTFail (@" Failed due to not able to find any flutterView with %@ seconds" ,
222+ @(kSecondsToWaitForFlutterView ));
223+ }
224+
225+ XCTAssertNotNil (flutterView);
226+
227+ [flutterView scrollByDeltaX: 0.0 deltaY: 1000.0 ];
228+
229+ // The hover pointer is removed after ~3.5 seconds, this ensures that all events are received
230+ XCTestExpectation* sleepExpectation = [self expectationWithDescription: @" never fires" ];
231+ sleepExpectation.inverted = true ;
232+ [self waitForExpectations: @[ sleepExpectation ] timeout: 5.0 ];
233+
234+ // There are hover events interspersed with the scroll events in a varying order
235+ // Ensure the individual orderings are respected without hardcoding the absolute sequence
236+ NSMutableDictionary <NSString *, NSMutableArray <NSNumber *>*>* messages =
237+ [[NSMutableDictionary alloc ] init ];
238+ for (XCUIElement* element in [app.textFields allElementsBoundByIndex ]) {
239+ NSString * rawMessage = element.value ;
240+ // Parse out the sequence number
241+ NSUInteger commaIndex = [rawMessage rangeOfString: @" ," ].location ;
242+ NSInteger messageSequenceNumber =
243+ [rawMessage substringWithRange: NSMakeRange (0 , commaIndex)].integerValue ;
244+ // Parse out the rest of the message
245+ NSString * message = [rawMessage
246+ substringWithRange: NSMakeRange (commaIndex + 1 , rawMessage.length - (commaIndex + 1 ))];
247+ NSMutableArray <NSNumber *>* messageSequenceNumberList = messages[message];
248+ if (messageSequenceNumberList == nil ) {
249+ messageSequenceNumberList = [[NSMutableArray alloc ] init ];
250+ messages[message] = messageSequenceNumberList;
251+ }
252+ [messageSequenceNumberList addObject: @(messageSequenceNumber)];
253+ }
254+ // The number of hover events is not consistent, there could be one or many
255+ int hoverAddedSequenceNumber =
256+ assertOneMessageAndGetSequenceNumber (messages, @" PointerChange.add,device=0,buttons=0" );
257+ NSMutableArray <NSNumber *>* hoverSequenceNumbers =
258+ messages[@" PointerChange.hover,device=0,buttons=0" ];
259+ int hoverRemovedSequenceNumber =
260+ assertOneMessageAndGetSequenceNumber (messages, @" PointerChange.remove,device=0,buttons=0" );
261+ int panZoomAddedSequenceNumber =
262+ assertOneMessageAndGetSequenceNumber (messages, @" PointerChange.add,device=1,buttons=0" );
263+ int panZoomStartSequenceNumber = assertOneMessageAndGetSequenceNumber (
264+ messages, @" PointerChange.panZoomStart,device=1,buttons=0" );
265+ // The number of pan/zoom update events is not consistent, there could be one or many
266+ NSMutableArray <NSNumber *>* panZoomUpdateSequenceNumbers =
267+ messages[@" PointerChange.panZoomUpdate,device=1,buttons=0" ];
268+ int panZoomEndSequenceNumber = assertOneMessageAndGetSequenceNumber (
269+ messages, @" PointerChange.panZoomEnd,device=1,buttons=0" );
270+
271+ XCTAssertGreaterThan (panZoomStartSequenceNumber, panZoomAddedSequenceNumber,
272+ @" PanZoomStart occured before pointer was added" );
273+ XCTAssertGreaterThan ([[panZoomUpdateSequenceNumbers firstObject ] intValue ],
274+ panZoomStartSequenceNumber, @" PanZoomUpdate occured before PanZoomStart" );
275+ XCTAssertGreaterThan (panZoomEndSequenceNumber,
276+ [[panZoomUpdateSequenceNumbers lastObject ] intValue ],
277+ @" PanZoomUpdate occured after PanZoomUpdate" );
278+
279+ XCTAssertGreaterThan ([[hoverSequenceNumbers firstObject ] intValue ], hoverAddedSequenceNumber,
280+ @" Hover occured before pointer was added" );
281+ XCTAssertGreaterThan (hoverRemovedSequenceNumber, [[hoverSequenceNumbers lastObject ] intValue ],
282+ @" Hover occured after pointer was removed" );
283+ }
199284#pragma clang diagnostic pop
200285
201286@end
0 commit comments