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