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