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