summaryrefslogtreecommitdiffstats
path: root/devtools/client/webconsole/test/browser/browser_webconsole_context_menu_copy_entire_message.js
diff options
context:
space:
mode:
Diffstat (limited to 'devtools/client/webconsole/test/browser/browser_webconsole_context_menu_copy_entire_message.js')
-rw-r--r--devtools/client/webconsole/test/browser/browser_webconsole_context_menu_copy_entire_message.js244
1 files changed, 244 insertions, 0 deletions
diff --git a/devtools/client/webconsole/test/browser/browser_webconsole_context_menu_copy_entire_message.js b/devtools/client/webconsole/test/browser/browser_webconsole_context_menu_copy_entire_message.js
new file mode 100644
index 0000000000..b4f076ec67
--- /dev/null
+++ b/devtools/client/webconsole/test/browser/browser_webconsole_context_menu_copy_entire_message.js
@@ -0,0 +1,244 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const httpServer = createTestHTTPServer();
+httpServer.registerPathHandler(`/`, function (request, response) {
+ response.setStatusLine(request.httpVersion, 200, "OK");
+ response.write(`
+ <meta charset=utf8>
+ <h1>Test "copy message" context menu entry</h1>
+ <script type="text/javascript" src="test.js"></script>`);
+});
+
+httpServer.registerPathHandler("/test.js", function (request, response) {
+ response.setHeader("Content-Type", "application/javascript");
+ response.write(`
+ window.logStuff = function() {
+ console.log("simple text message");
+ function wrapper() {
+ console.log(new Error("error object"));
+ console.trace();
+ for (let i = 0; i < 2; i++) console.log("repeated")
+ console.log(document.location + "?" + "z".repeat(100))
+ }
+ wrapper();
+ };
+ z.bar = "baz";
+ `);
+});
+
+const TEST_URI = `http://localhost:${httpServer.identity.primaryPort}/`;
+
+// RegExp that validates copied text for log lines.
+const LOG_FORMAT_WITH_TIMESTAMP = /^[\d:.]+ .+/;
+const PREF_MESSAGE_TIMESTAMP = "devtools.webconsole.timestampMessages";
+
+// Test the Copy menu item of the webconsole copies the expected clipboard text for
+// different log messages.
+
+add_task(async function () {
+ await pushPref(PREF_MESSAGE_TIMESTAMP, true);
+
+ const hud = await openNewTabAndConsole(TEST_URI);
+
+ info("Call the log function defined in the test page");
+ await SpecialPowers.spawn(gBrowser.selectedBrowser, [], () => {
+ content.wrappedJSObject.logStuff();
+ });
+
+ info("Test copy menu item with timestamp");
+ await testMessagesCopy(hud, true);
+
+ // Disable timestamp and wait until timestamp are not displayed anymore.
+ await toggleConsoleSetting(
+ hud,
+ ".webconsole-console-settings-menu-item-timestamps"
+ );
+ await waitFor(
+ () => hud.ui.outputNode.querySelector(".message .timestamp") === null
+ );
+
+ info("Test copy menu item without timestamp");
+ await testMessagesCopy(hud, false);
+});
+
+async function testMessagesCopy(hud, timestamp) {
+ const newLineString = "\n";
+
+ info("Test copy menu item for the simple log");
+ let message = await waitFor(() =>
+ findConsoleAPIMessage(hud, "simple text message")
+ );
+ let clipboardText = await copyMessageContent(hud, message);
+ ok(true, "Clipboard text was found and saved");
+
+ info("Check copied text for simple log message");
+ let lines = clipboardText.split(newLineString);
+ is(lines.length, 2, "There are 2 lines in the copied text");
+ is(lines[1], "", "The last line is an empty new line");
+ is(
+ lines[0],
+ `${
+ timestamp ? getTimestampText(message) + " " : ""
+ }simple text message test.js:3:15`,
+ "Line of simple log message has expected text"
+ );
+ if (timestamp) {
+ ok(
+ LOG_FORMAT_WITH_TIMESTAMP.test(lines[0]),
+ "Log line has the right format:\n" + lines[0]
+ );
+ }
+
+ info("Test copy menu item for the console.trace message");
+ message = await waitFor(() => findConsoleAPIMessage(hud, "console.trace"));
+ // Wait for the stacktrace to be rendered.
+ await waitFor(() => message.querySelector(".frames"));
+ clipboardText = await copyMessageContent(hud, message);
+ ok(true, "Clipboard text was found and saved");
+
+ info("Check copied text for the console.trace message");
+ lines = clipboardText.split(newLineString);
+ is(lines.length, 4, "There are 4 lines in the copied text");
+ is(lines[lines.length - 1], "", "The last line is an empty new line");
+ is(
+ lines[0],
+ `${
+ timestamp ? getTimestampText(message) + " " : ""
+ }console.trace() test.js:6:17`,
+ "Stacktrace first line has the expected text"
+ );
+ if (timestamp) {
+ ok(
+ LOG_FORMAT_WITH_TIMESTAMP.test(lines[0]),
+ "Log line has the right format:\n" + lines[0]
+ );
+ }
+ is(
+ lines[1],
+ ` wrapper ${TEST_URI}test.js:6`,
+ "Stacktrace first line has the expected text"
+ );
+ is(
+ lines[2],
+ ` logStuff ${TEST_URI}test.js:10`,
+ "Stacktrace second line has the expected text"
+ );
+
+ info("Test copy menu item for the error message");
+ message = await waitFor(() => findConsoleAPIMessage(hud, "Error:"));
+ // Wait for the stacktrace to be rendered.
+ await waitFor(() => message.querySelector(".frames"));
+ clipboardText = await copyMessageContent(hud, message);
+ ok(true, "Clipboard text was found and saved");
+ lines = clipboardText.split(newLineString);
+ is(
+ lines[0],
+ `${timestamp ? getTimestampText(message) + " " : ""}Error: error object`,
+ "Error object first line has expected text"
+ );
+ if (timestamp) {
+ ok(
+ LOG_FORMAT_WITH_TIMESTAMP.test(lines[0]),
+ "Log line has the right format:\n" + lines[0]
+ );
+ }
+ is(
+ lines[1],
+ ` wrapper ${TEST_URI}test.js:5`,
+ "Error Stacktrace first line has the expected text"
+ );
+ is(
+ lines[2],
+ ` logStuff ${TEST_URI}test.js:10`,
+ "Error Stacktrace second line has the expected text"
+ );
+
+ info("Test copy menu item for the reference error message");
+ message = await waitFor(() => findErrorMessage(hud, "ReferenceError:"));
+ clipboardText = await copyMessageContent(hud, message);
+ ok(true, "Clipboard text was found and saved");
+ lines = clipboardText.split(newLineString);
+ is(
+ lines[0],
+ (timestamp ? getTimestampText(message) + " " : "") +
+ "Uncaught ReferenceError: z is not defined",
+ "ReferenceError first line has expected text"
+ );
+ if (timestamp) {
+ ok(
+ LOG_FORMAT_WITH_TIMESTAMP.test(lines[0]),
+ "Log line has the right format:\n" + lines[0]
+ );
+ }
+ is(
+ lines[1],
+ ` <anonymous> ${TEST_URI}test.js:12`,
+ "ReferenceError second line has expected text"
+ );
+ ok(
+ !!message.querySelector(".learn-more-link"),
+ "There is a Learn More link in the ReferenceError message"
+ );
+ is(
+ clipboardText.toLowerCase().includes("Learn More"),
+ false,
+ "The Learn More text wasn't put in the clipboard"
+ );
+
+ message = await waitFor(() => findConsoleAPIMessage(hud, "repeated 2"));
+ clipboardText = await copyMessageContent(hud, message);
+ ok(true, "Clipboard text was found and saved");
+
+ info("Test copy menu item for the message with the cropped URL");
+ message = await waitFor(() => findConsoleAPIMessage(hud, "z".repeat(100)));
+ ok(!!message.querySelector("a.cropped-url"), "URL is cropped");
+ clipboardText = await copyMessageContent(hud, message);
+ ok(
+ clipboardText.startsWith(TEST_URI) + "?" + "z".repeat(100),
+ "Full URL was copied to clipboard"
+ );
+}
+
+function getTimestampText(messageEl) {
+ return getSelectionTextFromElement(messageEl.querySelector(".timestamp"));
+}
+
+/**
+ * Simple helper method to open the context menu on a given message, and click on the copy
+ * menu item.
+ */
+async function copyMessageContent(hud, messageEl) {
+ const menuPopup = await openContextMenu(hud, messageEl);
+ const copyMenuItem = menuPopup.querySelector("#console-menu-copy");
+ ok(copyMenuItem, "copy menu item is enabled");
+
+ const text = await waitForClipboardPromise(
+ () => copyMenuItem.click(),
+ data => data
+ );
+
+ menuPopup.hidePopup();
+ return text;
+}
+
+/**
+ * Return the string representation, as if it was selected with the mouse and copied,
+ * using the Selection API.
+ *
+ * @param {HTMLElement} el
+ * @returns {String} the text representation of the element.
+ */
+function getSelectionTextFromElement(el) {
+ const doc = el.ownerDocument;
+ const win = doc.defaultView;
+ const range = doc.createRange();
+ range.selectNode(el);
+ const selection = win.getSelection();
+ selection.addRange(range);
+ const selectionText = selection.toString();
+ selection.removeRange(range);
+ return selectionText;
+}