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 void RemoveSuggestion(const base::string16
& value
, int identifier
) override
{}
51 void ClearPreviewedForm() override
{}
52 base::WeakPtr
<AutofillExternalDelegate
> GetWeakPtr() {
53 return AutofillExternalDelegate::GetWeakPtr();
57 class MockAutofillClient
: public autofill::TestAutofillClient
{
59 MockAutofillClient() : prefs_(autofill::test::PrefServiceForTesting()) {}
60 ~MockAutofillClient() override
{}
62 PrefService
* GetPrefs() override
{ return prefs_
.get(); }
65 scoped_ptr
<PrefService
> prefs_
;
67 DISALLOW_COPY_AND_ASSIGN(MockAutofillClient
);
70 class TestAutofillPopupController
: public AutofillPopupControllerImpl
{
72 explicit TestAutofillPopupController(
73 base::WeakPtr
<AutofillExternalDelegate
> external_delegate
,
74 const gfx::RectF
& element_bounds
)
75 : AutofillPopupControllerImpl(
76 external_delegate
, NULL
, NULL
, element_bounds
,
77 base::i18n::UNKNOWN_DIRECTION
),
78 test_controller_common_(new TestPopupControllerCommon(element_bounds
)) {
79 controller_common_
.reset(test_controller_common_
);
81 virtual ~TestAutofillPopupController() {}
83 void set_display(const gfx::Display
& display
) {
84 test_controller_common_
->set_display(display
);
87 // Making protected functions public for testing
88 using AutofillPopupControllerImpl::SetPopupBounds
;
89 using AutofillPopupControllerImpl::GetLineCount
;
90 using AutofillPopupControllerImpl::GetSuggestionAt
;
91 using AutofillPopupControllerImpl::GetElidedValueAt
;
92 using AutofillPopupControllerImpl::GetElidedLabelAt
;
93 using AutofillPopupControllerImpl::selected_line
;
94 using AutofillPopupControllerImpl::SetSelectedLine
;
95 using AutofillPopupControllerImpl::SelectNextLine
;
96 using AutofillPopupControllerImpl::SelectPreviousLine
;
97 using AutofillPopupControllerImpl::RemoveSelectedLine
;
98 using AutofillPopupControllerImpl::popup_bounds
;
99 using AutofillPopupControllerImpl::element_bounds
;
100 #if !defined(OS_ANDROID)
101 using AutofillPopupControllerImpl::GetValueFontListForRow
;
102 using AutofillPopupControllerImpl::GetLabelFontList
;
103 using AutofillPopupControllerImpl::RowWidthWithoutText
;
105 using AutofillPopupControllerImpl::SetValues
;
106 using AutofillPopupControllerImpl::GetDesiredPopupWidth
;
107 using AutofillPopupControllerImpl::GetDesiredPopupHeight
;
108 using AutofillPopupControllerImpl::GetWeakPtr
;
109 MOCK_METHOD1(InvalidateRow
, void(size_t));
110 MOCK_METHOD0(UpdateBoundsAndRedrawPopup
, void());
111 MOCK_METHOD0(Hide
, void());
114 AutofillPopupControllerImpl::Hide();
118 virtual void ShowView() override
{}
120 TestPopupControllerCommon
* test_controller_common_
;
125 class AutofillPopupControllerUnitTest
: public ChromeRenderViewHostTestHarness
{
127 AutofillPopupControllerUnitTest()
128 : autofill_client_(new MockAutofillClient()),
129 autofill_popup_controller_(NULL
) {}
130 ~AutofillPopupControllerUnitTest() override
{}
132 void SetUp() override
{
133 ChromeRenderViewHostTestHarness::SetUp();
135 ContentAutofillDriverFactory::CreateForWebContentsAndDelegate(
136 web_contents(), autofill_client_
.get(), "en-US",
137 AutofillManager::ENABLE_AUTOFILL_DOWNLOAD_MANAGER
);
138 ContentAutofillDriverFactory
* factory
=
139 ContentAutofillDriverFactory::FromWebContents(web_contents());
140 ContentAutofillDriver
* driver
=
141 factory
->DriverForFrame(web_contents()->GetMainFrame());
142 external_delegate_
.reset(
143 new NiceMock
<MockAutofillExternalDelegate
>(
144 driver
->autofill_manager(),
147 autofill_popup_controller_
=
148 new testing::NiceMock
<TestAutofillPopupController
>(
149 external_delegate_
->GetWeakPtr(),gfx::Rect());
152 void TearDown() override
{
153 // This will make sure the controller and the view (if any) are both
155 if (autofill_popup_controller_
)
156 autofill_popup_controller_
->DoHide();
158 external_delegate_
.reset();
159 ChromeRenderViewHostTestHarness::TearDown();
162 TestAutofillPopupController
* popup_controller() {
163 return autofill_popup_controller_
;
166 MockAutofillExternalDelegate
* delegate() {
167 return external_delegate_
.get();
171 scoped_ptr
<MockAutofillClient
> autofill_client_
;
172 scoped_ptr
<NiceMock
<MockAutofillExternalDelegate
> > external_delegate_
;
173 testing::NiceMock
<TestAutofillPopupController
>* autofill_popup_controller_
;
176 TEST_F(AutofillPopupControllerUnitTest
, SetBounds
) {
177 // Ensure the popup size can be set and causes a redraw.
178 gfx::Rect
popup_bounds(10, 10, 100, 100);
180 EXPECT_CALL(*autofill_popup_controller_
,
181 UpdateBoundsAndRedrawPopup());
183 popup_controller()->SetPopupBounds(popup_bounds
);
185 EXPECT_EQ(popup_bounds
, popup_controller()->popup_bounds());
188 TEST_F(AutofillPopupControllerUnitTest
, ChangeSelectedLine
) {
190 std::vector
<Suggestion
> suggestions
;
191 suggestions
.push_back(Suggestion("", "", "", 0));
192 suggestions
.push_back(Suggestion("", "", "", 0));
193 autofill_popup_controller_
->Show(suggestions
);
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
199 static_cast<int>(autofill_popup_controller_
->GetLineCount()));
201 // Test wrapping before the front.
202 autofill_popup_controller_
->SelectPreviousLine();
203 EXPECT_EQ(static_cast<int>(
204 autofill_popup_controller_
->GetLineCount() - 1),
205 autofill_popup_controller_
->selected_line());
207 // Test wrapping after the end.
208 autofill_popup_controller_
->SelectNextLine();
209 EXPECT_EQ(0, autofill_popup_controller_
->selected_line());
212 TEST_F(AutofillPopupControllerUnitTest
, RedrawSelectedLine
) {
214 std::vector
<Suggestion
> suggestions
;
215 suggestions
.push_back(Suggestion("", "", "", 0));
216 suggestions
.push_back(Suggestion("", "", "", 0));
217 autofill_popup_controller_
->Show(suggestions
);
219 // Make sure that when a new line is selected, it is invalidated so it can
220 // be updated to show it is selected.
221 int selected_line
= 0;
222 EXPECT_CALL(*autofill_popup_controller_
, InvalidateRow(selected_line
));
223 autofill_popup_controller_
->SetSelectedLine(selected_line
);
225 // Ensure that the row isn't invalidated if it didn't change.
226 EXPECT_CALL(*autofill_popup_controller_
,
227 InvalidateRow(selected_line
)).Times(0);
228 autofill_popup_controller_
->SetSelectedLine(selected_line
);
230 // Change back to no selection.
231 EXPECT_CALL(*autofill_popup_controller_
, InvalidateRow(selected_line
));
232 autofill_popup_controller_
->SetSelectedLine(-1);
235 TEST_F(AutofillPopupControllerUnitTest
, RemoveLine
) {
237 std::vector
<Suggestion
> suggestions
;
238 suggestions
.push_back(Suggestion("", "", "", 1));
239 suggestions
.push_back(Suggestion("", "", "", 1));
240 suggestions
.push_back(Suggestion("", "", "", POPUP_ITEM_ID_AUTOFILL_OPTIONS
));
241 autofill_popup_controller_
->Show(suggestions
);
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());
248 // No line is selected so the removal should fail.
249 EXPECT_FALSE(autofill_popup_controller_
->RemoveSelectedLine());
251 // Try to remove the last entry and ensure it fails (it is an option).
252 autofill_popup_controller_
->SetSelectedLine(
253 autofill_popup_controller_
->GetLineCount() - 1);
254 EXPECT_FALSE(autofill_popup_controller_
->RemoveSelectedLine());
255 EXPECT_LE(0, autofill_popup_controller_
->selected_line());
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(),
434 NULL
, NULL
, gfx::Rect(), base::i18n::UNKNOWN_DIRECTION
);
435 EXPECT_TRUE(controller
.get());
439 controller
= AutofillPopupControllerImpl::GetOrCreate(
440 WeakPtr
<AutofillPopupControllerImpl
>(), delegate
.GetWeakPtr(),
441 NULL
, NULL
, gfx::Rect(), base::i18n::UNKNOWN_DIRECTION
);
442 EXPECT_TRUE(controller
.get());
444 WeakPtr
<AutofillPopupControllerImpl
> controller2
=
445 AutofillPopupControllerImpl::GetOrCreate(controller
,
446 delegate
.GetWeakPtr(),
450 base::i18n::UNKNOWN_DIRECTION
);
451 EXPECT_EQ(controller
.get(), controller2
.get());
454 testing::NiceMock
<TestAutofillPopupController
>* test_controller
=
455 new testing::NiceMock
<TestAutofillPopupController
>(delegate
.GetWeakPtr(),
457 EXPECT_CALL(*test_controller
, Hide());
459 gfx::RectF
bounds(0.f
, 0.f
, 1.f
, 2.f
);
460 base::WeakPtr
<AutofillPopupControllerImpl
> controller3
=
461 AutofillPopupControllerImpl::GetOrCreate(
462 test_controller
->GetWeakPtr(),
463 delegate
.GetWeakPtr(),
467 base::i18n::UNKNOWN_DIRECTION
);
470 static_cast<AutofillPopupController
*>(controller3
.get())->
474 // Hide the test_controller to delete it.
475 test_controller
->DoHide();
478 TEST_F(AutofillPopupControllerUnitTest
, ProperlyResetController
) {
479 std::vector
<Suggestion
> suggestions
;
480 suggestions
.push_back(Suggestion("", "", "", 0));
481 suggestions
.push_back(Suggestion("", "", "", 0));
482 popup_controller()->SetValues(suggestions
);
483 popup_controller()->SetSelectedLine(0);
485 // Now show a new popup with the same controller, but with fewer items.
486 WeakPtr
<AutofillPopupControllerImpl
> controller
=
487 AutofillPopupControllerImpl::GetOrCreate(
488 popup_controller()->GetWeakPtr(),
489 delegate()->GetWeakPtr(),
493 base::i18n::UNKNOWN_DIRECTION
);
494 EXPECT_NE(0, controller
->selected_line());
495 EXPECT_EQ(0u, controller
->GetLineCount());
498 #if !defined(OS_ANDROID)
499 TEST_F(AutofillPopupControllerUnitTest
, ElideText
) {
500 std::vector
<Suggestion
> suggestions
;
501 suggestions
.push_back(
502 Suggestion("Text that will need to be trimmed",
503 "Label that will be trimmed", "genericCC", 0));
504 suggestions
.push_back(
505 Suggestion("untrimmed", "Untrimmed", "genericCC", 0));
507 // Show the popup once so we can easily generate the size it needs.
508 autofill_popup_controller_
->Show(suggestions
);
510 // Ensure the popup will be too small to display all of the first row.
511 int popup_max_width
=
513 suggestions
[0].value
,
514 autofill_popup_controller_
->GetValueFontListForRow(0)) +
516 suggestions
[0].label
,
517 autofill_popup_controller_
->GetLabelFontList()) - 25;
518 gfx::Rect popup_bounds
= gfx::Rect(0, 0, popup_max_width
, 0);
519 autofill_popup_controller_
->set_display(gfx::Display(0, popup_bounds
));
521 autofill_popup_controller_
->Show(suggestions
);
523 // The first element was long so it should have been trimmed.
524 EXPECT_NE(autofill_popup_controller_
->GetSuggestionAt(0).value
,
525 autofill_popup_controller_
->GetElidedValueAt(0));
526 EXPECT_NE(autofill_popup_controller_
->GetSuggestionAt(0).label
,
527 autofill_popup_controller_
->GetElidedLabelAt(0));
529 // The second element was shorter so it should be unchanged.
530 EXPECT_EQ(autofill_popup_controller_
->GetSuggestionAt(1).value
,
531 autofill_popup_controller_
->GetElidedValueAt(1));
532 EXPECT_EQ(autofill_popup_controller_
->GetSuggestionAt(1).label
,
533 autofill_popup_controller_
->GetElidedLabelAt(1));
537 } // namespace autofill