@@ -250,60 +250,6 @@ - (bool)testComposingRegionRemovedByFramework {
250
250
return true ;
251
251
}
252
252
253
- - (bool )testInputContextIsKeptActive {
254
- id engineMock = OCMClassMock ([FlutterEngine class ]);
255
- FlutterViewController* viewController = [[FlutterViewController alloc ] initWithEngine: engineMock
256
- nibName: @" "
257
- bundle: nil ];
258
-
259
- FlutterTextInputPlugin* plugin =
260
- [[FlutterTextInputPlugin alloc ] initWithViewController: viewController];
261
-
262
- [plugin handleMethodCall: [FlutterMethodCall
263
- methodCallWithMethodName: @" TextInput.setClient"
264
- arguments: @[
265
- @(1 ), @{
266
- @" inputAction" : @" action" ,
267
- @" inputType" : @{@" name" : @" inputName" },
268
- }
269
- ]]
270
- result: ^(id ){
271
- }];
272
-
273
- [plugin handleMethodCall: [FlutterMethodCall methodCallWithMethodName: @" TextInput.setEditingState"
274
- arguments: @{
275
- @" text" : @" " ,
276
- @" selectionBase" : @(0 ),
277
- @" selectionExtent" : @(0 ),
278
- @" composingBase" : @(-1 ),
279
- @" composingExtent" : @(-1 ),
280
- }]
281
- result: ^(id ){
282
- }];
283
-
284
- [plugin handleMethodCall: [FlutterMethodCall methodCallWithMethodName: @" TextInput.show"
285
- arguments: @[]]
286
- result: ^(id ){
287
- }];
288
-
289
- [plugin.inputContext deactivate ];
290
- EXPECT_EQ (plugin.inputContext .isActive , NO );
291
- NSEvent * keyEvent = [NSEvent keyEventWithType: NSEventTypeKeyDown
292
- location: NSZeroPoint
293
- modifierFlags: 0x100
294
- timestamp: 0
295
- windowNumber: 0
296
- context: nil
297
- characters: @" "
298
- charactersIgnoringModifiers: @" "
299
- isARepeat: NO
300
- keyCode: 0x50 ];
301
-
302
- [plugin handleKeyEvent: keyEvent];
303
- EXPECT_EQ (plugin.inputContext .isActive , YES );
304
- return true ;
305
- }
306
-
307
253
- (bool )testClearClientDuringComposing {
308
254
// Set up FlutterTextInputPlugin.
309
255
id engineMock = OCMClassMock ([FlutterEngine class ]);
@@ -917,6 +863,63 @@ - (bool)testComposingWithDeltasWhenSelectionIsActive {
917
863
return true ;
918
864
}
919
865
866
+ - (bool )testPerformKeyEquivalent {
867
+ __block NSEvent * eventBeingDispatchedByKeyboardManager = nil ;
868
+ FlutterViewController* viewControllerMock = OCMClassMock ([FlutterViewController class ]);
869
+ OCMStub ([viewControllerMock isDispatchingKeyEvent: [OCMArg any ]])
870
+ .andDo (^(NSInvocation * invocation) {
871
+ NSEvent * event;
872
+ [invocation getArgument: (void *)&event atIndex: 2 ];
873
+ BOOL result = event == eventBeingDispatchedByKeyboardManager;
874
+ [invocation setReturnValue: &result];
875
+ });
876
+
877
+ NSEvent * event = [NSEvent keyEventWithType: NSEventTypeKeyDown
878
+ location: NSZeroPoint
879
+ modifierFlags: 0x100
880
+ timestamp: 0
881
+ windowNumber: 0
882
+ context: nil
883
+ characters: @" "
884
+ charactersIgnoringModifiers: @" "
885
+ isARepeat: NO
886
+ keyCode: 0x50 ];
887
+
888
+ FlutterTextInputPlugin* plugin =
889
+ [[FlutterTextInputPlugin alloc ] initWithViewController: viewControllerMock];
890
+
891
+ OCMExpect ([viewControllerMock keyDown: event]);
892
+
893
+ // Require that event is handled (returns YES)
894
+ if (![plugin performKeyEquivalent: event]) {
895
+ return false ;
896
+ };
897
+
898
+ @try {
899
+ OCMVerify ( // NOLINT(google-objc-avoid-throwing-exception)
900
+ [viewControllerMock keyDown: event]);
901
+ } @catch (...) {
902
+ return false ;
903
+ }
904
+
905
+ // performKeyEquivalent must not forward event if it is being
906
+ // dispatched by keyboard manager
907
+ eventBeingDispatchedByKeyboardManager = event;
908
+
909
+ OCMReject ([viewControllerMock keyDown: event]);
910
+ @try {
911
+ // Require that event is not handled (returns NO) and not
912
+ // forwarded to controller
913
+ if ([plugin performKeyEquivalent: event]) {
914
+ return false ;
915
+ };
916
+ } @catch (...) {
917
+ return false ;
918
+ }
919
+
920
+ return true ;
921
+ }
922
+
920
923
- (bool )testLocalTextAndSelectionUpdateAfterDelta {
921
924
id engineMock = OCMClassMock ([FlutterEngine class ]);
922
925
id binaryMessengerMock = OCMProtocolMock (@protocol (FlutterBinaryMessenger));
@@ -1005,10 +1008,6 @@ - (bool)testLocalTextAndSelectionUpdateAfterDelta {
1005
1008
ASSERT_TRUE ([[FlutterInputPluginTestObjc alloc ] testComposingRegionRemovedByFramework ]);
1006
1009
}
1007
1010
1008
- TEST (FlutterTextInputPluginTest, TestTextInputContextIsKeptAlive) {
1009
- ASSERT_TRUE ([[FlutterInputPluginTestObjc alloc ] testInputContextIsKeptActive ]);
1010
- }
1011
-
1012
1011
TEST (FlutterTextInputPluginTest, TestClearClientDuringComposing) {
1013
1012
ASSERT_TRUE ([[FlutterInputPluginTestObjc alloc ] testClearClientDuringComposing ]);
1014
1013
}
@@ -1037,6 +1036,10 @@ - (bool)testLocalTextAndSelectionUpdateAfterDelta {
1037
1036
ASSERT_TRUE ([[FlutterInputPluginTestObjc alloc ] testLocalTextAndSelectionUpdateAfterDelta ]);
1038
1037
}
1039
1038
1039
+ TEST (FlutterTextInputPluginTest, TestPerformKeyEquivalent) {
1040
+ ASSERT_TRUE ([[FlutterInputPluginTestObjc alloc ] testPerformKeyEquivalent ]);
1041
+ }
1042
+
1040
1043
TEST (FlutterTextInputPluginTest, CanWorkWithFlutterTextField) {
1041
1044
FlutterEngine* engine = CreateTestEngine ();
1042
1045
NSString * fixtures = @(testing::GetFixturesPath ());
@@ -1069,7 +1072,7 @@ - (bool)testLocalTextAndSelectionUpdateAfterDelta {
1069
1072
[[FlutterTextFieldMock alloc ] initWithPlatformNode: &text_platform_node
1070
1073
fieldEditor: viewController.textInputPlugin];
1071
1074
[viewController.view addSubview: mockTextField];
1072
- [mockTextField becomeFirstResponder ];
1075
+ [mockTextField startEditing ];
1073
1076
1074
1077
NSDictionary * arguments = @{
1075
1078
@" inputAction" : @" action" ,
@@ -1133,4 +1136,40 @@ - (bool)testLocalTextAndSelectionUpdateAfterDelta {
1133
1136
EXPECT_EQ ([textField becomeFirstResponder ], NO );
1134
1137
}
1135
1138
1139
+ TEST (FlutterTextInputPluginTest, IsAddedAndRemovedFromViewHierarchy) {
1140
+ FlutterEngine* engine = CreateTestEngine ();
1141
+ NSString * fixtures = @(testing::GetFixturesPath ());
1142
+ FlutterDartProject* project = [[FlutterDartProject alloc ]
1143
+ initWithAssetsPath: fixtures
1144
+ ICUDataPath: [fixtures stringByAppendingString: @" /icudtl.dat" ]];
1145
+ FlutterViewController* viewController = [[FlutterViewController alloc ] initWithProject: project];
1146
+ [viewController loadView ];
1147
+ [engine setViewController: viewController];
1148
+
1149
+ NSWindow * window = [[NSWindow alloc ] initWithContentRect: NSMakeRect (0 , 0 , 800 , 600 )
1150
+ styleMask: NSBorderlessWindowMask
1151
+ backing: NSBackingStoreBuffered
1152
+ defer: NO ];
1153
+ window.contentView = viewController.view ;
1154
+
1155
+ ASSERT_EQ (viewController.textInputPlugin .superview , nil );
1156
+ ASSERT_FALSE (window.firstResponder == viewController.textInputPlugin );
1157
+
1158
+ [viewController.textInputPlugin
1159
+ handleMethodCall: [FlutterMethodCall methodCallWithMethodName: @" TextInput.show" arguments: @[]]
1160
+ result: ^(id ){
1161
+ }];
1162
+
1163
+ ASSERT_EQ (viewController.textInputPlugin .superview , viewController.view );
1164
+ ASSERT_TRUE (window.firstResponder == viewController.textInputPlugin );
1165
+
1166
+ [viewController.textInputPlugin
1167
+ handleMethodCall: [FlutterMethodCall methodCallWithMethodName: @" TextInput.hide" arguments: @[]]
1168
+ result: ^(id ){
1169
+ }];
1170
+
1171
+ ASSERT_EQ (viewController.textInputPlugin .superview , nil );
1172
+ ASSERT_FALSE (window.firstResponder == viewController.textInputPlugin );
1173
+ }
1174
+
1136
1175
} // namespace flutter::testing
0 commit comments