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