browser_webconsole_custom_formatters.js (5950B)
1 /* Any copyright is dedicated to the Public Domain. 2 * http://creativecommons.org/publicdomain/zero/1.0/ */ 3 4 "use strict"; 5 6 // Check display of custom formatters. 7 const TEST_URI = 8 "https://example.com/browser/devtools/client/webconsole/" + 9 "test/browser/test-console-custom-formatters.html"; 10 11 add_task(async function () { 12 await pushPref("devtools.custom-formatters.enabled", true); 13 14 const hud = await openNewTabAndConsole(TEST_URI); 15 16 // Reload the browser to ensure the custom formatters are picked up 17 await reloadBrowser(); 18 19 await testString(hud); 20 await testNumber(hud); 21 await testObjectWithoutFormatting(hud); 22 await testObjectWithFormattedHeader(hud); 23 await testObjectWithFormattedHeaderAndBody(hud); 24 await testCustomFormatterWithObjectTag(hud); 25 await testProxyObject(hud); 26 }); 27 28 async function testString(hud) { 29 info("Test for string not being custom formatted"); 30 await testCustomFormatting(hud, { 31 hasCustomFormatter: false, 32 messageText: "string", 33 }); 34 } 35 36 async function testNumber(hud) { 37 info("Test for number not being custom formatted"); 38 await testCustomFormatting(hud, { 39 hasCustomFormatter: false, 40 messageText: 1337, 41 }); 42 } 43 44 async function testObjectWithoutFormatting(hud) { 45 info("Test for object not being custom formatted"); 46 await testCustomFormatting(hud, { 47 hasCustomFormatter: false, 48 messageText: "Object { noFormat: true }", 49 }); 50 } 51 52 async function testObjectWithFormattedHeader(hud) { 53 info("Simple test for custom formatted header"); 54 await testCustomFormatting(hud, { 55 hasCustomFormatter: true, 56 messageText: "custom formatted header", 57 headerStyles: "font-size: 3rem;", 58 }); 59 } 60 61 async function testObjectWithFormattedHeaderAndBody(hud) { 62 info("Simple test for custom formatted header with body"); 63 await testCustomFormatting(hud, { 64 hasCustomFormatter: true, 65 messageText: "custom formatted body", 66 headerStyles: "font-style: italic;", 67 bodyText: "body", 68 bodyStyles: "font-size: 2rem; font-family: serif;", 69 }); 70 } 71 72 async function testCustomFormatterWithObjectTag(hud) { 73 info(`Test for custom formatted object with "object" tag in the jsonMl`); 74 const node = await waitFor(() => { 75 return findConsoleAPIMessage(hud, "object tag"); 76 }); 77 78 const headerJsonMlNode = node.querySelector(".objectBox-jsonml"); 79 is( 80 headerJsonMlNode.getAttribute("style"), 81 "color: purple;", 82 "The custom formatting of the header is correct" 83 ); 84 const [buttonEl, child1, child2, child3, child4] = 85 headerJsonMlNode.childNodes; 86 is(child1.textContent, "object tag", "Got expected first item"); 87 is( 88 child2.textContent, 89 `~[1,"a"]~`, 90 "Got expected second item, the replaced object, custom formatted" 91 ); 92 ok( 93 child3.classList.contains("objectBox-null"), 94 "Got expected third item, an actual NullRep" 95 ); 96 is(child3.textContent, `null`, "third item has expected content"); 97 98 is( 99 child4.textContent, 100 ` | serialized: 42n undefined null Infinity [object Object]`, 101 "Got expected fourth item, serialized values" 102 ); 103 104 buttonEl.click(); 105 const bodyLevel1 = await waitFor(() => 106 node.querySelector(".objectBox-jsonml-body-wrapper .objectBox-jsonml") 107 ); 108 const [bodyChild1, bodyChild2] = bodyLevel1.childNodes; 109 is(bodyChild1.textContent, "body"); 110 111 const bodyCustomFormattedChild = await waitFor(() => 112 bodyChild2.querySelector(".objectBox-jsonml") 113 ); 114 const [subButtonEl, subChild1, subChild2, subChild3] = 115 bodyCustomFormattedChild.childNodes; 116 ok(!!subButtonEl, "The body child can also be expanded"); 117 is(subChild1.textContent, "object tag", "Got expected first item"); 118 is( 119 subChild2.textContent, 120 `~[2,"b"]~`, 121 "Got expected body second item, the replaced object, custom formatted" 122 ); 123 ok( 124 subChild3.classList.contains("object-inspector"), 125 "Got expected body third item, an actual ObjectInspector" 126 ); 127 is( 128 subChild3.textContent, 129 `Array [ 2, "b" ]`, 130 "body third item has expected content" 131 ); 132 } 133 134 async function testProxyObject(hud) { 135 info("Test that Proxy objects can be handled by custom formatter"); 136 await testCustomFormatting(hud, { 137 hasCustomFormatter: true, 138 messageText: "Formatted Proxy", 139 headerStyles: "font-weight: bold;", 140 }); 141 } 142 143 async function testCustomFormatting( 144 hud, 145 { hasCustomFormatter, messageText, headerStyles, bodyText, bodyStyles } 146 ) { 147 const node = await waitFor(() => { 148 return findMessageVirtualizedByType({ 149 hud, 150 text: messageText, 151 typeSelector: ".console-api", 152 }); 153 }); 154 155 const headerJsonMlNode = node.querySelector(".objectBox-jsonml"); 156 if (hasCustomFormatter) { 157 ok(headerJsonMlNode, "The message is custom formatted"); 158 159 if (!headerJsonMlNode) { 160 return; 161 } 162 163 is( 164 headerJsonMlNode.getAttribute("style"), 165 headerStyles, 166 "The custom formatting of the header is correct" 167 ); 168 169 if (bodyText) { 170 const arrow = node.querySelector(".collapse-button"); 171 172 ok(arrow, "There must be a toggle arrow for the header"); 173 174 info("Expanding the Object"); 175 const onBodyRendered = waitFor( 176 () => 177 !!node.querySelector( 178 ".objectBox-jsonml-body-wrapper .objectBox-jsonml" 179 ) 180 ); 181 182 arrow.click(); 183 await onBodyRendered; 184 185 ok( 186 node.querySelector(".collapse-button").classList.contains("expanded"), 187 "The arrow of the node has the expected class after clicking on it" 188 ); 189 190 const bodyJsonMlNode = node.querySelector( 191 ".objectBox-jsonml-body-wrapper > .objectBox-jsonml" 192 ); 193 ok(bodyJsonMlNode, "The body is custom formatted"); 194 195 is(bodyJsonMlNode?.textContent, bodyText, "The body text is correct"); 196 is( 197 bodyJsonMlNode.getAttribute("style"), 198 bodyStyles, 199 "The custom formatting of the body is correct" 200 ); 201 } 202 } else { 203 ok(!headerJsonMlNode, "The message is not custom formatted"); 204 } 205 }