1 // Copyright 2013 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 <vector>
6
7 #include "base/compiler_specific.h"
8 #include "base/message_loop/message_loop.h"
9 #include "base/strings/string16.h"
10 #include "base/strings/utf_string_conversions.h"
11 #include "components/autofill/core/browser/autofill_manager.h"
12 #include "components/autofill/core/browser/popup_item_ids.h"
13 #include "components/autofill/core/browser/test_autofill_client.h"
14 #include "components/autofill/core/browser/test_autofill_driver.h"
15 #include "components/autofill/core/browser/test_autofill_external_delegate.h"
16 #include "components/autofill/core/common/form_data.h"
17 #include "components/autofill/core/common/form_field_data.h"
18 #include "components/autofill/core/common/password_form_fill_data.h"
19 #include "testing/gmock/include/gmock/gmock.h"
20 #include "testing/gtest/include/gtest/gtest.h"
21 #include "ui/gfx/rect.h"
22
23 using base::ASCIIToUTF16;
24 using testing::_;
25
26 namespace autofill {
27
28 namespace {
29
30 // A constant value to use as the Autofill query ID.
31 const int kQueryId = 5;
32
33 // A constant value to use as an Autofill profile ID.
34 const int kAutofillProfileId = 1;
35
36 class MockAutofillDriver : public TestAutofillDriver {
37 public:
MockAutofillDriver()38 MockAutofillDriver() {}
39 // Mock methods to enable testability.
40 MOCK_METHOD1(RendererShouldAcceptDataListSuggestion,
41 void(const base::string16&));
42 MOCK_METHOD0(RendererShouldClearFilledForm, void());
43 MOCK_METHOD0(RendererShouldClearPreviewedForm, void());
44 MOCK_METHOD1(RendererShouldFillFieldWithValue, void(const base::string16&));
45 MOCK_METHOD1(RendererShouldPreviewFieldWithValue,
46 void(const base::string16&));
47
48 private:
49 DISALLOW_COPY_AND_ASSIGN(MockAutofillDriver);
50 };
51
52 class MockAutofillClient : public autofill::TestAutofillClient {
53 public:
MockAutofillClient()54 MockAutofillClient() {}
55
56 MOCK_METHOD7(ShowAutofillPopup,
57 void(const gfx::RectF& element_bounds,
58 base::i18n::TextDirection text_direction,
59 const std::vector<base::string16>& values,
60 const std::vector<base::string16>& labels,
61 const std::vector<base::string16>& icons,
62 const std::vector<int>& identifiers,
63 base::WeakPtr<AutofillPopupDelegate> delegate));
64
65 MOCK_METHOD2(UpdateAutofillPopupDataListValues,
66 void(const std::vector<base::string16>& values,
67 const std::vector<base::string16>& lables));
68
69 MOCK_METHOD0(HideAutofillPopup, void());
70
71 private:
72 DISALLOW_COPY_AND_ASSIGN(MockAutofillClient);
73 };
74
75 class MockAutofillManager : public AutofillManager {
76 public:
MockAutofillManager(AutofillDriver * driver,MockAutofillClient * client)77 MockAutofillManager(AutofillDriver* driver, MockAutofillClient* client)
78 // Force to use the constructor designated for unit test, but we don't
79 // really need personal_data in this test so we pass a NULL pointer.
80 : AutofillManager(driver, client, NULL) {}
~MockAutofillManager()81 virtual ~MockAutofillManager() {}
82
83 MOCK_METHOD5(FillOrPreviewForm,
84 void(AutofillDriver::RendererFormDataAction action,
85 int query_id,
86 const FormData& form,
87 const FormFieldData& field,
88 int unique_id));
89
90 private:
91 DISALLOW_COPY_AND_ASSIGN(MockAutofillManager);
92 };
93
94 } // namespace
95
96 class AutofillExternalDelegateUnitTest : public testing::Test {
97 protected:
SetUp()98 virtual void SetUp() OVERRIDE {
99 autofill_driver_.reset(new MockAutofillDriver());
100 autofill_manager_.reset(
101 new MockAutofillManager(autofill_driver_.get(), &autofill_client_));
102 external_delegate_.reset(
103 new AutofillExternalDelegate(
104 autofill_manager_.get(), autofill_driver_.get()));
105 }
106
TearDown()107 virtual void TearDown() OVERRIDE {
108 // Order of destruction is important as AutofillManager relies on
109 // PersonalDataManager to be around when it gets destroyed.
110 autofill_manager_.reset();
111 external_delegate_.reset();
112 autofill_driver_.reset();
113 }
114
115 // Issue an OnQuery call with the given |query_id|.
IssueOnQuery(int query_id)116 void IssueOnQuery(int query_id) {
117 const FormData form;
118 FormFieldData field;
119 field.is_focusable = true;
120 field.should_autocomplete = true;
121 const gfx::RectF element_bounds;
122
123 external_delegate_->OnQuery(query_id, form, field, element_bounds, true);
124 }
125
126 MockAutofillClient autofill_client_;
127 scoped_ptr<MockAutofillDriver> autofill_driver_;
128 scoped_ptr<MockAutofillManager> autofill_manager_;
129 scoped_ptr<AutofillExternalDelegate> external_delegate_;
130
131 base::MessageLoop message_loop_;
132 };
133
134 // Test that our external delegate called the virtual methods at the right time.
TEST_F(AutofillExternalDelegateUnitTest,TestExternalDelegateVirtualCalls)135 TEST_F(AutofillExternalDelegateUnitTest, TestExternalDelegateVirtualCalls) {
136 IssueOnQuery(kQueryId);
137
138 // The enums must be cast to ints to prevent compile errors on linux_rel.
139 EXPECT_CALL(
140 autofill_client_,
141 ShowAutofillPopup(_,
142 _,
143 _,
144 _,
145 _,
146 testing::ElementsAre(
147 kAutofillProfileId,
148 static_cast<int>(POPUP_ITEM_ID_SEPARATOR),
149 static_cast<int>(POPUP_ITEM_ID_AUTOFILL_OPTIONS)),
150 _));
151
152 // This should call ShowAutofillPopup.
153 std::vector<base::string16> autofill_item;
154 autofill_item.push_back(base::string16());
155 std::vector<int> autofill_ids;
156 autofill_ids.push_back(kAutofillProfileId);
157 external_delegate_->OnSuggestionsReturned(kQueryId,
158 autofill_item,
159 autofill_item,
160 autofill_item,
161 autofill_ids);
162
163 EXPECT_CALL(*autofill_manager_,
164 FillOrPreviewForm(
165 AutofillDriver::FORM_DATA_ACTION_FILL, _, _, _, _));
166 EXPECT_CALL(autofill_client_, HideAutofillPopup());
167
168 // This should trigger a call to hide the popup since we've selected an
169 // option.
170 external_delegate_->DidAcceptSuggestion(autofill_item[0], autofill_ids[0]);
171 }
172
173 // Test that data list elements for a node will appear in the Autofill popup.
TEST_F(AutofillExternalDelegateUnitTest,ExternalDelegateDataList)174 TEST_F(AutofillExternalDelegateUnitTest, ExternalDelegateDataList) {
175 IssueOnQuery(kQueryId);
176
177 std::vector<base::string16> data_list_items;
178 data_list_items.push_back(base::string16());
179
180 external_delegate_->SetCurrentDataListValues(data_list_items,
181 data_list_items);
182
183 // The enums must be cast to ints to prevent compile errors on linux_rel.
184 EXPECT_CALL(
185 autofill_client_,
186 ShowAutofillPopup(_,
187 _,
188 _,
189 _,
190 _,
191 testing::ElementsAre(
192 static_cast<int>(POPUP_ITEM_ID_DATALIST_ENTRY),
193 static_cast<int>(POPUP_ITEM_ID_SEPARATOR),
194 kAutofillProfileId,
195 static_cast<int>(POPUP_ITEM_ID_SEPARATOR),
196 static_cast<int>(POPUP_ITEM_ID_AUTOFILL_OPTIONS)),
197 _));
198
199 // This should call ShowAutofillPopup.
200 std::vector<base::string16> autofill_item;
201 autofill_item.push_back(base::string16());
202 std::vector<int> autofill_ids;
203 autofill_ids.push_back(kAutofillProfileId);
204 external_delegate_->OnSuggestionsReturned(kQueryId,
205 autofill_item,
206 autofill_item,
207 autofill_item,
208 autofill_ids);
209
210 // Try calling OnSuggestionsReturned with no Autofill values and ensure
211 // the datalist items are still shown.
212 // The enum must be cast to an int to prevent compile errors on linux_rel.
213 EXPECT_CALL(
214 autofill_client_,
215 ShowAutofillPopup(
216 _,
217 _,
218 _,
219 _,
220 _,
221 testing::ElementsAre(static_cast<int>(POPUP_ITEM_ID_DATALIST_ENTRY)),
222 _));
223
224 autofill_item = std::vector<base::string16>();
225 autofill_ids = std::vector<int>();
226 external_delegate_->OnSuggestionsReturned(kQueryId,
227 autofill_item,
228 autofill_item,
229 autofill_item,
230 autofill_ids);
231 }
232
233 // Test that datalist values can get updated while a popup is showing.
TEST_F(AutofillExternalDelegateUnitTest,UpdateDataListWhileShowingPopup)234 TEST_F(AutofillExternalDelegateUnitTest, UpdateDataListWhileShowingPopup) {
235 IssueOnQuery(kQueryId);
236
237 EXPECT_CALL(autofill_client_, ShowAutofillPopup(_, _, _, _, _, _, _))
238 .Times(0);
239
240 // Make sure just setting the data list values doesn't cause the popup to
241 // appear.
242 std::vector<base::string16> data_list_items;
243 data_list_items.push_back(base::string16());
244
245 external_delegate_->SetCurrentDataListValues(data_list_items,
246 data_list_items);
247
248 // The enums must be cast to ints to prevent compile errors on linux_rel.
249 EXPECT_CALL(
250 autofill_client_,
251 ShowAutofillPopup(_,
252 _,
253 _,
254 _,
255 _,
256 testing::ElementsAre(
257 static_cast<int>(POPUP_ITEM_ID_DATALIST_ENTRY),
258 static_cast<int>(POPUP_ITEM_ID_SEPARATOR),
259 kAutofillProfileId,
260 static_cast<int>(POPUP_ITEM_ID_SEPARATOR),
261 static_cast<int>(POPUP_ITEM_ID_AUTOFILL_OPTIONS)),
262 _));
263
264 // Ensure the popup is displayed.
265 std::vector<base::string16> autofill_item;
266 autofill_item.push_back(base::string16());
267 std::vector<int> autofill_ids;
268 autofill_ids.push_back(kAutofillProfileId);
269 external_delegate_->OnSuggestionsReturned(kQueryId,
270 autofill_item,
271 autofill_item,
272 autofill_item,
273 autofill_ids);
274
275 // This would normally get called from ShowAutofillPopup, but it is mocked so
276 // we need to call OnPopupShown ourselves.
277 external_delegate_->OnPopupShown();
278
279 // Update the current data list and ensure the popup is updated.
280 data_list_items.push_back(base::string16());
281
282 // The enums must be cast to ints to prevent compile errors on linux_rel.
283 EXPECT_CALL(
284 autofill_client_,
285 UpdateAutofillPopupDataListValues(data_list_items, data_list_items));
286
287 external_delegate_->SetCurrentDataListValues(data_list_items,
288 data_list_items);
289 }
290
291 // Test that the Autofill popup is able to display warnings explaining why
292 // Autofill is disabled for a website.
293 // Regression test for http://crbug.com/247880
TEST_F(AutofillExternalDelegateUnitTest,AutofillWarnings)294 TEST_F(AutofillExternalDelegateUnitTest, AutofillWarnings) {
295 IssueOnQuery(kQueryId);
296
297 // The enums must be cast to ints to prevent compile errors on linux_rel.
298 EXPECT_CALL(
299 autofill_client_,
300 ShowAutofillPopup(
301 _,
302 _,
303 _,
304 _,
305 _,
306 testing::ElementsAre(static_cast<int>(POPUP_ITEM_ID_WARNING_MESSAGE)),
307 _));
308
309 // This should call ShowAutofillPopup.
310 std::vector<base::string16> autofill_item;
311 autofill_item.push_back(base::string16());
312 std::vector<int> autofill_ids;
313 autofill_ids.push_back(POPUP_ITEM_ID_WARNING_MESSAGE);
314 external_delegate_->OnSuggestionsReturned(kQueryId,
315 autofill_item,
316 autofill_item,
317 autofill_item,
318 autofill_ids);
319 }
320
321 // Test that the Autofill popup doesn't display a warning explaining why
322 // Autofill is disabled for a website when there are no Autofill suggestions.
323 // Regression test for http://crbug.com/105636
TEST_F(AutofillExternalDelegateUnitTest,NoAutofillWarningsWithoutSuggestions)324 TEST_F(AutofillExternalDelegateUnitTest, NoAutofillWarningsWithoutSuggestions) {
325 const FormData form;
326 FormFieldData field;
327 field.is_focusable = true;
328 field.should_autocomplete = false;
329 const gfx::RectF element_bounds;
330
331 external_delegate_->OnQuery(kQueryId, form, field, element_bounds, true);
332
333 EXPECT_CALL(autofill_client_, ShowAutofillPopup(_, _, _, _, _, _, _))
334 .Times(0);
335 EXPECT_CALL(autofill_client_, HideAutofillPopup()).Times(1);
336
337 // This should not call ShowAutofillPopup.
338 std::vector<base::string16> autofill_item;
339 autofill_item.push_back(base::string16());
340 std::vector<int> autofill_ids;
341 autofill_ids.push_back(POPUP_ITEM_ID_AUTOCOMPLETE_ENTRY);
342 external_delegate_->OnSuggestionsReturned(kQueryId,
343 autofill_item,
344 autofill_item,
345 autofill_item,
346 autofill_ids);
347 }
348
349 // Test that the Autofill delegate doesn't try and fill a form with a
350 // negative unique id.
TEST_F(AutofillExternalDelegateUnitTest,ExternalDelegateInvalidUniqueId)351 TEST_F(AutofillExternalDelegateUnitTest, ExternalDelegateInvalidUniqueId) {
352 // Ensure it doesn't try to preview the negative id.
353 EXPECT_CALL(*autofill_manager_, FillOrPreviewForm(_, _, _, _, _)).Times(0);
354 EXPECT_CALL(*autofill_driver_, RendererShouldClearPreviewedForm()).Times(1);
355 external_delegate_->DidSelectSuggestion(base::string16(), -1);
356
357 // Ensure it doesn't try to fill the form in with the negative id.
358 EXPECT_CALL(autofill_client_, HideAutofillPopup());
359 EXPECT_CALL(*autofill_manager_, FillOrPreviewForm(_, _, _, _, _)).Times(0);
360 external_delegate_->DidAcceptSuggestion(base::string16(), -1);
361 }
362
363 // Test that the ClearPreview call is only sent if the form was being previewed
364 // (i.e. it isn't autofilling a password).
TEST_F(AutofillExternalDelegateUnitTest,ExternalDelegateClearPreviewedForm)365 TEST_F(AutofillExternalDelegateUnitTest, ExternalDelegateClearPreviewedForm) {
366 // Ensure selecting a new password entries or Autofill entries will
367 // cause any previews to get cleared.
368 EXPECT_CALL(*autofill_driver_, RendererShouldClearPreviewedForm()).Times(1);
369 external_delegate_->DidSelectSuggestion(ASCIIToUTF16("baz foo"),
370 POPUP_ITEM_ID_PASSWORD_ENTRY);
371 EXPECT_CALL(*autofill_driver_, RendererShouldClearPreviewedForm()).Times(1);
372 EXPECT_CALL(*autofill_manager_,
373 FillOrPreviewForm(
374 AutofillDriver::FORM_DATA_ACTION_PREVIEW, _, _, _, _));
375 external_delegate_->DidSelectSuggestion(ASCIIToUTF16("baz foo"), 1);
376
377 // Ensure selecting an autocomplete entry will cause any previews to
378 // get cleared.
379 EXPECT_CALL(*autofill_driver_, RendererShouldClearPreviewedForm()).Times(1);
380 EXPECT_CALL(*autofill_driver_, RendererShouldPreviewFieldWithValue(
381 ASCIIToUTF16("baz foo")));
382 external_delegate_->DidSelectSuggestion(ASCIIToUTF16("baz foo"),
383 POPUP_ITEM_ID_AUTOCOMPLETE_ENTRY);
384 }
385
386 // Test that the popup is hidden once we are done editing the autofill field.
TEST_F(AutofillExternalDelegateUnitTest,ExternalDelegateHidePopupAfterEditing)387 TEST_F(AutofillExternalDelegateUnitTest,
388 ExternalDelegateHidePopupAfterEditing) {
389 EXPECT_CALL(autofill_client_, ShowAutofillPopup(_, _, _, _, _, _, _));
390 autofill::GenerateTestAutofillPopup(external_delegate_.get());
391
392 EXPECT_CALL(autofill_client_, HideAutofillPopup());
393 external_delegate_->DidEndTextFieldEditing();
394 }
395
396 // Test that the driver is directed to accept the data list after being notified
397 // that the user accepted the data list suggestion.
TEST_F(AutofillExternalDelegateUnitTest,ExternalDelegateAcceptSuggestion)398 TEST_F(AutofillExternalDelegateUnitTest, ExternalDelegateAcceptSuggestion) {
399 EXPECT_CALL(autofill_client_, HideAutofillPopup());
400 base::string16 dummy_string(ASCIIToUTF16("baz qux"));
401 EXPECT_CALL(*autofill_driver_,
402 RendererShouldAcceptDataListSuggestion(dummy_string));
403 external_delegate_->DidAcceptSuggestion(dummy_string,
404 POPUP_ITEM_ID_DATALIST_ENTRY);
405 }
406
407 // Test that the driver is directed to clear the form after being notified that
408 // the user accepted the suggestion to clear the form.
TEST_F(AutofillExternalDelegateUnitTest,ExternalDelegateClearForm)409 TEST_F(AutofillExternalDelegateUnitTest, ExternalDelegateClearForm) {
410 EXPECT_CALL(autofill_client_, HideAutofillPopup());
411 EXPECT_CALL(*autofill_driver_, RendererShouldClearFilledForm());
412
413 external_delegate_->DidAcceptSuggestion(base::string16(),
414 POPUP_ITEM_ID_CLEAR_FORM);
415 }
416
TEST_F(AutofillExternalDelegateUnitTest,ExternalDelegateHideWarning)417 TEST_F(AutofillExternalDelegateUnitTest, ExternalDelegateHideWarning) {
418 // Set up a field that shouldn't get autocompleted or display warnings.
419 const FormData form;
420 FormFieldData field;
421 field.is_focusable = true;
422 field.should_autocomplete = false;
423 const gfx::RectF element_bounds;
424
425 external_delegate_->OnQuery(kQueryId, form, field, element_bounds, false);
426
427 std::vector<base::string16> autofill_items;
428 autofill_items.push_back(base::string16());
429 std::vector<int> autofill_ids;
430 autofill_ids.push_back(POPUP_ITEM_ID_AUTOCOMPLETE_ENTRY);
431
432 // Ensure the popup tries to hide itself, since it is not allowed to show
433 // anything.
434 EXPECT_CALL(autofill_client_, HideAutofillPopup());
435
436 external_delegate_->OnSuggestionsReturned(kQueryId,
437 autofill_items,
438 autofill_items,
439 autofill_items,
440 autofill_ids);
441 }
442
TEST_F(AutofillExternalDelegateUnitTest,ExternalDelegateFillFieldWithValue)443 TEST_F(AutofillExternalDelegateUnitTest, ExternalDelegateFillFieldWithValue) {
444 EXPECT_CALL(autofill_client_, HideAutofillPopup());
445 base::string16 dummy_string(ASCIIToUTF16("baz foo"));
446 EXPECT_CALL(*autofill_driver_,
447 RendererShouldFillFieldWithValue(dummy_string));
448 external_delegate_->DidAcceptSuggestion(dummy_string,
449 POPUP_ITEM_ID_AUTOCOMPLETE_ENTRY);
450 }
451
452 } // namespace autofill
453