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 */ 10function SettingsFormatWebUITest() {} 11 12/** 13 * Map of rule exemptions grouped by test. 14 * @const 15 */ 16SettingsFormatWebUITest.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 */ 29SettingsFormatWebUITest.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 36SettingsFormatWebUITest.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 */ 118TEST_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 */ 134TEST_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 */ 148TEST_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