Index: ios/chrome/browser/autofill/autofill_controller_unittest.mm |
diff --git a/ios/chrome/browser/autofill/autofill_controller_unittest.mm b/ios/chrome/browser/autofill/autofill_controller_unittest.mm |
new file mode 100644 |
index 0000000000000000000000000000000000000000..5942a05b0dc1d313d5880b4c6a17115bb673db8b |
--- /dev/null |
+++ b/ios/chrome/browser/autofill/autofill_controller_unittest.mm |
@@ -0,0 +1,587 @@ |
+// Copyright 2013 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/autofill_controller.h" |
+ |
+#include <memory> |
+#include <vector> |
+ |
+#include "base/guid.h" |
+#include "base/memory/ptr_util.h" |
+#include "base/strings/utf_string_conversions.h" |
+#include "base/test/histogram_tester.h" |
+#import "base/test/ios/wait_util.h" |
+#include "components/autofill/core/browser/autofill_manager.h" |
+#include "components/autofill/core/browser/autofill_metrics.h" |
+#include "components/autofill/core/browser/personal_data_manager.h" |
+#include "components/autofill/ios/browser/autofill_driver_ios.h" |
+#import "components/autofill/ios/browser/form_suggestion.h" |
+#include "components/infobars/core/confirm_infobar_delegate.h" |
+#include "components/infobars/core/infobar.h" |
+#include "components/infobars/core/infobar_manager.h" |
+#include "components/keyed_service/core/service_access_type.h" |
+#import "ios/chrome/browser/autofill/autofill_agent.h" |
+#import "ios/chrome/browser/autofill/form_input_accessory_view_controller.h" |
+#import "ios/chrome/browser/autofill/form_suggestion_controller.h" |
+#include "ios/chrome/browser/browser_state/test_chrome_browser_state.h" |
+#include "ios/chrome/browser/infobars/infobar_manager_impl.h" |
+#import "ios/chrome/browser/ui/autofill/autofill_client_ios.h" |
+#import "ios/chrome/browser/web/chrome_web_test.h" |
+#include "ios/chrome/browser/web_data_service_factory.h" |
+#import "ios/web/public/web_state/web_state.h" |
+#import "testing/gtest_mac.h" |
+#include "ui/base/test/ios/ui_view_test_utils.h" |
+ |
+#if !defined(__has_feature) || !__has_feature(objc_arc) |
+#error "This file requires ARC support." |
+#endif |
+ |
+// Real FormSuggestionController is wrapped to register the addition of |
+// suggestions. |
+@interface TestSuggestionController : FormSuggestionController |
+ |
+@property(nonatomic, copy) NSArray* suggestions; |
+@property(nonatomic, assign) BOOL suggestionRetrievalComplete; |
+ |
+@end |
+ |
+@implementation TestSuggestionController |
+ |
+@synthesize suggestions = _suggestions; |
+@synthesize suggestionRetrievalComplete = _suggestionRetrievalComplete; |
+ |
+- (void)retrieveSuggestionsForFormNamed:(const std::string&)formName |
+ fieldName:(const std::string&)fieldName |
+ type:(const std::string&)type |
+ webState:(web::WebState*)webState { |
+ self.suggestionRetrievalComplete = NO; |
+ [super retrieveSuggestionsForFormNamed:formName |
+ fieldName:fieldName |
+ type:type |
+ webState:webState]; |
+} |
+ |
+- (void)updateKeyboardWithSuggestions:(NSArray*)suggestions { |
+ self.suggestions = suggestions; |
+ self.suggestionRetrievalComplete = YES; |
+} |
+ |
+- (void)onNoSuggestionsAvailable { |
+ self.suggestionRetrievalComplete = YES; |
+}; |
+ |
+@end |
+ |
+namespace autofill { |
+ |
+namespace { |
+ |
+// The profile-type form used by tests. |
+NSString* const kProfileFormHtml = |
+ @"<form action='/submit' method='post'>" |
+ "Name <input type='text' name='name'>" |
+ "Address <input type='text' name='address'>" |
+ "City <input type='text' name='city'>" |
+ "State <input type='text' name='state'>" |
+ "Zip <input type='text' name='zip'>" |
+ "<input type='submit' id='submit' value='Submit'>" |
+ "</form>"; |
+ |
+// A minimal form with a name. |
+NSString* const kMinimalFormWithNameHtml = @"<form id='form1'>" |
+ "<input name='name'>" |
+ "<input name='address'>" |
+ "<input name='city'>" |
+ "</form>"; |
+ |
+// The key/value-type form used by tests. |
+NSString* const kKeyValueFormHtml = |
+ @"<form action='/submit' method='post'>" |
+ "Greeting <input type='text' name='greeting'>" |
+ "Dummy field <input type='text' name='dummy'>" |
+ "<input type='submit' id='submit' value='Submit'>" |
+ "</form>"; |
+ |
+// The credit card-type form used by tests. |
+NSString* const kCreditCardFormHtml = |
+ @"<form action='/submit' method='post'>" |
+ "Name on card: <input type='text' name='name'>" |
+ "Credit card number: <input type='text' name='CCNo'>" |
+ "Expiry Month: <input type='text' name='CCExpiresMonth'>" |
+ "Expiry Year: <input type='text' name='CCExpiresYear'>" |
+ "<input type='submit' id='submit' value='Submit'>" |
+ "</form>"; |
+ |
+// Experiment preference key. |
+NSString* const kAutofillVisible = @"AutofillVisible"; |
+ |
+// FAIL if a field with the supplied |name| and |fieldType| is not present on |
+// the |form|. |
+void CheckField(const FormStructure& form, |
+ ServerFieldType fieldType, |
+ const char* name) { |
+ for (const AutofillField* field : form) { |
+ if (field->heuristic_type() == fieldType) { |
+ EXPECT_EQ(base::UTF8ToUTF16(name), field->unique_name()); |
+ return; |
+ } |
+ } |
+ FAIL() << "Missing field " << name; |
+} |
+ |
+// WebDataServiceConsumer for receving vectors of strings and making them |
+// available to tests. |
+class TestConsumer : public WebDataServiceConsumer { |
+ public: |
+ void OnWebDataServiceRequestDone( |
+ WebDataServiceBase::Handle handle, |
+ std::unique_ptr<WDTypedResult> result) override { |
+ DCHECK_EQ(result->GetType(), AUTOFILL_VALUE_RESULT); |
+ result_ = static_cast<WDResult<std::vector<base::string16>>*>(result.get()) |
+ ->GetValue(); |
+ } |
+ std::vector<base::string16> result_; |
+}; |
+ |
+// Text fixture to test autofill. |
+class AutofillControllerTest : public ChromeWebTest { |
+ public: |
+ AutofillControllerTest() = default; |
+ ~AutofillControllerTest() override {} |
+ |
+ protected: |
+ void SetUp() override; |
+ void TearDown() override; |
+ void SetUpForSuggestions(NSString* data); |
+ |
+ // Adds key value data to the Personal Data Manager. |
+ void SetUpKeyValueData(); |
+ |
+ // Blocks until suggestion retrieval has completed. |
+ void WaitForSuggestionRetrieval(); |
+ |
+ // Fails if the specified metric was not registered the given number of times. |
+ void ExpectMetric(const std::string& histogram_name, int sum); |
+ |
+ // Fails if the specified user happiness metric was not registered. |
+ void ExpectHappinessMetric(AutofillMetrics::UserHappinessMetric metric); |
+ |
+ TestSuggestionController* suggestion_controller() { |
+ return suggestion_controller_; |
+ } |
+ |
+ private: |
+ // Weak pointer to AutofillAgent, owned by the AutofillController. |
+ __weak AutofillAgent* autofill_agent_; |
+ |
+ // Histogram tester for these tests. |
+ std::unique_ptr<base::HistogramTester> histogram_tester_; |
+ |
+ // Retrieves suggestions according to form events. |
+ TestSuggestionController* suggestion_controller_; |
+ |
+ // Retrieves accessory views according to form events. |
+ FormInputAccessoryViewController* accessory_controller_; |
+ |
+ // Manages autofill for a single page. |
+ AutofillController* autofill_controller_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(AutofillControllerTest); |
+}; |
+ |
+void AutofillControllerTest::SetUp() { |
+ ChromeWebTest::SetUp(); |
+ |
+ // Profile import requires a PersonalDataManager which itself needs the |
+ // WebDataService; this is not initialized on a TestChromeBrowserState by |
+ // default. |
+ chrome_browser_state_->CreateWebDataService(); |
+ // Enable autofill experiment. |
+ NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults]; |
+ [defaults setBool:YES forKey:kAutofillVisible]; |
+ |
+ AutofillAgent* agent = |
+ [[AutofillAgent alloc] initWithBrowserState:chrome_browser_state_.get() |
+ webState:web_state()]; |
+ autofill_agent_ = agent; |
+ InfoBarManagerImpl::CreateForWebState(web_state()); |
+ autofill_controller_ = [[AutofillController alloc] |
+ initWithBrowserState:chrome_browser_state_.get() |
+ webState:web_state() |
+ autofillAgent:autofill_agent_ |
+ passwordGenerationManager:nullptr |
+ downloadEnabled:NO]; |
+ suggestion_controller_ = [[TestSuggestionController alloc] |
+ initWithWebState:web_state() |
+ providers:@[ [autofill_controller_ suggestionProvider] ]]; |
+ accessory_controller_ = [[FormInputAccessoryViewController alloc] |
+ initWithWebState:web_state() |
+ providers:@[ [suggestion_controller_ accessoryViewProvider] ]]; |
+ histogram_tester_.reset(new base::HistogramTester()); |
+} |
+ |
+void AutofillControllerTest::TearDown() { |
+ [autofill_controller_ detachFromWebState]; |
+ [suggestion_controller_ detachFromWebState]; |
+ |
+ ChromeWebTest::TearDown(); |
+} |
+ |
+void AutofillControllerTest::WaitForSuggestionRetrieval() { |
+ // Wait for the message queue to ensure that JS events fired in the tests |
+ // trigger TestSuggestionController's retrieveSuggestionsForFormNamed: method |
+ // and set suggestionRetrievalComplete to NO. |
+ WaitForBackgroundTasks(); |
+ |
+ // Now we can wait for suggestionRetrievalComplete to be set to YES. |
+ WaitForCondition(^bool { |
+ return [suggestion_controller() suggestionRetrievalComplete]; |
+ }); |
+} |
+ |
+void AutofillControllerTest::ExpectMetric(const std::string& histogram_name, |
+ int sum) { |
+ histogram_tester_->ExpectBucketCount(histogram_name, sum, 1); |
+} |
+ |
+void AutofillControllerTest::ExpectHappinessMetric( |
+ AutofillMetrics::UserHappinessMetric metric) { |
+ histogram_tester_->ExpectBucketCount("Autofill.UserHappiness", metric, 1); |
+} |
+ |
+// Checks that viewing an HTML page containing a form results in the form being |
+// registered as a FormStructure by the AutofillManager. |
+TEST_F(AutofillControllerTest, ReadForm) { |
+ AutofillManager* autofill_manager = |
+ AutofillDriverIOS::FromWebState(web_state())->autofill_manager(); |
+ EXPECT_TRUE(autofill_manager->GetFormStructures().empty()) |
+ << "Forms are registered at beginning"; |
+ LoadHtml(kProfileFormHtml); |
+ const std::vector<std::unique_ptr<FormStructure>>& forms = |
+ autofill_manager->GetFormStructures(); |
+ ASSERT_EQ(1U, forms.size()); |
+ CheckField(*forms[0], NAME_FULL, "name_1"); |
+ CheckField(*forms[0], ADDRESS_HOME_LINE1, "address_1"); |
+ CheckField(*forms[0], ADDRESS_HOME_CITY, "city_1"); |
+ CheckField(*forms[0], ADDRESS_HOME_STATE, "state_1"); |
+ CheckField(*forms[0], ADDRESS_HOME_ZIP, "zip_1"); |
+ ExpectMetric("Autofill.IsEnabled.PageLoad", 1); |
+ ExpectHappinessMetric(AutofillMetrics::FORMS_LOADED); |
+}; |
+ |
+// Checks that viewing an HTML page containing a form with an 'id' results in |
+// the form being registered as a FormStructure by the AutofillManager, and the |
+// name is correctly set. |
+TEST_F(AutofillControllerTest, ReadFormName) { |
+ AutofillManager* autofill_manager = |
+ AutofillDriverIOS::FromWebState(web_state())->autofill_manager(); |
+ LoadHtml(kMinimalFormWithNameHtml); |
+ const std::vector<std::unique_ptr<FormStructure>>& forms = |
+ autofill_manager->GetFormStructures(); |
+ ASSERT_EQ(1U, forms.size()); |
+ EXPECT_EQ(base::UTF8ToUTF16("form1"), forms[0]->ToFormData().name); |
+}; |
+ |
+// Checks that an HTML page containing a profile-type form which is submitted |
+// with scripts (simulating user form submission) results in a profile being |
+// successfully imported into the PersonalDataManager. |
+TEST_F(AutofillControllerTest, ProfileImport) { |
+ AutofillManager* autofill_manager = |
+ AutofillDriverIOS::FromWebState(web_state())->autofill_manager(); |
+ PersonalDataManager* personal_data_manager = |
+ autofill_manager->client()->GetPersonalDataManager(); |
+ // Check there are no registered profiles already. |
+ EXPECT_EQ(0U, personal_data_manager->GetProfiles().size()); |
+ LoadHtml(kProfileFormHtml); |
+ ExecuteJavaScript(@"document.forms[0].name.value = 'Homer Simpson'"); |
+ ExecuteJavaScript(@"document.forms[0].address.value = '123 Main Street'"); |
+ ExecuteJavaScript(@"document.forms[0].city.value = 'Springfield'"); |
+ ExecuteJavaScript(@"document.forms[0].state.value = 'IL'"); |
+ ExecuteJavaScript(@"document.forms[0].zip.value = '55123'"); |
+ ExecuteJavaScript(@"submit.click()"); |
+ WaitForCondition(^bool { |
+ return personal_data_manager->GetProfiles().size(); |
+ }); |
+ const std::vector<AutofillProfile*>& profiles = |
+ personal_data_manager->GetProfiles(); |
+ if (profiles.size() != 1) |
+ FAIL() << "Not exactly one profile found after attempted import"; |
+ const AutofillProfile& profile = *profiles[0]; |
+ EXPECT_EQ(base::UTF8ToUTF16("Homer Simpson"), |
+ profile.GetInfo(AutofillType(NAME_FULL), "en-US")); |
+ EXPECT_EQ(base::UTF8ToUTF16("123 Main Street"), |
+ profile.GetInfo(AutofillType(ADDRESS_HOME_LINE1), "en-US")); |
+ EXPECT_EQ(base::UTF8ToUTF16("Springfield"), |
+ profile.GetInfo(AutofillType(ADDRESS_HOME_CITY), "en-US")); |
+ EXPECT_EQ(base::UTF8ToUTF16("IL"), |
+ profile.GetInfo(AutofillType(ADDRESS_HOME_STATE), "en-US")); |
+ EXPECT_EQ(base::UTF8ToUTF16("55123"), |
+ profile.GetInfo(AutofillType(ADDRESS_HOME_ZIP), "en-US")); |
+}; |
+ |
+void AutofillControllerTest::SetUpForSuggestions(NSString* data) { |
+ AutofillManager* autofill_manager = |
+ AutofillDriverIOS::FromWebState(web_state())->autofill_manager(); |
+ PersonalDataManager* personal_data_manager = |
+ autofill_manager->client()->GetPersonalDataManager(); |
+ AutofillProfile profile(base::GenerateGUID(), "https://www.example.com/"); |
+ profile.SetRawInfo(NAME_FULL, base::UTF8ToUTF16("Homer Simpson")); |
+ profile.SetRawInfo(ADDRESS_HOME_LINE1, base::UTF8ToUTF16("123 Main Street")); |
+ profile.SetRawInfo(ADDRESS_HOME_CITY, base::UTF8ToUTF16("Springfield")); |
+ profile.SetRawInfo(ADDRESS_HOME_STATE, base::UTF8ToUTF16("IL")); |
+ profile.SetRawInfo(ADDRESS_HOME_ZIP, base::UTF8ToUTF16("55123")); |
+ EXPECT_EQ(0U, personal_data_manager->GetProfiles().size()); |
+ personal_data_manager->SaveImportedProfile(profile); |
+ EXPECT_EQ(1U, personal_data_manager->GetProfiles().size()); |
+ LoadHtml(data); |
+} |
+ |
+// Checks that focusing on a text element of a profile-type form will result in |
+// suggestions being sent to the AutofillAgent, once data has been loaded into a |
+// test data manager. |
+TEST_F(AutofillControllerTest, ProfileSuggestions) { |
+ SetUpForSuggestions(kProfileFormHtml); |
+ WaitForBackgroundTasks(); |
+ ui::test::uiview_utils::ForceViewRendering(web_state()->GetView()); |
+ ExecuteJavaScript(@"document.forms[0].name.focus()"); |
+ WaitForSuggestionRetrieval(); |
+ ExpectMetric("Autofill.AddressSuggestionsCount", 1); |
+ ExpectHappinessMetric(AutofillMetrics::SUGGESTIONS_SHOWN); |
+ EXPECT_EQ(1U, [suggestion_controller() suggestions].count); |
+ FormSuggestion* suggestion = [suggestion_controller() suggestions][0]; |
+ EXPECT_NSEQ(@"Homer Simpson", suggestion.value); |
+}; |
+ |
+// Tests that the system is able to offer suggestions for an anonymous form when |
+// there is another anonymous form on the page. |
+TEST_F(AutofillControllerTest, ProfileSuggestionsTwoAnonymousForms) { |
+ SetUpForSuggestions( |
+ [NSString stringWithFormat:@"%@%@", kProfileFormHtml, kProfileFormHtml]); |
+ WaitForBackgroundTasks(); |
+ ui::test::uiview_utils::ForceViewRendering(web_state()->GetView()); |
+ ExecuteJavaScript(@"document.forms[0].name.focus()"); |
+ WaitForSuggestionRetrieval(); |
+ ExpectMetric("Autofill.AddressSuggestionsCount", 1); |
+ ExpectHappinessMetric(AutofillMetrics::SUGGESTIONS_SHOWN); |
+ EXPECT_EQ(1U, [suggestion_controller() suggestions].count); |
+ FormSuggestion* suggestion = [suggestion_controller() suggestions][0]; |
+ EXPECT_NSEQ(@"Homer Simpson", suggestion.value); |
+}; |
+ |
+// Checks that focusing on a select element in a profile-type form will result |
+// in suggestions being sent to the AutofillAgent, once data has been loaded |
+// into a test data manager. |
+TEST_F(AutofillControllerTest, ProfileSuggestionsFromSelectField) { |
+ SetUpForSuggestions(kProfileFormHtml); |
+ WaitForBackgroundTasks(); |
+ ui::test::uiview_utils::ForceViewRendering(web_state()->GetView()); |
+ ExecuteJavaScript(@"document.forms[0].state.focus()"); |
+ WaitForSuggestionRetrieval(); |
+ ExpectMetric("Autofill.AddressSuggestionsCount", 1); |
+ ExpectHappinessMetric(AutofillMetrics::SUGGESTIONS_SHOWN); |
+ EXPECT_EQ(1U, [suggestion_controller() suggestions].count); |
+ FormSuggestion* suggestion = [suggestion_controller() suggestions][0]; |
+ EXPECT_NSEQ(@"IL", suggestion.value); |
+}; |
+ |
+// Checks that multiple profiles will offer a matching number of suggestions. |
+TEST_F(AutofillControllerTest, MultipleProfileSuggestions) { |
+ AutofillManager* autofill_manager = |
+ AutofillDriverIOS::FromWebState(web_state())->autofill_manager(); |
+ PersonalDataManager* personal_data_manager = |
+ autofill_manager->client()->GetPersonalDataManager(); |
+ AutofillProfile profile(base::GenerateGUID(), "https://www.example.com/"); |
+ profile.SetRawInfo(NAME_FULL, base::UTF8ToUTF16("Homer Simpson")); |
+ profile.SetRawInfo(ADDRESS_HOME_LINE1, base::UTF8ToUTF16("123 Main Street")); |
+ profile.SetRawInfo(ADDRESS_HOME_CITY, base::UTF8ToUTF16("Springfield")); |
+ profile.SetRawInfo(ADDRESS_HOME_STATE, base::UTF8ToUTF16("IL")); |
+ profile.SetRawInfo(ADDRESS_HOME_ZIP, base::UTF8ToUTF16("55123")); |
+ EXPECT_EQ(0U, personal_data_manager->GetProfiles().size()); |
+ personal_data_manager->SaveImportedProfile(profile); |
+ EXPECT_EQ(1U, personal_data_manager->GetProfiles().size()); |
+ AutofillProfile profile2(base::GenerateGUID(), "https://www.example.com/"); |
+ profile2.SetRawInfo(NAME_FULL, base::UTF8ToUTF16("Larry Page")); |
+ profile2.SetRawInfo(ADDRESS_HOME_LINE1, |
+ base::UTF8ToUTF16("1600 Amphitheatre Parkway")); |
+ profile2.SetRawInfo(ADDRESS_HOME_CITY, base::UTF8ToUTF16("Mountain View")); |
+ profile2.SetRawInfo(ADDRESS_HOME_STATE, base::UTF8ToUTF16("CA")); |
+ profile2.SetRawInfo(ADDRESS_HOME_ZIP, base::UTF8ToUTF16("94043")); |
+ personal_data_manager->SaveImportedProfile(profile2); |
+ EXPECT_EQ(2U, personal_data_manager->GetProfiles().size()); |
+ LoadHtml(kProfileFormHtml); |
+ WaitForBackgroundTasks(); |
+ ui::test::uiview_utils::ForceViewRendering(web_state()->GetView()); |
+ ExecuteJavaScript(@"document.forms[0].name.focus()"); |
+ WaitForSuggestionRetrieval(); |
+ ExpectMetric("Autofill.AddressSuggestionsCount", 2); |
+ ExpectHappinessMetric(AutofillMetrics::SUGGESTIONS_SHOWN); |
+ EXPECT_EQ(2U, [suggestion_controller() suggestions].count); |
+} |
+ |
+// Check that an HTML page containing a key/value type form which is submitted |
+// with scripts (simulating user form submission) results in data being |
+// successfully registered. |
+TEST_F(AutofillControllerTest, KeyValueImport) { |
+ LoadHtml(kKeyValueFormHtml); |
+ ExecuteJavaScript(@"document.forms[0].greeting.value = 'Hello'"); |
+ scoped_refptr<AutofillWebDataService> web_data_service = |
+ ios::WebDataServiceFactory::GetAutofillWebDataForBrowserState( |
+ chrome_browser_state_.get(), ServiceAccessType::EXPLICIT_ACCESS); |
+ __block TestConsumer consumer; |
+ const int limit = 1; |
+ web_data_service->GetFormValuesForElementName( |
+ base::UTF8ToUTF16("greeting"), base::string16(), limit, &consumer); |
+ WaitForBackgroundTasks(); |
+ // No value should be returned before anything is loaded via form submission. |
+ ASSERT_EQ(0U, consumer.result_.size()); |
+ ExecuteJavaScript(@"submit.click()"); |
+ WaitForCondition(^bool { |
+ web_data_service->GetFormValuesForElementName( |
+ base::UTF8ToUTF16("greeting"), base::string16(), limit, &consumer); |
+ return consumer.result_.size(); |
+ }); |
+ WaitForBackgroundTasks(); |
+ // One result should be returned, matching the filled value. |
+ ASSERT_EQ(1U, consumer.result_.size()); |
+ EXPECT_EQ(base::UTF8ToUTF16("Hello"), consumer.result_[0]); |
+}; |
+ |
+void AutofillControllerTest::SetUpKeyValueData() { |
+ scoped_refptr<AutofillWebDataService> web_data_service = |
+ ios::WebDataServiceFactory::GetAutofillWebDataForBrowserState( |
+ chrome_browser_state_.get(), ServiceAccessType::EXPLICIT_ACCESS); |
+ // Load value into database. |
+ std::vector<FormFieldData> values; |
+ FormFieldData fieldData; |
+ fieldData.name = base::UTF8ToUTF16("greeting"); |
+ fieldData.value = base::UTF8ToUTF16("Bonjour"); |
+ values.push_back(fieldData); |
+ web_data_service->AddFormFields(values); |
+} |
+ |
+// Checks that focusing on an element of a key/value type form then typing the |
+// first letter of a suggestion will result in suggestions being sent to the |
+// AutofillAgent, once data has been loaded into a test data manager. |
+TEST_F(AutofillControllerTest, KeyValueSuggestions) { |
+ SetUpKeyValueData(); |
+ // Load test page and focus element. |
+ LoadHtml(kKeyValueFormHtml); |
+ WaitForBackgroundTasks(); |
+ ExecuteJavaScript(@"document.forms[0].greeting.value='B'"); |
+ ExecuteJavaScript(@"document.forms[0].greeting.focus()"); |
+ WaitForSuggestionRetrieval(); |
+ EXPECT_EQ(1U, [suggestion_controller() suggestions].count); |
+ FormSuggestion* suggestion = [suggestion_controller() suggestions][0]; |
+ EXPECT_NSEQ(@"Bonjour", suggestion.value); |
+}; |
+ |
+// Checks that typing events (simulated in script) result in suggestions. Note |
+// that the field is not explictly focused before typing starts; this can happen |
+// in practice and should not result in a crash or incorrect behavior. |
+TEST_F(AutofillControllerTest, KeyValueTypedSuggestions) { |
+ SetUpKeyValueData(); |
+ LoadHtml(kKeyValueFormHtml); |
+ WaitForBackgroundTasks(); |
+ ExecuteJavaScript(@"document.forms[0].greeting.select()"); |
+ ExecuteJavaScript(@"event = document.createEvent('TextEvent');"); |
+ ExecuteJavaScript( |
+ @"event.initTextEvent('textInput', true, true, window, 'B');"); |
+ ExecuteJavaScript(@"document.forms[0].greeting.dispatchEvent(event);"); |
+ WaitForSuggestionRetrieval(); |
+ EXPECT_EQ(1U, [suggestion_controller() suggestions].count); |
+ FormSuggestion* suggestion = [suggestion_controller() suggestions][0]; |
+ EXPECT_NSEQ(@"Bonjour", suggestion.value); |
+} |
+ |
+// Checks that focusing on and typing on one field, then changing focus before |
+// typing again, result in suggestions. |
+TEST_F(AutofillControllerTest, KeyValueFocusChange) { |
+ SetUpKeyValueData(); |
+ LoadHtml(kKeyValueFormHtml); |
+ |
+ // Focus the dummy field and confirm no suggestions are presented. |
+ ExecuteJavaScript(@"document.forms[0].dummy.focus()"); |
+ WaitForSuggestionRetrieval(); |
+ EXPECT_EQ(0U, [suggestion_controller() suggestions].count); |
+ |
+ // Enter 'B' in the dummy field and confirm no suggestions are presented. |
+ ExecuteJavaScript(@"event = document.createEvent('TextEvent');"); |
+ ExecuteJavaScript( |
+ @"event.initTextEvent('textInput', true, true, window, 'B');"); |
+ ExecuteJavaScript(@"document.forms[0].dummy.dispatchEvent(event);"); |
+ WaitForSuggestionRetrieval(); |
+ EXPECT_EQ(0U, [suggestion_controller() suggestions].count); |
+ |
+ // Enter 'B' in the greeting field and confirm that one suggestion ("Bonjour") |
+ // is presented. |
+ ExecuteJavaScript(@"document.forms[0].greeting.select()"); |
+ ExecuteJavaScript(@"event = document.createEvent('TextEvent');"); |
+ ExecuteJavaScript( |
+ @"event.initTextEvent('textInput', true, true, window, 'B');"); |
+ ExecuteJavaScript(@"document.forms[0].greeting.dispatchEvent(event);"); |
+ WaitForSuggestionRetrieval(); |
+ EXPECT_EQ(1U, [suggestion_controller() suggestions].count); |
+ FormSuggestion* suggestion = [suggestion_controller() suggestions][0]; |
+ EXPECT_NSEQ(@"Bonjour", suggestion.value); |
+} |
+ |
+// Checks that focusing on an element of a key/value type form without typing |
+// won't result in suggestions being sent to the AutofillAgent, once data has |
+// been loaded into a test data manager. |
+TEST_F(AutofillControllerTest, NoKeyValueSuggestionsWithoutTyping) { |
+ SetUpKeyValueData(); |
+ // Load test page and focus element. |
+ LoadHtml(kKeyValueFormHtml); |
+ ExecuteJavaScript(@"document.forms[0].greeting.focus()"); |
+ WaitForSuggestionRetrieval(); |
+ EXPECT_EQ(0U, [suggestion_controller() suggestions].count); |
+} |
+ |
+// Checks that an HTML page containing a credit card-type form which is |
+// submitted with scripts (simulating user form submission) results in a credit |
+// card being successfully imported into the PersonalDataManager. |
+TEST_F(AutofillControllerTest, CreditCardImport) { |
+ InfoBarManagerImpl::CreateForWebState(web_state()); |
+ AutofillManager* autofill_manager = |
+ AutofillDriverIOS::FromWebState(web_state())->autofill_manager(); |
+ PersonalDataManager* personal_data_manager = |
+ autofill_manager->client()->GetPersonalDataManager(); |
+ // Check there are no registered profiles already. |
+ EXPECT_EQ(0U, personal_data_manager->GetCreditCards().size()); |
+ LoadHtml(kCreditCardFormHtml); |
+ ExecuteJavaScript(@"document.forms[0].name.value = 'Superman'"); |
+ ExecuteJavaScript(@"document.forms[0].CCNo.value = '4000-4444-4444-4444'"); |
+ ExecuteJavaScript(@"document.forms[0].CCExpiresMonth.value = '11'"); |
+ ExecuteJavaScript(@"document.forms[0].CCExpiresYear.value = '2999'"); |
+ ExecuteJavaScript(@"submit.click()"); |
+ infobars::InfoBarManager* infobar_manager = |
+ InfoBarManagerImpl::FromWebState(web_state()); |
+ base::test::ios::WaitUntilCondition(^bool() { |
+ return infobar_manager->infobar_count(); |
+ }); |
+ ExpectMetric("Autofill.CreditCardInfoBar.Local", |
+ AutofillMetrics::INFOBAR_SHOWN); |
+ ASSERT_EQ(1U, infobar_manager->infobar_count()); |
+ infobars::InfoBarDelegate* infobar = |
+ infobar_manager->infobar_at(0)->delegate(); |
+ ConfirmInfoBarDelegate* confirm_infobar = infobar->AsConfirmInfoBarDelegate(); |
+ confirm_infobar->Accept(); |
+ const std::vector<CreditCard*>& credit_cards = |
+ personal_data_manager->GetCreditCards(); |
+ ASSERT_EQ(1U, credit_cards.size()); |
+ const CreditCard& credit_card = *credit_cards[0]; |
+ EXPECT_EQ(base::UTF8ToUTF16("Superman"), |
+ credit_card.GetInfo(AutofillType(CREDIT_CARD_NAME_FULL), "en-US")); |
+ EXPECT_EQ(base::UTF8ToUTF16("4000444444444444"), |
+ credit_card.GetInfo(AutofillType(CREDIT_CARD_NUMBER), "en-US")); |
+ EXPECT_EQ(base::UTF8ToUTF16("11"), |
+ credit_card.GetInfo(AutofillType(CREDIT_CARD_EXP_MONTH), "en-US")); |
+ EXPECT_EQ( |
+ base::UTF8ToUTF16("2999"), |
+ credit_card.GetInfo(AutofillType(CREDIT_CARD_EXP_4_DIGIT_YEAR), "en-US")); |
+}; |
+ |
+} // namespace |
+ |
+} // namespace autofill |