Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit 60af9a5

Browse files
add test
1 parent 8f828bf commit 60af9a5

File tree

5 files changed

+154
-45
lines changed

5 files changed

+154
-45
lines changed

shell/platform/darwin/ios/framework/Source/FlutterEngine.mm

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -536,7 +536,7 @@ - (void)updateEditingClient:(int)client withState:(NSDictionary*)state {
536536

537537
- (void)updateEditingClient:(int)client withState:(NSDictionary*)state withTag:(NSString*)tag {
538538
[_textInputChannel.get() invokeMethod:@"TextInputClient.updateEditingStateWithTag"
539-
arguments:@[ @(client), state, tag ]];
539+
arguments:@[ @(client), @{tag : state} ]];
540540
}
541541

542542
- (void)updateFloatingCursor:(FlutterFloatingCursorDragState)state

shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.h

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@
1010
#include "flutter/shell/platform/darwin/common/framework/Headers/FlutterChannels.h"
1111
#include "flutter/shell/platform/darwin/ios/framework/Source/FlutterTextInputDelegate.h"
1212

13+
#if FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG
14+
FLUTTER_EXPORT
15+
#endif
1316
@interface FlutterTextInputPlugin : NSObject
1417

1518
@property(nonatomic, assign) id<FlutterTextInputDelegate> textInputDelegate;
@@ -36,6 +39,9 @@
3639
@end
3740

3841
/** A range of text in the buffer of a Flutter text editing widget. */
42+
#if FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG
43+
FLUTTER_EXPORT
44+
#endif
3945
@interface FlutterTextRange : UITextRange <NSCopying>
4046

4147
@property(nonatomic, readonly) NSRange range;
@@ -44,4 +50,34 @@
4450

4551
@end
4652

53+
#if FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG
54+
FLUTTER_EXPORT
55+
#endif
56+
@interface FlutterTextInputView : UIView <UITextInput>
57+
58+
// UITextInput
59+
@property(nonatomic, readonly) NSMutableString* text;
60+
@property(nonatomic, readonly) NSMutableString* markedText;
61+
@property(readwrite, copy) UITextRange* selectedTextRange;
62+
@property(nonatomic, strong) UITextRange* markedTextRange;
63+
@property(nonatomic, copy) NSDictionary* markedTextStyle;
64+
@property(nonatomic, assign) id<UITextInputDelegate> inputDelegate;
65+
66+
// UITextInputTraits
67+
@property(nonatomic) UITextAutocapitalizationType autocapitalizationType;
68+
@property(nonatomic) UITextAutocorrectionType autocorrectionType;
69+
@property(nonatomic) UITextSpellCheckingType spellCheckingType;
70+
@property(nonatomic) BOOL enablesReturnKeyAutomatically;
71+
@property(nonatomic) UIKeyboardAppearance keyboardAppearance;
72+
@property(nonatomic) UIKeyboardType keyboardType;
73+
@property(nonatomic) UIReturnKeyType returnKeyType;
74+
@property(nonatomic, getter=isSecureTextEntry) BOOL secureTextEntry;
75+
@property(nonatomic) UITextSmartQuotesType smartQuotesType API_AVAILABLE(ios(11.0));
76+
@property(nonatomic) UITextSmartDashesType smartDashesType API_AVAILABLE(ios(11.0));
77+
@property(nonatomic, copy) UITextContentType textContentType API_AVAILABLE(ios(10.0));
78+
79+
@property(nonatomic, assign) id<FlutterTextInputDelegate> textInputDelegate;
80+
81+
@end
82+
4783
#endif // SHELL_PLATFORM_IOS_FRAMEWORK_SOURCE_FLUTTERTEXTINPUTPLUGIN_H_

shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.mm

Lines changed: 34 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -88,11 +88,18 @@ static UIReturnKeyType ToUIReturnKeyType(NSString* inputType) {
8888
return UIReturnKeyDefault;
8989
}
9090

