Source code

Revision control

Copy as Markdown

Other Tools

Test Info: Warnings

/* Any copyright is dedicated to the Public Domain.
"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;
}