| // Copyright 2013 The Flutter Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #import <Foundation/Foundation.h> |
| #import <OCMock/OCMock.h> |
| #import <XCTest/XCTest.h> |
| |
| #import "flutter/shell/platform/darwin/common/framework/Headers/FlutterMacros.h" |
| #import "flutter/shell/platform/darwin/ios/framework/Source/FlutterChannelKeyResponder.h" |
| #import "flutter/shell/platform/darwin/ios/framework/Source/FlutterFakeKeyEvents.h" |
| |
| FLUTTER_ASSERT_ARC; |
| |
| #define XCTAssertStrEqual(value, expected) \ |
| XCTAssertTrue([value isEqualToString:expected], \ |
| @"String \"%@\" not equal to the expected value of \"%@\"", value, expected) |
| |
| namespace { |
| API_AVAILABLE(ios(13.4)) |
| constexpr UIKeyboardHIDUsage keyACode = (UIKeyboardHIDUsage)0x4; // KeyA iOS scan code. |
| } // namespace |
| |
| API_AVAILABLE(ios(13.4)) |
| @interface FlutterChannelKeyResponderTest : XCTestCase |
| @property(copy, nonatomic) FlutterUIPressProxy* testKeyDownEvent API_AVAILABLE(ios(13.4)); |
| @property(copy, nonatomic) FlutterUIPressProxy* testKeyUpEvent API_AVAILABLE(ios(13.4)); |
| @end |
| |
| @implementation FlutterChannelKeyResponderTest |
| |
| - (void)setUp { |
| // All of these tests were designed to run on iOS 13.4 or later. |
| if (@available(iOS 13.4, *)) { |
| } else { |
| XCTSkip(@"Required API not present for test."); |
| } |
| _testKeyDownEvent = keyDownEvent(keyACode, 0x0, 0.0f, "a", "a"); |
| _testKeyUpEvent = keyUpEvent(keyACode, 0x0, 0.0f); |
| } |
| |
| - (void)tearDown API_AVAILABLE(ios(13.4)) { |
| _testKeyDownEvent = nil; |
| _testKeyUpEvent = nil; |
| } |
| |
| - (void)testBasicKeyEvent API_AVAILABLE(ios(13.4)) { |
| __block NSMutableArray<id>* messages = [[NSMutableArray<id> alloc] init]; |
| __block BOOL next_response = TRUE; |
| __block NSMutableArray<NSNumber*>* responses = [[NSMutableArray<NSNumber*> alloc] init]; |
| |
| id mockKeyEventChannel = OCMStrictClassMock([FlutterBasicMessageChannel class]); |
| OCMStub([mockKeyEventChannel sendMessage:[OCMArg any] reply:[OCMArg any]]) |
| .andDo((^(NSInvocation* invocation) { |
| [invocation retainArguments]; |
| NSDictionary* message; |
| [invocation getArgument:&message atIndex:2]; |
| [messages addObject:message]; |
| |
| FlutterReply callback; |
| [invocation getArgument:&callback atIndex:3]; |
| NSDictionary* keyMessage = @{ |
| @"handled" : @(next_response), |
| }; |
| callback(keyMessage); |
| })); |
| |
| // Key down |
| FlutterChannelKeyResponder* responder = |
| [[FlutterChannelKeyResponder alloc] initWithChannel:mockKeyEventChannel]; |
| [responder handlePress:_testKeyDownEvent |
| callback:^(BOOL handled) { |
| [responses addObject:@(handled)]; |
| }]; |
| |
| XCTAssertEqual([messages count], 1u); |
| XCTAssertStrEqual([messages lastObject][@"keymap"], @"ios"); |
| XCTAssertStrEqual([messages lastObject][@"type"], @"keydown"); |
| XCTAssertEqual([[messages lastObject][@"keyCode"] intValue], keyACode); |
| XCTAssertEqual([[messages lastObject][@"modifiers"] intValue], 0x0); |
| XCTAssertStrEqual([messages lastObject][@"characters"], @"a"); |
| XCTAssertStrEqual([messages lastObject][@"charactersIgnoringModifiers"], @"a"); |
| |
| XCTAssertEqual([responses count], 1u); |
| XCTAssertEqual([[responses lastObject] boolValue], TRUE); |
| |
| [messages removeAllObjects]; |
| [responses removeAllObjects]; |
| |
| // Key up |
| next_response = FALSE; |
| [responder handlePress:_testKeyUpEvent |
| callback:^(BOOL handled) { |
| [responses addObject:@(handled)]; |
| }]; |
| |
| XCTAssertEqual([messages count], 1u); |
| XCTAssertStrEqual([messages lastObject][@"keymap"], @"ios"); |
| XCTAssertStrEqual([messages lastObject][@"type"], @"keyup"); |
| XCTAssertEqual([[messages lastObject][@"keyCode"] intValue], keyACode); |
| XCTAssertEqual([[messages lastObject][@"modifiers"] intValue], 0x0); |
| |
| XCTAssertEqual([responses count], 1u); |
| XCTAssertEqual([[responses lastObject] boolValue], FALSE); |
| |
| [messages removeAllObjects]; |
| [responses removeAllObjects]; |
| } |
| |
| - (void)testEmptyResponseIsTakenAsHandled API_AVAILABLE(ios(13.4)) { |
| __block NSMutableArray<id>* messages = [[NSMutableArray<id> alloc] init]; |
| __block NSMutableArray<NSNumber*>* responses = [[NSMutableArray<NSNumber*> alloc] init]; |
| |
| id mockKeyEventChannel = OCMStrictClassMock([FlutterBasicMessageChannel class]); |
| OCMStub([mockKeyEventChannel sendMessage:[OCMArg any] reply:[OCMArg any]]) |
| .andDo((^(NSInvocation* invocation) { |
| [invocation retainArguments]; |
| NSDictionary* message; |
| [invocation getArgument:&message atIndex:2]; |
| [messages addObject:message]; |
| |
| FlutterReply callback; |
| [invocation getArgument:&callback atIndex:3]; |
| callback(nullptr); |
| })); |
| |
| FlutterChannelKeyResponder* responder = |
| [[FlutterChannelKeyResponder alloc] initWithChannel:mockKeyEventChannel]; |
| [responder handlePress:_testKeyDownEvent |
| callback:^(BOOL handled) { |
| [responses addObject:@(handled)]; |
| }]; |
| |
| XCTAssertEqual([messages count], 1u); |
| XCTAssertStrEqual([messages lastObject][@"keymap"], @"ios"); |
| XCTAssertStrEqual([messages lastObject][@"type"], @"keydown"); |
| XCTAssertEqual([[messages lastObject][@"keyCode"] intValue], keyACode); |
| XCTAssertEqual([[messages lastObject][@"modifiers"] intValue], 0x0); |
| XCTAssertStrEqual([messages lastObject][@"characters"], @"a"); |
| XCTAssertStrEqual([messages lastObject][@"charactersIgnoringModifiers"], @"a"); |
| |
| XCTAssertEqual([responses count], 1u); |
| XCTAssertEqual([[responses lastObject] boolValue], TRUE); |
| } |
| |
| @end |