browser_rules_copy_styles.js (11661B)
1 /* Any copyright is dedicated to the Public Domain. 2 http://creativecommons.org/publicdomain/zero/1.0/ */ 3 4 "use strict"; 5 6 /** 7 * Tests the behaviour of the copy styles context menu items in the rule 8 * view. 9 */ 10 11 const osString = Services.appinfo.OS; 12 13 const TEST_URI = URL_ROOT_SSL + "doc_copystyles.html"; 14 15 add_task(async function () { 16 await addTab(TEST_URI); 17 const { inspector, view } = await openRuleView(); 18 await selectNode("#testid", inspector); 19 20 const ruleEditor = getRuleViewRuleEditor(view, 1); 21 22 const data = [ 23 { 24 desc: "Test Copy Property Name", 25 node: ruleEditor.rule.textProps[0].editor.nameSpan, 26 menuItemLabel: "styleinspector.contextmenu.copyPropertyName", 27 expectedPattern: "color", 28 visible: { 29 copyLocation: false, 30 copyDeclaration: true, 31 copyPropertyName: true, 32 copyPropertyValue: false, 33 copySelector: false, 34 copyRule: true, 35 }, 36 }, 37 { 38 desc: "Test Copy Property Value", 39 node: ruleEditor.rule.textProps[2].editor.valueSpan, 40 menuItemLabel: "styleinspector.contextmenu.copyPropertyValue", 41 expectedPattern: "12px", 42 visible: { 43 copyLocation: false, 44 copyDeclaration: true, 45 copyPropertyName: false, 46 copyPropertyValue: true, 47 copySelector: false, 48 copyRule: true, 49 }, 50 }, 51 { 52 desc: "Test Copy Property Value with Priority", 53 node: ruleEditor.rule.textProps[3].editor.valueSpan, 54 menuItemLabel: "styleinspector.contextmenu.copyPropertyValue", 55 expectedPattern: "#00F !important", 56 visible: { 57 copyLocation: false, 58 copyDeclaration: true, 59 copyPropertyName: false, 60 copyPropertyValue: true, 61 copySelector: false, 62 copyRule: true, 63 }, 64 }, 65 { 66 desc: "Test Copy Property Declaration", 67 node: ruleEditor.rule.textProps[2].editor.nameSpan, 68 menuItemLabel: "styleinspector.contextmenu.copyDeclaration", 69 expectedPattern: "font-size: 12px;", 70 visible: { 71 copyLocation: false, 72 copyDeclaration: true, 73 copyPropertyName: true, 74 copyPropertyValue: false, 75 copySelector: false, 76 copyRule: true, 77 }, 78 }, 79 { 80 desc: "Test Copy Property Declaration with Priority", 81 node: ruleEditor.rule.textProps[3].editor.nameSpan, 82 menuItemLabel: "styleinspector.contextmenu.copyDeclaration", 83 expectedPattern: "border-color: #00F !important;", 84 visible: { 85 copyLocation: false, 86 copyDeclaration: true, 87 copyPropertyName: true, 88 copyPropertyValue: false, 89 copySelector: false, 90 copyRule: true, 91 }, 92 }, 93 { 94 desc: "Test Copy Rule", 95 node: ruleEditor.rule.textProps[2].editor.nameSpan, 96 menuItemLabel: "styleinspector.contextmenu.copyRule", 97 expectedPattern: 98 "#testid {[\\r\\n]+" + 99 "\tcolor: #F00;[\\r\\n]+" + 100 "\tbackground-color: #00F;[\\r\\n]+" + 101 "\tfont-size: 12px;[\\r\\n]+" + 102 "\tborder-color: #00F !important;[\\r\\n]+" + 103 '\t--var: "\\*/";[\\r\\n]+' + 104 "}", 105 visible: { 106 copyLocation: false, 107 copyDeclaration: true, 108 copyPropertyName: true, 109 copyPropertyValue: false, 110 copySelector: false, 111 copyRule: true, 112 }, 113 }, 114 { 115 desc: "Test Copy Rule with hidden unused variables", 116 node: getRuleViewRuleEditor(view, 2).rule.textProps[0].editor.nameSpan, 117 menuItemLabel: "styleinspector.contextmenu.copyRule", 118 expectedPattern: 119 ":where\\(#testid\\) {[\\r\\n]+" + 120 "\tcolor: gold;[\\r\\n]+" + 121 Array.from({ length: 13 }, (_, i) => { 122 return `\t--unused-${i + 1}: ${i + 1};[\\r\\n]+`; 123 }).join("") + 124 "\tbackground-color: tomato;[\\r\\n]+" + 125 "}", 126 visible: { 127 copyLocation: false, 128 copyDeclaration: true, 129 copyPropertyName: true, 130 copyPropertyValue: false, 131 copySelector: false, 132 copyRule: true, 133 }, 134 }, 135 { 136 desc: "Test Copy Selector", 137 node: ruleEditor.selectorText, 138 menuItemLabel: "styleinspector.contextmenu.copySelector", 139 expectedPattern: "html, body, #testid", 140 visible: { 141 copyLocation: false, 142 copyDeclaration: false, 143 copyPropertyName: false, 144 copyPropertyValue: false, 145 copySelector: true, 146 copyRule: true, 147 }, 148 }, 149 { 150 desc: "Test Copy Location", 151 node: ruleEditor.source, 152 menuItemLabel: "styleinspector.contextmenu.copyLocation", 153 expectedPattern: 154 "https://example.com/browser/devtools/client/" + 155 "inspector/rules/test/doc_copystyles.css", 156 visible: { 157 copyLocation: true, 158 copyDeclaration: false, 159 copyPropertyName: false, 160 copyPropertyValue: false, 161 copySelector: false, 162 copyRule: true, 163 }, 164 }, 165 { 166 async setup() { 167 await disableProperty(view, 0); 168 }, 169 desc: "Test Copy Rule with Disabled Property", 170 node: ruleEditor.rule.textProps[2].editor.nameSpan, 171 menuItemLabel: "styleinspector.contextmenu.copyRule", 172 expectedPattern: 173 "#testid {[\\r\\n]+" + 174 "\t/\\* color: #F00; \\*/[\\r\\n]+" + 175 "\tbackground-color: #00F;[\\r\\n]+" + 176 "\tfont-size: 12px;[\\r\\n]+" + 177 "\tborder-color: #00F !important;[\\r\\n]+" + 178 '\t--var: "\\*/";[\\r\\n]+' + 179 "}", 180 visible: { 181 copyLocation: false, 182 copyDeclaration: true, 183 copyPropertyName: true, 184 copyPropertyValue: false, 185 copySelector: false, 186 copyRule: true, 187 }, 188 }, 189 { 190 async setup() { 191 await disableProperty(view, 4); 192 }, 193 desc: "Test Copy Rule with Disabled Property with Comment", 194 node: ruleEditor.rule.textProps[2].editor.nameSpan, 195 menuItemLabel: "styleinspector.contextmenu.copyRule", 196 expectedPattern: 197 "#testid {[\\r\\n]+" + 198 "\t/\\* color: #F00; \\*/[\\r\\n]+" + 199 "\tbackground-color: #00F;[\\r\\n]+" + 200 "\tfont-size: 12px;[\\r\\n]+" + 201 "\tborder-color: #00F !important;[\\r\\n]+" + 202 '\t/\\* --var: "\\*\\\\/"; \\*/[\\r\\n]+' + 203 "}", 204 visible: { 205 copyLocation: false, 206 copyDeclaration: true, 207 copyPropertyName: true, 208 copyPropertyValue: false, 209 copySelector: false, 210 copyRule: true, 211 }, 212 }, 213 { 214 desc: "Test Copy Property Declaration with Disabled Property", 215 node: ruleEditor.rule.textProps[0].editor.nameSpan, 216 menuItemLabel: "styleinspector.contextmenu.copyDeclaration", 217 expectedPattern: "/\\* color: #F00; \\*/", 218 visible: { 219 copyLocation: false, 220 copyDeclaration: true, 221 copyPropertyName: true, 222 copyPropertyValue: false, 223 copySelector: false, 224 copyRule: true, 225 }, 226 }, 227 { 228 desc: "Test Copy Rule not visible", 229 // Click in the rule view, but not on a rule, to check that the "Copy Rule" entry 230 // is not displayed in the menu 231 node: ruleEditor.element.parentElement, 232 visible: { 233 copyLocation: false, 234 copyDeclaration: false, 235 copyPropertyName: false, 236 copyPropertyValue: false, 237 copySelector: false, 238 copyRule: false, 239 }, 240 }, 241 ]; 242 243 for (const { 244 setup, 245 desc, 246 node, 247 menuItemLabel, 248 expectedPattern, 249 visible, 250 } of data) { 251 if (setup) { 252 await setup(); 253 } 254 255 info(desc); 256 await checkCopyStyle(view, node, menuItemLabel, expectedPattern, visible); 257 } 258 }); 259 260 async function checkCopyStyle( 261 view, 262 node, 263 menuItemLabel, 264 expectedPattern, 265 visible 266 ) { 267 const allMenuItems = openStyleContextMenuAndGetAllItems(view, node); 268 269 const menuitemCopy = allMenuItems.find( 270 item => 271 item.label === 272 STYLE_INSPECTOR_L10N.getStr("styleinspector.contextmenu.copy") 273 ); 274 const menuitemCopyLocation = allMenuItems.find( 275 item => 276 item.label === 277 STYLE_INSPECTOR_L10N.getStr("styleinspector.contextmenu.copyLocation") 278 ); 279 const menuitemCopyDeclaration = allMenuItems.find( 280 item => 281 item.label === 282 STYLE_INSPECTOR_L10N.getStr("styleinspector.contextmenu.copyDeclaration") 283 ); 284 const menuitemCopyPropertyName = allMenuItems.find( 285 item => 286 item.label === 287 STYLE_INSPECTOR_L10N.getStr("styleinspector.contextmenu.copyPropertyName") 288 ); 289 const menuitemCopyPropertyValue = allMenuItems.find( 290 item => 291 item.label === 292 STYLE_INSPECTOR_L10N.getStr( 293 "styleinspector.contextmenu.copyPropertyValue" 294 ) 295 ); 296 const menuitemCopySelector = allMenuItems.find( 297 item => 298 item.label === 299 STYLE_INSPECTOR_L10N.getStr("styleinspector.contextmenu.copySelector") 300 ); 301 const menuitemCopyRule = allMenuItems.find( 302 item => 303 item.label === 304 STYLE_INSPECTOR_L10N.getStr("styleinspector.contextmenu.copyRule") 305 ); 306 307 ok(menuitemCopy.disabled, "Copy disabled is as expected: true"); 308 ok(menuitemCopy.visible, "Copy visible is as expected: true"); 309 310 is( 311 menuitemCopyLocation.visible, 312 visible.copyLocation, 313 "Copy Location visible attribute is as expected: " + visible.copyLocation 314 ); 315 316 is( 317 menuitemCopyDeclaration.visible, 318 visible.copyDeclaration, 319 "Copy Property Declaration visible attribute is as expected: " + 320 visible.copyDeclaration 321 ); 322 323 is( 324 menuitemCopyPropertyName.visible, 325 visible.copyPropertyName, 326 "Copy Property Name visible attribute is as expected: " + 327 visible.copyPropertyName 328 ); 329 330 is( 331 menuitemCopyPropertyValue.visible, 332 visible.copyPropertyValue, 333 "Copy Property Value visible attribute is as expected: " + 334 visible.copyPropertyValue 335 ); 336 337 is( 338 menuitemCopySelector.visible, 339 visible.copySelector, 340 "Copy Selector visible attribute is as expected: " + visible.copySelector 341 ); 342 343 is( 344 menuitemCopyRule.visible, 345 visible.copyRule, 346 "Copy Rule visible attribute is as expected: " + visible.copyRule 347 ); 348 349 if (menuItemLabel) { 350 const menuItem = allMenuItems.find( 351 item => item.label === STYLE_INSPECTOR_L10N.getStr(menuItemLabel) 352 ); 353 try { 354 await waitForClipboardPromise( 355 () => menuItem.click(), 356 () => checkClipboardData(expectedPattern) 357 ); 358 } catch (e) { 359 failedClipboard(expectedPattern); 360 } 361 } 362 } 363 364 async function disableProperty(view, index) { 365 const ruleEditor = getRuleViewRuleEditor(view, 1); 366 const textProp = ruleEditor.rule.textProps[index]; 367 await togglePropStatus(view, textProp); 368 } 369 370 function checkClipboardData(expectedPattern) { 371 const actual = SpecialPowers.getClipboardData("text/plain"); 372 const expectedRegExp = new RegExp(expectedPattern, "g"); 373 return expectedRegExp.test(actual); 374 } 375 376 function failedClipboard(expectedPattern) { 377 // Format expected text for comparison 378 const terminator = osString == "WINNT" ? "\r\n" : "\n"; 379 expectedPattern = expectedPattern.replace(/\[\\r\\n\][+*]/g, terminator); 380 expectedPattern = expectedPattern.replace(/\\\(/g, "("); 381 expectedPattern = expectedPattern.replace(/\\\)/g, ")"); 382 383 let actual = SpecialPowers.getClipboardData("text/plain"); 384 385 // Trim the right hand side of our strings. This is because expectedPattern 386 // accounts for windows sometimes adding a newline to our copied data. 387 expectedPattern = expectedPattern.trimRight(); 388 actual = actual.trimRight(); 389 390 ok( 391 false, 392 "Clipboard text does not match expected " + 393 "results (escaped for accurate comparison):\n" 394 ); 395 info("Actual: " + escape(actual)); 396 info("Expected: " + escape(expectedPattern)); 397 }