Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(381)

Unified Diff: ios/chrome/browser/autofill/form_suggestion_controller_unittest.mm

Issue 2580363002: Upstream Chrome on iOS source code [1/11]. (Closed)
Patch Set: Created 4 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: ios/chrome/browser/autofill/form_suggestion_controller_unittest.mm
diff --git a/ios/chrome/browser/autofill/form_suggestion_controller_unittest.mm b/ios/chrome/browser/autofill/form_suggestion_controller_unittest.mm
new file mode 100644
index 0000000000000000000000000000000000000000..68250b9e4c198689c9b45e4106d64a05d6371c3c
--- /dev/null
+++ b/ios/chrome/browser/autofill/form_suggestion_controller_unittest.mm
@@ -0,0 +1,451 @@
+// Copyright 2014 The Chromium 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 "ios/chrome/browser/autofill/form_suggestion_controller.h"
+
+#include <utility>
+#include <vector>
+
+#include "base/ios/ios_util.h"
+#include "base/mac/foundation_util.h"
+#import "components/autofill/ios/browser/form_suggestion.h"
+#import "ios/chrome/browser/autofill/form_input_accessory_view_controller.h"
+#import "ios/chrome/browser/autofill/form_suggestion_provider.h"
+#import "ios/chrome/browser/autofill/form_suggestion_view.h"
+#include "ios/chrome/browser/ui/ui_util.h"
+#import "ios/chrome/browser/web/chrome_web_test.h"
+#include "ios/chrome/test/base/scoped_block_swizzler.h"
+#import "ios/web/public/web_state/crw_web_view_proxy.h"
+#import "ios/web/web_state/ui/crw_web_controller.h"
+#import "testing/gtest_mac.h"
+#import "third_party/ocmock/OCMock/OCMock.h"
+#include "third_party/ocmock/gtest_support.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+@interface FormInputAccessoryViewController (Testing)
+- (instancetype)initWithWebState:(web::WebState*)webState
+ JSSuggestionManager:(JsSuggestionManager*)JSSuggestionManager
+ providers:(NSArray*)providers;
+@end
+
+// Test provider that records invocations of its interface methods.
+@interface TestSuggestionProvider : NSObject<FormSuggestionProvider>
+
+@property(weak, nonatomic, readonly) FormSuggestion* suggestion;
+@property(weak, nonatomic, readonly) NSString* formName;
+@property(weak, nonatomic, readonly) NSString* fieldName;
+@property(nonatomic, assign) BOOL selected;
+@property(nonatomic, assign) BOOL askedIfSuggestionsAvailable;
+@property(nonatomic, assign) BOOL askedForSuggestions;
+
+- (instancetype)initWithSuggestions:(NSArray*)suggestions;
+
+@end
+
+@implementation TestSuggestionProvider {
+ NSArray* _suggestions;
+ NSString* _formName;
+ NSString* _fieldName;
+ FormSuggestion* _suggestion;
+}
+
+@synthesize selected = _selected;
+@synthesize askedIfSuggestionsAvailable = _askedIfSuggestionsAvailable;
+@synthesize askedForSuggestions = _askedForSuggestions;
+
+- (instancetype)initWithSuggestions:(NSArray*)suggestions {
+ self = [super init];
+ if (self)
+ _suggestions = [suggestions copy];
+ return self;
+}
+
+- (NSString*)formName {
+ return _formName;
+}
+
+- (NSString*)fieldName {
+ return _fieldName;
+}
+
+- (FormSuggestion*)suggestion {
+ return _suggestion;
+}
+
+- (void)checkIfSuggestionsAvailableForForm:(NSString*)formName
+ field:(NSString*)fieldName
+ type:(NSString*)type
+ typedValue:(NSString*)typedValue
+ webState:(web::WebState*)webState
+ completionHandler:
+ (SuggestionsAvailableCompletion)completion {
+ self.askedIfSuggestionsAvailable = YES;
+ completion([_suggestions count] > 0);
+}
+
+- (void)retrieveSuggestionsForForm:(NSString*)formName
+ field:(NSString*)fieldName
+ type:(NSString*)type
+ typedValue:(NSString*)typedValue
+ webState:(web::WebState*)webState
+ completionHandler:(SuggestionsReadyCompletion)completion {
+ self.askedForSuggestions = YES;
+ completion(_suggestions, self);
+}
+
+- (void)didSelectSuggestion:(FormSuggestion*)suggestion
+ forField:(NSString*)fieldName
+ form:(NSString*)formName
+ completionHandler:(SuggestionHandledCompletion)completion {
+ self.selected = YES;
+ _suggestion = suggestion;
+ _formName = [formName copy];
+ _fieldName = [fieldName copy];
+ completion();
+}
+
+@end
+
+namespace {
+
+// Finds the FormSuggestionView in |parent|'s view hierarchy, if it exists.
+FormSuggestionView* GetSuggestionView(UIView* parent) {
+ if ([parent isKindOfClass:[FormSuggestionView class]])
+ return base::mac::ObjCCastStrict<FormSuggestionView>(parent);
+ for (UIView* child in parent.subviews) {
+ UIView* suggestion_view = GetSuggestionView(child);
+ if (suggestion_view)
+ return base::mac::ObjCCastStrict<FormSuggestionView>(suggestion_view);
+ }
+ return nil;
+}
+
+// Test fixture for FormSuggestionController testing.
+class FormSuggestionControllerTest : public ChromeWebTest {
+ public:
+ FormSuggestionControllerTest() {}
+
+ void SetUp() override {
+ ChromeWebTest::SetUp();
+
+ // Mock out the JsSuggestionManager.
+ mock_js_suggestion_manager_ =
+ [OCMockObject niceMockForClass:[JsSuggestionManager class]];
+
+ // Set up a fake keyboard accessory view. It is expected to have two
+ // subviews.
+ input_accessory_view_ = [[UIView alloc] init];
+ UIView* fake_view_1 = [[UIView alloc] init];
+ [input_accessory_view_ addSubview:fake_view_1];
+ UIView* fake_view_2 = [[UIView alloc] init];
+ [input_accessory_view_ addSubview:fake_view_2];
+
+ // Return the fake keyboard accessory view from the mock CRWWebViewProxy.
+ mock_web_view_proxy_ =
+ [OCMockObject niceMockForProtocol:@protocol(CRWWebViewProxy)];
+ [[[mock_web_view_proxy_ stub] andReturn:input_accessory_view_]
+ keyboardAccessory];
+ }
+
+ void TearDown() override {
+ [suggestion_controller_ detachFromWebState];
+ ChromeWebTest::TearDown();
+ }
+
+ // Sets |url| to be current for WebState.
+ void SetCurrentUrl(const std::string& url) { LoadHtml(@"", GURL(url)); }
+
+ // Swizzles the current web controller to set whether the content is HTML.
+ void SetContentIsHtml(BOOL content_is_html) {
+ id content_is_html_block = ^BOOL(CRWWebController* webController) {
+ return content_is_html;
+ };
+ content_is_html_swizzler_.reset(new ScopedBlockSwizzler(
+ [CRWWebController class], @selector(contentIsHTML),
+ content_is_html_block));
+ }
+
+ protected:
+ // Sets up |suggestion_controller_| with the specified array of
+ // FormSuggestionProviders.
+ void SetUpController(NSArray* providers) {
+ suggestion_controller_ = [[FormSuggestionController alloc]
+ initWithWebState:web_state()
+ providers:providers
+ JsSuggestionManager:mock_js_suggestion_manager_];
+ [suggestion_controller_ setWebViewProxy:mock_web_view_proxy_];
+ @autoreleasepool {
+ accessory_controller_ = [[FormInputAccessoryViewController alloc]
+ initWithWebState:web_state()
+ JSSuggestionManager:mock_js_suggestion_manager_
+ providers:@[
+ [suggestion_controller_ accessoryViewProvider]
+ ]];
+ }
+ // Mock out the FormInputAccessoryViewController so it can use the fake
+ // CRWWebViewProxy
+ id mock_accessory_controller =
+ [OCMockObject partialMockForObject:accessory_controller_];
+ [[[mock_accessory_controller stub] andReturn:mock_web_view_proxy_]
+ webViewProxy];
+
+ // On iPad devices, the suggestion view is added directly to the
+ // keyboard view instead of to the input accessory view which is no longer
+ // available on iPad devices. The following code mocks out the methods on
+ // FormInputAccessoryViewController that add and remove the suggestion view.
+ // The mocks now just add and remove it directly to and from
+ // input_accessory_view_ so that the tests can locate it with
+ // GetSuggestionView (defined above).
+ // TODO(crbug.com/661622): Revisit this to see if there's a better way to
+ // test the iPad case. At a minimum, the name 'input_accessory_view_' should
+ // be made more generic.
+ if (IsIPadIdiom()) {
+ void (^mockShow)(NSInvocation*) = ^(NSInvocation* invocation) {
+ __unsafe_unretained UIView* view;
+ [invocation getArgument:&view atIndex:2];
+ for (UIView* view in [input_accessory_view_ subviews]) {
+ [view removeFromSuperview];
+ }
+ [input_accessory_view_ addSubview:view];
+ };
+ [[[mock_accessory_controller stub] andDo:mockShow]
+ showCustomInputAccessoryView:[OCMArg any]];
+
+ void (^mockRestore)(NSInvocation*) = ^(NSInvocation* invocation) {
+ for (UIView* view in [input_accessory_view_ subviews]) {
+ [view removeFromSuperview];
+ }
+ };
+ [[[mock_accessory_controller stub] andDo:mockRestore]
+ restoreDefaultInputAccessoryView];
+ }
+ }
+
+ // Swizzler for [CRWWebController contentIsHTML].
+ std::unique_ptr<ScopedBlockSwizzler> content_is_html_swizzler_;
+
+ // The FormSuggestionController under test.
+ FormSuggestionController* suggestion_controller_;
+
+ // A fake keyboard accessory view.
+ UIView* input_accessory_view_;
+
+ // Mock JsSuggestionManager for verifying interactions.
+ id mock_js_suggestion_manager_;
+
+ // Mock CRWWebViewProxy for verifying interactions.
+ id mock_web_view_proxy_;
+
+ // Accessory view controller.
+ FormInputAccessoryViewController* accessory_controller_;
+
+ DISALLOW_COPY_AND_ASSIGN(FormSuggestionControllerTest);
+};
+
+// Tests that pages whose URLs don't have a web scheme aren't processed.
+TEST_F(FormSuggestionControllerTest, PageLoadShouldBeIgnoredWhenNotWebScheme) {
+ SetUpController(@[]);
+ SetCurrentUrl("data:text/html;charset=utf8;base64,");
+ [suggestion_controller_ webStateDidLoadPage:web_state()];
+
+ EXPECT_FALSE(GetSuggestionView(input_accessory_view_));
+ EXPECT_OCMOCK_VERIFY(mock_js_suggestion_manager_);
+}
+
+// Tests that pages whose content isn't HTML aren't processed.
+TEST_F(FormSuggestionControllerTest, PageLoadShouldBeIgnoredWhenNotHtml) {
+ SetUpController(@[]);
+ SetCurrentUrl("http://foo.com");
+ SetContentIsHtml(NO);
+ [suggestion_controller_ webStateDidLoadPage:web_state()];
+
+ EXPECT_FALSE(GetSuggestionView(input_accessory_view_));
+ EXPECT_OCMOCK_VERIFY(mock_js_suggestion_manager_);
+}
+
+// Tests that the keyboard accessory view is reset and JavaScript is injected
+// when a page is loaded.
+TEST_F(FormSuggestionControllerTest,
+ PageLoadShouldRestoreKeyboardAccessoryViewAndInjectJavaScript) {
+ SetUpController(@[]);
+ SetCurrentUrl("http://foo.com");
+
+ // Load the page. The JS should be injected.
+ [[mock_js_suggestion_manager_ expect] inject];
+ [suggestion_controller_ webStateDidLoadPage:web_state()];
+ EXPECT_OCMOCK_VERIFY(mock_js_suggestion_manager_);
+
+ // Trigger form activity, which should set up the suggestions view.
+ [accessory_controller_ webState:web_state()
+ didRegisterFormActivityWithFormNamed:"form"
+ fieldName:"field"
+ type:"type"
+ value:"value"
+ keyCode:web::WebStateObserver::
+ kInvalidFormKeyCode
+ inputMissing:false];
+ EXPECT_TRUE(GetSuggestionView(input_accessory_view_));
+
+ // Trigger another page load. The suggestions accessory view should
+ // not be present.
+ [accessory_controller_ webStateDidLoadPage:web_state()];
+ EXPECT_FALSE(GetSuggestionView(input_accessory_view_));
+}
+
+// Tests that "blur" events are ignored.
+TEST_F(FormSuggestionControllerTest, FormActivityBlurShouldBeIgnored) {
+ [accessory_controller_ webState:web_state()
+ didRegisterFormActivityWithFormNamed:"form"
+ fieldName:"field"
+ type:"blur" // blur!
+ value:"value"
+ keyCode:web::WebStateObserver::
+ kInvalidFormKeyCode
+ inputMissing:false];
+ EXPECT_FALSE(GetSuggestionView(input_accessory_view_));
+}
+
+// Tests that no suggestions are displayed when no providers are registered.
+TEST_F(FormSuggestionControllerTest,
+ FormActivityShouldRetrieveSuggestions_NoProvidersAvailable) {
+ // Set up the controller without any providers.
+ SetUpController(@[]);
+ SetCurrentUrl("http://foo.com");
+ [accessory_controller_ webState:web_state()
+ didRegisterFormActivityWithFormNamed:"form"
+ fieldName:"field"
+ type:"type"
+ value:"value"
+ keyCode:web::WebStateObserver::
+ kInvalidFormKeyCode
+ inputMissing:false];
+
+ // The suggestions accessory view should be empty.
+ FormSuggestionView* suggestionView = GetSuggestionView(input_accessory_view_);
+ EXPECT_TRUE(suggestionView);
+ EXPECT_EQ(0U, [suggestionView.suggestions count]);
+}
+
+// Tests that, when no providers have suggestions to offer for a form/field,
+// they aren't asked and no suggestions are displayed.
+TEST_F(FormSuggestionControllerTest,
+ FormActivityShouldRetrieveSuggestions_NoSuggestionsAvailable) {
+ // Set up the controller with some providers, but none of them will
+ // have suggestions available.
+ TestSuggestionProvider* provider1 =
+ [[TestSuggestionProvider alloc] initWithSuggestions:@[]];
+ TestSuggestionProvider* provider2 =
+ [[TestSuggestionProvider alloc] initWithSuggestions:@[]];
+ SetUpController(@[ provider1, provider2 ]);
+ SetCurrentUrl("http://foo.com");
+
+ [accessory_controller_ webState:web_state()
+ didRegisterFormActivityWithFormNamed:"form"
+ fieldName:"field"
+ type:"type"
+ value:"value"
+ keyCode:web::WebStateObserver::
+ kInvalidFormKeyCode
+ inputMissing:false];
+
+ // The providers should each be asked if they have suggestions for the
+ // form in question.
+ EXPECT_TRUE([provider1 askedIfSuggestionsAvailable]);
+ EXPECT_TRUE([provider2 askedIfSuggestionsAvailable]);
+
+ // Since none of the providers had suggestions available, none of them
+ // should have been asked for suggestions.
+ EXPECT_FALSE([provider1 askedForSuggestions]);
+ EXPECT_FALSE([provider2 askedForSuggestions]);
+
+ // The accessory view should be empty.
+ FormSuggestionView* suggestionView = GetSuggestionView(input_accessory_view_);
+ EXPECT_TRUE(suggestionView);
+ EXPECT_EQ(0U, [suggestionView.suggestions count]);
+}
+
+// Tests that, once a provider is asked if it has suggestions for a form/field,
+// it and only it is asked to provide them, and that they are then displayed
+// in the keyboard accessory view.
+TEST_F(FormSuggestionControllerTest,
+ FormActivityShouldRetrieveSuggestions_SuggestionsAddedToAccessoryView) {
+ // Set up the controller with some providers, one of which can provide
+ // suggestions.
+ NSArray* suggestions = @[
+ [FormSuggestion suggestionWithValue:@"foo"
+ displayDescription:nil
+ icon:@""
+ identifier:0],
+ [FormSuggestion suggestionWithValue:@"bar"
+ displayDescription:nil
+ icon:@""
+ identifier:1]
+ ];
+ TestSuggestionProvider* provider1 =
+ [[TestSuggestionProvider alloc] initWithSuggestions:suggestions];
+ TestSuggestionProvider* provider2 =
+ [[TestSuggestionProvider alloc] initWithSuggestions:@[]];
+ SetUpController(@[ provider1, provider2 ]);
+ SetCurrentUrl("http://foo.com");
+
+ [accessory_controller_ webState:web_state()
+ didRegisterFormActivityWithFormNamed:"form"
+ fieldName:"field"
+ type:"type"
+ value:"value"
+ keyCode:web::WebStateObserver::
+ kInvalidFormKeyCode
+ inputMissing:false];
+
+ // Since the first provider has suggestions available, it and only it
+ // should have been asked.
+ EXPECT_TRUE([provider1 askedIfSuggestionsAvailable]);
+ EXPECT_FALSE([provider2 askedIfSuggestionsAvailable]);
+
+ // Since the first provider said it had suggestions, it and only it
+ // should have been asked to provide them.
+ EXPECT_TRUE([provider1 askedForSuggestions]);
+ EXPECT_FALSE([provider2 askedForSuggestions]);
+
+ // The accessory view should show the suggestions.
+ FormSuggestionView* suggestionView = GetSuggestionView(input_accessory_view_);
+ EXPECT_TRUE(suggestionView);
+ EXPECT_NSEQ(suggestions, suggestionView.suggestions);
+}
+
+// Tests that selecting a suggestion from the accessory view informs the
+// specified delegate for that suggestion.
+TEST_F(FormSuggestionControllerTest, SelectingSuggestionShouldNotifyDelegate) {
+ // Send some suggestions to the controller and then tap one.
+ NSArray* suggestions = @[
+ [FormSuggestion suggestionWithValue:@"foo"
+ displayDescription:nil
+ icon:@""
+ identifier:0],
+ ];
+ TestSuggestionProvider* provider =
+ [[TestSuggestionProvider alloc] initWithSuggestions:suggestions];
+ SetUpController(@[ provider ]);
+ SetCurrentUrl("http://foo.com");
+ [accessory_controller_ webState:web_state()
+ didRegisterFormActivityWithFormNamed:"form"
+ fieldName:"field"
+ type:"type"
+ value:"value"
+ keyCode:web::WebStateObserver::
+ kInvalidFormKeyCode
+ inputMissing:false];
+
+ // Selecting a suggestion should notify the delegate.
+ [suggestion_controller_ didSelectSuggestion:suggestions[0]];
+ EXPECT_TRUE([provider selected]);
+ EXPECT_NSEQ(@"form", [provider formName]);
+ EXPECT_NSEQ(@"field", [provider fieldName]);
+ EXPECT_NSEQ(suggestions[0], [provider suggestion]);
+}
+
+} // namespace
« no previous file with comments | « ios/chrome/browser/autofill/form_structure_browsertest.mm ('k') | ios/chrome/browser/autofill/js_autofill_manager_unittest.mm » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698