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