forked from mirrors/gecko-dev
Bug 1777608 - [devtools] Change how we render cropped URLs in String rep. r=bomsy.
Instead of rendering the cropped URL, we split the URL in 3 parts, so the full URL text will be in the DOM, but we visually hide the middle part and replace it with an ellipsis. This way copying the message will still put the full URL in the clipboard. A test case is added to ensure this works as expected. Differential Revision: https://phabricator.services.mozilla.com/D165805
This commit is contained in:
parent
2c8367836d
commit
15e9036e65
6 changed files with 88 additions and 32 deletions
|
|
@ -59,6 +59,10 @@
|
|||
font-style: italic;
|
||||
}
|
||||
|
||||
.objectBox-string a {
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.objectBox-string a,
|
||||
.objectBox-string a:visited {
|
||||
color: currentColor;
|
||||
|
|
@ -71,6 +75,20 @@
|
|||
text-decoration: underline;
|
||||
}
|
||||
|
||||
/* Visually hide the middle of "cropped" url */
|
||||
.objectBox-string a .cropped-url-middle {
|
||||
max-width: 0;
|
||||
max-height: 0;
|
||||
display: inline-block;
|
||||
overflow: hidden;
|
||||
vertical-align: bottom;
|
||||
}
|
||||
|
||||
.objectBox-string a .cropped-url-end::before {
|
||||
content: "…";
|
||||
}
|
||||
|
||||
|
||||
.objectBox-function,
|
||||
.objectBox-profile {
|
||||
color: var(--object-color);
|
||||
|
|
|
|||
|
|
@ -252,7 +252,7 @@ define(function(require, exports, module) {
|
|||
}
|
||||
|
||||
currentIndex = currentIndex + contentStart;
|
||||
let linkText = getCroppedString(
|
||||
const linkText = getCroppedString(
|
||||
useUrl,
|
||||
currentIndex,
|
||||
startCropIndex,
|
||||
|
|
@ -260,21 +260,35 @@ define(function(require, exports, module) {
|
|||
);
|
||||
|
||||
if (linkText) {
|
||||
if (urlCropLimit && useUrl.length > urlCropLimit) {
|
||||
const linkItems = [];
|
||||
const shouldCrop = urlCropLimit && useUrl.length > urlCropLimit;
|
||||
if (shouldCrop) {
|
||||
const urlCropHalf = Math.ceil((urlCropLimit - ELLIPSIS.length) / 2);
|
||||
linkText = getCroppedString(
|
||||
useUrl,
|
||||
0,
|
||||
urlCropHalf,
|
||||
useUrl.length - urlCropHalf
|
||||
// We cut the string into 3 elements and we'll visually hide the second one
|
||||
// in CSS. This way people can still copy the full link.
|
||||
linkItems.push(
|
||||
span(
|
||||
{ className: "cropped-url-start" },
|
||||
useUrl.substring(0, urlCropHalf)
|
||||
),
|
||||
span(
|
||||
{ className: "cropped-url-middle" },
|
||||
useUrl.substring(urlCropHalf, useUrl.length - urlCropHalf)
|
||||
),
|
||||
span(
|
||||
{ className: "cropped-url-end" },
|
||||
useUrl.substring(useUrl.length - urlCropHalf)
|
||||
)
|
||||
);
|
||||
} else {
|
||||
linkItems.push(linkText);
|
||||
}
|
||||
|
||||
items.push(
|
||||
a(
|
||||
{
|
||||
key: `${useUrl}-${currentIndex}`,
|
||||
className: "url",
|
||||
className: "url" + (shouldCrop ? " cropped-url" : ""),
|
||||
title: useUrl,
|
||||
draggable: false,
|
||||
// Because we don't want the link to be open in the current
|
||||
|
|
@ -291,7 +305,7 @@ define(function(require, exports, module) {
|
|||
}
|
||||
: null,
|
||||
},
|
||||
linkText
|
||||
linkItems
|
||||
)
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -440,10 +440,16 @@ describe("test String with URL", () => {
|
|||
urlCropLimit: 20,
|
||||
});
|
||||
|
||||
expect(element.text()).toEqual("http://xyz…klmnopqrst is the best");
|
||||
const link = element.find("a").at(0);
|
||||
expect(element.text()).toEqual(text);
|
||||
const link = element.find("a.cropped-url").at(0);
|
||||
expect(link.prop("href")).toBe(xyzUrl);
|
||||
expect(link.prop("title")).toBe(xyzUrl);
|
||||
const linkParts = link.find("span");
|
||||
expect(linkParts.at(0).hasClass("cropped-url-start")).toBe(true);
|
||||
expect(linkParts.at(0).text()).toEqual("http://xyz");
|
||||
expect(linkParts.at(1).hasClass("cropped-url-middle")).toBe(true);
|
||||
expect(linkParts.at(2).hasClass("cropped-url-end")).toBe(true);
|
||||
expect(linkParts.at(2).text()).toEqual("klmnopqrst");
|
||||
});
|
||||
|
||||
it("renders multiple cropped URL", () => {
|
||||
|
|
@ -457,17 +463,28 @@ describe("test String with URL", () => {
|
|||
urlCropLimit: 20,
|
||||
});
|
||||
|
||||
expect(element.text()).toEqual(
|
||||
"http://xyz…klmnopqrst is lit, not http://abc…klmnopqrst"
|
||||
);
|
||||
expect(element.text()).toEqual(`${xyzUrl} is lit, not ${abcUrl}`);
|
||||
|
||||
const links = element.find("a");
|
||||
const links = element.find("a.cropped-url");
|
||||
const xyzLink = links.at(0);
|
||||
expect(xyzLink.prop("href")).toBe(xyzUrl);
|
||||
expect(xyzLink.prop("title")).toBe(xyzUrl);
|
||||
const xyzLinkParts = xyzLink.find("span");
|
||||
expect(xyzLinkParts.at(0).hasClass("cropped-url-start")).toBe(true);
|
||||
expect(xyzLinkParts.at(0).text()).toEqual("http://xyz");
|
||||
expect(xyzLinkParts.at(1).hasClass("cropped-url-middle")).toBe(true);
|
||||
expect(xyzLinkParts.at(2).hasClass("cropped-url-end")).toBe(true);
|
||||
expect(xyzLinkParts.at(2).text()).toEqual("klmnopqrst");
|
||||
|
||||
const abc = links.at(1);
|
||||
expect(abc.prop("href")).toBe(abcUrl);
|
||||
expect(abc.prop("title")).toBe(abcUrl);
|
||||
const abcLinkParts = abc.find("span");
|
||||
expect(abcLinkParts.at(0).hasClass("cropped-url-start")).toBe(true);
|
||||
expect(abcLinkParts.at(0).text()).toEqual("http://abc");
|
||||
expect(abcLinkParts.at(1).hasClass("cropped-url-middle")).toBe(true);
|
||||
expect(abcLinkParts.at(2).hasClass("cropped-url-end")).toBe(true);
|
||||
expect(abcLinkParts.at(2).text()).toEqual("klmnopqrst");
|
||||
});
|
||||
|
||||
it("renders full URL if smaller than cropLimit", () => {
|
||||
|
|
@ -484,6 +501,7 @@ describe("test String with URL", () => {
|
|||
const link = element.find("a").at(0);
|
||||
expect(link.prop("href")).toBe(xyzUrl);
|
||||
expect(link.prop("title")).toBe(xyzUrl);
|
||||
expect(link.find(".cropped-url-start").length).toBe(0);
|
||||
});
|
||||
|
||||
it("renders cropped URL followed by cropped string with urlCropLimit", () => {
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ httpServer.registerPathHandler("/test.js", function(request, response) {
|
|||
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();
|
||||
};
|
||||
|
|
@ -122,7 +123,7 @@ async function testMessagesCopy(hud, timestamp) {
|
|||
);
|
||||
is(
|
||||
lines[2],
|
||||
` logStuff ${TEST_URI}test.js:9`,
|
||||
` logStuff ${TEST_URI}test.js:10`,
|
||||
"Stacktrace second line has the expected text"
|
||||
);
|
||||
|
||||
|
|
@ -151,7 +152,7 @@ async function testMessagesCopy(hud, timestamp) {
|
|||
);
|
||||
is(
|
||||
lines[2],
|
||||
` logStuff ${TEST_URI}test.js:9`,
|
||||
` logStuff ${TEST_URI}test.js:10`,
|
||||
"Error Stacktrace second line has the expected text"
|
||||
);
|
||||
|
||||
|
|
@ -174,7 +175,7 @@ async function testMessagesCopy(hud, timestamp) {
|
|||
}
|
||||
is(
|
||||
lines[1],
|
||||
` <anonymous> ${TEST_URI}test.js:11`,
|
||||
` <anonymous> ${TEST_URI}test.js:12`,
|
||||
"ReferenceError second line has expected text"
|
||||
);
|
||||
ok(
|
||||
|
|
@ -190,6 +191,15 @@ async function testMessagesCopy(hud, timestamp) {
|
|||
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) {
|
||||
|
|
|
|||
|
|
@ -33,6 +33,11 @@ add_task(async function() {
|
|||
return `${origin}${params}`;
|
||||
};
|
||||
|
||||
const getVisibleLinkText = linkEl => {
|
||||
const [firstPart, , lastPart] = linkEl.children;
|
||||
return `${firstPart.innerText}${ELLIPSIS}${lastPart.innerText}`;
|
||||
};
|
||||
|
||||
const EXPECTED_MESSAGE = `get more information on this error`;
|
||||
|
||||
const msg = await waitFor(() => findErrorMessage(hud, EXPECTED_MESSAGE));
|
||||
|
|
@ -42,7 +47,7 @@ add_task(async function() {
|
|||
is(comLink.getAttribute("href"), url1, "First link has expected url");
|
||||
is(comLink.getAttribute("title"), url1, "First link has expected tooltip");
|
||||
is(
|
||||
comLink.textContent,
|
||||
getVisibleLinkText(comLink),
|
||||
getCroppedUrl("https://example.com"),
|
||||
"First link has expected text"
|
||||
);
|
||||
|
|
@ -50,7 +55,7 @@ add_task(async function() {
|
|||
is(orgLink.getAttribute("href"), url2, "Second link has expected url");
|
||||
is(orgLink.getAttribute("title"), url2, "Second link has expected tooltip");
|
||||
is(
|
||||
orgLink.textContent,
|
||||
getVisibleLinkText(orgLink),
|
||||
getCroppedUrl("https://example.org"),
|
||||
"Second link has expected text"
|
||||
);
|
||||
|
|
|
|||
|
|
@ -405,22 +405,13 @@ describe("PageError component:", () => {
|
|||
const message = prepareMessage(packet, { getNextId: () => "1" });
|
||||
const wrapper = render(PageError({ message, serviceContainer }));
|
||||
|
||||
// Keep in sync with `urlCropLimit` in PageError.js.
|
||||
const cropLimit = 120;
|
||||
const partLength = cropLimit / 2;
|
||||
const getCroppedUrl = url =>
|
||||
`${url}${"a".repeat(partLength - url.length)}…${"a".repeat(partLength)}`;
|
||||
|
||||
const croppedEvil = getCroppedUrl(evilDomain);
|
||||
const croppedbad = getCroppedUrl(badDomain);
|
||||
|
||||
const text = wrapper.find(".message-body").text();
|
||||
expect(text).toBe(
|
||||
`Uncaught “${croppedEvil}“ is evil and “${croppedbad}“ is not good either`
|
||||
`Uncaught “${evilURL}“ is evil and “${badURL}“ is not good either`
|
||||
);
|
||||
|
||||
// There should be 2 links.
|
||||
const links = wrapper.find(".message-body a");
|
||||
// There should be 2 cropped links.
|
||||
const links = wrapper.find(".message-body a.cropped-url");
|
||||
expect(links.length).toBe(2);
|
||||
|
||||
expect(links.eq(0).attr("href")).toBe(evilURL);
|
||||
|
|
|
|||
Loading…
Reference in a new issue