browser_css_getInfo.js (7675B)
1 /* Any copyright is dedicated to the Public Domain. 2 http://creativecommons.org/publicdomain/zero/1.0/ */ 3 4 "use strict"; 5 6 const CSSCompleter = require("resource://devtools/client/shared/sourceeditor/css-autocompleter.js"); 7 8 const source = [ 9 ".devtools-toolbar {", 10 " -moz-appearance: none;", 11 " padding:4px 3px;border-bottom-width: 1px;", 12 " border-bottom-style: solid;", 13 "}", 14 "", 15 "#devtools-menu.devtools-menulist,", 16 ".devtools-toolbarbutton#devtools-menu {", 17 " -moz-appearance: none;", 18 " align-items: center;", 19 " min-width: 78px;", 20 " min-height: 22px;", 21 " text-shadow: 0 -1px 0 hsla(210,8%,5%,.45);", 22 " border: 1px solid hsla(210,8%,5%,.45);", 23 " border-radius: 3px;", 24 " background: linear-gradient(hsla(212,7%,57%,.35),", 25 " hsla(212,7%,57%,.1)) padding-box;", 26 " margin: 0 3px;", 27 " color: inherit;", 28 "}", 29 "", 30 ".devtools-toolbarbutton > hbox.toolbarbutton-menubutton-button {", 31 " flex-direction: row;", 32 "}", 33 "", 34 ".devtools-menulist:active,", 35 "#devtools-toolbarbutton:focus {", 36 " outline: 1px dotted hsla(210,30%,85%,0.7);", 37 " outline-offset : -4px;", 38 "}", 39 "", 40 ".devtools-toolbarbutton:not([label]) {", 41 " min-width: 32px;", 42 "}", 43 "", 44 ".devtools-toolbarbutton:not([label]) > .toolbarbutton-text, .devtools-toolbar {", 45 " display: none;", 46 "}", 47 ".multiline > ", 48 ".complex + ", 49 "#selector {}", 50 ].join("\n"); 51 52 // Format of test cases : 53 // [ 54 // {line, ch}, - The caret position at which the getInfo call should be made 55 // expectedState, - The expected state at the caret 56 // expectedSelector, - The expected selector for the state 57 // expectedProperty, - The expected property name for states value and property 58 // expectedValue, - If state is value, then the expected value 59 // ] 60 61 /* eslint-disable max-len */ 62 const tests = [ 63 [{ line: 0, ch: 13 }, CSSCompleter.CSS_STATE_SELECTOR, ".devtools-toolbar"], 64 [ 65 { line: 8, ch: 13 }, 66 CSSCompleter.CSS_STATE_PROPERTY, 67 [ 68 "#devtools-menu.devtools-menulist", 69 ".devtools-toolbarbutton#devtools-menu ", 70 ], 71 "-moz-appearance", 72 ], 73 [ 74 { line: 28, ch: 25 }, 75 CSSCompleter.CSS_STATE_VALUE, 76 [".devtools-menulist:active", "#devtools-toolbarbutton:focus "], 77 "outline-offset", 78 "-4px", 79 ], 80 [{ line: 4, ch: 1 }, CSSCompleter.CSS_STATE_NULL], 81 [{ line: 5, ch: 0 }, CSSCompleter.CSS_STATE_NULL], 82 [ 83 { line: 31, ch: 13 }, 84 CSSCompleter.CSS_STATE_SELECTOR, 85 ".devtools-toolbarbutton:not([label])", 86 ], 87 [ 88 { line: 35, ch: 23 }, 89 CSSCompleter.CSS_STATE_SELECTOR, 90 ".devtools-toolbarbutton:not([label]) > .toolbarbutton-text", 91 ], 92 [{ line: 35, ch: 70 }, CSSCompleter.CSS_STATE_SELECTOR, ".devtools-toolbar"], 93 [ 94 { line: 27, ch: 14 }, 95 CSSCompleter.CSS_STATE_VALUE, 96 [".devtools-menulist:active", "#devtools-toolbarbutton:focus "], 97 "outline", 98 "1px dotted hsla(210,30%,85%,0.7)", 99 ], 100 [ 101 { line: 16, ch: 16 }, 102 CSSCompleter.CSS_STATE_VALUE, 103 [ 104 "#devtools-menu.devtools-menulist", 105 ".devtools-toolbarbutton#devtools-menu ", 106 ], 107 "background", 108 "linear-gradient(hsla(212,7%,57%,.35),\n hsla(212,7%,57%,.1)) padding-box", 109 ], 110 [ 111 { line: 16, ch: 3 }, 112 CSSCompleter.CSS_STATE_VALUE, 113 [ 114 "#devtools-menu.devtools-menulist", 115 ".devtools-toolbarbutton#devtools-menu ", 116 ], 117 "background", 118 "linear-gradient(hsla(212,7%,57%,.35),\n hsla(212,7%,57%,.1)) padding-box", 119 ], 120 [ 121 { line: 15, ch: 25 }, 122 CSSCompleter.CSS_STATE_VALUE, 123 [ 124 "#devtools-menu.devtools-menulist", 125 ".devtools-toolbarbutton#devtools-menu ", 126 ], 127 "background", 128 "linear-gradient(hsla(212,7%,57%,.35),\n hsla(212,7%,57%,.1)) padding-box", 129 ], 130 [ 131 { line: 38, ch: 5 }, 132 CSSCompleter.CSS_STATE_SELECTOR, 133 ".multiline > \n.complex + \n#selector", 134 ], 135 [ 136 { line: 39, ch: 5 }, 137 CSSCompleter.CSS_STATE_SELECTOR, 138 ".multiline > \n.complex + \n#selector", 139 ], 140 [ 141 { line: 40, ch: 5 }, 142 CSSCompleter.CSS_STATE_SELECTOR, 143 ".multiline > \n.complex + \n#selector", 144 ], 145 ]; 146 /* eslint-enable max-len */ 147 148 const TEST_URI = 149 "data:text/html;charset=UTF-8," + 150 encodeURIComponent( 151 [ 152 "<!DOCTYPE html>", 153 "<html>", 154 " <head>", 155 " <title>CSS contextual information tests.</title>", 156 " <style type='text/css'>", 157 "#progress {", 158 " width: 500px; height: 30px;", 159 " border: 1px solid black;", 160 " position: relative", 161 "}", 162 "#progress div {", 163 " width: 0%; height: 100%;", 164 " background: green;", 165 " position: absolute;", 166 " z-index: -1; top: 0", 167 "}", 168 "#progress.failed div {", 169 " background: red !important;", 170 "}", 171 "#progress.failed:after {", 172 " content: 'Some tests failed';", 173 " color: white", 174 "}", 175 "#progress:before {", 176 " content: 'Running test ' attr(data-progress) ' of " + 177 tests.length + 178 "';", 179 " color: white;", 180 " text-shadow: 0 0 2px darkgreen;", 181 "}", 182 " </style>", 183 " </head>", 184 " <body>", 185 " <h2>State machine tests for CSS autocompleter.</h2><br>", 186 " <div id='progress' data-progress='0'>", 187 " <div></div>", 188 " </div>", 189 " </body>", 190 " </html>", 191 ].join("\n") 192 ); 193 194 add_task(async function test() { 195 const tab = await addTab(TEST_URI); 196 const browser = tab.linkedBrowser; 197 198 const completer = new CSSCompleter({ 199 cssProperties: getClientCssProperties(), 200 }); 201 const matches = (arr, toCheck) => !arr.some((x, i) => x != toCheck[i]); 202 const checkState = (expected, actual) => { 203 if (expected[0] == CSSCompleter.CSS_STATE_NULL && actual == null) { 204 return true; 205 } else if ( 206 expected[0] == actual.state && 207 expected[0] == CSSCompleter.CSS_STATE_SELECTOR && 208 expected[1] == actual.selector 209 ) { 210 return true; 211 } else if ( 212 expected[0] == actual.state && 213 expected[0] == CSSCompleter.CSS_STATE_PROPERTY && 214 matches(expected[1], actual.selectors) && 215 expected[2] == actual.propertyName 216 ) { 217 return true; 218 } else if ( 219 expected[0] == actual.state && 220 expected[0] == CSSCompleter.CSS_STATE_VALUE && 221 matches(expected[1], actual.selectors) && 222 expected[2] == actual.propertyName && 223 expected[3] == actual.value 224 ) { 225 return true; 226 } 227 return false; 228 }; 229 230 let i = 0; 231 for (const expected of tests) { 232 ++i; 233 const caret = expected.splice(0, 1)[0]; 234 await SpecialPowers.spawn( 235 browser, 236 [[i, tests.length]], 237 function ([idx, len]) { 238 const progress = content.document.getElementById("progress"); 239 const progressDiv = content.document.querySelector("#progress > div"); 240 progress.dataset.progress = idx; 241 progressDiv.style.width = (100 * idx) / len + "%"; 242 } 243 ); 244 const actual = completer.getInfoAt(source, caret); 245 if (checkState(expected, actual)) { 246 ok(true, "Test " + i + " passed. "); 247 } else { 248 ok( 249 false, 250 "Test " + 251 i + 252 " failed. Expected state : " + 253 JSON.stringify(expected) + 254 " " + 255 "but found [" + 256 JSON.stringify([ 257 actual.state, 258 actual.selector || actual.selectors, 259 actual.propertyName, 260 actual.value, 261 ]) + 262 "." 263 ); 264 await SpecialPowers.spawn(browser, [], function () { 265 const progress = content.document.getElementById("progress"); 266 progress.classList.add("failed"); 267 }); 268 } 269 } 270 gBrowser.removeCurrentTab(); 271 });