#import #import #import @interface FlutterTextInputView : UIView @property(nonatomic, readonly) UITextField* tvosTextInputProxy; @end @interface FlutterTextInputPlugin : NSObject @property(nonatomic, weak) UIViewController* viewController; - (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result; - (UIView*)textInputView; @end @interface FlutterEngine (NativeTextInputTests) - (FlutterTextInputPlugin*)textInputPlugin; @end @interface FlutterViewController (NativeTextInputTests) @property(nonatomic, assign, getter=isTvosNativeTextInputActive) BOOL tvosNativeTextInputActive; @end @interface FlutterNativeTextInputTests : XCTestCase @end @implementation FlutterNativeTextInputTests { FlutterEngine* _engine; FlutterViewController* _flutterViewController; UIWindow* _window; FlutterTextInputPlugin* _textInputPlugin; } - (void)setUp { [super setUp]; id appDelegate = UIApplication.sharedApplication.delegate; _window = appDelegate.window; _flutterViewController = (FlutterViewController*)_window.rootViewController; XCTAssertTrue([_flutterViewController isKindOfClass:[FlutterViewController class]]); _engine = _flutterViewController.engine; _textInputPlugin = [_engine textInputPlugin]; XCTAssertNotNil(_textInputPlugin); } - (void)tearDown { [self invokeTextInput:@"TextInput.hide" arguments:nil]; [self invokeTextInput:@"TextInput.clearClient" arguments:nil]; _textInputPlugin = nil; _engine = nil; _flutterViewController = nil; _window = nil; [super tearDown]; } - (void)testPackagedEngineUsesRealUITextFieldProxyAndNativeSessionRouting { [self invokeTextInput:@"TextInput.setClient" arguments:@[ @123, @{ @"inputType" : @{@"name" : @"TextInputType.url"}, @"keyboardAppearance" : @"Brightness.dark", @"obscureText" : @NO, @"inputAction" : @"TextInputAction.done", @"smartDashesType" : @"0", @"smartQuotesType" : @"0", @"autocorrect" : @NO, @"enableInteractiveSelection" : @YES, }, ]]; [self invokeTextInput:@"TextInput.setEditingState" arguments:@{ @"text" : @"plezy.local", @"selectionBase" : @2, @"selectionExtent" : @7, @"selectionAffinity" : @"TextAffinity.downstream", @"composingBase" : @(-1), @"composingExtent" : @(-1), }]; FlutterTextInputView* inputView = (FlutterTextInputView*)[_textInputPlugin textInputView]; UITextField* proxy = inputView.tvosTextInputProxy; XCTAssertNotNil(proxy); XCTAssertEqualObjects(proxy.text, @"plezy.local"); XCTAssertEqual(proxy.keyboardType, UIKeyboardTypeURL); XCTAssertEqual(proxy.returnKeyType, UIReturnKeyDone); XCTAssertEqual(proxy.autocorrectionType, UITextAutocorrectionTypeNo); XCTAssertEqual([proxy offsetFromPosition:proxy.beginningOfDocument toPosition:proxy.selectedTextRange.start], 2); XCTAssertEqual([proxy offsetFromPosition:proxy.beginningOfDocument toPosition:proxy.selectedTextRange.end], 7); [self invokeTextInput:@"TextInput.show" arguments:nil]; [self waitForMainRunLoop]; XCTAssertTrue(proxy.isFirstResponder); XCTAssertTrue(_flutterViewController.tvosNativeTextInputActive); [self invokeTextInput:@"TextInput.hide" arguments:nil]; [self waitForMainRunLoop]; XCTAssertFalse(proxy.isFirstResponder); XCTAssertFalse(_flutterViewController.tvosNativeTextInputActive); } - (void)invokeTextInput:(NSString*)method arguments:(id)arguments { FlutterMethodCall* call = [FlutterMethodCall methodCallWithMethodName:method arguments:arguments]; [_textInputPlugin handleMethodCall:call result:^(id result){ }]; } - (void)waitForMainRunLoop { XCTestExpectation* expectation = [self expectationWithDescription:@"main run loop"]; dispatch_async(dispatch_get_main_queue(), ^{ [expectation fulfill]; }); [self waitForExpectations:@[ expectation ] timeout:2]; } @end