Skip to content
This repository was archived by the owner on Sep 21, 2021. It is now read-only.

Commit bbc07a3

Browse files
committed
Use onViewSourceInDebugger prop when clicking on the Error rep stacktrace location.
Fixes #942.
1 parent 89457e6 commit bbc07a3

File tree

3 files changed

+116
-6
lines changed

3 files changed

+116
-6
lines changed

packages/devtools-reps/src/reps/error.js

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ const { MODE } = require("./constants");
1515

1616
const dom = require("react-dom-factories");
1717
const { span } = dom;
18+
const IGNORED_SOURCE_URLS = ["debugger eval code"];
1819

1920
/**
2021
* Renders Error objects.
@@ -54,7 +55,7 @@ function ErrorRep(props) {
5455
}
5556

5657
if (preview.stack && props.mode !== MODE.TINY) {
57-
content.push("\n", getStacktraceElements(preview));
58+
content.push("\n", getStacktraceElements(props, preview));
5859
}
5960

6061
return span({
@@ -79,12 +80,12 @@ function ErrorRep(props) {
7980
* asdf (<anonymous>:2:10)
8081
* (<anonymous>:11:1)
8182
*/
82-
function getStacktraceElements(preview) {
83+
function getStacktraceElements(props, preview) {
8384
const stack = [];
8485
preview.stack
8586
.split("\n")
86-
.forEach((line, index) => {
87-
if (!line) {
87+
.forEach((frame, index) => {
88+
if (!frame) {
8889
// Skip any blank lines
8990
return;
9091
}
@@ -95,7 +96,7 @@ function getStacktraceElements(preview) {
9596
// Given the input: "functionName@scriptLocation:2:100"
9697
// Result:
9798
// ["functionName@scriptLocation:2:100", "functionName", "scriptLocation:2:100"]
98-
const result = line.match(/^(.*)@(.*)$/);
99+
const result = frame.match(/^(.*)@(.*)$/);
99100
if (result && result.length === 3) {
100101
functionName = result[1];
101102

@@ -109,13 +110,40 @@ function getStacktraceElements(preview) {
109110
functionName = "<anonymous>";
110111
}
111112

113+
let onLocationClick;
114+
// Given the input: "scriptLocation:2:100"
115+
// Result:
116+
// ["scriptLocation:2:100", "scriptLocation", "2", "100"]
117+
const locationParts = location.match(/^(.*):(\d+):(\d+)$/);
118+
if (
119+
props.onViewSourceInDebugger &&
120+
location &&
121+
!IGNORED_SOURCE_URLS.includes(locationParts[1]) &&
122+
locationParts
123+
) {
124+
let [, url, line, column] = locationParts;
125+
onLocationClick = e => {
126+
// Don't trigger ObjectInspector expand/collapse.
127+
e.stopPropagation();
128+
props.onViewSourceInDebugger({
129+
url,
130+
line: Number(line),
131+
column: Number(column),
132+
});
133+
};
134+
}
135+
112136
stack.push(span({
113137
key: "fn" + index,
114138
className: "objectBox-stackTrace-fn"
115139
}, cleanFunctionName(functionName)),
116140
span({
117141
key: "location" + index,
118-
className: "objectBox-stackTrace-location"
142+
className: "objectBox-stackTrace-location",
143+
onClick: onLocationClick,
144+
title: onLocationClick
145+
? "View source in debugger"
146+
: undefined,
119147
}, ` (${location})`));
120148
});
121149

packages/devtools-reps/src/reps/reps.css

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,10 @@
100100
padding-inline-start: 17px;
101101
}
102102

103+
.objectBox-stackTrace-location {
104+
cursor: pointer;
105+
}
106+
103107
.objectBox-Location,
104108
.location {
105109
color: var(--location-color);

packages/devtools-reps/src/reps/tests/error.js

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
* License, v. 2.0. If a copy of the MPL was not distributed with this
33
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
44

5+
/* global jest */
56
const { shallow } = require("enzyme");
67
const {
78
REPS,
@@ -332,3 +333,80 @@ describe("Error - base-loader.js", () => {
332333
expect(renderedComponent).toMatchSnapshot();
333334
});
334335
});
336+
337+
describe("Error - stacktrace location click", () => {
338+
it("Calls onViewSourceInDebugger with the expected arguments", () => {
339+
const onViewSourceInDebugger = jest.fn();
340+
const object = stubs.get("base-loader Error");
341+
342+
const renderedComponent = shallow(ErrorRep.rep({
343+
object,
344+
onViewSourceInDebugger
345+
}));
346+
347+
const locations = renderedComponent.find(".objectBox-stackTrace-location");
348+
expect(locations.exists()).toBeTruthy();
349+
350+
expect(locations.first().prop("title")).toBe("View source in debugger");
351+
locations.first().simulate("click", {
352+
type: "click",
353+
stopPropagation: () => {},
354+
});
355+
356+
expect(onViewSourceInDebugger.mock.calls.length).toEqual(1);
357+
let mockCall = onViewSourceInDebugger.mock.calls[0][0];
358+
expect(mockCall.url).toEqual("resource://devtools/shared/client/debugger-client.js");
359+
expect(mockCall.line).toEqual(856);
360+
expect(mockCall.column).toEqual(9);
361+
362+
expect(locations.last().prop("title")).toBe("View source in debugger");
363+
locations.last().simulate("click", {
364+
type: "click",
365+
stopPropagation: () => {},
366+
});
367+
368+
expect(onViewSourceInDebugger.mock.calls.length).toEqual(2);
369+
mockCall = onViewSourceInDebugger.mock.calls[1][0];
370+
expect(mockCall.url).toEqual("resource://devtools/shared/ThreadSafeDevToolsUtils.js");
371+
expect(mockCall.line).toEqual(109);
372+
expect(mockCall.column).toEqual(14);
373+
});
374+
375+
it("Does not call onViewSourceInDebugger on excluded urls", () => {
376+
const onViewSourceInDebugger = jest.fn();
377+
const object = stubs.get("URIError");
378+
379+
const renderedComponent = shallow(ErrorRep.rep({
380+
object,
381+
onViewSourceInDebugger
382+
}));
383+
384+
const locations = renderedComponent.find(".objectBox-stackTrace-location");
385+
expect(locations.exists()).toBeTruthy();
386+
expect(locations.first().prop("title")).toBe(undefined);
387+
388+
locations.first().simulate("click", {
389+
type: "click",
390+
stopPropagation: () => {},
391+
});
392+
393+
expect(onViewSourceInDebugger.mock.calls.length).toEqual(0);
394+
});
395+
396+
it("Does not throw when onViewSourceInDebugger props is not provided", () => {
397+
const object = stubs.get("base-loader Error");
398+
399+
const renderedComponent = shallow(ErrorRep.rep({
400+
object,
401+
}));
402+
403+
const locations = renderedComponent.find(".objectBox-stackTrace-location");
404+
expect(locations.exists()).toBeTruthy();
405+
expect(locations.first().prop("title")).toBe(undefined);
406+
407+
locations.first().simulate("click", {
408+
type: "click",
409+
stopPropagation: () => {},
410+
});
411+
});
412+
});

0 commit comments

Comments
 (0)