13 #import <OCMock/OCMock.h>
14 #import "flutter/testing/testing.h"
17 - (void)setPlatformNode:(
flutter::FlutterTextPlatformNode*)node;
22 @property(nonatomic, nullable, copy) NSString* lastUpdatedString;
23 @property(nonatomic) NSRange lastUpdatedSelection;
29 - (void)updateString:(NSString*)string withSelection:(NSRange)selection {
30 _lastUpdatedString = string;
31 _lastUpdatedSelection = selection;
45 - (nonnull
FlutterView*)createFlutterViewWithMTLDevice:(id<MTLDevice>)device
46 commandQueue:(id<MTLCommandQueue>)commandQueue {
47 return OCMClassMock([NSView
class]);
62 [engineMock binaryMessenger])
63 .andReturn(binaryMessengerMock);
72 NSDictionary* setClientConfig = @{
73 @"inputAction" :
@"action",
74 @"inputType" : @{
@"name" :
@"inputName"},
84 @"selectionBase" : @(0),
85 @"selectionExtent" : @(0),
86 @"composingBase" : @(-1),
87 @"composingExtent" : @(-1),
96 EXPECT_STREQ([editingState[
@"text"] UTF8String],
"Text");
97 EXPECT_STREQ([editingState[
@"selectionAffinity"] UTF8String],
"TextAffinity.upstream");
98 EXPECT_FALSE([editingState[
@"selectionIsDirectional"] boolValue]);
99 EXPECT_EQ([editingState[
@"selectionBase"] intValue], 0);
100 EXPECT_EQ([editingState[
@"selectionExtent"] intValue], 0);
101 EXPECT_EQ([editingState[
@"composingBase"] intValue], -1);
102 EXPECT_EQ([editingState[
@"composingExtent"] intValue], -1);
106 - (bool)testSetMarkedTextWithSelectionChange {
110 [engineMock binaryMessenger])
111 .andReturn(binaryMessengerMock);
120 NSDictionary* setClientConfig = @{
121 @"inputAction" :
@"action",
122 @"inputType" : @{
@"name" :
@"inputName"},
132 @"selectionBase" : @(4),
133 @"selectionExtent" : @(4),
134 @"composingBase" : @(-1),
135 @"composingExtent" : @(-1),
141 [plugin setMarkedText:@"marked"
142 selectedRange:NSMakeRange(1, 0)
143 replacementRange:NSMakeRange(NSNotFound, 0)];
145 NSDictionary* expectedState = @{
146 @"selectionBase" : @(5),
147 @"selectionExtent" : @(5),
148 @"selectionAffinity" :
@"TextAffinity.upstream",
149 @"selectionIsDirectional" : @(NO),
150 @"composingBase" : @(4),
151 @"composingExtent" : @(10),
152 @"text" :
@"Textmarked",
161 [binaryMessengerMock sendOnChannel:
@"flutter/textinput" message:updateCall]);
165 [binaryMessengerMock sendOnChannel:
@"flutter/textinput" message:updateCall]);
172 - (bool)testSetMarkedTextWithReplacementRange {
176 [engineMock binaryMessenger])
177 .andReturn(binaryMessengerMock);
186 NSDictionary* setClientConfig = @{
187 @"inputAction" :
@"action",
188 @"inputType" : @{
@"name" :
@"inputName"},
198 @"selectionBase" : @(3),
199 @"selectionExtent" : @(3),
200 @"composingBase" : @(-1),
201 @"composingExtent" : @(-1),
207 [plugin setMarkedText:@"marked"
208 selectedRange:NSMakeRange(1, 0)
209 replacementRange:NSMakeRange(1, 2)];
211 NSDictionary* expectedState = @{
212 @"selectionBase" : @(2),
213 @"selectionExtent" : @(2),
214 @"selectionAffinity" :
@"TextAffinity.upstream",
215 @"selectionIsDirectional" : @(NO),
216 @"composingBase" : @(1),
217 @"composingExtent" : @(7),
218 @"text" :
@"1marked4",
227 [binaryMessengerMock sendOnChannel:
@"flutter/textinput" message:updateCall]);
231 [binaryMessengerMock sendOnChannel:
@"flutter/textinput" message:updateCall]);
238 - (bool)testComposingRegionRemovedByFramework {
242 [engineMock binaryMessenger])
243 .andReturn(binaryMessengerMock);
252 NSDictionary* setClientConfig = @{
253 @"inputAction" :
@"action",
254 @"inputType" : @{
@"name" :
@"inputName"},
264 @"selectionBase" : @(4),
265 @"selectionExtent" : @(4),
266 @"composingBase" : @(2),
267 @"composingExtent" : @(4),
277 @"selectionBase" : @(2),
278 @"selectionExtent" : @(2),
279 @"composingBase" : @(-1),
280 @"composingExtent" : @(-1),
288 EXPECT_STREQ([editingState[
@"text"] UTF8String],
"Te");
289 EXPECT_STREQ([editingState[
@"selectionAffinity"] UTF8String],
"TextAffinity.upstream");
290 EXPECT_FALSE([editingState[
@"selectionIsDirectional"] boolValue]);
291 EXPECT_EQ([editingState[
@"selectionBase"] intValue], 2);
292 EXPECT_EQ([editingState[
@"selectionExtent"] intValue], 2);
293 EXPECT_EQ([editingState[
@"composingBase"] intValue], -1);
294 EXPECT_EQ([editingState[
@"composingExtent"] intValue], -1);
303 [engineMock binaryMessenger])
304 .andReturn(binaryMessengerMock);
312 NSDictionary* setClientConfig = @{
313 @"inputAction" :
@"action",
314 @"inputType" : @{
@"name" :
@"inputName"},
325 @"selectionBase" : @(0),
326 @"selectionExtent" : @(0),
327 @"composingBase" : @(0),
328 @"composingExtent" : @(1),
335 EXPECT_EQ([editingState[
@"composingBase"] intValue], 0);
336 EXPECT_EQ([editingState[
@"composingExtent"] intValue], 1);
346 EXPECT_EQ([editingState[
@"composingBase"] intValue], [editingState[
@"composingExtent"] intValue]);
350 - (bool)testAutocompleteDisabledWhenAutofillNotSet {
355 [engineMock binaryMessenger])
356 .andReturn(binaryMessengerMock);
364 NSDictionary* setClientConfig = @{
365 @"inputAction" :
@"action",
366 @"inputType" : @{
@"name" :
@"inputName"},
374 EXPECT_FALSE([plugin isAutomaticTextCompletionEnabled]);
378 - (bool)testAutocompleteEnabledWhenAutofillSet {
383 [engineMock binaryMessenger])
384 .andReturn(binaryMessengerMock);
392 NSDictionary* setClientConfig = @{
393 @"inputAction" :
@"action",
394 @"inputType" : @{
@"name" :
@"inputName"},
396 @"uniqueIdentifier" :
@"field1",
397 @"hints" : @[ @"name" ],
398 @"editingValue" : @{
@"text" :
@""},
407 EXPECT_TRUE([plugin isAutomaticTextCompletionEnabled]);
410 if (@available(macOS 11.0, *)) {
411 EXPECT_EQ([plugin contentType], nil);
416 - (bool)testAutocompleteEnabledWhenAutofillSetNoHint {
421 [engineMock binaryMessenger])
422 .andReturn(binaryMessengerMock);
430 NSDictionary* setClientConfig = @{
431 @"inputAction" :
@"action",
432 @"inputType" : @{
@"name" :
@"inputName"},
434 @"uniqueIdentifier" :
@"field1",
436 @"editingValue" : @{
@"text" :
@""},
445 EXPECT_TRUE([plugin isAutomaticTextCompletionEnabled]);
449 - (bool)testAutocompleteDisabledWhenObscureTextSet {
454 [engineMock binaryMessenger])
455 .andReturn(binaryMessengerMock);
463 NSDictionary* setClientConfig = @{
464 @"inputAction" :
@"action",
465 @"inputType" : @{
@"name" :
@"inputName"},
466 @"obscureText" : @YES,
468 @"uniqueIdentifier" :
@"field1",
469 @"editingValue" : @{
@"text" :
@""},
478 EXPECT_FALSE([plugin isAutomaticTextCompletionEnabled]);
482 - (bool)testAutocompleteDisabledWhenPasswordAutofillSet {
487 [engineMock binaryMessenger])
488 .andReturn(binaryMessengerMock);
496 NSDictionary* setClientConfig = @{
497 @"inputAction" :
@"action",
498 @"inputType" : @{
@"name" :
@"inputName"},
500 @"uniqueIdentifier" :
@"field1",
501 @"hints" : @[ @"password" ],
502 @"editingValue" : @{
@"text" :
@""},
511 EXPECT_FALSE([plugin isAutomaticTextCompletionEnabled]);
514 if (@available(macOS 11.0, *)) {
515 EXPECT_EQ([plugin contentType], NSTextContentTypePassword);
520 - (bool)testAutocompleteDisabledWhenAutofillGroupIncludesPassword {
525 [engineMock binaryMessenger])
526 .andReturn(binaryMessengerMock);
534 NSDictionary* setClientConfig = @{
535 @"inputAction" :
@"action",
536 @"inputType" : @{
@"name" :
@"inputName"},
539 @"inputAction" : @"action",
540 @"inputType" : @{@"name" : @"inputName"},
542 @"uniqueIdentifier" : @"field1",
543 @"hints" : @[ @"password" ],
544 @"editingValue" : @{@"text" : @""},
548 @"inputAction" : @"action",
549 @"inputType" : @{@"name" : @"inputName"},
551 @"uniqueIdentifier" : @"field2",
552 @"hints" : @[ @"name" ],
553 @"editingValue" : @{@"text" : @""},
564 EXPECT_FALSE([plugin isAutomaticTextCompletionEnabled]);
568 - (bool)testContentTypeWhenAutofillTypeIsUsername {
573 [engineMock binaryMessenger])
574 .andReturn(binaryMessengerMock);
582 NSDictionary* setClientConfig = @{
583 @"inputAction" :
@"action",
584 @"inputType" : @{
@"name" :
@"inputName"},
586 @"uniqueIdentifier" :
@"field1",
587 @"hints" : @[ @"name" ],
588 @"editingValue" : @{
@"text" :
@""},
597 EXPECT_FALSE([plugin isAutomaticTextCompletionEnabled]);
600 if (@available(macOS 11.0, *)) {
601 EXPECT_EQ([plugin contentType], NSTextContentTypeUsername);
606 - (bool)testContentTypeWhenAutofillTypeIsOneTimeCode {
611 [engineMock binaryMessenger])
612 .andReturn(binaryMessengerMock);
620 NSDictionary* setClientConfig = @{
621 @"inputAction" :
@"action",
622 @"inputType" : @{
@"name" :
@"inputName"},
624 @"uniqueIdentifier" :
@"field1",
625 @"hints" : @[ @"oneTimeCode" ],
626 @"editingValue" : @{
@"text" :
@""},
635 EXPECT_FALSE([plugin isAutomaticTextCompletionEnabled]);
638 if (@available(macOS 11.0, *)) {
639 EXPECT_EQ([plugin contentType], NSTextContentTypeOneTimeCode);
644 - (bool)testFirstRectForCharacterRange {
648 [engineMock binaryMessenger])
649 .andReturn(binaryMessengerMock);
652 [controllerMock loadView];
653 id viewMock = controllerMock.flutterView;
656 .andReturn(NSMakeRect(0, 0, 200, 200));
658 id windowMock = OCMClassMock([NSWindow
class]);
661 .andReturn(windowMock);
664 [viewMock convertRect:NSMakeRect(28, 10, 2, 19) toView:nil])
665 .andReturn(NSMakeRect(28, 10, 2, 19));
668 [windowMock convertRectToScreen:NSMakeRect(28, 10, 2, 19)])
669 .andReturn(NSMakeRect(38, 20, 2, 19));
679 @(1.0), @(0.0), @(0.0), @(0.0), @(0.0), @(1.0), @(0.0), @(0.0), @(0.0),
680 @(0.0), @(1.0), @(0.0), @(20.0), @(10.0), @(0.0), @(1.0)
704 [windowMock convertRectToScreen:NSMakeRect(28, 10, 2, 19)]);
709 return NSEqualRects(rect, NSMakeRect(38, 20, 2, 19));
712 - (bool)testFirstRectForCharacterRangeAtInfinity {
716 [engineMock binaryMessenger])
717 .andReturn(binaryMessengerMock);
720 [controllerMock loadView];
721 id viewMock = controllerMock.flutterView;
724 .andReturn(NSMakeRect(0, 0, 200, 200));
726 id windowMock = OCMClassMock([NSWindow
class]);
729 .andReturn(windowMock);
740 @(1.0), @(0.0), @(0.0), @(0.0), @(0.0), @(1.0), @(0.0), @(0.0), @(0.0),
741 @(0.0), @(1.0), @(0.0), @(20.0), @(10.0), @(0.0), @(0.0)
763 return NSEqualRects(rect, CGRectZero);
766 - (bool)testFirstRectForCharacterRangeWithEsotericAffineTransform {
770 [engineMock binaryMessenger])
771 .andReturn(binaryMessengerMock);
774 [controllerMock loadView];
775 id viewMock = controllerMock.flutterView;
778 .andReturn(NSMakeRect(0, 0, 200, 200));
780 id windowMock = OCMClassMock([NSWindow
class]);
783 .andReturn(windowMock);
786 [viewMock convertRect:NSMakeRect(-18, 6, 3, 3) toView:nil])
787 .andReturn(NSMakeRect(-18, 6, 3, 3));
790 [windowMock convertRectToScreen:NSMakeRect(-18, 6, 3, 3)])
791 .andReturn(NSMakeRect(-18, 6, 3, 3));
804 @(0.0), @(3.0), @(0.0), @(0.0), @(-3.0), @(0.0), @(0.0), @(0.0), @(0.0),
805 @(0.0), @(3.0), @(0.0), @(-6.0), @(3.0), @(9.0), @(1.0)
830 [windowMock convertRectToScreen:NSMakeRect(-18, 6, 3, 3)]);
835 return NSEqualRects(rect, NSMakeRect(-18, 6, 3, 3));
838 - (bool)testSetEditingStateWithTextEditingDelta {
842 [engineMock binaryMessenger])
843 .andReturn(binaryMessengerMock);
852 NSDictionary* setClientConfig = @{
853 @"inputAction" :
@"action",
854 @"enableDeltaModel" :
@"true",
855 @"inputType" : @{
@"name" :
@"inputName"},
865 @"selectionBase" : @(0),
866 @"selectionExtent" : @(0),
867 @"composingBase" : @(-1),
868 @"composingExtent" : @(-1),
877 EXPECT_STREQ([editingState[
@"text"] UTF8String],
"Text");
878 EXPECT_STREQ([editingState[
@"selectionAffinity"] UTF8String],
"TextAffinity.upstream");
879 EXPECT_FALSE([editingState[
@"selectionIsDirectional"] boolValue]);
880 EXPECT_EQ([editingState[
@"selectionBase"] intValue], 0);
881 EXPECT_EQ([editingState[
@"selectionExtent"] intValue], 0);
882 EXPECT_EQ([editingState[
@"composingBase"] intValue], -1);
883 EXPECT_EQ([editingState[
@"composingExtent"] intValue], -1);
887 - (bool)testOperationsThatTriggerDelta {
891 [engineMock binaryMessenger])
892 .andReturn(binaryMessengerMock);
901 NSDictionary* setClientConfig = @{
902 @"inputAction" :
@"action",
903 @"enableDeltaModel" :
@"true",
904 @"inputType" : @{
@"name" :
@"inputName"},
910 [plugin insertText:@"text to insert"];
912 NSDictionary* deltaToFramework = @{
914 @"deltaText" :
@"text to insert",
915 @"deltaStart" : @(0),
917 @"selectionBase" : @(14),
918 @"selectionExtent" : @(14),
919 @"selectionAffinity" :
@"TextAffinity.upstream",
920 @"selectionIsDirectional" : @(
false),
921 @"composingBase" : @(-1),
922 @"composingExtent" : @(-1),
924 NSDictionary* expectedState = @{
925 @"deltas" : @[ deltaToFramework ],
935 [binaryMessengerMock sendOnChannel:
@"flutter/textinput" message:updateCall]);
940 [plugin setMarkedText:@"marked text" selectedRange:NSMakeRange(0, 1)];
942 deltaToFramework = @{
943 @"oldText" :
@"text to insert",
944 @"deltaText" :
@"marked text",
945 @"deltaStart" : @(14),
947 @"selectionBase" : @(25),
948 @"selectionExtent" : @(25),
949 @"selectionAffinity" :
@"TextAffinity.upstream",
950 @"selectionIsDirectional" : @(
false),
951 @"composingBase" : @(14),
952 @"composingExtent" : @(25),
955 @"deltas" : @[ deltaToFramework ],
965 [binaryMessengerMock sendOnChannel:
@"flutter/textinput" message:updateCall]);
972 deltaToFramework = @{
973 @"oldText" :
@"text to insertmarked text",
975 @"deltaStart" : @(-1),
977 @"selectionBase" : @(25),
978 @"selectionExtent" : @(25),
979 @"selectionAffinity" :
@"TextAffinity.upstream",
980 @"selectionIsDirectional" : @(false),
981 @"composingBase" : @(-1),
982 @"composingExtent" : @(-1),
985 @"deltas" : @[ deltaToFramework ],
995 [binaryMessengerMock sendOnChannel:
@"flutter/textinput" message:updateCall]);
1002 - (bool)testComposingWithDelta {
1006 [engineMock binaryMessenger])
1007 .andReturn(binaryMessengerMock);
1016 NSDictionary* setClientConfig = @{
1017 @"inputAction" :
@"action",
1018 @"enableDeltaModel" :
@"true",
1019 @"inputType" : @{
@"name" :
@"inputName"},
1025 [plugin setMarkedText:@"m" selectedRange:NSMakeRange(0, 1)];
1027 NSDictionary* deltaToFramework = @{
1029 @"deltaText" :
@"m",
1030 @"deltaStart" : @(0),
1032 @"selectionBase" : @(1),
1033 @"selectionExtent" : @(1),
1034 @"selectionAffinity" :
@"TextAffinity.upstream",
1035 @"selectionIsDirectional" : @(
false),
1036 @"composingBase" : @(0),
1037 @"composingExtent" : @(1),
1039 NSDictionary* expectedState = @{
1040 @"deltas" : @[ deltaToFramework ],
1050 [binaryMessengerMock sendOnChannel:
@"flutter/textinput" message:updateCall]);
1055 [plugin setMarkedText:@"ma" selectedRange:NSMakeRange(0, 1)];
1057 deltaToFramework = @{
1059 @"deltaText" :
@"ma",
1060 @"deltaStart" : @(0),
1062 @"selectionBase" : @(2),
1063 @"selectionExtent" : @(2),
1064 @"selectionAffinity" :
@"TextAffinity.upstream",
1065 @"selectionIsDirectional" : @(
false),
1066 @"composingBase" : @(0),
1067 @"composingExtent" : @(2),
1070 @"deltas" : @[ deltaToFramework ],
1080 [binaryMessengerMock sendOnChannel:
@"flutter/textinput" message:updateCall]);
1085 [plugin setMarkedText:@"mar" selectedRange:NSMakeRange(0, 1)];
1087 deltaToFramework = @{
1089 @"deltaText" :
@"mar",
1090 @"deltaStart" : @(0),
1092 @"selectionBase" : @(3),
1093 @"selectionExtent" : @(3),
1094 @"selectionAffinity" :
@"TextAffinity.upstream",
1095 @"selectionIsDirectional" : @(
false),
1096 @"composingBase" : @(0),
1097 @"composingExtent" : @(3),
1100 @"deltas" : @[ deltaToFramework ],
1110 [binaryMessengerMock sendOnChannel:
@"flutter/textinput" message:updateCall]);
1115 [plugin setMarkedText:@"mark" selectedRange:NSMakeRange(0, 1)];
1117 deltaToFramework = @{
1118 @"oldText" :
@"mar",
1119 @"deltaText" :
@"mark",
1120 @"deltaStart" : @(0),
1122 @"selectionBase" : @(4),
1123 @"selectionExtent" : @(4),
1124 @"selectionAffinity" :
@"TextAffinity.upstream",
1125 @"selectionIsDirectional" : @(
false),
1126 @"composingBase" : @(0),
1127 @"composingExtent" : @(4),
1130 @"deltas" : @[ deltaToFramework ],
1140 [binaryMessengerMock sendOnChannel:
@"flutter/textinput" message:updateCall]);
1145 [plugin setMarkedText:@"marke" selectedRange:NSMakeRange(0, 1)];
1147 deltaToFramework = @{
1148 @"oldText" :
@"mark",
1149 @"deltaText" :
@"marke",
1150 @"deltaStart" : @(0),
1152 @"selectionBase" : @(5),
1153 @"selectionExtent" : @(5),
1154 @"selectionAffinity" :
@"TextAffinity.upstream",
1155 @"selectionIsDirectional" : @(
false),
1156 @"composingBase" : @(0),
1157 @"composingExtent" : @(5),
1160 @"deltas" : @[ deltaToFramework ],
1170 [binaryMessengerMock sendOnChannel:
@"flutter/textinput" message:updateCall]);
1175 [plugin setMarkedText:@"marked" selectedRange:NSMakeRange(0, 1)];
1177 deltaToFramework = @{
1178 @"oldText" :
@"marke",
1179 @"deltaText" :
@"marked",
1180 @"deltaStart" : @(0),
1182 @"selectionBase" : @(6),
1183 @"selectionExtent" : @(6),
1184 @"selectionAffinity" :
@"TextAffinity.upstream",
1185 @"selectionIsDirectional" : @(
false),
1186 @"composingBase" : @(0),
1187 @"composingExtent" : @(6),
1190 @"deltas" : @[ deltaToFramework ],
1200 [binaryMessengerMock sendOnChannel:
@"flutter/textinput" message:updateCall]);
1205 [plugin unmarkText];
1207 deltaToFramework = @{
1208 @"oldText" :
@"marked",
1210 @"deltaStart" : @(-1),
1211 @"deltaEnd" : @(-1),
1212 @"selectionBase" : @(6),
1213 @"selectionExtent" : @(6),
1214 @"selectionAffinity" :
@"TextAffinity.upstream",
1215 @"selectionIsDirectional" : @(false),
1216 @"composingBase" : @(-1),
1217 @"composingExtent" : @(-1),
1220 @"deltas" : @[ deltaToFramework ],
1230 [binaryMessengerMock sendOnChannel:
@"flutter/textinput" message:updateCall]);
1237 - (bool)testComposingWithDeltasWhenSelectionIsActive {
1241 [engineMock binaryMessenger])
1242 .andReturn(binaryMessengerMock);
1251 NSDictionary* setClientConfig = @{
1252 @"inputAction" :
@"action",
1253 @"enableDeltaModel" :
@"true",
1254 @"inputType" : @{
@"name" :
@"inputName"},
1264 @"selectionBase" : @(0),
1265 @"selectionExtent" : @(4),
1266 @"composingBase" : @(-1),
1267 @"composingExtent" : @(-1),
1273 [plugin setMarkedText:@"~"
1274 selectedRange:NSMakeRange(1, 0)
1275 replacementRange:NSMakeRange(NSNotFound, 0)];
1277 NSDictionary* deltaToFramework = @{
1278 @"oldText" :
@"Text",
1279 @"deltaText" :
@"~",
1280 @"deltaStart" : @(0),
1282 @"selectionBase" : @(1),
1283 @"selectionExtent" : @(1),
1284 @"selectionAffinity" :
@"TextAffinity.upstream",
1285 @"selectionIsDirectional" : @(
false),
1286 @"composingBase" : @(0),
1287 @"composingExtent" : @(1),
1289 NSDictionary* expectedState = @{
1290 @"deltas" : @[ deltaToFramework ],
1300 [binaryMessengerMock sendOnChannel:
@"flutter/textinput" message:updateCall]);
1307 - (bool)testPerformKeyEquivalent {
1308 __block NSEvent* eventBeingDispatchedByKeyboardManager = nil;
1310 OCMStub([viewControllerMock isDispatchingKeyEvent:[OCMArg any]])
1311 .andDo(^(NSInvocation* invocation) {
1313 [invocation getArgument:(void*)&event atIndex:2];
1314 BOOL result =
event == eventBeingDispatchedByKeyboardManager;
1315 [invocation setReturnValue:&result];
1318 NSEvent*
event = [NSEvent keyEventWithType:NSEventTypeKeyDown
1319 location:NSZeroPoint
1325 charactersIgnoringModifiers:@""
1332 OCMExpect([viewControllerMock keyDown:event]);
1335 if (![plugin performKeyEquivalent:event]) {
1341 [viewControllerMock keyDown:event]);
1348 eventBeingDispatchedByKeyboardManager = event;
1350 OCMReject([viewControllerMock keyDown:event]);
1354 if ([plugin performKeyEquivalent:event]) {
1364 - (bool)handleArrowKeyWhenImePopoverIsActive {
1368 [engineMock binaryMessenger])
1369 .andReturn(binaryMessengerMock);
1370 OCMStub([[engineMock ignoringNonObjectArgs] sendKeyEvent:FlutterKeyEvent {}
1374 NSTextInputContext* textInputContext = OCMClassMock([NSTextInputContext
class]);
1375 OCMStub([textInputContext handleEvent:[OCMArg any]]).andReturn(YES);
1386 NSDictionary* setClientConfig = @{
1387 @"inputAction" :
@"action",
1388 @"enableDeltaModel" :
@"true",
1389 @"inputType" : @{
@"name" :
@"inputName"},
1402 [plugin setMarkedText:@"m"
1403 selectedRange:NSMakeRange(0, 1)
1404 replacementRange:NSMakeRange(NSNotFound, 0)];
1407 NSEvent*
event = [NSEvent keyEventWithType:NSEventTypeKeyDown
1408 location:NSZeroPoint
1409 modifierFlags:0xa00100
1413 characters:@"\uF702"
1414 charactersIgnoringModifiers:@"\uF702"
1419 [plugin performKeyEquivalent:event];
1421 if ([plugin handleKeyEvent:event] !=
true) {
1426 event = [NSEvent keyEventWithType:NSEventTypeKeyDown
1427 location:NSZeroPoint
1428 modifierFlags:0x40101
1432 characters:@"\uF702"
1433 charactersIgnoringModifiers:@"\uF702"
1438 [plugin performKeyEquivalent:event];
1440 if ([plugin handleKeyEvent:event] !=
false) {
1447 - (bool)unhandledKeyEquivalent {
1451 [engineMock binaryMessenger])
1452 .andReturn(binaryMessengerMock);
1461 NSDictionary* setClientConfig = @{
1462 @"inputAction" :
@"action",
1463 @"enableDeltaModel" :
@"true",
1464 @"inputType" : @{
@"name" :
@"inputName"},
1477 NSEvent*
event = [NSEvent keyEventWithType:NSEventTypeKeyDown
1478 location:NSZeroPoint
1479 modifierFlags:0x40101
1484 charactersIgnoringModifiers:@"h"
1489 [plugin performKeyEquivalent:event];
1493 if ([plugin handleKeyEvent:event] !=
true) {
1498 event = [NSEvent keyEventWithType:NSEventTypeKeyDown
1499 location:NSZeroPoint
1500 modifierFlags:0x100108
1505 charactersIgnoringModifiers:@"w"
1510 [plugin performKeyEquivalent:event];
1514 if ([plugin handleKeyEvent:event] !=
false) {
1521 - (bool)testInsertNewLine {
1525 [engineMock binaryMessenger])
1526 .andReturn(binaryMessengerMock);
1527 OCMStub([[engineMock ignoringNonObjectArgs] sendKeyEvent:FlutterKeyEvent {}
1538 NSDictionary* setClientConfig = @{
1539 @"inputType" : @{
@"name" :
@"TextInputType.multiline"},
1540 @"inputAction" :
@"TextInputAction.newline",
1550 @"selectionBase" : @(4),
1551 @"selectionExtent" : @(4),
1552 @"composingBase" : @(-1),
1553 @"composingExtent" : @(-1),
1562 EXPECT_STREQ([editingState[
@"text"] UTF8String],
"Text");
1563 EXPECT_STREQ([editingState[
@"selectionAffinity"] UTF8String],
"TextAffinity.upstream");
1564 EXPECT_FALSE([editingState[
@"selectionIsDirectional"] boolValue]);
1565 EXPECT_EQ([editingState[
@"selectionBase"] intValue], 4);
1566 EXPECT_EQ([editingState[
@"selectionExtent"] intValue], 4);
1567 EXPECT_EQ([editingState[
@"composingBase"] intValue], -1);
1568 EXPECT_EQ([editingState[
@"composingExtent"] intValue], -1);
1570 [plugin doCommandBySelector:@selector(insertNewline:)];
1574 EXPECT_STREQ([editingState[
@"text"] UTF8String],
"Text\n");
1575 EXPECT_STREQ([editingState[
@"selectionAffinity"] UTF8String],
"TextAffinity.upstream");
1576 EXPECT_FALSE([editingState[
@"selectionIsDirectional"] boolValue]);
1577 EXPECT_EQ([editingState[
@"selectionBase"] intValue], 5);
1578 EXPECT_EQ([editingState[
@"selectionExtent"] intValue], 5);
1579 EXPECT_EQ([editingState[
@"composingBase"] intValue], -1);
1580 EXPECT_EQ([editingState[
@"composingExtent"] intValue], -1);
1585 - (bool)testSendActionDoNotInsertNewLine {
1589 [engineMock binaryMessenger])
1590 .andReturn(binaryMessengerMock);
1591 OCMStub([[engineMock ignoringNonObjectArgs] sendKeyEvent:FlutterKeyEvent {}
1602 NSDictionary* setClientConfig = @{
1603 @"inputType" : @{
@"name" :
@"TextInputType.multiline"},
1604 @"inputAction" :
@"TextInputAction.send",
1614 @"selectionBase" : @(4),
1615 @"selectionExtent" : @(4),
1616 @"composingBase" : @(-1),
1617 @"composingExtent" : @(-1),
1620 NSDictionary* expectedState = @{
1621 @"selectionBase" : @(4),
1622 @"selectionExtent" : @(4),
1623 @"selectionAffinity" :
@"TextAffinity.upstream",
1624 @"selectionIsDirectional" : @(NO),
1625 @"composingBase" : @(-1),
1626 @"composingExtent" : @(-1),
1636 [binaryMessengerMock sendOnChannel:
@"flutter/textinput" message:updateCall]);
1642 [plugin doCommandBySelector:@selector(insertNewline:)];
1647 arguments:@[ @(1), @"TextInputAction.send" ]]];
1652 [binaryMessengerMock sendOnChannel:
@"flutter/textinput" message:performActionCall]);
1657 NSDictionary* updatedState = @{
1658 @"selectionBase" : @(5),
1659 @"selectionExtent" : @(5),
1660 @"selectionAffinity" :
@"TextAffinity.upstream",
1661 @"selectionIsDirectional" : @(NO),
1662 @"composingBase" : @(-1),
1663 @"composingExtent" : @(-1),
1664 @"text" :
@"Text\n",
1675 [binaryMessengerMock sendOnChannel:
@"flutter/textinput" message:updateCall]);
1684 - (bool)testLocalTextAndSelectionUpdateAfterDelta {
1688 [engineMock binaryMessenger])
1689 .andReturn(binaryMessengerMock);
1698 NSDictionary* setClientConfig = @{
1699 @"inputAction" :
@"action",
1700 @"enableDeltaModel" :
@"true",
1701 @"inputType" : @{
@"name" :
@"inputName"},
1707 [plugin insertText:@"text to insert"];
1709 NSDictionary* deltaToFramework = @{
1711 @"deltaText" :
@"text to insert",
1712 @"deltaStart" : @(0),
1714 @"selectionBase" : @(14),
1715 @"selectionExtent" : @(14),
1716 @"selectionAffinity" :
@"TextAffinity.upstream",
1717 @"selectionIsDirectional" : @(
false),
1718 @"composingBase" : @(-1),
1719 @"composingExtent" : @(-1),
1721 NSDictionary* expectedState = @{
1722 @"deltas" : @[ deltaToFramework ],
1732 [binaryMessengerMock sendOnChannel:
@"flutter/textinput" message:updateCall]);
1737 bool localTextAndSelectionUpdated = [plugin.string isEqualToString:@"text to insert"] &&
1738 NSEqualRanges(plugin.selectedRange, NSMakeRange(14, 0));
1740 return localTextAndSelectionUpdated;
1743 - (bool)testSelectorsAreForwardedToFramework {
1747 [engineMock binaryMessenger])
1748 .andReturn(binaryMessengerMock);
1757 NSDictionary* setClientConfig = @{
1758 @"inputAction" :
@"action",
1759 @"enableDeltaModel" :
@"true",
1760 @"inputType" : @{
@"name" :
@"inputName"},
1769 NSString* runLoopMode =
@"FlutterTestRunLoopMode";
1773 [plugin doCommandBySelector:@selector(moveUp:)];
1774 [plugin doCommandBySelector:@selector(moveRightAndModifySelection:)];
1776 __block
bool done =
false;
1777 CFRunLoopPerformBlock(CFRunLoopGetMain(), (__bridge CFStringRef)runLoopMode, ^{
1783 CFRunLoopRunInMode((__bridge CFStringRef)runLoopMode, 0,
true);
1790 @(1), @[ @"moveUp:", @"moveRightAndModifySelection:" ]
1795 [binaryMessengerMock sendOnChannel:
@"flutter/textinput" message:performSelectorCall]);
1810 NSString* fixtures = @(testing::GetFixturesPath());
1812 initWithAssetsPath:fixtures
1813 ICUDataPath:[fixtures stringByAppendingString:@"/icudtl.dat"]];
1814 return [[
FlutterEngine alloc] initWithName:@"test" project:project allowHeadlessExecution:true];
1818 TEST(FlutterTextInputPluginTest, TestEmptyCompositionRange) {
1822 TEST(FlutterTextInputPluginTest, TestSetMarkedTextWithSelectionChange) {
1826 TEST(FlutterTextInputPluginTest, TestSetMarkedTextWithReplacementRange) {
1830 TEST(FlutterTextInputPluginTest, TestComposingRegionRemovedByFramework) {
1834 TEST(FlutterTextInputPluginTest, TestClearClientDuringComposing) {
1838 TEST(FlutterTextInputPluginTest, TestAutocompleteDisabledWhenAutofillNotSet) {
1842 TEST(FlutterTextInputPluginTest, TestAutocompleteEnabledWhenAutofillSet) {
1846 TEST(FlutterTextInputPluginTest, TestAutocompleteEnabledWhenAutofillSetNoHint) {
1850 TEST(FlutterTextInputPluginTest, TestAutocompleteDisabledWhenObscureTextSet) {
1854 TEST(FlutterTextInputPluginTest, TestAutocompleteDisabledWhenPasswordAutofillSet) {
1858 TEST(FlutterTextInputPluginTest, TestAutocompleteDisabledWhenAutofillGroupIncludesPassword) {
1860 testAutocompleteDisabledWhenAutofillGroupIncludesPassword]);
1863 TEST(FlutterTextInputPluginTest, TestFirstRectForCharacterRange) {
1867 TEST(FlutterTextInputPluginTest, TestFirstRectForCharacterRangeAtInfinity) {
1871 TEST(FlutterTextInputPluginTest, TestFirstRectForCharacterRangeWithEsotericAffineTransform) {
1873 testFirstRectForCharacterRangeWithEsotericAffineTransform]);
1876 TEST(FlutterTextInputPluginTest, TestSetEditingStateWithTextEditingDelta) {
1880 TEST(FlutterTextInputPluginTest, TestOperationsThatTriggerDelta) {
1884 TEST(FlutterTextInputPluginTest, TestComposingWithDelta) {
1888 TEST(FlutterTextInputPluginTest, testComposingWithDeltasWhenSelectionIsActive) {
1892 TEST(FlutterTextInputPluginTest, TestLocalTextAndSelectionUpdateAfterDelta) {
1896 TEST(FlutterTextInputPluginTest, TestPerformKeyEquivalent) {
1900 TEST(FlutterTextInputPluginTest, HandleArrowKeyWhenImePopoverIsActive) {
1904 TEST(FlutterTextInputPluginTest, UnhandledKeyEquivalent) {
1908 TEST(FlutterTextInputPluginTest, TestSelectorsAreForwardedToFramework) {
1912 TEST(FlutterTextInputPluginTest, TestInsertNewLine) {
1916 TEST(FlutterTextInputPluginTest, TestSendActionDoNotInsertNewLine) {
1920 TEST(FlutterTextInputPluginTest, CanWorkWithFlutterTextField) {
1925 [viewController loadView];
1927 NSWindow* window = [[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, 800, 600)
1928 styleMask:NSBorderlessWindowMask
1929 backing:NSBackingStoreBuffered
1931 window.contentView = viewController.view;
1933 engine.semanticsEnabled = YES;
1935 auto bridge = viewController.accessibilityBridge.lock();
1938 ui::AXNode ax_node(&tree,
nullptr, 0, 0);
1939 ui::AXNodeData node_data;
1940 node_data.SetValue(
"initial text");
1941 ax_node.SetData(node_data);
1942 delegate.
Init(viewController.accessibilityBridge, &ax_node);
1948 fieldEditor:viewController.textInputPlugin];
1949 [viewController.view addSubview:mockTextField];
1952 NSDictionary* setClientConfig = @{
1953 @"inputAction" :
@"action",
1954 @"inputType" : @{
@"name" :
@"inputName"},
1961 [viewController.textInputPlugin handleMethodCall:methodCall result:result];
1963 NSDictionary* arguments = @{
1964 @"text" :
@"new text",
1965 @"selectionBase" : @(1),
1966 @"selectionExtent" : @(2),
1967 @"composingBase" : @(-1),
1968 @"composingExtent" : @(-1),
1972 [viewController.textInputPlugin handleMethodCall:methodCall result:result];
1978 [mockTextField setPlatformNode:nil];
1982 [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
1985 TEST(FlutterTextInputPluginTest, CanNotBecomeResponderIfNoViewController) {
1990 [viewController loadView];
1992 NSWindow* window = [[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, 800, 600)
1993 styleMask:NSBorderlessWindowMask
1994 backing:NSBackingStoreBuffered
1996 window.contentView = viewController.view;
1998 engine.semanticsEnabled = YES;
2000 auto bridge = viewController.accessibilityBridge.lock();
2003 ui::AXNode ax_node(&tree,
nullptr, 0, 0);
2004 ui::AXNodeData node_data;
2005 node_data.SetValue(
"initial text");
2006 ax_node.SetData(node_data);
2007 delegate.
Init(viewController.accessibilityBridge, &ax_node);
2011 EXPECT_EQ([textField becomeFirstResponder], YES);
2013 [engine setViewController:nil];
2016 EXPECT_EQ([textField becomeFirstResponder], NO);
2019 TEST(FlutterTextInputPluginTest, IsAddedAndRemovedFromViewHierarchy) {
2024 [viewController loadView];
2026 NSWindow* window = [[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, 800, 600)
2027 styleMask:NSBorderlessWindowMask
2028 backing:NSBackingStoreBuffered
2030 window.contentView = viewController.view;
2032 ASSERT_EQ(viewController.textInputPlugin.superview, nil);
2033 ASSERT_FALSE(window.firstResponder == viewController.textInputPlugin);
2035 [viewController.textInputPlugin
2040 ASSERT_EQ(viewController.textInputPlugin.superview, viewController.view);
2041 ASSERT_TRUE(window.firstResponder == viewController.textInputPlugin);
2043 [viewController.textInputPlugin
2048 ASSERT_EQ(viewController.textInputPlugin.superview, nil);
2049 ASSERT_FALSE(window.firstResponder == viewController.textInputPlugin);
2052 TEST(FlutterTextInputPluginTest, HasZeroSizeAndClipsToBounds) {
2056 [engineMock binaryMessenger])
2057 .andReturn(binaryMessengerMock);
2066 ASSERT_TRUE(NSIsEmptyRect(plugin.frame));
2067 ASSERT_TRUE(plugin.clipsToBounds);