browser_rules_select-and-copy-styles.js (7514B)
1 /* Any copyright is dedicated to the Public Domain. 2 http://creativecommons.org/publicdomain/zero/1.0/ */ 3 4 "use strict"; 5 6 // Tests that properties can be selected and copied from the rule view 7 8 const osString = Services.appinfo.OS; 9 10 const TEST_URI = ` 11 <style type="text/css"> 12 html { 13 color: #000000; 14 } 15 span { 16 font-variant: small-caps; color: #000000; 17 } 18 .nomatches { 19 color: #ff0000; 20 } 21 22 html { 23 body { 24 container-type: inline-size; 25 @container (1px < width) { 26 #nested { 27 background: tomato; 28 color: gold; 29 } 30 } 31 } 32 } 33 </style> 34 <div id="first" style="margin: 10em; 35 font-size: 14pt; font-family: helvetica, sans-serif; color: #AAA"> 36 <h1>Some header text</h1> 37 <p id="salutation" style="font-size: 12pt">hi.</p> 38 <p id="body" style="font-size: 12pt">I am a test-case. This text exists 39 solely to provide some things to <span style="color: yellow"> 40 highlight</span> and <span style="font-weight: bold">count</span> 41 style list-items in the box at right. If you are reading this, 42 you should go do something else instead. Maybe read a book. Or better 43 yet, write some test-cases for another bit of code. 44 <span style="font-style: italic">some text</span></p> 45 <p id="closing">more text</p> 46 <p>even more text</p> 47 </div> 48 <section id=nested>Nested</section> 49 `; 50 51 add_task(async function () { 52 await addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI)); 53 const { inspector, view } = await openRuleView(); 54 await selectNode("div", inspector); 55 await checkCopySelection(view); 56 await checkSelectAll(view); 57 await checkCopyEditorValue(view); 58 59 await selectNode("#nested", inspector); 60 await checkCopyNestedRule(view); 61 }); 62 63 async function checkCopySelection(view) { 64 info("Testing selection copy"); 65 66 const contentDoc = view.styleDocument; 67 const win = view.styleWindow; 68 const prop = contentDoc.querySelector(".ruleview-property"); 69 const values = contentDoc.querySelectorAll( 70 ".ruleview-propertyvaluecontainer" 71 ); 72 73 let range = contentDoc.createRange(); 74 range.setStart(prop, 0); 75 range.setEnd(values[4], 2); 76 win.getSelection().addRange(range); 77 info("Checking that _Copy() returns the correct clipboard value"); 78 79 const expectedPattern = 80 " margin: 10em;[\\r\\n]+" + 81 " font-size: 14pt;[\\r\\n]+" + 82 " font-family: helvetica, sans-serif;[\\r\\n]+" + 83 " color: #AAA;[\\r\\n]+" + 84 "}[\\r\\n]+" + 85 "html {[\\r\\n]+" + 86 " color: #000000;[\\r\\n]*"; 87 88 const allMenuItems = openStyleContextMenuAndGetAllItems(view, prop); 89 const menuitemCopy = allMenuItems.find( 90 item => 91 item.label === 92 STYLE_INSPECTOR_L10N.getStr("styleinspector.contextmenu.copy") 93 ); 94 95 ok(menuitemCopy.visible, "Copy menu item is displayed as expected"); 96 97 try { 98 await waitForClipboardPromise( 99 () => menuitemCopy.click(), 100 () => checkClipboardData(expectedPattern) 101 ); 102 } catch (e) { 103 failedClipboard(expectedPattern); 104 } 105 106 info("Check copying from keyboard"); 107 win.getSelection().removeRange(range); 108 // Selecting the declaration `margin: 10em;` 109 range = contentDoc.createRange(); 110 range.setStart(prop, 0); 111 range.setEnd(prop, 1); 112 win.getSelection().addRange(range); 113 114 // Dispatching the copy event from the checkbox to make sure we cover Bug 1680893. 115 const declarationCheckbox = contentDoc.querySelector( 116 "input[type=checkbox].ruleview-enableproperty" 117 ); 118 const copyEvent = new win.Event("copy", { bubbles: true }); 119 await waitForClipboardPromise( 120 () => declarationCheckbox.dispatchEvent(copyEvent), 121 () => checkClipboardData("^ margin: 10em;$") 122 ); 123 win.getSelection().removeRange(range); 124 } 125 126 async function checkSelectAll(view) { 127 info("Testing select-all copy"); 128 129 const contentDoc = view.styleDocument; 130 const prop = contentDoc.querySelector(".ruleview-property"); 131 132 info( 133 "Checking that _SelectAll() then copy returns the correct " + 134 "clipboard value" 135 ); 136 view.contextMenu._onSelectAll(); 137 const expectedPattern = 138 "element {[\\r\\n]+" + 139 " margin: 10em;[\\r\\n]+" + 140 " font-size: 14pt;[\\r\\n]+" + 141 " font-family: helvetica, sans-serif;[\\r\\n]+" + 142 " color: #AAA;[\\r\\n]+" + 143 "}[\\r\\n]+" + 144 "html {[\\r\\n]+" + 145 " color: #000000;[\\r\\n]+" + 146 "}[\\r\\n]*"; 147 148 const allMenuItems = openStyleContextMenuAndGetAllItems(view, prop); 149 const menuitemCopy = allMenuItems.find( 150 item => 151 item.label === 152 STYLE_INSPECTOR_L10N.getStr("styleinspector.contextmenu.copy") 153 ); 154 155 ok(menuitemCopy.visible, "Copy menu item is displayed as expected"); 156 157 try { 158 await waitForClipboardPromise( 159 () => menuitemCopy.click(), 160 () => checkClipboardData(expectedPattern) 161 ); 162 } catch (e) { 163 failedClipboard(expectedPattern); 164 } 165 } 166 167 async function checkCopyEditorValue(view) { 168 info("Testing CSS property editor value copy"); 169 170 const ruleEditor = getRuleViewRuleEditor(view, 0); 171 const propEditor = ruleEditor.rule.textProps[0].editor; 172 173 const editor = await focusEditableField(view, propEditor.valueSpan); 174 175 info( 176 "Checking that copying a css property value editor returns the correct" + 177 " clipboard value" 178 ); 179 180 const expectedPattern = "10em"; 181 182 const allMenuItems = openStyleContextMenuAndGetAllItems(view, editor.input); 183 const menuitemCopy = allMenuItems.find( 184 item => 185 item.label === 186 STYLE_INSPECTOR_L10N.getStr("styleinspector.contextmenu.copy") 187 ); 188 189 ok(menuitemCopy.visible, "Copy menu item is displayed as expected"); 190 191 try { 192 await waitForClipboardPromise( 193 () => menuitemCopy.click(), 194 () => checkClipboardData(expectedPattern) 195 ); 196 } catch (e) { 197 failedClipboard(expectedPattern); 198 } 199 } 200 201 async function checkCopyNestedRule(view) { 202 info("Select nested rule"); 203 const doc = view.styleDocument; 204 const range = doc.createRange(); 205 const nestedRule = doc.querySelector(".ruleview-rule:nth-of-type(2)"); 206 range.selectNode(nestedRule); 207 const win = view.styleWindow; 208 win.getSelection().addRange(range); 209 210 const copyEvent = new win.Event("copy", { bubbles: true }); 211 const expectedNested = `html { 212 & body { 213 @container (1px < width) { 214 & #nested { 215 background: tomato; 216 color: gold; 217 } 218 } 219 } 220 } 221 `; 222 223 await waitForClipboardPromise( 224 () => nestedRule.dispatchEvent(copyEvent), 225 expectedNested 226 ); 227 } 228 229 function checkClipboardData(expectedPattern) { 230 const actual = SpecialPowers.getClipboardData("text/plain"); 231 const expectedRegExp = new RegExp(expectedPattern, "g"); 232 return expectedRegExp.test(actual); 233 } 234 235 function failedClipboard(expectedPattern) { 236 // Format expected text for comparison 237 const terminator = osString == "WINNT" ? "\r\n" : "\n"; 238 expectedPattern = expectedPattern.replace(/\[\\r\\n\][+*]/g, terminator); 239 expectedPattern = expectedPattern.replace(/\\\(/g, "("); 240 expectedPattern = expectedPattern.replace(/\\\)/g, ")"); 241 242 let actual = SpecialPowers.getClipboardData("text/plain"); 243 244 // Trim the right hand side of our strings. This is because expectedPattern 245 // accounts for windows sometimes adding a newline to our copied data. 246 expectedPattern = expectedPattern.trimRight(); 247 actual = actual.trimRight(); 248 249 dump( 250 "TEST-UNEXPECTED-FAIL | Clipboard text does not match expected ... " + 251 "results (escaped for accurate comparison):\n" 252 ); 253 info("Actual: " + escape(actual)); 254 info("Expected: " + escape(expectedPattern)); 255 }