91+
// TODO(LongCatIsLooong): add translation for evey predefined
92+
// UITextContentType.
93+
static UITextContentType ToUITextContentType(NSArray<NSString*>* hints) {
94+
if (hints == nil || hints.count == 0)
95+
return @"";
96+
97+
return hints[0];
98+
}
99+
91100
static NSString* _uniqueIdFromDictionary(NSDictionary* dictionary) {
92101
NSDictionary* autofill = dictionary[@"autofill"];
93-
if (autofill == nil)
94-
return nil;
95-
return autofill[@"uniqueIdentifier"];
102+
return autofill == nil ? nil : autofill[@"uniqueIdentifier"];
96103
}
97104

98105
#pragma mark - FlutterTextPosition
@@ -147,34 +154,8 @@ - (id)copyWithZone:(NSZone*)zone {
147154

148155
@end
149156

150-
@interface FlutterTextInputView : UIView <UITextInput>
151-
152-
// UITextInput
153-
@property(nonatomic, readonly) NSMutableString* text;
154-
@property(nonatomic, readonly) NSMutableString* markedText;
155-
@property(readwrite, copy) UITextRange* selectedTextRange;
156-
@property(nonatomic, strong) UITextRange* markedTextRange;
157-
@property(nonatomic, copy) NSDictionary* markedTextStyle;
158-
@property(nonatomic, assign) id<UITextInputDelegate> inputDelegate;
159-
160-
// UITextInputTraits
161-
@property(nonatomic) UITextAutocapitalizationType autocapitalizationType;
162-
@property(nonatomic) UITextAutocorrectionType autocorrectionType;
163-
@property(nonatomic) UITextSpellCheckingType spellCheckingType;
164-
@property(nonatomic) BOOL enablesReturnKeyAutomatically;
165-
@property(nonatomic) UIKeyboardAppearance keyboardAppearance;
166-
@property(nonatomic) UIKeyboardType keyboardType;
167-
@property(nonatomic) UIReturnKeyType returnKeyType;
168-
@property(nonatomic, getter=isSecureTextEntry) BOOL secureTextEntry;
169-
@property(nonatomic) UITextSmartQuotesType smartQuotesType API_AVAILABLE(ios(11.0));
170-
@property(nonatomic) UITextSmartDashesType smartDashesType API_AVAILABLE(ios(11.0));
171-
172-
@property(nonatomic, assign) id<FlutterTextInputDelegate> textInputDelegate;
173-
174-
@end
175-
176157
@implementation FlutterTextInputView {
177-
NSString* _uniqueIdentifier;
158+
NSString* _autofillId;
178159
int _textInputClient;
179160
const char* _selectionAffinity;
180161
FlutterTextRange* _selectedTextRange;
@@ -218,15 +199,18 @@ - (void)dealloc {
218199
[_markedTextRange release];
219200
[_selectedTextRange release];
220201
[_tokenizer release];
221-
if (_uniqueIdentifier != nil)
222-
[_uniqueIdentifier release];
202+
[_autofillId release];
223203
[super dealloc];
224204
}
225205

226206
- (void)setTextInputClient:(int)client {
227207
_textInputClient = client;
228208
}
229209

210+
- (void)setAutofillId:(NSString*)autofillId {
211+
_autofillId = [autofillId copy];
212+
}
213+
230214
- (void)setTextInputState:(NSDictionary*)state {
231215
NSString* newText = state[@"text"];
232216
BOOL textChanged = ![self.text isEqualToString:newText];
@@ -637,10 +621,8 @@ - (void)updateEditingState {
637621
@"text" : [NSString stringWithString:self.text],
638622
};
639623

640-
if (_textInputClient == 0 && _uniqueIdentifier != nil)
641-
[_textInputDelegate updateEditingClient:_textInputClient
642-
withState:state
643-
withTag:_uniqueIdentifier];
624+
if (_textInputClient == 0 && _autofillId != nil)
625+
[_textInputDelegate updateEditingClient:_textInputClient withState:state withTag:_autofillId];
644626
else
645627
[_textInputDelegate updateEditingClient:_textInputClient withState:state];
646628
}
@@ -810,9 +792,13 @@ - (void)setTextInputClient:(int)client withConfiguration:(NSDictionary*)configur
810792

811793
for (NSDictionary* field in allFields) {
812794
FlutterTextInputView* newInputView = [[FlutterTextInputView alloc] init];
795+
newInputView.textInputDelegate = _textInputDelegate;
813796
[_inputViews addObject:newInputView];
814797

815-
if ([clientUniqueId isEqualToString:_uniqueIdFromDictionary(field)])
798+
NSString* autofillId = _uniqueIdFromDictionary(field);
799+
[newInputView setAutofillId:autofillId];
800+
801+
if ([clientUniqueId isEqualToString:autofillId])
816802
_activeView = newInputView;
817803

818804
[FlutterTextInputPlugin setupInputView:newInputView WithConfiguration:field];
@@ -858,14 +844,17 @@ + (void)setupInputView:(FlutterTextInputView*)inputView
858844
inputView.autocorrectionType = autocorrect && ![autocorrect boolValue]
859845
? UITextAutocorrectionTypeNo
860846
: UITextAutocorrectionTypeDefault;
861-
if (autofill == nil) {
862-
if (@available(iOS 10.0, *))
847+
if (@available(iOS 10.0, *)) {
848+
if (autofill == nil) {
863849
inputView.textContentType = @"";
864-
} else {
865-
if (@available(iOS 10.0, *))
866-
inputView.textContentType = autofill[@"hints"];
867-
868-
[inputView setTextInputState:autofill[@"editingValue"]];
850+
} else {
851+
inputView.textContentType = ToUITextContentType(autofill[@"hints"]);
852+
[inputView setTextInputState:autofill[@"editingValue"]];
853+
// An input field needs to be visible in order to get
854+
// autofilled when it's not the one that triggered
855+
// autofill.
856+
inputView.frame = CGRectMake(0, 0, 1, 1);
857+
}
869858
}
870859
}
871860

@@ -876,4 +865,5 @@ - (void)setTextInputEditingState:(NSDictionary*)state {
876865
- (void)clearTextInputClient {
877866
[_activeView setTextInputClient:0];
878867
}
868+
879869
@end
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
#import <OCMock/OCMock.h>
6+
#import <XCTest/XCTest.h>
7+
#include "flutter/shell/platform/darwin/common/framework/Headers/FlutterMacros.h"
8+
#import "flutter/shell/platform/darwin/ios/framework/Headers/FlutterEngine.h"
9+
#include "flutter/shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.h"
10+
11+
FLUTTER_ASSERT_ARC
12+
13+
@interface FlutterTextInputPluginTest : XCTestCase
14+
@end
15+
16+
@implementation FlutterTextInputPluginTest
17+
18+
- (void)testAutofillInputViews {
19+
// Setup test.
20+
id engine = OCMClassMock([FlutterEngine class]);
21+
FlutterTextInputPlugin* textInputPlugin = [[FlutterTextInputPlugin alloc] init];
22+
textInputPlugin.textInputDelegate = engine;
23+
24+
NSDictionary* template = @{
25+
@"inputType" : @{@"name" : @"TextInuptType.text"},
26+
@"keyboardAppearance" : @"Brightness.light",
27+
@"obscureText" : @NO,
28+
@"inputAction" : @"TextInputAction.unspecified",
29+
@"smartDashesType" : @"0",
30+
@"smartQuotesType" : @"0",
31+
@"autocorrect" : @YES
32+
};
33+
34+
NSMutableDictionary* field1 = [template mutableCopy];
35+
[field1 setValue:@{
36+
@"uniqueIdentifier" : @"field1",
37+
@"hints" : @[ @"hint1" ],
38+
@"editingValue" : @{@"text" : @""}
39+
}
40+
forKey:@"autofill"];
41+
42+
NSMutableDictionary* field2 = [template mutableCopy];
43+
[field2 setValue:@{
44+
@"uniqueIdentifier" : @"field2",
45+
@"hints" : @[ @"hint2" ],
46+
@"editingValue" : @{@"text" : @""}
47+
}
48+
forKey:@"autofill"];
49+
50+
NSMutableDictionary* config = [field1 mutableCopy];
51+
[config setValue:@[ field1, field2 ] forKey:@"allFields"];
52+
53+
FlutterMethodCall* setClientCall =
54+
[FlutterMethodCall methodCallWithMethodName:@"TextInput.setClient"
55+
arguments:@[ @123, config ]];
56+
57+
[textInputPlugin handleMethodCall:setClientCall
58+
result:^(id _Nullable result){
59+
}];
60+
61+
// Find all input views in the input hider view.
62+
NSArray<FlutterTextInputView*>* inputFields =
63+
[[[textInputPlugin textInputView] superview] subviews];
64+
65+
XCTAssertEqual(inputFields.count, 2);
66+
67+
// Find the inactive autofillable input field.
68+
FlutterTextInputView* inactiveView = inputFields[1];
69+
[inactiveView replaceRange:[FlutterTextRange rangeWithNSRange:NSMakeRange(0, 0)]
70+
withText:@"Autofilled!"];
71+
72+
// Verify behavior.
73+
OCMVerify([engine updateEditingClient:0 withState:[OCMArg isNotNil] withTag:@"field2"]);
74+
75+
// Clean up mocks
76+
[engine stopMocking];
77+
}
78+
79+
@end

testing/ios/IosUnitTests/IosUnitTests.xcodeproj/project.pbxproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
0D6AB6EB22BB40E700EEE540 /* FlutterEngineTest.mm in Sources */ = {isa = PBXBuildFile; fileRef = 0D6AB6E722BB40CF00EEE540 /* FlutterEngineTest.mm */; settings = {COMPILER_FLAGS = "-fobjc-arc"; }; };
2121
0D6AB72C22BC339F00EEE540 /* libOCMock.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 0D6AB72522BC336100EEE540 /* libOCMock.a */; };
2222
0D6AB73F22BD8F0200EEE540 /* FlutterEngineConfig.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 0D6AB73E22BD8F0200EEE540 /* FlutterEngineConfig.xcconfig */; };
23+
3D3E3323243679E800DE8862 /* FlutterTextInputPluginTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 3D3E331C243679E800DE8862 /* FlutterTextInputPluginTest.m */; settings = {COMPILER_FLAGS = "-fobjc-arc"; }; };
2324
/* End PBXBuildFile section */
2425

2526
/* Begin PBXContainerItemProxy section */
@@ -94,6 +95,7 @@
9495
0D6AB6E722BB40CF00EEE540 /* FlutterEngineTest.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FlutterEngineTest.mm; sourceTree = "<group>"; };
9596
0D6AB71722BC336100EEE540 /* OCMock.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = OCMock.xcodeproj; path = ../../../../../third_party/ocmock/Source/OCMock.xcodeproj; sourceTree = "<group>"; };
9697
0D6AB73E22BD8F0200EEE540 /* FlutterEngineConfig.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = FlutterEngineConfig.xcconfig; sourceTree = "<group>"; };
98+
3D3E331C243679E800DE8862 /* FlutterTextInputPluginTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FlutterTextInputPluginTest.m; sourceTree = "<group>"; };
9799
/* End PBXFileReference section */
98100

99101
/* Begin PBXFrameworksBuildPhase section */
@@ -175,6 +177,7 @@
175177
isa = PBXGroup;
176178
children = (
177179
0D52D3B622C566D50011DEBD /* FlutterBinaryMessengerRelayTest.mm */,
180+
3D3E331C243679E800DE8862 /* FlutterTextInputPluginTest.m */,
178181
0D6AB6E722BB40CF00EEE540 /* FlutterEngineTest.mm */,
179182
0D17A5BF22D78FCD0057279F /* FlutterViewControllerTest.m */,
180183
0D4C3FAF22DF9F5300A67C70 /* FlutterPluginAppLifeCycleDelegateTest.m */,
@@ -387,6 +390,7 @@
387390
buildActionMask = 2147483647;
388391
files = (
389392
0D6AB6EB22BB40E700EEE540 /* FlutterEngineTest.mm in Sources */,
393+
3D3E3323243679E800DE8862 /* FlutterTextInputPluginTest.m in Sources */,
390394
0D17A5C022D78FCD0057279F /* FlutterViewControllerTest.m in Sources */,
391395
0D1CE5D8233430F400E5D880 /* FlutterChannelsTest.m in Sources */,
392396
0D52D3BD22C566D50011DEBD /* FlutterBinaryMessengerRelayTest.mm in Sources */,

0 commit comments

Comments
 (0)