autofill_interactive_uitest.cc revision c2e0dbddbe15c98d52c4786dac06cb8952a8ae6d
1// Copyright (c) 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 "base/memory/ref_counted.h"
6#include "base/memory/scoped_ptr.h"
7#include "chrome/browser/autofill/personal_data_manager_factory.h"
8#include "chrome/browser/infobars/confirm_infobar_delegate.h"
9#include "chrome/browser/ui/browser.h"
10#include "chrome/browser/ui/tabs/tab_strip_model.h"
11#include "chrome/common/chrome_notification_types.h"
12#include "chrome/test/base/in_process_browser_test.h"
13#include "chrome/test/base/ui_test_utils.h"
14#include "components/autofill/browser/autofill_common_test.h"
15#include "components/autofill/browser/autofill_external_delegate.h"
16#include "components/autofill/browser/autofill_manager.h"
17#include "components/autofill/browser/autofill_manager_test_delegate.h"
18#include "components/autofill/browser/autofill_profile.h"
19#include "components/autofill/browser/personal_data_manager.h"
20#include "components/autofill/browser/personal_data_manager_observer.h"
21#include "content/public/browser/notification_observer.h"
22#include "content/public/browser/notification_registrar.h"
23#include "content/public/browser/notification_service.h"
24#include "content/public/browser/render_view_host.h"
25#include "content/public/browser/web_contents.h"
26#include "content/public/test/browser_test_utils.h"
27#include "content/public/test/test_utils.h"
28#include "ui/base/keycodes/keyboard_codes.h"
29
30using content::RenderViewHost;
31
32// TODO(csharp): Most of this file was just copied from autofill_browsertests.cc
33// The repeated code should be moved into a helper file, instead of being
34// repeated.
35
36namespace autofill {
37
38static const char* kDataURIPrefix = "data:text/html;charset=utf-8,";
39static const char* kTestFormString =
40    "<form action=\"http://www.example.com/\" method=\"POST\">"
41    "<label for=\"firstname\">First name:</label>"
42    " <input type=\"text\" id=\"firstname\""
43    "        onFocus=\"domAutomationController.send(true)\"><br>"
44    "<label for=\"lastname\">Last name:</label>"
45    " <input type=\"text\" id=\"lastname\"><br>"
46    "<label for=\"address1\">Address line 1:</label>"
47    " <input type=\"text\" id=\"address1\"><br>"
48    "<label for=\"address2\">Address line 2:</label>"
49    " <input type=\"text\" id=\"address2\"><br>"
50    "<label for=\"city\">City:</label>"
51    " <input type=\"text\" id=\"city\"><br>"
52    "<label for=\"state\">State:</label>"
53    " <select id=\"state\">"
54    " <option value=\"\" selected=\"yes\">--</option>"
55    " <option value=\"CA\">California</option>"
56    " <option value=\"TX\">Texas</option>"
57    " </select><br>"
58    "<label for=\"zip\">ZIP code:</label>"
59    " <input type=\"text\" id=\"zip\"><br>"
60    "<label for=\"country\">Country:</label>"
61    " <select id=\"country\">"
62    " <option value=\"\" selected=\"yes\">--</option>"
63    " <option value=\"CA\">Canada</option>"
64    " <option value=\"US\">United States</option>"
65    " </select><br>"
66    "<label for=\"phone\">Phone number:</label>"
67    " <input type=\"text\" id=\"phone\"><br>"
68    "</form>";
69
70class AutofillManagerTestDelegateImpl
71    : public autofill::AutofillManagerTestDelegate {
72 public:
73  AutofillManagerTestDelegateImpl() {}
74
75  virtual void DidPreviewFormData() OVERRIDE {
76    loop_runner_->Quit();
77  }
78
79  virtual void DidFillFormData() OVERRIDE {
80    loop_runner_->Quit();
81  }
82
83  virtual void DidShowSuggestions() OVERRIDE {
84    loop_runner_->Quit();
85  }
86
87  void Reset() {
88    loop_runner_ = new content::MessageLoopRunner();
89  }
90
91  void Wait() {
92    loop_runner_->Run();
93  }
94
95 private:
96  scoped_refptr<content::MessageLoopRunner> loop_runner_;
97
98  DISALLOW_COPY_AND_ASSIGN(AutofillManagerTestDelegateImpl);
99};
100
101class WindowedPersonalDataManagerObserver
102    : public PersonalDataManagerObserver,
103      public content::NotificationObserver {
104 public:
105  explicit WindowedPersonalDataManagerObserver(Browser* browser)
106      : alerted_(false),
107        has_run_message_loop_(false),
108        browser_(browser),
109        infobar_service_(NULL) {
110    PersonalDataManagerFactory::GetForProfile(browser_->profile())->
111        AddObserver(this);
112    registrar_.Add(this, chrome::NOTIFICATION_TAB_CONTENTS_INFOBAR_ADDED,
113                   content::NotificationService::AllSources());
114  }
115
116  virtual ~WindowedPersonalDataManagerObserver() {
117    if (infobar_service_ && infobar_service_->infobar_count() > 0)
118      infobar_service_->RemoveInfoBar(infobar_service_->infobar_at(0));
119  }
120
121  void Wait() {
122    if (!alerted_) {
123      has_run_message_loop_ = true;
124      content::RunMessageLoop();
125    }
126    PersonalDataManagerFactory::GetForProfile(browser_->profile())->
127        RemoveObserver(this);
128  }
129
130  // PersonalDataManagerObserver:
131  virtual void OnPersonalDataChanged() OVERRIDE {
132    if (has_run_message_loop_) {
133      MessageLoopForUI::current()->Quit();
134      has_run_message_loop_ = false;
135    }
136    alerted_ = true;
137  }
138
139  virtual void OnInsufficientFormData() OVERRIDE {
140    OnPersonalDataChanged();
141  }
142
143  // content::NotificationObserver:
144  virtual void Observe(int type,
145                       const content::NotificationSource& source,
146                       const content::NotificationDetails& details) OVERRIDE {
147    // Accept in the infobar.
148    infobar_service_ = InfoBarService::FromWebContents(
149        browser_->tab_strip_model()->GetActiveWebContents());
150    InfoBarDelegate* infobar = infobar_service_->infobar_at(0);
151
152    ConfirmInfoBarDelegate* confirm_infobar =
153        infobar->AsConfirmInfoBarDelegate();
154    confirm_infobar->Accept();
155  }
156
157 private:
158  bool alerted_;
159  bool has_run_message_loop_;
160  Browser* browser_;
161  content::NotificationRegistrar registrar_;
162  InfoBarService* infobar_service_;
163};
164
165class TestAutofillExternalDelegate : public AutofillExternalDelegate {
166 public:
167  TestAutofillExternalDelegate(content::WebContents* web_contents,
168                               AutofillManager* autofill_manager)
169      : AutofillExternalDelegate(web_contents, autofill_manager),
170        keyboard_listener_(NULL) {
171  }
172  virtual ~TestAutofillExternalDelegate() {}
173
174  virtual void OnPopupShown(content::KeyboardListener* listener) OVERRIDE {
175    AutofillExternalDelegate::OnPopupShown(listener);
176    keyboard_listener_ = listener;
177  }
178
179  virtual void OnPopupHidden(content::KeyboardListener* listener) OVERRIDE {
180    keyboard_listener_ = NULL;
181    AutofillExternalDelegate::OnPopupHidden(listener);
182  }
183
184  content::KeyboardListener* keyboard_listener() {
185    return keyboard_listener_;
186  }
187
188 private:
189  // The popup that is currently registered as a keyboard listener, or NULL if
190  // there is none.
191  content::KeyboardListener* keyboard_listener_;
192
193  DISALLOW_COPY_AND_ASSIGN(TestAutofillExternalDelegate);
194};
195
196class AutofillInteractiveTest : public InProcessBrowserTest {
197 protected:
198  AutofillInteractiveTest() {}
199
200  virtual void SetUpOnMainThread() OVERRIDE {
201    // Don't want Keychain coming up on Mac.
202    test::DisableSystemServices(browser()->profile());
203
204    // When testing the native UI, hook up a test external delegate, which
205    // allows us to forward keyboard events to the popup directly.
206    content::WebContents* web_contents =
207        browser()->tab_strip_model()->GetActiveWebContents();
208    AutofillManager* autofill_manager =
209        AutofillManager::FromWebContents(web_contents);
210    if (autofill_manager->IsNativeUiEnabled()) {
211      external_delegate_.reset(
212          new TestAutofillExternalDelegate(web_contents, autofill_manager));
213      autofill_manager->SetExternalDelegate(external_delegate_.get());
214    }
215    autofill_manager->SetTestDelegate(&test_delegate_);
216  }
217
218  virtual void CleanUpOnMainThread() OVERRIDE {
219    // Make sure to close any showing popups prior to tearing down the UI.
220    content::WebContents* web_contents =
221        browser()->tab_strip_model()->GetActiveWebContents();
222    AutofillManager* autofill_manager =
223        AutofillManager::FromWebContents(web_contents);
224    autofill_manager->delegate()->HideAutofillPopup();
225  }
226
227  PersonalDataManager* personal_data_manager() {
228    return PersonalDataManagerFactory::GetForProfile(browser()->profile());
229  }
230
231  void CreateTestProfile() {
232    AutofillProfile profile;
233    test::SetProfileInfo(
234        &profile, "Milton", "C.", "Waddams",
235        "red.swingline@initech.com", "Initech", "4120 Freidrich Lane",
236        "Basement", "Austin", "Texas", "78744", "US", "5125551234");
237
238    WindowedPersonalDataManagerObserver observer(browser());
239    personal_data_manager()->AddProfile(profile);
240
241    // AddProfile is asynchronous. Wait for it to finish before continuing the
242    // tests.
243    observer.Wait();
244  }
245
246  void ExpectFieldValue(const std::string& field_name,
247                        const std::string& expected_value) {
248    std::string value;
249    ASSERT_TRUE(content::ExecuteScriptAndExtractString(
250        browser()->tab_strip_model()->GetActiveWebContents(),
251        "window.domAutomationController.send("
252        "    document.getElementById('" + field_name + "').value);",
253        &value));
254    EXPECT_EQ(expected_value, value);
255  }
256
257  RenderViewHost* render_view_host() {
258    return browser()->tab_strip_model()->GetActiveWebContents()->
259        GetRenderViewHost();
260  }
261
262  void FocusFirstNameField() {
263    LOG(WARNING) << "Clicking on the tab.";
264    content::SimulateMouseClick(
265        browser()->tab_strip_model()->GetActiveWebContents(),
266        0,
267        WebKit::WebMouseEvent::ButtonLeft);
268
269    LOG(WARNING) << "Focusing the first name field.";
270    bool result = false;
271    ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
272        render_view_host(),
273        "if (document.readyState === 'complete')"
274        "  document.getElementById('firstname').focus();"
275        "else"
276        "  domAutomationController.send(false);",
277        &result));
278    ASSERT_TRUE(result);
279  }
280
281  void ExpectFilledTestForm() {
282    ExpectFieldValue("firstname", "Milton");
283    ExpectFieldValue("lastname", "Waddams");
284    ExpectFieldValue("address1", "4120 Freidrich Lane");
285    ExpectFieldValue("address2", "Basement");
286    ExpectFieldValue("city", "Austin");
287    ExpectFieldValue("state", "TX");
288    ExpectFieldValue("zip", "78744");
289    ExpectFieldValue("country", "US");
290    ExpectFieldValue("phone", "5125551234");
291  }
292
293  void SendKeyToPageAndWait(ui::KeyboardCode key) {
294    test_delegate_.Reset();
295    content::SimulateKeyPress(
296        browser()->tab_strip_model()->GetActiveWebContents(),
297        key, false, false, false, false);
298    test_delegate_.Wait();
299  }
300
301  void SendKeyToPopupAndWait(ui::KeyboardCode key) {
302    // TODO(isherman): Remove this condition once the WebKit popup UI code is
303    // removed.
304    if (!external_delegate_) {
305      // When testing the WebKit-based UI, route all keys to the page.
306      SendKeyToPageAndWait(key);
307      return;
308    }
309
310    // When testing the native UI, route popup-targeted key presses via the
311    // external delegate.
312    content::NativeWebKeyboardEvent event;
313    event.windowsKeyCode = key;
314    test_delegate_.Reset();
315    external_delegate_->keyboard_listener()->HandleKeyPressEvent(event);
316    test_delegate_.Wait();
317  }
318
319  TestAutofillExternalDelegate* external_delegate() {
320    return external_delegate_.get();
321  }
322
323  AutofillManagerTestDelegateImpl test_delegate_;
324
325 private:
326  scoped_ptr<TestAutofillExternalDelegate> external_delegate_;
327};
328
329IN_PROC_BROWSER_TEST_F(AutofillInteractiveTest, DISABLED_AutofillSelectViaTab) {
330  CreateTestProfile();
331
332  // Load the test page.
333  ASSERT_NO_FATAL_FAILURE(ui_test_utils::NavigateToURL(browser(),
334      GURL(std::string(kDataURIPrefix) + kTestFormString)));
335
336  // Focus a fillable field.
337  FocusFirstNameField();
338
339  // Press the down arrow to initiate Autofill and wait for the popup to be
340  // shown.
341  SendKeyToPageAndWait(ui::VKEY_DOWN);
342
343  // Press the down arrow to select the suggestion and preview the autofilled
344  // form.
345  SendKeyToPopupAndWait(ui::VKEY_DOWN);
346
347  // Press tab to accept the autofill suggestions.
348  SendKeyToPopupAndWait(ui::VKEY_TAB);
349
350  // The form should be filled.
351  ExpectFilledTestForm();
352}
353
354}  // namespace autofill
355