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.
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 "grit/webkit_resources.h"
19 #include "testing/gmock/include/gmock/gmock.h"
20 #include "testing/gtest/include/gtest/gtest.h"
21 #include "third_party/WebKit/public/web/WebAutofillClient.h"
22 #include "ui/base/resource/resource_bundle.h"
23 #include "ui/gfx/display.h"
24 #include "ui/gfx/rect.h"
27 using ::testing::AtLeast;
28 using ::testing::NiceMock;
30 using WebKit::WebAutofillClient;
35 class MockAutofillExternalDelegate : public AutofillExternalDelegate {
37 MockAutofillExternalDelegate(content::WebContents* web_contents,
38 AutofillManager* autofill_manager,
39 AutofillDriver* autofill_driver)
40 : AutofillExternalDelegate(web_contents, autofill_manager,
42 virtual ~MockAutofillExternalDelegate() {}
44 virtual void DidSelectSuggestion(int identifier) OVERRIDE {}
45 virtual void RemoveSuggestion(const string16& value, int identifier) OVERRIDE
47 virtual void ClearPreviewedForm() OVERRIDE {}
48 base::WeakPtr<AutofillExternalDelegate> GetWeakPtr() {
49 return AutofillExternalDelegate::GetWeakPtr();
53 class MockAutofillManagerDelegate
54 : public autofill::TestAutofillManagerDelegate {
56 MockAutofillManagerDelegate() {}
57 virtual ~MockAutofillManagerDelegate() {}
59 virtual PrefService* GetPrefs() OVERRIDE { return &prefs_; }
62 TestingPrefServiceSimple prefs_;
64 DISALLOW_COPY_AND_ASSIGN(MockAutofillManagerDelegate);
67 class TestAutofillPopupController : public AutofillPopupControllerImpl {
69 explicit TestAutofillPopupController(
70 base::WeakPtr<AutofillExternalDelegate> external_delegate,
71 const gfx::RectF& element_bounds)
72 : AutofillPopupControllerImpl(
73 external_delegate, NULL, NULL, element_bounds,
74 base::i18n::UNKNOWN_DIRECTION) {}
75 virtual ~TestAutofillPopupController() {}
77 void set_display(const gfx::Display display) {
80 virtual gfx::Display GetDisplayNearestPoint(const gfx::Point& point) const
85 // Making protected functions public for testing
86 using AutofillPopupControllerImpl::SetPopupBounds;
87 using AutofillPopupControllerImpl::names;
88 using AutofillPopupControllerImpl::subtexts;
89 using AutofillPopupControllerImpl::identifiers;
90 using AutofillPopupControllerImpl::selected_line;
91 using AutofillPopupControllerImpl::SetSelectedLine;
92 using AutofillPopupControllerImpl::SelectNextLine;
93 using AutofillPopupControllerImpl::SelectPreviousLine;
94 using AutofillPopupControllerImpl::RemoveSelectedLine;
95 using AutofillPopupControllerImpl::popup_bounds;
96 using AutofillPopupControllerImpl::element_bounds;
97 #if !defined(OS_ANDROID)
98 using AutofillPopupControllerImpl::GetNameFontForRow;
99 using AutofillPopupControllerImpl::subtext_font;
100 using AutofillPopupControllerImpl::RowWidthWithoutText;
102 using AutofillPopupControllerImpl::SetValues;
103 using AutofillPopupControllerImpl::GetDesiredPopupWidth;
104 using AutofillPopupControllerImpl::GetDesiredPopupHeight;
105 using AutofillPopupControllerImpl::GetWeakPtr;
106 MOCK_METHOD1(InvalidateRow, void(size_t));
107 MOCK_METHOD0(UpdateBoundsAndRedrawPopup, void());
108 MOCK_METHOD0(Hide, void());
111 AutofillPopupControllerImpl::Hide();
115 virtual void ShowView() OVERRIDE {}
117 gfx::Display display_;
122 class AutofillPopupControllerUnitTest : public ChromeRenderViewHostTestHarness {
124 AutofillPopupControllerUnitTest()
125 : manager_delegate_(new MockAutofillManagerDelegate()),
126 autofill_popup_controller_(NULL) {}
127 virtual ~AutofillPopupControllerUnitTest() {}
129 virtual void SetUp() OVERRIDE {
130 ChromeRenderViewHostTestHarness::SetUp();
132 AutofillDriverImpl::CreateForWebContentsAndDelegate(
134 manager_delegate_.get(),
136 AutofillManager::ENABLE_AUTOFILL_DOWNLOAD_MANAGER);
137 AutofillDriverImpl* driver =
138 AutofillDriverImpl::FromWebContents(web_contents());
139 external_delegate_.reset(
140 new NiceMock<MockAutofillExternalDelegate>(
142 driver->autofill_manager(),
145 autofill_popup_controller_ =
146 new testing::NiceMock<TestAutofillPopupController>(
147 external_delegate_->GetWeakPtr(),gfx::Rect());
150 virtual void TearDown() OVERRIDE {
151 // This will make sure the controller and the view (if any) are both
153 if (autofill_popup_controller_)
154 autofill_popup_controller_->DoHide();
156 external_delegate_.reset();
157 ChromeRenderViewHostTestHarness::TearDown();
160 TestAutofillPopupController* popup_controller() {
161 return autofill_popup_controller_;
164 MockAutofillExternalDelegate* delegate() {
165 return external_delegate_.get();
169 scoped_ptr<MockAutofillManagerDelegate> manager_delegate_;
170 scoped_ptr<NiceMock<MockAutofillExternalDelegate> > external_delegate_;
171 testing::NiceMock<TestAutofillPopupController>* autofill_popup_controller_;
174 TEST_F(AutofillPopupControllerUnitTest, SetBounds) {
175 // Ensure the popup size can be set and causes a redraw.
176 gfx::Rect popup_bounds(10, 10, 100, 100);
178 EXPECT_CALL(*autofill_popup_controller_,
179 UpdateBoundsAndRedrawPopup());
181 popup_controller()->SetPopupBounds(popup_bounds);
183 EXPECT_EQ(popup_bounds, popup_controller()->popup_bounds());
186 TEST_F(AutofillPopupControllerUnitTest, ChangeSelectedLine) {
188 std::vector<string16> names(2, string16());
189 std::vector<int> autofill_ids(2, 0);
190 autofill_popup_controller_->Show(names, names, names, autofill_ids);
192 EXPECT_LT(autofill_popup_controller_->selected_line(), 0);
193 // Check that there are at least 2 values so that the first and last selection
196 static_cast<int>(autofill_popup_controller_->subtexts().size()));
198 // Test wrapping before the front.
199 autofill_popup_controller_->SelectPreviousLine();
200 EXPECT_EQ(static_cast<int>(
201 autofill_popup_controller_->subtexts().size() - 1),
202 autofill_popup_controller_->selected_line());
204 // Test wrapping after the end.
205 autofill_popup_controller_->SelectNextLine();
206 EXPECT_EQ(0, autofill_popup_controller_->selected_line());
209 TEST_F(AutofillPopupControllerUnitTest, RedrawSelectedLine) {
211 std::vector<string16> names(2, string16());
212 std::vector<int> autofill_ids(2, 0);
213 autofill_popup_controller_->Show(names, names, names, autofill_ids);
215 // Make sure that when a new line is selected, it is invalidated so it can
216 // be updated to show it is selected.
217 int selected_line = 0;
218 EXPECT_CALL(*autofill_popup_controller_, InvalidateRow(selected_line));
219 autofill_popup_controller_->SetSelectedLine(selected_line);
221 // Ensure that the row isn't invalidated if it didn't change.
222 EXPECT_CALL(*autofill_popup_controller_,
223 InvalidateRow(selected_line)).Times(0);
224 autofill_popup_controller_->SetSelectedLine(selected_line);
226 // Change back to no selection.
227 EXPECT_CALL(*autofill_popup_controller_, InvalidateRow(selected_line));
228 autofill_popup_controller_->SetSelectedLine(-1);
231 TEST_F(AutofillPopupControllerUnitTest, RemoveLine) {
233 std::vector<string16> names(3, string16());
234 std::vector<int> autofill_ids;
235 autofill_ids.push_back(1);
236 autofill_ids.push_back(1);
237 autofill_ids.push_back(WebAutofillClient::MenuItemIDAutofillOptions);
238 autofill_popup_controller_->Show(names, names, names, autofill_ids);
240 // Generate a popup, so it can be hidden later. It doesn't matter what the
241 // external_delegate thinks is being shown in the process, since we are just
242 // testing the popup here.
243 autofill::GenerateTestAutofillPopup(external_delegate_.get());
245 // No line is selected so the removal should fail.
246 EXPECT_FALSE(autofill_popup_controller_->RemoveSelectedLine());
248 // Try to remove the last entry and ensure it fails (it is an option).
249 autofill_popup_controller_->SetSelectedLine(
250 autofill_popup_controller_->subtexts().size() - 1);
251 EXPECT_FALSE(autofill_popup_controller_->RemoveSelectedLine());
252 EXPECT_LE(0, autofill_popup_controller_->selected_line());
254 // Remove the first entry. The popup should be redrawn since its size has
256 EXPECT_CALL(*autofill_popup_controller_, UpdateBoundsAndRedrawPopup());
257 autofill_popup_controller_->SetSelectedLine(0);
258 EXPECT_TRUE(autofill_popup_controller_->RemoveSelectedLine());
260 // Remove the last entry. The popup should then be hidden since there are
261 // no Autofill entries left.
262 EXPECT_CALL(*autofill_popup_controller_, Hide());
263 autofill_popup_controller_->SetSelectedLine(0);
264 EXPECT_TRUE(autofill_popup_controller_->RemoveSelectedLine());
267 TEST_F(AutofillPopupControllerUnitTest, RemoveOnlyLine) {
269 std::vector<string16> names(1, string16());
270 std::vector<int> autofill_ids;
271 autofill_ids.push_back(1);
272 autofill_popup_controller_->Show(names, names, names, autofill_ids);
275 autofill::GenerateTestAutofillPopup(external_delegate_.get());
277 // Select the only line.
278 autofill_popup_controller_->SetSelectedLine(0);
280 // Remove the only line. There should be no row invalidation and the popup
281 // should then be hidden since there are no Autofill entries left.
282 EXPECT_CALL(*autofill_popup_controller_, Hide());
283 EXPECT_CALL(*autofill_popup_controller_, InvalidateRow(_)).Times(0);
284 EXPECT_TRUE(autofill_popup_controller_->RemoveSelectedLine());
287 TEST_F(AutofillPopupControllerUnitTest, SkipSeparator) {
289 std::vector<string16> names(3, string16());
290 std::vector<int> autofill_ids;
291 autofill_ids.push_back(1);
292 autofill_ids.push_back(WebAutofillClient::MenuItemIDSeparator);
293 autofill_ids.push_back(WebAutofillClient::MenuItemIDAutofillOptions);
294 autofill_popup_controller_->Show(names, names, names, autofill_ids);
296 autofill_popup_controller_->SetSelectedLine(0);
298 // Make sure next skips the unselectable separator.
299 autofill_popup_controller_->SelectNextLine();
300 EXPECT_EQ(2, autofill_popup_controller_->selected_line());
302 // Make sure previous skips the unselectable separator.
303 autofill_popup_controller_->SelectPreviousLine();
304 EXPECT_EQ(0, autofill_popup_controller_->selected_line());
307 TEST_F(AutofillPopupControllerUnitTest, RowWidthWithoutText) {
308 std::vector<string16> names(4);
309 std::vector<string16> subtexts(4);
310 std::vector<string16> icons(4);
311 std::vector<int> ids(4);
313 // Set up some visible display so the text values are kept.
314 gfx::Display display(0, gfx::Rect(0, 0, 100, 100));
315 autofill_popup_controller_->set_display(display);
317 // Give elements 1 and 3 subtexts and elements 2 and 3 icons, to ensure
318 // all combinations of subtexts and icons.
319 subtexts[1] = ASCIIToUTF16("x");
320 subtexts[3] = ASCIIToUTF16("x");
321 icons[2] = ASCIIToUTF16("americanExpressCC");
322 icons[3] = ASCIIToUTF16("genericCC");
323 autofill_popup_controller_->Show(names, subtexts, icons, ids);
326 AutofillPopupView::kEndPadding * 2 +
327 AutofillPopupView::kBorderThickness * 2;
328 int subtext_increase = AutofillPopupView::kNamePadding;
330 EXPECT_EQ(base_size, autofill_popup_controller_->RowWidthWithoutText(0));
331 EXPECT_EQ(base_size + subtext_increase,
332 autofill_popup_controller_->RowWidthWithoutText(1));
333 EXPECT_EQ(base_size + AutofillPopupView::kIconPadding +
334 ui::ResourceBundle::GetSharedInstance().GetImageNamed(
335 IDR_AUTOFILL_CC_AMEX).Width(),
336 autofill_popup_controller_->RowWidthWithoutText(2));
337 EXPECT_EQ(base_size + subtext_increase + AutofillPopupView::kIconPadding +
338 ui::ResourceBundle::GetSharedInstance().GetImageNamed(
339 IDR_AUTOFILL_CC_GENERIC).Width(),
340 autofill_popup_controller_->RowWidthWithoutText(3));
343 TEST_F(AutofillPopupControllerUnitTest, UpdateDataListValues) {
344 std::vector<string16> items;
345 items.push_back(string16());
346 std::vector<int> ids;
349 autofill_popup_controller_->Show(items, items, items, ids);
351 EXPECT_EQ(items, autofill_popup_controller_->names());
352 EXPECT_EQ(ids, autofill_popup_controller_->identifiers());
354 // Add one data list entry.
355 std::vector<string16> data_list_values;
356 data_list_values.push_back(ASCIIToUTF16("data list value 1"));
358 autofill_popup_controller_->UpdateDataListValues(data_list_values,
361 // Update the expected values.
362 items.insert(items.begin(), data_list_values[0]);
363 items.insert(items.begin() + 1, string16());
364 ids.insert(ids.begin(), WebAutofillClient::MenuItemIDDataListEntry);
365 ids.insert(ids.begin() + 1, WebAutofillClient::MenuItemIDSeparator);
367 EXPECT_EQ(items, autofill_popup_controller_->names());
368 EXPECT_EQ(ids, autofill_popup_controller_->identifiers());
370 // Add two data list entries (which should replace the current one).
371 data_list_values.push_back(ASCIIToUTF16("data list value 2"));
373 autofill_popup_controller_->UpdateDataListValues(data_list_values,
376 // Update the expected values.
377 items.insert(items.begin() + 1, data_list_values[1]);
378 ids.insert(ids.begin(), WebAutofillClient::MenuItemIDDataListEntry);
380 EXPECT_EQ(items, autofill_popup_controller_->names());
381 EXPECT_EQ(ids, autofill_popup_controller_->identifiers());
383 // Clear all data list values.
384 data_list_values.clear();
386 autofill_popup_controller_->UpdateDataListValues(data_list_values,
390 items.push_back(string16());
394 EXPECT_EQ(items, autofill_popup_controller_->names());
395 EXPECT_EQ(ids, autofill_popup_controller_->identifiers());
398 TEST_F(AutofillPopupControllerUnitTest, PopupsWithOnlyDataLists) {
399 // Create the popup with a single datalist element.
400 std::vector<string16> items;
401 items.push_back(string16());
402 std::vector<int> ids;
403 ids.push_back(WebAutofillClient::MenuItemIDDataListEntry);
405 autofill_popup_controller_->Show(items, items, items, ids);
407 EXPECT_EQ(items, autofill_popup_controller_->names());
408 EXPECT_EQ(ids, autofill_popup_controller_->identifiers());
410 // Replace the datalist element with a new one.
411 std::vector<string16> data_list_values;
412 data_list_values.push_back(ASCIIToUTF16("data list value 1"));
414 autofill_popup_controller_->UpdateDataListValues(data_list_values,
417 EXPECT_EQ(data_list_values, autofill_popup_controller_->names());
418 // The id value should stay the same.
419 EXPECT_EQ(ids, autofill_popup_controller_->identifiers());
421 // Clear datalist values and check that the popup becomes hidden.
422 EXPECT_CALL(*autofill_popup_controller_, Hide());
423 data_list_values.clear();
424 autofill_popup_controller_->UpdateDataListValues(data_list_values,
428 TEST_F(AutofillPopupControllerUnitTest, GetOrCreate) {
429 AutofillDriverImpl* driver =
430 AutofillDriverImpl::FromWebContents(web_contents());
431 MockAutofillExternalDelegate delegate(
432 web_contents(), driver->autofill_manager(), driver);
434 WeakPtr<AutofillPopupControllerImpl> controller =
435 AutofillPopupControllerImpl::GetOrCreate(
436 WeakPtr<AutofillPopupControllerImpl>(), delegate.GetWeakPtr(),
437 NULL, NULL, gfx::Rect(), base::i18n::UNKNOWN_DIRECTION);
438 EXPECT_TRUE(controller.get());
442 controller = AutofillPopupControllerImpl::GetOrCreate(
443 WeakPtr<AutofillPopupControllerImpl>(), delegate.GetWeakPtr(),
444 NULL, NULL, gfx::Rect(), base::i18n::UNKNOWN_DIRECTION);
445 EXPECT_TRUE(controller.get());
447 WeakPtr<AutofillPopupControllerImpl> controller2 =
448 AutofillPopupControllerImpl::GetOrCreate(controller,
449 delegate.GetWeakPtr(),
453 base::i18n::UNKNOWN_DIRECTION);
454 EXPECT_EQ(controller.get(), controller2.get());
457 testing::NiceMock<TestAutofillPopupController>* test_controller =
458 new testing::NiceMock<TestAutofillPopupController>(delegate.GetWeakPtr(),
460 EXPECT_CALL(*test_controller, Hide());
462 gfx::RectF bounds(0.f, 0.f, 1.f, 2.f);
463 base::WeakPtr<AutofillPopupControllerImpl> controller3 =
464 AutofillPopupControllerImpl::GetOrCreate(
465 test_controller->GetWeakPtr(),
466 delegate.GetWeakPtr(),
470 base::i18n::UNKNOWN_DIRECTION);
473 static_cast<AutofillPopupController*>(controller3.get())->
477 // Hide the test_controller to delete it.
478 test_controller->DoHide();
481 TEST_F(AutofillPopupControllerUnitTest, ProperlyResetController) {
482 std::vector<string16> names(2);
483 std::vector<int> ids(2);
484 popup_controller()->SetValues(names, names, names, ids);
485 popup_controller()->SetSelectedLine(0);
487 // Now show a new popup with the same controller, but with fewer items.
488 WeakPtr<AutofillPopupControllerImpl> controller =
489 AutofillPopupControllerImpl::GetOrCreate(
490 popup_controller()->GetWeakPtr(),
491 delegate()->GetWeakPtr(),
495 base::i18n::UNKNOWN_DIRECTION);
496 EXPECT_NE(0, controller->selected_line());
497 EXPECT_TRUE(controller->names().empty());
500 #if !defined(OS_ANDROID)
501 TEST_F(AutofillPopupControllerUnitTest, ElideText) {
502 std::vector<string16> names;
503 names.push_back(ASCIIToUTF16("Text that will need to be trimmed"));
504 names.push_back(ASCIIToUTF16("Untrimmed"));
506 std::vector<string16> subtexts;
507 subtexts.push_back(ASCIIToUTF16("Label that will be trimmed"));
508 subtexts.push_back(ASCIIToUTF16("Untrimmed"));
510 std::vector<string16> icons(2, ASCIIToUTF16("genericCC"));
511 std::vector<int> autofill_ids(2, 0);
513 // Show the popup once so we can easily generate the size it needs.
514 autofill_popup_controller_->Show(names, subtexts, icons, autofill_ids);
516 // Ensure the popup will be too small to display all of the first row.
517 int popup_max_width =
518 autofill_popup_controller_->GetNameFontForRow(0).GetStringWidth(
520 autofill_popup_controller_->subtext_font().GetStringWidth(subtexts[0]) -
522 gfx::Rect popup_bounds = gfx::Rect(0, 0, popup_max_width, 0);
523 autofill_popup_controller_->set_display(gfx::Display(0, popup_bounds));
525 autofill_popup_controller_->Show(names, subtexts, icons, autofill_ids);
527 // The first element was long so it should have been trimmed.
528 EXPECT_NE(names[0], autofill_popup_controller_->names()[0]);
529 EXPECT_NE(subtexts[0], autofill_popup_controller_->subtexts()[0]);
531 // The second element was shorter so it should be unchanged.
532 EXPECT_EQ(names[1], autofill_popup_controller_->names()[1]);
533 EXPECT_EQ(subtexts[1], autofill_popup_controller_->subtexts()[1]);
537 TEST_F(AutofillPopupControllerUnitTest, GrowPopupInSpace) {
538 std::vector<string16> names(1);
539 std::vector<int> autofill_ids(1, 1);
541 // Call Show so that GetDesired...() will be able to provide valid values.
542 autofill_popup_controller_->Show(names, names, names, autofill_ids);
543 int desired_width = autofill_popup_controller_->GetDesiredPopupWidth();
544 int desired_height = autofill_popup_controller_->GetDesiredPopupHeight();
546 // Set up the visible screen space.
547 gfx::Display display(0,
548 gfx::Rect(0, 0, desired_width * 2, desired_height * 2));
550 // Store the possible element bounds and the popup bounds they should result
552 std::vector<gfx::RectF> element_bounds;
553 std::vector<gfx::Rect> expected_popup_bounds;
555 // The popup grows down and to the right.
556 element_bounds.push_back(gfx::RectF(0, 0, 0, 0));
557 expected_popup_bounds.push_back(
558 gfx::Rect(0, 0, desired_width, desired_height));
560 // The popup grows down and to the left.
561 element_bounds.push_back(gfx::RectF(2 * desired_width, 0, 0, 0));
562 expected_popup_bounds.push_back(
563 gfx::Rect(desired_width, 0, desired_width, desired_height));
565 // The popup grows up and to the right.
566 element_bounds.push_back(gfx::RectF(0, 2 * desired_height, 0, 0));
567 expected_popup_bounds.push_back(
568 gfx::Rect(0, desired_height, desired_width, desired_height));
570 // The popup grows up and to the left.
571 element_bounds.push_back(
572 gfx::RectF(2 * desired_width, 2 * desired_height, 0, 0));
573 expected_popup_bounds.push_back(
574 gfx::Rect(desired_width, desired_height, desired_width, desired_height));
576 // The popup would be partial off the top and left side of the screen.
577 element_bounds.push_back(
578 gfx::RectF(-desired_width / 2, -desired_height / 2, 0, 0));
579 expected_popup_bounds.push_back(
580 gfx::Rect(0, 0, desired_width, desired_height));
582 // The popup would be partially off the bottom and the right side of
584 element_bounds.push_back(
585 gfx::RectF(desired_width * 1.5, desired_height * 1.5, 0, 0));
586 expected_popup_bounds.push_back(
587 gfx::Rect((desired_width + 1) / 2, (desired_height + 1) / 2,
588 desired_width, desired_height));
590 for (size_t i = 0; i < element_bounds.size(); ++i) {
591 AutofillDriverImpl* driver =
592 AutofillDriverImpl::FromWebContents(web_contents());
593 NiceMock<MockAutofillExternalDelegate> external_delegate(
594 web_contents(), driver->autofill_manager(), driver);
595 TestAutofillPopupController* autofill_popup_controller =
596 new TestAutofillPopupController(external_delegate.GetWeakPtr(),
599 autofill_popup_controller->set_display(display);
600 autofill_popup_controller->Show(names, names, names, autofill_ids);
602 EXPECT_EQ(expected_popup_bounds[i].ToString(),
603 autofill_popup_controller->popup_bounds().ToString()) <<
604 "Popup bounds failed to match for test " << i;
606 // Hide the controller to delete it.
607 autofill_popup_controller->DoHide();
611 } // namespace autofill