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