OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2015 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/strings/stringprintf.h" | |
6 #include "chrome/browser/autofill/autofill_uitest_util.h" | |
7 #include "chrome/browser/chrome_notification_types.h" | |
8 #include "chrome/browser/ui/browser_window.h" | |
9 #include "chrome/browser/ui/chrome_pages.h" | |
10 #include "chrome/browser/ui/tabs/tab_strip_model.h" | |
11 #include "chrome/common/url_constants.h" | |
12 #include "chrome/test/base/in_process_browser_test.h" | |
13 #include "chrome/test/base/interactive_test_utils.h" | |
14 #include "components/autofill/core/browser/autofill_profile.h" | |
15 #include "components/autofill/core/browser/autofill_test_utils.h" | |
16 #include "components/autofill/core/browser/personal_data_manager.h" | |
17 #include "content/public/test/browser_test_utils.h" | |
18 | |
19 namespace { | |
20 | |
21 // This class tests the Autofill options settings. | |
22 // This test is part of the interactive_ui_tests instead of browser_tests | |
23 // because it is necessary to emulate pushing the tab key. | |
24 class AutofillOptionsWebUITest : public InProcessBrowserTest { | |
25 public: | |
26 AutofillOptionsWebUITest() {} | |
27 | |
28 // Navigate to the autofillEditAddress page. | |
29 void SetUpOnMainThread() override { | |
30 const GURL url = chrome::GetSettingsUrl("autofillEditAddress"); | |
31 ui_test_utils::NavigateToURL(browser(), url); | |
32 } | |
33 | |
34 protected: | |
35 const std::string kEditAddressOverlaySelector = | |
36 "#autofill-edit-address-overlay"; | |
37 | |
38 content::RenderFrameHost* GetActiveFrame() { | |
39 return GetActiveWebContents()->GetFocusedFrame(); | |
40 } | |
41 | |
42 content::RenderViewHost* GetRenderViewHost() { | |
43 return GetActiveWebContents()->GetRenderViewHost(); | |
44 } | |
45 | |
46 content::WebContents* GetActiveWebContents() { | |
47 return browser()->tab_strip_model()->GetActiveWebContents(); | |
48 } | |
49 | |
50 void CreateTestProfile() { | |
51 autofill::AddTestProfile(browser(), autofill::test::GetFullProfile()); | |
52 } | |
53 | |
54 // Returns true if element contains document.activeElement. | |
55 bool ContainsActiveElement(const std::string& element_selector) { | |
56 const std::string script = base::StringPrintf( | |
57 "domAutomationController.send(" | |
58 "document.querySelector('%s').contains(document.activeElement));", | |
59 element_selector.c_str()); | |
60 bool result; | |
61 EXPECT_TRUE(content::ExecuteScriptAndExtractBool( | |
62 GetActiveFrame(), | |
63 script, | |
64 &result)); | |
65 return result; | |
66 } | |
67 | |
68 // Returns the number of items in the list. | |
69 int GetListSize(const std::string& list_selector) { | |
70 const std::string script = base::StringPrintf( | |
71 "domAutomationController.send(" | |
72 "document.querySelector('%s').items.length);", | |
73 list_selector.c_str()); | |
74 int length = -1; | |
75 EXPECT_TRUE(content::ExecuteScriptAndExtractInt( | |
76 GetActiveFrame(), | |
77 script, | |
78 &length)); | |
79 return length; | |
80 } | |
81 | |
82 // Focus the first input field of the first list item. | |
83 void FocusFirstListItemInput(const std::string& list_selector) { | |
84 const std::string script = base::StringPrintf( | |
85 "document.querySelector('%s input').focus();", | |
86 list_selector.c_str()); | |
87 EXPECT_TRUE(content::ExecuteScript(GetActiveFrame(), script)); | |
88 } | |
89 | |
90 // Returns the text of the first item in the list. | |
91 std::string GetFirstListItemText(const std::string& list_selector) { | |
92 // EXPECT_TRUE will fail if there is no first item or first item does not | |
93 // have 'input'. | |
94 const std::string script = base::StringPrintf( | |
95 "domAutomationController.send(" | |
96 "document.querySelector('%s input').value);", | |
97 list_selector.c_str()); | |
98 std::string result; | |
99 EXPECT_TRUE(content::ExecuteScriptAndExtractString( | |
100 GetActiveFrame(), | |
101 script, | |
102 &result)); | |
103 return result; | |
104 } | |
105 | |
106 // Returns true if the first item in the list has 'selected' attribute. | |
107 bool GetFirstListItemSelected(const std::string& list_selector) { | |
108 // EXPECT_TRUE will fail if there is no first item. | |
109 const std::string script = base::StringPrintf( | |
110 "domAutomationController.send(" | |
111 "document.querySelector('%s').items[0].hasAttribute('selected'));", | |
112 list_selector.c_str()); | |
113 bool result = false; | |
114 EXPECT_TRUE(content::ExecuteScriptAndExtractBool( | |
115 GetActiveFrame(), | |
116 script, | |
117 &result)); | |
118 return result; | |
119 } | |
120 | |
121 // Returns true if a row delete button ('X' button) is focused. | |
122 bool GetDeleteButtonFocused() { | |
123 const std::string script = | |
124 "domAutomationController.send(" | |
125 "document.activeElement.classList.contains('row-delete-button'));"; | |
126 bool result = false; | |
127 EXPECT_TRUE(content::ExecuteScriptAndExtractBool( | |
128 GetActiveFrame(), | |
129 script, | |
130 &result)); | |
131 return result; | |
132 } | |
133 | |
134 // Insert text into currently focused element. | |
135 void InsertText(const std::string& text) { | |
136 ASSERT_EQ(std::string::npos, text.find("'")); | |
137 const std::string script = base::StringPrintf( | |
138 "document.execCommand('insertText', false, '%s');", | |
139 text.c_str()); | |
140 EXPECT_TRUE(content::ExecuteScript(GetActiveFrame(), script)); | |
141 } | |
142 | |
143 // Press and release tab key in the browser. This will wait for the element on | |
144 // the page to change. | |
145 bool PressTab(bool shift) { | |
146 return ui_test_utils::SendKeyPressAndWait( | |
147 browser(), | |
148 ui::VKEY_TAB, | |
149 false, | |
150 shift, | |
151 false, | |
152 false, | |
153 content::NOTIFICATION_FOCUS_CHANGED_IN_PAGE, | |
154 content::Source<content::RenderViewHost>(GetRenderViewHost())); | |
155 } | |
156 | |
157 void InitializeDomMessageQueue() { | |
158 dom_message_queue_.reset(new content::DOMMessageQueue); | |
159 } | |
160 | |
161 // Wait for a message from the DOM automation controller. | |
162 void WaitForDomMessage(const std::string& message) { | |
163 const std::string expected = "\"" + message + "\""; | |
164 std::string received; | |
165 do { | |
166 ASSERT_TRUE(dom_message_queue_->WaitForMessage(&received)); | |
167 } while (received != expected); | |
168 } | |
169 | |
170 void ListenForFirstItemSelected(const std::string& list_selector) { | |
171 const std::string script = base::StringPrintf( | |
172 "document.querySelector('%s').items[0].addEventListener(" | |
173 "'selectedChange', function(e) {" | |
174 "if (e.newValue) {" | |
175 "domAutomationController.setAutomationId(0);" | |
176 "domAutomationController.send('first item selected');" | |
177 "}" | |
178 "});", | |
179 list_selector.c_str()); | |
180 | |
181 EXPECT_TRUE(content::ExecuteScript( | |
182 GetActiveFrame(), | |
183 script)); | |
184 } | |
185 | |
186 void ListenForCommitEdit(const std::string& list_selector) { | |
187 const std::string script = base::StringPrintf( | |
188 "document.querySelector('%s').addEventListener(" | |
189 "'commitedit', function() {" | |
190 "domAutomationController.setAutomationId(0);" | |
191 "domAutomationController.send('done commitedit');" | |
192 "});", | |
193 list_selector.c_str()); | |
194 | |
195 EXPECT_TRUE(content::ExecuteScript( | |
196 GetActiveFrame(), | |
197 script)); | |
198 } | |
199 | |
200 // Add an event listener to send a DOM automation controller message from | |
201 // JavaScript each time validation completes for the list. | |
202 void ListenForDoneValidating(const std::string& list_selector) { | |
203 // doneValidating will execute the 'then' function immediately if no | |
204 // validations are pending, so wait for 'commitedit' event before calling | |
205 // doneValidating. | |
206 const std::string script = base::StringPrintf( | |
207 "document.querySelector('%s').addEventListener('commitedit'," | |
208 "function() {" | |
209 "document.querySelector('%s').doneValidating().then(function() {" | |
210 "domAutomationController.setAutomationId(0);" | |
211 "domAutomationController.send('done validating');" | |
212 "});" | |
213 "});", | |
214 list_selector.c_str(), | |
215 list_selector.c_str()); | |
216 | |
217 EXPECT_TRUE(content::ExecuteScript( | |
218 GetActiveFrame(), | |
219 script)); | |
220 } | |
221 | |
222 // Verifies that everything is the way it should be after list item is | |
223 // added or edited. | |
224 void VerifyEditAddressListPostConditions(const std::string& list_selector, | |
225 const std::string& input_text, | |
226 bool list_requires_validation) { | |
227 // Verify that neither the list nor any of its children still have focus. | |
228 EXPECT_FALSE(ContainsActiveElement(list_selector)); | |
229 | |
230 // Verify that focus moved to a different element of the overlay. | |
231 EXPECT_TRUE(ContainsActiveElement(kEditAddressOverlaySelector)); | |
232 | |
233 // Verify that list has exactly two items. They will be the item that was | |
234 // just added/modified + the placeholder. | |
235 EXPECT_EQ(2, GetListSize(list_selector)); | |
236 | |
237 // Verify that the first list item has the string that was inserted. | |
238 EXPECT_EQ(input_text, GetFirstListItemText(list_selector)); | |
239 | |
240 // TODO(bondd): phone list doesn't select first item after validation. | |
241 // It becomes selected later when the list is given focus. | |
242 if (!list_requires_validation) { | |
243 // Verify that the first list item is the selected item in the list. | |
244 EXPECT_TRUE(GetFirstListItemSelected(list_selector)); | |
245 } | |
246 } | |
247 | |
248 // Make sure that when text is entered in the placeholder of an empty list and | |
249 // the tab key is pressed: | |
250 // + Focus leaves the list and goes to a different element on the page. | |
251 // + The text stays added and a new placeholder is created. | |
252 // + The list item with the newly added text is the selected list item (not | |
253 // the placeholder). | |
254 // | |
255 // Added to prevent http://crbug.com/440760 from regressing again. | |
256 void TestEditAddressListTabKeyAddItem(const std::string& list_selector, | |
257 const std::string& input_text, | |
258 bool list_requires_validation) { | |
259 LOG(INFO) << "Starting TestEditAddressListTabKeyAddItem"; | |
260 | |
261 // Focus the input field and insert test string. | |
262 FocusFirstListItemInput(list_selector); | |
263 WaitForDomMessage("first item selected"); | |
264 | |
265 InsertText(input_text); | |
266 | |
267 // Press tab key to move to next element after the list. | |
268 PressTab(false); | |
269 | |
270 if (list_requires_validation) | |
271 WaitForDomMessage("done validating"); | |
272 else | |
273 WaitForDomMessage("done commitedit"); | |
274 | |
275 // Make sure everything ended up the way it should be. | |
276 VerifyEditAddressListPostConditions(list_selector, input_text, | |
277 list_requires_validation); | |
278 } | |
279 | |
280 // Depends on state set up by TestEditAddressListTabKeyAddItem. Should be | |
281 // called immediately after that method. | |
282 // | |
283 // Make sure that when a list item's text is edited and the tab key is | |
284 // pressed twice: | |
285 // + After the first tab press the item's delete button is focused. | |
286 // + After the second tab press focus leaves the list and goes to a | |
287 // different element on the page. | |
288 // + The edited text persists. | |
289 // + The edited list item is the selected list item. | |
290 // | |
291 // Added to prevent http://crbug.com/443491 from regressing again. | |
292 void TestEditAddressListTabKeyEditItem(const std::string& list_selector, | |
293 const std::string& input_text, | |
294 bool list_requires_validation, | |
295 bool skip_ok_button) { | |
296 LOG(INFO) << "Starting TestEditAddressListTabKeyEditItem"; | |
Dan Beam
2015/02/04 20:47:29
remove debugging statements
bondd
2015/02/04 22:14:17
Done.
| |
297 | |
298 if (skip_ok_button) | |
299 PressTab(true); | |
300 | |
301 // Press shift+tab to move back to the first list item's delete button. | |
302 PressTab(true); | |
303 EXPECT_TRUE(GetDeleteButtonFocused()); | |
304 | |
305 // Press shift+tab to move back to the first list item's input field. | |
306 PressTab(true); | |
307 // Verify that the first item in the list is focused. | |
308 EXPECT_TRUE(ContainsActiveElement(list_selector + " input")); | |
309 | |
310 // Insert modified text in the first list item. | |
311 std::string second_input = "second" + input_text; | |
312 InsertText(second_input); | |
313 | |
314 // Press tab key to focus the list item's delete button. | |
315 PressTab(false); | |
316 EXPECT_TRUE(GetDeleteButtonFocused()); | |
317 | |
318 // Press tab key again to move to next element after the list. | |
319 PressTab(false); | |
320 | |
321 if (list_requires_validation) | |
322 WaitForDomMessage("done validating"); | |
323 else | |
324 WaitForDomMessage("done commitedit"); | |
325 | |
326 // Make sure everything ended up the way it should be. | |
327 VerifyEditAddressListPostConditions(list_selector, second_input, | |
328 list_requires_validation); | |
329 } | |
330 | |
331 void TestEditAddressListTabKey(const std::string& field_name, | |
332 const std::string& input_text, | |
333 bool list_requires_validation, | |
334 bool skip_ok_button) { | |
335 const std::string list_selector = kEditAddressOverlaySelector + " [field=" + | |
336 field_name + "]"; | |
337 | |
338 InitializeDomMessageQueue(); | |
339 ListenForFirstItemSelected(list_selector); | |
340 if (list_requires_validation) | |
341 ListenForDoneValidating(list_selector); | |
342 else | |
343 ListenForCommitEdit(list_selector); | |
344 | |
345 TestEditAddressListTabKeyAddItem(list_selector, input_text, | |
346 list_requires_validation); | |
347 TestEditAddressListTabKeyEditItem(list_selector, input_text, | |
348 list_requires_validation, skip_ok_button); | |
349 } | |
350 | |
351 private: | |
352 scoped_ptr<content::DOMMessageQueue> dom_message_queue_; | |
353 | |
354 DISALLOW_COPY_AND_ASSIGN(AutofillOptionsWebUITest); | |
355 }; | |
356 | |
357 } // namespace | |
358 | |
359 // Test the 'fullName' InlineEditableItemList in autofillEditAddress overlay. | |
360 IN_PROC_BROWSER_TEST_F(AutofillOptionsWebUITest, | |
361 TestEditAddressNameTabKey) { | |
362 TestEditAddressListTabKey("fullName", "Test Name", false, false); | |
363 } | |
364 | |
365 // Test the 'phone' InlineEditableItemList in autofillEditAddress overlay. | |
366 IN_PROC_BROWSER_TEST_F(AutofillOptionsWebUITest, | |
367 TestEditAddressPhoneTabKey) { | |
368 CreateTestProfile(); | |
369 TestEditAddressListTabKey("phone", "123-456-7890", true, false); | |
370 } | |
371 | |
372 // Test the 'email' InlineEditableItemList in autofillEditAddress overlay. | |
373 IN_PROC_BROWSER_TEST_F(AutofillOptionsWebUITest, | |
374 TestEditAddressEmailTabKey) { | |
375 #if defined(OS_WIN) || defined(OS_CHROMEOS) | |
Dan Beam
2015/02/04 20:47:29
add a note that this because the button strip is r
bondd
2015/02/04 22:14:17
Done.
| |
376 bool skip_ok_button = true; | |
377 #else | |
378 bool skip_ok_button = false; | |
379 #endif | |
380 | |
381 TestEditAddressListTabKey("email", "test@example.com", false, skip_ok_button); | |
382 } | |
OLD | NEW |