browser_dump_scope.js (7357B)
1 /* Any copyright is dedicated to the Public Domain. 2 * http://creativecommons.org/publicdomain/zero/1.0/ */ 3 4 "use strict"; 5 const { dumpScope } = ChromeUtils.importESModule( 6 "resource://devtools/shared/test-helpers/dump-scope.sys.mjs", 7 { global: "devtools" } 8 ); 9 10 function nestedSync1() { 11 /* eslint-disable no-unused-vars */ 12 const a = { prop: 1 }; 13 const b = [1, 2, 3]; 14 const c = function () {}; 15 /* eslint-enable no-unused-vars */ 16 17 return nestedSync2(); 18 } 19 20 function nestedSync2() { 21 /* eslint-disable no-unused-vars */ 22 const d = { prop: -1 }; 23 const e = ["4", "5", "6"]; 24 const f = new Uint8Array([7, 8, 9]); 25 /* eslint-enable no-unused-vars */ 26 27 return dumpScope({ saveAsFile: false }); 28 } 29 30 add_task(async function testSyncStack() { 31 const scopes = await nestedSync1(); 32 is(scopes.length, 3, "dumpScope returned 3 frames"); 33 34 // Test frame for nestedSync1 35 const testScope1 = scopes[1]; 36 assertScopeInformation(testScope1); 37 38 let scope = testScope1.blocks[0]; 39 is(typeof scope.a, "object", "Object type is correct"); 40 is(scope.a.prop, "1", "Object property value is correct"); 41 42 is(typeof scope.b, "object"); 43 ok(Array.isArray(scope.b)); 44 is(scope.b[0], "1", "Array of number value is correct"); 45 is(scope.b[1], "2", "Array of number value is correct"); 46 is(scope.b[2], "3", "Array of number value is correct"); 47 48 is(typeof scope.c, "string", "Function type is correct"); 49 is(scope.c, "Function c", "Function value is correct"); 50 51 // Test frame for nestedSync2 52 const testScope2 = scopes[0]; 53 assertScopeInformation(testScope2); 54 55 scope = testScope2.blocks[0]; 56 is(typeof scope.d, "object", "Object type is correct"); 57 is(scope.d.prop, "-1", "Object property value is correct"); 58 59 // Check regular array of strings 60 is(typeof scope.e, "object"); 61 ok(Array.isArray(scope.e), "Array of string type is correct"); 62 is(scope.e[0], "4", "Array of string value is correct"); 63 is(scope.e[1], "5", "Array of string value is correct"); 64 is(scope.e[2], "6", "Array of string value is correct"); 65 66 // Check typed array 67 is(typeof scope.e, "object"); 68 ok(Array.isArray(scope.f), "Typed array type is correct"); 69 is(scope.f[0], "7", "Typed array value is correct"); 70 is(scope.f[1], "8", "Typed array value is correct"); 71 is(scope.f[2], "9", "Typed array value is correct"); 72 }); 73 74 async function nestedAsync1() { 75 // eslint-disable-next-line no-unused-vars 76 const asyncA = 1; 77 const scopes = await nestedAsync2(); 78 return scopes; 79 } 80 81 async function nestedAsync2() { 82 // eslint-disable-next-line no-unused-vars 83 const asyncB = 2; 84 const scopes = await nestedAsync3(); 85 return scopes; 86 } 87 88 async function nestedAsync3() { 89 // eslint-disable-next-line no-unused-vars 90 const asyncC = 3; 91 return dumpScope({ saveAsFile: false }); 92 } 93 94 add_task(async function testAsyncStack() { 95 const scopes = await nestedAsync1(); 96 is(scopes.length, 4, "dumpScope returned 4 frames"); 97 98 const testScope1 = scopes[2]; 99 assertScopeInformation(testScope1); 100 is(testScope1.blocks[0].asyncA, "1", "Async frame value is correct"); 101 const testScope2 = scopes[1]; 102 assertScopeInformation(testScope2); 103 is(testScope2.blocks[0].asyncB, "2", "Async frame value is correct"); 104 const testScope3 = scopes[0]; 105 assertScopeInformation(testScope3); 106 is(testScope3.blocks[0].asyncC, "3", "Async frame value is correct"); 107 }); 108 109 function cyclicReference() { 110 const a = {}; 111 const b = { a }; 112 a.b = b; 113 114 return dumpScope({ saveAsFile: false }); 115 } 116 117 add_task(async function testCyclicReference() { 118 const scopes = await cyclicReference(); 119 is(scopes.length, 2, "dumpScope returned 2 frames"); 120 121 const testScope = scopes[0]; 122 assertScopeInformation(testScope); 123 is(typeof testScope.blocks[0].a, "object", "Cyclic object type is correct"); 124 125 // Note that the actual way cyclic references are handled could change in the 126 // future. If we decide to start handling them as references, this test should 127 // simply be updated! 128 is(typeof testScope.blocks[0].a.b, "object", "Cyclic object type is correct"); 129 is( 130 typeof testScope.blocks[0].a.b.a, 131 "object", 132 "Cyclic object type is correct" 133 ); 134 is( 135 typeof testScope.blocks[0].a.b.a.b, 136 "object", 137 "Cyclic object type is correct" 138 ); 139 is( 140 testScope.blocks[0].a.b.a.b.a, 141 "Object (max depth)", 142 "Cyclic object value is correct" 143 ); 144 }); 145 146 function maxDepth() { 147 const obj = {}; 148 149 let o = obj; 150 for (let i = 0; i < 100; i++) { 151 o = o[`a${i}`] = {}; 152 } 153 154 return dumpScope({ saveAsFile: false }); 155 } 156 157 add_task(async function testMaxDepth() { 158 const scopes = await maxDepth(); 159 is(scopes.length, 2, "dumpScope returned 2 frames"); 160 161 const testScope = scopes[0]; 162 assertScopeInformation(testScope); 163 is( 164 typeof testScope.blocks[0].obj, 165 "object", 166 "Max depth object type is correct" 167 ); 168 is( 169 typeof testScope.blocks[0].obj.a0, 170 "object", 171 "Max depth object type is correct" 172 ); 173 is( 174 typeof testScope.blocks[0].obj.a0.a1, 175 "object", 176 "Max depth object type is correct" 177 ); 178 is( 179 typeof testScope.blocks[0].obj.a0.a1.a2, 180 "object", 181 "Max depth object type is correct" 182 ); 183 is( 184 typeof testScope.blocks[0].obj.a0.a1.a2.a3, 185 "string", 186 "Max depth object type is correct" 187 ); 188 is( 189 testScope.blocks[0].obj.a0.a1.a2.a3, 190 "Object (max depth)", 191 "Max depth object value is correct" 192 ); 193 }); 194 195 function nestedInABlock() { 196 /* eslint-disable no-unused-vars */ 197 const outerBlock = "outerBlock"; 198 199 { 200 const innerBlock = "innerBlock"; 201 return dumpScope({ saveAsFile: false }); 202 } 203 /* eslint-enable no-unused-vars */ 204 } 205 206 add_task(async function testBlockNesting() { 207 const scopes = await nestedInABlock(); 208 is(scopes.length, 2, "dumpScope returned 2 frames"); 209 210 // Test frame for nestedInABlock 211 const testScope1 = scopes[0]; 212 assertScopeInformation(testScope1); 213 214 const block1 = testScope1.blocks[0]; 215 is(typeof block1.innerBlock, "string", "innerBlock type is correct"); 216 is(block1.innerBlock, "innerBlock", "innerBlock value is correct"); 217 const block2 = testScope1.blocks[1]; 218 is(typeof block2.outerBlock, "string", "outerBlock type is correct"); 219 is(block2.outerBlock, "outerBlock", "outerBlock value is correct"); 220 }); 221 222 function uninitialized() { 223 /* eslint-disable no-unused-vars */ 224 const scopes = dumpScope({ saveAsFile: false }); 225 const afterCallToDumpScope = "afterCallToDumpScope"; 226 return scopes; 227 /* eslint-enable no-unused-vars */ 228 } 229 230 add_task(async function testUninitialized() { 231 const scopes = await uninitialized(); 232 is(scopes.length, 2, "dumpScope returned 2 frames"); 233 234 // Test frame for uninitialized 235 const testScope1 = scopes[0]; 236 assertScopeInformation(testScope1); 237 238 const block1 = testScope1.blocks[0]; 239 is( 240 block1.afterCallToDumpScope, 241 "(uninitialized)", 242 "variable initialized after the call to dumpScope is marked as (uninitialized)" 243 ); 244 }); 245 246 /** 247 * Basic test helper to assert the shape of the frame object 248 */ 249 function assertScopeInformation(scopeInfo) { 250 is( 251 scopeInfo.details.frameScriptUrl, 252 "chrome://mochitests/content/browser/devtools/shared/test-helpers/browser_dump_scope.js" 253 ); 254 is(typeof scopeInfo.details.columnNumber, "number"); 255 is(typeof scopeInfo.details.lineNumber, "number"); 256 ok(Array.isArray(scopeInfo.blocks)); 257 for (const block of scopeInfo.blocks) { 258 is(typeof block, "object"); 259 } 260 }