Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(trace viewer): Extending existing NetworkTab view. #5009

Merged
merged 21 commits into from
Jan 26, 2021
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
0f76c24
feat(trace viewer): Extending existing NetworkTab view.
domderen Jan 14, 2021
076e2ad
Merge branch 'master' into trace-viewer-network-view
domderen Jan 14, 2021
894c413
Fixing typescript compilation errors.
domderen Jan 14, 2021
fd86e00
Adding test for new network event properties.
domderen Jan 14, 2021
67ca4fc
feat(trace viewer): Adding response status code to NetworkTab details.
domderen Jan 14, 2021
a2874cb
fix(trace viewer): splitting networkTab & networkResourceDetails comp…
domderen Jan 14, 2021
a046a44
Merge branch 'master' into trace-viewer-network-view
domderen Jan 15, 2021
c3f5e19
Merge branch 'master' into trace-viewer-network-view
domderen Jan 17, 2021
dabf6da
Merge branch 'master' into trace-viewer-network-view
domderen Jan 24, 2021
592c53c
feat(trace viewer): Renaming sha1 to responseSha1.
domderen Jan 24, 2021
32b49c0
fix(trace viewer):adjusting PR to use _traceDir browser context argiu…
domderen Jan 24, 2021
9fa4ccc
fix(tracce viewer): Removing "only" modifier on tests.
domderen Jan 24, 2021
ecb0ff9
feat(trace viewer): Modifying styles in Trace Viewer Network Tab to b…
domderen Jan 24, 2021
1d537c0
fix(trace viewer): removing unnecessary check.
domderen Jan 24, 2021
d2923b2
fix(trace viewer): Fixing the way "selected" property is managed in N…
domderen Jan 24, 2021
4087030
fix(trace viewer): Fixing the trace viewer runtime.
domderen Jan 24, 2021
50723d2
feat(trace viewer): Resetting network tab expanded & selected propert…
domderen Jan 24, 2021
38c9935
fix(trace viewer): Fixing a race condition in the tracer test.
domderen Jan 25, 2021
7a2c3dc
fix(trace viewer): Fixing header size in Network tab.
domderen Jan 25, 2021
be8d044
feat(trace viewer): Improving handling for unavailable and empty resp…
domderen Jan 25, 2021
201eae2
Merge branch 'master' into trace-viewer-network-view
domderen Jan 26, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/cli/traceViewer/traceViewer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,9 @@ class TraceViewer {
await uiPage.exposeBinding('readFile', async (_, path: string) => {
return fs.readFileSync(path).toString();
});
await uiPage.exposeBinding('readResource', async (_, sha1: string) => {
return fs.readFileSync(path.join(this._traceStorageDir, sha1)).toString('base64');
});
await uiPage.exposeBinding('renderSnapshot', async (_, action: ActionTraceEvent) => {
try {
if (!action.snapshot) {
Expand Down
1 change: 1 addition & 0 deletions src/cli/traceViewer/web/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ declare global {
getTraceModel(): Promise<TraceModel>;
getVideoMetaInfo(videoId: string): Promise<VideoMetaInfo | undefined>;
readFile(filePath: string): Promise<string>;
readResource(sha1: string): Promise<string>;
renderSnapshot(action: trace.ActionTraceEvent): void;
}
}
Expand Down
5 changes: 3 additions & 2 deletions src/cli/traceViewer/web/ui/helpers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,10 @@ export function useMeasure<T extends Element>() {
export const Expandable: React.FunctionComponent<{
title: JSX.Element,
body: JSX.Element,
setExpanded: Function,
expanded: Boolean,
style?: React.CSSProperties,
}> = ({ title, body, style }) => {
const [expanded, setExpanded] = React.useState(true);
}> = ({ title, body, setExpanded, expanded, style }) => {
return <div style={{ ...style, display: 'flex', flexDirection: 'column' }}>
<div style={{ display: 'flex', flexDirection: 'row', alignItems: 'center', whiteSpace: 'nowrap' }}>
<div
Expand Down
112 changes: 112 additions & 0 deletions src/cli/traceViewer/web/ui/networkResourceDetails.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
/*
Copyright (c) Microsoft Corporation.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

.network-request {
box-shadow: var(--box-shadow);
white-space: nowrap;
display: flex;
align-items: center;
padding: 0 10px;
margin-bottom: 10px;
background: #fdfcfc;
width: 100%;
border: 3px solid transparent;
flex: none;
outline: none;
}

.network-request.selected,
.network-request:hover {
border-color: var(--inactive-focus-ring);
}

.network-request.selected:focus {
border-color: var(--orange);
}

.network-request-title {
height: 36px;
display: flex;
align-items: center;
flex: 1;
}

.network-request-title-status {
font-weight: bold;
height: 100%;
display: flex;
align-items: center;
padding: 0px 5px;
margin-right: 5px;
}

.status-success {
dgozman marked this conversation as resolved.
Show resolved Hide resolved
background-color: rgb(124, 187, 62);
}

.status-failure {
background-color: rgb(211, 35, 35);
dgozman marked this conversation as resolved.
Show resolved Hide resolved
color: white;
}

.status-neutral {
background-color: white;
}

.network-request-title-method {
font-weight: bold;
}

.network-request-title-url {
overflow: hidden;
text-overflow: ellipsis;
flex: 1;
}

.network-request-title-content-type {
font-weight: bold;
}

.network-request-details {
font-family: var(--monospace-font);
width: 100%;
}

.network-request-details-url {
white-space: normal;
word-wrap: break-word;
}

.network-request-headers {
white-space: pre;
overflow: hidden;
}

.network-request-body {
white-space: pre;
overflow: scroll;
background-color: #dcdcdb;
border: black 1px solid;
max-height: 500px;
}

.network-request-response-body {
white-space: pre;
overflow: scroll;
background-color: #dcdcdb;
border: black 1px solid;
max-height: 500px;
}
106 changes: 106 additions & 0 deletions src/cli/traceViewer/web/ui/networkResourceDetails.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
/**
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import './networkResourceDetails.css';
import * as React from 'react';
import { Expandable } from './helpers';
import { NetworkResourceTraceEvent } from '../../../../trace/traceTypes';


export const NetworkResourceDetails: React.FunctionComponent<{
resource: NetworkResourceTraceEvent,
selected: string,
dgozman marked this conversation as resolved.
Show resolved Hide resolved
setSelected: React.Dispatch<React.SetStateAction<string>>,
}> = ({ resource, selected, setSelected }) => {
const [expanded, setExpanded] = React.useState(false);
const [requestBody, setRequestBody] = React.useState<string | null>(null);
const [responseBody, setResponseBody] = React.useState<string | null>(null);

React.useEffect(() => {
const readResources = async () => {
if (resource.requestSha1 !== 'none') {
const requestResource = await window.readResource(resource.requestSha1);
setRequestBody(requestResource);
}

if (resource.sha1 !== 'none') {
const responseResource = await window.readResource(resource.sha1);
setResponseBody(responseResource);
}
};

readResources();
}, [expanded, resource.sha1, resource.requestSha1]);

function formatBody(body: string | null, contentType: string): string {
if (body === null)
return 'Loading...';

const bodyStr = atob(body);

if (contentType.includes('application/json')) {
try {
return JSON.stringify(JSON.parse(bodyStr), null, 2);
} catch (err) {
return bodyStr;
}
}

if (contentType.includes('application/x-www-form-urlencoded'))
return decodeURIComponent(bodyStr);

return bodyStr;
}

function formatStatus(status: number): string {
if (status >= 200 && status < 400)
return 'status-success';

if (status >= 400)
return 'status-failure';

return 'status-neutral';
}

const requestContentTypeHeader = resource.requestHeaders.find(q => q.name === 'Content-Type');
const requestContentType = requestContentTypeHeader ? requestContentTypeHeader.value : '';

return <div
className={'network-request ' + (selected === resource.url ? 'selected' : '')} onClick={() => setSelected(resource.url)}>
<Expandable expanded={expanded} setExpanded={setExpanded} style={{ width: '100%' }} title={
<div className='network-request-title'>
<div className={'network-request-title-status ' + formatStatus(resource.status)}>{resource.status}</div>
<div className='network-request-title-method'>{resource.method}: &nbsp;</div>
<div className='network-request-title-url'>{resource.url}</div>
<div className='network-request-title-content-type'>{resource.contentType}</div>
</div>
} body={
<div className='network-request-details'>
<h4>URL</h4>
<div className='network-request-details-url'>{resource.url}</div>
<h4>Request Headers</h4>
<div className='network-request-headers'>{resource.requestHeaders.map(pair => `${pair.name}: ${pair.value}`).join('\n')}</div>
<h4>Response Headers</h4>
<div className='network-request-headers'>{resource.responseHeaders.map(pair => `${pair.name}: ${pair.value}`).join('\n')}</div>
{resource.requestSha1 !== 'none' ? <h3>Request Body</h3> : ''}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Everything is h4, but this one is h3 - looks strange.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doh, should be fixed now.

{resource.requestSha1 !== 'none' ? <div className='network-request-body'>{formatBody(requestBody, requestContentType)}</div> : ''}
<h4>Response Body</h4>
{resource.sha1 !== 'none' && responseBody !== null && resource.contentType.includes('image') ? <img src={`data:${resource.contentType};base64,${responseBody}`} /> : ''}
dgozman marked this conversation as resolved.
Show resolved Hide resolved
{resource.sha1 !== 'none' && responseBody !== null && !resource.contentType.includes('image') ? <div className='network-request-response-body'>{formatBody(responseBody, resource.contentType)}</div> : ''}
</div>
}/>
</div>;
};
42 changes: 0 additions & 42 deletions src/cli/traceViewer/web/ui/networkTab.css
Original file line number Diff line number Diff line change
Expand Up @@ -24,45 +24,3 @@
.network-tab:focus {
outline: none;
}

.network-request {
box-shadow: var(--box-shadow);
white-space: nowrap;
display: flex;
align-items: center;
padding: 0 10px;
margin-bottom: 10px;
background: #fdfcfc;
width: 100%;
border: 3px solid transparent;
flex: none;
outline: none;
}

.network-request-title {
height: 36px;
display: flex;
align-items: center;
overflow: hidden;
text-overflow: ellipsis;
}

.network-request-details {
font-family: var(--monospace-font);
white-space: pre;
overflow: hidden;
}

.network-request-title > div {
overflow: hidden;
text-overflow: ellipsis;
}

.network-request.selected,
.network-request:hover {
border-color: var(--inactive-focus-ring);
}

.network-request.selected:focus {
border-color: var(--orange);
}
17 changes: 6 additions & 11 deletions src/cli/traceViewer/web/ui/networkTab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,23 +17,18 @@
import { ActionEntry } from '../../traceModel';
import './networkTab.css';
import * as React from 'react';
import { Expandable } from './helpers';
import { NetworkResourceDetails } from './networkResourceDetails';

export const NetworkTab: React.FunctionComponent<{
actionEntry: ActionEntry | undefined,
}> = ({ actionEntry }) => {
const [selected, setSelected] = React.useState(0);
const [selected, setSelected] = React.useState('');
dgozman marked this conversation as resolved.
Show resolved Hide resolved

return <div className='network-tab'>{
(actionEntry ? actionEntry.resources : []).map((resource, index) => {
return <div key={index}
className={'network-request ' + (index === selected ? 'selected' : '')}
onClick={() => setSelected(index)}>
<Expandable style={{ width: '100%' }} title={
<div className='network-request-title'><div>{resource.url}</div></div>
} body={
<div className='network-request-details'>{resource.responseHeaders.map(pair => `${pair.name}: ${pair.value}`).join('\n')}</div>
}/>
</div>;
return <NetworkResourceDetails resource={resource} key={index} selected={selected} setSelected={setSelected} />;
})
}</div>;
};


16 changes: 16 additions & 0 deletions src/trace/snapshotter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ export type SnapshotterResource = {
url: string,
contentType: string,
responseHeaders: { name: string, value: string }[],
requestHeaders: { name: string, value: string }[],
method: string,
status: number,
requestSha1: string,
sha1: string,
dgozman marked this conversation as resolved.
Show resolved Hide resolved
};

Expand Down Expand Up @@ -88,6 +92,11 @@ export class Snapshotter {
contentType = value;
}

const method = original.method();
const status = response.status();
const requestBody = original.postDataBuffer();
const requestSha1 = requestBody ? calculateSha1(requestBody) : 'none';
const requestHeaders = original.headers();
const body = await response.body().catch(e => debugLogger.log('error', e));
const sha1 = body ? calculateSha1(body) : 'none';
const resource: SnapshotterResource = {
Expand All @@ -96,9 +105,16 @@ export class Snapshotter {
url,
contentType,
responseHeaders: response.headers(),
requestHeaders,
method,
status,
requestSha1,

sha1,
};
this._delegate.onResource(resource);
if (requestBody)
this._delegate.onBlob({ sha1: requestSha1, buffer: requestBody });
if (body)
this._delegate.onBlob({ sha1, buffer: body });
}
Expand Down
4 changes: 4 additions & 0 deletions src/trace/traceTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@ export type NetworkResourceTraceEvent = {
url: string,
contentType: string,
responseHeaders: { name: string, value: string }[],
requestHeaders: { name: string, value: string }[],
method: string,
status: number,
requestSha1: string,
sha1: string,
};

Expand Down
4 changes: 4 additions & 0 deletions src/trace/tracer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,10 @@ class ContextTracer implements SnapshotterDelegate, ActionListener {
url: resource.url,
contentType: resource.contentType,
responseHeaders: resource.responseHeaders,
requestHeaders: resource.requestHeaders,
method: resource.method,
status: resource.status,
requestSha1: resource.requestSha1,
sha1: resource.sha1,
};
this._appendTraceEvent(event);
Expand Down