mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-21 01:05:45 +00:00
Bug 1441079 - Allow to filter console by regular expression. r=nchevobbe
Any text enclosed between forward slashes is considered as a regex search. If the entered regex is invalid, a normal text search is performed. Differential Revision: https://phabricator.services.mozilla.com/D26310 --HG-- extra : moz-landing-system : lando
This commit is contained in:
parent
315a07dce8
commit
4c96eaa08d
@ -959,7 +959,15 @@ function passCssFilters(message, filters) {
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
function passSearchFilters(message, filters) {
|
||||
const text = (filters.text || "").trim();
|
||||
const text = (filters.text || "").trim().toLocaleLowerCase();
|
||||
let regex;
|
||||
if (text.startsWith("/") && text.endsWith("/") && text.length > 2) {
|
||||
try {
|
||||
regex = new RegExp(text.slice(1, -1), "im");
|
||||
} catch (e) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// If there is no search, the message passes the filter.
|
||||
if (!text) {
|
||||
@ -968,26 +976,26 @@ function passSearchFilters(message, filters) {
|
||||
|
||||
return (
|
||||
// Look for a match in parameters.
|
||||
isTextInParameters(text, message.parameters)
|
||||
isTextInParameters(text, regex, message.parameters)
|
||||
// Look for a match in location.
|
||||
|| isTextInFrame(text, message.frame)
|
||||
|| isTextInFrame(text, regex, message.frame)
|
||||
// Look for a match in net events.
|
||||
|| isTextInNetEvent(text, message.request)
|
||||
|| isTextInNetEvent(text, regex, message.request)
|
||||
// Look for a match in stack-trace.
|
||||
|| isTextInStackTrace(text, message.stacktrace)
|
||||
|| isTextInStackTrace(text, regex, message.stacktrace)
|
||||
// Look for a match in messageText.
|
||||
|| isTextInMessageText(text, message.messageText)
|
||||
|| isTextInMessageText(text, regex, message.messageText)
|
||||
// Look for a match in notes.
|
||||
|| isTextInNotes(text, message.notes)
|
||||
|| isTextInNotes(text, regex, message.notes)
|
||||
// Look for a match in prefix.
|
||||
|| isTextInPrefix(text, message.prefix)
|
||||
|| isTextInPrefix(text, regex, message.prefix)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if given text is included in provided stack frame.
|
||||
*/
|
||||
function isTextInFrame(text, frame) {
|
||||
function isTextInFrame(text, regex, frame) {
|
||||
if (!frame) {
|
||||
return false;
|
||||
}
|
||||
@ -1001,53 +1009,50 @@ function isTextInFrame(text, frame) {
|
||||
const { short } = getSourceNames(source);
|
||||
const unicodeShort = getUnicodeUrlPath(short);
|
||||
|
||||
const includes =
|
||||
`${functionName ? functionName + " " : ""}${unicodeShort}:${line}:${column}`
|
||||
.toLocaleLowerCase()
|
||||
.includes(text.toLocaleLowerCase());
|
||||
return includes;
|
||||
const str =
|
||||
`${functionName ? functionName + " " : ""}${unicodeShort}:${line}:${column}`;
|
||||
return regex ? regex.test(str) : str.toLocaleLowerCase().includes(text);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if given text is included in provided parameters.
|
||||
*/
|
||||
function isTextInParameters(text, parameters) {
|
||||
function isTextInParameters(text, regex, parameters) {
|
||||
if (!parameters) {
|
||||
return false;
|
||||
}
|
||||
|
||||
text = text.toLocaleLowerCase();
|
||||
return getAllProps(parameters).some(prop =>
|
||||
(prop + "").toLocaleLowerCase().includes(text)
|
||||
);
|
||||
return getAllProps(parameters).some(prop => {
|
||||
const str = (prop + "");
|
||||
return regex ? regex.test(str) : str.toLocaleLowerCase().includes(text);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if given text is included in provided net event grip.
|
||||
*/
|
||||
function isTextInNetEvent(text, request) {
|
||||
function isTextInNetEvent(text, regex, request) {
|
||||
if (!request) {
|
||||
return false;
|
||||
}
|
||||
|
||||
text = text.toLocaleLowerCase();
|
||||
|
||||
const method = request.method.toLocaleLowerCase();
|
||||
const url = request.url.toLocaleLowerCase();
|
||||
return method.includes(text) || url.includes(text);
|
||||
const method = request.method;
|
||||
const url = request.url;
|
||||
return regex ? regex.test(method) || regex.test(url) :
|
||||
method.toLocaleLowerCase().includes(text) || url.toLocaleLowerCase().includes(text);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if given text is included in provided stack trace.
|
||||
*/
|
||||
function isTextInStackTrace(text, stacktrace) {
|
||||
function isTextInStackTrace(text, regex, stacktrace) {
|
||||
if (!Array.isArray(stacktrace)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// isTextInFrame expect the properties of the frame object to be in the same
|
||||
// order they are rendered in the Frame component.
|
||||
return stacktrace.some(frame => isTextInFrame(text, {
|
||||
return stacktrace.some(frame => isTextInFrame(text, regex, {
|
||||
functionName: frame.functionName || l10n.getStr("stacktrace.anonymousFunction"),
|
||||
source: frame.filename,
|
||||
lineNumber: frame.lineNumber,
|
||||
@ -1058,17 +1063,19 @@ function isTextInStackTrace(text, stacktrace) {
|
||||
/**
|
||||
* Returns true if given text is included in `messageText` field.
|
||||
*/
|
||||
function isTextInMessageText(text, messageText) {
|
||||
function isTextInMessageText(text, regex, messageText) {
|
||||
if (!messageText) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (typeof messageText === "string") {
|
||||
return messageText.toLocaleLowerCase().includes(text.toLocaleLowerCase());
|
||||
return regex ? regex.test(messageText) :
|
||||
messageText.toLocaleLowerCase().includes(text);
|
||||
}
|
||||
|
||||
if (messageText.type === "longString") {
|
||||
return messageText.initial.toLocaleLowerCase().includes(text.toLocaleLowerCase());
|
||||
return regex ? regex.test(messageText.initial) :
|
||||
messageText.initial.toLocaleLowerCase().includes(text);
|
||||
}
|
||||
|
||||
return true;
|
||||
@ -1077,18 +1084,21 @@ function isTextInMessageText(text, messageText) {
|
||||
/**
|
||||
* Returns true if given text is included in notes.
|
||||
*/
|
||||
function isTextInNotes(text, notes) {
|
||||
function isTextInNotes(text, regex, notes) {
|
||||
if (!Array.isArray(notes)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return notes.some(note =>
|
||||
// Look for a match in location.
|
||||
isTextInFrame(text, note.frame) ||
|
||||
isTextInFrame(text, regex, note.frame) ||
|
||||
// Look for a match in messageBody.
|
||||
(
|
||||
note.messageBody &&
|
||||
note.messageBody.toLocaleLowerCase().includes(text.toLocaleLowerCase())
|
||||
(
|
||||
regex ? regex.test(note.messageBody) :
|
||||
note.messageBody.toLocaleLowerCase().includes(text)
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
@ -1096,12 +1106,14 @@ function isTextInNotes(text, notes) {
|
||||
/**
|
||||
* Returns true if given text is included in prefix.
|
||||
*/
|
||||
function isTextInPrefix(text, prefix) {
|
||||
function isTextInPrefix(text, regex, prefix) {
|
||||
if (!prefix) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return `${prefix}: `.toLocaleLowerCase().includes(text.toLocaleLowerCase());
|
||||
const str = `${prefix}: `;
|
||||
|
||||
return regex ? regex.test(str) : str.toLocaleLowerCase().includes(text);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -32,6 +32,7 @@ support-files =
|
||||
test-click-function-to-source.js
|
||||
test-closure-optimized-out.html
|
||||
test-console-filters.html
|
||||
test-console-filter-by-regex-input.html
|
||||
test-console-group.html
|
||||
test-console-iframes.html
|
||||
test-console-table.html
|
||||
@ -297,6 +298,7 @@ subsuite = clipboard
|
||||
[browser_webconsole_file_uri.js]
|
||||
skip-if = true # Bug 1404382
|
||||
[browser_webconsole_filter_by_input.js]
|
||||
[browser_webconsole_filter_by_regex_input.js]
|
||||
[browser_webconsole_filter_scroll.js]
|
||||
[browser_webconsole_filters.js]
|
||||
[browser_webconsole_filters_persist.js]
|
||||
|
@ -0,0 +1,84 @@
|
||||
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
||||
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
const MESSAGES = [
|
||||
"123-456-7890",
|
||||
"foo@bar.com",
|
||||
"http://abc.com/q?fizz=buzz&alpha=beta/",
|
||||
"https://xyz.com/?path=/world",
|
||||
"foooobaaaar",
|
||||
"123 working",
|
||||
];
|
||||
|
||||
const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
|
||||
"test/mochitest/test-console-filter-by-regex-input.html";
|
||||
|
||||
add_task(async function() {
|
||||
const hud = await openNewTabAndConsole(TEST_URI);
|
||||
const { outputNode } = hud.ui;
|
||||
|
||||
await waitFor(() => findMessage(hud, MESSAGES[5]), null, 200);
|
||||
|
||||
let filteredNodes;
|
||||
|
||||
info("Filter out messages that begin with numbers");
|
||||
await setFilterInput(hud, "/^[0-9]/", MESSAGES[5]);
|
||||
filteredNodes = outputNode.querySelectorAll(".message");
|
||||
checkFilteredMessages(filteredNodes, [
|
||||
MESSAGES[0],
|
||||
MESSAGES[5],
|
||||
], 2);
|
||||
|
||||
info("Filter out messages that are phone numbers");
|
||||
await setFilterInput(hud, "/\\d{3}\\-\\d{3}\\-\\d{4}/", MESSAGES[0]);
|
||||
filteredNodes = outputNode.querySelectorAll(".message");
|
||||
checkFilteredMessages(filteredNodes, [MESSAGES[0]], 1);
|
||||
|
||||
info("Filter out messages that are an email address");
|
||||
await setFilterInput(hud, "/^\\w+@[a-zA-Z]+\\.[a-zA-Z]{2,3}$/", MESSAGES[1]);
|
||||
filteredNodes = outputNode.querySelectorAll(".message");
|
||||
checkFilteredMessages(filteredNodes, [MESSAGES[1]], 1);
|
||||
|
||||
info("Filter out messages that contain query strings");
|
||||
await setFilterInput(hud, "/\\?([^=&]+=[^=&]+&?)*\\//", MESSAGES[2]);
|
||||
filteredNodes = outputNode.querySelectorAll(".message");
|
||||
checkFilteredMessages(filteredNodes, [MESSAGES[2]], 1);
|
||||
|
||||
// If regex is invalid, do a normal text search instead
|
||||
info("Filter messages using normal text search if regex is invalid");
|
||||
await setFilterInput(hud, "/?path=/", MESSAGES[3]);
|
||||
filteredNodes = outputNode.querySelectorAll(".message");
|
||||
checkFilteredMessages(filteredNodes, [MESSAGES[3]], 1);
|
||||
|
||||
info("Filter out messages not ending with numbers");
|
||||
await setFilterInput(hud, "/[^0-9]$/", MESSAGES[5]);
|
||||
filteredNodes = outputNode.querySelectorAll(".message");
|
||||
checkFilteredMessages(filteredNodes, [
|
||||
MESSAGES[1],
|
||||
MESSAGES[2],
|
||||
MESSAGES[3],
|
||||
MESSAGES[4],
|
||||
MESSAGES[5],
|
||||
], 5);
|
||||
});
|
||||
|
||||
async function setFilterInput(hud, value, lastMessage) {
|
||||
hud.ui.filterBox.focus();
|
||||
hud.ui.filterBox.select();
|
||||
EventUtils.sendString(value);
|
||||
await waitFor(() => findMessage(hud, lastMessage), null, 200);
|
||||
}
|
||||
|
||||
function checkFilteredMessages(filteredNodes, expectedMessages, expectedCount) {
|
||||
is(filteredNodes.length, expectedCount,
|
||||
`${expectedCount} messages should be displayed`);
|
||||
|
||||
filteredNodes.forEach((node, id) => {
|
||||
const messageBody = node.querySelector(".message-body").textContent;
|
||||
ok(messageBody, expectedMessages[id]);
|
||||
});
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Web Console test regex input.</title>
|
||||
</head>
|
||||
<body>
|
||||
<p>Web Console test for filtering messages by regex input.</p>
|
||||
<script>
|
||||
"use strict";
|
||||
|
||||
console.log("123-456-7890");
|
||||
console.log("foo@bar.com");
|
||||
console.log("http://abc.com/q?fizz=buzz&alpha=beta/");
|
||||
console.log("https://xyz.com/?path=/world");
|
||||
console.log("foooobaaaar");
|
||||
console.log("123 working");
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
Loading…
Reference in New Issue
Block a user