diff --git a/devtools/client/webconsole/components/Output/message-types/ConsoleCommand.js b/devtools/client/webconsole/components/Output/message-types/ConsoleCommand.js index 5132f34b9ade..dbb482faba30 100644 --- a/devtools/client/webconsole/components/Output/message-types/ConsoleCommand.js +++ b/devtools/client/webconsole/components/Output/message-types/ConsoleCommand.js @@ -36,11 +36,13 @@ function ConsoleCommand(props) { maybeScrollToBottom, } = props; - const { indent, source, type, level, messageText, timeStamp } = message; + const { indent, source, type, level, timeStamp } = message; + const messageText = trimCode(message.messageText); // This uses a Custom Element to syntax highlight when possible. If it's not // (no CodeMirror editor), then it will just render text. const messageBody = createElement("syntax-highlighted", null, messageText); + return Message({ source, type, @@ -57,3 +59,21 @@ function ConsoleCommand(props) { } module.exports = ConsoleCommand; + +/** + * Trim user input to avoid blank lines before and after messages + */ +function trimCode(input) { + if (typeof input !== "string") { + return input; + } + + // Trim on both edges if we have a single line of content + if (input.trim().includes("\n") === false) { + return input.trim(); + } + + // For multiline input we want to keep the indentation of the first line + // with non-whitespace, so we can't .trim()/.trimStart(). + return input.replace(/^\s*\n/, "").trimEnd(); +} diff --git a/devtools/client/webconsole/test/mochitest/browser.ini b/devtools/client/webconsole/test/mochitest/browser.ini index fa50e8bf4a4d..e6e5ee140f33 100644 --- a/devtools/client/webconsole/test/mochitest/browser.ini +++ b/devtools/client/webconsole/test/mochitest/browser.ini @@ -413,6 +413,7 @@ tags = clipboard [browser_webconsole_output_copy_newlines.js] tags = clipboard [browser_webconsole_output_order.js] +[browser_webconsole_output_trimmed.js] [browser_webconsole_persist.js] [browser_webconsole_primitive_stacktrace.js] [browser_webconsole_promise_rejected_object.js] diff --git a/devtools/client/webconsole/test/mochitest/browser_webconsole_output_trimmed.js b/devtools/client/webconsole/test/mochitest/browser_webconsole_output_trimmed.js new file mode 100644 index 000000000000..7e7cd8ff6e0c --- /dev/null +++ b/devtools/client/webconsole/test/mochitest/browser_webconsole_output_trimmed.js @@ -0,0 +1,102 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Tests that we trim start and end whitespace in user input +// in the messages list + +"use strict"; + +const TEST_URI = `http://example.com/browser/devtools/client/webconsole/test/mochitest/test-console.html`; + +const TEST_ITEMS = [ + { + name: "Commands without whitespace are not affected by trimming", + command: "Math.PI==='3.14159'", + expected: "Math.PI==='3.14159'", + }, + { + name: "Trims whitespace before and after a command (single line case)", + command: "\t\t (window.o_O || {}) [' O_o '] ", + expected: "(window.o_O || {}) [' O_o ']", + }, + { + name: + "When trimming a whitespace before and after a command, " + + "it keeps indentation for each contentful line", + command: " \n \n 1,\n 2,\n 3\n \n ", + expected: " 1,\n 2,\n 3", + }, + { + name: + "When trimming a whitespace before and after a command, " + + "it keeps trailing whitespace for all lines except the last", + command: + "\n" + + " let numbers = [1,\n" + + " 2, \n" + + " 3];\n" + + " \n" + + " \n" + + " function addNumber() { \n" + + " numbers.push(numbers.length + 1);\n" + + " } \n" + + " ", + expected: + " let numbers = [1,\n" + + " 2, \n" + + " 3];\n" + + " \n" + + " \n" + + " function addNumber() { \n" + + " numbers.push(numbers.length + 1);\n" + + " }", + }, +]; + +add_task(async function() { + const hud = await openNewTabAndConsole(TEST_URI); + + // Check that expected output and actual trimmed output match + for (const { name, command, expected } of TEST_ITEMS) { + hud.ui.clearOutput(); + await hud.jsterm.execute(command); + + const result = await waitFor(() => getDisplayedInput(hud)); + + if (result === expected) { + ok(true, name); + } else { + ok(false, formatError(name, result, expected)); + } + } +}); + +/** + * Get the text content of the latest command logged in the console + * @param {WebConsole} hud: The webconsole + * @return {string|null} + */ +function getDisplayedInput(hud) { + const message = Array.from( + hud.ui.outputNode.querySelectorAll(".message.command") + ).pop(); + + if (message) { + return message.querySelector("syntax-highlighted").textContent; + } + + return null; +} + +/** + * Format a "Got vs Expected" error message on multiple lines, + * making whitespace more visible in console output. + */ +function formatError(name, result, expected) { + const quote = str => + typeof str === "string" + ? "> " + str.replace(/ /g, "\u{B7}").replace(/\n/g, "\n> ") + : str; + + return `${name}\nGot:\n${quote(result)}\nExpected:\n${quote(expected)}\n`; +}