browser_webconsole_stubs_console_api.js (9068B)
1 /* Any copyright is dedicated to the Public Domain. 2 * http://creativecommons.org/publicdomain/zero/1.0/ */ 3 4 "use strict"; 5 6 const { 7 STUBS_UPDATE_ENV, 8 createCommandsForTab, 9 getStubFile, 10 getCleanedPacket, 11 getSerializedPacket, 12 writeStubsToFile, 13 } = require(`${CHROME_URL_ROOT}stub-generator-helpers`); 14 15 const TEST_URI = 16 "https://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html"; 17 const STUB_FILE = "consoleApi.js"; 18 19 add_task(async function () { 20 const isStubsUpdate = Services.env.get(STUBS_UPDATE_ENV) == "true"; 21 info(`${isStubsUpdate ? "Update" : "Check"} ${STUB_FILE}`); 22 23 const generatedStubs = await generateConsoleApiStubs(); 24 25 if (isStubsUpdate) { 26 await writeStubsToFile(STUB_FILE, generatedStubs); 27 ok(true, `${STUB_FILE} was updated`); 28 return; 29 } 30 const existingStubs = getStubFile(STUB_FILE); 31 const FAILURE_MSG = 32 "The consoleApi stubs file needs to be updated by running `" + 33 `mach test ${getCurrentTestFilePath()} --headless --setenv WEBCONSOLE_STUBS_UPDATE=true` + 34 "`"; 35 36 if (generatedStubs.size !== existingStubs.rawPackets.size) { 37 ok(false, FAILURE_MSG); 38 return; 39 } 40 41 let failed = false; 42 for (const [key, packet] of generatedStubs) { 43 const packetStr = getSerializedPacket(packet, { 44 sortKeys: true, 45 replaceActorIds: true, 46 }); 47 const existingPacketStr = getSerializedPacket( 48 existingStubs.rawPackets.get(key), 49 { sortKeys: true, replaceActorIds: true } 50 ); 51 52 is(packetStr, existingPacketStr, `"${key}" packet has expected value`); 53 failed = failed || packetStr !== existingPacketStr; 54 } 55 56 if (failed) { 57 ok(false, FAILURE_MSG); 58 } else { 59 ok(true, "Stubs are up to date"); 60 } 61 }); 62 63 async function generateConsoleApiStubs() { 64 const stubs = new Map(); 65 66 const tab = await addTab(TEST_URI); 67 const commands = await createCommandsForTab(tab); 68 await commands.targetCommand.startListening(); 69 const resourceCommand = commands.resourceCommand; 70 71 // Ensure waiting for sources in order to populate message.sourceId correctly. 72 await resourceCommand.watchResources([resourceCommand.TYPES.SOURCE], { 73 onAvailable() {}, 74 }); 75 76 // The resource-watcher only supports a single call to watch/unwatch per 77 // instance, so we attach a unique watch callback, which will forward the 78 // resource to `handleConsoleMessage`, dynamically updated for each command. 79 let handleConsoleMessage = function () {}; 80 81 const onConsoleMessage = resources => { 82 for (const resource of resources) { 83 handleConsoleMessage(resource); 84 } 85 }; 86 await resourceCommand.watchResources( 87 [resourceCommand.TYPES.CONSOLE_MESSAGE], 88 { 89 onAvailable: onConsoleMessage, 90 } 91 ); 92 93 for (const { keys, code } of getCommands()) { 94 const received = new Promise(resolve => { 95 let i = 0; 96 handleConsoleMessage = async res => { 97 const callKey = keys[i]; 98 99 stubs.set(callKey, getCleanedPacket(callKey, res)); 100 101 if (++i === keys.length) { 102 resolve(); 103 } 104 }; 105 }); 106 107 await SpecialPowers.spawn( 108 gBrowser.selectedBrowser, 109 [code], 110 function (subCode) { 111 const script = content.document.createElement("script"); 112 script.append( 113 content.document.createTextNode( 114 `function triggerPacket() {${subCode}}` 115 ) 116 ); 117 content.document.body.append(script); 118 content.wrappedJSObject.triggerPacket(); 119 script.remove(); 120 } 121 ); 122 123 await received; 124 } 125 126 resourceCommand.unwatchResources([resourceCommand.TYPES.CONSOLE_MESSAGE], { 127 onAvailable: onConsoleMessage, 128 }); 129 130 await commands.destroy(); 131 132 return stubs; 133 } 134 135 function getCommands() { 136 const consoleApiCommands = [ 137 "console.log('foobar', 'test')", 138 "console.log(undefined)", 139 "console.warn('danger, will robinson!')", 140 "console.log(NaN)", 141 "console.log(null)", 142 "console.log('\u9f2c')", 143 "console.clear()", 144 "console.count('bar')", 145 "console.assert(false, {message: 'foobar'})", 146 "console.log('\xFA\u1E47\u0129\xE7\xF6d\xEA \u021B\u0115\u0219\u0165')", 147 "console.dirxml(window)", 148 "console.log('myarray', ['red', 'green', 'blue'])", 149 "console.log('myregex', /a.b.c/)", 150 "console.table(['red', 'green', 'blue']);", 151 "console.log('myobject', {red: 'redValue', green: 'greenValue', blue: 'blueValue'});", 152 "console.debug('debug message');", 153 "console.info('info message');", 154 "console.error('error message');", 155 "console.log(Symbol.for('foo'))", 156 "console.log(Symbol.for('bar'))", 157 ]; 158 159 const consoleApi = consoleApiCommands.map(cmd => ({ 160 keys: [cmd], 161 code: cmd, 162 })); 163 164 consoleApi.push( 165 { 166 keys: ["console.log('mymap')"], 167 code: ` 168 var map = new Map(); 169 map.set("key1", "value1"); 170 map.set("key2", "value2"); 171 console.log('mymap', map); 172 `, 173 }, 174 { 175 keys: ["console.log('myset')"], 176 code: ` 177 console.log('myset', new Set(["a", "b"])); 178 `, 179 }, 180 { 181 keys: ["console.trace()"], 182 code: ` 183 function testStacktraceFiltering() { 184 console.trace() 185 } 186 function foo() { 187 testStacktraceFiltering() 188 } 189 190 foo() 191 `, 192 }, 193 { 194 keys: ["console.trace('bar', {'foo': 'bar'}, [1,2,3])"], 195 code: ` 196 function testStacktraceWithLog() { 197 console.trace('bar', {'foo': 'bar'}, [1,2,3]) 198 } 199 function foo() { 200 testStacktraceWithLog() 201 } 202 203 foo() 204 `, 205 }, 206 { 207 keys: ['console.trace("%cHello%c|%cWorld")'], 208 code: ` 209 console.trace( 210 "%cHello%c|%cWorld", 211 "color:red", 212 "", 213 "color: blue" 214 ); 215 `, 216 }, 217 { 218 keys: [ 219 "console.time('bar')", 220 "timerAlreadyExists", 221 "console.timeLog('bar') - 1", 222 "console.timeLog('bar') - 2", 223 "console.timeEnd('bar')", 224 "timeEnd.timerDoesntExist", 225 "timeLog.timerDoesntExist", 226 ], 227 code: ` 228 console.time("bar"); 229 console.time("bar"); 230 console.timeLog("bar"); 231 console.timeLog("bar", "second call", {state: 1}); 232 console.timeEnd("bar"); 233 console.timeEnd("bar"); 234 console.timeLog("bar"); 235 `, 236 }, 237 { 238 keys: ["console.table('bar')"], 239 code: ` 240 console.table('bar'); 241 `, 242 }, 243 { 244 keys: ["console.table(['a', 'b', 'c'])"], 245 code: ` 246 console.table(['a', 'b', 'c']); 247 `, 248 }, 249 { 250 keys: ["console.group('bar')", "console.groupEnd('bar')"], 251 code: ` 252 console.group("bar"); 253 console.groupEnd(); 254 `, 255 }, 256 { 257 keys: ["console.groupCollapsed('foo')", "console.groupEnd('foo')"], 258 code: ` 259 console.groupCollapsed("foo"); 260 console.groupEnd(); 261 `, 262 }, 263 { 264 keys: ["console.group()", "console.groupEnd()"], 265 code: ` 266 console.group(); 267 console.groupEnd(); 268 `, 269 }, 270 { 271 keys: ["console.log(%cfoobar)"], 272 code: ` 273 console.log( 274 "%cfoo%cbar", 275 "color:blue; font-size:1.3em; background:url('data:image/png,base64,iVBORw0KGgoAAAAN'), url('https://example.com/test'); position:absolute; top:10px; ", 276 "color:red; line-height: 1.5; background:\\165rl('https://example.com/test')" 277 ); 278 `, 279 }, 280 { 281 keys: ['console.log("%cHello%c|%cWorld")'], 282 code: ` 283 console.log( 284 "%cHello%c|%cWorld", 285 "color:red", 286 "", 287 "color: blue" 288 ); 289 `, 290 }, 291 { 292 keys: ["console.group(%cfoo%cbar)", "console.groupEnd(%cfoo%cbar)"], 293 code: ` 294 console.group( 295 "%cfoo%cbar", 296 "color:blue;font-size:1.3em;background:url('https://example.com/test');position:absolute;top:10px", 297 "color:red;background:\\165rl('https://example.com/test')"); 298 console.groupEnd(); 299 `, 300 }, 301 { 302 keys: [ 303 "console.groupCollapsed(%cfoo%cbaz)", 304 "console.groupEnd(%cfoo%cbaz)", 305 ], 306 code: ` 307 console.groupCollapsed( 308 "%cfoo%cbaz", 309 "color:blue;font-size:1.3em;background:url('https://example.com/test');position:absolute;top:10px", 310 "color:red;background:\\165rl('https://example.com/test')"); 311 console.groupEnd(); 312 `, 313 }, 314 { 315 keys: ["console.dir({C, M, Y, K})"], 316 code: "console.dir({cyan: 'C', magenta: 'M', yellow: 'Y', black: 'K'});", 317 }, 318 { 319 keys: [ 320 "console.count | default: 1", 321 "console.count | default: 2", 322 "console.count | test counter: 1", 323 "console.count | test counter: 2", 324 "console.count | default: 3", 325 "console.count | clear", 326 "console.count | default: 4", 327 "console.count | test counter: 3", 328 "console.countReset | test counter: 0", 329 "console.countReset | counterDoesntExist", 330 ], 331 code: ` 332 console.count(); 333 console.count(); 334 console.count("test counter"); 335 console.count("test counter"); 336 console.count(); 337 console.clear(); 338 console.count(); 339 console.count("test counter"); 340 console.countReset("test counter"); 341 console.countReset("test counter"); 342 `, 343 }, 344 { 345 keys: ["console.log escaped characters"], 346 code: "console.log('hello \\nfrom \\rthe \\\"string world!')", 347 } 348 ); 349 return consoleApi; 350 }