Skip to content

Commit 050bc26

Browse files
committed
Expose a totalMarkerDuration function in console
1 parent 6169e49 commit 050bc26

File tree

3 files changed

+165
-1
lines changed

3 files changed

+165
-1
lines changed

src/test/unit/__snapshots__/window-console.test.js.snap

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Jest Snapshot v1, https://goo.gl/fbAQLP
1+
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
22

33
exports[`console-accessible values on the window object logs a friendly message 1`] = `
44
Array [
@@ -28,6 +28,7 @@ Array [
2828
%cwindow.filteredMarkers%c - The current filtered and processed markers
2929
%cwindow.selectedMarker%c - The selected processed marker in the current thread
3030
%cwindow.callTree%c - The call tree of the current filtered thread
31+
%cwindow.totalMarkerDuration%c - Calculate total duration of a marker array (e.g., totalMarkerDuration(filteredMarkers))
3132
%cwindow.getState%c - The function that returns the current Redux state.
3233
%cwindow.selectors%c - All the selectors that are used to get data from the Redux state.
3334
%cwindow.dispatch%c - The function to dispatch a Redux action to change the state.
@@ -76,6 +77,8 @@ The CallTree class's source code is available here:
7677
"",
7778
"font-weight: bold;",
7879
"",
80+
"font-weight: bold;",
81+
"",
7982
"font-style: italic; text-decoration: underline;",
8083
"",
8184
"font-style: italic; text-decoration: underline;",

src/test/unit/window-console.test.js

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,4 +73,138 @@ describe('console-accessible values on the window object', function () {
7373
1970-01-01 00:00:00.190000000 UTC - [Unknown Process 0: Empty]: D/nsJarProtocol nsJARChannel::nsJARChannel [this=0x87f1ec80]
7474
`);
7575
});
76+
77+
describe('totalMarkerDuration', function () {
78+
let target;
79+
let consoleLogSpy;
80+
81+
beforeEach(function () {
82+
const store = storeWithSimpleProfile();
83+
target = {};
84+
addDataToWindowObject(store.getState, store.dispatch, target);
85+
consoleLogSpy = jest.spyOn(console, 'log').mockImplementation(() => {});
86+
});
87+
88+
afterEach(function () {
89+
consoleLogSpy.mockRestore();
90+
});
91+
92+
it('returns 0 for empty array', function () {
93+
const result = target.totalMarkerDuration([]);
94+
expect(result).toBe(0);
95+
});
96+
97+
it('returns 0 and logs error for non-array input', function () {
98+
const consoleErrorSpy = jest
99+
.spyOn(console, 'error')
100+
.mockImplementation(() => {});
101+
const result = target.totalMarkerDuration('not an array');
102+
expect(result).toBe(0);
103+
expect(consoleErrorSpy).toHaveBeenCalledWith(
104+
'totalMarkerDuration expects an array of markers'
105+
);
106+
consoleErrorSpy.mockRestore();
107+
});
108+
109+
it('calculates duration for interval markers', function () {
110+
const markers = [
111+
{
112+
start: 100,
113+
end: 200,
114+
name: 'marker1',
115+
category: 0,
116+
data: null,
117+
},
118+
{
119+
start: 150,
120+
end: 250,
121+
name: 'marker2',
122+
category: 0,
123+
data: null,
124+
},
125+
];
126+
const result = target.totalMarkerDuration(markers);
127+
expect(result).toBe(200); // (200-100) + (250-150) = 100 + 100 = 200
128+
});
129+
130+
it('skips instant markers with null end times', function () {
131+
const markers = [
132+
{
133+
start: 100,
134+
end: 200,
135+
name: 'interval',
136+
category: 0,
137+
threadId: null,
138+
data: null,
139+
},
140+
{
141+
start: 150,
142+
end: null,
143+
name: 'instant',
144+
category: 0,
145+
threadId: null,
146+
data: null,
147+
},
148+
{
149+
start: 300,
150+
end: 400,
151+
name: 'interval2',
152+
category: 0,
153+
threadId: null,
154+
data: null,
155+
},
156+
];
157+
const result = target.totalMarkerDuration(markers);
158+
expect(result).toBe(200); // (200-100) + (400-300) = 100 + 100 = 200
159+
});
160+
161+
it('handles mixed valid and invalid markers', function () {
162+
const markers = [
163+
{
164+
start: 100,
165+
end: 200,
166+
name: 'valid',
167+
category: 0,
168+
threadId: null,
169+
data: null,
170+
},
171+
null,
172+
{
173+
start: 'invalid',
174+
end: 300,
175+
name: 'invalid',
176+
category: 0,
177+
threadId: null,
178+
data: null,
179+
},
180+
{
181+
start: 400,
182+
end: 500,
183+
name: 'valid2',
184+
category: 0,
185+
threadId: null,
186+
data: null,
187+
},
188+
];
189+
const result = target.totalMarkerDuration(markers);
190+
expect(result).toBe(200); // (200-100) + (500-400) = 100 + 100 = 200
191+
});
192+
193+
it('logs formatted duration to console', function () {
194+
const markers = [
195+
{
196+
start: 100,
197+
end: 350,
198+
name: 'marker',
199+
category: 0,
200+
threadId: null,
201+
data: null,
202+
},
203+
];
204+
target.totalMarkerDuration(markers);
205+
expect(consoleLogSpy).toHaveBeenCalledWith(
206+
'Total marker duration: 250ms'
207+
);
208+
});
209+
});
76210
});

