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/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/content_autofill_driver.h"
16 #include "components/autofill/content/browser/content_autofill_driver_factory.h"
17 #include "components/autofill/core/browser/autofill_external_delegate.h"
18 #include "components/autofill/core/browser/autofill_manager.h"
19 #include "components/autofill/core/browser/autofill_test_utils.h"
20 #include "components/autofill/core/browser/popup_item_ids.h"
21 #include "components/autofill/core/browser/test_autofill_client.h"
22 #include "components/autofill/core/browser/test_autofill_external_delegate.h"
23 #include "content/public/browser/web_contents.h"
24 #include "grit/components_scaled_resources.h"
25 #include "testing/gmock/include/gmock/gmock.h"
26 #include "testing/gtest/include/gtest/gtest.h"
27 #include "ui/base/resource/resource_bundle.h"
28 #include "ui/gfx/display.h"
29 #include "ui/gfx/geometry/rect.h"
30 #include "ui/gfx/text_utils.h"
33 using ::testing::AtLeast
;
34 using ::testing::NiceMock
;
35 using base::ASCIIToUTF16
;
41 class MockAutofillExternalDelegate
: public AutofillExternalDelegate
{
43 MockAutofillExternalDelegate(AutofillManager
* autofill_manager
,
44 AutofillDriver
* autofill_driver
)
45 : AutofillExternalDelegate(autofill_manager
, autofill_driver
) {}
46 ~MockAutofillExternalDelegate() override
{}
48 void DidSelectSuggestion(const base::string16
& value
,
49 int identifier
) override
{}
50 bool RemoveSuggestion(const base::string16
& value
, int identifier
) override
{
53 void ClearPreviewedForm() override
{}
54 base::WeakPtr
<AutofillExternalDelegate
> GetWeakPtr() {
55 return AutofillExternalDelegate::GetWeakPtr();
59 class MockAutofillClient
: public autofill::TestAutofillClient
{
61 MockAutofillClient() : prefs_(autofill::test::PrefServiceForTesting()) {}
62 ~MockAutofillClient() override
{}
64 PrefService
* GetPrefs() override
{ return prefs_
.get(); }
67 scoped_ptr
<PrefService
> prefs_
;
69 DISALLOW_COPY_AND_ASSIGN(MockAutofillClient
);
72 class TestAutofillPopupController
: public AutofillPopupControllerImpl
{
74 explicit TestAutofillPopupController(
75 base::WeakPtr
<AutofillExternalDelegate
> external_delegate
,
76 const gfx::RectF
& element_bounds
)
77 : AutofillPopupControllerImpl(external_delegate
,
81 base::i18n::UNKNOWN_DIRECTION
),
82 test_controller_common_(
83 new TestPopupControllerCommon(element_bounds
,
84 base::i18n::LEFT_TO_RIGHT
)) {
85 controller_common_
.reset(test_controller_common_
);
87 ~TestAutofillPopupController() override
{}
89 void set_display(const gfx::Display
& display
) {
90 test_controller_common_
->set_display(display
);
93 // Making protected functions public for testing
94 using AutofillPopupControllerImpl::SetPopupBounds
;
95 using AutofillPopupControllerImpl::GetLineCount
;
96 using AutofillPopupControllerImpl::GetSuggestionAt
;
97 using AutofillPopupControllerImpl::GetElidedValueAt
;
98 using AutofillPopupControllerImpl::GetElidedLabelAt
;
99 using AutofillPopupControllerImpl::selected_line
;
100 using AutofillPopupControllerImpl::SetSelectedLine
;
101 using AutofillPopupControllerImpl::SelectNextLine
;
102 using AutofillPopupControllerImpl::SelectPreviousLine
;
103 using AutofillPopupControllerImpl::RemoveSelectedLine
;
104 using AutofillPopupControllerImpl::popup_bounds
;
105 using AutofillPopupControllerImpl::element_bounds
;
106 #if !defined(OS_ANDROID)
107 using AutofillPopupControllerImpl::GetValueFontListForRow
;
108 using AutofillPopupControllerImpl::GetLabelFontList
;
109 using AutofillPopupControllerImpl::RowWidthWithoutText
;
111 using AutofillPopupControllerImpl::SetValues
;
112 using AutofillPopupControllerImpl::GetDesiredPopupWidth
;
113 using AutofillPopupControllerImpl::GetDesiredPopupHeight
;
114 using AutofillPopupControllerImpl::GetWeakPtr
;
115 MOCK_METHOD1(InvalidateRow
, void(size_t));
116 MOCK_METHOD0(UpdateBoundsAndRedrawPopup
, void());
117 MOCK_METHOD0(Hide
, void());
120 AutofillPopupControllerImpl::Hide();
124 void ShowView() override
{}
126 TestPopupControllerCommon
* test_controller_common_
;
131 class AutofillPopupControllerUnitTest
: public ChromeRenderViewHostTestHarness
{
133 AutofillPopupControllerUnitTest()
134 : autofill_client_(new MockAutofillClient()),
135 autofill_popup_controller_(NULL
) {}
136 ~AutofillPopupControllerUnitTest() override
{}
138 void SetUp() override
{
139 ChromeRenderViewHostTestHarness::SetUp();
141 ContentAutofillDriverFactory::CreateForWebContentsAndDelegate(
142 web_contents(), autofill_client_
.get(), "en-US",
143 AutofillManager::ENABLE_AUTOFILL_DOWNLOAD_MANAGER
);
144 ContentAutofillDriverFactory
* factory
=
145 ContentAutofillDriverFactory::FromWebContents(web_contents());
146 ContentAutofillDriver
* driver
=
147 factory
->DriverForFrame(web_contents()->GetMainFrame());
148 external_delegate_
.reset(
149 new NiceMock
<MockAutofillExternalDelegate
>(
150 driver
->autofill_manager(),
153 autofill_popup_controller_
=
154 new testing::NiceMock
<TestAutofillPopupController
>(
155 external_delegate_
->GetWeakPtr(), gfx::RectF());
158 void TearDown() override
{
159 // This will make sure the controller and the view (if any) are both
161 if (autofill_popup_controller_
)
162 autofill_popup_controller_
->DoHide();
164 external_delegate_
.reset();
165 ChromeRenderViewHostTestHarness::TearDown();
168 TestAutofillPopupController
* popup_controller() {
169 return autofill_popup_controller_
;
172 MockAutofillExternalDelegate
* delegate() {
173 return external_delegate_
.get();
177 scoped_ptr
<MockAutofillClient
> autofill_client_
;
178 scoped_ptr
<NiceMock
<MockAutofillExternalDelegate
> > external_delegate_
;
179 testing::NiceMock
<TestAutofillPopupController
>* autofill_popup_controller_
;
182 TEST_F(AutofillPopupControllerUnitTest
, SetBounds
) {
183 // Ensure the popup size can be set and causes a redraw.
184 gfx::Rect
popup_bounds(10, 10, 100, 100);
186 EXPECT_CALL(*autofill_popup_controller_
,
187 UpdateBoundsAndRedrawPopup());
189 popup_controller()->SetPopupBounds(popup_bounds
);
191 EXPECT_EQ(popup_bounds
, popup_controller()->popup_bounds());
194 TEST_F(AutofillPopupControllerUnitTest
, ChangeSelectedLine
) {
196 std::vector
<Suggestion
> suggestions
;
197 suggestions
.push_back(Suggestion("", "", "", 0));
198 suggestions
.push_back(Suggestion("", "", "", 0));
199 autofill_popup_controller_
->Show(suggestions
);
201 EXPECT_LT(autofill_popup_controller_
->selected_line(), 0);
202 // Check that there are at least 2 values so that the first and last selection
205 static_cast<int>(autofill_popup_controller_
->GetLineCount()));
207 // Test wrapping before the front.
208 autofill_popup_controller_
->SelectPreviousLine();
209 EXPECT_EQ(static_cast<int>(
210 autofill_popup_controller_
->GetLineCount() - 1),
211 autofill_popup_controller_
->selected_line());
213 // Test wrapping after the end.
214 autofill_popup_controller_
->SelectNextLine();
215 EXPECT_EQ(0, autofill_popup_controller_
->selected_line());
218 TEST_F(AutofillPopupControllerUnitTest
, RedrawSelectedLine
) {
220 std::vector
<Suggestion
> suggestions
;
221 suggestions
.push_back(Suggestion("", "", "", 0));
222 suggestions
.push_back(Suggestion("", "", "", 0));
223 autofill_popup_controller_
->Show(suggestions
);
225 // Make sure that when a new line is selected, it is invalidated so it can
226 // be updated to show it is selected.
227 int selected_line
= 0;
228 EXPECT_CALL(*autofill_popup_controller_
, InvalidateRow(selected_line
));
229 autofill_popup_controller_
->SetSelectedLine(selected_line
);
231 // Ensure that the row isn't invalidated if it didn't change.
232 EXPECT_CALL(*autofill_popup_controller_
,
233 InvalidateRow(selected_line
)).Times(0);
234 autofill_popup_controller_
->SetSelectedLine(selected_line
);
236 // Change back to no selection.
237 EXPECT_CALL(*autofill_popup_controller_
, InvalidateRow(selected_line
));
238 autofill_popup_controller_
->SetSelectedLine(-1);
241 TEST_F(AutofillPopupControllerUnitTest
, RemoveLine
) {
243 std::vector
<Suggestion
> suggestions
;
244 suggestions
.push_back(Suggestion("", "", "", 1));
245 suggestions
.push_back(Suggestion("", "", "", 1));
246 suggestions
.push_back(Suggestion("", "", "", POPUP_ITEM_ID_AUTOFILL_OPTIONS
));
247 autofill_popup_controller_
->Show(suggestions
);
249 // Generate a popup, so it can be hidden later. It doesn't matter what the
250 // external_delegate thinks is being shown in the process, since we are just
251 // testing the popup here.
252 autofill::GenerateTestAutofillPopup(external_delegate_
.get());
254 // No line is selected so the removal should fail.
255 EXPECT_FALSE(autofill_popup_controller_
->RemoveSelectedLine());
257 // Remove the first entry. The popup should be redrawn since its size has
259 EXPECT_CALL(*autofill_popup_controller_
, UpdateBoundsAndRedrawPopup());
260 autofill_popup_controller_
->SetSelectedLine(0);
261 EXPECT_TRUE(autofill_popup_controller_
->RemoveSelectedLine());
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());
270 TEST_F(AutofillPopupControllerUnitTest
, RemoveOnlyLine
) {
272 std::vector
<Suggestion
> suggestions
;
273 suggestions
.push_back(Suggestion("", "", "", 1));
274 autofill_popup_controller_
->Show(suggestions
);
277 autofill::GenerateTestAutofillPopup(external_delegate_
.get());
279 // Select the only line.
280 autofill_popup_controller_
->SetSelectedLine(0);
282 // Remove the only line. There should be no row invalidation and the popup
283 // should then be hidden since there are no Autofill entries left.
284 EXPECT_CALL(*autofill_popup_controller_
, Hide());
285 EXPECT_CALL(*autofill_popup_controller_
, InvalidateRow(_
)).Times(0);
286 EXPECT_TRUE(autofill_popup_controller_
->RemoveSelectedLine());
289 TEST_F(AutofillPopupControllerUnitTest
, SkipSeparator
) {
291 std::vector
<Suggestion
> suggestions
;
292 suggestions
.push_back(Suggestion("", "", "", 1));
293 suggestions
.push_back(Suggestion("", "", "", POPUP_ITEM_ID_SEPARATOR
));
294 suggestions
.push_back(Suggestion("", "", "", POPUP_ITEM_ID_AUTOFILL_OPTIONS
));
295 autofill_popup_controller_
->Show(suggestions
);
297 autofill_popup_controller_
->SetSelectedLine(0);
299 // Make sure next skips the unselectable separator.
300 autofill_popup_controller_
->SelectNextLine();
301 EXPECT_EQ(2, autofill_popup_controller_
->selected_line());
303 // Make sure previous skips the unselectable separator.
304 autofill_popup_controller_
->SelectPreviousLine();
305 EXPECT_EQ(0, autofill_popup_controller_
->selected_line());
308 TEST_F(AutofillPopupControllerUnitTest
, RowWidthWithoutText
) {
309 // Give elements 1 and 3 subtexts and elements 2 and 3 icons, to ensure
310 // all combinations of subtexts and icons.
311 std::vector
<Suggestion
> suggestions
;
312 suggestions
.push_back(Suggestion("", "", "", 0));
313 suggestions
.push_back(Suggestion("", "x", "", 0));
314 suggestions
.push_back(Suggestion("", "", "americanExpressCC", 0));
315 suggestions
.push_back(Suggestion("", "x", "genericCC", 0));
317 // Set up some visible display so the text values are kept.
318 gfx::Display
display(0, gfx::Rect(0, 0, 100, 100));
319 autofill_popup_controller_
->set_display(display
);
321 autofill_popup_controller_
->Show(suggestions
);
324 AutofillPopupView::kEndPadding
* 2 +
325 kPopupBorderThickness
* 2;
326 int subtext_increase
= AutofillPopupView::kNamePadding
;
328 EXPECT_EQ(base_size
, autofill_popup_controller_
->RowWidthWithoutText(0));
329 EXPECT_EQ(base_size
+ subtext_increase
,
330 autofill_popup_controller_
->RowWidthWithoutText(1));
331 EXPECT_EQ(base_size
+ AutofillPopupView::kIconPadding
+
332 ui::ResourceBundle::GetSharedInstance().GetImageNamed(
333 IDR_AUTOFILL_CC_AMEX
).Width(),
334 autofill_popup_controller_
->RowWidthWithoutText(2));
335 EXPECT_EQ(base_size
+ subtext_increase
+ AutofillPopupView::kIconPadding
+
336 ui::ResourceBundle::GetSharedInstance().GetImageNamed(
337 IDR_AUTOFILL_CC_GENERIC
).Width(),
338 autofill_popup_controller_
->RowWidthWithoutText(3));
341 TEST_F(AutofillPopupControllerUnitTest
, UpdateDataListValues
) {
342 std::vector
<Suggestion
> suggestions
;
343 suggestions
.push_back(Suggestion("", "", "", 1));
344 autofill_popup_controller_
->Show(suggestions
);
346 // Add one data list entry.
347 base::string16 value1
= ASCIIToUTF16("data list value 1");
348 std::vector
<base::string16
> data_list_values
;
349 data_list_values
.push_back(value1
);
351 autofill_popup_controller_
->UpdateDataListValues(data_list_values
,
354 ASSERT_EQ(3u, autofill_popup_controller_
->GetLineCount());
356 Suggestion result0
= autofill_popup_controller_
->GetSuggestionAt(0);
357 EXPECT_EQ(value1
, result0
.value
);
358 EXPECT_EQ(value1
, autofill_popup_controller_
->GetElidedValueAt(0));
359 EXPECT_EQ(value1
, result0
.label
);
360 EXPECT_EQ(value1
, autofill_popup_controller_
->GetElidedLabelAt(0));
361 EXPECT_EQ(POPUP_ITEM_ID_DATALIST_ENTRY
, result0
.frontend_id
);
363 Suggestion result1
= autofill_popup_controller_
->GetSuggestionAt(1);
364 EXPECT_EQ(base::string16(), result1
.value
);
365 EXPECT_EQ(base::string16(), result1
.label
);
366 EXPECT_EQ(POPUP_ITEM_ID_SEPARATOR
, result1
.frontend_id
);
368 Suggestion result2
= autofill_popup_controller_
->GetSuggestionAt(2);
369 EXPECT_EQ(base::string16(), result2
.value
);
370 EXPECT_EQ(base::string16(), result2
.label
);
371 EXPECT_EQ(1, result2
.frontend_id
);
373 // Add two data list entries (which should replace the current one).
374 base::string16 value2
= ASCIIToUTF16("data list value 2");
375 data_list_values
.push_back(value2
);
377 autofill_popup_controller_
->UpdateDataListValues(data_list_values
,
379 ASSERT_EQ(4u, autofill_popup_controller_
->GetLineCount());
381 // Original one first, followed by new one, then separator.
382 EXPECT_EQ(value1
, autofill_popup_controller_
->GetSuggestionAt(0).value
);
383 EXPECT_EQ(value1
, autofill_popup_controller_
->GetElidedValueAt(0));
384 EXPECT_EQ(value2
, autofill_popup_controller_
->GetSuggestionAt(1).value
);
385 EXPECT_EQ(value2
, autofill_popup_controller_
->GetElidedValueAt(1));
386 EXPECT_EQ(POPUP_ITEM_ID_SEPARATOR
,
387 autofill_popup_controller_
->GetSuggestionAt(2).frontend_id
);
389 // Clear all data list values.
390 data_list_values
.clear();
391 autofill_popup_controller_
->UpdateDataListValues(data_list_values
,
394 ASSERT_EQ(1u, autofill_popup_controller_
->GetLineCount());
395 EXPECT_EQ(1, autofill_popup_controller_
->GetSuggestionAt(0).frontend_id
);
398 TEST_F(AutofillPopupControllerUnitTest
, PopupsWithOnlyDataLists
) {
399 // Create the popup with a single datalist element.
400 std::vector
<Suggestion
> suggestions
;
401 suggestions
.push_back(Suggestion("", "", "", POPUP_ITEM_ID_DATALIST_ENTRY
));
402 autofill_popup_controller_
->Show(suggestions
);
404 // Replace the datalist element with a new one.
405 base::string16 value1
= ASCIIToUTF16("data list value 1");
406 std::vector
<base::string16
> data_list_values
;
407 data_list_values
.push_back(value1
);
409 autofill_popup_controller_
->UpdateDataListValues(data_list_values
,
412 ASSERT_EQ(1u, autofill_popup_controller_
->GetLineCount());
413 EXPECT_EQ(value1
, autofill_popup_controller_
->GetSuggestionAt(0).value
);
414 EXPECT_EQ(POPUP_ITEM_ID_DATALIST_ENTRY
,
415 autofill_popup_controller_
->GetSuggestionAt(0).frontend_id
);
417 // Clear datalist values and check that the popup becomes hidden.
418 EXPECT_CALL(*autofill_popup_controller_
, Hide());
419 data_list_values
.clear();
420 autofill_popup_controller_
->UpdateDataListValues(data_list_values
,
424 TEST_F(AutofillPopupControllerUnitTest
, GetOrCreate
) {
425 ContentAutofillDriverFactory
* factory
=
426 ContentAutofillDriverFactory::FromWebContents(web_contents());
427 ContentAutofillDriver
* driver
=
428 factory
->DriverForFrame(web_contents()->GetMainFrame());
429 MockAutofillExternalDelegate
delegate(driver
->autofill_manager(), driver
);
431 WeakPtr
<AutofillPopupControllerImpl
> controller
=
432 AutofillPopupControllerImpl::GetOrCreate(
433 WeakPtr
<AutofillPopupControllerImpl
>(), delegate
.GetWeakPtr(), NULL
,
434 NULL
, gfx::RectF(), base::i18n::UNKNOWN_DIRECTION
);
435 EXPECT_TRUE(controller
.get());
439 controller
= AutofillPopupControllerImpl::GetOrCreate(
440 WeakPtr
<AutofillPopupControllerImpl
>(), delegate
.GetWeakPtr(), NULL
, NULL
,
441 gfx::RectF(), base::i18n::UNKNOWN_DIRECTION
);
442 EXPECT_TRUE(controller
.get());
444 WeakPtr
<AutofillPopupControllerImpl
> controller2
=
445 AutofillPopupControllerImpl::GetOrCreate(
446 controller
, delegate
.GetWeakPtr(), NULL
, NULL
, gfx::RectF(),
447 base::i18n::UNKNOWN_DIRECTION
);
448 EXPECT_EQ(controller
.get(), controller2
.get());
451 testing::NiceMock
<TestAutofillPopupController
>* test_controller
=
452 new testing::NiceMock
<TestAutofillPopupController
>(delegate
.GetWeakPtr(),
454 EXPECT_CALL(*test_controller
, Hide());
456 gfx::RectF
bounds(0.f
, 0.f
, 1.f
, 2.f
);
457 base::WeakPtr
<AutofillPopupControllerImpl
> controller3
=
458 AutofillPopupControllerImpl::GetOrCreate(
459 test_controller
->GetWeakPtr(),
460 delegate
.GetWeakPtr(),
464 base::i18n::UNKNOWN_DIRECTION
);
467 static_cast<AutofillPopupController
*>(controller3
.get())->
471 // Hide the test_controller to delete it.
472 test_controller
->DoHide();
475 TEST_F(AutofillPopupControllerUnitTest
, ProperlyResetController
) {
476 std::vector
<Suggestion
> suggestions
;
477 suggestions
.push_back(Suggestion("", "", "", 0));
478 suggestions
.push_back(Suggestion("", "", "", 0));
479 popup_controller()->SetValues(suggestions
);
480 popup_controller()->SetSelectedLine(0);
482 // Now show a new popup with the same controller, but with fewer items.
483 WeakPtr
<AutofillPopupControllerImpl
> controller
=
484 AutofillPopupControllerImpl::GetOrCreate(
485 popup_controller()->GetWeakPtr(), delegate()->GetWeakPtr(), NULL
,
486 NULL
, gfx::RectF(), base::i18n::UNKNOWN_DIRECTION
);
487 EXPECT_NE(0, controller
->selected_line());
488 EXPECT_EQ(0u, controller
->GetLineCount());
491 #if !defined(OS_ANDROID)
492 TEST_F(AutofillPopupControllerUnitTest
, ElideText
) {
493 std::vector
<Suggestion
> suggestions
;
494 suggestions
.push_back(
495 Suggestion("Text that will need to be trimmed",
496 "Label that will be trimmed", "genericCC", 0));
497 suggestions
.push_back(
498 Suggestion("untrimmed", "Untrimmed", "genericCC", 0));
500 // Show the popup once so we can easily generate the size it needs.
501 autofill_popup_controller_
->Show(suggestions
);
503 // Ensure the popup will be too small to display all of the first row.
504 int popup_max_width
=
506 suggestions
[0].value
,
507 autofill_popup_controller_
->GetValueFontListForRow(0)) +
509 suggestions
[0].label
,
510 autofill_popup_controller_
->GetLabelFontList()) - 25;
511 gfx::Rect popup_bounds
= gfx::Rect(0, 0, popup_max_width
, 0);
512 autofill_popup_controller_
->set_display(gfx::Display(0, popup_bounds
));
514 autofill_popup_controller_
->Show(suggestions
);
516 // The first element was long so it should have been trimmed.
517 EXPECT_NE(autofill_popup_controller_
->GetSuggestionAt(0).value
,
518 autofill_popup_controller_
->GetElidedValueAt(0));
519 EXPECT_NE(autofill_popup_controller_
->GetSuggestionAt(0).label
,
520 autofill_popup_controller_
->GetElidedLabelAt(0));
522 // The second element was shorter so it should be unchanged.
523 EXPECT_EQ(autofill_popup_controller_
->GetSuggestionAt(1).value
,
524 autofill_popup_controller_
->GetElidedValueAt(1));
525 EXPECT_EQ(autofill_popup_controller_
->GetSuggestionAt(1).label
,
526 autofill_popup_controller_
->GetElidedLabelAt(1));
530 } // namespace autofill