autofill_dialog_controller_browsertest.cc revision b2df76ea8fec9e32f6f3718986dba0d95315b29c
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 bool InputIsValid(AutofillFieldType type, 102 const string16& value) const OVERRIDE { 103 return true; 104 } 105 106 virtual ValidityData InputsAreValid( 107 const DetailOutputMap& inputs, 108 ValidationType validation_type) const OVERRIDE { 109 return ValidityData(); 110 } 111 112 // Increase visibility for testing. 113 AutofillDialogView* view() { return AutofillDialogControllerImpl::view(); } 114 const DetailInput* input_showing_popup() const { 115 return AutofillDialogControllerImpl::input_showing_popup(); 116 } 117 118 TestPersonalDataManager* GetTestingManager() { 119 return &test_manager_; 120 } 121 122 protected: 123 virtual PersonalDataManager* GetManager() OVERRIDE { 124 return &test_manager_; 125 } 126 127 private: 128 // To specify our own metric logger. 129 virtual const AutofillMetrics& GetMetricLogger() const OVERRIDE { 130 return metric_logger_; 131 } 132 133 const AutofillMetrics& metric_logger_; 134 TestPersonalDataManager test_manager_; 135 scoped_refptr<content::MessageLoopRunner> message_loop_runner_; 136 137 DISALLOW_COPY_AND_ASSIGN(TestAutofillDialogController); 138}; 139 140} // namespace 141 142class AutofillDialogControllerTest : public InProcessBrowserTest { 143 public: 144 AutofillDialogControllerTest() {} 145 virtual ~AutofillDialogControllerTest() {} 146 147 void InitializeControllerOfType(DialogType dialog_type) { 148 FormData form; 149 form.name = ASCIIToUTF16("TestForm"); 150 form.method = ASCIIToUTF16("POST"); 151 form.origin = GURL("http://example.com/form.html"); 152 form.action = GURL("http://example.com/submit.html"); 153 form.user_submitted = true; 154 155 FormFieldData field; 156 field.autocomplete_attribute = "email"; 157 form.fields.push_back(field); 158 159 message_loop_runner_ = new content::MessageLoopRunner; 160 controller_ = new TestAutofillDialogController( 161 GetActiveWebContents(), 162 form, 163 metric_logger_, 164 message_loop_runner_, 165 dialog_type); 166 controller_->Show(); 167 } 168 169 content::WebContents* GetActiveWebContents() { 170 return browser()->tab_strip_model()->GetActiveWebContents(); 171 } 172 173 const MockAutofillMetrics& metric_logger() { return metric_logger_; } 174 TestAutofillDialogController* controller() { return controller_; } 175 176 void RunMessageLoop() { 177 message_loop_runner_->Run(); 178 } 179 180 private: 181 MockAutofillMetrics metric_logger_; 182 TestAutofillDialogController* controller_; // Weak reference. 183 scoped_refptr<content::MessageLoopRunner> message_loop_runner_; 184 DISALLOW_COPY_AND_ASSIGN(AutofillDialogControllerTest); 185}; 186 187// TODO(isherman): Enable these tests on other platforms once the UI is 188// implemented on those platforms. 189#if defined(TOOLKIT_VIEWS) 190// Submit the form data. 191IN_PROC_BROWSER_TEST_F(AutofillDialogControllerTest, Submit) { 192 InitializeControllerOfType(DIALOG_TYPE_REQUEST_AUTOCOMPLETE); 193 controller()->view()->GetTestableView()->SubmitForTesting(); 194 195 RunMessageLoop(); 196 197 EXPECT_EQ(AutofillMetrics::DIALOG_ACCEPTED, 198 metric_logger().dialog_dismissal_action()); 199 EXPECT_EQ(DIALOG_TYPE_REQUEST_AUTOCOMPLETE, metric_logger().dialog_type()); 200} 201 202// Cancel out of the dialog. 203IN_PROC_BROWSER_TEST_F(AutofillDialogControllerTest, Cancel) { 204 InitializeControllerOfType(DIALOG_TYPE_REQUEST_AUTOCOMPLETE); 205 controller()->view()->GetTestableView()->CancelForTesting(); 206 207 RunMessageLoop(); 208 209 EXPECT_EQ(AutofillMetrics::DIALOG_CANCELED, 210 metric_logger().dialog_dismissal_action()); 211 EXPECT_EQ(DIALOG_TYPE_REQUEST_AUTOCOMPLETE, metric_logger().dialog_type()); 212} 213 214// Take some other action that dismisses the dialog. 215IN_PROC_BROWSER_TEST_F(AutofillDialogControllerTest, Hide) { 216 InitializeControllerOfType(DIALOG_TYPE_REQUEST_AUTOCOMPLETE); 217 controller()->Hide(); 218 219 RunMessageLoop(); 220 221 EXPECT_EQ(AutofillMetrics::DIALOG_CANCELED, 222 metric_logger().dialog_dismissal_action()); 223 EXPECT_EQ(DIALOG_TYPE_REQUEST_AUTOCOMPLETE, metric_logger().dialog_type()); 224} 225 226// Test Autocheckout success metrics. 227IN_PROC_BROWSER_TEST_F(AutofillDialogControllerTest, AutocheckoutSuccess) { 228 InitializeControllerOfType(DIALOG_TYPE_AUTOCHECKOUT); 229 controller()->view()->GetTestableView()->SubmitForTesting(); 230 231 EXPECT_EQ(AutofillMetrics::DIALOG_ACCEPTED, 232 metric_logger().dialog_dismissal_action()); 233 EXPECT_EQ(DIALOG_TYPE_AUTOCHECKOUT, metric_logger().dialog_type()); 234 235 controller()->Hide(); 236 RunMessageLoop(); 237 238 EXPECT_EQ(AutofillMetrics::AUTOCHECKOUT_SUCCEEDED, 239 metric_logger().autocheckout_status()); 240} 241 242// Test Autocheckout failure metric. 243IN_PROC_BROWSER_TEST_F(AutofillDialogControllerTest, AutocheckoutError) { 244 InitializeControllerOfType(DIALOG_TYPE_AUTOCHECKOUT); 245 controller()->view()->GetTestableView()->SubmitForTesting(); 246 247 EXPECT_EQ(AutofillMetrics::DIALOG_ACCEPTED, 248 metric_logger().dialog_dismissal_action()); 249 EXPECT_EQ(DIALOG_TYPE_AUTOCHECKOUT, metric_logger().dialog_type()); 250 251 controller()->OnAutocheckoutError(); 252 controller()->view()->GetTestableView()->CancelForTesting(); 253 254 RunMessageLoop(); 255 256 EXPECT_EQ(AutofillMetrics::AUTOCHECKOUT_FAILED, 257 metric_logger().autocheckout_status()); 258} 259 260IN_PROC_BROWSER_TEST_F(AutofillDialogControllerTest, FillInputFromAutofill) { 261 InitializeControllerOfType(DIALOG_TYPE_REQUEST_AUTOCOMPLETE); 262 263 AutofillProfile full_profile(test::GetFullProfile()); 264 controller()->GetTestingManager()->AddTestingProfile(&full_profile); 265 266 const DetailInputs& inputs = 267 controller()->RequestedFieldsForSection(SECTION_SHIPPING); 268 const DetailInput& triggering_input = inputs[0]; 269 string16 value = full_profile.GetRawInfo(triggering_input.type); 270 TestableAutofillDialogView* view = controller()->view()->GetTestableView(); 271 view->SetTextContentsOfInput(triggering_input, 272 value.substr(0, value.size() / 2)); 273 view->ActivateInput(triggering_input); 274 275 ASSERT_EQ(&triggering_input, controller()->input_showing_popup()); 276 controller()->DidAcceptSuggestion(string16(), 0); 277 278 // All inputs should be filled. 279 AutofillProfileWrapper wrapper(&full_profile, 0); 280 for (size_t i = 0; i < inputs.size(); ++i) { 281 EXPECT_EQ(wrapper.GetInfo(inputs[i].type), 282 view->GetTextContentsOfInput(inputs[i])); 283 } 284 285 // Now simulate some user edits and try again. 286 std::vector<string16> expectations; 287 for (size_t i = 0; i < inputs.size(); ++i) { 288 string16 users_input = i % 2 == 0 ? string16() : ASCIIToUTF16("dummy"); 289 view->SetTextContentsOfInput(inputs[i], users_input); 290 // Empty inputs should be filled, others should be left alone. 291 string16 expectation = 292 &inputs[i] == &triggering_input || users_input.empty() ? 293 wrapper.GetInfo(inputs[i].type) : 294 users_input; 295 expectations.push_back(expectation); 296 } 297 298 view->SetTextContentsOfInput(triggering_input, 299 value.substr(0, value.size() / 2)); 300 view->ActivateInput(triggering_input); 301 ASSERT_EQ(&triggering_input, controller()->input_showing_popup()); 302 controller()->DidAcceptSuggestion(string16(), 0); 303 304 for (size_t i = 0; i < inputs.size(); ++i) { 305 EXPECT_EQ(expectations[i], view->GetTextContentsOfInput(inputs[i])); 306 } 307} 308 309// Test that the Autocheckout progress bar is showing after submitting the 310// dialog for controller with type DIALOG_TYPE_AUTOCHECKOUT. 311IN_PROC_BROWSER_TEST_F(AutofillDialogControllerTest, 312 AutocheckoutShowsProgressBar) { 313 InitializeControllerOfType(DIALOG_TYPE_AUTOCHECKOUT); 314 EXPECT_TRUE(controller()->ShouldShowDetailArea()); 315 EXPECT_FALSE(controller()->ShouldShowProgressBar()); 316 317 controller()->view()->GetTestableView()->SubmitForTesting(); 318 EXPECT_FALSE(controller()->ShouldShowDetailArea()); 319 EXPECT_TRUE(controller()->ShouldShowProgressBar()); 320} 321 322// Test that the Autocheckout progress bar is not showing after submitting the 323// dialog for controller with type DIALOG_TYPE_REQUEST_AUTOCOMPLETE. 324IN_PROC_BROWSER_TEST_F(AutofillDialogControllerTest, 325 RequestAutocompleteDoesntShowProgressBar) { 326 InitializeControllerOfType(DIALOG_TYPE_REQUEST_AUTOCOMPLETE); 327 EXPECT_TRUE(controller()->ShouldShowDetailArea()); 328 EXPECT_FALSE(controller()->ShouldShowProgressBar()); 329 330 controller()->view()->GetTestableView()->SubmitForTesting(); 331 EXPECT_TRUE(controller()->ShouldShowDetailArea()); 332 EXPECT_FALSE(controller()->ShouldShowProgressBar()); 333} 334 335// Tests that changing the value of a CC expiration date combobox works as 336// expected when Autofill is used to fill text inputs. 337IN_PROC_BROWSER_TEST_F(AutofillDialogControllerTest, FillComboboxFromAutofill) { 338 InitializeControllerOfType(DIALOG_TYPE_REQUEST_AUTOCOMPLETE); 339 340 CreditCard card1; 341 test::SetCreditCardInfo(&card1, "JJ Smith", "4111111111111111", "12", "2018"); 342 controller()->GetTestingManager()->AddTestingCreditCard(&card1); 343 CreditCard card2; 344 test::SetCreditCardInfo(&card2, "B Bird", "3111111111111111", "11", "2017"); 345 controller()->GetTestingManager()->AddTestingCreditCard(&card2); 346 AutofillProfile full_profile(test::GetFullProfile()); 347 controller()->GetTestingManager()->AddTestingProfile(&full_profile); 348 349 const DetailInputs& inputs = 350 controller()->RequestedFieldsForSection(SECTION_CC); 351 const DetailInput& triggering_input = inputs[0]; 352 string16 value = card1.GetRawInfo(triggering_input.type); 353 TestableAutofillDialogView* view = controller()->view()->GetTestableView(); 354 view->SetTextContentsOfInput(triggering_input, 355 value.substr(0, value.size() / 2)); 356 view->ActivateInput(triggering_input); 357 358 ASSERT_EQ(&triggering_input, controller()->input_showing_popup()); 359 controller()->DidAcceptSuggestion(string16(), 0); 360 361 // All inputs should be filled. 362 AutofillCreditCardWrapper wrapper1(&card1); 363 for (size_t i = 0; i < inputs.size(); ++i) { 364 EXPECT_EQ(wrapper1.GetInfo(inputs[i].type), 365 view->GetTextContentsOfInput(inputs[i])); 366 } 367 368 // Try again with different data. Only expiration date and the triggering 369 // input should be overwritten. 370 value = card2.GetRawInfo(triggering_input.type); 371 view->SetTextContentsOfInput(triggering_input, 372 value.substr(0, value.size() / 2)); 373 view->ActivateInput(triggering_input); 374 ASSERT_EQ(&triggering_input, controller()->input_showing_popup()); 375 controller()->DidAcceptSuggestion(string16(), 0); 376 377 AutofillCreditCardWrapper wrapper2(&card2); 378 for (size_t i = 0; i < inputs.size(); ++i) { 379 const DetailInput& input = inputs[i]; 380 if (&input == &triggering_input || 381 input.type == CREDIT_CARD_EXP_MONTH || 382 input.type == CREDIT_CARD_EXP_4_DIGIT_YEAR) { 383 EXPECT_EQ(wrapper2.GetInfo(input.type), 384 view->GetTextContentsOfInput(input)); 385 } else if (input.type == CREDIT_CARD_VERIFICATION_CODE) { 386 EXPECT_TRUE(view->GetTextContentsOfInput(input).empty()); 387 } else { 388 EXPECT_EQ(wrapper1.GetInfo(input.type), 389 view->GetTextContentsOfInput(input)); 390 } 391 } 392 393 // Now fill from a profile. It should not overwrite any CC info. 394 const DetailInputs& billing_inputs = 395 controller()->RequestedFieldsForSection(SECTION_BILLING); 396 const DetailInput& billing_triggering_input = billing_inputs[0]; 397 value = full_profile.GetRawInfo(triggering_input.type); 398 view->SetTextContentsOfInput(billing_triggering_input, 399 value.substr(0, value.size() / 2)); 400 view->ActivateInput(billing_triggering_input); 401 402 ASSERT_EQ(&billing_triggering_input, controller()->input_showing_popup()); 403 controller()->DidAcceptSuggestion(string16(), 0); 404 405 for (size_t i = 0; i < inputs.size(); ++i) { 406 const DetailInput& input = inputs[i]; 407 if (&input == &triggering_input || 408 input.type == CREDIT_CARD_EXP_MONTH || 409 input.type == CREDIT_CARD_EXP_4_DIGIT_YEAR) { 410 EXPECT_EQ(wrapper2.GetInfo(input.type), 411 view->GetTextContentsOfInput(input)); 412 } else if (input.type == CREDIT_CARD_VERIFICATION_CODE) { 413 EXPECT_TRUE(view->GetTextContentsOfInput(input).empty()); 414 } else { 415 EXPECT_EQ(wrapper1.GetInfo(input.type), 416 view->GetTextContentsOfInput(input)); 417 } 418 } 419} 420#endif // defined(TOOLKIT_VIEWS) 421 422} // namespace autofill 423