test_smart-trace-source-maps.html (8738B)
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 <!DOCTYPE HTML> 5 <html> 6 <!-- 7 Test the rendering of a stack trace 8 --> 9 <head> 10 <meta charset="utf-8"> 11 <title>StackTrace component test</title> 12 <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> 13 <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"> 14 </head> 15 <body> 16 <script src="head.js"></script> 17 <script> 18 "use strict"; 19 20 window.onload = function() { 21 const ReactDOM = browserRequire("devtools/client/shared/vendor/react-dom"); 22 const React = browserRequire("devtools/client/shared/vendor/react"); 23 const SmartTrace = React.createFactory( 24 browserRequire("devtools/client/shared/components/SmartTrace")); 25 ok(SmartTrace, "Got the SmartTrace factory"); 26 27 add_task(async function testHappyPath() { 28 const stacktrace = [ 29 { 30 filename: "https://myfile.com/bundle.js", 31 lineNumber: 1, 32 columnNumber: 10, 33 }, 34 { 35 functionName: "loadFunc", 36 filename: "https://myfile.com/bundle.js", 37 lineNumber: 2, 38 }, 39 ]; 40 41 let onReadyCount = 0; 42 const props = { 43 stacktrace, 44 initialRenderDelay: 2000, 45 onViewSourceInDebugger: () => {}, 46 onReady: () => { 47 onReadyCount++; 48 }, 49 // A mock source map service. 50 sourceMapURLService: { 51 subscribeByLocation ({ line, column }, callback) { 52 const newLine = Number(line.toString().repeat(2)); 53 // Resolve immediately. 54 callback({ 55 url: "https://bugzilla.mozilla.org/original.js", 56 line: newLine, 57 column, 58 }); 59 return () => {}; 60 }, 61 }, 62 }; 63 64 const trace = ReactDOM.render(SmartTrace(props), 65 window.document.body.querySelector("#s1")); 66 await forceRender(trace); 67 68 const traceEl = ReactDOM.findDOMNode(trace); 69 ok(traceEl, "Rendered SmartTrace has an element"); 70 71 const frameEls = Array.from(traceEl.querySelectorAll(".frame")); 72 ok(frameEls, "Rendered SmartTrace has frames"); 73 is(frameEls.length, 2, "SmartTrace has 2 frames"); 74 75 checkSmartFrameString({ 76 el: frameEls[0], 77 functionName: "<anonymous>", 78 location: "original.js:11", 79 tooltip: "View source in Debugger → https://bugzilla.mozilla.org/original.js:11", 80 }); 81 82 checkSmartFrameString({ 83 el: frameEls[1], 84 functionName: "loadFunc", 85 location: "original.js:22", 86 tooltip: "View source in Debugger → https://bugzilla.mozilla.org/original.js:22", 87 }); 88 89 is(onReadyCount, 1, "onReady was called once"); 90 }); 91 92 add_task(async function testSlowSourcemapService() { 93 const stacktrace = [ 94 { 95 filename: "https://myfile.com/bundle.js", 96 functionName: "last", 97 lineNumber: 1, 98 columnNumber: 10, 99 }, 100 { 101 filename: "https://myfile.com/bundle.js", 102 functionName: "first", 103 lineNumber: 2, 104 columnNumber: 10, 105 }, 106 ]; 107 108 const sourcemapTimeout = 2000; 109 const initialRenderDelay = 300; 110 let onReadyCount = 0; 111 112 const props = { 113 stacktrace, 114 initialRenderDelay, 115 onViewSourceInDebugger: () => {}, 116 onReady: () => { 117 onReadyCount++; 118 }, 119 // A mock source map service. 120 sourceMapURLService: { 121 subscribeByLocation ({ line, column }, callback) { 122 // Resolve after a while. 123 setTimeout(() => { 124 const newLine = Number(line.toString().repeat(2)); 125 callback({ 126 url: "https://myfile.com/react.js", 127 line: newLine, 128 column, 129 }); 130 }, sourcemapTimeout) 131 132 return () => {}; 133 }, 134 }, 135 }; 136 137 const trace = ReactDOM.render(SmartTrace(props), 138 window.document.body.querySelector("#s2")); 139 140 let traceEl = ReactDOM.findDOMNode(trace); 141 ok(!traceEl, "Nothing was rendered at first"); 142 is(onReadyCount, 0, "onReady isn't called if SmartTrace isn't rendered"); 143 144 info("Wait for the initial delay to be over"); 145 await new Promise(res => setTimeout(res, initialRenderDelay)); 146 147 traceEl = ReactDOM.findDOMNode(trace); 148 ok(traceEl, "The trace was rendered"); 149 150 let frameEls = Array.from(traceEl.querySelectorAll(".frame")); 151 ok(frameEls, "Rendered SmartTrace has frames"); 152 is(frameEls.length, 2, "SmartTrace has 2 frames"); 153 154 info("Check that the original frames are displayed after the initial delay"); 155 checkSmartFrameString({ 156 el: frameEls[0], 157 functionName: "last", 158 location: "https://myfile.com/bundle.js:1", 159 tooltip: "View source in Debugger → https://myfile.com/bundle.js:1", 160 }); 161 162 checkSmartFrameString({ 163 el: frameEls[1], 164 functionName: "first", 165 location: "https://myfile.com/bundle.js:2", 166 tooltip: "View source in Debugger → https://myfile.com/bundle.js:2", 167 }); 168 169 is(onReadyCount, 1, "onReady was called once"); 170 171 info("Check the the sourcemapped version is rendered after the sourcemapTimeout"); 172 await waitFor(() => !!traceEl.querySelector(".frames-group")); 173 174 frameEls = Array.from(traceEl.querySelectorAll(".frame:not(.frames-group)")); 175 is(frameEls.length, 0, "SmartTrace has no frame"); 176 177 const groups = Array.from(traceEl.querySelectorAll(".frames-group")); 178 is(groups.length, 1, "SmartTrace has a group"); 179 is(groups[0].textContent.trim(), "React 2", "A collapsed React group is displayed"); 180 181 is(onReadyCount, 1, "onReady was only called once"); 182 }); 183 184 add_task(async function testFlakySourcemapService() { 185 const stacktrace = [ 186 { 187 filename: "https://myfile.com/bundle.js", 188 functionName: "last", 189 lineNumber: 1, 190 columnNumber: 10, 191 }, 192 { 193 filename: "https://myfile.com/bundle.js", 194 functionName: "pending", 195 lineNumber: 2, 196 columnNumber: 10, 197 }, 198 { 199 filename: "https://myfile.com/bundle.js", 200 functionName: "first", 201 lineNumber: 3, 202 columnNumber: 10, 203 }, 204 ]; 205 206 const initialRenderDelay = 300; 207 const onSourceMapResultDebounceDelay = 50; 208 let onReadyCount = 0; 209 210 const props = { 211 stacktrace, 212 initialRenderDelay, 213 onSourceMapResultDebounceDelay, 214 onViewSourceInDebugger: () => {}, 215 onReady: () => { 216 onReadyCount++; 217 }, 218 // A mock source map service. 219 sourceMapURLService: { 220 subscribeByLocation ({ line, column }, callback) { 221 // Don't call the callback for the second frame to simulate a flaky sourcemap 222 // service request. 223 if (line === 2) { 224 return () => {}; 225 } 226 227 const newLine = Number(line.toString().repeat(2)); 228 callback({ 229 url: `https://myfile.com/file-${line}.js`, 230 line: newLine, 231 column, 232 }); 233 return () => {}; 234 }, 235 }, 236 }; 237 238 const trace = ReactDOM.render(SmartTrace(props), 239 window.document.body.querySelector("#s3")); 240 241 let traceEl = ReactDOM.findDOMNode(trace); 242 ok(!traceEl, "Nothing was rendered at first"); 243 is(onReadyCount, 0, "onReady isn't called if SmartTrace isn't rendered"); 244 245 info("Wait for the initial delay + debounce to be over"); 246 await waitFor(() => { 247 const el = ReactDOM.findDOMNode(trace) 248 return el && el.textContent.includes("file-1.js"); 249 }); 250 251 traceEl = ReactDOM.findDOMNode(trace); 252 ok(traceEl, "The trace was rendered"); 253 254 const frameEls = Array.from(traceEl.querySelectorAll(".frame")); 255 ok(frameEls, "Rendered SmartTrace has frames"); 256 is(frameEls.length, 3, "SmartTrace has 3 frames"); 257 258 info("Check that the original frames are displayed even if there's no sourcemap " + 259 "response for some frames"); 260 checkSmartFrameString({ 261 el: frameEls[0], 262 functionName: "last", 263 location: "file-1.js:11", 264 tooltip: "View source in Debugger → https://myfile.com/file-1.js:11", 265 }); 266 267 checkSmartFrameString({ 268 el: frameEls[1], 269 functionName: "pending", 270 location: "bundle.js:2", 271 tooltip: "View source in Debugger → https://myfile.com/bundle.js:2", 272 }); 273 274 checkSmartFrameString({ 275 el: frameEls[2], 276 functionName: "first", 277 location: "file-3.js:33", 278 tooltip: "View source in Debugger → https://myfile.com/file-3.js:33", 279 }); 280 281 is(onReadyCount, 1, "onReady was only called once"); 282 }); 283 284 }; 285 </script> 286 <section id=s1></section> 287 <section id=s2></section> 288 <section id=s3></section> 289 </body> 290 </html>