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