autofill_popup_controller_unittest.cc revision 5d1f7b1de12d16ceb2c938c56701a3e8bfa558f7
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/memory/scoped_ptr.h"
6#include "base/memory/weak_ptr.h"
7#include "base/prefs/pref_service.h"
8#include "base/strings/utf_string_conversions.h"
9#include "chrome/browser/ui/autofill/autofill_popup_controller_impl.h"
10#include "chrome/browser/ui/autofill/autofill_popup_view.h"
11#include "chrome/browser/ui/autofill/popup_constants.h"
12#include "chrome/browser/ui/autofill/test_popup_controller_common.h"
13#include "chrome/test/base/chrome_render_view_host_test_harness.h"
14#include "chrome/test/base/testing_profile.h"
15#include "components/autofill/content/browser/autofill_driver_impl.h"
16#include "components/autofill/core/browser/autofill_external_delegate.h"
17#include "components/autofill/core/browser/autofill_manager.h"
18#include "components/autofill/core/browser/autofill_test_utils.h"
19#include "components/autofill/core/browser/test_autofill_external_delegate.h"
20#include "components/autofill/core/browser/test_autofill_manager_delegate.h"
21#include "grit/webkit_resources.h"
22#include "testing/gmock/include/gmock/gmock.h"
23#include "testing/gtest/include/gtest/gtest.h"
24#include "third_party/WebKit/public/web/WebAutofillClient.h"
25#include "ui/base/resource/resource_bundle.h"
26#include "ui/gfx/display.h"
27#include "ui/gfx/rect.h"
28#include "ui/gfx/text_utils.h"
29
30using ::testing::_;
31using ::testing::AtLeast;
32using ::testing::NiceMock;
33using base::ASCIIToUTF16;
34using base::WeakPtr;
35using blink::WebAutofillClient;
36
37namespace autofill {
38namespace {
39
40class MockAutofillExternalDelegate : public AutofillExternalDelegate {
41 public:
42  MockAutofillExternalDelegate(AutofillManager* autofill_manager,
43                               AutofillDriver* autofill_driver)
44      : AutofillExternalDelegate(autofill_manager, autofill_driver) {}
45  virtual ~MockAutofillExternalDelegate() {}
46
47  virtual void DidSelectSuggestion(int identifier) OVERRIDE {}
48  virtual void RemoveSuggestion(const base::string16& value,
49                                int identifier) OVERRIDE {}
50  virtual void ClearPreviewedForm() OVERRIDE {}
51  base::WeakPtr<AutofillExternalDelegate> GetWeakPtr() {
52    return AutofillExternalDelegate::GetWeakPtr();
53  }
54};
55
56class MockAutofillManagerDelegate
57    : public autofill::TestAutofillManagerDelegate {
58 public:
59  MockAutofillManagerDelegate()
60      : prefs_(autofill::test::PrefServiceForTesting()) {
61  }
62  virtual ~MockAutofillManagerDelegate() {}
63
64  virtual PrefService* GetPrefs() OVERRIDE { return prefs_.get(); }
65
66 private:
67  scoped_ptr<PrefService> prefs_;
68
69  DISALLOW_COPY_AND_ASSIGN(MockAutofillManagerDelegate);
70};
71
72class TestAutofillPopupController : public AutofillPopupControllerImpl {
73 public:
74  explicit TestAutofillPopupController(
75      base::WeakPtr<AutofillExternalDelegate> external_delegate,
76      const gfx::RectF& element_bounds)
77      : AutofillPopupControllerImpl(
78            external_delegate, NULL, NULL, element_bounds,
79            base::i18n::UNKNOWN_DIRECTION),
80        test_controller_common_(new TestPopupControllerCommon(element_bounds)) {
81    controller_common_.reset(test_controller_common_);
82  }
83  virtual ~TestAutofillPopupController() {}
84
85  void set_display(const gfx::Display& display) {
86    test_controller_common_->set_display(display);
87  }
88
89  // Making protected functions public for testing
90  using AutofillPopupControllerImpl::SetPopupBounds;
91  using AutofillPopupControllerImpl::names;
92  using AutofillPopupControllerImpl::subtexts;
93  using AutofillPopupControllerImpl::identifiers;
94  using AutofillPopupControllerImpl::selected_line;
95  using AutofillPopupControllerImpl::SetSelectedLine;
96  using AutofillPopupControllerImpl::SelectNextLine;
97  using AutofillPopupControllerImpl::SelectPreviousLine;
98  using AutofillPopupControllerImpl::RemoveSelectedLine;
99  using AutofillPopupControllerImpl::popup_bounds;
100  using AutofillPopupControllerImpl::element_bounds;
101#if !defined(OS_ANDROID)
102  using AutofillPopupControllerImpl::GetNameFontListForRow;
103  using AutofillPopupControllerImpl::subtext_font_list;
104  using AutofillPopupControllerImpl::RowWidthWithoutText;
105#endif
106  using AutofillPopupControllerImpl::SetValues;
107  using AutofillPopupControllerImpl::GetDesiredPopupWidth;
108  using AutofillPopupControllerImpl::GetDesiredPopupHeight;
109  using AutofillPopupControllerImpl::GetWeakPtr;
110  MOCK_METHOD1(InvalidateRow, void(size_t));
111  MOCK_METHOD0(UpdateBoundsAndRedrawPopup, void());
112  MOCK_METHOD0(Hide, void());
113
114  void DoHide() {
115    AutofillPopupControllerImpl::Hide();
116  }
117
118 private:
119  virtual void ShowView() OVERRIDE {}
120
121  TestPopupControllerCommon* test_controller_common_;
122};
123
124}  // namespace
125
126class AutofillPopupControllerUnitTest : public ChromeRenderViewHostTestHarness {
127 public:
128  AutofillPopupControllerUnitTest()
129      : manager_delegate_(new MockAutofillManagerDelegate()),
130        autofill_popup_controller_(NULL) {}
131  virtual ~AutofillPopupControllerUnitTest() {}
132
133  virtual void SetUp() OVERRIDE {
134    ChromeRenderViewHostTestHarness::SetUp();
135
136    AutofillDriverImpl::CreateForWebContentsAndDelegate(
137        web_contents(),
138        manager_delegate_.get(),
139        "en-US",
140        AutofillManager::ENABLE_AUTOFILL_DOWNLOAD_MANAGER);
141    AutofillDriverImpl* driver =
142        AutofillDriverImpl::FromWebContents(web_contents());
143    external_delegate_.reset(
144        new NiceMock<MockAutofillExternalDelegate>(
145            driver->autofill_manager(),
146            driver));
147
148    autofill_popup_controller_ =
149        new testing::NiceMock<TestAutofillPopupController>(
150            external_delegate_->GetWeakPtr(),gfx::Rect());
151  }
152
153  virtual void TearDown() OVERRIDE {
154    // This will make sure the controller and the view (if any) are both
155    // cleaned up.
156    if (autofill_popup_controller_)
157      autofill_popup_controller_->DoHide();
158
159    external_delegate_.reset();
160    ChromeRenderViewHostTestHarness::TearDown();
161  }
162
163  TestAutofillPopupController* popup_controller() {
164    return autofill_popup_controller_;
165  }
166
167  MockAutofillExternalDelegate* delegate() {
168    return external_delegate_.get();
169  }
170
171 protected:
172  scoped_ptr<MockAutofillManagerDelegate> manager_delegate_;
173  scoped_ptr<NiceMock<MockAutofillExternalDelegate> > external_delegate_;
174  testing::NiceMock<TestAutofillPopupController>* autofill_popup_controller_;
175};
176
177TEST_F(AutofillPopupControllerUnitTest, SetBounds) {
178  // Ensure the popup size can be set and causes a redraw.
179  gfx::Rect popup_bounds(10, 10, 100, 100);
180
181  EXPECT_CALL(*autofill_popup_controller_,
182              UpdateBoundsAndRedrawPopup());
183
184  popup_controller()->SetPopupBounds(popup_bounds);
185
186  EXPECT_EQ(popup_bounds, popup_controller()->popup_bounds());
187}
188
189TEST_F(AutofillPopupControllerUnitTest, ChangeSelectedLine) {
190  // Set up the popup.
191  std::vector<base::string16> names(2, base::string16());
192  std::vector<int> autofill_ids(2, 0);
193  autofill_popup_controller_->Show(names, names, names, autofill_ids);
194
195  EXPECT_LT(autofill_popup_controller_->selected_line(), 0);
196  // Check that there are at least 2 values so that the first and last selection
197  // are different.
198  EXPECT_GE(2,
199      static_cast<int>(autofill_popup_controller_->subtexts().size()));
200
201  // Test wrapping before the front.
202  autofill_popup_controller_->SelectPreviousLine();
203  EXPECT_EQ(static_cast<int>(
204      autofill_popup_controller_->subtexts().size() - 1),
205      autofill_popup_controller_->selected_line());
206
207  // Test wrapping after the end.
208  autofill_popup_controller_->SelectNextLine();
209  EXPECT_EQ(0, autofill_popup_controller_->selected_line());
210}
211
212TEST_F(AutofillPopupControllerUnitTest, RedrawSelectedLine) {
213  // Set up the popup.
214  std::vector<base::string16> names(2, base::string16());
215  std::vector<int> autofill_ids(2, 0);
216  autofill_popup_controller_->Show(names, names, names, autofill_ids);
217
218  // Make sure that when a new line is selected, it is invalidated so it can
219  // be updated to show it is selected.
220  int selected_line = 0;
221  EXPECT_CALL(*autofill_popup_controller_, InvalidateRow(selected_line));
222  autofill_popup_controller_->SetSelectedLine(selected_line);
223
224  // Ensure that the row isn't invalidated if it didn't change.
225  EXPECT_CALL(*autofill_popup_controller_,
226              InvalidateRow(selected_line)).Times(0);
227  autofill_popup_controller_->SetSelectedLine(selected_line);
228
229  // Change back to no selection.
230  EXPECT_CALL(*autofill_popup_controller_, InvalidateRow(selected_line));
231  autofill_popup_controller_->SetSelectedLine(-1);
232}
233
234TEST_F(AutofillPopupControllerUnitTest, RemoveLine) {
235  // Set up the popup.
236  std::vector<base::string16> names(3, base::string16());
237  std::vector<int> autofill_ids;
238  autofill_ids.push_back(1);
239  autofill_ids.push_back(1);
240  autofill_ids.push_back(WebAutofillClient::MenuItemIDAutofillOptions);
241  autofill_popup_controller_->Show(names, names, names, autofill_ids);
242
243  // Generate a popup, so it can be hidden later. It doesn't matter what the
244  // external_delegate thinks is being shown in the process, since we are just
245  // testing the popup here.
246  autofill::GenerateTestAutofillPopup(external_delegate_.get());
247
248  // No line is selected so the removal should fail.
249  EXPECT_FALSE(autofill_popup_controller_->RemoveSelectedLine());
250
251  // Try to remove the last entry and ensure it fails (it is an option).
252  autofill_popup_controller_->SetSelectedLine(
253      autofill_popup_controller_->subtexts().size() - 1);
254  EXPECT_FALSE(autofill_popup_controller_->RemoveSelectedLine());
255  EXPECT_LE(0, autofill_popup_controller_->selected_line());
256
257  // Remove the first entry. The popup should be redrawn since its size has
258  // changed.
259  EXPECT_CALL(*autofill_popup_controller_, UpdateBoundsAndRedrawPopup());
260  autofill_popup_controller_->SetSelectedLine(0);
261  EXPECT_TRUE(autofill_popup_controller_->RemoveSelectedLine());
262
263  // Remove the last entry. The popup should then be hidden since there are
264  // no Autofill entries left.
265  EXPECT_CALL(*autofill_popup_controller_, Hide());
266  autofill_popup_controller_->SetSelectedLine(0);
267  EXPECT_TRUE(autofill_popup_controller_->RemoveSelectedLine());
268}
269
270TEST_F(AutofillPopupControllerUnitTest, RemoveOnlyLine) {
271  // Set up the popup.
272  std::vector<base::string16> names(1, base::string16());
273  std::vector<int> autofill_ids;
274  autofill_ids.push_back(1);
275  autofill_popup_controller_->Show(names, names, names, autofill_ids);
276
277  // Generate a popup.
278  autofill::GenerateTestAutofillPopup(external_delegate_.get());
279
280  // Select the only line.
281  autofill_popup_controller_->SetSelectedLine(0);
282
283  // Remove the only line. There should be no row invalidation and the popup
284  // should then be hidden since there are no Autofill entries left.
285  EXPECT_CALL(*autofill_popup_controller_, Hide());
286  EXPECT_CALL(*autofill_popup_controller_, InvalidateRow(_)).Times(0);
287  EXPECT_TRUE(autofill_popup_controller_->RemoveSelectedLine());
288}
289
290TEST_F(AutofillPopupControllerUnitTest, SkipSeparator) {
291  // Set up the popup.
292  std::vector<base::string16> names(3, base::string16());
293  std::vector<int> autofill_ids;
294  autofill_ids.push_back(1);
295  autofill_ids.push_back(WebAutofillClient::MenuItemIDSeparator);
296  autofill_ids.push_back(WebAutofillClient::MenuItemIDAutofillOptions);
297  autofill_popup_controller_->Show(names, names, names, autofill_ids);
298
299  autofill_popup_controller_->SetSelectedLine(0);
300
301  // Make sure next skips the unselectable separator.
302  autofill_popup_controller_->SelectNextLine();
303  EXPECT_EQ(2, autofill_popup_controller_->selected_line());
304
305  // Make sure previous skips the unselectable separator.
306  autofill_popup_controller_->SelectPreviousLine();
307  EXPECT_EQ(0, autofill_popup_controller_->selected_line());
308}
309
310TEST_F(AutofillPopupControllerUnitTest, RowWidthWithoutText) {
311  std::vector<base::string16> names(4);
312  std::vector<base::string16> subtexts(4);
313  std::vector<base::string16> icons(4);
314  std::vector<int> ids(4);
315
316  // Set up some visible display so the text values are kept.
317  gfx::Display display(0, gfx::Rect(0, 0, 100, 100));
318  autofill_popup_controller_->set_display(display);
319
320  // Give elements 1 and 3 subtexts and elements 2 and 3 icons, to ensure
321  // all combinations of subtexts and icons.
322  subtexts[1] = ASCIIToUTF16("x");
323  subtexts[3] = ASCIIToUTF16("x");
324  icons[2] = ASCIIToUTF16("americanExpressCC");
325  icons[3] = ASCIIToUTF16("genericCC");
326  autofill_popup_controller_->Show(names, subtexts, icons, ids);
327
328  int base_size =
329      AutofillPopupView::kEndPadding * 2 +
330      kPopupBorderThickness * 2;
331  int subtext_increase = AutofillPopupView::kNamePadding;
332
333  EXPECT_EQ(base_size, autofill_popup_controller_->RowWidthWithoutText(0));
334  EXPECT_EQ(base_size + subtext_increase,
335            autofill_popup_controller_->RowWidthWithoutText(1));
336  EXPECT_EQ(base_size + AutofillPopupView::kIconPadding +
337                ui::ResourceBundle::GetSharedInstance().GetImageNamed(
338                    IDR_AUTOFILL_CC_AMEX).Width(),
339            autofill_popup_controller_->RowWidthWithoutText(2));
340  EXPECT_EQ(base_size + subtext_increase + AutofillPopupView::kIconPadding +
341                ui::ResourceBundle::GetSharedInstance().GetImageNamed(
342                    IDR_AUTOFILL_CC_GENERIC).Width(),
343            autofill_popup_controller_->RowWidthWithoutText(3));
344}
345
346TEST_F(AutofillPopupControllerUnitTest, UpdateDataListValues) {
347  std::vector<base::string16> items;
348  items.push_back(base::string16());
349  std::vector<int> ids;
350  ids.push_back(1);
351
352  autofill_popup_controller_->Show(items, items, items, ids);
353
354  EXPECT_EQ(items, autofill_popup_controller_->names());
355  EXPECT_EQ(ids, autofill_popup_controller_->identifiers());
356
357  // Add one data list entry.
358  std::vector<base::string16> data_list_values;
359  data_list_values.push_back(ASCIIToUTF16("data list value 1"));
360
361  autofill_popup_controller_->UpdateDataListValues(data_list_values,
362                                                   data_list_values);
363
364  // Update the expected values.
365  items.insert(items.begin(), data_list_values[0]);
366  items.insert(items.begin() + 1, base::string16());
367  ids.insert(ids.begin(), WebAutofillClient::MenuItemIDDataListEntry);
368  ids.insert(ids.begin() + 1, WebAutofillClient::MenuItemIDSeparator);
369
370  EXPECT_EQ(items, autofill_popup_controller_->names());
371  EXPECT_EQ(ids, autofill_popup_controller_->identifiers());
372
373  // Add two data list entries (which should replace the current one).
374  data_list_values.push_back(ASCIIToUTF16("data list value 2"));
375
376  autofill_popup_controller_->UpdateDataListValues(data_list_values,
377                                                   data_list_values);
378
379  // Update the expected values.
380  items.insert(items.begin() + 1, data_list_values[1]);
381  ids.insert(ids.begin(), WebAutofillClient::MenuItemIDDataListEntry);
382
383  EXPECT_EQ(items, autofill_popup_controller_->names());
384  EXPECT_EQ(ids, autofill_popup_controller_->identifiers());
385
386  // Clear all data list values.
387  data_list_values.clear();
388
389  autofill_popup_controller_->UpdateDataListValues(data_list_values,
390                                                   data_list_values);
391
392  items.clear();
393  items.push_back(base::string16());
394  ids.clear();
395  ids.push_back(1);
396
397  EXPECT_EQ(items, autofill_popup_controller_->names());
398  EXPECT_EQ(ids, autofill_popup_controller_->identifiers());
399}
400
401TEST_F(AutofillPopupControllerUnitTest, PopupsWithOnlyDataLists) {
402  // Create the popup with a single datalist element.
403  std::vector<base::string16> items;
404  items.push_back(base::string16());
405  std::vector<int> ids;
406  ids.push_back(WebAutofillClient::MenuItemIDDataListEntry);
407
408  autofill_popup_controller_->Show(items, items, items, ids);
409
410  EXPECT_EQ(items, autofill_popup_controller_->names());
411  EXPECT_EQ(ids, autofill_popup_controller_->identifiers());
412
413  // Replace the datalist element with a new one.
414  std::vector<base::string16> data_list_values;
415  data_list_values.push_back(ASCIIToUTF16("data list value 1"));
416
417  autofill_popup_controller_->UpdateDataListValues(data_list_values,
418                                                   data_list_values);
419
420  EXPECT_EQ(data_list_values, autofill_popup_controller_->names());
421  // The id value should stay the same.
422  EXPECT_EQ(ids, autofill_popup_controller_->identifiers());
423
424  // Clear datalist values and check that the popup becomes hidden.
425  EXPECT_CALL(*autofill_popup_controller_, Hide());
426  data_list_values.clear();
427  autofill_popup_controller_->UpdateDataListValues(data_list_values,
428                                                   data_list_values);
429}
430
431TEST_F(AutofillPopupControllerUnitTest, GetOrCreate) {
432  AutofillDriverImpl* driver =
433      AutofillDriverImpl::FromWebContents(web_contents());
434  MockAutofillExternalDelegate delegate(driver->autofill_manager(), driver);
435
436  WeakPtr<AutofillPopupControllerImpl> controller =
437      AutofillPopupControllerImpl::GetOrCreate(
438          WeakPtr<AutofillPopupControllerImpl>(), delegate.GetWeakPtr(),
439          NULL, NULL, gfx::Rect(), base::i18n::UNKNOWN_DIRECTION);
440  EXPECT_TRUE(controller.get());
441
442  controller->Hide();
443
444  controller = AutofillPopupControllerImpl::GetOrCreate(
445      WeakPtr<AutofillPopupControllerImpl>(), delegate.GetWeakPtr(),
446      NULL, NULL, gfx::Rect(), base::i18n::UNKNOWN_DIRECTION);
447  EXPECT_TRUE(controller.get());
448
449  WeakPtr<AutofillPopupControllerImpl> controller2 =
450      AutofillPopupControllerImpl::GetOrCreate(controller,
451                                               delegate.GetWeakPtr(),
452                                               NULL,
453                                               NULL,
454                                               gfx::Rect(),
455                                               base::i18n::UNKNOWN_DIRECTION);
456  EXPECT_EQ(controller.get(), controller2.get());
457  controller->Hide();
458
459  testing::NiceMock<TestAutofillPopupController>* test_controller =
460      new testing::NiceMock<TestAutofillPopupController>(delegate.GetWeakPtr(),
461                                                         gfx::Rect());
462  EXPECT_CALL(*test_controller, Hide());
463
464  gfx::RectF bounds(0.f, 0.f, 1.f, 2.f);
465  base::WeakPtr<AutofillPopupControllerImpl> controller3 =
466      AutofillPopupControllerImpl::GetOrCreate(
467          test_controller->GetWeakPtr(),
468          delegate.GetWeakPtr(),
469          NULL,
470          NULL,
471          bounds,
472          base::i18n::UNKNOWN_DIRECTION);
473  EXPECT_EQ(
474      bounds,
475      static_cast<AutofillPopupController*>(controller3.get())->
476          element_bounds());
477  controller3->Hide();
478
479  // Hide the test_controller to delete it.
480  test_controller->DoHide();
481}
482
483TEST_F(AutofillPopupControllerUnitTest, ProperlyResetController) {
484  std::vector<base::string16> names(2);
485  std::vector<int> ids(2);
486  popup_controller()->SetValues(names, names, names, ids);
487  popup_controller()->SetSelectedLine(0);
488
489  // Now show a new popup with the same controller, but with fewer items.
490  WeakPtr<AutofillPopupControllerImpl> controller =
491      AutofillPopupControllerImpl::GetOrCreate(
492          popup_controller()->GetWeakPtr(),
493          delegate()->GetWeakPtr(),
494          NULL,
495          NULL,
496          gfx::Rect(),
497          base::i18n::UNKNOWN_DIRECTION);
498  EXPECT_NE(0, controller->selected_line());
499  EXPECT_TRUE(controller->names().empty());
500}
501
502#if !defined(OS_ANDROID)
503TEST_F(AutofillPopupControllerUnitTest, ElideText) {
504  std::vector<base::string16> names;
505  names.push_back(ASCIIToUTF16("Text that will need to be trimmed"));
506  names.push_back(ASCIIToUTF16("Untrimmed"));
507
508  std::vector<base::string16> subtexts;
509  subtexts.push_back(ASCIIToUTF16("Label that will be trimmed"));
510  subtexts.push_back(ASCIIToUTF16("Untrimmed"));
511
512  std::vector<base::string16> icons(2, ASCIIToUTF16("genericCC"));
513  std::vector<int> autofill_ids(2, 0);
514
515  // Show the popup once so we can easily generate the size it needs.
516  autofill_popup_controller_->Show(names, subtexts, icons, autofill_ids);
517
518  // Ensure the popup will be too small to display all of the first row.
519  int popup_max_width =
520      gfx::GetStringWidth(
521          names[0], autofill_popup_controller_->GetNameFontListForRow(0)) +
522      gfx::GetStringWidth(
523          subtexts[0], autofill_popup_controller_->subtext_font_list()) - 25;
524  gfx::Rect popup_bounds = gfx::Rect(0, 0, popup_max_width, 0);
525  autofill_popup_controller_->set_display(gfx::Display(0, popup_bounds));
526
527  autofill_popup_controller_->Show(names, subtexts, icons, autofill_ids);
528
529  // The first element was long so it should have been trimmed.
530  EXPECT_NE(names[0], autofill_popup_controller_->names()[0]);
531  EXPECT_NE(subtexts[0], autofill_popup_controller_->subtexts()[0]);
532
533  // The second element was shorter so it should be unchanged.
534  EXPECT_EQ(names[1], autofill_popup_controller_->names()[1]);
535  EXPECT_EQ(subtexts[1], autofill_popup_controller_->subtexts()[1]);
536}
537#endif
538
539}  // namespace autofill
540