mirror of
https://github.com/microsoft/playwright.git
synced 2025-06-26 21:40:17 +00:00
chore: fix har date types (#26783)
This commit is contained in:
parent
d98e3a2bed
commit
fd31f5bc50
@ -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');
|
@ -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) {
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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') }>
|
||||
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('/'));
|
||||
|
@ -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);
|
||||
|
@ -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',
|
||||
|
@ -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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user