chore: fix har date types (#26783)

This commit is contained in:
Pavel Feldman 2023-08-30 12:40:46 -07:00 committed by GitHub
parent d98e3a2bed
commit fd31f5bc50
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 95 additions and 62 deletions

View File

@ -127,8 +127,9 @@ export class HarTracer {
return;
let pageEntry = this._pageEntries.get(page);
if (!pageEntry) {
const date = new Date();
pageEntry = {
startedDateTime: new Date(),
startedDateTime: date.toISOString(),
id: page.guid,
title: '',
pageTimings: this._options.omitTiming ? {} : {
@ -136,6 +137,7 @@ export class HarTracer {
onLoad: -1,
},
};
(pageEntry as any)[startedDateSymbol] = date;
page.mainFrame().on(Frame.Events.AddLifecycle, (event: LifecycleEvent) => {
if (event === 'load')
@ -221,7 +223,7 @@ export class HarTracer {
harEntry.response.cookies = this._options.omitCookies ? [] : event.cookies.map(c => {
return {
...c,
expires: c.expires === -1 ? undefined : new Date(c.expires)
expires: c.expires === -1 ? undefined : new Date(c.expires).toISOString()
};
});
@ -456,9 +458,10 @@ export class HarTracer {
};
if (!this._options.omitTiming) {
const startDateTime = pageEntry ? ((pageEntry as any)[startedDateSymbol] as Date).valueOf() : 0;
const timing = response.timing();
if (pageEntry && pageEntry.startedDateTime.valueOf() > timing.startTime)
pageEntry.startedDateTime = new Date(timing.startTime);
if (pageEntry && startDateTime > timing.startTime)
pageEntry.startedDateTime = new Date(timing.startTime).toISOString();
const dns = timing.domainLookupEnd !== -1 ? helper.millisToRoundishMillis(timing.domainLookupEnd - timing.domainLookupStart) : -1;
const connect = timing.connectEnd !== -1 ? helper.millisToRoundishMillis(timing.connectEnd - timing.connectStart) : -1;
const ssl = timing.connectEnd !== -1 ? helper.millisToRoundishMillis(timing.connectEnd - timing.secureConnectionStart) : -1;
@ -535,12 +538,13 @@ export class HarTracer {
};
if (!this._options.omitTiming) {
for (const pageEntry of log.pages || []) {
const startDateTime = ((pageEntry as any)[startedDateSymbol] as Date).valueOf();
if (typeof pageEntry.pageTimings.onContentLoad === 'number' && pageEntry.pageTimings.onContentLoad >= 0)
pageEntry.pageTimings.onContentLoad -= pageEntry.startedDateTime.valueOf();
pageEntry.pageTimings.onContentLoad -= startDateTime;
else
pageEntry.pageTimings.onContentLoad = -1;
if (typeof pageEntry.pageTimings.onLoad === 'number' && pageEntry.pageTimings.onLoad >= 0)
pageEntry.pageTimings.onLoad -= pageEntry.startedDateTime.valueOf();
pageEntry.pageTimings.onLoad -= startDateTime;
else
pageEntry.pageTimings.onLoad = -1;
}
@ -597,7 +601,7 @@ function createHarEntry(method: string, url: URL, frameref: string | undefined,
const harEntry: har.Entry = {
_frameref: options.includeTraceInfo ? frameref : undefined,
_monotonicTime: options.includeTraceInfo ? monotonicTime() : undefined,
startedDateTime: new Date(),
startedDateTime: new Date().toISOString(),
time: -1,
request: {
method: method,
@ -654,11 +658,11 @@ function parseCookie(c: string): har.Cookie {
if (name === 'Domain')
cookie.domain = value;
if (name === 'Expires')
cookie.expires = new Date(value);
cookie.expires = new Date(value).toISOString();
if (name === 'HttpOnly')
cookie.httpOnly = true;
if (name === 'Max-Age')
cookie.expires = new Date(Date.now() + (+value) * 1000);
cookie.expires = new Date(Date.now() + (+value) * 1000).toISOString();
if (name === 'Path')
cookie.path = value;
if (name === 'SameSite')
@ -668,3 +672,5 @@ function parseCookie(c: string): har.Cookie {
}
return cookie;
}
const startedDateSymbol = Symbol('startedDate');

View File

@ -493,6 +493,8 @@ function visitTraceEvent(object: any, sha1s: Set<string>): any {
return object.map(o => visitTraceEvent(o, sha1s));
if (object instanceof Buffer)
return undefined;
if (object instanceof Date)
return object;
if (typeof object === 'object') {
const result: any = {};
for (const key in object) {

View File

@ -29,12 +29,18 @@
background-color: var(--vscode-statusBarItem-remoteBackground);
}
.network-request-start {
flex: 0 0 65px;
justify-content: right;
padding-right: 10px;
}
.network-request-status {
flex: 0 0 70px;
flex: 0 0 65px;
}
.network-request-method {
flex: 0 0 70px;
flex: 0 0 65px;
}
.network-request-file {
@ -49,7 +55,7 @@
}
.network-request-content-type,
.network-request-time,
.network-request-duration,
.network-request-route,
.network-request-size {
overflow: hidden;
@ -81,6 +87,14 @@
display: flex;
align-items: center;
white-space: nowrap;
height: 100%;
}
.network-request-header.filter-start.positive .network-request-start .codicon-triangle-down {
display: initial !important;
}
.network-request-header.filter-start.negative .network-request-start .codicon-triangle-up {
display: initial !important;
}
.network-request-header.filter-status.positive .network-request-status .codicon-triangle-down {
@ -111,10 +125,10 @@
display: initial !important;
}
.network-request-header.filter-time.positive .network-request-time .codicon-triangle-down {
.network-request-header.filter-duration.positive .network-request-duration .codicon-triangle-down {
display: initial !important;
}
.network-request-header.filter-time.negative .network-request-time .codicon-triangle-up {
.network-request-header.filter-duration.negative .network-request-duration .codicon-triangle-up {
display: initial !important;
}

View File

@ -25,16 +25,17 @@ import { bytesToString, msToString } from '@web/uiUtils';
const NetworkListView = ListView<Entry>;
type Filter = 'status' | 'method' | 'file' | 'time' | 'size' | 'content-type';
type SortBy = 'start' | 'status' | 'method' | 'file' | 'duration' | 'size' | 'content-type';
type Sorting = { by: SortBy, negate: boolean};
export const NetworkTab: React.FunctionComponent<{
model: modelUtil.MultiTraceModel | undefined,
boundaries: Boundaries,
selectedTime: Boundaries | undefined,
onEntryHovered: (entry: Entry | undefined) => void,
}> = ({ model, selectedTime, onEntryHovered }) => {
}> = ({ model, boundaries, selectedTime, onEntryHovered }) => {
const [resource, setResource] = React.useState<Entry | undefined>();
const [filter, setFilter] = React.useState<Filter | undefined>(undefined);
const [negateFilter, setNegateFilter] = React.useState<boolean>(false);
const [sorting, setSorting] = React.useState<Sorting | undefined>(undefined);
const resources = React.useMemo(() => {
const resources = model?.resources || [];
@ -43,27 +44,22 @@ export const NetworkTab: React.FunctionComponent<{
return true;
return !!resource._monotonicTime && (resource._monotonicTime >= selectedTime.minimum && resource._monotonicTime <= selectedTime.maximum);
});
if (filter)
sort(filtered, filter, negateFilter);
if (sorting)
sort(filtered, sorting);
return filtered;
}, [filter, model, negateFilter, selectedTime]);
}, [sorting, model, selectedTime]);
const toggleFilter = React.useCallback((f: Filter) => {
if (filter === f) {
setNegateFilter(!negateFilter);
} else {
setNegateFilter(false);
setFilter(f);
}
}, [filter, negateFilter]);
const toggleSorting = React.useCallback((f: SortBy) => {
setSorting({ by: f, negate: sorting?.by === f ? !sorting.negate : false });
}, [sorting]);
return <>
{!resource && <div className='vbox'>
<NetworkHeader filter={filter} negateFilter={negateFilter} toggleFilter={toggleFilter} />
<NetworkHeader sorting={sorting} toggleSorting={toggleSorting} />
<NetworkListView
dataTestId='network-request-list'
items={resources}
render={entry => <NetworkResource resource={entry}></NetworkResource>}
render={entry => <NetworkResource boundaries={boundaries} resource={entry}></NetworkResource>}
onSelected={setResource}
onHighlighted={onEntryHovered}
/>
@ -73,37 +69,40 @@ export const NetworkTab: React.FunctionComponent<{
};
const NetworkHeader: React.FunctionComponent<{
filter: Filter | undefined,
negateFilter: boolean,
toggleFilter: (filter: Filter) => void,
}> = ({ toggleFilter, filter, negateFilter }) => {
return <div className={'hbox network-request-header' + (filter ? ' filter-' + filter : '') + (negateFilter ? ' negative' : ' positive')}>
<div className='network-request-status' onClick={() => toggleFilter('status') }>
sorting: Sorting | undefined,
toggleSorting: (sortBy: SortBy) => void,
}> = ({ toggleSorting: toggleSortBy, sorting }) => {
return <div className={'hbox network-request-header' + (sorting ? ' filter-' + sorting.by + (sorting.negate ? ' negative' : ' positive') : '')}>
<div className='network-request-start' onClick={() => toggleSortBy('start') }>
<span className='codicon codicon-triangle-up' />
<span className='codicon codicon-triangle-down' />
</div>
<div className='network-request-status' onClick={() => toggleSortBy('status') }>
&nbsp;Status
<span className='codicon codicon-triangle-up' />
<span className='codicon codicon-triangle-down' />
</div>
<div className='network-request-method' onClick={() => toggleFilter('method') }>
<div className='network-request-method' onClick={() => toggleSortBy('method') }>
Method
<span className='codicon codicon-triangle-up' />
<span className='codicon codicon-triangle-down' />
</div>
<div className='network-request-file' onClick={() => toggleFilter('file') }>
<div className='network-request-file' onClick={() => toggleSortBy('file') }>
Request
<span className='codicon codicon-triangle-up' />
<span className='codicon codicon-triangle-down' />
</div>
<div className='network-request-content-type' onClick={() => toggleFilter('content-type') }>
<div className='network-request-content-type' onClick={() => toggleSortBy('content-type') }>
Content Type
<span className='codicon codicon-triangle-up' />
<span className='codicon codicon-triangle-down' />
</div>
<div className='network-request-time' onClick={() => toggleFilter('time') }>
Time
<div className='network-request-duration' onClick={() => toggleSortBy('duration') }>
Duration
<span className='codicon codicon-triangle-up' />
<span className='codicon codicon-triangle-down' />
</div>
<div className='network-request-size' onClick={() => toggleFilter('size') }>
<div className='network-request-size' onClick={() => toggleSortBy('size') }>
Size
<span className='codicon codicon-triangle-up' />
<span className='codicon codicon-triangle-down' />
@ -114,7 +113,8 @@ const NetworkHeader: React.FunctionComponent<{
const NetworkResource: React.FunctionComponent<{
resource: Entry,
}> = ({ resource }) => {
boundaries: Boundaries,
}> = ({ resource, boundaries }) => {
const { routeStatus, resourceName, contentType } = React.useMemo(() => {
const routeStatus = formatRouteStatus(resource);
const resourceName = resource.request.url.substring(resource.request.url.lastIndexOf('/'));
@ -126,6 +126,9 @@ const NetworkResource: React.FunctionComponent<{
}, [resource]);
return <div className='hbox'>
<div className='hbox network-request-start'>
<div>{msToString(resource._monotonicTime! - boundaries.minimum)}</div>
</div>
<div className='hbox network-request-status'>
<div className={formatStatus(resource.response.status)} title={resource.response.statusText}>{resource.response.status}</div>
</div>
@ -136,7 +139,7 @@ const NetworkResource: React.FunctionComponent<{
<div className='network-request-file-url' title={resource.request.url}>{resourceName}</div>
</div>
<div className='network-request-content-type' title={contentType}>{contentType}</div>
<div className='network-request-time'>{msToString(resource.time)}</div>
<div className='network-request-duration'>{msToString(resource.time)}</div>
<div className='network-request-size'>{bytesToString(resource.response._transferSize! > 0 ? resource.response._transferSize! : resource.response.bodySize)}</div>
<div className='network-request-route'>
{routeStatus && <div className={`status-route ${routeStatus}`}>{routeStatus}</div>}
@ -164,22 +167,25 @@ function formatRouteStatus(request: Entry): string {
return '';
}
function sort(resources: Entry[], filter: Filter | undefined, negate: boolean) {
const c = comparator(filter);
function sort(resources: Entry[], sorting: Sorting) {
const c = comparator(sorting?.by);
if (c)
resources.sort(c);
if (negate)
if (sorting.negate)
resources.reverse();
}
function comparator(filter: Filter | undefined) {
if (filter === 'time')
function comparator(sortBy: SortBy) {
if (sortBy === 'start')
return (a: Entry, b: Entry) => a._monotonicTime! - b._monotonicTime!;
if (sortBy === 'duration')
return (a: Entry, b: Entry) => a.time - b.time;
if (filter === 'status')
if (sortBy === 'status')
return (a: Entry, b: Entry) => a.response.status - b.response.status;
if (filter === 'method') {
if (sortBy === 'method') {
return (a: Entry, b: Entry) => {
const valueA = a.request.method;
const valueB = b.request.method;
@ -187,7 +193,7 @@ function comparator(filter: Filter | undefined) {
};
}
if (filter === 'size') {
if (sortBy === 'size') {
return (a: Entry, b: Entry) => {
const sizeA = a.response._transferSize! > 0 ? a.response._transferSize! : a.response.bodySize;
const sizeB = b.response._transferSize! > 0 ? b.response._transferSize! : b.response.bodySize;
@ -195,7 +201,7 @@ function comparator(filter: Filter | undefined) {
};
}
if (filter === 'content-type') {
if (sortBy === 'content-type') {
return (a: Entry, b: Entry) => {
const valueA = a.response.content.mimeType;
const valueB = b.response.content.mimeType;
@ -203,7 +209,7 @@ function comparator(filter: Filter | undefined) {
};
}
if (filter === 'file') {
if (sortBy === 'file') {
return (a: Entry, b: Entry) => {
const nameA = a.request.url.substring(a.request.url.lastIndexOf('/'));
const nameB = b.request.url.substring(b.request.url.lastIndexOf('/'));

View File

@ -73,7 +73,7 @@ export const Timeline: React.FunctionComponent<{
rightTime: entry.endTime || boundaries.maximum,
leftPosition: timeToPosition(measure.width, boundaries, entry.startTime),
rightPosition: timeToPosition(measure.width, boundaries, entry.endTime || boundaries.maximum),
active: highlightedAction === entry,
active: false,
});
}
@ -86,11 +86,16 @@ export const Timeline: React.FunctionComponent<{
rightTime: endTime,
leftPosition: timeToPosition(measure.width, boundaries, startTime),
rightPosition: timeToPosition(measure.width, boundaries, endTime),
active: highlightedEntry === resource,
active: false,
});
}
return bars;
}, [model, boundaries, measure, highlightedAction, highlightedEntry]);
}, [model, boundaries, measure]);
React.useMemo(() => {
for (const bar of bars)
bar.active = (!!highlightedAction && bar.action === highlightedAction) || (!!highlightedEntry && bar.resource === highlightedEntry);
}, [bars, highlightedAction, highlightedEntry]);
const onMouseDown = React.useCallback((event: React.MouseEvent) => {
setPreviewPoint(undefined);

View File

@ -124,7 +124,7 @@ export const Workbench: React.FunctionComponent<{
const networkTab: TabbedPaneTabModel = {
id: 'network',
title: 'Network',
render: () => <NetworkTab model={model} selectedTime={selectedTime} onEntryHovered={setHighlightedEntry}/>
render: () => <NetworkTab model={model} boundaries={boundaries} selectedTime={selectedTime} onEntryHovered={setHighlightedEntry}/>
};
const attachmentsTab: TabbedPaneTabModel = {
id: 'attachments',

View File

@ -41,7 +41,7 @@ export type Browser = {
};
export type Page = {
startedDateTime: Date;
startedDateTime: string;
id: string;
title: string;
pageTimings: PageTimings;
@ -56,7 +56,7 @@ export type PageTimings = {
export type Entry = {
pageref?: string;
startedDateTime: Date;
startedDateTime: string;
time: number;
request: Request;
response: Response;
@ -107,7 +107,7 @@ export type Cookie = {
value: string;
path?: string;
domain?: string;
expires?: Date;
expires?: string;
httpOnly?: boolean;
secure?: boolean;
sameSite?: string;