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/command_line.h" 7 #include "base/memory/ref_counted.h" 8 #include "base/memory/weak_ptr.h" 9 #include "base/message_loop/message_loop.h" 10 #include "base/strings/utf_string_conversions.h" 11 #include "base/time/time.h" 12 #include "chrome/browser/autofill/personal_data_manager_factory.h" 13 #include "chrome/browser/profiles/profile.h" 14 #include "chrome/browser/ui/autofill/autofill_dialog_controller_impl.h" 15 #include "chrome/browser/ui/autofill/autofill_dialog_view.h" 16 #include "chrome/browser/ui/autofill/data_model_wrapper.h" 17 #include "chrome/browser/ui/autofill/tab_autofill_manager_delegate.h" 18 #include "chrome/browser/ui/autofill/testable_autofill_dialog_view.h" 19 #include "chrome/browser/ui/browser.h" 20 #include "chrome/browser/ui/tabs/tab_strip_model.h" 21 #include "chrome/test/base/in_process_browser_test.h" 22 #include "chrome/test/base/ui_test_utils.h" 23 #include "components/autofill/content/browser/wallet/mock_wallet_client.h" 24 #include "components/autofill/content/browser/wallet/wallet_test_util.h" 25 #include "components/autofill/core/browser/autofill_common_test.h" 26 #include "components/autofill/core/browser/autofill_metrics.h" 27 #include "components/autofill/core/browser/test_personal_data_manager.h" 28 #include "components/autofill/core/browser/validation.h" 29 #include "components/autofill/core/common/autofill_switches.h" 30 #include "components/autofill/core/common/form_data.h" 31 #include "components/autofill/core/common/form_field_data.h" 32 #include "content/public/browser/browser_thread.h" 33 #include "content/public/browser/web_contents.h" 34 #include "content/public/browser/web_contents_delegate.h" 35 #include "content/public/test/browser_test_utils.h" 36 #include "content/public/test/test_utils.h" 37 #include "testing/gmock/include/gmock/gmock.h" 38 #include "testing/gtest/include/gtest/gtest.h" 39 #include "third_party/WebKit/public/web/WebInputEvent.h" 40 41 namespace autofill { 42 43 namespace { 44 45 void MockCallback(const FormStructure*, const std::string&) {} 46 47 class MockAutofillMetrics : public AutofillMetrics { 48 public: 49 MockAutofillMetrics() 50 : dialog_type_(static_cast<DialogType>(-1)), 51 dialog_dismissal_action_( 52 static_cast<AutofillMetrics::DialogDismissalAction>(-1)), 53 autocheckout_status_( 54 static_cast<AutofillMetrics::AutocheckoutCompletionStatus>(-1)) {} 55 virtual ~MockAutofillMetrics() {} 56 57 // AutofillMetrics: 58 virtual void LogAutocheckoutDuration( 59 const base::TimeDelta& duration, 60 AutocheckoutCompletionStatus status) const OVERRIDE { 61 // Ignore constness for testing. 62 MockAutofillMetrics* mutable_this = const_cast<MockAutofillMetrics*>(this); 63 mutable_this->autocheckout_status_ = status; 64 } 65 66 virtual void LogDialogUiDuration( 67 const base::TimeDelta& duration, 68 DialogType dialog_type, 69 DialogDismissalAction dismissal_action) const OVERRIDE { 70 // Ignore constness for testing. 71 MockAutofillMetrics* mutable_this = const_cast<MockAutofillMetrics*>(this); 72 mutable_this->dialog_type_ = dialog_type; 73 mutable_this->dialog_dismissal_action_ = dismissal_action; 74 } 75 76 DialogType dialog_type() const { return dialog_type_; } 77 AutofillMetrics::DialogDismissalAction dialog_dismissal_action() const { 78 return dialog_dismissal_action_; 79 } 80 81 AutofillMetrics::AutocheckoutCompletionStatus autocheckout_status() const { 82 return autocheckout_status_; 83 } 84 85 MOCK_CONST_METHOD2(LogDialogDismissalState, 86 void(DialogType dialog_type, DialogDismissalState state)); 87 88 private: 89 DialogType dialog_type_; 90 AutofillMetrics::DialogDismissalAction dialog_dismissal_action_; 91 AutofillMetrics::AutocheckoutCompletionStatus autocheckout_status_; 92 93 DISALLOW_COPY_AND_ASSIGN(MockAutofillMetrics); 94 }; 95 96 class TestAutofillDialogController : public AutofillDialogControllerImpl { 97 public: 98 TestAutofillDialogController(content::WebContents* contents, 99 const FormData& form_data, 100 const AutofillMetrics& metric_logger, 101 scoped_refptr<content::MessageLoopRunner> runner, 102 const DialogType dialog_type) 103 : AutofillDialogControllerImpl(contents, 104 form_data, 105 GURL(), 106 dialog_type, 107 base::Bind(&MockCallback)), 108 metric_logger_(metric_logger), 109 mock_wallet_client_( 110 Profile::FromBrowserContext(contents->GetBrowserContext())-> 111 GetRequestContext(), this), 112 message_loop_runner_(runner), 113 use_validation_(false), 114 weak_ptr_factory_(this) {} 115 116 virtual ~TestAutofillDialogController() {} 117 118 virtual void ViewClosed() OVERRIDE { 119 message_loop_runner_->Quit(); 120 AutofillDialogControllerImpl::ViewClosed(); 121 } 122 123 virtual string16 InputValidityMessage( 124 DialogSection section, 125 ServerFieldType type, 126 const string16& value) OVERRIDE { 127 if (!use_validation_) 128 return string16(); 129 return AutofillDialogControllerImpl::InputValidityMessage( 130 section, type, value); 131 } 132 133 virtual ValidityData InputsAreValid( 134 DialogSection section, 135 const DetailOutputMap& inputs, 136 ValidationType validation_type) OVERRIDE { 137 if (!use_validation_) 138 return ValidityData(); 139 return AutofillDialogControllerImpl::InputsAreValid( 140 section, inputs, validation_type); 141 } 142 143 // Saving to Chrome is tested in AutofillDialogControllerImpl unit tests. 144 // TODO(estade): test that the view defaults to saving to Chrome. 145 virtual bool ShouldOfferToSaveInChrome() const OVERRIDE { 146 return false; 147 } 148 149 // Increase visibility for testing. 150 using AutofillDialogControllerImpl::view; 151 using AutofillDialogControllerImpl::input_showing_popup; 152 153 virtual std::vector<DialogNotification> CurrentNotifications() OVERRIDE { 154 return notifications_; 155 } 156 157 void set_notifications(const std::vector<DialogNotification>& notifications) { 158 notifications_ = notifications; 159 } 160 161 TestPersonalDataManager* GetTestingManager() { 162 return &test_manager_; 163 } 164 165 using AutofillDialogControllerImpl::IsEditingExistingData; 166 using AutofillDialogControllerImpl::IsManuallyEditingSection; 167 168 void set_use_validation(bool use_validation) { 169 use_validation_ = use_validation; 170 } 171 172 base::WeakPtr<TestAutofillDialogController> AsWeakPtr() { 173 return weak_ptr_factory_.GetWeakPtr(); 174 } 175 176 protected: 177 virtual PersonalDataManager* GetManager() OVERRIDE { 178 return &test_manager_; 179 } 180 181 virtual wallet::WalletClient* GetWalletClient() OVERRIDE { 182 return &mock_wallet_client_; 183 } 184 185 private: 186 // To specify our own metric logger. 187 virtual const AutofillMetrics& GetMetricLogger() const OVERRIDE { 188 return metric_logger_; 189 } 190 191 const AutofillMetrics& metric_logger_; 192 TestPersonalDataManager test_manager_; 193 testing::NiceMock<wallet::MockWalletClient> mock_wallet_client_; 194 scoped_refptr<content::MessageLoopRunner> message_loop_runner_; 195 bool use_validation_; 196 197 // A list of notifications to show in the notification area of the dialog. 198 // This is used to control what |CurrentNotifications()| returns for testing. 199 std::vector<DialogNotification> notifications_; 200 201 // Allows generation of WeakPtrs, so controller liveness can be tested. 202 base::WeakPtrFactory<TestAutofillDialogController> weak_ptr_factory_; 203 204 DISALLOW_COPY_AND_ASSIGN(TestAutofillDialogController); 205 }; 206 207 } // namespace 208 209 class AutofillDialogControllerTest : public InProcessBrowserTest { 210 public: 211 AutofillDialogControllerTest() {} 212 virtual ~AutofillDialogControllerTest() {} 213 214 virtual void SetUpOnMainThread() OVERRIDE { 215 autofill::test::DisableSystemServices(browser()->profile()); 216 } 217 218 virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE { 219 CommandLine::ForCurrentProcess()->AppendSwitch( 220 switches::kEnableInteractiveAutocomplete); 221 } 222 223 void InitializeControllerOfType(DialogType dialog_type) { 224 FormData form; 225 form.name = ASCIIToUTF16("TestForm"); 226 form.method = ASCIIToUTF16("POST"); 227 form.origin = GURL("http://example.com/form.html"); 228 form.action = GURL("http://example.com/submit.html"); 229 form.user_submitted = true; 230 231 FormFieldData field; 232 field.autocomplete_attribute = "shipping tel"; 233 form.fields.push_back(field); 234 235 message_loop_runner_ = new content::MessageLoopRunner; 236 controller_ = new TestAutofillDialogController( 237 GetActiveWebContents(), 238 form, 239 metric_logger_, 240 message_loop_runner_, 241 dialog_type); 242 controller_->Show(); 243 } 244 245 content::WebContents* GetActiveWebContents() { 246 return browser()->tab_strip_model()->GetActiveWebContents(); 247 } 248 249 const MockAutofillMetrics& metric_logger() { return metric_logger_; } 250 TestAutofillDialogController* controller() { return controller_; } 251 252 void RunMessageLoop() { 253 message_loop_runner_->Run(); 254 } 255 256 // Loads an HTML page in |GetActiveWebContents()| with markup as follows: 257 // <form>|form_inner_html|</form>. After loading, emulates a click event on 258 // the page as requestAutocomplete() must be in response to a user gesture. 259 // Returns the |AutofillDialogControllerImpl| created by this invocation. 260 AutofillDialogControllerImpl* SetUpHtmlAndInvoke( 261 const std::string& form_inner_html) { 262 content::WebContents* contents = GetActiveWebContents(); 263 TabAutofillManagerDelegate* delegate = 264 TabAutofillManagerDelegate::FromWebContents(contents); 265 DCHECK(!delegate->GetDialogControllerForTesting()); 266 267 ui_test_utils::NavigateToURL( 268 browser(), GURL(std::string("data:text/html,") + 269 "<!doctype html>" 270 "<html>" 271 "<body>" 272 "<form>" + form_inner_html + "</form>" 273 "<script>" 274 "function send(msg) {" 275 "domAutomationController.setAutomationId(0);" 276 "domAutomationController.send(msg);" 277 "}" 278 "document.forms[0].onautocompleteerror = function(e) {" 279 "send('error: ' + e.reason);" 280 "};" 281 "document.forms[0].onautocomplete = function() {" 282 "send('success');" 283 "};" 284 "window.onclick = function() {" 285 "document.forms[0].requestAutocomplete();" 286 "send('clicked');" 287 "};" 288 "</script>" 289 "</body>" 290 "</html>")); 291 content::WaitForLoadStop(contents); 292 293 dom_message_queue_.reset(new content::DOMMessageQueue); 294 295 // Triggers the onclick handler which invokes requestAutocomplete(). 296 content::SimulateMouseClick(contents, 0, WebKit::WebMouseEvent::ButtonLeft); 297 ExpectDomMessage("clicked"); 298 299 AutofillDialogControllerImpl* controller = 300 static_cast<AutofillDialogControllerImpl*>( 301 delegate->GetDialogControllerForTesting()); 302 DCHECK(controller); 303 return controller; 304 } 305 306 // Wait for a message from the DOM automation controller (from JS in the 307 // page). Requires |SetUpHtmlAndInvoke()| be called first. 308 void ExpectDomMessage(const std::string& expected) { 309 std::string message; 310 ASSERT_TRUE(dom_message_queue_->WaitForMessage(&message)); 311 dom_message_queue_->ClearQueue(); 312 EXPECT_EQ("\"" + expected + "\"", message); 313 } 314 315 void AddCreditcardToProfile(Profile* profile, const CreditCard& card) { 316 PersonalDataManagerFactory::GetForProfile(profile)->AddCreditCard(card); 317 WaitForWebDB(); 318 } 319 320 void AddAutofillProfileToProfile(Profile* profile, 321 const AutofillProfile& autofill_profile) { 322 PersonalDataManagerFactory::GetForProfile(profile)->AddProfile( 323 autofill_profile); 324 WaitForWebDB(); 325 } 326 327 private: 328 void WaitForWebDB() { 329 content::RunAllPendingInMessageLoop(content::BrowserThread::DB); 330 } 331 332 MockAutofillMetrics metric_logger_; 333 TestAutofillDialogController* controller_; // Weak reference. 334 scoped_refptr<content::MessageLoopRunner> message_loop_runner_; 335 scoped_ptr<content::DOMMessageQueue> dom_message_queue_; 336 DISALLOW_COPY_AND_ASSIGN(AutofillDialogControllerTest); 337 }; 338 339 #if defined(TOOLKIT_VIEWS) || defined(OS_MACOSX) 340 // Submit the form data. 341 IN_PROC_BROWSER_TEST_F(AutofillDialogControllerTest, Submit) { 342 InitializeControllerOfType(DIALOG_TYPE_REQUEST_AUTOCOMPLETE); 343 controller()->GetTestableView()->SubmitForTesting(); 344 345 RunMessageLoop(); 346 347 EXPECT_EQ(AutofillMetrics::DIALOG_ACCEPTED, 348 metric_logger().dialog_dismissal_action()); 349 EXPECT_EQ(DIALOG_TYPE_REQUEST_AUTOCOMPLETE, metric_logger().dialog_type()); 350 } 351 352 // Cancel out of the dialog. 353 IN_PROC_BROWSER_TEST_F(AutofillDialogControllerTest, Cancel) { 354 InitializeControllerOfType(DIALOG_TYPE_REQUEST_AUTOCOMPLETE); 355 controller()->GetTestableView()->CancelForTesting(); 356 357 RunMessageLoop(); 358 359 EXPECT_EQ(AutofillMetrics::DIALOG_CANCELED, 360 metric_logger().dialog_dismissal_action()); 361 EXPECT_EQ(DIALOG_TYPE_REQUEST_AUTOCOMPLETE, metric_logger().dialog_type()); 362 } 363 364 // Take some other action that dismisses the dialog. 365 IN_PROC_BROWSER_TEST_F(AutofillDialogControllerTest, Hide) { 366 InitializeControllerOfType(DIALOG_TYPE_REQUEST_AUTOCOMPLETE); 367 controller()->Hide(); 368 369 RunMessageLoop(); 370 371 EXPECT_EQ(AutofillMetrics::DIALOG_CANCELED, 372 metric_logger().dialog_dismissal_action()); 373 EXPECT_EQ(DIALOG_TYPE_REQUEST_AUTOCOMPLETE, metric_logger().dialog_type()); 374 } 375 376 // Ensure that Hide() will only destroy the controller object after the 377 // message loop has run. Otherwise, there may be read-after-free issues 378 // during some tests. 379 IN_PROC_BROWSER_TEST_F(AutofillDialogControllerTest, DeferredDestruction) { 380 InitializeControllerOfType(DIALOG_TYPE_REQUEST_AUTOCOMPLETE); 381 382 base::WeakPtr<TestAutofillDialogController> weak_ptr = 383 controller()->AsWeakPtr(); 384 EXPECT_TRUE(weak_ptr.get()); 385 386 controller()->Hide(); 387 EXPECT_TRUE(weak_ptr.get()); 388 389 RunMessageLoop(); 390 EXPECT_FALSE(weak_ptr.get()); 391 } 392 393 // Ensure that the expected metric is logged when the dialog is closed during 394 // signin. 395 IN_PROC_BROWSER_TEST_F(AutofillDialogControllerTest, CloseDuringSignin) { 396 InitializeControllerOfType(DIALOG_TYPE_REQUEST_AUTOCOMPLETE); 397 controller()->SignInLinkClicked(); 398 399 EXPECT_CALL(metric_logger(), 400 LogDialogDismissalState( 401 DIALOG_TYPE_REQUEST_AUTOCOMPLETE, 402 AutofillMetrics::DIALOG_CANCELED_DURING_SIGNIN)); 403 controller()->GetTestableView()->CancelForTesting(); 404 405 RunMessageLoop(); 406 407 EXPECT_EQ(AutofillMetrics::DIALOG_CANCELED, 408 metric_logger().dialog_dismissal_action()); 409 EXPECT_EQ(DIALOG_TYPE_REQUEST_AUTOCOMPLETE, metric_logger().dialog_type()); 410 } 411 412 // Test Autocheckout success metrics. 413 IN_PROC_BROWSER_TEST_F(AutofillDialogControllerTest, AutocheckoutSuccess) { 414 InitializeControllerOfType(DIALOG_TYPE_AUTOCHECKOUT); 415 controller()->GetTestableView()->SubmitForTesting(); 416 417 EXPECT_EQ(AutofillMetrics::DIALOG_ACCEPTED, 418 metric_logger().dialog_dismissal_action()); 419 EXPECT_EQ(DIALOG_TYPE_AUTOCHECKOUT, metric_logger().dialog_type()); 420 421 controller()->OnAutocheckoutSuccess(); 422 controller()->GetTestableView()->CancelForTesting(); 423 RunMessageLoop(); 424 425 EXPECT_EQ(AutofillMetrics::AUTOCHECKOUT_SUCCEEDED, 426 metric_logger().autocheckout_status()); 427 428 // Ensure closing the dialog doesn't fire any new metrics. 429 EXPECT_EQ(AutofillMetrics::DIALOG_ACCEPTED, 430 metric_logger().dialog_dismissal_action()); 431 EXPECT_EQ(DIALOG_TYPE_AUTOCHECKOUT, metric_logger().dialog_type()); 432 } 433 434 // Test Autocheckout failure metric. 435 IN_PROC_BROWSER_TEST_F(AutofillDialogControllerTest, AutocheckoutError) { 436 InitializeControllerOfType(DIALOG_TYPE_AUTOCHECKOUT); 437 controller()->GetTestableView()->SubmitForTesting(); 438 439 EXPECT_EQ(AutofillMetrics::DIALOG_ACCEPTED, 440 metric_logger().dialog_dismissal_action()); 441 EXPECT_EQ(DIALOG_TYPE_AUTOCHECKOUT, metric_logger().dialog_type()); 442 443 controller()->OnAutocheckoutError(); 444 controller()->GetTestableView()->CancelForTesting(); 445 RunMessageLoop(); 446 447 EXPECT_EQ(AutofillMetrics::AUTOCHECKOUT_FAILED, 448 metric_logger().autocheckout_status()); 449 450 // Ensure closing the dialog doesn't fire any new metrics. 451 EXPECT_EQ(AutofillMetrics::DIALOG_ACCEPTED, 452 metric_logger().dialog_dismissal_action()); 453 EXPECT_EQ(DIALOG_TYPE_AUTOCHECKOUT, metric_logger().dialog_type()); 454 } 455 456 IN_PROC_BROWSER_TEST_F(AutofillDialogControllerTest, AutocheckoutCancelled) { 457 InitializeControllerOfType(DIALOG_TYPE_AUTOCHECKOUT); 458 controller()->GetTestableView()->SubmitForTesting(); 459 460 EXPECT_EQ(AutofillMetrics::DIALOG_ACCEPTED, 461 metric_logger().dialog_dismissal_action()); 462 EXPECT_EQ(DIALOG_TYPE_AUTOCHECKOUT, metric_logger().dialog_type()); 463 464 controller()->GetTestableView()->CancelForTesting(); 465 RunMessageLoop(); 466 467 EXPECT_EQ(AutofillMetrics::AUTOCHECKOUT_CANCELLED, 468 metric_logger().autocheckout_status()); 469 470 // Ensure closing the dialog doesn't fire any new metrics. 471 EXPECT_EQ(AutofillMetrics::DIALOG_ACCEPTED, 472 metric_logger().dialog_dismissal_action()); 473 EXPECT_EQ(DIALOG_TYPE_AUTOCHECKOUT, metric_logger().dialog_type()); 474 } 475 476 IN_PROC_BROWSER_TEST_F(AutofillDialogControllerTest, FillInputFromAutofill) { 477 InitializeControllerOfType(DIALOG_TYPE_REQUEST_AUTOCOMPLETE); 478 479 AutofillProfile full_profile(test::GetFullProfile()); 480 controller()->GetTestingManager()->AddTestingProfile(&full_profile); 481 482 const DetailInputs& inputs = 483 controller()->RequestedFieldsForSection(SECTION_SHIPPING); 484 const DetailInput& triggering_input = inputs[0]; 485 string16 value = full_profile.GetRawInfo(triggering_input.type); 486 TestableAutofillDialogView* view = controller()->GetTestableView(); 487 view->SetTextContentsOfInput(triggering_input, 488 value.substr(0, value.size() / 2)); 489 view->ActivateInput(triggering_input); 490 491 ASSERT_EQ(&triggering_input, controller()->input_showing_popup()); 492 controller()->DidAcceptSuggestion(string16(), 0); 493 494 // All inputs should be filled. 495 AutofillProfileWrapper wrapper(&full_profile, 0); 496 for (size_t i = 0; i < inputs.size(); ++i) { 497 EXPECT_EQ(wrapper.GetInfo(AutofillType(inputs[i].type)), 498 view->GetTextContentsOfInput(inputs[i])); 499 } 500 501 // Now simulate some user edits and try again. 502 std::vector<string16> expectations; 503 for (size_t i = 0; i < inputs.size(); ++i) { 504 string16 users_input = i % 2 == 0 ? string16() : ASCIIToUTF16("dummy"); 505 view->SetTextContentsOfInput(inputs[i], users_input); 506 // Empty inputs should be filled, others should be left alone. 507 string16 expectation = 508 &inputs[i] == &triggering_input || users_input.empty() ? 509 wrapper.GetInfo(AutofillType(inputs[i].type)) : 510 users_input; 511 expectations.push_back(expectation); 512 } 513 514 view->SetTextContentsOfInput(triggering_input, 515 value.substr(0, value.size() / 2)); 516 view->ActivateInput(triggering_input); 517 ASSERT_EQ(&triggering_input, controller()->input_showing_popup()); 518 controller()->DidAcceptSuggestion(string16(), 0); 519 520 for (size_t i = 0; i < inputs.size(); ++i) { 521 EXPECT_EQ(expectations[i], view->GetTextContentsOfInput(inputs[i])); 522 } 523 } 524 525 // Test that Autocheckout steps are shown after submitting the 526 // dialog for controller with type DIALOG_TYPE_AUTOCHECKOUT. 527 IN_PROC_BROWSER_TEST_F(AutofillDialogControllerTest, AutocheckoutShowsSteps) { 528 InitializeControllerOfType(DIALOG_TYPE_AUTOCHECKOUT); 529 controller()->AddAutocheckoutStep(AUTOCHECKOUT_STEP_PROXY_CARD); 530 531 EXPECT_TRUE(controller()->ShouldShowDetailArea()); 532 EXPECT_TRUE(controller()->CurrentAutocheckoutSteps().empty()); 533 EXPECT_FALSE(controller()->ShouldShowProgressBar()); 534 535 controller()->GetTestableView()->SubmitForTesting(); 536 EXPECT_FALSE(controller()->ShouldShowDetailArea()); 537 EXPECT_FALSE(controller()->CurrentAutocheckoutSteps().empty()); 538 EXPECT_TRUE(controller()->ShouldShowProgressBar()); 539 controller()->GetTestableView()->CancelForTesting(); 540 RunMessageLoop(); 541 } 542 543 // Test that Autocheckout steps are not showing after submitting the 544 // dialog for controller with type DIALOG_TYPE_REQUEST_AUTOCOMPLETE. 545 IN_PROC_BROWSER_TEST_F(AutofillDialogControllerTest, 546 RequestAutocompleteDoesntShowSteps) { 547 InitializeControllerOfType(DIALOG_TYPE_REQUEST_AUTOCOMPLETE); 548 controller()->AddAutocheckoutStep(AUTOCHECKOUT_STEP_PROXY_CARD); 549 550 EXPECT_TRUE(controller()->ShouldShowDetailArea()); 551 EXPECT_TRUE(controller()->CurrentAutocheckoutSteps().empty()); 552 EXPECT_FALSE(controller()->ShouldShowProgressBar()); 553 554 controller()->GetTestableView()->SubmitForTesting(); 555 EXPECT_TRUE(controller()->ShouldShowDetailArea()); 556 EXPECT_TRUE(controller()->CurrentAutocheckoutSteps().empty()); 557 EXPECT_FALSE(controller()->ShouldShowProgressBar()); 558 } 559 560 // Tests that changing the value of a CC expiration date combobox works as 561 // expected when Autofill is used to fill text inputs. 562 // 563 // Flaky on Win7, WinXP, and Win Aura. http://crbug.com/270314. 564 // TODO(groby): Enable this test on mac once AutofillDialogCocoa handles 565 // comboboxes for GetTextContentsForInput. http://crbug.com/270205 566 #if defined(OS_MACOSX) || defined(OS_WIN) 567 #define MAYBE_FillComboboxFromAutofill DISABLED_FillComboboxFromAutofill 568 #else 569 #define MAYBE_FillComboboxFromAutofill FillComboboxFromAutofill 570 #endif 571 IN_PROC_BROWSER_TEST_F(AutofillDialogControllerTest, 572 MAYBE_FillComboboxFromAutofill) { 573 InitializeControllerOfType(DIALOG_TYPE_REQUEST_AUTOCOMPLETE); 574 575 CreditCard card1; 576 test::SetCreditCardInfo(&card1, "JJ Smith", "4111111111111111", "12", "2018"); 577 controller()->GetTestingManager()->AddTestingCreditCard(&card1); 578 CreditCard card2; 579 test::SetCreditCardInfo(&card2, "B Bird", "3111111111111111", "11", "2017"); 580 controller()->GetTestingManager()->AddTestingCreditCard(&card2); 581 AutofillProfile full_profile(test::GetFullProfile()); 582 controller()->GetTestingManager()->AddTestingProfile(&full_profile); 583 584 const DetailInputs& inputs = 585 controller()->RequestedFieldsForSection(SECTION_CC); 586 const DetailInput& triggering_input = inputs[0]; 587 string16 value = card1.GetRawInfo(triggering_input.type); 588 TestableAutofillDialogView* view = controller()->GetTestableView(); 589 view->SetTextContentsOfInput(triggering_input, 590 value.substr(0, value.size() / 2)); 591 view->ActivateInput(triggering_input); 592 593 ASSERT_EQ(&triggering_input, controller()->input_showing_popup()); 594 controller()->DidAcceptSuggestion(string16(), 0); 595 596 // All inputs should be filled. 597 AutofillCreditCardWrapper wrapper1(&card1); 598 for (size_t i = 0; i < inputs.size(); ++i) { 599 EXPECT_EQ(wrapper1.GetInfo(AutofillType(inputs[i].type)), 600 view->GetTextContentsOfInput(inputs[i])); 601 } 602 603 // Try again with different data. Only expiration date and the triggering 604 // input should be overwritten. 605 value = card2.GetRawInfo(triggering_input.type); 606 view->SetTextContentsOfInput(triggering_input, 607 value.substr(0, value.size() / 2)); 608 view->ActivateInput(triggering_input); 609 ASSERT_EQ(&triggering_input, controller()->input_showing_popup()); 610 controller()->DidAcceptSuggestion(string16(), 0); 611 612 AutofillCreditCardWrapper wrapper2(&card2); 613 for (size_t i = 0; i < inputs.size(); ++i) { 614 const DetailInput& input = inputs[i]; 615 if (&input == &triggering_input || 616 input.type == CREDIT_CARD_EXP_MONTH || 617 input.type == CREDIT_CARD_EXP_4_DIGIT_YEAR) { 618 EXPECT_EQ(wrapper2.GetInfo(AutofillType(input.type)), 619 view->GetTextContentsOfInput(input)); 620 } else if (input.type == CREDIT_CARD_VERIFICATION_CODE) { 621 EXPECT_TRUE(view->GetTextContentsOfInput(input).empty()); 622 } else { 623 EXPECT_EQ(wrapper1.GetInfo(AutofillType(input.type)), 624 view->GetTextContentsOfInput(input)); 625 } 626 } 627 628 // Now fill from a profile. It should not overwrite any CC info. 629 const DetailInputs& billing_inputs = 630 controller()->RequestedFieldsForSection(SECTION_BILLING); 631 const DetailInput& billing_triggering_input = billing_inputs[0]; 632 value = full_profile.GetRawInfo(triggering_input.type); 633 view->SetTextContentsOfInput(billing_triggering_input, 634 value.substr(0, value.size() / 2)); 635 view->ActivateInput(billing_triggering_input); 636 637 ASSERT_EQ(&billing_triggering_input, controller()->input_showing_popup()); 638 controller()->DidAcceptSuggestion(string16(), 0); 639 640 for (size_t i = 0; i < inputs.size(); ++i) { 641 const DetailInput& input = inputs[i]; 642 if (&input == &triggering_input || 643 input.type == CREDIT_CARD_EXP_MONTH || 644 input.type == CREDIT_CARD_EXP_4_DIGIT_YEAR) { 645 EXPECT_EQ(wrapper2.GetInfo(AutofillType(input.type)), 646 view->GetTextContentsOfInput(input)); 647 } else if (input.type == CREDIT_CARD_VERIFICATION_CODE) { 648 EXPECT_TRUE(view->GetTextContentsOfInput(input).empty()); 649 } else { 650 EXPECT_EQ(wrapper1.GetInfo(AutofillType(input.type)), 651 view->GetTextContentsOfInput(input)); 652 } 653 } 654 } 655 656 // Tests that credit card number is disabled while editing a Wallet instrument. 657 IN_PROC_BROWSER_TEST_F(AutofillDialogControllerTest, WalletCreditCardDisabled) { 658 InitializeControllerOfType(DIALOG_TYPE_REQUEST_AUTOCOMPLETE); 659 controller()->OnUserNameFetchSuccess("user (at) example.com"); 660 661 scoped_ptr<wallet::WalletItems> wallet_items = wallet::GetTestWalletItems(); 662 wallet_items->AddInstrument(wallet::GetTestMaskedInstrument()); 663 controller()->OnDidGetWalletItems(wallet_items.Pass()); 664 665 // Click "Edit" in the billing section (while using Wallet). 666 controller()->EditClickedForSection(SECTION_CC_BILLING); 667 668 const DetailInputs& edit_inputs = 669 controller()->RequestedFieldsForSection(SECTION_CC_BILLING); 670 size_t i; 671 for (i = 0; i < edit_inputs.size(); ++i) { 672 if (edit_inputs[i].type == CREDIT_CARD_NUMBER) { 673 EXPECT_FALSE(edit_inputs[i].editable); 674 break; 675 } 676 } 677 ASSERT_LT(i, edit_inputs.size()); 678 679 // Select "Add new billing info..." while using Wallet. 680 ui::MenuModel* model = controller()->MenuModelForSection(SECTION_CC_BILLING); 681 model->ActivatedAt(model->GetItemCount() - 2); 682 683 const DetailInputs& add_inputs = 684 controller()->RequestedFieldsForSection(SECTION_CC_BILLING); 685 for (i = 0; i < add_inputs.size(); ++i) { 686 if (add_inputs[i].type == CREDIT_CARD_NUMBER) { 687 EXPECT_TRUE(add_inputs[i].editable); 688 break; 689 } 690 } 691 ASSERT_LT(i, add_inputs.size()); 692 } 693 694 // Ensure that expired cards trigger invalid suggestions. 695 IN_PROC_BROWSER_TEST_F(AutofillDialogControllerTest, ExpiredCard) { 696 InitializeControllerOfType(DIALOG_TYPE_REQUEST_AUTOCOMPLETE); 697 698 CreditCard verified_card(test::GetCreditCard()); 699 verified_card.set_origin("Chrome settings"); 700 ASSERT_TRUE(verified_card.IsVerified()); 701 controller()->GetTestingManager()->AddTestingCreditCard(&verified_card); 702 703 CreditCard expired_card(test::GetCreditCard()); 704 expired_card.set_origin("Chrome settings"); 705 expired_card.SetRawInfo(CREDIT_CARD_EXP_4_DIGIT_YEAR, ASCIIToUTF16("2007")); 706 ASSERT_TRUE(expired_card.IsVerified()); 707 ASSERT_FALSE( 708 autofill::IsValidCreditCardExpirationDate( 709 expired_card.GetRawInfo(CREDIT_CARD_EXP_4_DIGIT_YEAR), 710 expired_card.GetRawInfo(CREDIT_CARD_EXP_MONTH), 711 base::Time::Now())); 712 controller()->GetTestingManager()->AddTestingCreditCard(&expired_card); 713 714 ui::MenuModel* model = controller()->MenuModelForSection(SECTION_CC); 715 ASSERT_EQ(4, model->GetItemCount()); 716 717 ASSERT_TRUE(model->IsItemCheckedAt(0)); 718 EXPECT_FALSE(controller()->IsEditingExistingData(SECTION_CC)); 719 720 model->ActivatedAt(1); 721 ASSERT_TRUE(model->IsItemCheckedAt(1)); 722 EXPECT_TRUE(controller()->IsEditingExistingData(SECTION_CC)); 723 } 724 725 // Notifications with long message text should not make the dialog bigger. 726 IN_PROC_BROWSER_TEST_F(AutofillDialogControllerTest, LongNotifications) { 727 InitializeControllerOfType(DIALOG_TYPE_REQUEST_AUTOCOMPLETE); 728 729 const gfx::Size no_notification_size = 730 controller()->GetTestableView()->GetSize(); 731 ASSERT_GT(no_notification_size.width(), 0); 732 733 std::vector<DialogNotification> notifications; 734 notifications.push_back( 735 DialogNotification(DialogNotification::DEVELOPER_WARNING, ASCIIToUTF16( 736 "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do " 737 "eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim " 738 "ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut " 739 "aliquip ex ea commodo consequat. Duis aute irure dolor in " 740 "reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla " 741 "pariatur. Excepteur sint occaecat cupidatat non proident, sunt in " 742 "culpa qui officia deserunt mollit anim id est laborum."))); 743 controller()->set_notifications(notifications); 744 controller()->view()->UpdateNotificationArea(); 745 746 EXPECT_EQ(no_notification_size.width(), 747 controller()->GetTestableView()->GetSize().width()); 748 } 749 750 IN_PROC_BROWSER_TEST_F(AutofillDialogControllerTest, AutocompleteEvent) { 751 AutofillDialogControllerImpl* controller = 752 SetUpHtmlAndInvoke("<input autocomplete='cc-name'>"); 753 754 AddCreditcardToProfile(controller->profile(), test::GetVerifiedCreditCard()); 755 AddAutofillProfileToProfile(controller->profile(), 756 test::GetVerifiedProfile()); 757 758 TestableAutofillDialogView* view = controller->GetTestableView(); 759 view->SetTextContentsOfSuggestionInput(SECTION_CC, ASCIIToUTF16("123")); 760 view->SubmitForTesting(); 761 ExpectDomMessage("success"); 762 } 763 764 IN_PROC_BROWSER_TEST_F(AutofillDialogControllerTest, 765 AutocompleteErrorEventReasonInvalid) { 766 AutofillDialogControllerImpl* controller = 767 SetUpHtmlAndInvoke("<input autocomplete='cc-name' pattern='.*zebra.*'>"); 768 769 const CreditCard& credit_card = test::GetVerifiedCreditCard(); 770 ASSERT_TRUE( 771 credit_card.GetRawInfo(CREDIT_CARD_NAME).find(ASCIIToUTF16("zebra")) == 772 base::string16::npos); 773 AddCreditcardToProfile(controller->profile(), credit_card); 774 AddAutofillProfileToProfile(controller->profile(), 775 test::GetVerifiedProfile()); 776 777 TestableAutofillDialogView* view = controller->GetTestableView(); 778 view->SetTextContentsOfSuggestionInput(SECTION_CC, ASCIIToUTF16("123")); 779 view->SubmitForTesting(); 780 ExpectDomMessage("error: invalid"); 781 } 782 783 IN_PROC_BROWSER_TEST_F(AutofillDialogControllerTest, 784 AutocompleteErrorEventReasonCancel) { 785 SetUpHtmlAndInvoke("<input autocomplete='cc-name'>")->GetTestableView()-> 786 CancelForTesting(); 787 ExpectDomMessage("error: cancel"); 788 } 789 790 IN_PROC_BROWSER_TEST_F(AutofillDialogControllerTest, NoCvcSegfault) { 791 InitializeControllerOfType(DIALOG_TYPE_REQUEST_AUTOCOMPLETE); 792 controller()->set_use_validation(true); 793 794 CreditCard credit_card(test::GetVerifiedCreditCard()); 795 controller()->GetTestingManager()->AddTestingCreditCard(&credit_card); 796 EXPECT_FALSE(controller()->IsEditingExistingData(SECTION_CC)); 797 798 ASSERT_NO_FATAL_FAILURE( 799 controller()->GetTestableView()->SubmitForTesting()); 800 } 801 802 // Flaky on Win7, WinXP, and Win Aura. http://crbug.com/270314. 803 #if defined(OS_WIN) 804 #define MAYBE_PreservedSections DISABLED_PreservedSections 805 #else 806 #define MAYBE_PreservedSections PreservedSections 807 #endif 808 IN_PROC_BROWSER_TEST_F(AutofillDialogControllerTest, MAYBE_PreservedSections) { 809 InitializeControllerOfType(DIALOG_TYPE_REQUEST_AUTOCOMPLETE); 810 controller()->set_use_validation(true); 811 812 // Set up some Autofill state. 813 CreditCard credit_card(test::GetVerifiedCreditCard()); 814 controller()->GetTestingManager()->AddTestingCreditCard(&credit_card); 815 816 AutofillProfile profile(test::GetVerifiedProfile()); 817 controller()->GetTestingManager()->AddTestingProfile(&profile); 818 819 EXPECT_TRUE(controller()->SectionIsActive(SECTION_CC)); 820 EXPECT_TRUE(controller()->SectionIsActive(SECTION_BILLING)); 821 EXPECT_FALSE(controller()->SectionIsActive(SECTION_CC_BILLING)); 822 EXPECT_TRUE(controller()->SectionIsActive(SECTION_SHIPPING)); 823 824 EXPECT_FALSE(controller()->IsManuallyEditingSection(SECTION_CC)); 825 EXPECT_FALSE(controller()->IsManuallyEditingSection(SECTION_BILLING)); 826 EXPECT_FALSE(controller()->IsManuallyEditingSection(SECTION_SHIPPING)); 827 828 // Set up some Wallet state. 829 controller()->OnUserNameFetchSuccess("user (at) example.com"); 830 controller()->OnDidGetWalletItems(wallet::GetTestWalletItems()); 831 832 ui::MenuModel* account_chooser = controller()->MenuModelForAccountChooser(); 833 ASSERT_TRUE(account_chooser->IsItemCheckedAt(0)); 834 835 // Check that the view's in the state we expect before starting to simulate 836 // user input. 837 EXPECT_FALSE(controller()->SectionIsActive(SECTION_CC)); 838 EXPECT_FALSE(controller()->SectionIsActive(SECTION_BILLING)); 839 EXPECT_TRUE(controller()->SectionIsActive(SECTION_CC_BILLING)); 840 EXPECT_TRUE(controller()->SectionIsActive(SECTION_SHIPPING)); 841 842 EXPECT_TRUE(controller()->IsManuallyEditingSection(SECTION_CC_BILLING)); 843 844 // Create some valid inputted billing data. 845 const DetailInput& cc_number = 846 controller()->RequestedFieldsForSection(SECTION_CC_BILLING)[0]; 847 EXPECT_EQ(CREDIT_CARD_NUMBER, cc_number.type); 848 TestableAutofillDialogView* view = controller()->GetTestableView(); 849 view->SetTextContentsOfInput(cc_number, ASCIIToUTF16("4111111111111111")); 850 851 // Select "Add new shipping info..." from suggestions menu. 852 ui::MenuModel* shipping_model = 853 controller()->MenuModelForSection(SECTION_SHIPPING); 854 shipping_model->ActivatedAt(shipping_model->GetItemCount() - 2); 855 856 EXPECT_TRUE(controller()->IsManuallyEditingSection(SECTION_SHIPPING)); 857 858 // Create some invalid, manually inputted shipping data. 859 const DetailInput& shipping_zip = 860 controller()->RequestedFieldsForSection(SECTION_SHIPPING)[5]; 861 ASSERT_EQ(ADDRESS_HOME_ZIP, shipping_zip.type); 862 view->SetTextContentsOfInput(shipping_zip, ASCIIToUTF16("shipping zip")); 863 864 // Switch to using Autofill. 865 account_chooser->ActivatedAt(1); 866 867 // Check that appropriate sections are preserved and in manually editing mode 868 // (or disabled, in the case of the combined cc + billing section). 869 EXPECT_TRUE(controller()->SectionIsActive(SECTION_CC)); 870 EXPECT_TRUE(controller()->SectionIsActive(SECTION_BILLING)); 871 EXPECT_FALSE(controller()->SectionIsActive(SECTION_CC_BILLING)); 872 EXPECT_TRUE(controller()->SectionIsActive(SECTION_SHIPPING)); 873 874 EXPECT_TRUE(controller()->IsManuallyEditingSection(SECTION_CC)); 875 EXPECT_FALSE(controller()->IsManuallyEditingSection(SECTION_BILLING)); 876 EXPECT_FALSE(controller()->IsManuallyEditingSection(SECTION_SHIPPING)); 877 878 const DetailInput& new_cc_number = 879 controller()->RequestedFieldsForSection(SECTION_CC).front(); 880 EXPECT_EQ(cc_number.type, new_cc_number.type); 881 EXPECT_EQ(ASCIIToUTF16("4111111111111111"), 882 view->GetTextContentsOfInput(new_cc_number)); 883 884 EXPECT_NE(ASCIIToUTF16("shipping name"), 885 view->GetTextContentsOfInput(shipping_zip)); 886 } 887 #endif // defined(TOOLKIT_VIEWS) || defined(OS_MACOSX) 888 889 } // namespace autofill 890