autofill_dialog_controller_browsertest.cc revision ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16
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*, const std::string&) {}
46
47class 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
96class 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
209class 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#if defined(OS_MACOSX)
220    // OSX support for requestAutocomplete is still hidden behind a switch.
221    // Pending resolution of http://crbug.com/157274
222    CommandLine::ForCurrentProcess()->AppendSwitch(
223        switches::kEnableInteractiveAutocomplete);
224#endif
225  }
226
227  void InitializeControllerOfType(DialogType dialog_type) {
228    FormData form;
229    form.name = ASCIIToUTF16("TestForm");
230    form.method = ASCIIToUTF16("POST");
231    form.origin = GURL("http://example.com/form.html");
232    form.action = GURL("http://example.com/submit.html");
233    form.user_submitted = true;
234
235    FormFieldData field;
236    field.autocomplete_attribute = "shipping tel";
237    form.fields.push_back(field);
238
239    message_loop_runner_ = new content::MessageLoopRunner;
240    controller_ = new TestAutofillDialogController(
241        GetActiveWebContents(),
242        form,
243        metric_logger_,
244        message_loop_runner_,
245        dialog_type);
246    controller_->Show();
247  }
248
249  content::WebContents* GetActiveWebContents() {
250    return browser()->tab_strip_model()->GetActiveWebContents();
251  }
252
253  const MockAutofillMetrics& metric_logger() { return metric_logger_; }
254  TestAutofillDialogController* controller() { return controller_; }
255
256  void RunMessageLoop() {
257    message_loop_runner_->Run();
258  }
259
260  // Loads an HTML page in |GetActiveWebContents()| with markup as follows:
261  // <form>|form_inner_html|</form>. After loading, emulates a click event on
262  // the page as requestAutocomplete() must be in response to a user gesture.
263  // Returns the |AutofillDialogControllerImpl| created by this invocation.
264  AutofillDialogControllerImpl* SetUpHtmlAndInvoke(
265      const std::string& form_inner_html) {
266    content::WebContents* contents = GetActiveWebContents();
267    TabAutofillManagerDelegate* delegate =
268        TabAutofillManagerDelegate::FromWebContents(contents);
269    DCHECK(!delegate->GetDialogControllerForTesting());
270
271    ui_test_utils::NavigateToURL(
272        browser(), GURL(std::string("data:text/html,") +
273        "<!doctype html>"
274        "<html>"
275          "<body>"
276            "<form>" + form_inner_html + "</form>"
277            "<script>"
278              "function send(msg) {"
279                "domAutomationController.setAutomationId(0);"
280                "domAutomationController.send(msg);"
281              "}"
282              "document.forms[0].onautocompleteerror = function(e) {"
283                "send('error: ' + e.reason);"
284              "};"
285              "document.forms[0].onautocomplete = function() {"
286                "send('success');"
287              "};"
288              "window.onclick = function() {"
289                "document.forms[0].requestAutocomplete();"
290                "send('clicked');"
291              "};"
292            "</script>"
293          "</body>"
294        "</html>"));
295    content::WaitForLoadStop(contents);
296
297    dom_message_queue_.reset(new content::DOMMessageQueue);
298
299    // Triggers the onclick handler which invokes requestAutocomplete().
300    content::SimulateMouseClick(contents, 0, WebKit::WebMouseEvent::ButtonLeft);
301    ExpectDomMessage("clicked");
302
303    AutofillDialogControllerImpl* controller =
304        static_cast<AutofillDialogControllerImpl*>(
305            delegate->GetDialogControllerForTesting());
306    DCHECK(controller);
307    return controller;
308  }
309
310  // Wait for a message from the DOM automation controller (from JS in the
311  // page). Requires |SetUpHtmlAndInvoke()| be called first.
312  void ExpectDomMessage(const std::string& expected) {
313    std::string message;
314    ASSERT_TRUE(dom_message_queue_->WaitForMessage(&message));
315    dom_message_queue_->ClearQueue();
316    EXPECT_EQ("\"" + expected + "\"", message);
317  }
318
319  void AddCreditcardToProfile(Profile* profile, const CreditCard& card) {
320    PersonalDataManagerFactory::GetForProfile(profile)->AddCreditCard(card);
321    WaitForWebDB();
322  }
323
324  void AddAutofillProfileToProfile(Profile* profile,
325                                   const AutofillProfile& autofill_profile) {
326    PersonalDataManagerFactory::GetForProfile(profile)->AddProfile(
327        autofill_profile);
328    WaitForWebDB();
329  }
330
331 private:
332  void WaitForWebDB() {
333    content::RunAllPendingInMessageLoop(content::BrowserThread::DB);
334  }
335
336  MockAutofillMetrics metric_logger_;
337  TestAutofillDialogController* controller_;  // Weak reference.
338  scoped_refptr<content::MessageLoopRunner> message_loop_runner_;
339  scoped_ptr<content::DOMMessageQueue> dom_message_queue_;
340  DISALLOW_COPY_AND_ASSIGN(AutofillDialogControllerTest);
341};
342
343#if defined(TOOLKIT_VIEWS) || defined(OS_MACOSX)
344// Submit the form data.
345IN_PROC_BROWSER_TEST_F(AutofillDialogControllerTest, Submit) {
346  InitializeControllerOfType(DIALOG_TYPE_REQUEST_AUTOCOMPLETE);
347  controller()->GetTestableView()->SubmitForTesting();
348
349  RunMessageLoop();
350
351  EXPECT_EQ(AutofillMetrics::DIALOG_ACCEPTED,
352            metric_logger().dialog_dismissal_action());
353  EXPECT_EQ(DIALOG_TYPE_REQUEST_AUTOCOMPLETE, metric_logger().dialog_type());
354}
355
356// Cancel out of the dialog.
357IN_PROC_BROWSER_TEST_F(AutofillDialogControllerTest, Cancel) {
358  InitializeControllerOfType(DIALOG_TYPE_REQUEST_AUTOCOMPLETE);
359  controller()->GetTestableView()->CancelForTesting();
360
361  RunMessageLoop();
362
363  EXPECT_EQ(AutofillMetrics::DIALOG_CANCELED,
364            metric_logger().dialog_dismissal_action());
365  EXPECT_EQ(DIALOG_TYPE_REQUEST_AUTOCOMPLETE, metric_logger().dialog_type());
366}
367
368// Take some other action that dismisses the dialog.
369IN_PROC_BROWSER_TEST_F(AutofillDialogControllerTest, Hide) {
370  InitializeControllerOfType(DIALOG_TYPE_REQUEST_AUTOCOMPLETE);
371  controller()->Hide();
372
373  RunMessageLoop();
374
375  EXPECT_EQ(AutofillMetrics::DIALOG_CANCELED,
376            metric_logger().dialog_dismissal_action());
377  EXPECT_EQ(DIALOG_TYPE_REQUEST_AUTOCOMPLETE, metric_logger().dialog_type());
378}
379
380// Ensure that Hide() will only destroy the controller object after the
381// message loop has run. Otherwise, there may be read-after-free issues
382// during some tests.
383IN_PROC_BROWSER_TEST_F(AutofillDialogControllerTest, DeferredDestruction) {
384  InitializeControllerOfType(DIALOG_TYPE_REQUEST_AUTOCOMPLETE);
385
386  base::WeakPtr<TestAutofillDialogController> weak_ptr =
387      controller()->AsWeakPtr();
388  EXPECT_TRUE(weak_ptr.get());
389
390  controller()->Hide();
391  EXPECT_TRUE(weak_ptr.get());
392
393  RunMessageLoop();
394  EXPECT_FALSE(weak_ptr.get());
395}
396
397// Ensure that the expected metric is logged when the dialog is closed during
398// signin.
399IN_PROC_BROWSER_TEST_F(AutofillDialogControllerTest, CloseDuringSignin) {
400  InitializeControllerOfType(DIALOG_TYPE_REQUEST_AUTOCOMPLETE);
401  controller()->SignInLinkClicked();
402
403  EXPECT_CALL(metric_logger(),
404              LogDialogDismissalState(
405                  DIALOG_TYPE_REQUEST_AUTOCOMPLETE,
406                  AutofillMetrics::DIALOG_CANCELED_DURING_SIGNIN));
407  controller()->GetTestableView()->CancelForTesting();
408
409  RunMessageLoop();
410
411  EXPECT_EQ(AutofillMetrics::DIALOG_CANCELED,
412            metric_logger().dialog_dismissal_action());
413  EXPECT_EQ(DIALOG_TYPE_REQUEST_AUTOCOMPLETE, metric_logger().dialog_type());
414}
415
416// Test Autocheckout success metrics.
417IN_PROC_BROWSER_TEST_F(AutofillDialogControllerTest, AutocheckoutSuccess) {
418  InitializeControllerOfType(DIALOG_TYPE_AUTOCHECKOUT);
419  controller()->GetTestableView()->SubmitForTesting();
420
421  EXPECT_EQ(AutofillMetrics::DIALOG_ACCEPTED,
422            metric_logger().dialog_dismissal_action());
423  EXPECT_EQ(DIALOG_TYPE_AUTOCHECKOUT, metric_logger().dialog_type());
424
425  controller()->OnAutocheckoutSuccess();
426  controller()->GetTestableView()->CancelForTesting();
427  RunMessageLoop();
428
429  EXPECT_EQ(AutofillMetrics::AUTOCHECKOUT_SUCCEEDED,
430            metric_logger().autocheckout_status());
431
432  // Ensure closing the dialog doesn't fire any new metrics.
433  EXPECT_EQ(AutofillMetrics::DIALOG_ACCEPTED,
434            metric_logger().dialog_dismissal_action());
435  EXPECT_EQ(DIALOG_TYPE_AUTOCHECKOUT, metric_logger().dialog_type());
436}
437
438// Test Autocheckout failure metric.
439IN_PROC_BROWSER_TEST_F(AutofillDialogControllerTest, AutocheckoutError) {
440  InitializeControllerOfType(DIALOG_TYPE_AUTOCHECKOUT);
441  controller()->GetTestableView()->SubmitForTesting();
442
443  EXPECT_EQ(AutofillMetrics::DIALOG_ACCEPTED,
444            metric_logger().dialog_dismissal_action());
445  EXPECT_EQ(DIALOG_TYPE_AUTOCHECKOUT, metric_logger().dialog_type());
446
447  controller()->OnAutocheckoutError();
448  controller()->GetTestableView()->CancelForTesting();
449  RunMessageLoop();
450
451  EXPECT_EQ(AutofillMetrics::AUTOCHECKOUT_FAILED,
452            metric_logger().autocheckout_status());
453
454  // Ensure closing the dialog doesn't fire any new metrics.
455  EXPECT_EQ(AutofillMetrics::DIALOG_ACCEPTED,
456            metric_logger().dialog_dismissal_action());
457  EXPECT_EQ(DIALOG_TYPE_AUTOCHECKOUT, metric_logger().dialog_type());
458}
459
460IN_PROC_BROWSER_TEST_F(AutofillDialogControllerTest, AutocheckoutCancelled) {
461  InitializeControllerOfType(DIALOG_TYPE_AUTOCHECKOUT);
462  controller()->GetTestableView()->SubmitForTesting();
463
464  EXPECT_EQ(AutofillMetrics::DIALOG_ACCEPTED,
465            metric_logger().dialog_dismissal_action());
466  EXPECT_EQ(DIALOG_TYPE_AUTOCHECKOUT, metric_logger().dialog_type());
467
468  controller()->GetTestableView()->CancelForTesting();
469  RunMessageLoop();
470
471  EXPECT_EQ(AutofillMetrics::AUTOCHECKOUT_CANCELLED,
472            metric_logger().autocheckout_status());
473
474  // Ensure closing the dialog doesn't fire any new metrics.
475  EXPECT_EQ(AutofillMetrics::DIALOG_ACCEPTED,
476            metric_logger().dialog_dismissal_action());
477  EXPECT_EQ(DIALOG_TYPE_AUTOCHECKOUT, metric_logger().dialog_type());
478}
479
480IN_PROC_BROWSER_TEST_F(AutofillDialogControllerTest, FillInputFromAutofill) {
481  InitializeControllerOfType(DIALOG_TYPE_REQUEST_AUTOCOMPLETE);
482
483  AutofillProfile full_profile(test::GetFullProfile());
484  controller()->GetTestingManager()->AddTestingProfile(&full_profile);
485
486  const DetailInputs& inputs =
487      controller()->RequestedFieldsForSection(SECTION_SHIPPING);
488  const DetailInput& triggering_input = inputs[0];
489  string16 value = full_profile.GetRawInfo(triggering_input.type);
490  TestableAutofillDialogView* view = controller()->GetTestableView();
491  view->SetTextContentsOfInput(triggering_input,
492                               value.substr(0, value.size() / 2));
493  view->ActivateInput(triggering_input);
494
495  ASSERT_EQ(&triggering_input, controller()->input_showing_popup());
496  controller()->DidAcceptSuggestion(string16(), 0);
497
498  // All inputs should be filled.
499  AutofillProfileWrapper wrapper(&full_profile, 0);
500  for (size_t i = 0; i < inputs.size(); ++i) {
501    EXPECT_EQ(wrapper.GetInfo(AutofillType(inputs[i].type)),
502              view->GetTextContentsOfInput(inputs[i]));
503  }
504
505  // Now simulate some user edits and try again.
506  std::vector<string16> expectations;
507  for (size_t i = 0; i < inputs.size(); ++i) {
508    string16 users_input = i % 2 == 0 ? string16() : ASCIIToUTF16("dummy");
509    view->SetTextContentsOfInput(inputs[i], users_input);
510    // Empty inputs should be filled, others should be left alone.
511    string16 expectation =
512        &inputs[i] == &triggering_input || users_input.empty() ?
513        wrapper.GetInfo(AutofillType(inputs[i].type)) :
514        users_input;
515    expectations.push_back(expectation);
516  }
517
518  view->SetTextContentsOfInput(triggering_input,
519                               value.substr(0, value.size() / 2));
520  view->ActivateInput(triggering_input);
521  ASSERT_EQ(&triggering_input, controller()->input_showing_popup());
522  controller()->DidAcceptSuggestion(string16(), 0);
523
524  for (size_t i = 0; i < inputs.size(); ++i) {
525    EXPECT_EQ(expectations[i], view->GetTextContentsOfInput(inputs[i]));
526  }
527}
528
529// Test that Autocheckout steps are shown after submitting the
530// dialog for controller with type DIALOG_TYPE_AUTOCHECKOUT.
531IN_PROC_BROWSER_TEST_F(AutofillDialogControllerTest, AutocheckoutShowsSteps) {
532  InitializeControllerOfType(DIALOG_TYPE_AUTOCHECKOUT);
533  controller()->AddAutocheckoutStep(AUTOCHECKOUT_STEP_PROXY_CARD);
534
535  EXPECT_TRUE(controller()->ShouldShowDetailArea());
536  EXPECT_TRUE(controller()->CurrentAutocheckoutSteps().empty());
537  EXPECT_FALSE(controller()->ShouldShowProgressBar());
538
539  controller()->GetTestableView()->SubmitForTesting();
540  EXPECT_FALSE(controller()->ShouldShowDetailArea());
541  EXPECT_FALSE(controller()->CurrentAutocheckoutSteps().empty());
542  EXPECT_TRUE(controller()->ShouldShowProgressBar());
543  controller()->GetTestableView()->CancelForTesting();
544  RunMessageLoop();
545}
546
547// Test that Autocheckout steps are not showing after submitting the
548// dialog for controller with type DIALOG_TYPE_REQUEST_AUTOCOMPLETE.
549IN_PROC_BROWSER_TEST_F(AutofillDialogControllerTest,
550                       RequestAutocompleteDoesntShowSteps) {
551  InitializeControllerOfType(DIALOG_TYPE_REQUEST_AUTOCOMPLETE);
552  controller()->AddAutocheckoutStep(AUTOCHECKOUT_STEP_PROXY_CARD);
553
554  EXPECT_TRUE(controller()->ShouldShowDetailArea());
555  EXPECT_TRUE(controller()->CurrentAutocheckoutSteps().empty());
556  EXPECT_FALSE(controller()->ShouldShowProgressBar());
557
558  controller()->GetTestableView()->SubmitForTesting();
559  EXPECT_TRUE(controller()->ShouldShowDetailArea());
560  EXPECT_TRUE(controller()->CurrentAutocheckoutSteps().empty());
561  EXPECT_FALSE(controller()->ShouldShowProgressBar());
562}
563
564// Tests that changing the value of a CC expiration date combobox works as
565// expected when Autofill is used to fill text inputs.
566//
567// Flaky on Win7, WinXP, and Win Aura.  http://crbug.com/270314.
568// TODO(groby): Enable this test on mac once AutofillDialogCocoa handles
569// comboboxes for GetTextContentsForInput. http://crbug.com/270205
570#if defined(OS_MACOSX) || defined(OS_WIN)
571#define MAYBE_FillComboboxFromAutofill DISABLED_FillComboboxFromAutofill
572#else
573#define MAYBE_FillComboboxFromAutofill FillComboboxFromAutofill
574#endif
575IN_PROC_BROWSER_TEST_F(AutofillDialogControllerTest,
576                       MAYBE_FillComboboxFromAutofill) {
577  InitializeControllerOfType(DIALOG_TYPE_REQUEST_AUTOCOMPLETE);
578
579  CreditCard card1;
580  test::SetCreditCardInfo(&card1, "JJ Smith", "4111111111111111", "12", "2018");
581  controller()->GetTestingManager()->AddTestingCreditCard(&card1);
582  CreditCard card2;
583  test::SetCreditCardInfo(&card2, "B Bird", "3111111111111111", "11", "2017");
584  controller()->GetTestingManager()->AddTestingCreditCard(&card2);
585  AutofillProfile full_profile(test::GetFullProfile());
586  controller()->GetTestingManager()->AddTestingProfile(&full_profile);
587
588  const DetailInputs& inputs =
589      controller()->RequestedFieldsForSection(SECTION_CC);
590  const DetailInput& triggering_input = inputs[0];
591  string16 value = card1.GetRawInfo(triggering_input.type);
592  TestableAutofillDialogView* view = controller()->GetTestableView();
593  view->SetTextContentsOfInput(triggering_input,
594                               value.substr(0, value.size() / 2));
595  view->ActivateInput(triggering_input);
596
597  ASSERT_EQ(&triggering_input, controller()->input_showing_popup());
598  controller()->DidAcceptSuggestion(string16(), 0);
599
600  // All inputs should be filled.
601  AutofillCreditCardWrapper wrapper1(&card1);
602  for (size_t i = 0; i < inputs.size(); ++i) {
603    EXPECT_EQ(wrapper1.GetInfo(AutofillType(inputs[i].type)),
604              view->GetTextContentsOfInput(inputs[i]));
605  }
606
607  // Try again with different data. Only expiration date and the triggering
608  // input should be overwritten.
609  value = card2.GetRawInfo(triggering_input.type);
610  view->SetTextContentsOfInput(triggering_input,
611                               value.substr(0, value.size() / 2));
612  view->ActivateInput(triggering_input);
613  ASSERT_EQ(&triggering_input, controller()->input_showing_popup());
614  controller()->DidAcceptSuggestion(string16(), 0);
615
616  AutofillCreditCardWrapper wrapper2(&card2);
617  for (size_t i = 0; i < inputs.size(); ++i) {
618    const DetailInput& input = inputs[i];
619    if (&input == &triggering_input ||
620        input.type == CREDIT_CARD_EXP_MONTH ||
621        input.type == CREDIT_CARD_EXP_4_DIGIT_YEAR) {
622      EXPECT_EQ(wrapper2.GetInfo(AutofillType(input.type)),
623                view->GetTextContentsOfInput(input));
624    } else if (input.type == CREDIT_CARD_VERIFICATION_CODE) {
625      EXPECT_TRUE(view->GetTextContentsOfInput(input).empty());
626    } else {
627      EXPECT_EQ(wrapper1.GetInfo(AutofillType(input.type)),
628                view->GetTextContentsOfInput(input));
629    }
630  }
631
632  // Now fill from a profile. It should not overwrite any CC info.
633  const DetailInputs& billing_inputs =
634      controller()->RequestedFieldsForSection(SECTION_BILLING);
635  const DetailInput& billing_triggering_input = billing_inputs[0];
636  value = full_profile.GetRawInfo(triggering_input.type);
637  view->SetTextContentsOfInput(billing_triggering_input,
638                               value.substr(0, value.size() / 2));
639  view->ActivateInput(billing_triggering_input);
640
641  ASSERT_EQ(&billing_triggering_input, controller()->input_showing_popup());
642  controller()->DidAcceptSuggestion(string16(), 0);
643
644  for (size_t i = 0; i < inputs.size(); ++i) {
645    const DetailInput& input = inputs[i];
646    if (&input == &triggering_input ||
647        input.type == CREDIT_CARD_EXP_MONTH ||
648        input.type == CREDIT_CARD_EXP_4_DIGIT_YEAR) {
649      EXPECT_EQ(wrapper2.GetInfo(AutofillType(input.type)),
650                view->GetTextContentsOfInput(input));
651    } else if (input.type == CREDIT_CARD_VERIFICATION_CODE) {
652      EXPECT_TRUE(view->GetTextContentsOfInput(input).empty());
653    } else {
654      EXPECT_EQ(wrapper1.GetInfo(AutofillType(input.type)),
655                view->GetTextContentsOfInput(input));
656    }
657  }
658}
659
660// Tests that credit card number is disabled while editing a Wallet instrument.
661IN_PROC_BROWSER_TEST_F(AutofillDialogControllerTest, WalletCreditCardDisabled) {
662  InitializeControllerOfType(DIALOG_TYPE_REQUEST_AUTOCOMPLETE);
663  controller()->OnUserNameFetchSuccess("user@example.com");
664
665  scoped_ptr<wallet::WalletItems> wallet_items = wallet::GetTestWalletItems();
666  wallet_items->AddInstrument(wallet::GetTestMaskedInstrument());
667  controller()->OnDidGetWalletItems(wallet_items.Pass());
668
669  // Click "Edit" in the billing section (while using Wallet).
670  controller()->EditClickedForSection(SECTION_CC_BILLING);
671
672  const DetailInputs& edit_inputs =
673      controller()->RequestedFieldsForSection(SECTION_CC_BILLING);
674  size_t i;
675  for (i = 0; i < edit_inputs.size(); ++i) {
676    if (edit_inputs[i].type == CREDIT_CARD_NUMBER) {
677      EXPECT_FALSE(edit_inputs[i].editable);
678      break;
679    }
680  }
681  ASSERT_LT(i, edit_inputs.size());
682
683  // Select "Add new billing info..." while using Wallet.
684  ui::MenuModel* model = controller()->MenuModelForSection(SECTION_CC_BILLING);
685  model->ActivatedAt(model->GetItemCount() - 2);
686
687  const DetailInputs& add_inputs =
688      controller()->RequestedFieldsForSection(SECTION_CC_BILLING);
689  for (i = 0; i < add_inputs.size(); ++i) {
690    if (add_inputs[i].type == CREDIT_CARD_NUMBER) {
691      EXPECT_TRUE(add_inputs[i].editable);
692      break;
693    }
694  }
695  ASSERT_LT(i, add_inputs.size());
696}
697
698// Ensure that expired cards trigger invalid suggestions.
699IN_PROC_BROWSER_TEST_F(AutofillDialogControllerTest, ExpiredCard) {
700  InitializeControllerOfType(DIALOG_TYPE_REQUEST_AUTOCOMPLETE);
701
702  CreditCard verified_card(test::GetCreditCard());
703  verified_card.set_origin("Chrome settings");
704  ASSERT_TRUE(verified_card.IsVerified());
705  controller()->GetTestingManager()->AddTestingCreditCard(&verified_card);
706
707  CreditCard expired_card(test::GetCreditCard());
708  expired_card.set_origin("Chrome settings");
709  expired_card.SetRawInfo(CREDIT_CARD_EXP_4_DIGIT_YEAR, ASCIIToUTF16("2007"));
710  ASSERT_TRUE(expired_card.IsVerified());
711  ASSERT_FALSE(
712      autofill::IsValidCreditCardExpirationDate(
713          expired_card.GetRawInfo(CREDIT_CARD_EXP_4_DIGIT_YEAR),
714          expired_card.GetRawInfo(CREDIT_CARD_EXP_MONTH),
715          base::Time::Now()));
716  controller()->GetTestingManager()->AddTestingCreditCard(&expired_card);
717
718  ui::MenuModel* model = controller()->MenuModelForSection(SECTION_CC);
719  ASSERT_EQ(4, model->GetItemCount());
720
721  ASSERT_TRUE(model->IsItemCheckedAt(0));
722  EXPECT_FALSE(controller()->IsEditingExistingData(SECTION_CC));
723
724  model->ActivatedAt(1);
725  ASSERT_TRUE(model->IsItemCheckedAt(1));
726  EXPECT_TRUE(controller()->IsEditingExistingData(SECTION_CC));
727}
728
729// Notifications with long message text should not make the dialog bigger.
730IN_PROC_BROWSER_TEST_F(AutofillDialogControllerTest, LongNotifications) {
731  InitializeControllerOfType(DIALOG_TYPE_REQUEST_AUTOCOMPLETE);
732
733  const gfx::Size no_notification_size =
734      controller()->GetTestableView()->GetSize();
735  ASSERT_GT(no_notification_size.width(), 0);
736
737  std::vector<DialogNotification> notifications;
738  notifications.push_back(
739      DialogNotification(DialogNotification::DEVELOPER_WARNING, ASCIIToUTF16(
740          "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do "
741          "eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim "
742          "ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut "
743          "aliquip ex ea commodo consequat. Duis aute irure dolor in "
744          "reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla "
745          "pariatur. Excepteur sint occaecat cupidatat non proident, sunt in "
746          "culpa qui officia deserunt mollit anim id est laborum.")));
747  controller()->set_notifications(notifications);
748  controller()->view()->UpdateNotificationArea();
749
750  EXPECT_EQ(no_notification_size.width(),
751            controller()->GetTestableView()->GetSize().width());
752}
753
754IN_PROC_BROWSER_TEST_F(AutofillDialogControllerTest, AutocompleteEvent) {
755  AutofillDialogControllerImpl* controller =
756      SetUpHtmlAndInvoke("<input autocomplete='cc-name'>");
757
758  AddCreditcardToProfile(controller->profile(), test::GetVerifiedCreditCard());
759  AddAutofillProfileToProfile(controller->profile(),
760                              test::GetVerifiedProfile());
761
762  TestableAutofillDialogView* view = controller->GetTestableView();
763  view->SetTextContentsOfSuggestionInput(SECTION_CC, ASCIIToUTF16("123"));
764  view->SubmitForTesting();
765  ExpectDomMessage("success");
766}
767
768IN_PROC_BROWSER_TEST_F(AutofillDialogControllerTest,
769                       AutocompleteErrorEventReasonInvalid) {
770  AutofillDialogControllerImpl* controller =
771      SetUpHtmlAndInvoke("<input autocomplete='cc-name' pattern='.*zebra.*'>");
772
773  const CreditCard& credit_card = test::GetVerifiedCreditCard();
774  ASSERT_TRUE(
775    credit_card.GetRawInfo(CREDIT_CARD_NAME).find(ASCIIToUTF16("zebra")) ==
776        base::string16::npos);
777  AddCreditcardToProfile(controller->profile(), credit_card);
778  AddAutofillProfileToProfile(controller->profile(),
779                              test::GetVerifiedProfile());
780
781  TestableAutofillDialogView* view = controller->GetTestableView();
782  view->SetTextContentsOfSuggestionInput(SECTION_CC, ASCIIToUTF16("123"));
783  view->SubmitForTesting();
784  ExpectDomMessage("error: invalid");
785}
786
787IN_PROC_BROWSER_TEST_F(AutofillDialogControllerTest,
788                       AutocompleteErrorEventReasonCancel) {
789  SetUpHtmlAndInvoke("<input autocomplete='cc-name'>")->GetTestableView()->
790      CancelForTesting();
791  ExpectDomMessage("error: cancel");
792}
793
794IN_PROC_BROWSER_TEST_F(AutofillDialogControllerTest, NoCvcSegfault) {
795  InitializeControllerOfType(DIALOG_TYPE_REQUEST_AUTOCOMPLETE);
796  controller()->set_use_validation(true);
797
798  CreditCard credit_card(test::GetVerifiedCreditCard());
799  controller()->GetTestingManager()->AddTestingCreditCard(&credit_card);
800  EXPECT_FALSE(controller()->IsEditingExistingData(SECTION_CC));
801
802  ASSERT_NO_FATAL_FAILURE(
803      controller()->GetTestableView()->SubmitForTesting());
804}
805
806// Flaky on Win7, WinXP, and Win Aura.  http://crbug.com/270314.
807#if defined(OS_WIN)
808#define MAYBE_PreservedSections DISABLED_PreservedSections
809#else
810#define MAYBE_PreservedSections PreservedSections
811#endif
812IN_PROC_BROWSER_TEST_F(AutofillDialogControllerTest, MAYBE_PreservedSections) {
813  InitializeControllerOfType(DIALOG_TYPE_REQUEST_AUTOCOMPLETE);
814  controller()->set_use_validation(true);
815
816  // Set up some Autofill state.
817  CreditCard credit_card(test::GetVerifiedCreditCard());
818  controller()->GetTestingManager()->AddTestingCreditCard(&credit_card);
819
820  AutofillProfile profile(test::GetVerifiedProfile());
821  controller()->GetTestingManager()->AddTestingProfile(&profile);
822
823  EXPECT_TRUE(controller()->SectionIsActive(SECTION_CC));
824  EXPECT_TRUE(controller()->SectionIsActive(SECTION_BILLING));
825  EXPECT_FALSE(controller()->SectionIsActive(SECTION_CC_BILLING));
826  EXPECT_TRUE(controller()->SectionIsActive(SECTION_SHIPPING));
827
828  EXPECT_FALSE(controller()->IsManuallyEditingSection(SECTION_CC));
829  EXPECT_FALSE(controller()->IsManuallyEditingSection(SECTION_BILLING));
830  EXPECT_FALSE(controller()->IsManuallyEditingSection(SECTION_SHIPPING));
831
832  // Set up some Wallet state.
833  controller()->OnUserNameFetchSuccess("user@example.com");
834  controller()->OnDidGetWalletItems(wallet::GetTestWalletItems());
835
836  ui::MenuModel* account_chooser = controller()->MenuModelForAccountChooser();
837  ASSERT_TRUE(account_chooser->IsItemCheckedAt(0));
838
839  // Check that the view's in the state we expect before starting to simulate
840  // user input.
841  EXPECT_FALSE(controller()->SectionIsActive(SECTION_CC));
842  EXPECT_FALSE(controller()->SectionIsActive(SECTION_BILLING));
843  EXPECT_TRUE(controller()->SectionIsActive(SECTION_CC_BILLING));
844  EXPECT_TRUE(controller()->SectionIsActive(SECTION_SHIPPING));
845
846  EXPECT_TRUE(controller()->IsManuallyEditingSection(SECTION_CC_BILLING));
847
848  // Create some valid inputted billing data.
849  const DetailInput& cc_number =
850      controller()->RequestedFieldsForSection(SECTION_CC_BILLING)[0];
851  EXPECT_EQ(CREDIT_CARD_NUMBER, cc_number.type);
852  TestableAutofillDialogView* view = controller()->GetTestableView();
853  view->SetTextContentsOfInput(cc_number, ASCIIToUTF16("4111111111111111"));
854
855  // Select "Add new shipping info..." from suggestions menu.
856  ui::MenuModel* shipping_model =
857      controller()->MenuModelForSection(SECTION_SHIPPING);
858  shipping_model->ActivatedAt(shipping_model->GetItemCount() - 2);
859
860  EXPECT_TRUE(controller()->IsManuallyEditingSection(SECTION_SHIPPING));
861
862  // Create some invalid, manually inputted shipping data.
863  const DetailInput& shipping_zip =
864      controller()->RequestedFieldsForSection(SECTION_SHIPPING)[5];
865  ASSERT_EQ(ADDRESS_HOME_ZIP, shipping_zip.type);
866  view->SetTextContentsOfInput(shipping_zip, ASCIIToUTF16("shipping zip"));
867
868  // Switch to using Autofill.
869  account_chooser->ActivatedAt(1);
870
871  // Check that appropriate sections are preserved and in manually editing mode
872  // (or disabled, in the case of the combined cc + billing section).
873  EXPECT_TRUE(controller()->SectionIsActive(SECTION_CC));
874  EXPECT_TRUE(controller()->SectionIsActive(SECTION_BILLING));
875  EXPECT_FALSE(controller()->SectionIsActive(SECTION_CC_BILLING));
876  EXPECT_TRUE(controller()->SectionIsActive(SECTION_SHIPPING));
877
878  EXPECT_TRUE(controller()->IsManuallyEditingSection(SECTION_CC));
879  EXPECT_FALSE(controller()->IsManuallyEditingSection(SECTION_BILLING));
880  EXPECT_FALSE(controller()->IsManuallyEditingSection(SECTION_SHIPPING));
881
882  const DetailInput& new_cc_number =
883      controller()->RequestedFieldsForSection(SECTION_CC).front();
884  EXPECT_EQ(cc_number.type, new_cc_number.type);
885  EXPECT_EQ(ASCIIToUTF16("4111111111111111"),
886            view->GetTextContentsOfInput(new_cc_number));
887
888  EXPECT_NE(ASCIIToUTF16("shipping name"),
889            view->GetTextContentsOfInput(shipping_zip));
890}
891#endif  // defined(TOOLKIT_VIEWS) || defined(OS_MACOSX)
892
893}  // namespace autofill
894