OLD | NEW |
(Empty) | |
| 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 #import <UIKit/UIKit.h> |
| 6 |
| 7 #include "base/format_macros.h" |
| 8 #include "base/ios/ios_util.h" |
| 9 #include "base/strings/sys_string_conversions.h" |
| 10 #include "components/autofill/core/common/autofill_constants.h" |
| 11 #include "ios/chrome/browser/web/chrome_web_test.h" |
| 12 #import "ios/web/public/test/web_js_test.h" |
| 13 #include "testing/gtest/include/gtest/gtest.h" |
| 14 #include "testing/gtest_mac.h" |
| 15 |
| 16 #if !defined(__has_feature) || !__has_feature(objc_arc) |
| 17 #error "This file requires ARC support." |
| 18 #endif |
| 19 |
| 20 // Unit tests for ios/chrome/browser/web/resources/autofill_controller.js |
| 21 namespace { |
| 22 |
| 23 // Structure for getting element by name using JavaScripts. |
| 24 struct ElementByName { |
| 25 // The name of the element. |
| 26 const char* element_name; |
| 27 // The index in the elements that have |element_name|. |
| 28 const int index; |
| 29 // The option index if the element is a select, -1 otherwise. |
| 30 const int option_index; |
| 31 }; |
| 32 |
| 33 // TODO(crbug.com/619982): MobileSafari/iOS10 corrected |
| 34 // HTMLInputElement.maxLength with the specification |
| 35 // ( https://bugs.webkit.org/show_bug.cgi?id=154906 ). Add support for old and |
| 36 // new default maxLength value until we drop iOS 9. |
| 37 NSString* GetDefaultMaxLengthString() { |
| 38 return base::ios::IsRunningOnIOS10OrLater() ? @"-1" : @"524288"; |
| 39 } |
| 40 NSNumber* GetDefaultMaxLength() { |
| 41 return base::ios::IsRunningOnIOS10OrLater() ? @-1 : @524288; |
| 42 } |
| 43 |
| 44 // Generates the JavaScript that gets an element by name. |
| 45 NSString* GetElementByNameJavaScript(ElementByName element) { |
| 46 NSString* query = |
| 47 [NSString stringWithFormat:@"window.document.getElementsByName('%s')[%d]", |
| 48 element.element_name, element.index]; |
| 49 if (element.option_index >= 0) { |
| 50 query = |
| 51 [query stringByAppendingFormat:@".options[%d]", element.option_index]; |
| 52 } |
| 53 return query; |
| 54 } |
| 55 |
| 56 // Generates an array of JavaScripts that get each element in |elements| by |
| 57 // name. |
| 58 NSArray* GetElementsByNameJavaScripts(const ElementByName elements[], |
| 59 size_t elements_size) { |
| 60 NSMutableArray* array = [NSMutableArray array]; |
| 61 for (size_t i = 0; i < elements_size; ++i) { |
| 62 NSString* query = GetElementByNameJavaScript(elements[i]); |
| 63 [array addObject:query]; |
| 64 } |
| 65 return array; |
| 66 } |
| 67 |
| 68 // clang-format off |
| 69 NSString* kHTMLForTestingElements = @"<html><body>" |
| 70 "<input type=hidden name='gl' value='us'>" |
| 71 "<form name='testform'>" |
| 72 " <input type=hidden name='hl' value='en'>" |
| 73 " <input type='text' name='firstname'>" |
| 74 " <input type='text' name='lastname'>" |
| 75 " <input type='email' name='email'>" |
| 76 " <input type='tel' name='phone'>" |
| 77 " <input type='url' autocomplete='off' name='blog'>" |
| 78 " <input type='number' name='expected number of clicks'>" |
| 79 " <input type='password' autocomplete='off' name='pwd'>" |
| 80 " <input type='checkbox' name='vehicle' value='Bike'>" |
| 81 " <input type='checkbox' name='vehicle' value='Car'>" |
| 82 " <input type='checkbox' name='vehicle' value='Rocket'>" |
| 83 " <input type='radio' name='boolean' value='true'>" |
| 84 " <input type='radio' name='boolean' value='false'>" |
| 85 " <input type='radio' name='boolean' value='other'>" |
| 86 " <label>State:" |
| 87 " <select name='state'>" |
| 88 " <option value='CA'>CA</option>" |
| 89 " <option value='MA'>MA</option>" |
| 90 " </select>" |
| 91 " </label>" |
| 92 " <label>Course:" |
| 93 " <select name='course'>" |
| 94 " <optgroup label='8.01 Physics I: Classical Mechanics'>" |
| 95 " <option value='8.01.1'>Lecture 01: Powers of Ten" |
| 96 " <option value='8.01.2'>Lecture 02: 1D Kinematics" |
| 97 " <option value='8.01.3'>Lecture 03: Vectors" |
| 98 " <optgroup label='8.02 Electricity and Magnestism'>" |
| 99 " <option value='8.02.1'>Lecture 01: What holds our world together?" |
| 100 " <option value='8.02.2'>Lecture 02: Electric Field" |
| 101 " <option value='8.02.3'>Lecture 03: Electric Flux" |
| 102 " </select>" |
| 103 " </label>" |
| 104 " <label>Cars:" |
| 105 " <select name='cars' multiple>" |
| 106 " <option value='volvo'>Volvo</option>" |
| 107 " <option value='saab'>Saab</option>" |
| 108 " <option value='opel'>Opel</option>" |
| 109 " <option value='audi'>Audi</option>" |
| 110 " </select>" |
| 111 " </label>" |
| 112 " <input type='submit' name='submit' value='Submit'>" |
| 113 "</form>" |
| 114 "</body></html>"; |
| 115 // clang-format on |
| 116 |
| 117 // A bit field mask to extract data from WebFormControlElement. They are from |
| 118 // autofill_controller.js |
| 119 enum ExtractMask { |
| 120 EXTRACT_NONE = 0, |
| 121 EXTRACT_VALUE = 1 << 0, // Extract value from WebFormControlElement. |
| 122 EXTRACT_OPTION_TEXT = 1 << 1, // Extract option text from |
| 123 // WebFormSelectElement. Only valid when |
| 124 // |EXTRACT_VALUE| is set. |
| 125 // This is used for form submission where |
| 126 // human readable value is captured. |
| 127 EXTRACT_OPTIONS = 1 << 2, // Extract options from |
| 128 // WebFormControlElement. |
| 129 }; |
| 130 |
| 131 const ExtractMask kFormExtractMasks[] = { |
| 132 EXTRACT_NONE, EXTRACT_VALUE, EXTRACT_OPTION_TEXT, EXTRACT_OPTIONS, |
| 133 }; |
| 134 |
| 135 // Gets the attributes to check for a mask in |kFormExtractMasks|. |
| 136 NSArray* GetFormFieldAttributeListsToCheck(NSUInteger mask) { |
| 137 if (!(mask & EXTRACT_VALUE)) { |
| 138 return @[ |
| 139 @"name", @"form_control_type", @"autocomplete_attribute", @"max_length", |
| 140 @"should_autocomplete", @"is_checkable" |
| 141 ]; |
| 142 } |
| 143 |
| 144 if (mask & EXTRACT_OPTIONS) { |
| 145 return @[ |
| 146 @"name", @"form_control_type", @"autocomplete_attribute", @"max_length", |
| 147 @"should_autocomplete", @"is_checkable", @"value", @"option_values", |
| 148 @"option_contents" |
| 149 ]; |
| 150 } |
| 151 |
| 152 return @[ |
| 153 @"name", @"form_control_type", @"autocomplete_attribute", @"max_length", |
| 154 @"should_autocomplete", @"is_checkable", @"value" |
| 155 ]; |
| 156 } |
| 157 |
| 158 // ***** Clang-formatting is disabled for the following block of testdata ***** |
| 159 // clang-format off |
| 160 |
| 161 // Getters for form control element testing data. The returned data is |
| 162 // an array consisting of an html fragment followed by an array |
| 163 // of dictionaries containing the expected field attributes for elements in the |
| 164 // given html fragment. |
| 165 NSArray* GetTestFormInputElementWithLabelFromPrevious() { |
| 166 return @[ |
| 167 @("* First name: " |
| 168 "<INPUT type='text' name='firstname' value='John'/>"), |
| 169 [NSDictionary dictionaryWithObjectsAndKeys: |
| 170 @"'* First name:'", @"label", |
| 171 @"'firstname'", @"name", |
| 172 @"'text'", @"form_control_type", |
| 173 @"undefined", @"autocomplete_attribute", |
| 174 GetDefaultMaxLengthString(), @"max_length", |
| 175 @"true", @"should_autocomplete", |
| 176 @"false", @"is_checkable", |
| 177 @"'John'", @"value", |
| 178 @"'John'", @"value_option_text", |
| 179 @"undefined", @"option_values", |
| 180 @"undefined", @"option_contents", |
| 181 nil]]; |
| 182 } |
| 183 |
| 184 NSArray* GetTestFormInputElementWithLabelFromPreviousSpan() { |
| 185 return @[ |
| 186 @("* Last name<span>:</span> " |
| 187 "<INPUT type='text' name='lastname' value='John'/>"), |
| 188 [NSDictionary dictionaryWithObjectsAndKeys: |
| 189 @"'* Last name:'", @"label", |
| 190 @"'lastname'", @"name", |
| 191 @"'text'", @"form_control_type", |
| 192 @"undefined", @"autocomplete_attribute", |
| 193 GetDefaultMaxLengthString(), @"max_length", |
| 194 @"true", @"should_autocomplete", |
| 195 @"false", @"is_checkable", |
| 196 @"'John'", @"value", |
| 197 @"'John'", @"value_option_text", |
| 198 @"undefined", @"option_values", |
| 199 @"undefined", @"option_contents", |
| 200 nil]]; |
| 201 } |
| 202 |
| 203 NSArray* GetTestFormInputElementWithLabelFromPreviousParagraph() { |
| 204 return @[ |
| 205 @("<p>* Email:</p> " |
| 206 "<INPUT type='email' name='email' value='john@example.com'/>"), |
| 207 [NSDictionary dictionaryWithObjectsAndKeys: |
| 208 @"'* Email:'", @"label", |
| 209 @"'email'", @"name", |
| 210 @"'email'", @"form_control_type", |
| 211 @"undefined", @"autocomplete_attribute", |
| 212 GetDefaultMaxLengthString(), @"max_length", |
| 213 @"true", @"should_autocomplete", |
| 214 @"false", @"is_checkable", |
| 215 @"'john@example.com'", @"value", |
| 216 @"'john@example.com'", @"value_option_text", |
| 217 @"undefined", @"option_values", |
| 218 @"undefined", @"option_contents", |
| 219 nil]]; |
| 220 } |
| 221 |
| 222 NSArray* GetTestFormInputElementWithLabelFromPreviousLabel() { |
| 223 return @[ |
| 224 @("<label>* Telephone: </label> " |
| 225 "<INPUT type='tel' id='telephone' value='12345678'/>"), |
| 226 [NSDictionary dictionaryWithObjectsAndKeys: |
| 227 @"'* Telephone:'", @"label", |
| 228 @"'telephone'", @"name", |
| 229 @"'tel'", @"form_control_type", |
| 230 @"undefined", @"autocomplete_attribute", |
| 231 GetDefaultMaxLengthString(), @"max_length", |
| 232 @"true", @"should_autocomplete", |
| 233 @"false", @"is_checkable", |
| 234 @"'12345678'", @"value", |
| 235 @"'12345678'", @"value_option_text", |
| 236 @"undefined", @"option_values", |
| 237 @"undefined", @"option_contents", |
| 238 nil]]; |
| 239 } |
| 240 |
| 241 NSArray* GetTestFormInputElementWithLabelFromPreviousLabelOtherIgnored() { |
| 242 return @[ |
| 243 @("Other Text <label>* Blog:</label> " |
| 244 "<INPUT type='url' autocomplete='off' value='www.jogh.blog'/>"), |
| 245 [NSDictionary dictionaryWithObjectsAndKeys: |
| 246 @"'* Blog:'", @"label", |
| 247 @"''", @"name", |
| 248 @"'url'", @"form_control_type", |
| 249 @"'off'", @"autocomplete_attribute", |
| 250 GetDefaultMaxLengthString(), @"max_length", |
| 251 @"false", @"should_autocomplete", |
| 252 @"false", @"is_checkable", |
| 253 @"'www.jogh.blog'", @"value", |
| 254 @"'www.jogh.blog'", @"value_option_text", |
| 255 @"undefined", @"option_values", |
| 256 @"undefined", @"option_contents", |
| 257 nil]]; |
| 258 } |
| 259 |
| 260 NSArray* GetTestFormInputElementWithLabelFromPreviousTextSpanBr() { |
| 261 return @[ |
| 262 @("* Expected visits<span>:</span> <br>" |
| 263 "<INPUT type='number' name='expected number of clicks'/>"), |
| 264 [NSDictionary dictionaryWithObjectsAndKeys: |
| 265 @"'* Expected visits:'", @"label", |
| 266 @"'expected number of clicks'", @"name", |
| 267 @"'number'", @"form_control_type", |
| 268 @"undefined", @"autocomplete_attribute", |
| 269 GetDefaultMaxLengthString(), @"max_length", |
| 270 @"true", @"should_autocomplete", |
| 271 @"false", @"is_checkable", |
| 272 @"''", @"value", |
| 273 @"''", @"value_option_text", |
| 274 @"undefined", @"option_values", |
| 275 @"undefined", @"option_contents", |
| 276 nil]]; |
| 277 } |
| 278 |
| 279 NSArray* GetTestFormInputElementWithLabelFromPreviousTextBrAndSpan() { |
| 280 return @[ |
| 281 @("Other <br> * Password<span>:</span> " |
| 282 "<INPUT type='password' autocomplete='off' name='pwd'/>"), |
| 283 [NSDictionary dictionaryWithObjectsAndKeys: |
| 284 @"'* Password:'", @"label", |
| 285 @"'pwd'", @"name", |
| 286 @"'password'", @"form_control_type", |
| 287 @"'off'", @"autocomplete_attribute", |
| 288 GetDefaultMaxLengthString(), @"max_length", |
| 289 @"false", @"should_autocomplete", |
| 290 @"false", @"is_checkable", |
| 291 @"''", @"value", |
| 292 @"''", @"value_option_text", |
| 293 @"undefined", @"option_values", |
| 294 @"undefined", @"option_contents", |
| 295 nil]]; |
| 296 } |
| 297 |
| 298 NSArray* GetTestFormInputElementWithLabelFromListItem() { |
| 299 return @[ |
| 300 @("<LI>" |
| 301 "<LABEL><EM>*</EM> Code:</LABEL>" |
| 302 "<INPUT type='text' id='first code' value='415'/>" |
| 303 "<INPUT type='text' id='middle code' value='555'/>" |
| 304 "<INPUT type='text' id='last code' value='1212'/>" |
| 305 "</LI>"), |
| 306 [NSDictionary dictionaryWithObjectsAndKeys: |
| 307 @"'* Code:'", @"label", |
| 308 @"'first code'", @"name", |
| 309 @"'text'", @"form_control_type", |
| 310 @"undefined", @"autocomplete_attribute", |
| 311 GetDefaultMaxLengthString(), @"max_length", |
| 312 @"true", @"should_autocomplete", |
| 313 @"false", @"is_checkable", |
| 314 @"'415'", @"value", |
| 315 @"'415'", @"value_option_text", |
| 316 @"undefined", @"option_values", |
| 317 @"undefined", @"option_contents", |
| 318 nil], |
| 319 [NSDictionary dictionaryWithObjectsAndKeys: |
| 320 @"'* Code:'", @"label", |
| 321 @"'middle code'", @"name", |
| 322 @"'text'", @"form_control_type", |
| 323 @"undefined", @"autocomplete_attribute", |
| 324 GetDefaultMaxLengthString(), @"max_length", |
| 325 @"true", @"should_autocomplete", |
| 326 @"false", @"is_checkable", |
| 327 @"'555'", @"value", |
| 328 @"'555'", @"value_option_text", |
| 329 @"undefined", @"option_values", |
| 330 @"undefined", @"option_contents", |
| 331 nil], |
| 332 [NSDictionary dictionaryWithObjectsAndKeys: |
| 333 @"'* Code:'", @"label", |
| 334 @"'last code'", @"name", |
| 335 @"'text'", @"form_control_type", |
| 336 @"undefined", @"autocomplete_attribute", |
| 337 GetDefaultMaxLengthString(), @"max_length", |
| 338 @"true", @"should_autocomplete", |
| 339 @"false", @"is_checkable", |
| 340 @"'1212'", @"value", |
| 341 @"'1212'", @"value_option_text", |
| 342 @"undefined", @"option_values", |
| 343 @"undefined", @"option_contents", |
| 344 nil]]; |
| 345 } |
| 346 |
| 347 NSArray* GetTestFormInputElementWithLabelFromTableColumnTD() { |
| 348 return @[ |
| 349 @("<TABLE>" |
| 350 "<TR>" |
| 351 " <TD>* First name:</TD>" |
| 352 " <TD><INPUT type='text' id='tabletdname' value='John'/></TD>" |
| 353 "</TR>" |
| 354 "<TR>" |
| 355 " <TD>Email:</TD>" |
| 356 " <TD><INPUT type='email' id='tabletdemail'" |
| 357 " value='john@example.com'/></TD>" |
| 358 "</TR>" |
| 359 "</TABLE>"), |
| 360 [NSDictionary dictionaryWithObjectsAndKeys: |
| 361 @"'* First name:'", @"label", |
| 362 @"'tabletdname'", @"name", |
| 363 @"'text'", @"form_control_type", |
| 364 @"undefined", @"autocomplete_attribute", |
| 365 GetDefaultMaxLengthString(), @"max_length", |
| 366 @"true", @"should_autocomplete", |
| 367 @"false", @"is_checkable", |
| 368 @"'John'", @"value", |
| 369 @"'John'", @"value_option_text", |
| 370 @"undefined", @"option_values", |
| 371 @"undefined", @"option_contents", |
| 372 nil], |
| 373 [NSDictionary dictionaryWithObjectsAndKeys: |
| 374 @"'Email:'", @"label", |
| 375 @"'tabletdemail'", @"name", |
| 376 @"'email'", @"form_control_type", |
| 377 @"undefined", @"autocomplete_attribute", |
| 378 GetDefaultMaxLengthString(), @"max_length", |
| 379 @"true", @"should_autocomplete", |
| 380 @"false", @"is_checkable", |
| 381 @"'john@example.com'", @"value", |
| 382 @"'john@example.com'", @"value_option_text", |
| 383 @"undefined", @"option_values", |
| 384 @"undefined", @"option_contents", |
| 385 nil]]; |
| 386 } |
| 387 |
| 388 NSArray* GetTestFormInputElementWithLabelFromTableColumnTH() { |
| 389 return @[ |
| 390 @("<TABLE>" |
| 391 "<TR>" |
| 392 " <TH>* First name:</TH>" |
| 393 " <TD><INPUT type='text' name='nameintableth' value='John'/></TD>" |
| 394 "</TR>" |
| 395 "<TR>" |
| 396 " <TD>Email:</TD>" |
| 397 " <TD><INPUT type='email' id='emailtableth'" |
| 398 " value='john@example.com'/></TD>" |
| 399 "</TR>" |
| 400 "</TABLE>"), |
| 401 [NSDictionary dictionaryWithObjectsAndKeys: |
| 402 @"'* First name:'", @"label", |
| 403 @"'nameintableth'", @"name", |
| 404 @"'text'", @"form_control_type", |
| 405 @"undefined", @"autocomplete_attribute", |
| 406 GetDefaultMaxLengthString(), @"max_length", |
| 407 @"true", @"should_autocomplete", |
| 408 @"false", @"is_checkable", |
| 409 @"'John'", @"value", |
| 410 @"'John'", @"value_option_text", |
| 411 @"undefined", @"option_values", |
| 412 @"undefined", @"option_contents", |
| 413 nil], |
| 414 [NSDictionary dictionaryWithObjectsAndKeys: |
| 415 @"'Email:'", @"label", |
| 416 @"'emailtableth'", @"name", |
| 417 @"'email'", @"form_control_type", |
| 418 @"undefined", @"autocomplete_attribute", |
| 419 GetDefaultMaxLengthString(), @"max_length", |
| 420 @"true", @"should_autocomplete", |
| 421 @"false", @"is_checkable", |
| 422 @"'john@example.com'", @"value", |
| 423 @"'john@example.com'", @"value_option_text", |
| 424 @"undefined", @"option_values", |
| 425 @"undefined", @"option_contents", |
| 426 nil]]; |
| 427 } |
| 428 |
| 429 NSArray* GetTestFormInputElementWithLabelFromTableNested() { |
| 430 return @[ |
| 431 @("<TABLE>" |
| 432 "<TR>" |
| 433 " <TD><FONT>* First </FONT><FONT>name:</FONT></TD>" |
| 434 " <TD><INPUT type='text' id='nametablenested' value='John'/></TD>" |
| 435 "</TR>" |
| 436 "</TABLE>"), |
| 437 [NSDictionary dictionaryWithObjectsAndKeys: |
| 438 @"'* First name:'", @"label", |
| 439 @"'nametablenested'", @"name", |
| 440 @"'text'", @"form_control_type", |
| 441 @"undefined", @"autocomplete_attribute", |
| 442 GetDefaultMaxLengthString(), @"max_length", |
| 443 @"true", @"should_autocomplete", |
| 444 @"false", @"is_checkable", |
| 445 @"'John'", @"value", |
| 446 @"'John'", @"value_option_text", |
| 447 @"undefined", @"option_values", |
| 448 @"undefined", @"option_contents", |
| 449 nil]]; |
| 450 } |
| 451 |
| 452 NSArray* GetTestFormInputElementWithLabelFromTableRow() { |
| 453 return @[ |
| 454 @("<TABLE>" |
| 455 "<TR>" |
| 456 " <TD>* <FONT>First </FONT><FONT>name:</FONT></TD>" |
| 457 "</TR>" |
| 458 "<TR>" |
| 459 " <TD><INPUT type='text' name='nametablerow' value='John'/></TD>" |
| 460 "</TR>" |
| 461 "</TABLE>"), |
| 462 [NSDictionary dictionaryWithObjectsAndKeys: |
| 463 @"'* First name:'", @"label", |
| 464 @"'nametablerow'", @"name", |
| 465 @"'text'", @"form_control_type", |
| 466 @"undefined", @"autocomplete_attribute", |
| 467 GetDefaultMaxLengthString(), @"max_length", |
| 468 @"true", @"should_autocomplete", |
| 469 @"false", @"is_checkable", |
| 470 @"'John'", @"value", |
| 471 @"'John'", @"value_option_text", |
| 472 @"undefined", @"option_values", |
| 473 @"undefined", @"option_contents", |
| 474 nil]]; |
| 475 } |
| 476 |
| 477 NSArray* GetTestFormInputElementWithLabelFromDivTable() { |
| 478 return @[ |
| 479 @("<DIV>* First name:<BR>" |
| 480 "<SPAN>" |
| 481 "<INPUT type='text' name='namedivtable' value='John'>" |
| 482 "</SPAN>" |
| 483 "</DIV>"), |
| 484 [NSDictionary dictionaryWithObjectsAndKeys: |
| 485 @"'* First name:'", @"label", |
| 486 @"'namedivtable'", @"name", |
| 487 @"'text'", @"form_control_type", |
| 488 @"undefined", @"autocomplete_attribute", |
| 489 GetDefaultMaxLengthString(), @"max_length", |
| 490 @"true", @"should_autocomplete", |
| 491 @"false", @"is_checkable", |
| 492 @"'John'", @"value", |
| 493 @"'John'", @"value_option_text", |
| 494 @"undefined", @"option_values", |
| 495 @"undefined", @"option_contents", |
| 496 nil]]; |
| 497 } |
| 498 |
| 499 NSArray* GetTestFormInputElementWithLabelFromDefinitionList() { |
| 500 return @[ |
| 501 @("<DL>" |
| 502 " <DT>" |
| 503 " <SPAN>" |
| 504 " *" |
| 505 " </SPAN>" |
| 506 " <SPAN>" |
| 507 " Favorite Sport" |
| 508 " </SPAN>" |
| 509 " </DT>" |
| 510 " <DD>" |
| 511 " <FONT>" |
| 512 " <INPUT type='favorite sport' name='sport' value='Tennis'/>" |
| 513 " </FONT>" |
| 514 " </DD>" |
| 515 " </DL>"), |
| 516 [NSDictionary dictionaryWithObjectsAndKeys: |
| 517 @"'* Favorite Sport'", @"label", |
| 518 @"'sport'", @"name", |
| 519 @"'text'", @"form_control_type", |
| 520 @"undefined", @"autocomplete_attribute", |
| 521 GetDefaultMaxLengthString(), @"max_length", |
| 522 @"true", @"should_autocomplete", |
| 523 @"false", @"is_checkable", |
| 524 @"'Tennis'", @"value", |
| 525 @"'Tennis'", @"value_option_text", |
| 526 @"undefined", @"option_values", |
| 527 @"undefined", @"option_contents", |
| 528 nil]]; |
| 529 } |
| 530 |
| 531 NSArray* GetTestInputRadio() { |
| 532 return @[ |
| 533 @("<input type='radio' name='boolean' value='true'/> True" |
| 534 "<input type='radio' name='boolean' value='false'/> False"), |
| 535 [NSDictionary dictionaryWithObjectsAndKeys: |
| 536 @"'True'", @"label", |
| 537 @"'boolean'", @"name", |
| 538 @"'radio'", @"form_control_type", |
| 539 @"undefined", @"autocomplete_attribute", |
| 540 @"undefined", @"max_length", |
| 541 @"true", @"should_autocomplete", |
| 542 @"true", @"is_checkable", |
| 543 @"'true'", @"value", |
| 544 @"'true'", @"value_option_text", |
| 545 @"undefined", @"option_values", |
| 546 @"undefined", @"option_contents", |
| 547 nil], |
| 548 [NSDictionary dictionaryWithObjectsAndKeys: |
| 549 @"'False'", @"label", |
| 550 @"'boolean'", @"name", |
| 551 @"'radio'", @"form_control_type", |
| 552 @"undefined", @"autocomplete_attribute", |
| 553 @"undefined", @"max_length", |
| 554 @"true", @"should_autocomplete", |
| 555 @"true", @"is_checkable", |
| 556 @"'false'", @"value", |
| 557 @"'false'", @"value_option_text", |
| 558 @"undefined", @"option_values", |
| 559 @"undefined", @"option_contents", |
| 560 nil]]; |
| 561 } |
| 562 |
| 563 NSArray* GetTestInputCheckbox() { |
| 564 return @[ |
| 565 @("<input type='checkbox' name='vehicle' value='Bike'> Bicycle" |
| 566 "<input type='checkbox' name='vehicle' value='Car'> Automobile" |
| 567 "<input type='checkbox' name='vehicle' value='Rocket'> Missile"), |
| 568 [NSDictionary dictionaryWithObjectsAndKeys: |
| 569 @"'Bicycle'", @"label", |
| 570 @"'vehicle'", @"name", |
| 571 @"'checkbox'", @"form_control_type", |
| 572 @"undefined", @"autocomplete_attribute", |
| 573 @"undefined", @"max_length", |
| 574 @"true", @"should_autocomplete", |
| 575 @"true", @"is_checkable", |
| 576 @"'Bike'", @"value", |
| 577 @"'Bike'", @"value_option_text", |
| 578 @"undefined", @"option_values", |
| 579 @"undefined", @"option_contents", |
| 580 nil], |
| 581 [NSDictionary dictionaryWithObjectsAndKeys: |
| 582 @"'Automobile'", @"label", |
| 583 @"'vehicle'", @"name", |
| 584 @"'checkbox'", @"form_control_type", |
| 585 @"undefined", @"autocomplete_attribute", |
| 586 @"undefined", @"max_length", |
| 587 @"true", @"should_autocomplete", |
| 588 @"true", @"is_checkable", |
| 589 @"'Car'", @"value", |
| 590 @"'Car'", @"value_option_text", |
| 591 @"undefined", @"option_values", |
| 592 @"undefined", @"option_contents", |
| 593 nil], |
| 594 [NSDictionary dictionaryWithObjectsAndKeys: |
| 595 @"'Missile'", @"label", |
| 596 @"'vehicle'", @"name", |
| 597 @"'checkbox'", @"form_control_type", |
| 598 @"undefined", @"autocomplete_attribute", |
| 599 @"undefined", @"max_length", |
| 600 @"true", @"should_autocomplete", |
| 601 @"true", @"is_checkable", |
| 602 @"'Rocket'", @"value", |
| 603 @"'Rocket'", @"value_option_text", |
| 604 @"undefined", @"option_values", |
| 605 @"undefined", @"option_contents", |
| 606 nil]]; |
| 607 } |
| 608 |
| 609 NSArray* GetTestFormSelectElement() { |
| 610 return @[ |
| 611 @(" <label>State:" |
| 612 " <select name='state'>" |
| 613 " <option value='CA'>California</option>" |
| 614 " <option value='TX'>Texas</option>" |
| 615 " </select>" |
| 616 " </label>"), |
| 617 [NSDictionary dictionaryWithObjectsAndKeys: |
| 618 @"'State:'", @"label", |
| 619 @"'state'", @"name", |
| 620 @"'select-one'", @"form_control_type", |
| 621 @"undefined", @"autocomplete_attribute", |
| 622 @"undefined", @"max_length", |
| 623 @"true", @"should_autocomplete", |
| 624 @"undefined", @"is_checkable", |
| 625 @"'CA'", @"value", |
| 626 @"'California'", @"value_option_text", |
| 627 @[@"'CA'", @"'TX'"], @"option_values", |
| 628 @[@"'California'", @"'Texas'"], @"option_contents", |
| 629 nil]]; |
| 630 } |
| 631 |
| 632 NSArray* GetTestFormSelectElementWithOptgroup() { |
| 633 return @[ |
| 634 @(" <label>Course:" |
| 635 " <select name='course'>" |
| 636 " <optgroup label='8.01 Physics I: Classical Mechanics'>" |
| 637 " <option value='8.01.1'>Lecture 01: Powers of Ten" |
| 638 " <option value='8.01.2'>Lecture 02: 1D Kinematics" |
| 639 " <option value='8.01.3'>Lecture 03: Vectors" |
| 640 " <optgroup label='8.02 Electricity and Magnestism'>" |
| 641 " <option value='8.02.1'>Lecture 01: What holds world together?" |
| 642 " <option value='8.02.2'>Lecture 02: Electric Field" |
| 643 " <option value='8.02.3'>Lecture 03: Electric Flux" |
| 644 " </select>" |
| 645 " </label>"), |
| 646 [NSDictionary dictionaryWithObjectsAndKeys: |
| 647 @"'Course:'", @"label", |
| 648 @"'course'", @"name", |
| 649 @"'select-one'", @"form_control_type", |
| 650 @"undefined", @"autocomplete_attribute", |
| 651 @"undefined", @"max_length", |
| 652 @"true", @"should_autocomplete", |
| 653 @"undefined", @"is_checkable", |
| 654 @"'8.01.1'", @"value", |
| 655 @"'Lecture 01: Powers of Ten'", @"value_option_text", |
| 656 @[@"'8.01.1'", |
| 657 @"'8.01.2'", |
| 658 @"'8.01.3'", |
| 659 @"'8.02.1'", |
| 660 @"'8.02.2'", |
| 661 @"'8.02.3'"], @"option_values", |
| 662 @[@"'Lecture 01: Powers of Ten'", |
| 663 @"'Lecture 02: 1D Kinematics'", |
| 664 @"'Lecture 03: Vectors'", |
| 665 @"'Lecture 01: What holds world together?'", |
| 666 @"'Lecture 02: Electric Field'", |
| 667 @"'Lecture 03: Electric Flux'"], @"option_contents", |
| 668 nil]]; |
| 669 } |
| 670 |
| 671 // clang-format on |
| 672 |
| 673 // Generates JavaScripts to check a JavaScripts object |results| with the |
| 674 // expected values given in |expected|, which is a dictionary with string |
| 675 // values for all the keys other than @"option_vaues" and @"option_contents"; |
| 676 // the values of @"option_vaues" and @"option_contents" are arrays of |
| 677 // strings or undefined. Only attributes in |attributes_to_check| are checked. |
| 678 // A different expected value is chosen in |expected| for different |
| 679 // |extract_mask|. |
| 680 NSString* GenerateElementItemVerifyingJavaScripts( |
| 681 NSString* results, |
| 682 NSUInteger extract_mask, |
| 683 NSDictionary* expected, |
| 684 NSArray* attributes_to_check) { |
| 685 NSMutableArray* verifying_javascripts = [NSMutableArray array]; |
| 686 |
| 687 for (NSString* attribute in attributes_to_check) { |
| 688 if ([attribute isEqualToString:@"option_values"] || |
| 689 [attribute isEqualToString:@"option_contents"]) { |
| 690 id expected_value = [expected objectForKey:attribute]; |
| 691 if ([expected_value isKindOfClass:[NSString class]]) { |
| 692 [verifying_javascripts |
| 693 addObject:[NSString |
| 694 stringWithFormat:@"%@['%@']===%@", results, attribute, |
| 695 [expected objectForKey:attribute]]]; |
| 696 } else { |
| 697 for (NSUInteger i = 0; i < [(NSArray*)expected_value count]; ++i) { |
| 698 [verifying_javascripts |
| 699 addObject:[NSString |
| 700 stringWithFormat:@"%@['%@'][%" PRIuNS "] === %@", |
| 701 results, attribute, i, |
| 702 [expected_value objectAtIndex:i]]]; |
| 703 } |
| 704 } |
| 705 } else { |
| 706 NSString* expected_value = [expected objectForKey:attribute]; |
| 707 // Option text is used as value for extract_mask 1 << 1 |
| 708 if ((extract_mask & 1 << 1) && [attribute isEqualToString:@"value"]) |
| 709 expected_value = [expected objectForKey:@"value_option_text"]; |
| 710 [verifying_javascripts |
| 711 addObject:[NSString stringWithFormat:@"%@['%@']===%@", results, |
| 712 attribute, expected_value]]; |
| 713 } |
| 714 } |
| 715 |
| 716 return [verifying_javascripts componentsJoinedByString:@"&&"]; |
| 717 } |
| 718 |
| 719 // Generates JavaScripts to check a JavaScripts array |results| with the |
| 720 // expected values given in |expected|, which is an array of dictionaries; each |
| 721 // dictionary is the expected values of the corresponding item in |results|. |
| 722 // Only attributes in |attributes_to_check| are checked. A different expected |
| 723 // value is chosen in |expected| for different |extract_mask|. |
| 724 NSString* GenerateTestItemVerifyingJavaScripts(NSString* results, |
| 725 NSUInteger extract_mask, |
| 726 NSArray* expected, |
| 727 NSArray* attributed_to_check) { |
| 728 NSMutableArray* verifying_javascripts = [NSMutableArray array]; |
| 729 |
| 730 NSUInteger controlCount = 0; |
| 731 for (NSUInteger indexOfTestData = 0; indexOfTestData < [expected count]; |
| 732 ++indexOfTestData) { |
| 733 NSArray* expectedData = [expected objectAtIndex:indexOfTestData]; |
| 734 for (NSUInteger i = 1; i < [expectedData count]; ++i, ++controlCount) { |
| 735 NSDictionary* expected = [expectedData objectAtIndex:i]; |
| 736 NSString* itemVerifyingJavaScripts = |
| 737 GenerateElementItemVerifyingJavaScripts( |
| 738 [NSString stringWithFormat:@"%@['fields'][%" PRIuNS "]", results, |
| 739 controlCount], |
| 740 extract_mask, expected, attributed_to_check); |
| 741 [verifying_javascripts addObject:itemVerifyingJavaScripts]; |
| 742 } |
| 743 } |
| 744 return [verifying_javascripts componentsJoinedByString:@"&&"]; |
| 745 } |
| 746 |
| 747 // Test fixture to test autofill controller. |
| 748 class AutofillControllerJsTest : public web::WebJsTest<ChromeWebTest> { |
| 749 public: |
| 750 AutofillControllerJsTest() |
| 751 : web::WebJsTest<ChromeWebTest>(@[ @"autofill_controller" ]) {} |
| 752 |
| 753 protected: |
| 754 // Helper method that EXPECTs |javascript| evaluation on page |
| 755 // |kHTMLForTestingElements| with expectation given by |
| 756 // |elements_with_true_expected|. |
| 757 void TestExecutingBooleanJavaScriptOnElement( |
| 758 NSString* javascript, |
| 759 const ElementByName elements_with_true_expected[], |
| 760 size_t size_elements_with_true_expected); |
| 761 |
| 762 // Helper method that EXPECTs |
| 763 // |__gCrWeb.autofill.webFormControlElementToFormField|. This method applies |
| 764 // |__gCrWeb.autofill.webFormControlElementToFormField| on each element in |
| 765 // |test_data| with all possible extract masks and verify the results. |
| 766 void TestWebFormControlElementToFormField(NSArray* test_data, |
| 767 NSString* tag_name); |
| 768 |
| 769 // Helper method for testing |javascripts_statement| that evalutate |
| 770 // |attribute_name| of the elements in |test_data| which has tag name |
| 771 // |tag_name|. EXPECTs JavaScript evaluation on |
| 772 // "window.document.getElementsByTagName()" |
| 773 void TestInputElementDataEvaluation(NSString* javascripts_statement, |
| 774 NSString* attribute_name, |
| 775 NSArray* test_data, |
| 776 NSString* tag_name); |
| 777 |
| 778 // Helper method that EXPECTs |__gCrWeb.autofill.webFormElementToFormData| on |
| 779 // a form element obtained by |get_form_element_javascripts|. The results |
| 780 // are verified with |verifying_java_scripts|. |
| 781 void TestWebFormElementToFormDataForOneForm( |
| 782 NSString* get_form_element_javascripts, |
| 783 NSUInteger extract_mask, |
| 784 NSString* expected_result, |
| 785 NSString* verifying_javascripts); |
| 786 |
| 787 // EXPECTs |__gCrWeb.autofill.webFormElementToFormData| on all the test data. |
| 788 void TestWebFormElementToFormData(NSArray* test_items); |
| 789 |
| 790 // EXPECTs |__gCrWeb.autofill.extractNewForms| on |html|. |
| 791 void TestExtractNewForms(NSString* html, |
| 792 BOOL is_origin_window_location, |
| 793 NSArray* expected_items); |
| 794 }; |
| 795 |
| 796 void AutofillControllerJsTest::TestExecutingBooleanJavaScriptOnElement( |
| 797 NSString* javascript, |
| 798 const ElementByName elements_with_true_expected[], |
| 799 size_t size_elements_with_true_expected) { |
| 800 // Elements in |kHTMLForTestingElements|. |
| 801 const ElementByName elementsByName[] = { |
| 802 {"hl", 0, -1}, |
| 803 {"firstname", 0, -1}, |
| 804 {"lastname", 0, -1}, |
| 805 {"email", 0, -1}, |
| 806 {"phone", 0, -1}, |
| 807 {"blog", 0, -1}, |
| 808 {"expected number of clicks", 0, -1}, |
| 809 {"pwd", 0, -1}, |
| 810 {"vehicle", 0, -1}, |
| 811 {"vehicle", 1, -1}, |
| 812 {"vehicle", 2, -1}, |
| 813 {"boolean", 0, -1}, |
| 814 {"boolean", 1, -1}, |
| 815 {"boolean", 2, -1}, |
| 816 {"state", 0, -1}, |
| 817 {"state", 0, 0}, |
| 818 {"state", 0, 1}, |
| 819 {"course", 0, -1}, |
| 820 {"course", 0, 0}, |
| 821 {"course", 0, 1}, |
| 822 {"course", 0, 2}, |
| 823 {"course", 0, 3}, |
| 824 {"course", 0, 4}, |
| 825 {"course", 0, 5}, |
| 826 {"cars", 0, -1}, |
| 827 {"cars", 0, 0}, |
| 828 {"cars", 0, 1}, |
| 829 {"cars", 0, 2}, |
| 830 {"cars", 0, 3}, |
| 831 {"submit", 0, -1}, |
| 832 }; |
| 833 |
| 834 LoadHtmlAndInject(kHTMLForTestingElements); |
| 835 ExecuteBooleanJavaScriptOnElementsAndCheck( |
| 836 javascript, |
| 837 GetElementsByNameJavaScripts(elementsByName, arraysize(elementsByName)), |
| 838 GetElementsByNameJavaScripts(elements_with_true_expected, |
| 839 size_elements_with_true_expected)); |
| 840 } |
| 841 |
| 842 TEST_F(AutofillControllerJsTest, HasTagName) { |
| 843 const ElementByName elements_expecting_true[] = { |
| 844 {"hl", 0, -1}, |
| 845 {"firstname", 0, -1}, |
| 846 {"lastname", 0, -1}, |
| 847 {"email", 0, -1}, |
| 848 {"phone", 0, -1}, |
| 849 {"blog", 0, -1}, |
| 850 {"expected number of clicks", 0, -1}, |
| 851 {"pwd", 0, -1}, |
| 852 {"vehicle", 0, -1}, |
| 853 {"vehicle", 1, -1}, |
| 854 {"vehicle", 2, -1}, |
| 855 {"boolean", 0, -1}, |
| 856 {"boolean", 1, -1}, |
| 857 {"boolean", 2, -1}, |
| 858 {"submit", 0, -1}, |
| 859 }; |
| 860 |
| 861 TestExecutingBooleanJavaScriptOnElement( |
| 862 @"__gCrWeb.autofill.hasTagName(%@, 'input')", elements_expecting_true, |
| 863 arraysize(elements_expecting_true)); |
| 864 } |
| 865 |
| 866 TEST_F(AutofillControllerJsTest, CombineAndCollapseWhitespace) { |
| 867 LoadHtmlAndInject(@"<html><body></body></html>"); |
| 868 |
| 869 EXPECT_NSEQ(@"foobar", ExecuteJavaScriptWithFormat( |
| 870 @"__gCrWeb.autofill.combineAndCollapseWhitespace('" |
| 871 @"foo', 'bar', false)")); |
| 872 EXPECT_NSEQ(@"foo bar", ExecuteJavaScriptWithFormat( |
| 873 @"__gCrWeb.autofill.combineAndCollapseWhitespace(" |
| 874 @"'foo', 'bar', true)")); |
| 875 EXPECT_NSEQ(@"foo bar", ExecuteJavaScriptWithFormat( |
| 876 @"__gCrWeb.autofill.combineAndCollapseWhitespace(" |
| 877 "'foo ', 'bar', false)")); |
| 878 EXPECT_NSEQ(@"foo bar", ExecuteJavaScriptWithFormat( |
| 879 @"__gCrWeb.autofill.combineAndCollapseWhitespace(" |
| 880 "'foo', ' bar', false)")); |
| 881 EXPECT_NSEQ(@"foo bar", ExecuteJavaScriptWithFormat( |
| 882 @"__gCrWeb.autofill.combineAndCollapseWhitespace(" |
| 883 "'foo', ' bar', true)")); |
| 884 EXPECT_NSEQ(@"foo bar", ExecuteJavaScriptWithFormat( |
| 885 @"__gCrWeb.autofill.combineAndCollapseWhitespace(" |
| 886 "'foo ', ' bar', false)")); |
| 887 EXPECT_NSEQ(@"foobar ", ExecuteJavaScriptWithFormat( |
| 888 @"__gCrWeb.autofill.combineAndCollapseWhitespace(" |
| 889 "'foo', 'bar ', false)")); |
| 890 EXPECT_NSEQ(@" foo bar", |
| 891 ExecuteJavaScriptWithFormat( |
| 892 @"__gCrWeb.autofill.combineAndCollapseWhitespace(" |
| 893 "' foo', 'bar', true)")); |
| 894 } |
| 895 |
| 896 void AutofillControllerJsTest::TestInputElementDataEvaluation( |
| 897 NSString* javascripts_statement, |
| 898 NSString* attribute_name, |
| 899 NSArray* test_data, |
| 900 NSString* tag_name) { |
| 901 NSString* html_fragment = [test_data objectAtIndex:0U]; |
| 902 LoadHtmlAndInject(html_fragment); |
| 903 |
| 904 for (NSUInteger i = 1; i < [test_data count]; ++i) { |
| 905 NSString* get_element_javascripts = [NSString |
| 906 stringWithFormat:@"window.document.getElementsByTagName('%@')[%" PRIuNS |
| 907 "]", |
| 908 tag_name, i - 1]; |
| 909 id actual = ExecuteJavaScriptWithFormat( |
| 910 @"%@(%@) === %@", javascripts_statement, get_element_javascripts, |
| 911 [[test_data objectAtIndex:i] objectForKey:attribute_name]); |
| 912 EXPECT_NSEQ(@YES, actual); |
| 913 } |
| 914 } |
| 915 |
| 916 TEST_F(AutofillControllerJsTest, InferLabelFromPrevious) { |
| 917 TestInputElementDataEvaluation( |
| 918 @"__gCrWeb.autofill.inferLabelFromPrevious", @"label", |
| 919 GetTestFormInputElementWithLabelFromPrevious(), @"input"); |
| 920 } |
| 921 |
| 922 TEST_F(AutofillControllerJsTest, InferLabelFromPreviousSpan) { |
| 923 TestInputElementDataEvaluation( |
| 924 @"__gCrWeb.autofill.inferLabelFromPrevious", @"label", |
| 925 GetTestFormInputElementWithLabelFromPreviousSpan(), @"input"); |
| 926 } |
| 927 |
| 928 TEST_F(AutofillControllerJsTest, InferLabelFromPreviousParagraph) { |
| 929 TestInputElementDataEvaluation( |
| 930 @"__gCrWeb.autofill.inferLabelFromPrevious", @"label", |
| 931 GetTestFormInputElementWithLabelFromPreviousParagraph(), @"input"); |
| 932 } |
| 933 |
| 934 TEST_F(AutofillControllerJsTest, InferLabelFromPreviousLabel) { |
| 935 TestInputElementDataEvaluation( |
| 936 @"__gCrWeb.autofill.inferLabelFromPrevious", @"label", |
| 937 GetTestFormInputElementWithLabelFromPreviousLabel(), @"input"); |
| 938 } |
| 939 |
| 940 TEST_F(AutofillControllerJsTest, InferLabelFromPreviousLabelOtherIgnored) { |
| 941 TestInputElementDataEvaluation( |
| 942 @"__gCrWeb.autofill.inferLabelFromPrevious", @"label", |
| 943 GetTestFormInputElementWithLabelFromPreviousLabelOtherIgnored(), |
| 944 @"input"); |
| 945 } |
| 946 |
| 947 TEST_F(AutofillControllerJsTest, InferLabelFromPreviousTextBrAndSpan) { |
| 948 TestInputElementDataEvaluation( |
| 949 @"__gCrWeb.autofill.inferLabelFromPrevious", @"label", |
| 950 GetTestFormInputElementWithLabelFromPreviousTextBrAndSpan(), @"input"); |
| 951 } |
| 952 |
| 953 TEST_F(AutofillControllerJsTest, InferLabelFromListItem) { |
| 954 TestInputElementDataEvaluation( |
| 955 @"__gCrWeb.autofill.inferLabelFromListItem", @"label", |
| 956 GetTestFormInputElementWithLabelFromListItem(), @"input"); |
| 957 } |
| 958 |
| 959 TEST_F(AutofillControllerJsTest, InferLabelFromTableColumnTD) { |
| 960 TestInputElementDataEvaluation( |
| 961 @"__gCrWeb.autofill.inferLabelFromTableColumn", @"label", |
| 962 GetTestFormInputElementWithLabelFromTableColumnTD(), @"input"); |
| 963 } |
| 964 |
| 965 TEST_F(AutofillControllerJsTest, InferLabelFromTableColumnTH) { |
| 966 TestInputElementDataEvaluation( |
| 967 @"__gCrWeb.autofill.inferLabelFromTableColumn", @"label", |
| 968 GetTestFormInputElementWithLabelFromTableColumnTH(), @"input"); |
| 969 } |
| 970 |
| 971 TEST_F(AutofillControllerJsTest, InferLabelFromTableColumnNested) { |
| 972 TestInputElementDataEvaluation( |
| 973 @"__gCrWeb.autofill.inferLabelFromTableColumn", @"label", |
| 974 GetTestFormInputElementWithLabelFromTableNested(), @"input"); |
| 975 } |
| 976 |
| 977 TEST_F(AutofillControllerJsTest, InferLabelFromTableRow) { |
| 978 TestInputElementDataEvaluation( |
| 979 @"__gCrWeb.autofill.inferLabelFromTableRow", @"label", |
| 980 GetTestFormInputElementWithLabelFromTableRow(), @"input"); |
| 981 } |
| 982 |
| 983 TEST_F(AutofillControllerJsTest, InferLabelFromDivTable) { |
| 984 TestInputElementDataEvaluation( |
| 985 @"__gCrWeb.autofill.inferLabelFromDivTable", @"label", |
| 986 GetTestFormInputElementWithLabelFromDivTable(), @"input"); |
| 987 } |
| 988 |
| 989 TEST_F(AutofillControllerJsTest, InferLabelFromDefinitionList) { |
| 990 TestInputElementDataEvaluation( |
| 991 @"__gCrWeb.autofill.inferLabelFromDefinitionList", @"label", |
| 992 GetTestFormInputElementWithLabelFromDefinitionList(), @"input"); |
| 993 } |
| 994 |
| 995 TEST_F(AutofillControllerJsTest, InferLabelForElement) { |
| 996 NSArray* testingElements = @[ |
| 997 GetTestFormInputElementWithLabelFromPrevious(), |
| 998 GetTestFormInputElementWithLabelFromPreviousSpan(), |
| 999 GetTestFormInputElementWithLabelFromPreviousParagraph(), |
| 1000 GetTestFormInputElementWithLabelFromPreviousLabel(), |
| 1001 GetTestFormInputElementWithLabelFromPreviousLabelOtherIgnored(), |
| 1002 GetTestFormInputElementWithLabelFromPreviousTextSpanBr(), |
| 1003 GetTestFormInputElementWithLabelFromPreviousTextBrAndSpan(), |
| 1004 GetTestFormInputElementWithLabelFromListItem(), |
| 1005 GetTestFormInputElementWithLabelFromTableColumnTD(), |
| 1006 GetTestFormInputElementWithLabelFromTableColumnTH(), |
| 1007 GetTestFormInputElementWithLabelFromTableNested(), |
| 1008 GetTestFormInputElementWithLabelFromTableRow(), |
| 1009 GetTestFormInputElementWithLabelFromDivTable(), |
| 1010 GetTestFormInputElementWithLabelFromDefinitionList(), GetTestInputRadio(), |
| 1011 GetTestInputCheckbox() |
| 1012 ]; |
| 1013 for (NSArray* testingElement in testingElements) { |
| 1014 TestInputElementDataEvaluation(@"__gCrWeb.autofill.inferLabelForElement", |
| 1015 @"label", testingElement, @"input"); |
| 1016 } |
| 1017 |
| 1018 TestInputElementDataEvaluation(@"__gCrWeb.autofill.inferLabelForElement", |
| 1019 @"label", GetTestFormSelectElement(), |
| 1020 @"select"); |
| 1021 |
| 1022 TestInputElementDataEvaluation( |
| 1023 @"__gCrWeb.autofill.inferLabelForElement", @"label", |
| 1024 GetTestFormSelectElementWithOptgroup(), @"select"); |
| 1025 } |
| 1026 |
| 1027 TEST_F(AutofillControllerJsTest, IsAutofillableElement) { |
| 1028 const ElementByName elements_expecting_true[] = { |
| 1029 {"firstname", 0, -1}, {"lastname", 0, -1}, |
| 1030 {"email", 0, -1}, {"phone", 0, -1}, |
| 1031 {"blog", 0, -1}, {"expected number of clicks", 0, -1}, |
| 1032 {"pwd", 0, -1}, {"vehicle", 0, -1}, |
| 1033 {"vehicle", 1, -1}, {"vehicle", 2, -1}, |
| 1034 {"boolean", 0, -1}, {"boolean", 1, -1}, |
| 1035 {"boolean", 2, -1}, {"state", 0, -1}, |
| 1036 {"course", 0, -1}, |
| 1037 }; |
| 1038 |
| 1039 TestExecutingBooleanJavaScriptOnElement( |
| 1040 @"__gCrWeb.autofill.isAutofillableElement(%@)", elements_expecting_true, |
| 1041 arraysize(elements_expecting_true)); |
| 1042 } |
| 1043 |
| 1044 TEST_F(AutofillControllerJsTest, GetOptionStringsFromElement) { |
| 1045 ElementByName testing_elements[] = { |
| 1046 {"state", 0, -1}, {"course", 0, -1}, {"cars", 0, -1}}; |
| 1047 |
| 1048 LoadHtmlAndInject(kHTMLForTestingElements); |
| 1049 ExecuteJavaScriptOnElementsAndCheck( |
| 1050 @"var field = {};" |
| 1051 "__gCrWeb.autofill.getOptionStringsFromElement(%@, field);" |
| 1052 "__gCrWeb.stringify(field);", |
| 1053 GetElementsByNameJavaScripts(testing_elements, |
| 1054 arraysize(testing_elements)), |
| 1055 @[ |
| 1056 @("{\"option_values\":[\"CA\",\"MA\"]," |
| 1057 "\"option_contents\":[\"CA\",\"MA\"]}"), |
| 1058 @("{\"option_values\":[" |
| 1059 "\"8.01.1\",\"8.01.2\",\"8.01.3\"," |
| 1060 "\"8.02.1\",\"8.02.2\",\"8.02.3\"]," |
| 1061 "\"option_contents\":[" |
| 1062 "\"Lecture 01: Powers of Ten\"," |
| 1063 "\"Lecture 02: 1D Kinematics\"," |
| 1064 "\"Lecture 03: Vectors\"," |
| 1065 "\"Lecture 01: What holds our world together?\"," |
| 1066 "\"Lecture 02: Electric Field\"," |
| 1067 "\"Lecture 03: Electric Flux\"" |
| 1068 "]}"), |
| 1069 @("{\"option_values\":[\"volvo\",\"saab\",\"opel\",\"audi\"]," |
| 1070 "\"option_contents\":[\"Volvo\",\"Saab\",\"Opel\",\"Audi\"]}") |
| 1071 ]); |
| 1072 } |
| 1073 |
| 1074 TEST_F(AutofillControllerJsTest, FillFormField) { |
| 1075 LoadHtmlAndInject(kHTMLForTestingElements); |
| 1076 |
| 1077 // Test text and select elements of which the value should be changed. |
| 1078 const ElementByName elements[] = { |
| 1079 {"firstname", 0, -1}, {"state", 0, -1}, |
| 1080 }; |
| 1081 NSArray* values = @[ |
| 1082 @"new name", |
| 1083 @"MA", |
| 1084 ]; |
| 1085 for (size_t i = 0; i < arraysize(elements); ++i) { |
| 1086 NSString* get_element_javascript = GetElementByNameJavaScript(elements[i]); |
| 1087 NSString* new_value = [values objectAtIndex:i]; |
| 1088 EXPECT_NSEQ(new_value, |
| 1089 ExecuteJavaScriptWithFormat( |
| 1090 @"var element=%@;var data={'value':'%@'};" |
| 1091 @"__gCrWeb.autofill.fillFormField(data, element);" |
| 1092 @"element.value", |
| 1093 get_element_javascript, new_value)); |
| 1094 } |
| 1095 |
| 1096 // Test clickable elements, of which 'checked' should be updated. |
| 1097 ElementByName checkable_elements[] = { |
| 1098 {"vehicle", 0, -1}, {"vehicle", 1, -1}, {"vehicle", 2, -1}, |
| 1099 {"boolean", 0, -1}, {"boolean", 1, -1}, {"boolean", 2, -1}, |
| 1100 }; |
| 1101 const bool final_is_checked_values[] = { |
| 1102 true, false, true, false, true, true, |
| 1103 }; |
| 1104 for (size_t i = 0; i < arraysize(checkable_elements); ++i) { |
| 1105 NSString* get_element_javascript = |
| 1106 GetElementByNameJavaScript(checkable_elements[i]); |
| 1107 bool is_checked = final_is_checked_values[i]; |
| 1108 |
| 1109 EXPECT_NSEQ( |
| 1110 @(is_checked), |
| 1111 ExecuteJavaScriptWithFormat( |
| 1112 @"var element=%@; var value=element.value; " |
| 1113 @"var data={'value':value,'is_checked':%@};" |
| 1114 @"__gCrWeb.autofill.fillFormField(data, element); element.checked", |
| 1115 get_element_javascript, is_checked ? @"true" : @"false")); |
| 1116 } |
| 1117 |
| 1118 // Test elements of which the value should not be changed. |
| 1119 ElementByName unchanged_elements[] = { |
| 1120 {"hl", 0, -1}, // hidden element |
| 1121 {"state", 0, 0}, // option element |
| 1122 {"state", 0, 1}, // option element |
| 1123 }; |
| 1124 for (size_t i = 0; i < arraysize(unchanged_elements); ++i) { |
| 1125 NSString* get_element_javascript = |
| 1126 GetElementByNameJavaScript(unchanged_elements[i]); |
| 1127 NSString* actual = ExecuteJavaScriptWithFormat( |
| 1128 @"var element=%@;" |
| 1129 @"var oldValue=element.value; var data={'value':'new'};" |
| 1130 @"__gCrWeb.autofill.fillFormField(data, element);" |
| 1131 @"element.value === oldValue", |
| 1132 get_element_javascript); |
| 1133 EXPECT_NSEQ(@YES, actual); |
| 1134 } |
| 1135 } |
| 1136 |
| 1137 TEST_F(AutofillControllerJsTest, IsTextInput) { |
| 1138 const ElementByName elements_expecting_true[] = { |
| 1139 {"firstname", 0, -1}, {"lastname", 0, -1}, |
| 1140 {"email", 0, -1}, {"phone", 0, -1}, |
| 1141 {"blog", 0, -1}, {"expected number of clicks", 0, -1}, |
| 1142 {"pwd", 0, -1}, |
| 1143 }; |
| 1144 |
| 1145 TestExecutingBooleanJavaScriptOnElement(@"__gCrWeb.autofill.isTextInput(%@)", |
| 1146 elements_expecting_true, |
| 1147 arraysize(elements_expecting_true)); |
| 1148 } |
| 1149 |
| 1150 TEST_F(AutofillControllerJsTest, IsSelectElement) { |
| 1151 const ElementByName elements_expecting_true[] = { |
| 1152 {"state", 0, -1}, {"course", 0, -1}, |
| 1153 }; |
| 1154 |
| 1155 TestExecutingBooleanJavaScriptOnElement( |
| 1156 @"__gCrWeb.autofill.isSelectElement(%@)", elements_expecting_true, |
| 1157 arraysize(elements_expecting_true)); |
| 1158 } |
| 1159 |
| 1160 TEST_F(AutofillControllerJsTest, IsCheckableElement) { |
| 1161 const ElementByName elements_expecting_true[] = { |
| 1162 {"vehicle", 0, -1}, {"vehicle", 1, -1}, {"vehicle", 2, -1}, |
| 1163 {"boolean", 0, -1}, {"boolean", 1, -1}, {"boolean", 2, -1}, |
| 1164 }; |
| 1165 |
| 1166 TestExecutingBooleanJavaScriptOnElement( |
| 1167 @"__gCrWeb.autofill.isCheckableElement(%@)", elements_expecting_true, |
| 1168 arraysize(elements_expecting_true)); |
| 1169 } |
| 1170 |
| 1171 TEST_F(AutofillControllerJsTest, IsAutofillableInputElement) { |
| 1172 const ElementByName elements_expecting_true[] = { |
| 1173 {"firstname", 0, -1}, {"lastname", 0, -1}, |
| 1174 {"email", 0, -1}, {"phone", 0, -1}, |
| 1175 {"blog", 0, -1}, {"expected number of clicks", 0, -1}, |
| 1176 {"pwd", 0, -1}, {"vehicle", 0, -1}, |
| 1177 {"vehicle", 1, -1}, {"vehicle", 2, -1}, |
| 1178 {"boolean", 0, -1}, {"boolean", 1, -1}, |
| 1179 {"boolean", 2, -1}, |
| 1180 }; |
| 1181 |
| 1182 TestExecutingBooleanJavaScriptOnElement( |
| 1183 @"__gCrWeb.autofill.isAutofillableInputElement(%@)", |
| 1184 elements_expecting_true, arraysize(elements_expecting_true)); |
| 1185 } |
| 1186 |
| 1187 TEST_F(AutofillControllerJsTest, ExtractAutofillableElements) { |
| 1188 LoadHtmlAndInject(kHTMLForTestingElements); |
| 1189 ElementByName expected_elements[] = { |
| 1190 {"firstname", 0, -1}, {"lastname", 0, -1}, |
| 1191 {"email", 0, -1}, {"phone", 0, -1}, |
| 1192 {"blog", 0, -1}, {"expected number of clicks", 0, -1}, |
| 1193 {"pwd", 0, -1}, {"vehicle", 0, -1}, |
| 1194 {"vehicle", 1, -1}, {"vehicle", 2, -1}, |
| 1195 {"boolean", 0, -1}, {"boolean", 1, -1}, |
| 1196 {"boolean", 2, -1}, {"state", 0, -1}, |
| 1197 }; |
| 1198 NSArray* expected = GetElementsByNameJavaScripts( |
| 1199 expected_elements, arraysize(expected_elements)); |
| 1200 |
| 1201 NSString* parameter = @"window.document.getElementsByTagName('form')[0]"; |
| 1202 for (NSUInteger index = 0; index < [expected count]; index++) { |
| 1203 EXPECT_NSEQ(@YES, |
| 1204 ExecuteJavaScriptWithFormat( |
| 1205 @"var controlElements=" |
| 1206 "__gCrWeb.autofill.extractAutofillableElementsInForm(%@);" |
| 1207 "controlElements[%" PRIuNS "] === %@", |
| 1208 parameter, index, expected[index])); |
| 1209 } |
| 1210 } |
| 1211 |
| 1212 void AutofillControllerJsTest::TestWebFormControlElementToFormField( |
| 1213 NSArray* test_data, |
| 1214 NSString* tag_name) { |
| 1215 LoadHtmlAndInject([test_data firstObject]); |
| 1216 |
| 1217 for (NSUInteger i = 0; i < arraysize(kFormExtractMasks); ++i) { |
| 1218 ExtractMask extract_mask = kFormExtractMasks[i]; |
| 1219 NSArray* attributes_to_check = |
| 1220 GetFormFieldAttributeListsToCheck(extract_mask); |
| 1221 |
| 1222 for (NSUInteger i = 1; i < [test_data count]; ++i) { |
| 1223 NSString* get_element_to_test = |
| 1224 [NSString stringWithFormat:@"var element = " |
| 1225 "window.document.getElementsByTagName('%" |
| 1226 "@')[%" PRIuNS "]", |
| 1227 tag_name, i - 1]; |
| 1228 NSDictionary* expected = [test_data objectAtIndex:i]; |
| 1229 // Generates JavaScripts to verify the results. Parameter |results| is |
| 1230 // @"field" as in the evaluation JavaScripts the results are returned in |
| 1231 // |field|. |
| 1232 NSString* verifying_javascripts = GenerateElementItemVerifyingJavaScripts( |
| 1233 @"field", extract_mask, expected, attributes_to_check); |
| 1234 EXPECT_NSEQ(@YES, |
| 1235 ExecuteJavaScriptWithFormat( |
| 1236 @"%@; var field = {};" |
| 1237 "__gCrWeb.autofill.webFormControlElementToFormField(" |
| 1238 " element, %u, field);" |
| 1239 "%@", |
| 1240 get_element_to_test, extract_mask, verifying_javascripts)) |
| 1241 << base::SysNSStringToUTF8([NSString |
| 1242 stringWithFormat: |
| 1243 @"webFormControlElementToFormField actual results are: " |
| 1244 @"%@, \n" |
| 1245 "expected to be verified by %@", |
| 1246 ExecuteJavaScriptWithFormat( |
| 1247 @"%@; var field = {};" |
| 1248 "__gCrWeb.autofill.webFormControlElementToFormField(" |
| 1249 " element, %u, field);__gCrWeb.stringify(field);", |
| 1250 get_element_to_test, extract_mask), |
| 1251 verifying_javascripts]); |
| 1252 } |
| 1253 } |
| 1254 } |
| 1255 |
| 1256 TEST_F(AutofillControllerJsTest, WebFormControlElementToFormField) { |
| 1257 NSArray* test_input_elements = @[ |
| 1258 GetTestFormInputElementWithLabelFromPrevious(), |
| 1259 GetTestFormInputElementWithLabelFromPreviousSpan(), |
| 1260 GetTestFormInputElementWithLabelFromPreviousParagraph(), |
| 1261 GetTestFormInputElementWithLabelFromPreviousLabel(), |
| 1262 GetTestFormInputElementWithLabelFromPreviousLabelOtherIgnored(), |
| 1263 GetTestFormInputElementWithLabelFromPreviousTextBrAndSpan(), |
| 1264 GetTestFormInputElementWithLabelFromPreviousTextSpanBr(), |
| 1265 GetTestFormInputElementWithLabelFromListItem(), |
| 1266 GetTestFormInputElementWithLabelFromTableColumnTD(), |
| 1267 GetTestFormInputElementWithLabelFromTableColumnTH(), |
| 1268 GetTestFormInputElementWithLabelFromTableNested(), |
| 1269 GetTestFormInputElementWithLabelFromTableRow(), |
| 1270 GetTestFormInputElementWithLabelFromDivTable(), |
| 1271 GetTestFormInputElementWithLabelFromDefinitionList(), GetTestInputRadio(), |
| 1272 GetTestInputCheckbox() |
| 1273 ]; |
| 1274 for (NSArray* test_element in test_input_elements) { |
| 1275 TestWebFormControlElementToFormField(test_element, @"input"); |
| 1276 } |
| 1277 |
| 1278 TestWebFormControlElementToFormField(GetTestFormSelectElement(), @"select"); |
| 1279 TestWebFormControlElementToFormField(GetTestFormSelectElementWithOptgroup(), |
| 1280 @"select"); |
| 1281 } |
| 1282 |
| 1283 void AutofillControllerJsTest::TestWebFormElementToFormDataForOneForm( |
| 1284 NSString* get_form_element_javascripts, |
| 1285 NSUInteger extract_mask, |
| 1286 NSString* expected_result, |
| 1287 NSString* verifying_javascripts) { |
| 1288 NSString* actual = ExecuteJavaScriptWithFormat( |
| 1289 @"var form={}; var field={};" |
| 1290 "(__gCrWeb.autofill.webFormElementToFormData(" |
| 1291 "window, %@, null, %" PRIuNS ", form, field) === %@) && %@", |
| 1292 get_form_element_javascripts, extract_mask, expected_result, |
| 1293 verifying_javascripts); |
| 1294 |
| 1295 EXPECT_NSEQ(@YES, actual) << base::SysNSStringToUTF8([NSString |
| 1296 stringWithFormat:@"Actual:\n%@; expected to be verifyied by\n%@", |
| 1297 ExecuteJavaScriptWithFormat( |
| 1298 @"var form={};" |
| 1299 "__gCrWeb.autofill." |
| 1300 "webFormElementToFormData(window, %@, null," |
| 1301 "%" PRIuNS ", form, null);" |
| 1302 "__gCrWeb.stringify(form);", |
| 1303 get_form_element_javascripts, extract_mask), |
| 1304 verifying_javascripts]); |
| 1305 } |
| 1306 |
| 1307 void AutofillControllerJsTest::TestWebFormElementToFormData( |
| 1308 NSArray* test_items) { |
| 1309 NSString* form_html_fragment = |
| 1310 @"<form name='TestForm' action='http://cnn.com' method='post'>"; |
| 1311 for (NSUInteger i = 0; i < [test_items count]; ++i) { |
| 1312 form_html_fragment = [form_html_fragment |
| 1313 stringByAppendingString:[[test_items objectAtIndex:i] |
| 1314 objectAtIndex:0U]]; |
| 1315 } |
| 1316 form_html_fragment = [form_html_fragment stringByAppendingString:@"</form>"]; |
| 1317 LoadHtmlAndInject(form_html_fragment); |
| 1318 |
| 1319 NSString* parameter = @"document.getElementsByTagName('form')[0]"; |
| 1320 for (NSUInteger extract_index = 0; |
| 1321 extract_index < arraysize(kFormExtractMasks); ++extract_index) { |
| 1322 NSString* expected_result = @"true"; |
| 1323 // We don't verify 'action' here as action is generated as a complete url |
| 1324 // and here data url is used. |
| 1325 NSMutableArray* verifying_javascripts = [NSMutableArray |
| 1326 arrayWithObjects:@"form['name'] === 'TestForm'", |
| 1327 @"form['method'] === 'post'", |
| 1328 @"form['origin'] === window.location.href", nil]; |
| 1329 ExtractMask extract_mask = kFormExtractMasks[extract_index]; |
| 1330 [verifying_javascripts |
| 1331 addObject:GenerateTestItemVerifyingJavaScripts( |
| 1332 @"form", extract_mask, test_items, |
| 1333 GetFormFieldAttributeListsToCheck(extract_mask))]; |
| 1334 TestWebFormElementToFormDataForOneForm( |
| 1335 parameter, extract_mask, expected_result, |
| 1336 [verifying_javascripts componentsJoinedByString:@"&&"]); |
| 1337 } |
| 1338 } |
| 1339 |
| 1340 TEST_F(AutofillControllerJsTest, WebFormElementToFormData) { |
| 1341 NSArray* test_elements = @[ |
| 1342 GetTestFormInputElementWithLabelFromPrevious(), |
| 1343 GetTestFormInputElementWithLabelFromPreviousSpan(), |
| 1344 GetTestFormInputElementWithLabelFromPreviousParagraph(), |
| 1345 GetTestFormInputElementWithLabelFromPreviousLabel(), |
| 1346 GetTestFormInputElementWithLabelFromPreviousLabelOtherIgnored(), |
| 1347 GetTestFormInputElementWithLabelFromPreviousTextSpanBr(), |
| 1348 GetTestFormInputElementWithLabelFromPreviousTextBrAndSpan(), |
| 1349 GetTestFormInputElementWithLabelFromListItem(), |
| 1350 GetTestFormInputElementWithLabelFromTableColumnTD(), |
| 1351 GetTestFormInputElementWithLabelFromTableColumnTH(), |
| 1352 GetTestFormInputElementWithLabelFromTableNested(), |
| 1353 GetTestFormInputElementWithLabelFromTableRow(), |
| 1354 GetTestFormInputElementWithLabelFromDivTable(), |
| 1355 GetTestFormInputElementWithLabelFromDefinitionList(), GetTestInputRadio(), |
| 1356 GetTestInputCheckbox(), GetTestFormSelectElement(), |
| 1357 GetTestFormSelectElementWithOptgroup() |
| 1358 ]; |
| 1359 // Test a form that has a signle item in the array. |
| 1360 for (NSArray* testElement in test_elements) { |
| 1361 TestWebFormElementToFormData(@[ testElement ]); |
| 1362 } |
| 1363 |
| 1364 // Also test a form that has all the above items. |
| 1365 TestWebFormElementToFormData(test_elements); |
| 1366 } |
| 1367 |
| 1368 TEST_F(AutofillControllerJsTest, |
| 1369 DISABLED_WebFormElementToFormDataTooManyFields) { |
| 1370 NSString* html_fragment = |
| 1371 @"<FORM name='Test' action='http://c.com' method='post'>"; |
| 1372 // In autofill_controller.js, the maximum number of parsable element is 100 |
| 1373 // (__gCrWeb.autofill.MAX_PARSEABE_FIELDS = 100). Here an HTML page with 101 |
| 1374 // elements is generated for testing. |
| 1375 for (NSUInteger index = 0; index < 101; ++index) { |
| 1376 html_fragment = |
| 1377 [html_fragment stringByAppendingFormat:@"<INPUT type='text'/>"]; |
| 1378 } |
| 1379 html_fragment = [html_fragment stringByAppendingFormat:@"</FORM>"]; |
| 1380 |
| 1381 LoadHtmlAndInject(html_fragment); |
| 1382 TestWebFormElementToFormDataForOneForm( |
| 1383 @"document.getElementsByTagName('form')[0]", 1, @"false", @"true"); |
| 1384 } |
| 1385 |
| 1386 TEST_F(AutofillControllerJsTest, WebFormElementToFormEmpty) { |
| 1387 NSString* html_fragment = |
| 1388 @"<FORM name='Test' action='http://c.com' method='post'>"; |
| 1389 html_fragment = [html_fragment stringByAppendingFormat:@"</FORM>"]; |
| 1390 |
| 1391 LoadHtmlAndInject(html_fragment); |
| 1392 TestWebFormElementToFormDataForOneForm( |
| 1393 @"document.getElementsByTagName('form')[0]", 1, @"false", @"true"); |
| 1394 } |
| 1395 |
| 1396 void AutofillControllerJsTest::TestExtractNewForms( |
| 1397 NSString* html, |
| 1398 BOOL is_origin_window_location, |
| 1399 NSArray* expected_items) { |
| 1400 LoadHtmlAndInject(html); |
| 1401 // Generates verifying javascripts. |
| 1402 NSMutableArray* verifying_javascripts = [NSMutableArray array]; |
| 1403 for (NSUInteger i = 0U; i < [expected_items count]; ++i) { |
| 1404 [verifying_javascripts |
| 1405 addObject:[NSString stringWithFormat:@"forms[%" PRIuNS |
| 1406 "]['name'] === 'TestForm'", |
| 1407 i]]; |
| 1408 [verifying_javascripts |
| 1409 addObject:[NSString stringWithFormat:@"forms[%" PRIuNS |
| 1410 "]['method'] === 'post'", |
| 1411 i]]; |
| 1412 if (is_origin_window_location) { |
| 1413 [verifying_javascripts |
| 1414 addObject:[NSString stringWithFormat: |
| 1415 @"forms[%" PRIuNS |
| 1416 "]['origin'] === window.location.href", |
| 1417 i]]; |
| 1418 } |
| 1419 // This is the extract mask used by |
| 1420 // __gCrWeb.autofill.extractNewForms. |
| 1421 NSUInteger extract_mask = EXTRACT_VALUE | EXTRACT_OPTIONS; |
| 1422 [verifying_javascripts |
| 1423 addObject:GenerateTestItemVerifyingJavaScripts( |
| 1424 [NSString stringWithFormat:@"forms[%" PRIuNS "]", i], |
| 1425 extract_mask, [expected_items objectAtIndex:i], |
| 1426 // The relevant attributes for the extract mask |
| 1427 GetFormFieldAttributeListsToCheck(extract_mask))]; |
| 1428 } |
| 1429 |
| 1430 NSString* actual = ExecuteJavaScriptWithFormat( |
| 1431 @"var forms = __gCrWeb.autofill.extractNewForms(%" PRIuS "); %@", |
| 1432 autofill::kRequiredFieldsForPredictionRoutines, |
| 1433 [verifying_javascripts componentsJoinedByString:@"&&"]); |
| 1434 |
| 1435 EXPECT_NSEQ(@YES, actual) << base::SysNSStringToUTF8([NSString |
| 1436 stringWithFormat:@"actually forms = %@, " |
| 1437 "but it is expected to be verified by %@", |
| 1438 ExecuteJavaScriptWithFormat( |
| 1439 @"var forms = __gCrWeb.autofill.extractNewForms(" |
| 1440 "%" PRIuS "); __gCrWeb.stringify(forms)", |
| 1441 autofill::kRequiredFieldsForPredictionRoutines), |
| 1442 verifying_javascripts]); |
| 1443 } |
| 1444 |
| 1445 TEST_F(AutofillControllerJsTest, ExtractFormsAndFormElements) { |
| 1446 NSArray* testFirstFormItems = @[ |
| 1447 GetTestFormInputElementWithLabelFromPrevious(), |
| 1448 GetTestFormInputElementWithLabelFromPreviousSpan(), |
| 1449 GetTestFormInputElementWithLabelFromPreviousParagraph(), |
| 1450 GetTestFormInputElementWithLabelFromPreviousLabel(), |
| 1451 GetTestFormInputElementWithLabelFromPreviousLabelOtherIgnored(), |
| 1452 GetTestFormInputElementWithLabelFromPreviousTextSpanBr(), |
| 1453 GetTestFormInputElementWithLabelFromPreviousTextBrAndSpan(), |
| 1454 GetTestFormInputElementWithLabelFromListItem(), |
| 1455 GetTestFormInputElementWithLabelFromTableColumnTD(), |
| 1456 GetTestFormInputElementWithLabelFromTableColumnTH(), |
| 1457 GetTestFormInputElementWithLabelFromTableNested(), |
| 1458 GetTestFormInputElementWithLabelFromTableRow(), |
| 1459 GetTestFormInputElementWithLabelFromDivTable(), |
| 1460 GetTestFormInputElementWithLabelFromDefinitionList(), GetTestInputRadio(), |
| 1461 GetTestInputCheckbox() |
| 1462 ]; |
| 1463 NSArray* testSecondFormItems = @[ |
| 1464 GetTestFormInputElementWithLabelFromDivTable(), GetTestFormSelectElement(), |
| 1465 GetTestFormSelectElementWithOptgroup() |
| 1466 ]; |
| 1467 NSArray* test_forms = @[ testFirstFormItems, testSecondFormItems ]; |
| 1468 |
| 1469 NSString* html = @"<html><body>"; |
| 1470 for (NSArray* testFormItems in test_forms) { |
| 1471 html = [html |
| 1472 stringByAppendingString: |
| 1473 @"<form name='TestForm' action='http://c.com' method='post'>"]; |
| 1474 for (NSArray* testItem in testFormItems) { |
| 1475 html = [html stringByAppendingString:[testItem objectAtIndex:0U]]; |
| 1476 } |
| 1477 html = [html stringByAppendingFormat:@"</form>"]; |
| 1478 } |
| 1479 html = [html stringByAppendingFormat:@"</body></html>"]; |
| 1480 TestExtractNewForms(html, true, test_forms); |
| 1481 } |
| 1482 |
| 1483 TEST_F(AutofillControllerJsTest, ExtractFormsAndFormElementsNestedFrame) { |
| 1484 NSArray* testFirstFormItems = @[ |
| 1485 GetTestFormInputElementWithLabelFromPrevious(), |
| 1486 GetTestFormInputElementWithLabelFromPreviousSpan(), |
| 1487 GetTestFormInputElementWithLabelFromPreviousParagraph(), |
| 1488 GetTestFormInputElementWithLabelFromPreviousLabel(), |
| 1489 GetTestFormInputElementWithLabelFromPreviousLabelOtherIgnored(), |
| 1490 GetTestFormInputElementWithLabelFromPreviousTextSpanBr(), |
| 1491 GetTestFormInputElementWithLabelFromPreviousTextBrAndSpan(), |
| 1492 GetTestFormInputElementWithLabelFromListItem(), |
| 1493 GetTestFormInputElementWithLabelFromTableColumnTD(), |
| 1494 GetTestFormInputElementWithLabelFromTableColumnTH(), |
| 1495 GetTestFormInputElementWithLabelFromTableNested(), |
| 1496 GetTestFormInputElementWithLabelFromTableRow(), |
| 1497 GetTestFormInputElementWithLabelFromDivTable(), |
| 1498 GetTestFormInputElementWithLabelFromDefinitionList(), GetTestInputRadio(), |
| 1499 GetTestInputCheckbox() |
| 1500 ]; |
| 1501 NSArray* testSecondFormItems = @[ |
| 1502 GetTestFormInputElementWithLabelFromDivTable(), GetTestFormSelectElement(), |
| 1503 GetTestFormSelectElementWithOptgroup() |
| 1504 ]; |
| 1505 NSArray* test_forms = @[ testFirstFormItems, testSecondFormItems ]; |
| 1506 |
| 1507 // Test an html that has nested frames. |
| 1508 NSString* nested_frame_html_fragment = @"<html><body>"; |
| 1509 for (NSUInteger i = 0; i < [test_forms count]; ++i) { |
| 1510 NSArray* test_elements = [test_forms objectAtIndex:i]; |
| 1511 NSString* form_string = |
| 1512 @"<form name='TestForm' action='http://c.com' method='post'>"; |
| 1513 for (NSUInteger j = 0; j < [test_elements count]; ++j) { |
| 1514 form_string = |
| 1515 [form_string stringByAppendingString:[[test_elements objectAtIndex:j] |
| 1516 objectAtIndex:0U]]; |
| 1517 } |
| 1518 form_string = [form_string stringByAppendingFormat:@"</form>"]; |
| 1519 |
| 1520 if (i == 0) { |
| 1521 nested_frame_html_fragment = |
| 1522 [nested_frame_html_fragment stringByAppendingString:form_string]; |
| 1523 } else { |
| 1524 nested_frame_html_fragment = [nested_frame_html_fragment |
| 1525 stringByAppendingString: |
| 1526 [NSString stringWithFormat:@"<iframe srcdoc=\"%@\"></iframe>", |
| 1527 form_string]]; |
| 1528 } |
| 1529 } |
| 1530 nested_frame_html_fragment = |
| 1531 [nested_frame_html_fragment stringByAppendingString:@"</body></html>"]; |
| 1532 TestExtractNewForms(nested_frame_html_fragment, false, test_forms); |
| 1533 } |
| 1534 |
| 1535 TEST_F(AutofillControllerJsTest, |
| 1536 ExtractFormsAndFormElementsWithFormAssociatedElementsOutOfForm) { |
| 1537 NSString* html = @"<html><body>" |
| 1538 "<form id='testform'></form>" |
| 1539 "1 <input type='text' name='name1' form='testform'></input>" |
| 1540 "2 <input type='text' name='name2' form='testform'></input>" |
| 1541 "3 <input type='text' name='name3' form='testform'></input>" |
| 1542 "4 <input type='text' name='name4' form='testform'></input>" |
| 1543 "</body></html>"; |
| 1544 LoadHtmlAndInject(html); |
| 1545 |
| 1546 NSString* verifying_javascript = @"forms[0]['fields'][0]['name']==='name1' &&" |
| 1547 @"forms[0]['fields'][0]['label']==='1' &&" |
| 1548 @"forms[0]['fields'][1]['name']==='name2' &&" |
| 1549 @"forms[0]['fields'][1]['label']==='2' &&" |
| 1550 @"forms[0]['fields'][2]['name']==='name3' &&" |
| 1551 @"forms[0]['fields'][2]['label']==='3' &&" |
| 1552 @"forms[0]['fields'][3]['name']==='name4' &&" |
| 1553 @"forms[0]['fields'][3]['label']==='4'"; |
| 1554 EXPECT_NSEQ(@YES, ExecuteJavaScriptWithFormat( |
| 1555 @"var forms = " |
| 1556 "__gCrWeb.autofill.extractNewForms(1); %@", |
| 1557 verifying_javascript)); |
| 1558 } |
| 1559 |
| 1560 TEST_F(AutofillControllerJsTest, ExtractForms) { |
| 1561 NSString* html = @"<html><body>"; |
| 1562 html = |
| 1563 [html stringByAppendingString: |
| 1564 @"<form name='TestForm' action='http://c.com' method='post'>"]; |
| 1565 html = [html |
| 1566 stringByAppendingString:[GetTestFormInputElementWithLabelFromPrevious() |
| 1567 objectAtIndex:0U]]; |
| 1568 html = |
| 1569 [html stringByAppendingString:[GetTestInputCheckbox() objectAtIndex:0U]]; |
| 1570 html = [html stringByAppendingString: |
| 1571 [GetTestFormInputElementWithLabelFromTableColumnTH() |
| 1572 objectAtIndex:0U]]; |
| 1573 html = [html stringByAppendingString: |
| 1574 [GetTestFormInputElementWithLabelFromPreviousTextBrAndSpan() |
| 1575 objectAtIndex:0U]]; |
| 1576 html = [html |
| 1577 stringByAppendingString:[GetTestFormSelectElement() objectAtIndex:0U]]; |
| 1578 html = [html stringByAppendingFormat:@"</form>"]; |
| 1579 html = [html stringByAppendingFormat:@"</body></html>"]; |
| 1580 |
| 1581 LoadHtmlAndInject(html); |
| 1582 |
| 1583 NSDictionary* expected = @{ |
| 1584 @"name" : @"TestForm", |
| 1585 @"method" : @"post", |
| 1586 @"fields" : @[ |
| 1587 @{ |
| 1588 @"name" : @"firstname", |
| 1589 @"form_control_type" : @"text", |
| 1590 @"max_length" : GetDefaultMaxLength(), |
| 1591 @"should_autocomplete" : @true, |
| 1592 @"is_checkable" : @false, |
| 1593 @"is_focusable" : @true, |
| 1594 @"value" : @"John", |
| 1595 @"label" : @"* First name:" |
| 1596 }, |
| 1597 @{ |
| 1598 @"name" : @"vehicle", |
| 1599 @"form_control_type" : @"checkbox", |
| 1600 @"should_autocomplete" : @true, |
| 1601 @"is_checkable" : @true, |
| 1602 @"is_focusable" : @true, |
| 1603 @"value" : @"Bike", |
| 1604 @"label" : @"Bicycle" |
| 1605 }, |
| 1606 @{ |
| 1607 @"name" : @"vehicle", |
| 1608 @"form_control_type" : @"checkbox", |
| 1609 @"should_autocomplete" : @true, |
| 1610 @"is_checkable" : @true, |
| 1611 @"is_focusable" : @true, |
| 1612 @"value" : @"Car", |
| 1613 @"label" : @"Automobile" |
| 1614 }, |
| 1615 @{ |
| 1616 @"name" : @"vehicle", |
| 1617 @"form_control_type" : @"checkbox", |
| 1618 @"should_autocomplete" : @true, |
| 1619 @"is_checkable" : @true, |
| 1620 @"is_focusable" : @true, |
| 1621 @"value" : @"Rocket", |
| 1622 @"label" : @"Missile" |
| 1623 }, |
| 1624 @{ |
| 1625 @"name" : @"nameintableth", |
| 1626 @"form_control_type" : @"text", |
| 1627 @"max_length" : GetDefaultMaxLength(), |
| 1628 @"should_autocomplete" : @true, |
| 1629 @"is_checkable" : @false, |
| 1630 @"is_focusable" : @true, |
| 1631 @"value" : @"John", |
| 1632 @"label" : @"* First name:" |
| 1633 }, |
| 1634 @{ |
| 1635 @"name" : @"emailtableth", |
| 1636 @"form_control_type" : @"email", |
| 1637 @"max_length" : GetDefaultMaxLength(), |
| 1638 @"should_autocomplete" : @true, |
| 1639 @"is_checkable" : @false, |
| 1640 @"is_focusable" : @true, |
| 1641 @"value" : @"john@example.com", |
| 1642 @"label" : @"Email:" |
| 1643 }, |
| 1644 @{ |
| 1645 @"name" : @"pwd", |
| 1646 @"form_control_type" : @"password", |
| 1647 @"autocomplete_attribute" : @"off", |
| 1648 @"max_length" : GetDefaultMaxLength(), |
| 1649 @"should_autocomplete" : @false, |
| 1650 @"is_checkable" : @false, |
| 1651 @"is_focusable" : @true, |
| 1652 @"value" : @"", |
| 1653 @"label" : @"* Password:" |
| 1654 }, |
| 1655 @{ |
| 1656 @"name" : @"state", |
| 1657 @"form_control_type" : @"select-one", |
| 1658 @"is_focusable" : @1, |
| 1659 @"option_values" : @[ @"CA", @"TX" ], |
| 1660 @"option_contents" : @[ @"California", @"Texas" ], |
| 1661 @"should_autocomplete" : @1, |
| 1662 @"value" : @"CA", |
| 1663 @"label" : @"State:" |
| 1664 } |
| 1665 ] |
| 1666 }; |
| 1667 |
| 1668 NSString* result = ExecuteJavaScriptWithFormat( |
| 1669 @"__gCrWeb.autofill.extractForms(%zu)", |
| 1670 autofill::kRequiredFieldsForPredictionRoutines); |
| 1671 NSDictionary* resultDict = [NSJSONSerialization |
| 1672 JSONObjectWithData:[result dataUsingEncoding:NSUTF8StringEncoding] |
| 1673 options:0 |
| 1674 error:nil]; |
| 1675 ASSERT_NSNE(nil, resultDict); |
| 1676 |
| 1677 NSDictionary* forms = [resultDict[@"forms"] objectAtIndex:0]; |
| 1678 [expected enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL* stop) { |
| 1679 EXPECT_NSEQ(forms[key], obj); |
| 1680 }]; |
| 1681 |
| 1682 // Test with Object.prototype.toJSON override. |
| 1683 result = ExecuteJavaScriptWithFormat( |
| 1684 @"Object.prototype.toJSON=function(){return 'abcde';};" |
| 1685 "__gCrWeb.autofill.extractForms(%zu)", |
| 1686 autofill::kRequiredFieldsForPredictionRoutines); |
| 1687 resultDict = [NSJSONSerialization |
| 1688 JSONObjectWithData:[result dataUsingEncoding:NSUTF8StringEncoding] |
| 1689 options:0 |
| 1690 error:nil]; |
| 1691 ASSERT_NSNE(nil, resultDict); |
| 1692 |
| 1693 forms = [resultDict[@"forms"] objectAtIndex:0]; |
| 1694 [expected enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL* stop) { |
| 1695 EXPECT_NSEQ(forms[key], obj); |
| 1696 }]; |
| 1697 |
| 1698 // Test with Array.prototype.toJSON override. |
| 1699 result = ExecuteJavaScriptWithFormat( |
| 1700 @"Array.prototype.toJSON=function(){return 'abcde';};" |
| 1701 "__gCrWeb.autofill.extractForms(%zu)", |
| 1702 autofill::kRequiredFieldsForPredictionRoutines); |
| 1703 resultDict = [NSJSONSerialization |
| 1704 JSONObjectWithData:[result dataUsingEncoding:NSUTF8StringEncoding] |
| 1705 options:0 |
| 1706 error:nil]; |
| 1707 ASSERT_NSNE(nil, resultDict); |
| 1708 |
| 1709 forms = [resultDict[@"forms"] objectAtIndex:0]; |
| 1710 [expected enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL* stop) { |
| 1711 EXPECT_NSEQ(forms[key], obj); |
| 1712 }]; |
| 1713 } |
| 1714 |
| 1715 TEST_F(AutofillControllerJsTest, FillActiveFormField) { |
| 1716 LoadHtmlAndInject(kHTMLForTestingElements); |
| 1717 |
| 1718 NSString* newValue = @"new value"; |
| 1719 EXPECT_NSEQ(newValue, |
| 1720 ExecuteJavaScriptWithFormat( |
| 1721 @"var element=document.getElementsByName('lastname')[0];" |
| 1722 "element.focus();" |
| 1723 "var data={\"name\":\"lastname\",\"value\":\"%@\"};" |
| 1724 "__gCrWeb.autofill.fillActiveFormField(data);" |
| 1725 "element.value", |
| 1726 newValue)); |
| 1727 |
| 1728 EXPECT_NSEQ(@YES, ExecuteJavaScriptWithFormat( |
| 1729 @"var element=document.getElementsByName('gl')[0];" |
| 1730 "element.focus();" |
| 1731 "var oldValue = element.value;" |
| 1732 "var data={\"name\":\"lastname\",\"value\":\"%@\"};" |
| 1733 "__gCrWeb.autofill.fillActiveFormField(data);" |
| 1734 "element.value === oldValue", |
| 1735 newValue)) |
| 1736 << "A non-form element's value should changed."; |
| 1737 } |
| 1738 |
| 1739 // iOS version of FormAutofillTest.FormCache_ExtractNewForms from |
| 1740 // chrome/renderer/autofill/form_autofill_browsertest.cc |
| 1741 TEST_F(AutofillControllerJsTest, ExtractNewForms) { |
| 1742 NSArray* testCases = @[ |
| 1743 // An empty form should not be extracted |
| 1744 @{ |
| 1745 @"html" : @"<FORM name='TestForm' action='http://buh.com' method='post'>" |
| 1746 "</FORM>", |
| 1747 @"expected_forms" : @0 |
| 1748 }, |
| 1749 // A form with less than three fields with no autocomplete type(s) should |
| 1750 // not be extracted. |
| 1751 @{ |
| 1752 @"html" : @"<FORM name='TestForm' action='http://buh.com' method='post'>" |
| 1753 " <INPUT type='name' id='firstname'/>" |
| 1754 "</FORM>", |
| 1755 @"expected_forms" : @0 |
| 1756 }, |
| 1757 // A form with less than three fields with at least one autocomplete type |
| 1758 // should be extracted. |
| 1759 @{ |
| 1760 @"html" : @"<FORM name='TestForm' action='http://buh.com' method='post'>" |
| 1761 " <INPUT type='name' id='firstname'" |
| 1762 " autocomplete='given-name'/>" |
| 1763 "</FORM>", |
| 1764 @"expected_forms" : @1 |
| 1765 }, |
| 1766 // A form with three or more fields should be extracted. |
| 1767 @{ |
| 1768 @"html" : @"<FORM name='TestForm' action='http://buh.com' method='post'>" |
| 1769 " <INPUT type='text' id='firstname'/>" |
| 1770 " <INPUT type='text' id='lastname'/>" |
| 1771 " <INPUT type='text' id='email'/>" |
| 1772 " <INPUT type='submit' value='Send'/>" |
| 1773 "</FORM>", |
| 1774 @"expected_forms" : @1 |
| 1775 } |
| 1776 ]; |
| 1777 |
| 1778 for (NSDictionary* testCase in testCases) { |
| 1779 LoadHtmlAndInject(testCase[@"html"]); |
| 1780 |
| 1781 NSString* result = |
| 1782 ExecuteJavaScriptWithFormat(@"__gCrWeb.autofill.extractForms(%zu)", |
| 1783 autofill::kRequiredFieldsForUpload); |
| 1784 NSDictionary* resultDict = [NSJSONSerialization |
| 1785 JSONObjectWithData:[result dataUsingEncoding:NSUTF8StringEncoding] |
| 1786 options:0 |
| 1787 error:nil]; |
| 1788 ASSERT_NSNE(nil, resultDict); |
| 1789 NSUInteger expectedCount = |
| 1790 [testCase[@"expected_forms"] unsignedIntegerValue]; |
| 1791 EXPECT_EQ(expectedCount, [resultDict[@"forms"] count]) |
| 1792 << base::SysNSStringToUTF8(testCase[@"html"]); |
| 1793 } |
| 1794 } |
| 1795 |
| 1796 } // namespace |
OLD | NEW |