src/utils/window-console.js

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { selectorsForConsole } from 'firefox-profiler/selectors';
88
import actions from 'firefox-profiler/actions';
99
import { shortenUrl } from 'firefox-profiler/utils/shorten-url';
1010
import { createBrowserConnection } from 'firefox-profiler/app-logic/browser-connection';
11+
import { formatTimestamp } from 'firefox-profiler/utils/format-numbers';
1112

1213
// Despite providing a good libdef for Object.defineProperty, Flow still
1314
// special-cases the `value` property: if it's missing it throws an error. Using
@@ -263,6 +264,28 @@ export function addDataToWindowObject(
263264
return logs.sort().join('\n');
264265
};
265266

267+
target.totalMarkerDuration = function (markers) {
268+
if (!Array.isArray(markers)) {
269+
console.error('totalMarkerDuration expects an array of markers');
270+
return 0;
271+
}
272+
273+
let totalDuration = 0;
274+
for (const marker of markers) {
275+
if (
276+
marker &&
277+
typeof marker.start === 'number' &&
278+
typeof marker.end === 'number'
279+
) {
280+
totalDuration += marker.end - marker.start;
281+
}
282+
// Skip markers with null end times (instant markers have no duration)
283+
}
284+
285+
console.log(`Total marker duration: ${formatTimestamp(totalDuration)}`);
286+
return totalDuration;
287+
};
288+
266289
target.shortenUrl = shortenUrl;
267290
target.getState = getState;
268291
target.selectors = selectorsForConsole;
@@ -315,6 +338,7 @@ export function logFriendlyPreamble() {
315338
%cwindow.filteredMarkers%c - The current filtered and processed markers
316339
%cwindow.selectedMarker%c - The selected processed marker in the current thread
317340
%cwindow.callTree%c - The call tree of the current filtered thread
341+
%cwindow.totalMarkerDuration%c - Calculate total duration of a marker array (e.g., totalMarkerDuration(filteredMarkers))
318342
%cwindow.getState%c - The function that returns the current Redux state.
319343
%cwindow.selectors%c - All the selectors that are used to get data from the Redux state.
320344
%cwindow.dispatch%c - The function to dispatch a Redux action to change the state.
@@ -350,6 +374,9 @@ export function logFriendlyPreamble() {
350374
// "window.callTree"
351375
bold,
352376
reset,
377+
// "window.totalMarkerDuration"
378+
bold,
379+
reset,
353380
// "window.getState"
354381
bold,
355382
reset,

0 commit comments

Comments
 (0)