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