Home | History | Annotate | Download | only in options
      1 // Copyright (c) 2012 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 /**
      6  * TestFixture for testing the formatting of settings pages.
      7  * @extends {testing.Test}
      8  * @constructor
      9  */
     10 function SettingsFormatWebUITest() {}
     11 
     12 /**
     13  * Map of rule exemptions grouped by test.
     14  * @const
     15  */
     16 SettingsFormatWebUITest.Filters = {
     17   /**
     18    * Exemption for checkboxes that do not require an id or pref property.
     19    * Input methods use inputMethodId instead of id for unique identification.
     20    */
     21   'pref': ['language-options-input-method-template',
     22            'language-options-input-method-list']
     23 };
     24 
     25 /**
     26  * Collection of error messages.
     27  * @const
     28  */
     29 SettingsFormatWebUITest.Messages = {
     30     MISSING_CHECK_WRAPPER: 'Element $1 should be enclosed in <div class="$2">',
     31     MISSING_ID_OR_PREF: 'Missing id or pref preoperty for checkbox $1.',
     32     MISSING_RADIO_BUTTON_NAME: 'Radio button $1 is missing the name property',
     33     MISSING_RADIO_BUTTON_VALUE: 'Radio button $1 is missing the value property',
     34 };
     35 
     36 SettingsFormatWebUITest.prototype = {
     37   __proto__: testing.Test.prototype,
     38 
     39   /**
     40    * Navigate to browser settings.
     41    */
     42   browsePreload: 'chrome://settings-frame/',
     43 
     44   /**
     45    * List of errors generated during a test. Used instead of expect* functions
     46    * to suppress verbosity. The implementation of errorsToMessage in the
     47    * testing API generates a call stack for each error produced which greatly
     48    * reduces readability.
     49    * @type {Array.<string>}
     50    */
     51   errors: null,
     52 
     53   setUp: function() {
     54     this.errors = [];
     55   },
     56 
     57   tearDown: function() {
     58     assertTrue(this.errors.length == 0, '\n' + this.errors.join('\n'));
     59   },
     60 
     61   /**
     62    * Generates a failure message. During tear down of the test, the accumulation
     63    * of pending messages triggers a test failure.
     64    * @param {string} key Label of the message formatting string.
     65    * @param {!Element} element The element that triggered the failure.
     66    * @param {...string} args Additional arguments for formatting the message.
     67    */
     68   fail: function(key, element, args) {
     69     var subs = [this.getLabel(element)].concat(
     70         Array.prototype.slice.call(arguments, 2));
     71     var message = SettingsFormatWebUITest.Messages[key].replace(
     72         /\$\d/g,
     73         function(m) {
     74       return subs[m[1] - 1] || '$' + m[1];
     75     });
     76     assertFalse(/\$\d/.test(message), 'found unreplaced subs');
     77     this.errors.push(message);
     78   },
     79 
     80  /**
     81   * String for identifying a node within an error message.
     82   * @param {!Element} element The target element to identify.
     83   * @return {string} Name to facilitate tracking down the element.
     84   */
     85   getLabel: function(element) {
     86     if (element.id)
     87       return element.id;
     88 
     89     if (element.pref)
     90       return element.pref;
     91 
     92     if (element.name && element.value)
     93       return element.name + '-' + element.value;
     94 
     95     return this.getLabel(element.parentNode);
     96   },
     97 
     98 
     99   /**
    100    * Checks if the node is exempt from following the formatting rule.
    101    * @param {!Element} element The candidate element.
    102    * @param {Array.<string>} filters List of exemptions.
    103    * @return {boolean} True if the element is exempt.
    104    */
    105   isExempt: function(element, filters) {
    106     var target = this.getLabel(element);
    107     for (var i = 0; i < filters.length; i++) {
    108       if (filters[i] == target)
    109         return true;
    110     }
    111     return false;
    112   }
    113 };
    114 
    115 /**
    116  * Ensure that radio and checkbox buttons have consistent layout.
    117  */
    118 TEST_F('SettingsFormatWebUITest', 'RadioCheckboxStyleCheck', function() {
    119   var settings = $('settings');
    120   assertTrue(settings != null, 'Unable to access settings');
    121   var query = 'input[type=checkbox], input[type=radio]';
    122   var elements = document.querySelectorAll(query);
    123   assertTrue(elements.length > 0);
    124   for (var i = 0; i < elements.length; i++) {
    125     var element = elements[i];
    126     if (!findAncestorByClass(element, element.type))
    127       this.fail('MISSING_CHECK_WRAPPER', element, element.type);
    128   }
    129 });
    130 
    131 /**
    132  * Each checkbox requires an id or pref property.
    133  */
    134 TEST_F('SettingsFormatWebUITest', 'CheckboxIdOrPrefCheck', function() {
    135   var query =
    136       'input[type=checkbox]:not([pref]):not([id]):not(.spacer-checkbox)';
    137   var elements = document.querySelectorAll(query);
    138   for (var i = 0; i < elements.length; i++) {
    139     var element = elements[i];
    140     if (!this.isExempt(element, SettingsFormatWebUITest.Filters['pref']))
    141       this.fail('MISSING_ID_OR_PREF', element);
    142   }
    143 });
    144 
    145 /**
    146  * Each radio button requires name and value properties.
    147  */
    148 TEST_F('SettingsFormatWebUITest', 'RadioButtonNameValueCheck', function() {
    149   var elements = document.querySelectorAll('input[type=radio]');
    150   for (var i = 0; i < elements.length; i++) {
    151     var element = elements[i];
    152     if (!element.name)
    153       this.fail('MISSING_RADIO_BUTTON_NAME', element);
    154 
    155     if (!element.getAttribute('value'))
    156       this.fail('MISSING_RADIO_BUTTON_VALUE', element);
    157   }
    158 });
    159