Index: ios/chrome/browser/autofill/autofill_controller_js_unittest.mm |
diff --git a/ios/chrome/browser/autofill/autofill_controller_js_unittest.mm b/ios/chrome/browser/autofill/autofill_controller_js_unittest.mm |
new file mode 100644 |
index 0000000000000000000000000000000000000000..b2ecb6f49eb17893df3ec02c0a239306a49ba823 |
--- /dev/null |
+++ b/ios/chrome/browser/autofill/autofill_controller_js_unittest.mm |
@@ -0,0 +1,1796 @@ |
+// Copyright 2013 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#import <UIKit/UIKit.h> |
+ |
+#include "base/format_macros.h" |
+#include "base/ios/ios_util.h" |
+#include "base/strings/sys_string_conversions.h" |
+#include "components/autofill/core/common/autofill_constants.h" |
+#include "ios/chrome/browser/web/chrome_web_test.h" |
+#import "ios/web/public/test/web_js_test.h" |
+#include "testing/gtest/include/gtest/gtest.h" |
+#include "testing/gtest_mac.h" |
+ |
+#if !defined(__has_feature) || !__has_feature(objc_arc) |
+#error "This file requires ARC support." |
+#endif |
+ |
+// Unit tests for ios/chrome/browser/web/resources/autofill_controller.js |
+namespace { |
+ |
+// Structure for getting element by name using JavaScripts. |
+struct ElementByName { |
+ // The name of the element. |
+ const char* element_name; |
+ // The index in the elements that have |element_name|. |
+ const int index; |
+ // The option index if the element is a select, -1 otherwise. |
+ const int option_index; |
+}; |
+ |
+// TODO(crbug.com/619982): MobileSafari/iOS10 corrected |
+// HTMLInputElement.maxLength with the specification |
+// ( https://bugs.webkit.org/show_bug.cgi?id=154906 ). Add support for old and |
+// new default maxLength value until we drop iOS 9. |
+NSString* GetDefaultMaxLengthString() { |
+ return base::ios::IsRunningOnIOS10OrLater() ? @"-1" : @"524288"; |
+} |
+NSNumber* GetDefaultMaxLength() { |
+ return base::ios::IsRunningOnIOS10OrLater() ? @-1 : @524288; |
+} |
+ |
+// Generates the JavaScript that gets an element by name. |
+NSString* GetElementByNameJavaScript(ElementByName element) { |
+ NSString* query = |
+ [NSString stringWithFormat:@"window.document.getElementsByName('%s')[%d]", |
+ element.element_name, element.index]; |
+ if (element.option_index >= 0) { |
+ query = |
+ [query stringByAppendingFormat:@".options[%d]", element.option_index]; |
+ } |
+ return query; |
+} |
+ |
+// Generates an array of JavaScripts that get each element in |elements| by |
+// name. |
+NSArray* GetElementsByNameJavaScripts(const ElementByName elements[], |
+ size_t elements_size) { |
+ NSMutableArray* array = [NSMutableArray array]; |
+ for (size_t i = 0; i < elements_size; ++i) { |
+ NSString* query = GetElementByNameJavaScript(elements[i]); |
+ [array addObject:query]; |
+ } |
+ return array; |
+} |
+ |
+// clang-format off |
+NSString* kHTMLForTestingElements = @"<html><body>" |
+ "<input type=hidden name='gl' value='us'>" |
+ "<form name='testform'>" |
+ " <input type=hidden name='hl' value='en'>" |
+ " <input type='text' name='firstname'>" |
+ " <input type='text' name='lastname'>" |
+ " <input type='email' name='email'>" |
+ " <input type='tel' name='phone'>" |
+ " <input type='url' autocomplete='off' name='blog'>" |
+ " <input type='number' name='expected number of clicks'>" |
+ " <input type='password' autocomplete='off' name='pwd'>" |
+ " <input type='checkbox' name='vehicle' value='Bike'>" |
+ " <input type='checkbox' name='vehicle' value='Car'>" |
+ " <input type='checkbox' name='vehicle' value='Rocket'>" |
+ " <input type='radio' name='boolean' value='true'>" |
+ " <input type='radio' name='boolean' value='false'>" |
+ " <input type='radio' name='boolean' value='other'>" |
+ " <label>State:" |
+ " <select name='state'>" |
+ " <option value='CA'>CA</option>" |
+ " <option value='MA'>MA</option>" |
+ " </select>" |
+ " </label>" |
+ " <label>Course:" |
+ " <select name='course'>" |
+ " <optgroup label='8.01 Physics I: Classical Mechanics'>" |
+ " <option value='8.01.1'>Lecture 01: Powers of Ten" |
+ " <option value='8.01.2'>Lecture 02: 1D Kinematics" |
+ " <option value='8.01.3'>Lecture 03: Vectors" |
+ " <optgroup label='8.02 Electricity and Magnestism'>" |
+ " <option value='8.02.1'>Lecture 01: What holds our world together?" |
+ " <option value='8.02.2'>Lecture 02: Electric Field" |
+ " <option value='8.02.3'>Lecture 03: Electric Flux" |
+ " </select>" |
+ " </label>" |
+ " <label>Cars:" |
+ " <select name='cars' multiple>" |
+ " <option value='volvo'>Volvo</option>" |
+ " <option value='saab'>Saab</option>" |
+ " <option value='opel'>Opel</option>" |
+ " <option value='audi'>Audi</option>" |
+ " </select>" |
+ " </label>" |
+ " <input type='submit' name='submit' value='Submit'>" |
+ "</form>" |
+ "</body></html>"; |
+// clang-format on |
+ |
+// A bit field mask to extract data from WebFormControlElement. They are from |
+// autofill_controller.js |
+enum ExtractMask { |
+ EXTRACT_NONE = 0, |
+ EXTRACT_VALUE = 1 << 0, // Extract value from WebFormControlElement. |
+ EXTRACT_OPTION_TEXT = 1 << 1, // Extract option text from |
+ // WebFormSelectElement. Only valid when |
+ // |EXTRACT_VALUE| is set. |
+ // This is used for form submission where |
+ // human readable value is captured. |
+ EXTRACT_OPTIONS = 1 << 2, // Extract options from |
+ // WebFormControlElement. |
+}; |
+ |
+const ExtractMask kFormExtractMasks[] = { |
+ EXTRACT_NONE, EXTRACT_VALUE, EXTRACT_OPTION_TEXT, EXTRACT_OPTIONS, |
+}; |
+ |
+// Gets the attributes to check for a mask in |kFormExtractMasks|. |
+NSArray* GetFormFieldAttributeListsToCheck(NSUInteger mask) { |
+ if (!(mask & EXTRACT_VALUE)) { |
+ return @[ |
+ @"name", @"form_control_type", @"autocomplete_attribute", @"max_length", |
+ @"should_autocomplete", @"is_checkable" |
+ ]; |
+ } |
+ |
+ if (mask & EXTRACT_OPTIONS) { |
+ return @[ |
+ @"name", @"form_control_type", @"autocomplete_attribute", @"max_length", |
+ @"should_autocomplete", @"is_checkable", @"value", @"option_values", |
+ @"option_contents" |
+ ]; |
+ } |
+ |
+ return @[ |
+ @"name", @"form_control_type", @"autocomplete_attribute", @"max_length", |
+ @"should_autocomplete", @"is_checkable", @"value" |
+ ]; |
+} |
+ |
+// ***** Clang-formatting is disabled for the following block of testdata ***** |
+// clang-format off |
+ |
+// Getters for form control element testing data. The returned data is |
+// an array consisting of an html fragment followed by an array |
+// of dictionaries containing the expected field attributes for elements in the |
+// given html fragment. |
+NSArray* GetTestFormInputElementWithLabelFromPrevious() { |
+ return @[ |
+ @("* First name: " |
+ "<INPUT type='text' name='firstname' value='John'/>"), |
+ [NSDictionary dictionaryWithObjectsAndKeys: |
+ @"'* First name:'", @"label", |
+ @"'firstname'", @"name", |
+ @"'text'", @"form_control_type", |
+ @"undefined", @"autocomplete_attribute", |
+ GetDefaultMaxLengthString(), @"max_length", |
+ @"true", @"should_autocomplete", |
+ @"false", @"is_checkable", |
+ @"'John'", @"value", |
+ @"'John'", @"value_option_text", |
+ @"undefined", @"option_values", |
+ @"undefined", @"option_contents", |
+ nil]]; |
+} |
+ |
+NSArray* GetTestFormInputElementWithLabelFromPreviousSpan() { |
+ return @[ |
+ @("* Last name<span>:</span> " |
+ "<INPUT type='text' name='lastname' value='John'/>"), |
+ [NSDictionary dictionaryWithObjectsAndKeys: |
+ @"'* Last name:'", @"label", |
+ @"'lastname'", @"name", |
+ @"'text'", @"form_control_type", |
+ @"undefined", @"autocomplete_attribute", |
+ GetDefaultMaxLengthString(), @"max_length", |
+ @"true", @"should_autocomplete", |
+ @"false", @"is_checkable", |
+ @"'John'", @"value", |
+ @"'John'", @"value_option_text", |
+ @"undefined", @"option_values", |
+ @"undefined", @"option_contents", |
+ nil]]; |
+} |
+ |
+NSArray* GetTestFormInputElementWithLabelFromPreviousParagraph() { |
+ return @[ |
+ @("<p>* Email:</p> " |
+ "<INPUT type='email' name='email' value='john@example.com'/>"), |
+ [NSDictionary dictionaryWithObjectsAndKeys: |
+ @"'* Email:'", @"label", |
+ @"'email'", @"name", |
+ @"'email'", @"form_control_type", |
+ @"undefined", @"autocomplete_attribute", |
+ GetDefaultMaxLengthString(), @"max_length", |
+ @"true", @"should_autocomplete", |
+ @"false", @"is_checkable", |
+ @"'john@example.com'", @"value", |
+ @"'john@example.com'", @"value_option_text", |
+ @"undefined", @"option_values", |
+ @"undefined", @"option_contents", |
+ nil]]; |
+} |
+ |
+NSArray* GetTestFormInputElementWithLabelFromPreviousLabel() { |
+ return @[ |
+ @("<label>* Telephone: </label> " |
+ "<INPUT type='tel' id='telephone' value='12345678'/>"), |
+ [NSDictionary dictionaryWithObjectsAndKeys: |
+ @"'* Telephone:'", @"label", |
+ @"'telephone'", @"name", |
+ @"'tel'", @"form_control_type", |
+ @"undefined", @"autocomplete_attribute", |
+ GetDefaultMaxLengthString(), @"max_length", |
+ @"true", @"should_autocomplete", |
+ @"false", @"is_checkable", |
+ @"'12345678'", @"value", |
+ @"'12345678'", @"value_option_text", |
+ @"undefined", @"option_values", |
+ @"undefined", @"option_contents", |
+ nil]]; |
+} |
+ |
+NSArray* GetTestFormInputElementWithLabelFromPreviousLabelOtherIgnored() { |
+ return @[ |
+ @("Other Text <label>* Blog:</label> " |
+ "<INPUT type='url' autocomplete='off' value='www.jogh.blog'/>"), |
+ [NSDictionary dictionaryWithObjectsAndKeys: |
+ @"'* Blog:'", @"label", |
+ @"''", @"name", |
+ @"'url'", @"form_control_type", |
+ @"'off'", @"autocomplete_attribute", |
+ GetDefaultMaxLengthString(), @"max_length", |
+ @"false", @"should_autocomplete", |
+ @"false", @"is_checkable", |
+ @"'www.jogh.blog'", @"value", |
+ @"'www.jogh.blog'", @"value_option_text", |
+ @"undefined", @"option_values", |
+ @"undefined", @"option_contents", |
+ nil]]; |
+} |
+ |
+NSArray* GetTestFormInputElementWithLabelFromPreviousTextSpanBr() { |
+ return @[ |
+ @("* Expected visits<span>:</span> <br>" |
+ "<INPUT type='number' name='expected number of clicks'/>"), |
+ [NSDictionary dictionaryWithObjectsAndKeys: |
+ @"'* Expected visits:'", @"label", |
+ @"'expected number of clicks'", @"name", |
+ @"'number'", @"form_control_type", |
+ @"undefined", @"autocomplete_attribute", |
+ GetDefaultMaxLengthString(), @"max_length", |
+ @"true", @"should_autocomplete", |
+ @"false", @"is_checkable", |
+ @"''", @"value", |
+ @"''", @"value_option_text", |
+ @"undefined", @"option_values", |
+ @"undefined", @"option_contents", |
+ nil]]; |
+} |
+ |
+NSArray* GetTestFormInputElementWithLabelFromPreviousTextBrAndSpan() { |
+ return @[ |
+ @("Other <br> * Password<span>:</span> " |
+ "<INPUT type='password' autocomplete='off' name='pwd'/>"), |
+ [NSDictionary dictionaryWithObjectsAndKeys: |
+ @"'* Password:'", @"label", |
+ @"'pwd'", @"name", |
+ @"'password'", @"form_control_type", |
+ @"'off'", @"autocomplete_attribute", |
+ GetDefaultMaxLengthString(), @"max_length", |
+ @"false", @"should_autocomplete", |
+ @"false", @"is_checkable", |
+ @"''", @"value", |
+ @"''", @"value_option_text", |
+ @"undefined", @"option_values", |
+ @"undefined", @"option_contents", |
+ nil]]; |
+} |
+ |
+NSArray* GetTestFormInputElementWithLabelFromListItem() { |
+ return @[ |
+ @("<LI>" |
+ "<LABEL><EM>*</EM> Code:</LABEL>" |
+ "<INPUT type='text' id='first code' value='415'/>" |
+ "<INPUT type='text' id='middle code' value='555'/>" |
+ "<INPUT type='text' id='last code' value='1212'/>" |
+ "</LI>"), |
+ [NSDictionary dictionaryWithObjectsAndKeys: |
+ @"'* Code:'", @"label", |
+ @"'first code'", @"name", |
+ @"'text'", @"form_control_type", |
+ @"undefined", @"autocomplete_attribute", |
+ GetDefaultMaxLengthString(), @"max_length", |
+ @"true", @"should_autocomplete", |
+ @"false", @"is_checkable", |
+ @"'415'", @"value", |
+ @"'415'", @"value_option_text", |
+ @"undefined", @"option_values", |
+ @"undefined", @"option_contents", |
+ nil], |
+ [NSDictionary dictionaryWithObjectsAndKeys: |
+ @"'* Code:'", @"label", |
+ @"'middle code'", @"name", |
+ @"'text'", @"form_control_type", |
+ @"undefined", @"autocomplete_attribute", |
+ GetDefaultMaxLengthString(), @"max_length", |
+ @"true", @"should_autocomplete", |
+ @"false", @"is_checkable", |
+ @"'555'", @"value", |
+ @"'555'", @"value_option_text", |
+ @"undefined", @"option_values", |
+ @"undefined", @"option_contents", |
+ nil], |
+ [NSDictionary dictionaryWithObjectsAndKeys: |
+ @"'* Code:'", @"label", |
+ @"'last code'", @"name", |
+ @"'text'", @"form_control_type", |
+ @"undefined", @"autocomplete_attribute", |
+ GetDefaultMaxLengthString(), @"max_length", |
+ @"true", @"should_autocomplete", |
+ @"false", @"is_checkable", |
+ @"'1212'", @"value", |
+ @"'1212'", @"value_option_text", |
+ @"undefined", @"option_values", |
+ @"undefined", @"option_contents", |
+ nil]]; |
+} |
+ |
+NSArray* GetTestFormInputElementWithLabelFromTableColumnTD() { |
+ return @[ |
+ @("<TABLE>" |
+ "<TR>" |
+ " <TD>* First name:</TD>" |
+ " <TD><INPUT type='text' id='tabletdname' value='John'/></TD>" |
+ "</TR>" |
+ "<TR>" |
+ " <TD>Email:</TD>" |
+ " <TD><INPUT type='email' id='tabletdemail'" |
+ " value='john@example.com'/></TD>" |
+ "</TR>" |
+ "</TABLE>"), |
+ [NSDictionary dictionaryWithObjectsAndKeys: |
+ @"'* First name:'", @"label", |
+ @"'tabletdname'", @"name", |
+ @"'text'", @"form_control_type", |
+ @"undefined", @"autocomplete_attribute", |
+ GetDefaultMaxLengthString(), @"max_length", |
+ @"true", @"should_autocomplete", |
+ @"false", @"is_checkable", |
+ @"'John'", @"value", |
+ @"'John'", @"value_option_text", |
+ @"undefined", @"option_values", |
+ @"undefined", @"option_contents", |
+ nil], |
+ [NSDictionary dictionaryWithObjectsAndKeys: |
+ @"'Email:'", @"label", |
+ @"'tabletdemail'", @"name", |
+ @"'email'", @"form_control_type", |
+ @"undefined", @"autocomplete_attribute", |
+ GetDefaultMaxLengthString(), @"max_length", |
+ @"true", @"should_autocomplete", |
+ @"false", @"is_checkable", |
+ @"'john@example.com'", @"value", |
+ @"'john@example.com'", @"value_option_text", |
+ @"undefined", @"option_values", |
+ @"undefined", @"option_contents", |
+ nil]]; |
+} |
+ |
+NSArray* GetTestFormInputElementWithLabelFromTableColumnTH() { |
+ return @[ |
+ @("<TABLE>" |
+ "<TR>" |
+ " <TH>* First name:</TH>" |
+ " <TD><INPUT type='text' name='nameintableth' value='John'/></TD>" |
+ "</TR>" |
+ "<TR>" |
+ " <TD>Email:</TD>" |
+ " <TD><INPUT type='email' id='emailtableth'" |
+ " value='john@example.com'/></TD>" |
+ "</TR>" |
+ "</TABLE>"), |
+ [NSDictionary dictionaryWithObjectsAndKeys: |
+ @"'* First name:'", @"label", |
+ @"'nameintableth'", @"name", |
+ @"'text'", @"form_control_type", |
+ @"undefined", @"autocomplete_attribute", |
+ GetDefaultMaxLengthString(), @"max_length", |
+ @"true", @"should_autocomplete", |
+ @"false", @"is_checkable", |
+ @"'John'", @"value", |
+ @"'John'", @"value_option_text", |
+ @"undefined", @"option_values", |
+ @"undefined", @"option_contents", |
+ nil], |
+ [NSDictionary dictionaryWithObjectsAndKeys: |
+ @"'Email:'", @"label", |
+ @"'emailtableth'", @"name", |
+ @"'email'", @"form_control_type", |
+ @"undefined", @"autocomplete_attribute", |
+ GetDefaultMaxLengthString(), @"max_length", |
+ @"true", @"should_autocomplete", |
+ @"false", @"is_checkable", |
+ @"'john@example.com'", @"value", |
+ @"'john@example.com'", @"value_option_text", |
+ @"undefined", @"option_values", |
+ @"undefined", @"option_contents", |
+ nil]]; |
+} |
+ |
+NSArray* GetTestFormInputElementWithLabelFromTableNested() { |
+ return @[ |
+ @("<TABLE>" |
+ "<TR>" |
+ " <TD><FONT>* First </FONT><FONT>name:</FONT></TD>" |
+ " <TD><INPUT type='text' id='nametablenested' value='John'/></TD>" |
+ "</TR>" |
+ "</TABLE>"), |
+ [NSDictionary dictionaryWithObjectsAndKeys: |
+ @"'* First name:'", @"label", |
+ @"'nametablenested'", @"name", |
+ @"'text'", @"form_control_type", |
+ @"undefined", @"autocomplete_attribute", |
+ GetDefaultMaxLengthString(), @"max_length", |
+ @"true", @"should_autocomplete", |
+ @"false", @"is_checkable", |
+ @"'John'", @"value", |
+ @"'John'", @"value_option_text", |
+ @"undefined", @"option_values", |
+ @"undefined", @"option_contents", |
+ nil]]; |
+} |
+ |
+NSArray* GetTestFormInputElementWithLabelFromTableRow() { |
+ return @[ |
+ @("<TABLE>" |
+ "<TR>" |
+ " <TD>* <FONT>First </FONT><FONT>name:</FONT></TD>" |
+ "</TR>" |
+ "<TR>" |
+ " <TD><INPUT type='text' name='nametablerow' value='John'/></TD>" |
+ "</TR>" |
+ "</TABLE>"), |
+ [NSDictionary dictionaryWithObjectsAndKeys: |
+ @"'* First name:'", @"label", |
+ @"'nametablerow'", @"name", |
+ @"'text'", @"form_control_type", |
+ @"undefined", @"autocomplete_attribute", |
+ GetDefaultMaxLengthString(), @"max_length", |
+ @"true", @"should_autocomplete", |
+ @"false", @"is_checkable", |
+ @"'John'", @"value", |
+ @"'John'", @"value_option_text", |
+ @"undefined", @"option_values", |
+ @"undefined", @"option_contents", |
+ nil]]; |
+} |
+ |
+NSArray* GetTestFormInputElementWithLabelFromDivTable() { |
+ return @[ |
+ @("<DIV>* First name:<BR>" |
+ "<SPAN>" |
+ "<INPUT type='text' name='namedivtable' value='John'>" |
+ "</SPAN>" |
+ "</DIV>"), |
+ [NSDictionary dictionaryWithObjectsAndKeys: |
+ @"'* First name:'", @"label", |
+ @"'namedivtable'", @"name", |
+ @"'text'", @"form_control_type", |
+ @"undefined", @"autocomplete_attribute", |
+ GetDefaultMaxLengthString(), @"max_length", |
+ @"true", @"should_autocomplete", |
+ @"false", @"is_checkable", |
+ @"'John'", @"value", |
+ @"'John'", @"value_option_text", |
+ @"undefined", @"option_values", |
+ @"undefined", @"option_contents", |
+ nil]]; |
+} |
+ |
+NSArray* GetTestFormInputElementWithLabelFromDefinitionList() { |
+ return @[ |
+ @("<DL>" |
+ " <DT>" |
+ " <SPAN>" |
+ " *" |
+ " </SPAN>" |
+ " <SPAN>" |
+ " Favorite Sport" |
+ " </SPAN>" |
+ " </DT>" |
+ " <DD>" |
+ " <FONT>" |
+ " <INPUT type='favorite sport' name='sport' value='Tennis'/>" |
+ " </FONT>" |
+ " </DD>" |
+ " </DL>"), |
+ [NSDictionary dictionaryWithObjectsAndKeys: |
+ @"'* Favorite Sport'", @"label", |
+ @"'sport'", @"name", |
+ @"'text'", @"form_control_type", |
+ @"undefined", @"autocomplete_attribute", |
+ GetDefaultMaxLengthString(), @"max_length", |
+ @"true", @"should_autocomplete", |
+ @"false", @"is_checkable", |
+ @"'Tennis'", @"value", |
+ @"'Tennis'", @"value_option_text", |
+ @"undefined", @"option_values", |
+ @"undefined", @"option_contents", |
+ nil]]; |
+} |
+ |
+NSArray* GetTestInputRadio() { |
+ return @[ |
+ @("<input type='radio' name='boolean' value='true'/> True" |
+ "<input type='radio' name='boolean' value='false'/> False"), |
+ [NSDictionary dictionaryWithObjectsAndKeys: |
+ @"'True'", @"label", |
+ @"'boolean'", @"name", |
+ @"'radio'", @"form_control_type", |
+ @"undefined", @"autocomplete_attribute", |
+ @"undefined", @"max_length", |
+ @"true", @"should_autocomplete", |
+ @"true", @"is_checkable", |
+ @"'true'", @"value", |
+ @"'true'", @"value_option_text", |
+ @"undefined", @"option_values", |
+ @"undefined", @"option_contents", |
+ nil], |
+ [NSDictionary dictionaryWithObjectsAndKeys: |
+ @"'False'", @"label", |
+ @"'boolean'", @"name", |
+ @"'radio'", @"form_control_type", |
+ @"undefined", @"autocomplete_attribute", |
+ @"undefined", @"max_length", |
+ @"true", @"should_autocomplete", |
+ @"true", @"is_checkable", |
+ @"'false'", @"value", |
+ @"'false'", @"value_option_text", |
+ @"undefined", @"option_values", |
+ @"undefined", @"option_contents", |
+ nil]]; |
+} |
+ |
+NSArray* GetTestInputCheckbox() { |
+ return @[ |
+ @("<input type='checkbox' name='vehicle' value='Bike'> Bicycle" |
+ "<input type='checkbox' name='vehicle' value='Car'> Automobile" |
+ "<input type='checkbox' name='vehicle' value='Rocket'> Missile"), |
+ [NSDictionary dictionaryWithObjectsAndKeys: |
+ @"'Bicycle'", @"label", |
+ @"'vehicle'", @"name", |
+ @"'checkbox'", @"form_control_type", |
+ @"undefined", @"autocomplete_attribute", |
+ @"undefined", @"max_length", |
+ @"true", @"should_autocomplete", |
+ @"true", @"is_checkable", |
+ @"'Bike'", @"value", |
+ @"'Bike'", @"value_option_text", |
+ @"undefined", @"option_values", |
+ @"undefined", @"option_contents", |
+ nil], |
+ [NSDictionary dictionaryWithObjectsAndKeys: |
+ @"'Automobile'", @"label", |
+ @"'vehicle'", @"name", |
+ @"'checkbox'", @"form_control_type", |
+ @"undefined", @"autocomplete_attribute", |
+ @"undefined", @"max_length", |
+ @"true", @"should_autocomplete", |
+ @"true", @"is_checkable", |
+ @"'Car'", @"value", |
+ @"'Car'", @"value_option_text", |
+ @"undefined", @"option_values", |
+ @"undefined", @"option_contents", |
+ nil], |
+ [NSDictionary dictionaryWithObjectsAndKeys: |
+ @"'Missile'", @"label", |
+ @"'vehicle'", @"name", |
+ @"'checkbox'", @"form_control_type", |
+ @"undefined", @"autocomplete_attribute", |
+ @"undefined", @"max_length", |
+ @"true", @"should_autocomplete", |
+ @"true", @"is_checkable", |
+ @"'Rocket'", @"value", |
+ @"'Rocket'", @"value_option_text", |
+ @"undefined", @"option_values", |
+ @"undefined", @"option_contents", |
+ nil]]; |
+} |
+ |
+NSArray* GetTestFormSelectElement() { |
+ return @[ |
+ @(" <label>State:" |
+ " <select name='state'>" |
+ " <option value='CA'>California</option>" |
+ " <option value='TX'>Texas</option>" |
+ " </select>" |
+ " </label>"), |
+ [NSDictionary dictionaryWithObjectsAndKeys: |
+ @"'State:'", @"label", |
+ @"'state'", @"name", |
+ @"'select-one'", @"form_control_type", |
+ @"undefined", @"autocomplete_attribute", |
+ @"undefined", @"max_length", |
+ @"true", @"should_autocomplete", |
+ @"undefined", @"is_checkable", |
+ @"'CA'", @"value", |
+ @"'California'", @"value_option_text", |
+ @[@"'CA'", @"'TX'"], @"option_values", |
+ @[@"'California'", @"'Texas'"], @"option_contents", |
+ nil]]; |
+} |
+ |
+NSArray* GetTestFormSelectElementWithOptgroup() { |
+ return @[ |
+ @(" <label>Course:" |
+ " <select name='course'>" |
+ " <optgroup label='8.01 Physics I: Classical Mechanics'>" |
+ " <option value='8.01.1'>Lecture 01: Powers of Ten" |
+ " <option value='8.01.2'>Lecture 02: 1D Kinematics" |
+ " <option value='8.01.3'>Lecture 03: Vectors" |
+ " <optgroup label='8.02 Electricity and Magnestism'>" |
+ " <option value='8.02.1'>Lecture 01: What holds world together?" |
+ " <option value='8.02.2'>Lecture 02: Electric Field" |
+ " <option value='8.02.3'>Lecture 03: Electric Flux" |
+ " </select>" |
+ " </label>"), |
+ [NSDictionary dictionaryWithObjectsAndKeys: |
+ @"'Course:'", @"label", |
+ @"'course'", @"name", |
+ @"'select-one'", @"form_control_type", |
+ @"undefined", @"autocomplete_attribute", |
+ @"undefined", @"max_length", |
+ @"true", @"should_autocomplete", |
+ @"undefined", @"is_checkable", |
+ @"'8.01.1'", @"value", |
+ @"'Lecture 01: Powers of Ten'", @"value_option_text", |
+ @[@"'8.01.1'", |
+ @"'8.01.2'", |
+ @"'8.01.3'", |
+ @"'8.02.1'", |
+ @"'8.02.2'", |
+ @"'8.02.3'"], @"option_values", |
+ @[@"'Lecture 01: Powers of Ten'", |
+ @"'Lecture 02: 1D Kinematics'", |
+ @"'Lecture 03: Vectors'", |
+ @"'Lecture 01: What holds world together?'", |
+ @"'Lecture 02: Electric Field'", |
+ @"'Lecture 03: Electric Flux'"], @"option_contents", |
+ nil]]; |
+} |
+ |
+// clang-format on |
+ |
+// Generates JavaScripts to check a JavaScripts object |results| with the |
+// expected values given in |expected|, which is a dictionary with string |
+// values for all the keys other than @"option_vaues" and @"option_contents"; |
+// the values of @"option_vaues" and @"option_contents" are arrays of |
+// strings or undefined. Only attributes in |attributes_to_check| are checked. |
+// A different expected value is chosen in |expected| for different |
+// |extract_mask|. |
+NSString* GenerateElementItemVerifyingJavaScripts( |
+ NSString* results, |
+ NSUInteger extract_mask, |
+ NSDictionary* expected, |
+ NSArray* attributes_to_check) { |
+ NSMutableArray* verifying_javascripts = [NSMutableArray array]; |
+ |
+ for (NSString* attribute in attributes_to_check) { |
+ if ([attribute isEqualToString:@"option_values"] || |
+ [attribute isEqualToString:@"option_contents"]) { |
+ id expected_value = [expected objectForKey:attribute]; |
+ if ([expected_value isKindOfClass:[NSString class]]) { |
+ [verifying_javascripts |
+ addObject:[NSString |
+ stringWithFormat:@"%@['%@']===%@", results, attribute, |
+ [expected objectForKey:attribute]]]; |
+ } else { |
+ for (NSUInteger i = 0; i < [(NSArray*)expected_value count]; ++i) { |
+ [verifying_javascripts |
+ addObject:[NSString |
+ stringWithFormat:@"%@['%@'][%" PRIuNS "] === %@", |
+ results, attribute, i, |
+ [expected_value objectAtIndex:i]]]; |
+ } |
+ } |
+ } else { |
+ NSString* expected_value = [expected objectForKey:attribute]; |
+ // Option text is used as value for extract_mask 1 << 1 |
+ if ((extract_mask & 1 << 1) && [attribute isEqualToString:@"value"]) |
+ expected_value = [expected objectForKey:@"value_option_text"]; |
+ [verifying_javascripts |
+ addObject:[NSString stringWithFormat:@"%@['%@']===%@", results, |
+ attribute, expected_value]]; |
+ } |
+ } |
+ |
+ return [verifying_javascripts componentsJoinedByString:@"&&"]; |
+} |
+ |
+// Generates JavaScripts to check a JavaScripts array |results| with the |
+// expected values given in |expected|, which is an array of dictionaries; each |
+// dictionary is the expected values of the corresponding item in |results|. |
+// Only attributes in |attributes_to_check| are checked. A different expected |
+// value is chosen in |expected| for different |extract_mask|. |
+NSString* GenerateTestItemVerifyingJavaScripts(NSString* results, |
+ NSUInteger extract_mask, |
+ NSArray* expected, |
+ NSArray* attributed_to_check) { |
+ NSMutableArray* verifying_javascripts = [NSMutableArray array]; |
+ |
+ NSUInteger controlCount = 0; |
+ for (NSUInteger indexOfTestData = 0; indexOfTestData < [expected count]; |
+ ++indexOfTestData) { |
+ NSArray* expectedData = [expected objectAtIndex:indexOfTestData]; |
+ for (NSUInteger i = 1; i < [expectedData count]; ++i, ++controlCount) { |
+ NSDictionary* expected = [expectedData objectAtIndex:i]; |
+ NSString* itemVerifyingJavaScripts = |
+ GenerateElementItemVerifyingJavaScripts( |
+ [NSString stringWithFormat:@"%@['fields'][%" PRIuNS "]", results, |
+ controlCount], |
+ extract_mask, expected, attributed_to_check); |
+ [verifying_javascripts addObject:itemVerifyingJavaScripts]; |
+ } |
+ } |
+ return [verifying_javascripts componentsJoinedByString:@"&&"]; |
+} |
+ |
+// Test fixture to test autofill controller. |
+class AutofillControllerJsTest : public web::WebJsTest<ChromeWebTest> { |
+ public: |
+ AutofillControllerJsTest() |
+ : web::WebJsTest<ChromeWebTest>(@[ @"autofill_controller" ]) {} |
+ |
+ protected: |
+ // Helper method that EXPECTs |javascript| evaluation on page |
+ // |kHTMLForTestingElements| with expectation given by |
+ // |elements_with_true_expected|. |
+ void TestExecutingBooleanJavaScriptOnElement( |
+ NSString* javascript, |
+ const ElementByName elements_with_true_expected[], |
+ size_t size_elements_with_true_expected); |
+ |
+ // Helper method that EXPECTs |
+ // |__gCrWeb.autofill.webFormControlElementToFormField|. This method applies |
+ // |__gCrWeb.autofill.webFormControlElementToFormField| on each element in |
+ // |test_data| with all possible extract masks and verify the results. |
+ void TestWebFormControlElementToFormField(NSArray* test_data, |
+ NSString* tag_name); |
+ |
+ // Helper method for testing |javascripts_statement| that evalutate |
+ // |attribute_name| of the elements in |test_data| which has tag name |
+ // |tag_name|. EXPECTs JavaScript evaluation on |
+ // "window.document.getElementsByTagName()" |
+ void TestInputElementDataEvaluation(NSString* javascripts_statement, |
+ NSString* attribute_name, |
+ NSArray* test_data, |
+ NSString* tag_name); |
+ |
+ // Helper method that EXPECTs |__gCrWeb.autofill.webFormElementToFormData| on |
+ // a form element obtained by |get_form_element_javascripts|. The results |
+ // are verified with |verifying_java_scripts|. |
+ void TestWebFormElementToFormDataForOneForm( |
+ NSString* get_form_element_javascripts, |
+ NSUInteger extract_mask, |
+ NSString* expected_result, |
+ NSString* verifying_javascripts); |
+ |
+ // EXPECTs |__gCrWeb.autofill.webFormElementToFormData| on all the test data. |
+ void TestWebFormElementToFormData(NSArray* test_items); |
+ |
+ // EXPECTs |__gCrWeb.autofill.extractNewForms| on |html|. |
+ void TestExtractNewForms(NSString* html, |
+ BOOL is_origin_window_location, |
+ NSArray* expected_items); |
+}; |
+ |
+void AutofillControllerJsTest::TestExecutingBooleanJavaScriptOnElement( |
+ NSString* javascript, |
+ const ElementByName elements_with_true_expected[], |
+ size_t size_elements_with_true_expected) { |
+ // Elements in |kHTMLForTestingElements|. |
+ const ElementByName elementsByName[] = { |
+ {"hl", 0, -1}, |
+ {"firstname", 0, -1}, |
+ {"lastname", 0, -1}, |
+ {"email", 0, -1}, |
+ {"phone", 0, -1}, |
+ {"blog", 0, -1}, |
+ {"expected number of clicks", 0, -1}, |
+ {"pwd", 0, -1}, |
+ {"vehicle", 0, -1}, |
+ {"vehicle", 1, -1}, |
+ {"vehicle", 2, -1}, |
+ {"boolean", 0, -1}, |
+ {"boolean", 1, -1}, |
+ {"boolean", 2, -1}, |
+ {"state", 0, -1}, |
+ {"state", 0, 0}, |
+ {"state", 0, 1}, |
+ {"course", 0, -1}, |
+ {"course", 0, 0}, |
+ {"course", 0, 1}, |
+ {"course", 0, 2}, |
+ {"course", 0, 3}, |
+ {"course", 0, 4}, |
+ {"course", 0, 5}, |
+ {"cars", 0, -1}, |
+ {"cars", 0, 0}, |
+ {"cars", 0, 1}, |
+ {"cars", 0, 2}, |
+ {"cars", 0, 3}, |
+ {"submit", 0, -1}, |
+ }; |
+ |
+ LoadHtmlAndInject(kHTMLForTestingElements); |
+ ExecuteBooleanJavaScriptOnElementsAndCheck( |
+ javascript, |
+ GetElementsByNameJavaScripts(elementsByName, arraysize(elementsByName)), |
+ GetElementsByNameJavaScripts(elements_with_true_expected, |
+ size_elements_with_true_expected)); |
+} |
+ |
+TEST_F(AutofillControllerJsTest, HasTagName) { |
+ const ElementByName elements_expecting_true[] = { |
+ {"hl", 0, -1}, |
+ {"firstname", 0, -1}, |
+ {"lastname", 0, -1}, |
+ {"email", 0, -1}, |
+ {"phone", 0, -1}, |
+ {"blog", 0, -1}, |
+ {"expected number of clicks", 0, -1}, |
+ {"pwd", 0, -1}, |
+ {"vehicle", 0, -1}, |
+ {"vehicle", 1, -1}, |
+ {"vehicle", 2, -1}, |
+ {"boolean", 0, -1}, |
+ {"boolean", 1, -1}, |
+ {"boolean", 2, -1}, |
+ {"submit", 0, -1}, |
+ }; |
+ |
+ TestExecutingBooleanJavaScriptOnElement( |
+ @"__gCrWeb.autofill.hasTagName(%@, 'input')", elements_expecting_true, |
+ arraysize(elements_expecting_true)); |
+} |
+ |
+TEST_F(AutofillControllerJsTest, CombineAndCollapseWhitespace) { |
+ LoadHtmlAndInject(@"<html><body></body></html>"); |
+ |
+ EXPECT_NSEQ(@"foobar", ExecuteJavaScriptWithFormat( |
+ @"__gCrWeb.autofill.combineAndCollapseWhitespace('" |
+ @"foo', 'bar', false)")); |
+ EXPECT_NSEQ(@"foo bar", ExecuteJavaScriptWithFormat( |
+ @"__gCrWeb.autofill.combineAndCollapseWhitespace(" |
+ @"'foo', 'bar', true)")); |
+ EXPECT_NSEQ(@"foo bar", ExecuteJavaScriptWithFormat( |
+ @"__gCrWeb.autofill.combineAndCollapseWhitespace(" |
+ "'foo ', 'bar', false)")); |
+ EXPECT_NSEQ(@"foo bar", ExecuteJavaScriptWithFormat( |
+ @"__gCrWeb.autofill.combineAndCollapseWhitespace(" |
+ "'foo', ' bar', false)")); |
+ EXPECT_NSEQ(@"foo bar", ExecuteJavaScriptWithFormat( |
+ @"__gCrWeb.autofill.combineAndCollapseWhitespace(" |
+ "'foo', ' bar', true)")); |
+ EXPECT_NSEQ(@"foo bar", ExecuteJavaScriptWithFormat( |
+ @"__gCrWeb.autofill.combineAndCollapseWhitespace(" |
+ "'foo ', ' bar', false)")); |
+ EXPECT_NSEQ(@"foobar ", ExecuteJavaScriptWithFormat( |
+ @"__gCrWeb.autofill.combineAndCollapseWhitespace(" |
+ "'foo', 'bar ', false)")); |
+ EXPECT_NSEQ(@" foo bar", |
+ ExecuteJavaScriptWithFormat( |
+ @"__gCrWeb.autofill.combineAndCollapseWhitespace(" |
+ "' foo', 'bar', true)")); |
+} |
+ |
+void AutofillControllerJsTest::TestInputElementDataEvaluation( |
+ NSString* javascripts_statement, |
+ NSString* attribute_name, |
+ NSArray* test_data, |
+ NSString* tag_name) { |
+ NSString* html_fragment = [test_data objectAtIndex:0U]; |
+ LoadHtmlAndInject(html_fragment); |
+ |
+ for (NSUInteger i = 1; i < [test_data count]; ++i) { |
+ NSString* get_element_javascripts = [NSString |
+ stringWithFormat:@"window.document.getElementsByTagName('%@')[%" PRIuNS |
+ "]", |
+ tag_name, i - 1]; |
+ id actual = ExecuteJavaScriptWithFormat( |
+ @"%@(%@) === %@", javascripts_statement, get_element_javascripts, |
+ [[test_data objectAtIndex:i] objectForKey:attribute_name]); |
+ EXPECT_NSEQ(@YES, actual); |
+ } |
+} |
+ |
+TEST_F(AutofillControllerJsTest, InferLabelFromPrevious) { |
+ TestInputElementDataEvaluation( |
+ @"__gCrWeb.autofill.inferLabelFromPrevious", @"label", |
+ GetTestFormInputElementWithLabelFromPrevious(), @"input"); |
+} |
+ |
+TEST_F(AutofillControllerJsTest, InferLabelFromPreviousSpan) { |
+ TestInputElementDataEvaluation( |
+ @"__gCrWeb.autofill.inferLabelFromPrevious", @"label", |
+ GetTestFormInputElementWithLabelFromPreviousSpan(), @"input"); |
+} |
+ |
+TEST_F(AutofillControllerJsTest, InferLabelFromPreviousParagraph) { |
+ TestInputElementDataEvaluation( |
+ @"__gCrWeb.autofill.inferLabelFromPrevious", @"label", |
+ GetTestFormInputElementWithLabelFromPreviousParagraph(), @"input"); |
+} |
+ |
+TEST_F(AutofillControllerJsTest, InferLabelFromPreviousLabel) { |
+ TestInputElementDataEvaluation( |
+ @"__gCrWeb.autofill.inferLabelFromPrevious", @"label", |
+ GetTestFormInputElementWithLabelFromPreviousLabel(), @"input"); |
+} |
+ |
+TEST_F(AutofillControllerJsTest, InferLabelFromPreviousLabelOtherIgnored) { |
+ TestInputElementDataEvaluation( |
+ @"__gCrWeb.autofill.inferLabelFromPrevious", @"label", |
+ GetTestFormInputElementWithLabelFromPreviousLabelOtherIgnored(), |
+ @"input"); |
+} |
+ |
+TEST_F(AutofillControllerJsTest, InferLabelFromPreviousTextBrAndSpan) { |
+ TestInputElementDataEvaluation( |
+ @"__gCrWeb.autofill.inferLabelFromPrevious", @"label", |
+ GetTestFormInputElementWithLabelFromPreviousTextBrAndSpan(), @"input"); |
+} |
+ |
+TEST_F(AutofillControllerJsTest, InferLabelFromListItem) { |
+ TestInputElementDataEvaluation( |
+ @"__gCrWeb.autofill.inferLabelFromListItem", @"label", |
+ GetTestFormInputElementWithLabelFromListItem(), @"input"); |
+} |
+ |
+TEST_F(AutofillControllerJsTest, InferLabelFromTableColumnTD) { |
+ TestInputElementDataEvaluation( |
+ @"__gCrWeb.autofill.inferLabelFromTableColumn", @"label", |
+ GetTestFormInputElementWithLabelFromTableColumnTD(), @"input"); |
+} |
+ |
+TEST_F(AutofillControllerJsTest, InferLabelFromTableColumnTH) { |
+ TestInputElementDataEvaluation( |
+ @"__gCrWeb.autofill.inferLabelFromTableColumn", @"label", |
+ GetTestFormInputElementWithLabelFromTableColumnTH(), @"input"); |
+} |
+ |
+TEST_F(AutofillControllerJsTest, InferLabelFromTableColumnNested) { |
+ TestInputElementDataEvaluation( |
+ @"__gCrWeb.autofill.inferLabelFromTableColumn", @"label", |
+ GetTestFormInputElementWithLabelFromTableNested(), @"input"); |
+} |
+ |
+TEST_F(AutofillControllerJsTest, InferLabelFromTableRow) { |
+ TestInputElementDataEvaluation( |
+ @"__gCrWeb.autofill.inferLabelFromTableRow", @"label", |
+ GetTestFormInputElementWithLabelFromTableRow(), @"input"); |
+} |
+ |
+TEST_F(AutofillControllerJsTest, InferLabelFromDivTable) { |
+ TestInputElementDataEvaluation( |
+ @"__gCrWeb.autofill.inferLabelFromDivTable", @"label", |
+ GetTestFormInputElementWithLabelFromDivTable(), @"input"); |
+} |
+ |
+TEST_F(AutofillControllerJsTest, InferLabelFromDefinitionList) { |
+ TestInputElementDataEvaluation( |
+ @"__gCrWeb.autofill.inferLabelFromDefinitionList", @"label", |
+ GetTestFormInputElementWithLabelFromDefinitionList(), @"input"); |
+} |
+ |
+TEST_F(AutofillControllerJsTest, InferLabelForElement) { |
+ NSArray* testingElements = @[ |
+ GetTestFormInputElementWithLabelFromPrevious(), |
+ GetTestFormInputElementWithLabelFromPreviousSpan(), |
+ GetTestFormInputElementWithLabelFromPreviousParagraph(), |
+ GetTestFormInputElementWithLabelFromPreviousLabel(), |
+ GetTestFormInputElementWithLabelFromPreviousLabelOtherIgnored(), |
+ GetTestFormInputElementWithLabelFromPreviousTextSpanBr(), |
+ GetTestFormInputElementWithLabelFromPreviousTextBrAndSpan(), |
+ GetTestFormInputElementWithLabelFromListItem(), |
+ GetTestFormInputElementWithLabelFromTableColumnTD(), |
+ GetTestFormInputElementWithLabelFromTableColumnTH(), |
+ GetTestFormInputElementWithLabelFromTableNested(), |
+ GetTestFormInputElementWithLabelFromTableRow(), |
+ GetTestFormInputElementWithLabelFromDivTable(), |
+ GetTestFormInputElementWithLabelFromDefinitionList(), GetTestInputRadio(), |
+ GetTestInputCheckbox() |
+ ]; |
+ for (NSArray* testingElement in testingElements) { |
+ TestInputElementDataEvaluation(@"__gCrWeb.autofill.inferLabelForElement", |
+ @"label", testingElement, @"input"); |
+ } |
+ |
+ TestInputElementDataEvaluation(@"__gCrWeb.autofill.inferLabelForElement", |
+ @"label", GetTestFormSelectElement(), |
+ @"select"); |
+ |
+ TestInputElementDataEvaluation( |
+ @"__gCrWeb.autofill.inferLabelForElement", @"label", |
+ GetTestFormSelectElementWithOptgroup(), @"select"); |
+} |
+ |
+TEST_F(AutofillControllerJsTest, IsAutofillableElement) { |
+ const ElementByName elements_expecting_true[] = { |
+ {"firstname", 0, -1}, {"lastname", 0, -1}, |
+ {"email", 0, -1}, {"phone", 0, -1}, |
+ {"blog", 0, -1}, {"expected number of clicks", 0, -1}, |
+ {"pwd", 0, -1}, {"vehicle", 0, -1}, |
+ {"vehicle", 1, -1}, {"vehicle", 2, -1}, |
+ {"boolean", 0, -1}, {"boolean", 1, -1}, |
+ {"boolean", 2, -1}, {"state", 0, -1}, |
+ {"course", 0, -1}, |
+ }; |
+ |
+ TestExecutingBooleanJavaScriptOnElement( |
+ @"__gCrWeb.autofill.isAutofillableElement(%@)", elements_expecting_true, |
+ arraysize(elements_expecting_true)); |
+} |
+ |
+TEST_F(AutofillControllerJsTest, GetOptionStringsFromElement) { |
+ ElementByName testing_elements[] = { |
+ {"state", 0, -1}, {"course", 0, -1}, {"cars", 0, -1}}; |
+ |
+ LoadHtmlAndInject(kHTMLForTestingElements); |
+ ExecuteJavaScriptOnElementsAndCheck( |
+ @"var field = {};" |
+ "__gCrWeb.autofill.getOptionStringsFromElement(%@, field);" |
+ "__gCrWeb.stringify(field);", |
+ GetElementsByNameJavaScripts(testing_elements, |
+ arraysize(testing_elements)), |
+ @[ |
+ @("{\"option_values\":[\"CA\",\"MA\"]," |
+ "\"option_contents\":[\"CA\",\"MA\"]}"), |
+ @("{\"option_values\":[" |
+ "\"8.01.1\",\"8.01.2\",\"8.01.3\"," |
+ "\"8.02.1\",\"8.02.2\",\"8.02.3\"]," |
+ "\"option_contents\":[" |
+ "\"Lecture 01: Powers of Ten\"," |
+ "\"Lecture 02: 1D Kinematics\"," |
+ "\"Lecture 03: Vectors\"," |
+ "\"Lecture 01: What holds our world together?\"," |
+ "\"Lecture 02: Electric Field\"," |
+ "\"Lecture 03: Electric Flux\"" |
+ "]}"), |
+ @("{\"option_values\":[\"volvo\",\"saab\",\"opel\",\"audi\"]," |
+ "\"option_contents\":[\"Volvo\",\"Saab\",\"Opel\",\"Audi\"]}") |
+ ]); |
+} |
+ |
+TEST_F(AutofillControllerJsTest, FillFormField) { |
+ LoadHtmlAndInject(kHTMLForTestingElements); |
+ |
+ // Test text and select elements of which the value should be changed. |
+ const ElementByName elements[] = { |
+ {"firstname", 0, -1}, {"state", 0, -1}, |
+ }; |
+ NSArray* values = @[ |
+ @"new name", |
+ @"MA", |
+ ]; |
+ for (size_t i = 0; i < arraysize(elements); ++i) { |
+ NSString* get_element_javascript = GetElementByNameJavaScript(elements[i]); |
+ NSString* new_value = [values objectAtIndex:i]; |
+ EXPECT_NSEQ(new_value, |
+ ExecuteJavaScriptWithFormat( |
+ @"var element=%@;var data={'value':'%@'};" |
+ @"__gCrWeb.autofill.fillFormField(data, element);" |
+ @"element.value", |
+ get_element_javascript, new_value)); |
+ } |
+ |
+ // Test clickable elements, of which 'checked' should be updated. |
+ ElementByName checkable_elements[] = { |
+ {"vehicle", 0, -1}, {"vehicle", 1, -1}, {"vehicle", 2, -1}, |
+ {"boolean", 0, -1}, {"boolean", 1, -1}, {"boolean", 2, -1}, |
+ }; |
+ const bool final_is_checked_values[] = { |
+ true, false, true, false, true, true, |
+ }; |
+ for (size_t i = 0; i < arraysize(checkable_elements); ++i) { |
+ NSString* get_element_javascript = |
+ GetElementByNameJavaScript(checkable_elements[i]); |
+ bool is_checked = final_is_checked_values[i]; |
+ |
+ EXPECT_NSEQ( |
+ @(is_checked), |
+ ExecuteJavaScriptWithFormat( |
+ @"var element=%@; var value=element.value; " |
+ @"var data={'value':value,'is_checked':%@};" |
+ @"__gCrWeb.autofill.fillFormField(data, element); element.checked", |
+ get_element_javascript, is_checked ? @"true" : @"false")); |
+ } |
+ |
+ // Test elements of which the value should not be changed. |
+ ElementByName unchanged_elements[] = { |
+ {"hl", 0, -1}, // hidden element |
+ {"state", 0, 0}, // option element |
+ {"state", 0, 1}, // option element |
+ }; |
+ for (size_t i = 0; i < arraysize(unchanged_elements); ++i) { |
+ NSString* get_element_javascript = |
+ GetElementByNameJavaScript(unchanged_elements[i]); |
+ NSString* actual = ExecuteJavaScriptWithFormat( |
+ @"var element=%@;" |
+ @"var oldValue=element.value; var data={'value':'new'};" |
+ @"__gCrWeb.autofill.fillFormField(data, element);" |
+ @"element.value === oldValue", |
+ get_element_javascript); |
+ EXPECT_NSEQ(@YES, actual); |
+ } |
+} |
+ |
+TEST_F(AutofillControllerJsTest, IsTextInput) { |
+ const ElementByName elements_expecting_true[] = { |
+ {"firstname", 0, -1}, {"lastname", 0, -1}, |
+ {"email", 0, -1}, {"phone", 0, -1}, |
+ {"blog", 0, -1}, {"expected number of clicks", 0, -1}, |
+ {"pwd", 0, -1}, |
+ }; |
+ |
+ TestExecutingBooleanJavaScriptOnElement(@"__gCrWeb.autofill.isTextInput(%@)", |
+ elements_expecting_true, |
+ arraysize(elements_expecting_true)); |
+} |
+ |
+TEST_F(AutofillControllerJsTest, IsSelectElement) { |
+ const ElementByName elements_expecting_true[] = { |
+ {"state", 0, -1}, {"course", 0, -1}, |
+ }; |
+ |
+ TestExecutingBooleanJavaScriptOnElement( |
+ @"__gCrWeb.autofill.isSelectElement(%@)", elements_expecting_true, |
+ arraysize(elements_expecting_true)); |
+} |
+ |
+TEST_F(AutofillControllerJsTest, IsCheckableElement) { |
+ const ElementByName elements_expecting_true[] = { |
+ {"vehicle", 0, -1}, {"vehicle", 1, -1}, {"vehicle", 2, -1}, |
+ {"boolean", 0, -1}, {"boolean", 1, -1}, {"boolean", 2, -1}, |
+ }; |
+ |
+ TestExecutingBooleanJavaScriptOnElement( |
+ @"__gCrWeb.autofill.isCheckableElement(%@)", elements_expecting_true, |
+ arraysize(elements_expecting_true)); |
+} |
+ |
+TEST_F(AutofillControllerJsTest, IsAutofillableInputElement) { |
+ const ElementByName elements_expecting_true[] = { |
+ {"firstname", 0, -1}, {"lastname", 0, -1}, |
+ {"email", 0, -1}, {"phone", 0, -1}, |
+ {"blog", 0, -1}, {"expected number of clicks", 0, -1}, |
+ {"pwd", 0, -1}, {"vehicle", 0, -1}, |
+ {"vehicle", 1, -1}, {"vehicle", 2, -1}, |
+ {"boolean", 0, -1}, {"boolean", 1, -1}, |
+ {"boolean", 2, -1}, |
+ }; |
+ |
+ TestExecutingBooleanJavaScriptOnElement( |
+ @"__gCrWeb.autofill.isAutofillableInputElement(%@)", |
+ elements_expecting_true, arraysize(elements_expecting_true)); |
+} |
+ |
+TEST_F(AutofillControllerJsTest, ExtractAutofillableElements) { |
+ LoadHtmlAndInject(kHTMLForTestingElements); |
+ ElementByName expected_elements[] = { |
+ {"firstname", 0, -1}, {"lastname", 0, -1}, |
+ {"email", 0, -1}, {"phone", 0, -1}, |
+ {"blog", 0, -1}, {"expected number of clicks", 0, -1}, |
+ {"pwd", 0, -1}, {"vehicle", 0, -1}, |
+ {"vehicle", 1, -1}, {"vehicle", 2, -1}, |
+ {"boolean", 0, -1}, {"boolean", 1, -1}, |
+ {"boolean", 2, -1}, {"state", 0, -1}, |
+ }; |
+ NSArray* expected = GetElementsByNameJavaScripts( |
+ expected_elements, arraysize(expected_elements)); |
+ |
+ NSString* parameter = @"window.document.getElementsByTagName('form')[0]"; |
+ for (NSUInteger index = 0; index < [expected count]; index++) { |
+ EXPECT_NSEQ(@YES, |
+ ExecuteJavaScriptWithFormat( |
+ @"var controlElements=" |
+ "__gCrWeb.autofill.extractAutofillableElementsInForm(%@);" |
+ "controlElements[%" PRIuNS "] === %@", |
+ parameter, index, expected[index])); |
+ } |
+} |
+ |
+void AutofillControllerJsTest::TestWebFormControlElementToFormField( |
+ NSArray* test_data, |
+ NSString* tag_name) { |
+ LoadHtmlAndInject([test_data firstObject]); |
+ |
+ for (NSUInteger i = 0; i < arraysize(kFormExtractMasks); ++i) { |
+ ExtractMask extract_mask = kFormExtractMasks[i]; |
+ NSArray* attributes_to_check = |
+ GetFormFieldAttributeListsToCheck(extract_mask); |
+ |
+ for (NSUInteger i = 1; i < [test_data count]; ++i) { |
+ NSString* get_element_to_test = |
+ [NSString stringWithFormat:@"var element = " |
+ "window.document.getElementsByTagName('%" |
+ "@')[%" PRIuNS "]", |
+ tag_name, i - 1]; |
+ NSDictionary* expected = [test_data objectAtIndex:i]; |
+ // Generates JavaScripts to verify the results. Parameter |results| is |
+ // @"field" as in the evaluation JavaScripts the results are returned in |
+ // |field|. |
+ NSString* verifying_javascripts = GenerateElementItemVerifyingJavaScripts( |
+ @"field", extract_mask, expected, attributes_to_check); |
+ EXPECT_NSEQ(@YES, |
+ ExecuteJavaScriptWithFormat( |
+ @"%@; var field = {};" |
+ "__gCrWeb.autofill.webFormControlElementToFormField(" |
+ " element, %u, field);" |
+ "%@", |
+ get_element_to_test, extract_mask, verifying_javascripts)) |
+ << base::SysNSStringToUTF8([NSString |
+ stringWithFormat: |
+ @"webFormControlElementToFormField actual results are: " |
+ @"%@, \n" |
+ "expected to be verified by %@", |
+ ExecuteJavaScriptWithFormat( |
+ @"%@; var field = {};" |
+ "__gCrWeb.autofill.webFormControlElementToFormField(" |
+ " element, %u, field);__gCrWeb.stringify(field);", |
+ get_element_to_test, extract_mask), |
+ verifying_javascripts]); |
+ } |
+ } |
+} |
+ |
+TEST_F(AutofillControllerJsTest, WebFormControlElementToFormField) { |
+ NSArray* test_input_elements = @[ |
+ GetTestFormInputElementWithLabelFromPrevious(), |
+ GetTestFormInputElementWithLabelFromPreviousSpan(), |
+ GetTestFormInputElementWithLabelFromPreviousParagraph(), |
+ GetTestFormInputElementWithLabelFromPreviousLabel(), |
+ GetTestFormInputElementWithLabelFromPreviousLabelOtherIgnored(), |
+ GetTestFormInputElementWithLabelFromPreviousTextBrAndSpan(), |
+ GetTestFormInputElementWithLabelFromPreviousTextSpanBr(), |
+ GetTestFormInputElementWithLabelFromListItem(), |
+ GetTestFormInputElementWithLabelFromTableColumnTD(), |
+ GetTestFormInputElementWithLabelFromTableColumnTH(), |
+ GetTestFormInputElementWithLabelFromTableNested(), |
+ GetTestFormInputElementWithLabelFromTableRow(), |
+ GetTestFormInputElementWithLabelFromDivTable(), |
+ GetTestFormInputElementWithLabelFromDefinitionList(), GetTestInputRadio(), |
+ GetTestInputCheckbox() |
+ ]; |
+ for (NSArray* test_element in test_input_elements) { |
+ TestWebFormControlElementToFormField(test_element, @"input"); |
+ } |
+ |
+ TestWebFormControlElementToFormField(GetTestFormSelectElement(), @"select"); |
+ TestWebFormControlElementToFormField(GetTestFormSelectElementWithOptgroup(), |
+ @"select"); |
+} |
+ |
+void AutofillControllerJsTest::TestWebFormElementToFormDataForOneForm( |
+ NSString* get_form_element_javascripts, |
+ NSUInteger extract_mask, |
+ NSString* expected_result, |
+ NSString* verifying_javascripts) { |
+ NSString* actual = ExecuteJavaScriptWithFormat( |
+ @"var form={}; var field={};" |
+ "(__gCrWeb.autofill.webFormElementToFormData(" |
+ "window, %@, null, %" PRIuNS ", form, field) === %@) && %@", |
+ get_form_element_javascripts, extract_mask, expected_result, |
+ verifying_javascripts); |
+ |
+ EXPECT_NSEQ(@YES, actual) << base::SysNSStringToUTF8([NSString |
+ stringWithFormat:@"Actual:\n%@; expected to be verifyied by\n%@", |
+ ExecuteJavaScriptWithFormat( |
+ @"var form={};" |
+ "__gCrWeb.autofill." |
+ "webFormElementToFormData(window, %@, null," |
+ "%" PRIuNS ", form, null);" |
+ "__gCrWeb.stringify(form);", |
+ get_form_element_javascripts, extract_mask), |
+ verifying_javascripts]); |
+} |
+ |
+void AutofillControllerJsTest::TestWebFormElementToFormData( |
+ NSArray* test_items) { |
+ NSString* form_html_fragment = |
+ @"<form name='TestForm' action='http://cnn.com' method='post'>"; |
+ for (NSUInteger i = 0; i < [test_items count]; ++i) { |
+ form_html_fragment = [form_html_fragment |
+ stringByAppendingString:[[test_items objectAtIndex:i] |
+ objectAtIndex:0U]]; |
+ } |
+ form_html_fragment = [form_html_fragment stringByAppendingString:@"</form>"]; |
+ LoadHtmlAndInject(form_html_fragment); |
+ |
+ NSString* parameter = @"document.getElementsByTagName('form')[0]"; |
+ for (NSUInteger extract_index = 0; |
+ extract_index < arraysize(kFormExtractMasks); ++extract_index) { |
+ NSString* expected_result = @"true"; |
+ // We don't verify 'action' here as action is generated as a complete url |
+ // and here data url is used. |
+ NSMutableArray* verifying_javascripts = [NSMutableArray |
+ arrayWithObjects:@"form['name'] === 'TestForm'", |
+ @"form['method'] === 'post'", |
+ @"form['origin'] === window.location.href", nil]; |
+ ExtractMask extract_mask = kFormExtractMasks[extract_index]; |
+ [verifying_javascripts |
+ addObject:GenerateTestItemVerifyingJavaScripts( |
+ @"form", extract_mask, test_items, |
+ GetFormFieldAttributeListsToCheck(extract_mask))]; |
+ TestWebFormElementToFormDataForOneForm( |
+ parameter, extract_mask, expected_result, |
+ [verifying_javascripts componentsJoinedByString:@"&&"]); |
+ } |
+} |
+ |
+TEST_F(AutofillControllerJsTest, WebFormElementToFormData) { |
+ NSArray* test_elements = @[ |
+ GetTestFormInputElementWithLabelFromPrevious(), |
+ GetTestFormInputElementWithLabelFromPreviousSpan(), |
+ GetTestFormInputElementWithLabelFromPreviousParagraph(), |
+ GetTestFormInputElementWithLabelFromPreviousLabel(), |
+ GetTestFormInputElementWithLabelFromPreviousLabelOtherIgnored(), |
+ GetTestFormInputElementWithLabelFromPreviousTextSpanBr(), |
+ GetTestFormInputElementWithLabelFromPreviousTextBrAndSpan(), |
+ GetTestFormInputElementWithLabelFromListItem(), |
+ GetTestFormInputElementWithLabelFromTableColumnTD(), |
+ GetTestFormInputElementWithLabelFromTableColumnTH(), |
+ GetTestFormInputElementWithLabelFromTableNested(), |
+ GetTestFormInputElementWithLabelFromTableRow(), |
+ GetTestFormInputElementWithLabelFromDivTable(), |
+ GetTestFormInputElementWithLabelFromDefinitionList(), GetTestInputRadio(), |
+ GetTestInputCheckbox(), GetTestFormSelectElement(), |
+ GetTestFormSelectElementWithOptgroup() |
+ ]; |
+ // Test a form that has a signle item in the array. |
+ for (NSArray* testElement in test_elements) { |
+ TestWebFormElementToFormData(@[ testElement ]); |
+ } |
+ |
+ // Also test a form that has all the above items. |
+ TestWebFormElementToFormData(test_elements); |
+} |
+ |
+TEST_F(AutofillControllerJsTest, |
+ DISABLED_WebFormElementToFormDataTooManyFields) { |
+ NSString* html_fragment = |
+ @"<FORM name='Test' action='http://c.com' method='post'>"; |
+ // In autofill_controller.js, the maximum number of parsable element is 100 |
+ // (__gCrWeb.autofill.MAX_PARSEABE_FIELDS = 100). Here an HTML page with 101 |
+ // elements is generated for testing. |
+ for (NSUInteger index = 0; index < 101; ++index) { |
+ html_fragment = |
+ [html_fragment stringByAppendingFormat:@"<INPUT type='text'/>"]; |
+ } |
+ html_fragment = [html_fragment stringByAppendingFormat:@"</FORM>"]; |
+ |
+ LoadHtmlAndInject(html_fragment); |
+ TestWebFormElementToFormDataForOneForm( |
+ @"document.getElementsByTagName('form')[0]", 1, @"false", @"true"); |
+} |
+ |
+TEST_F(AutofillControllerJsTest, WebFormElementToFormEmpty) { |
+ NSString* html_fragment = |
+ @"<FORM name='Test' action='http://c.com' method='post'>"; |
+ html_fragment = [html_fragment stringByAppendingFormat:@"</FORM>"]; |
+ |
+ LoadHtmlAndInject(html_fragment); |
+ TestWebFormElementToFormDataForOneForm( |
+ @"document.getElementsByTagName('form')[0]", 1, @"false", @"true"); |
+} |
+ |
+void AutofillControllerJsTest::TestExtractNewForms( |
+ NSString* html, |
+ BOOL is_origin_window_location, |
+ NSArray* expected_items) { |
+ LoadHtmlAndInject(html); |
+ // Generates verifying javascripts. |
+ NSMutableArray* verifying_javascripts = [NSMutableArray array]; |
+ for (NSUInteger i = 0U; i < [expected_items count]; ++i) { |
+ [verifying_javascripts |
+ addObject:[NSString stringWithFormat:@"forms[%" PRIuNS |
+ "]['name'] === 'TestForm'", |
+ i]]; |
+ [verifying_javascripts |
+ addObject:[NSString stringWithFormat:@"forms[%" PRIuNS |
+ "]['method'] === 'post'", |
+ i]]; |
+ if (is_origin_window_location) { |
+ [verifying_javascripts |
+ addObject:[NSString stringWithFormat: |
+ @"forms[%" PRIuNS |
+ "]['origin'] === window.location.href", |
+ i]]; |
+ } |
+ // This is the extract mask used by |
+ // __gCrWeb.autofill.extractNewForms. |
+ NSUInteger extract_mask = EXTRACT_VALUE | EXTRACT_OPTIONS; |
+ [verifying_javascripts |
+ addObject:GenerateTestItemVerifyingJavaScripts( |
+ [NSString stringWithFormat:@"forms[%" PRIuNS "]", i], |
+ extract_mask, [expected_items objectAtIndex:i], |
+ // The relevant attributes for the extract mask |
+ GetFormFieldAttributeListsToCheck(extract_mask))]; |
+ } |
+ |
+ NSString* actual = ExecuteJavaScriptWithFormat( |
+ @"var forms = __gCrWeb.autofill.extractNewForms(%" PRIuS "); %@", |
+ autofill::kRequiredFieldsForPredictionRoutines, |
+ [verifying_javascripts componentsJoinedByString:@"&&"]); |
+ |
+ EXPECT_NSEQ(@YES, actual) << base::SysNSStringToUTF8([NSString |
+ stringWithFormat:@"actually forms = %@, " |
+ "but it is expected to be verified by %@", |
+ ExecuteJavaScriptWithFormat( |
+ @"var forms = __gCrWeb.autofill.extractNewForms(" |
+ "%" PRIuS "); __gCrWeb.stringify(forms)", |
+ autofill::kRequiredFieldsForPredictionRoutines), |
+ verifying_javascripts]); |
+} |
+ |
+TEST_F(AutofillControllerJsTest, ExtractFormsAndFormElements) { |
+ NSArray* testFirstFormItems = @[ |
+ GetTestFormInputElementWithLabelFromPrevious(), |
+ GetTestFormInputElementWithLabelFromPreviousSpan(), |
+ GetTestFormInputElementWithLabelFromPreviousParagraph(), |
+ GetTestFormInputElementWithLabelFromPreviousLabel(), |
+ GetTestFormInputElementWithLabelFromPreviousLabelOtherIgnored(), |
+ GetTestFormInputElementWithLabelFromPreviousTextSpanBr(), |
+ GetTestFormInputElementWithLabelFromPreviousTextBrAndSpan(), |
+ GetTestFormInputElementWithLabelFromListItem(), |
+ GetTestFormInputElementWithLabelFromTableColumnTD(), |
+ GetTestFormInputElementWithLabelFromTableColumnTH(), |
+ GetTestFormInputElementWithLabelFromTableNested(), |
+ GetTestFormInputElementWithLabelFromTableRow(), |
+ GetTestFormInputElementWithLabelFromDivTable(), |
+ GetTestFormInputElementWithLabelFromDefinitionList(), GetTestInputRadio(), |
+ GetTestInputCheckbox() |
+ ]; |
+ NSArray* testSecondFormItems = @[ |
+ GetTestFormInputElementWithLabelFromDivTable(), GetTestFormSelectElement(), |
+ GetTestFormSelectElementWithOptgroup() |
+ ]; |
+ NSArray* test_forms = @[ testFirstFormItems, testSecondFormItems ]; |
+ |
+ NSString* html = @"<html><body>"; |
+ for (NSArray* testFormItems in test_forms) { |
+ html = [html |
+ stringByAppendingString: |
+ @"<form name='TestForm' action='http://c.com' method='post'>"]; |
+ for (NSArray* testItem in testFormItems) { |
+ html = [html stringByAppendingString:[testItem objectAtIndex:0U]]; |
+ } |
+ html = [html stringByAppendingFormat:@"</form>"]; |
+ } |
+ html = [html stringByAppendingFormat:@"</body></html>"]; |
+ TestExtractNewForms(html, true, test_forms); |
+} |
+ |
+TEST_F(AutofillControllerJsTest, ExtractFormsAndFormElementsNestedFrame) { |
+ NSArray* testFirstFormItems = @[ |
+ GetTestFormInputElementWithLabelFromPrevious(), |
+ GetTestFormInputElementWithLabelFromPreviousSpan(), |
+ GetTestFormInputElementWithLabelFromPreviousParagraph(), |
+ GetTestFormInputElementWithLabelFromPreviousLabel(), |
+ GetTestFormInputElementWithLabelFromPreviousLabelOtherIgnored(), |
+ GetTestFormInputElementWithLabelFromPreviousTextSpanBr(), |
+ GetTestFormInputElementWithLabelFromPreviousTextBrAndSpan(), |
+ GetTestFormInputElementWithLabelFromListItem(), |
+ GetTestFormInputElementWithLabelFromTableColumnTD(), |
+ GetTestFormInputElementWithLabelFromTableColumnTH(), |
+ GetTestFormInputElementWithLabelFromTableNested(), |
+ GetTestFormInputElementWithLabelFromTableRow(), |
+ GetTestFormInputElementWithLabelFromDivTable(), |
+ GetTestFormInputElementWithLabelFromDefinitionList(), GetTestInputRadio(), |
+ GetTestInputCheckbox() |
+ ]; |
+ NSArray* testSecondFormItems = @[ |
+ GetTestFormInputElementWithLabelFromDivTable(), GetTestFormSelectElement(), |
+ GetTestFormSelectElementWithOptgroup() |
+ ]; |
+ NSArray* test_forms = @[ testFirstFormItems, testSecondFormItems ]; |
+ |
+ // Test an html that has nested frames. |
+ NSString* nested_frame_html_fragment = @"<html><body>"; |
+ for (NSUInteger i = 0; i < [test_forms count]; ++i) { |
+ NSArray* test_elements = [test_forms objectAtIndex:i]; |
+ NSString* form_string = |
+ @"<form name='TestForm' action='http://c.com' method='post'>"; |
+ for (NSUInteger j = 0; j < [test_elements count]; ++j) { |
+ form_string = |
+ [form_string stringByAppendingString:[[test_elements objectAtIndex:j] |
+ objectAtIndex:0U]]; |
+ } |
+ form_string = [form_string stringByAppendingFormat:@"</form>"]; |
+ |
+ if (i == 0) { |
+ nested_frame_html_fragment = |
+ [nested_frame_html_fragment stringByAppendingString:form_string]; |
+ } else { |
+ nested_frame_html_fragment = [nested_frame_html_fragment |
+ stringByAppendingString: |
+ [NSString stringWithFormat:@"<iframe srcdoc=\"%@\"></iframe>", |
+ form_string]]; |
+ } |
+ } |
+ nested_frame_html_fragment = |
+ [nested_frame_html_fragment stringByAppendingString:@"</body></html>"]; |
+ TestExtractNewForms(nested_frame_html_fragment, false, test_forms); |
+} |
+ |
+TEST_F(AutofillControllerJsTest, |
+ ExtractFormsAndFormElementsWithFormAssociatedElementsOutOfForm) { |
+ NSString* html = @"<html><body>" |
+ "<form id='testform'></form>" |
+ "1 <input type='text' name='name1' form='testform'></input>" |
+ "2 <input type='text' name='name2' form='testform'></input>" |
+ "3 <input type='text' name='name3' form='testform'></input>" |
+ "4 <input type='text' name='name4' form='testform'></input>" |
+ "</body></html>"; |
+ LoadHtmlAndInject(html); |
+ |
+ NSString* verifying_javascript = @"forms[0]['fields'][0]['name']==='name1' &&" |
+ @"forms[0]['fields'][0]['label']==='1' &&" |
+ @"forms[0]['fields'][1]['name']==='name2' &&" |
+ @"forms[0]['fields'][1]['label']==='2' &&" |
+ @"forms[0]['fields'][2]['name']==='name3' &&" |
+ @"forms[0]['fields'][2]['label']==='3' &&" |
+ @"forms[0]['fields'][3]['name']==='name4' &&" |
+ @"forms[0]['fields'][3]['label']==='4'"; |
+ EXPECT_NSEQ(@YES, ExecuteJavaScriptWithFormat( |
+ @"var forms = " |
+ "__gCrWeb.autofill.extractNewForms(1); %@", |
+ verifying_javascript)); |
+} |
+ |
+TEST_F(AutofillControllerJsTest, ExtractForms) { |
+ NSString* html = @"<html><body>"; |
+ html = |
+ [html stringByAppendingString: |
+ @"<form name='TestForm' action='http://c.com' method='post'>"]; |
+ html = [html |
+ stringByAppendingString:[GetTestFormInputElementWithLabelFromPrevious() |
+ objectAtIndex:0U]]; |
+ html = |
+ [html stringByAppendingString:[GetTestInputCheckbox() objectAtIndex:0U]]; |
+ html = [html stringByAppendingString: |
+ [GetTestFormInputElementWithLabelFromTableColumnTH() |
+ objectAtIndex:0U]]; |
+ html = [html stringByAppendingString: |
+ [GetTestFormInputElementWithLabelFromPreviousTextBrAndSpan() |
+ objectAtIndex:0U]]; |
+ html = [html |
+ stringByAppendingString:[GetTestFormSelectElement() objectAtIndex:0U]]; |
+ html = [html stringByAppendingFormat:@"</form>"]; |
+ html = [html stringByAppendingFormat:@"</body></html>"]; |
+ |
+ LoadHtmlAndInject(html); |
+ |
+ NSDictionary* expected = @{ |
+ @"name" : @"TestForm", |
+ @"method" : @"post", |
+ @"fields" : @[ |
+ @{ |
+ @"name" : @"firstname", |
+ @"form_control_type" : @"text", |
+ @"max_length" : GetDefaultMaxLength(), |
+ @"should_autocomplete" : @true, |
+ @"is_checkable" : @false, |
+ @"is_focusable" : @true, |
+ @"value" : @"John", |
+ @"label" : @"* First name:" |
+ }, |
+ @{ |
+ @"name" : @"vehicle", |
+ @"form_control_type" : @"checkbox", |
+ @"should_autocomplete" : @true, |
+ @"is_checkable" : @true, |
+ @"is_focusable" : @true, |
+ @"value" : @"Bike", |
+ @"label" : @"Bicycle" |
+ }, |
+ @{ |
+ @"name" : @"vehicle", |
+ @"form_control_type" : @"checkbox", |
+ @"should_autocomplete" : @true, |
+ @"is_checkable" : @true, |
+ @"is_focusable" : @true, |
+ @"value" : @"Car", |
+ @"label" : @"Automobile" |
+ }, |
+ @{ |
+ @"name" : @"vehicle", |
+ @"form_control_type" : @"checkbox", |
+ @"should_autocomplete" : @true, |
+ @"is_checkable" : @true, |
+ @"is_focusable" : @true, |
+ @"value" : @"Rocket", |
+ @"label" : @"Missile" |
+ }, |
+ @{ |
+ @"name" : @"nameintableth", |
+ @"form_control_type" : @"text", |
+ @"max_length" : GetDefaultMaxLength(), |
+ @"should_autocomplete" : @true, |
+ @"is_checkable" : @false, |
+ @"is_focusable" : @true, |
+ @"value" : @"John", |
+ @"label" : @"* First name:" |
+ }, |
+ @{ |
+ @"name" : @"emailtableth", |
+ @"form_control_type" : @"email", |
+ @"max_length" : GetDefaultMaxLength(), |
+ @"should_autocomplete" : @true, |
+ @"is_checkable" : @false, |
+ @"is_focusable" : @true, |
+ @"value" : @"john@example.com", |
+ @"label" : @"Email:" |
+ }, |
+ @{ |
+ @"name" : @"pwd", |
+ @"form_control_type" : @"password", |
+ @"autocomplete_attribute" : @"off", |
+ @"max_length" : GetDefaultMaxLength(), |
+ @"should_autocomplete" : @false, |
+ @"is_checkable" : @false, |
+ @"is_focusable" : @true, |
+ @"value" : @"", |
+ @"label" : @"* Password:" |
+ }, |
+ @{ |
+ @"name" : @"state", |
+ @"form_control_type" : @"select-one", |
+ @"is_focusable" : @1, |
+ @"option_values" : @[ @"CA", @"TX" ], |
+ @"option_contents" : @[ @"California", @"Texas" ], |
+ @"should_autocomplete" : @1, |
+ @"value" : @"CA", |
+ @"label" : @"State:" |
+ } |
+ ] |
+ }; |
+ |
+ NSString* result = ExecuteJavaScriptWithFormat( |
+ @"__gCrWeb.autofill.extractForms(%zu)", |
+ autofill::kRequiredFieldsForPredictionRoutines); |
+ NSDictionary* resultDict = [NSJSONSerialization |
+ JSONObjectWithData:[result dataUsingEncoding:NSUTF8StringEncoding] |
+ options:0 |
+ error:nil]; |
+ ASSERT_NSNE(nil, resultDict); |
+ |
+ NSDictionary* forms = [resultDict[@"forms"] objectAtIndex:0]; |
+ [expected enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL* stop) { |
+ EXPECT_NSEQ(forms[key], obj); |
+ }]; |
+ |
+ // Test with Object.prototype.toJSON override. |
+ result = ExecuteJavaScriptWithFormat( |
+ @"Object.prototype.toJSON=function(){return 'abcde';};" |
+ "__gCrWeb.autofill.extractForms(%zu)", |
+ autofill::kRequiredFieldsForPredictionRoutines); |
+ resultDict = [NSJSONSerialization |
+ JSONObjectWithData:[result dataUsingEncoding:NSUTF8StringEncoding] |
+ options:0 |
+ error:nil]; |
+ ASSERT_NSNE(nil, resultDict); |
+ |
+ forms = [resultDict[@"forms"] objectAtIndex:0]; |
+ [expected enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL* stop) { |
+ EXPECT_NSEQ(forms[key], obj); |
+ }]; |
+ |
+ // Test with Array.prototype.toJSON override. |
+ result = ExecuteJavaScriptWithFormat( |
+ @"Array.prototype.toJSON=function(){return 'abcde';};" |
+ "__gCrWeb.autofill.extractForms(%zu)", |
+ autofill::kRequiredFieldsForPredictionRoutines); |
+ resultDict = [NSJSONSerialization |
+ JSONObjectWithData:[result dataUsingEncoding:NSUTF8StringEncoding] |
+ options:0 |
+ error:nil]; |
+ ASSERT_NSNE(nil, resultDict); |
+ |
+ forms = [resultDict[@"forms"] objectAtIndex:0]; |
+ [expected enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL* stop) { |
+ EXPECT_NSEQ(forms[key], obj); |
+ }]; |
+} |
+ |
+TEST_F(AutofillControllerJsTest, FillActiveFormField) { |
+ LoadHtmlAndInject(kHTMLForTestingElements); |
+ |
+ NSString* newValue = @"new value"; |
+ EXPECT_NSEQ(newValue, |
+ ExecuteJavaScriptWithFormat( |
+ @"var element=document.getElementsByName('lastname')[0];" |
+ "element.focus();" |
+ "var data={\"name\":\"lastname\",\"value\":\"%@\"};" |
+ "__gCrWeb.autofill.fillActiveFormField(data);" |
+ "element.value", |
+ newValue)); |
+ |
+ EXPECT_NSEQ(@YES, ExecuteJavaScriptWithFormat( |
+ @"var element=document.getElementsByName('gl')[0];" |
+ "element.focus();" |
+ "var oldValue = element.value;" |
+ "var data={\"name\":\"lastname\",\"value\":\"%@\"};" |
+ "__gCrWeb.autofill.fillActiveFormField(data);" |
+ "element.value === oldValue", |
+ newValue)) |
+ << "A non-form element's value should changed."; |
+} |
+ |
+// iOS version of FormAutofillTest.FormCache_ExtractNewForms from |
+// chrome/renderer/autofill/form_autofill_browsertest.cc |
+TEST_F(AutofillControllerJsTest, ExtractNewForms) { |
+ NSArray* testCases = @[ |
+ // An empty form should not be extracted |
+ @{ |
+ @"html" : @"<FORM name='TestForm' action='http://buh.com' method='post'>" |
+ "</FORM>", |
+ @"expected_forms" : @0 |
+ }, |
+ // A form with less than three fields with no autocomplete type(s) should |
+ // not be extracted. |
+ @{ |
+ @"html" : @"<FORM name='TestForm' action='http://buh.com' method='post'>" |
+ " <INPUT type='name' id='firstname'/>" |
+ "</FORM>", |
+ @"expected_forms" : @0 |
+ }, |
+ // A form with less than three fields with at least one autocomplete type |
+ // should be extracted. |
+ @{ |
+ @"html" : @"<FORM name='TestForm' action='http://buh.com' method='post'>" |
+ " <INPUT type='name' id='firstname'" |
+ " autocomplete='given-name'/>" |
+ "</FORM>", |
+ @"expected_forms" : @1 |
+ }, |
+ // A form with three or more fields should be extracted. |
+ @{ |
+ @"html" : @"<FORM name='TestForm' action='http://buh.com' method='post'>" |
+ " <INPUT type='text' id='firstname'/>" |
+ " <INPUT type='text' id='lastname'/>" |
+ " <INPUT type='text' id='email'/>" |
+ " <INPUT type='submit' value='Send'/>" |
+ "</FORM>", |
+ @"expected_forms" : @1 |
+ } |
+ ]; |
+ |
+ for (NSDictionary* testCase in testCases) { |
+ LoadHtmlAndInject(testCase[@"html"]); |
+ |
+ NSString* result = |
+ ExecuteJavaScriptWithFormat(@"__gCrWeb.autofill.extractForms(%zu)", |
+ autofill::kRequiredFieldsForUpload); |
+ NSDictionary* resultDict = [NSJSONSerialization |
+ JSONObjectWithData:[result dataUsingEncoding:NSUTF8StringEncoding] |
+ options:0 |
+ error:nil]; |
+ ASSERT_NSNE(nil, resultDict); |
+ NSUInteger expectedCount = |
+ [testCase[@"expected_forms"] unsignedIntegerValue]; |
+ EXPECT_EQ(expectedCount, [resultDict[@"forms"] count]) |
+ << base::SysNSStringToUTF8(testCase[@"html"]); |
+ } |
+} |
+ |
+} // namespace |