5#import <XCTest/XCTest.h>
17 self.continueAfterFailure = NO;
21 NSInvocation* invocation = [NSInvocation
22 invocationWithMethodSignature:[[target class] instanceMethodSignatureForSelector:selector]];
23 [invocation setSelector:selector];
24 [invocation setTarget:target];
27 [invocation getReturnValue:&returnValue];
32 NSMutableArray<NSNumber*>* matchingMessages = messages[message];
33 XCTAssertNotNil(matchingMessages,
@"Did not receive \"%@\
" message",
message);
34 XCTAssertEqual(matchingMessages.count, 1,
@"More than one \"%@\
" message",
message);
35 return matchingMessages.firstObject.intValue;
39#pragma clang diagnostic push
40#pragma clang diagnostic ignored "-Wundeclared-selector"
41#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
43 BOOL supportsPointerInteraction = NO;
44 SEL supportsPointerInteractionSelector =
@selector(supportsPointerInteraction);
45 if ([XCUIDevice.sharedDevice respondsToSelector:supportsPointerInteractionSelector]) {
46 supportsPointerInteraction =
49 XCTSkipUnless(supportsPointerInteraction,
"Device does not support pointer interaction.");
50 XCUIApplication*
app = [[XCUIApplication alloc] init];
51 app.launchArguments = @[ @"--pointer-events" ];
54 NSPredicate* predicateToFindFlutterView =
55 [NSPredicate predicateWithFormat:@"identifier BEGINSWITH 'flutter_view'"];
56 XCUIElement* flutterView = [[app descendantsMatchingType:XCUIElementTypeAny]
57 elementMatchingPredicate:predicateToFindFlutterView];
59 NSLog(
@"%@",
app.debugDescription);
60 XCTFail(
@"Failed due to not able to find any flutterView with %@ seconds",
64 XCTAssertNotNil(flutterView);
69 [
app.textFields[
@"0,PointerChange.add,device=0,buttons=0"] waitForExistenceWithTimeout:1],
70 @"PointerChange.add event did not occur for a normal tap");
73 [
app.textFields[
@"1,PointerChange.down,device=0,buttons=0"] waitForExistenceWithTimeout:1],
74 @"PointerChange.down event did not occur for a normal tap");
76 [
app.textFields[
@"2,PointerChange.up,device=0,buttons=0"] waitForExistenceWithTimeout:1],
77 @"PointerChange.up event did not occur for a normal tap");
79 [
app.textFields[
@"3,PointerChange.remove,device=0,buttons=0"] waitForExistenceWithTimeout:1],
80 @"PointerChange.remove event did not occur for a normal tap");
81 SEL rightClick =
@selector(rightClick);
82 XCTAssertTrue([flutterView respondsToSelector:rightClick],
83 @"If supportsPointerInteraction is true, this should be true too.");
84 [flutterView performSelector:rightClick];
87 [
app.textFields[
@"4,PointerChange.add,device=1,buttons=0"] waitForExistenceWithTimeout:1],
88 @"PointerChange.add event did not occur for a right-click's hover pointer");
91 XCTestExpectation* sleepExpectation = [
self expectationWithDescription:@"never fires"];
92 sleepExpectation.inverted =
true;
93 [
self waitForExpectations:@[ sleepExpectation ] timeout:5.0];
97 NSMutableDictionary<NSString*, NSMutableArray<NSNumber*>*>* messages =
98 [[NSMutableDictionary alloc] init];
99 for (XCUIElement* element in [
app.textFields allElementsBoundByIndex]) {
100 NSString* rawMessage = element.value;
102 NSUInteger commaIndex = [rawMessage rangeOfString:@","].location;
103 NSInteger messageSequenceNumber =
104 [rawMessage substringWithRange:NSMakeRange(0, commaIndex)].integerValue;
106 NSString*
message = [rawMessage
107 substringWithRange:NSMakeRange(commaIndex + 1, rawMessage.length - (commaIndex + 1))];
108 NSMutableArray<NSNumber*>* messageSequenceNumberList = messages[message];
109 if (messageSequenceNumberList == nil) {
110 messageSequenceNumberList = [[NSMutableArray alloc] init];
111 messages[message] = messageSequenceNumberList;
113 [messageSequenceNumberList addObject:@(messageSequenceNumber)];
116 NSMutableArray<NSNumber*>* hoverSequenceNumbers =
117 messages[@"PointerChange.hover,device=1,buttons=0"];
118 int hoverRemovedSequenceNumber =
121 int rightClickAddedSequenceNumber;
122 int rightClickDownSequenceNumber;
123 int rightClickUpSequenceNumber;
124 if (messages[
@"PointerChange.add,device=2,buttons=0"] == nil) {
126 rightClickAddedSequenceNumber = 0;
127 rightClickDownSequenceNumber =
129 rightClickUpSequenceNumber =
132 rightClickAddedSequenceNumber =
134 rightClickDownSequenceNumber =
136 rightClickUpSequenceNumber =
139 XCTAssertGreaterThan(rightClickDownSequenceNumber, rightClickAddedSequenceNumber,
140 @"Right-click pointer was pressed before it was added");
141 XCTAssertGreaterThan(rightClickUpSequenceNumber, rightClickDownSequenceNumber,
142 @"Right-click pointer was released before it was pressed");
143 XCTAssertGreaterThan([[hoverSequenceNumbers firstObject] intValue], 4,
144 @"Hover occured before hover pointer was added");
145 XCTAssertGreaterThan(hoverRemovedSequenceNumber, [[hoverSequenceNumbers lastObject] intValue],
146 @"Hover occured after hover pointer was removed");
150 BOOL supportsPointerInteraction = NO;
151 SEL supportsPointerInteractionSelector =
@selector(supportsPointerInteraction);
152 if ([XCUIDevice.sharedDevice respondsToSelector:supportsPointerInteractionSelector]) {
153 supportsPointerInteraction =
156 XCTSkipUnless(supportsPointerInteraction,
"Device does not support pointer interaction.");
157 XCUIApplication*
app = [[XCUIApplication alloc] init];
158 app.launchArguments = @[ @"--pointer-events" ];
161 NSPredicate* predicateToFindFlutterView =
162 [NSPredicate predicateWithFormat:@"identifier BEGINSWITH 'flutter_view'"];
163 XCUIElement* flutterView = [[app descendantsMatchingType:XCUIElementTypeAny]
164 elementMatchingPredicate:predicateToFindFlutterView];
166 NSLog(
@"%@",
app.debugDescription);
167 XCTFail(
@"Failed due to not able to find any flutterView with %@ seconds",
171 XCTAssertNotNil(flutterView);
173 SEL hover =
@selector(hover);
174 XCTAssertTrue([flutterView respondsToSelector:hover],
175 @"If supportsPointerInteraction is true, this should be true too.");
176 [flutterView performSelector:hover];
178 [
app.textFields[
@"0,PointerChange.add,device=0,buttons=0"] waitForExistenceWithTimeout:1],
179 @"PointerChange.add event did not occur for a hover");
181 [
app.textFields[
@"1,PointerChange.hover,device=0,buttons=0"] waitForExistenceWithTimeout:1],
182 @"PointerChange.hover event did not occur for a hover");
184 NSInteger lastHoverSequenceNumber = -1;
185 NSPredicate* predicateToFindHoverEvents =
186 [NSPredicate predicateWithFormat:@"value ENDSWITH ',PointerChange.hover,device=0,buttons=0'"];
187 for (XCUIElement* textField in
188 [[
app.textFields matchingPredicate:predicateToFindHoverEvents] allElementsBoundByIndex]) {
189 NSInteger messageSequenceNumber =
190 [[textField.value componentsSeparatedByString:@","] firstObject].integerValue;
191 if (messageSequenceNumber > lastHoverSequenceNumber) {
192 lastHoverSequenceNumber = messageSequenceNumber;
195 XCTAssertNotEqual(lastHoverSequenceNumber, -1,
196 @"PointerChange.hover event did not occur for a hover");
197 NSString* removeMessage = [NSString
198 stringWithFormat:@"%ld,PointerChange.remove,device=0,buttons=0", lastHoverSequenceNumber + 1];
199 XCTAssertTrue([
app.textFields[removeMessage] waitForExistenceWithTimeout:1],
200 @"PointerChange.remove event did not occur for a hover");
204 BOOL supportsPointerInteraction = NO;
205 SEL supportsPointerInteractionSelector =
@selector(supportsPointerInteraction);
206 if ([XCUIDevice.sharedDevice respondsToSelector:supportsPointerInteractionSelector]) {
207 supportsPointerInteraction =
210 XCTSkipUnless(supportsPointerInteraction,
"Device does not support pointer interaction.");
211 XCUIApplication*
app = [[XCUIApplication alloc] init];
212 app.launchArguments = @[ @"--pointer-events" ];
215 NSPredicate* predicateToFindFlutterView =
216 [NSPredicate predicateWithFormat:@"identifier BEGINSWITH 'flutter_view'"];
217 XCUIElement* flutterView = [[app descendantsMatchingType:XCUIElementTypeAny]
218 elementMatchingPredicate:predicateToFindFlutterView];
220 XCTFail(
@"Failed due to not able to find any flutterView with %@ seconds",
224 XCTAssertNotNil(flutterView);
226 SEL scroll =
@selector(scrollByDeltaX:deltaY:);
227 XCTAssertTrue([flutterView respondsToSelector:scroll],
228 @"If supportsPointerInteraction is true, this should be true too.");
230 NSInvocation* invocation = [NSInvocation
231 invocationWithMethodSignature:[XCUIElement instanceMethodSignatureForSelector:scroll]];
232 [invocation setSelector:scroll];
233 CGFloat deltaX = 0.0;
234 CGFloat deltaY = 1000.0;
235 [invocation setArgument:&deltaX atIndex:2];
236 [invocation setArgument:&deltaY atIndex:3];
237 [invocation invokeWithTarget:flutterView];
240 SEL hover =
@selector(hover:);
241 XCTAssertTrue([flutterView respondsToSelector:hover],
242 @"If supportsPointerInteraction is true, this should be true too.");
243 [flutterView performSelector:hover];
249 XCTestExpectation* sleepExpectation = [
self expectationWithDescription:@"never fires"];
250 sleepExpectation.inverted =
true;
251 [
self waitForExpectations:@[ sleepExpectation ] timeout:5.0];
255 NSMutableDictionary<NSString*, NSMutableArray<NSNumber*>*>* messages =
256 [[NSMutableDictionary alloc] init];
257 for (XCUIElement* element in [
app.textFields allElementsBoundByIndex]) {
258 NSString* rawMessage = element.value;
260 NSUInteger commaIndex = [rawMessage rangeOfString:@","].location;
261 NSInteger messageSequenceNumber =
262 [rawMessage substringWithRange:NSMakeRange(0, commaIndex)].integerValue;
264 NSString*
message = [rawMessage
265 substringWithRange:NSMakeRange(commaIndex + 1, rawMessage.length - (commaIndex + 1))];
266 NSMutableArray<NSNumber*>* messageSequenceNumberList = messages[message];
267 if (messageSequenceNumberList == nil) {
268 messageSequenceNumberList = [[NSMutableArray alloc] init];
269 messages[message] = messageSequenceNumberList;
271 [messageSequenceNumberList addObject:@(messageSequenceNumber)];
275 messages,
@"PointerChange.add,device=0,buttons=0,signalKind=PointerSignalKind.none");
276 NSMutableArray<NSNumber*>* hoverSequenceNumbers =
277 messages[@"PointerChange.hover,device=0,buttons=0,signalKind=PointerSignalKind.none"];
279 messages,
@"PointerChange.remove,device=0,buttons=0,signalKind=PointerSignalKind.none");
281 messages,
@"PointerChange.add,device=1,buttons=0,signalKind=PointerSignalKind.none");
283 messages,
@"PointerChange.panZoomStart,device=1,buttons=0,signalKind=PointerSignalKind.none");
285 NSMutableArray<NSNumber*>* panZoomUpdateSequenceNumbers =
286 messages[@"PointerChange.panZoomUpdate,device=1,buttons=0,signalKind=PointerSignalKind.none"];
288 messages,
@"PointerChange.panZoomEnd,device=1,buttons=0,signalKind=PointerSignalKind.none");
291 @"PointerChange.cancel,device=2,buttons=0,signalKind=PointerSignalKind.scrollInertiaCancel");
293 XCTAssertGreaterThan(panZoomStartSequenceNumber, panZoomAddedSequenceNumber,
294 @"PanZoomStart occured before pointer was added");
295 XCTAssertGreaterThan([[panZoomUpdateSequenceNumbers firstObject] intValue],
296 panZoomStartSequenceNumber,
@"PanZoomUpdate occured before PanZoomStart");
297 XCTAssertGreaterThan(panZoomEndSequenceNumber,
298 [[panZoomUpdateSequenceNumbers lastObject] intValue],
299 @"PanZoomUpdate occured after PanZoomEnd");
300 XCTAssertGreaterThan(inertiaCancelSequenceNumber, panZoomEndSequenceNumber,
301 @"ScrollInertiaCancel occured before PanZoomEnd");
303 XCTAssertGreaterThan([[hoverSequenceNumbers firstObject] intValue], hoverAddedSequenceNumber,
304 @"Hover occured before pointer was added");
305 XCTAssertGreaterThan(hoverRemovedSequenceNumber, [[hoverSequenceNumbers lastObject] intValue],
306 @"Hover occured after pointer was removed");
308#pragma clang diagnostic pop
static const NSInteger kSecondsToWaitForFlutterView
static BOOL performBoolSelector(id target, SEL selector)
void testPointerButtons()
static int assertOneMessageAndGetSequenceNumber(NSMutableDictionary *messages, NSString *message)