autofill_dialog_controller_browsertest.cc revision a93a17c8d99d686bd4a1511e5504e5e6cc9fcadf
1// Copyright (c) 2012 The Chromium 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#include "base/bind.h" 6#include "base/memory/ref_counted.h" 7#include "base/message_loop.h" 8#include "base/time.h" 9#include "base/utf_string_conversions.h" 10#include "chrome/browser/ui/autofill/autofill_dialog_controller_impl.h" 11#include "chrome/browser/ui/autofill/autofill_dialog_view.h" 12#include "chrome/browser/ui/autofill/data_model_wrapper.h" 13#include "chrome/browser/ui/autofill/testable_autofill_dialog_view.h" 14#include "chrome/browser/ui/browser.h" 15#include "chrome/browser/ui/tabs/tab_strip_model.h" 16#include "chrome/test/base/in_process_browser_test.h" 17#include "components/autofill/browser/autofill_common_test.h" 18#include "components/autofill/browser/autofill_metrics.h" 19#include "components/autofill/browser/test_personal_data_manager.h" 20#include "components/autofill/common/form_data.h" 21#include "components/autofill/common/form_field_data.h" 22#include "content/public/test/test_utils.h" 23#include "testing/gtest/include/gtest/gtest.h" 24 25namespace autofill { 26 27namespace { 28 29void MockCallback(const FormStructure*, const std::string&) {} 30 31class MockAutofillMetrics : public AutofillMetrics { 32 public: 33 MockAutofillMetrics() 34 : dialog_type_(static_cast<DialogType>(-1)), 35 dialog_dismissal_action_( 36 static_cast<AutofillMetrics::DialogDismissalAction>(-1)), 37 autocheckout_status_( 38 static_cast<AutofillMetrics::AutocheckoutCompletionStatus>(-1)) {} 39 virtual ~MockAutofillMetrics() {} 40 41 // AutofillMetrics: 42 virtual void LogAutocheckoutDuration( 43 const base::TimeDelta& duration, 44 AutocheckoutCompletionStatus status) const OVERRIDE { 45 // Ignore constness for testing. 46 MockAutofillMetrics* mutable_this = const_cast<MockAutofillMetrics*>(this); 47 mutable_this->autocheckout_status_ = status; 48 } 49 50 virtual void LogDialogUiDuration( 51 const base::TimeDelta& duration, 52 DialogType dialog_type, 53 DialogDismissalAction dismissal_action) const OVERRIDE { 54 // Ignore constness for testing. 55 MockAutofillMetrics* mutable_this = const_cast<MockAutofillMetrics*>(this); 56 mutable_this->dialog_type_ = dialog_type; 57 mutable_this->dialog_dismissal_action_ = dismissal_action; 58 } 59 60 DialogType dialog_type() const { return dialog_type_; } 61 AutofillMetrics::DialogDismissalAction dialog_dismissal_action() const { 62 return dialog_dismissal_action_; 63 } 64 65 AutofillMetrics::AutocheckoutCompletionStatus autocheckout_status() const { 66 return autocheckout_status_; 67 } 68 69 private: 70 DialogType dialog_type_; 71 AutofillMetrics::DialogDismissalAction dialog_dismissal_action_; 72 AutofillMetrics::AutocheckoutCompletionStatus autocheckout_status_; 73 74 DISALLOW_COPY_AND_ASSIGN(MockAutofillMetrics); 75}; 76 77class TestAutofillDialogController : public AutofillDialogControllerImpl { 78 public: 79 TestAutofillDialogController(content::WebContents* contents, 80 const FormData& form_data, 81 const AutofillMetrics& metric_logger, 82 scoped_refptr<content::MessageLoopRunner> runner, 83 const DialogType dialog_type) 84 : AutofillDialogControllerImpl(contents, 85 form_data, 86 GURL(), 87 dialog_type, 88 base::Bind(&MockCallback)), 89 metric_logger_(metric_logger), 90 message_loop_runner_(runner) { 91 DisableWallet(); 92 } 93 94 virtual ~TestAutofillDialogController() {} 95 96 virtual void ViewClosed() OVERRIDE { 97 message_loop_runner_->Quit(); 98 AutofillDialogControllerImpl::ViewClosed(); 99 } 100 101 virtual string16 InputValidityMessage( 102 AutofillFieldType type, 103 const string16& value) const OVERRIDE { 104 return string16(); 105 } 106 107 virtual ValidityData InputsAreValid( 108 const DetailOutputMap& inputs, 109 ValidationType validation_type) const OVERRIDE { 110 return ValidityData(); 111 } 112 113 // Increase visibility for testing. 114 AutofillDialogView* view() { return AutofillDialogControllerImpl::view(); } 115 const DetailInput* input_showing_popup() const { 116 return AutofillDialogControllerImpl::input_showing_popup(); 117 } 118 119 TestPersonalDataManager* GetTestingManager() { 120 return &test_manager_; 121 } 122 123 protected: 124 virtual PersonalDataManager* GetManager() OVERRIDE { 125 return &test_manager_; 126 } 127 128 private: 129 // To specify our own metric logger. 130 virtual const AutofillMetrics& GetMetricLogger() const OVERRIDE { 131 return metric_logger_; 132 } 133 134 const AutofillMetrics& metric_logger_; 135 TestPersonalDataManager test_manager_; 136 scoped_refptr<content::MessageLoopRunner> message_loop_runner_; 137 138 DISALLOW_COPY_AND_ASSIGN(TestAutofillDialogController); 139}; 140 141} // namespace 142 143class AutofillDialogControllerTest : public InProcessBrowserTest { 144 public: 145 AutofillDialogControllerTest() {} 146 virtual ~AutofillDialogControllerTest() {} 147 148 void InitializeControllerOfType(DialogType dialog_type) { 149 FormData form; 150 form.name = ASCIIToUTF16("TestForm"); 151 form.method = ASCIIToUTF16("POST"); 152 form.origin = GURL("http://example.com/form.html"); 153 form.action = GURL("http://example.com/submit.html"); 154 form.user_submitted = true; 155 156 FormFieldData field; 157 field.autocomplete_attribute = "email"; 158 form.fields.push_back(field); 159 160 message_loop_runner_ = new content::MessageLoopRunner; 161 controller_ = new TestAutofillDialogController( 162 GetActiveWebContents(), 163 form, 164 metric_logger_, 165 message_loop_runner_, 166 dialog_type); 167 controller_->Show(); 168 } 169 170 content::WebContents* GetActiveWebContents() { 171 return browser()->tab_strip_model()->GetActiveWebContents(); 172 } 173 174 const MockAutofillMetrics& metric_logger() { return metric_logger_; } 175 TestAutofillDialogController* controller() { return controller_; } 176 177 void RunMessageLoop() { 178 message_loop_runner_->Run(); 179 } 180 181 private: 182 MockAutofillMetrics metric_logger_; 183 TestAutofillDialogController* controller_; // Weak reference. 184 scoped_refptr<content::MessageLoopRunner> message_loop_runner_; 185 DISALLOW_COPY_AND_ASSIGN(AutofillDialogControllerTest); 186}; 187 188// TODO(isherman): Enable these tests on other platforms once the UI is 189// implemented on those platforms. 190#if defined(TOOLKIT_VIEWS) 191// Submit the form data. 192IN_PROC_BROWSER_TEST_F(AutofillDialogControllerTest, Submit) { 193 InitializeControllerOfType(DIALOG_TYPE_REQUEST_AUTOCOMPLETE); 194 controller()->view()->GetTestableView()->SubmitForTesting(); 195 196 RunMessageLoop(); 197 198 EXPECT_EQ(AutofillMetrics::DIALOG_ACCEPTED, 199 metric_logger().dialog_dismissal_action()); 200 EXPECT_EQ(DIALOG_TYPE_REQUEST_AUTOCOMPLETE, metric_logger().dialog_type()); 201} 202 203// Cancel out of the dialog. 204IN_PROC_BROWSER_TEST_F(AutofillDialogControllerTest, Cancel) { 205 InitializeControllerOfType(DIALOG_TYPE_REQUEST_AUTOCOMPLETE); 206 controller()->view()->GetTestableView()->CancelForTesting(); 207 208 RunMessageLoop(); 209 210 EXPECT_EQ(AutofillMetrics::DIALOG_CANCELED, 211 metric_logger().dialog_dismissal_action()); 212 EXPECT_EQ(DIALOG_TYPE_REQUEST_AUTOCOMPLETE, metric_logger().dialog_type()); 213} 214 215// Take some other action that dismisses the dialog. 216IN_PROC_BROWSER_TEST_F(AutofillDialogControllerTest, Hide) { 217 InitializeControllerOfType(DIALOG_TYPE_REQUEST_AUTOCOMPLETE); 218 controller()->Hide(); 219 220 RunMessageLoop(); 221 222 EXPECT_EQ(AutofillMetrics::DIALOG_CANCELED, 223 metric_logger().dialog_dismissal_action()); 224 EXPECT_EQ(DIALOG_TYPE_REQUEST_AUTOCOMPLETE, metric_logger().dialog_type()); 225} 226 227// Test Autocheckout success metrics. 228IN_PROC_BROWSER_TEST_F(AutofillDialogControllerTest, AutocheckoutSuccess) { 229 InitializeControllerOfType(DIALOG_TYPE_AUTOCHECKOUT); 230 controller()->view()->GetTestableView()->SubmitForTesting(); 231 232 EXPECT_EQ(AutofillMetrics::DIALOG_ACCEPTED, 233 metric_logger().dialog_dismissal_action()); 234 EXPECT_EQ(DIALOG_TYPE_AUTOCHECKOUT, metric_logger().dialog_type()); 235 236 controller()->Hide(); 237 RunMessageLoop(); 238 239 EXPECT_EQ(AutofillMetrics::AUTOCHECKOUT_SUCCEEDED, 240 metric_logger().autocheckout_status()); 241} 242 243// Test Autocheckout failure metric. 244IN_PROC_BROWSER_TEST_F(AutofillDialogControllerTest, AutocheckoutError) { 245 InitializeControllerOfType(DIALOG_TYPE_AUTOCHECKOUT); 246 controller()->view()->GetTestableView()->SubmitForTesting(); 247 248 EXPECT_EQ(AutofillMetrics::DIALOG_ACCEPTED, 249 metric_logger().dialog_dismissal_action()); 250 EXPECT_EQ(DIALOG_TYPE_AUTOCHECKOUT, metric_logger().dialog_type()); 251 252 controller()->OnAutocheckoutError(); 253 controller()->view()->GetTestableView()->CancelForTesting(); 254 255 RunMessageLoop(); 256 257 EXPECT_EQ(AutofillMetrics::AUTOCHECKOUT_FAILED, 258 metric_logger().autocheckout_status()); 259} 260 261IN_PROC_BROWSER_TEST_F(AutofillDialogControllerTest, FillInputFromAutofill) { 262 InitializeControllerOfType(DIALOG_TYPE_REQUEST_AUTOCOMPLETE); 263 264 AutofillProfile full_profile(test::GetFullProfile()); 265 controller()->GetTestingManager()->AddTestingProfile(&full_profile); 266 267 const DetailInputs& inputs = 268 controller()->RequestedFieldsForSection(SECTION_SHIPPING); 269 const DetailInput& triggering_input = inputs[0]; 270 string16 value = full_profile.GetRawInfo(triggering_input.type); 271 TestableAutofillDialogView* view = controller()->view()->GetTestableView(); 272 view->SetTextContentsOfInput(triggering_input, 273 value.substr(0, value.size() / 2)); 274 view->ActivateInput(triggering_input); 275 276 ASSERT_EQ(&triggering_input, controller()->input_showing_popup()); 277 controller()->DidAcceptSuggestion(string16(), 0); 278 279 // All inputs should be filled. 280 AutofillProfileWrapper wrapper(&full_profile, 0); 281 for (size_t i = 0; i < inputs.size(); ++i) { 282 EXPECT_EQ(wrapper.GetInfo(inputs[i].type), 283 view->GetTextContentsOfInput(inputs[i])); 284 } 285 286 // Now simulate some user edits and try again. 287 std::vector<string16> expectations; 288 for (size_t i = 0; i < inputs.size(); ++i) { 289 string16 users_input = i % 2 == 0 ? string16() : ASCIIToUTF16("dummy"); 290 view->SetTextContentsOfInput(inputs[i], users_input); 291 // Empty inputs should be filled, others should be left alone. 292 string16 expectation = 293 &inputs[i] == &triggering_input || users_input.empty() ? 294 wrapper.GetInfo(inputs[i].type) : 295 users_input; 296 expectations.push_back(expectation); 297 } 298 299 view->SetTextContentsOfInput(triggering_input, 300 value.substr(0, value.size() / 2)); 301 view->ActivateInput(triggering_input); 302 ASSERT_EQ(&triggering_input, controller()->input_showing_popup()); 303 controller()->DidAcceptSuggestion(string16(), 0); 304 305 for (size_t i = 0; i < inputs.size(); ++i) { 306 EXPECT_EQ(expectations[i], view->GetTextContentsOfInput(inputs[i])); 307 } 308} 309 310// Test that the Autocheckout progress bar is showing after submitting the 311// dialog for controller with type DIALOG_TYPE_AUTOCHECKOUT. 312IN_PROC_BROWSER_TEST_F(AutofillDialogControllerTest, 313 AutocheckoutShowsProgressBar) { 314 InitializeControllerOfType(DIALOG_TYPE_AUTOCHECKOUT); 315 EXPECT_TRUE(controller()->ShouldShowDetailArea()); 316 EXPECT_FALSE(controller()->ShouldShowProgressBar()); 317 318 controller()->view()->GetTestableView()->SubmitForTesting(); 319 EXPECT_FALSE(controller()->ShouldShowDetailArea()); 320 EXPECT_TRUE(controller()->ShouldShowProgressBar()); 321} 322 323// Test that the Autocheckout progress bar is not showing after submitting the 324// dialog for controller with type DIALOG_TYPE_REQUEST_AUTOCOMPLETE. 325IN_PROC_BROWSER_TEST_F(AutofillDialogControllerTest, 326 RequestAutocompleteDoesntShowProgressBar) { 327 InitializeControllerOfType(DIALOG_TYPE_REQUEST_AUTOCOMPLETE); 328 EXPECT_TRUE(controller()->ShouldShowDetailArea()); 329 EXPECT_FALSE(controller()->ShouldShowProgressBar()); 330 331 controller()->view()->GetTestableView()->SubmitForTesting(); 332 EXPECT_TRUE(controller()->ShouldShowDetailArea()); 333 EXPECT_FALSE(controller()->ShouldShowProgressBar()); 334} 335 336// Tests that changing the value of a CC expiration date combobox works as 337// expected when Autofill is used to fill text inputs. 338IN_PROC_BROWSER_TEST_F(AutofillDialogControllerTest, FillComboboxFromAutofill) { 339 InitializeControllerOfType(DIALOG_TYPE_REQUEST_AUTOCOMPLETE); 340 341 CreditCard card1; 342 test::SetCreditCardInfo(&card1, "JJ Smith", "4111111111111111", "12", "2018"); 343 controller()->GetTestingManager()->AddTestingCreditCard(&card1); 344 CreditCard card2; 345 test::SetCreditCardInfo(&card2, "B Bird", "3111111111111111", "11", "2017"); 346 controller()->GetTestingManager()->AddTestingCreditCard(&card2); 347 AutofillProfile full_profile(test::GetFullProfile()); 348 controller()->GetTestingManager()->AddTestingProfile(&full_profile); 349 350 const DetailInputs& inputs = 351 controller()->RequestedFieldsForSection(SECTION_CC); 352 const DetailInput& triggering_input = inputs[0]; 353 string16 value = card1.GetRawInfo(triggering_input.type); 354 TestableAutofillDialogView* view = controller()->view()->GetTestableView(); 355 view->SetTextContentsOfInput(triggering_input, 356 value.substr(0, value.size() / 2)); 357 view->ActivateInput(triggering_input); 358 359 ASSERT_EQ(&triggering_input, controller()->input_showing_popup()); 360 controller()->DidAcceptSuggestion(string16(), 0); 361 362 // All inputs should be filled. 363 AutofillCreditCardWrapper wrapper1(&card1); 364 for (size_t i = 0; i < inputs.size(); ++i) { 365 EXPECT_EQ(wrapper1.GetInfo(inputs[i].type), 366 view->GetTextContentsOfInput(inputs[i])); 367 } 368 369 // Try again with different data. Only expiration date and the triggering 370 // input should be overwritten. 371 value = card2.GetRawInfo(triggering_input.type); 372 view->SetTextContentsOfInput(triggering_input, 373 value.substr(0, value.size() / 2)); 374 view->ActivateInput(triggering_input); 375 ASSERT_EQ(&triggering_input, controller()->input_showing_popup()); 376 controller()->DidAcceptSuggestion(string16(), 0); 377 378 AutofillCreditCardWrapper wrapper2(&card2); 379 for (size_t i = 0; i < inputs.size(); ++i) { 380 const DetailInput& input = inputs[i]; 381 if (&input == &triggering_input || 382 input.type == CREDIT_CARD_EXP_MONTH || 383 input.type == CREDIT_CARD_EXP_4_DIGIT_YEAR) { 384 EXPECT_EQ(wrapper2.GetInfo(input.type), 385 view->GetTextContentsOfInput(input)); 386 } else if (input.type == CREDIT_CARD_VERIFICATION_CODE) { 387 EXPECT_TRUE(view->GetTextContentsOfInput(input).empty()); 388 } else { 389 EXPECT_EQ(wrapper1.GetInfo(input.type), 390 view->GetTextContentsOfInput(input)); 391 } 392 } 393 394 // Now fill from a profile. It should not overwrite any CC info. 395 const DetailInputs& billing_inputs = 396 controller()->RequestedFieldsForSection(SECTION_BILLING); 397 const DetailInput& billing_triggering_input = billing_inputs[0]; 398 value = full_profile.GetRawInfo(triggering_input.type); 399 view->SetTextContentsOfInput(billing_triggering_input, 400 value.substr(0, value.size() / 2)); 401 view->ActivateInput(billing_triggering_input); 402 403 ASSERT_EQ(&billing_triggering_input, controller()->input_showing_popup()); 404 controller()->DidAcceptSuggestion(string16(), 0); 405 406 for (size_t i = 0; i < inputs.size(); ++i) { 407 const DetailInput& input = inputs[i]; 408 if (&input == &triggering_input || 409 input.type == CREDIT_CARD_EXP_MONTH || 410 input.type == CREDIT_CARD_EXP_4_DIGIT_YEAR) { 411 EXPECT_EQ(wrapper2.GetInfo(input.type), 412 view->GetTextContentsOfInput(input)); 413 } else if (input.type == CREDIT_CARD_VERIFICATION_CODE) { 414 EXPECT_TRUE(view->GetTextContentsOfInput(input).empty()); 415 } else { 416 EXPECT_EQ(wrapper1.GetInfo(input.type), 417 view->GetTextContentsOfInput(input)); 418 } 419 } 420} 421#endif // defined(TOOLKIT_VIEWS) 422 423} // namespace autofill 424