browser_dbg-pretty-print-inline-scripts.js (8633B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */ 4 5 // Tests pretty-printing of HTML file and its inner scripts. 6 7 "use strict"; 8 9 const TEST_FILENAME = `doc-pretty-print-inline-scripts.html`; 10 const PRETTY_PRINTED_FILENAME = `${TEST_FILENAME}:formatted`; 11 12 const BREAKABLE_LINE_HINT_CHAR = `➤`; 13 14 requestLongerTimeout(2); 15 16 // Import helpers for the inspector 17 Services.scriptloader.loadSubScript( 18 "chrome://mochitests/content/browser/devtools/client/inspector/test/shared-head.js", 19 this 20 ); 21 22 add_task(async function () { 23 const dbg = await initDebugger(TEST_FILENAME); 24 25 await selectSource(dbg, TEST_FILENAME); 26 await togglePrettyPrint(dbg); 27 28 const prettyPrintedSource = findSourceContent(dbg, PRETTY_PRINTED_FILENAME); 29 30 ok(prettyPrintedSource, "Pretty-printed source exists"); 31 32 info("Check that the HTML file was pretty-printed as expected"); 33 const expectedPrettyHtml = getExpectedPrettyPrintedHtml(); 34 is( 35 prettyPrintedSource.value, 36 // ➤ is used to indicate breakable lines, we remove them to assert the actual 37 // content of the editor. 38 expectedPrettyHtml.replaceAll(BREAKABLE_LINE_HINT_CHAR, ""), 39 "HTML file is pretty printed as expected" 40 ); 41 42 info("Check breakable lines"); 43 const htmlLines = expectedPrettyHtml.split("\n"); 44 const expectedBreakableLines = []; 45 htmlLines.forEach((line, index) => { 46 if (line.startsWith(BREAKABLE_LINE_HINT_CHAR)) { 47 // Lines in the debugger are 1-based 48 expectedBreakableLines.push(index + 1); 49 } 50 }); 51 52 await assertBreakableLines( 53 dbg, 54 PRETTY_PRINTED_FILENAME, 55 htmlLines.length, 56 expectedBreakableLines 57 ); 58 59 info("Check that console messages are pointing to pretty-printed file"); 60 const { toolbox } = dbg; 61 await toolbox.selectTool("webconsole"); 62 63 const { hud } = await dbg.toolbox.getPanel("webconsole"); 64 info("Wait until messages are sourcemapped"); 65 await waitFor( 66 () => !!findMessageByType(hud, PRETTY_PRINTED_FILENAME, ".log") 67 ); 68 69 const firstMessage = await waitFor(() => 70 findMessageByType(hud, "User information", ".log") 71 ); 72 const firstMessageLink = firstMessage.querySelector(".frame-link-source"); 73 is( 74 firstMessageLink.innerText, 75 `${PRETTY_PRINTED_FILENAME}:18:9`, 76 "first console message has expected location" 77 ); 78 info( 79 "Click on the link of the first message to open the file in the debugger" 80 ); 81 firstMessageLink.click(); 82 await waitForSelectedSource(dbg, PRETTY_PRINTED_FILENAME); 83 ok(true, "pretty printed file was selected in debugger…"); 84 await waitForSelectedLocation(dbg, 18); 85 ok(true, "…at the expected location"); 86 87 info("Go back to the console to check an other message"); 88 await toolbox.selectTool("webconsole"); 89 90 const secondMessage = await waitFor(() => 91 findMessageByType(hud, "42 yay", ".log") 92 ); 93 const secondMessageLink = secondMessage.querySelector(".frame-link-source"); 94 is( 95 secondMessageLink.innerText, 96 `${PRETTY_PRINTED_FILENAME}:41:13`, 97 "second console message has expected location" 98 ); 99 info( 100 "Click on the link of the second message to open the file in the debugger" 101 ); 102 secondMessageLink.click(); 103 await waitForSelectedSource(dbg, PRETTY_PRINTED_FILENAME); 104 ok(true, "pretty printed file was selected in debugger…"); 105 await waitForSelectedLocation(dbg, 41); 106 ok(true, "…at the expected location"); 107 108 info("Check that event listener popup is pointing to pretty-printed file"); 109 const inspector = await toolbox.selectTool("inspector"); 110 111 const nodeFront = await getNodeFront("h1", inspector); 112 const markupContainer = inspector.markup.getContainer(nodeFront); 113 const evHolder = markupContainer.elt.querySelector( 114 ".inspector-badge.interactive[data-event]" 115 ); 116 const eventTooltip = inspector.markup.eventDetailsTooltip; 117 118 info("Open event tooltip."); 119 EventUtils.synthesizeMouseAtCenter( 120 evHolder, 121 {}, 122 inspector.markup.doc.defaultView 123 ); 124 await eventTooltip.once("shown"); 125 ok(true, "event tooltip opened"); 126 // wait for filename to be sourcemapped 127 await waitFor(() => 128 eventTooltip.panel 129 .querySelector(".event-header") 130 ?.innerText.includes(PRETTY_PRINTED_FILENAME) 131 ); 132 const header = eventTooltip.panel.querySelector(".event-header"); 133 const headerFilename = header.querySelector( 134 ".event-tooltip-filename" 135 ).innerText; 136 ok( 137 headerFilename.endsWith(`${PRETTY_PRINTED_FILENAME}:51`), 138 `Location in event tooltip is the pretty printed one (${headerFilename})` 139 ); 140 info( 141 "Check that clicking on open debugger icon selects the pretty printed file" 142 ); 143 header.querySelector(".event-tooltip-debugger-icon").click(); 144 await waitForSelectedSource(dbg, PRETTY_PRINTED_FILENAME); 145 ok(true, "pretty printed file was selected in debugger…"); 146 await waitForSelectedLocation(dbg, 51); 147 ok(true, "…at the expected location"); 148 }); 149 150 add_task(async function prettyPrintSingleLineDataUrl() { 151 const TEST_URL = `data:text/html,<meta charset=utf8><script>{"use strict"; globalThis.foo = function() {}}</script>`; 152 const PRETTY_PRINTED_URL = `${TEST_URL}:formatted`; 153 const dbg = await initDebuggerWithAbsoluteURL(TEST_URL); 154 155 await selectSource(dbg, TEST_URL); 156 await togglePrettyPrint(dbg); 157 158 const prettyPrintedSource = findSourceContent(dbg, PRETTY_PRINTED_URL); 159 160 ok(prettyPrintedSource, "Pretty-printed source exists"); 161 162 info("Check that the HTML file was pretty-printed as expected"); 163 const expectedPrettyHtml = `<meta charset=utf8><script> 164 { 165 'use strict'; 166 globalThis.foo = function () { 167 } 168 } 169 </script>`; 170 is( 171 prettyPrintedSource.value, 172 expectedPrettyHtml, 173 "HTML file is pretty printed as expected" 174 ); 175 }); 176 177 // Asserts that files which contain characters which are 178 // encoded by two code units (surrogate pairs) 179 add_task(async function prettyPrintHtmlWithSurrogatePairCharacters() { 180 const TEST_URL = `doc-pretty-print-with-emojis.html`; 181 const PRETTY_PRINTED_URL = `${TEST_URL}:formatted`; 182 const dbg = await initDebugger(TEST_URL); 183 184 await selectSource(dbg, TEST_URL); 185 await togglePrettyPrint(dbg); 186 const prettyPrintedSource = findSourceContent(dbg, PRETTY_PRINTED_URL); 187 ok(prettyPrintedSource, "Pretty-printed source exists"); 188 189 info("Check that the HTML file was pretty-printed as expected"); 190 const expectedPrettyHtml = 191 "<!DOCTYPE html><html><head><meta charset=\"utf-8\" /></head><body>\n🥁🤯<script>\nconsole.log('%', '🤯🤯🤯🤯🤯')\n</script>🥁</body>"; 192 is( 193 prettyPrintedSource.value, 194 expectedPrettyHtml, 195 "HTML file is pretty printed as expected" 196 ); 197 }); 198 199 /** 200 * Return the expected pretty-printed HTML. Lines starting with ➤ indicate breakable 201 * lines for easier maintenance. 202 * 203 * @returns {string} 204 */ 205 function getExpectedPrettyPrintedHtml() { 206 return `<!-- This Source Code Form is subject to the terms of the Mozilla Public 207 - License, v. 2.0. If a copy of the MPL was not distributed with this 208 - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> 209 <!doctype html> 210 211 <html> 212 <head> 213 <meta charset="utf-8"/> 214 <title>Debugger test page</title> 215 <!-- Added to check we don't handle non js scripts --> 216 <script id="json-data" type="application/json"> 217 { "foo": "bar" } 218 </script> 219 220 <!-- the unusual formatting is wanted to check inline scripts pretty printing --> 221 <script id="inline" type="application/javascript"> 222 ➤const userInfo = JSON.parse(document.getElementById('json-data').text); 223 ➤console.log('User information: %o', userInfo); 224 ➤document.addEventListener( 225 'click', 226 function onClick(e) { 227 ➤ console.log('in inline script'); 228 // this is 229 // something 230 ➤ e.target; 231 ➤ } 232 ); 233 </script> 234 </head> 235 236 <body> 237 <h1>Minified</h1> 238 <!-- Checking that having empty inline scripts doesn't throw off pretty printing --> 239 <script></script> 240 <!-- Single line "minified" script --> 241 <script id="minified" type="module"> 242 ➤for (const x of [ 243 42 244 ]) { 245 ➤ if (x > 0) { 246 ➤ console.log(x, 'yay') 247 } else { 248 ➤ console.log(x, 'booh') 249 } 250 ➤} 251 </script> 252 <!-- Multiple line "minified" script, with content on the first line --> 253 <script> 254 { 255 'use strict'; 256 ➤ document.querySelector('h1').addEventListener('mousedown', e => { 257 ➤ console.log('mousedown on h1') 258 ➤ }) 259 ➤} 260 </script> 261 <hr><script> 262 ➤const x = { 263 a: 1, 264 b: 2 265 ➤}; 266 </script><hr><!-- multiple scripts on same line --><script> 267 ➤const y = [ 268 1, 269 2, 270 3 271 ]; 272 ➤y.map(i => i * 2) 273 </script> 274 </body> 275 </html> 276 `; 277 }