chore(tracing): simplify resource treatment (#6571)
This commit is contained in:
parent
9b0aeeffae
commit
7b844c5fab
|
|
@ -56,13 +56,11 @@ export class Dialog extends SdkObject {
|
||||||
assert(!this._handled, 'Cannot accept dialog which is already handled!');
|
assert(!this._handled, 'Cannot accept dialog which is already handled!');
|
||||||
this._handled = true;
|
this._handled = true;
|
||||||
await this._onHandle(true, promptText);
|
await this._onHandle(true, promptText);
|
||||||
this._page.emit(Page.Events.InternalDialogClosed, this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async dismiss() {
|
async dismiss() {
|
||||||
assert(!this._handled, 'Cannot dismiss dialog which is already handled!');
|
assert(!this._handled, 'Cannot dismiss dialog which is already handled!');
|
||||||
this._handled = true;
|
this._handled = true;
|
||||||
await this._onHandle(false);
|
await this._onHandle(false);
|
||||||
this._page.emit(Page.Events.InternalDialogClosed, this);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -97,7 +97,6 @@ export class Page extends SdkObject {
|
||||||
Crash: 'crash',
|
Crash: 'crash',
|
||||||
Console: 'console',
|
Console: 'console',
|
||||||
Dialog: 'dialog',
|
Dialog: 'dialog',
|
||||||
InternalDialogClosed: 'internaldialogclosed',
|
|
||||||
Download: 'download',
|
Download: 'download',
|
||||||
FileChooser: 'filechooser',
|
FileChooser: 'filechooser',
|
||||||
DOMContentLoaded: 'domcontentloaded',
|
DOMContentLoaded: 'domcontentloaded',
|
||||||
|
|
|
||||||
|
|
@ -66,35 +66,6 @@ export type FrameSnapshotTraceEvent = {
|
||||||
snapshot: FrameSnapshot,
|
snapshot: FrameSnapshot,
|
||||||
};
|
};
|
||||||
|
|
||||||
export type DialogOpenedEvent = {
|
|
||||||
timestamp: number,
|
|
||||||
type: 'dialog-opened',
|
|
||||||
pageId: string,
|
|
||||||
dialogType: string,
|
|
||||||
message?: string,
|
|
||||||
};
|
|
||||||
|
|
||||||
export type DialogClosedEvent = {
|
|
||||||
timestamp: number,
|
|
||||||
type: 'dialog-closed',
|
|
||||||
pageId: string,
|
|
||||||
dialogType: string,
|
|
||||||
};
|
|
||||||
|
|
||||||
export type NavigationEvent = {
|
|
||||||
timestamp: number,
|
|
||||||
type: 'navigation',
|
|
||||||
pageId: string,
|
|
||||||
url: string,
|
|
||||||
sameDocument: boolean,
|
|
||||||
};
|
|
||||||
|
|
||||||
export type LoadEvent = {
|
|
||||||
timestamp: number,
|
|
||||||
type: 'load',
|
|
||||||
pageId: string,
|
|
||||||
};
|
|
||||||
|
|
||||||
export type TraceEvent =
|
export type TraceEvent =
|
||||||
ContextCreatedTraceEvent |
|
ContextCreatedTraceEvent |
|
||||||
PageCreatedTraceEvent |
|
PageCreatedTraceEvent |
|
||||||
|
|
@ -102,8 +73,4 @@ export type TraceEvent =
|
||||||
ScreencastFrameTraceEvent |
|
ScreencastFrameTraceEvent |
|
||||||
ActionTraceEvent |
|
ActionTraceEvent |
|
||||||
ResourceSnapshotTraceEvent |
|
ResourceSnapshotTraceEvent |
|
||||||
FrameSnapshotTraceEvent |
|
FrameSnapshotTraceEvent;
|
||||||
DialogOpenedEvent |
|
|
||||||
DialogClosedEvent |
|
|
||||||
NavigationEvent |
|
|
||||||
LoadEvent;
|
|
||||||
|
|
|
||||||
|
|
@ -21,9 +21,7 @@ import yazl from 'yazl';
|
||||||
import { calculateSha1, createGuid, mkdirIfNeeded, monotonicTime } from '../../../utils/utils';
|
import { calculateSha1, createGuid, mkdirIfNeeded, monotonicTime } from '../../../utils/utils';
|
||||||
import { Artifact } from '../../artifact';
|
import { Artifact } from '../../artifact';
|
||||||
import { BrowserContext } from '../../browserContext';
|
import { BrowserContext } from '../../browserContext';
|
||||||
import { Dialog } from '../../dialog';
|
|
||||||
import { ElementHandle } from '../../dom';
|
import { ElementHandle } from '../../dom';
|
||||||
import { Frame, NavigationEvent } from '../../frames';
|
|
||||||
import { helper, RegisteredListener } from '../../helper';
|
import { helper, RegisteredListener } from '../../helper';
|
||||||
import { CallMetadata, InstrumentationListener, SdkObject } from '../../instrumentation';
|
import { CallMetadata, InstrumentationListener, SdkObject } from '../../instrumentation';
|
||||||
import { Page } from '../../page';
|
import { Page } from '../../page';
|
||||||
|
|
@ -188,51 +186,6 @@ export class Tracing implements InstrumentationListener {
|
||||||
page.setScreencastOptions({ width: 800, height: 600, quality: 90 });
|
page.setScreencastOptions({ width: 800, height: 600, quality: 90 });
|
||||||
|
|
||||||
this._eventListeners.push(
|
this._eventListeners.push(
|
||||||
helper.addEventListener(page, Page.Events.Dialog, (dialog: Dialog) => {
|
|
||||||
const event: trace.DialogOpenedEvent = {
|
|
||||||
timestamp: monotonicTime(),
|
|
||||||
type: 'dialog-opened',
|
|
||||||
pageId,
|
|
||||||
dialogType: dialog.type(),
|
|
||||||
message: dialog.message(),
|
|
||||||
};
|
|
||||||
this._appendTraceEvent(event);
|
|
||||||
}),
|
|
||||||
|
|
||||||
helper.addEventListener(page, Page.Events.InternalDialogClosed, (dialog: Dialog) => {
|
|
||||||
const event: trace.DialogClosedEvent = {
|
|
||||||
timestamp: monotonicTime(),
|
|
||||||
type: 'dialog-closed',
|
|
||||||
pageId,
|
|
||||||
dialogType: dialog.type(),
|
|
||||||
};
|
|
||||||
this._appendTraceEvent(event);
|
|
||||||
}),
|
|
||||||
|
|
||||||
helper.addEventListener(page.mainFrame(), Frame.Events.Navigation, (navigationEvent: NavigationEvent) => {
|
|
||||||
if (page.mainFrame().url() === 'about:blank')
|
|
||||||
return;
|
|
||||||
const event: trace.NavigationEvent = {
|
|
||||||
timestamp: monotonicTime(),
|
|
||||||
type: 'navigation',
|
|
||||||
pageId,
|
|
||||||
url: navigationEvent.url,
|
|
||||||
sameDocument: !navigationEvent.newDocument,
|
|
||||||
};
|
|
||||||
this._appendTraceEvent(event);
|
|
||||||
}),
|
|
||||||
|
|
||||||
helper.addEventListener(page, Page.Events.Load, () => {
|
|
||||||
if (page.mainFrame().url() === 'about:blank')
|
|
||||||
return;
|
|
||||||
const event: trace.LoadEvent = {
|
|
||||||
timestamp: monotonicTime(),
|
|
||||||
type: 'load',
|
|
||||||
pageId,
|
|
||||||
};
|
|
||||||
this._appendTraceEvent(event);
|
|
||||||
}),
|
|
||||||
|
|
||||||
helper.addEventListener(page, Page.Events.ScreencastFrame, params => {
|
helper.addEventListener(page, Page.Events.ScreencastFrame, params => {
|
||||||
const sha1 = calculateSha1(createGuid()); // no need to compute sha1 for screenshots
|
const sha1 = calculateSha1(createGuid()); // no need to compute sha1 for screenshots
|
||||||
const event: trace.ScreencastFrameTraceEvent = {
|
const event: trace.ScreencastFrameTraceEvent = {
|
||||||
|
|
@ -248,7 +201,6 @@ export class Tracing implements InstrumentationListener {
|
||||||
await fsWriteFileAsync(path.join(this._resourcesDir!, sha1), params.buffer).catch(() => {});
|
await fsWriteFileAsync(path.join(this._resourcesDir!, sha1), params.buffer).catch(() => {});
|
||||||
});
|
});
|
||||||
}),
|
}),
|
||||||
|
|
||||||
helper.addEventListener(page, Page.Events.Close, () => {
|
helper.addEventListener(page, Page.Events.Close, () => {
|
||||||
const event: trace.PageDestroyedTraceEvent = {
|
const event: trace.PageDestroyedTraceEvent = {
|
||||||
timestamp: monotonicTime(),
|
timestamp: monotonicTime(),
|
||||||
|
|
|
||||||
|
|
@ -34,18 +34,10 @@ export class TraceModel {
|
||||||
appendEvents(events: trace.TraceEvent[], snapshotStorage: SnapshotStorage) {
|
appendEvents(events: trace.TraceEvent[], snapshotStorage: SnapshotStorage) {
|
||||||
for (const event of events)
|
for (const event of events)
|
||||||
this.appendEvent(event);
|
this.appendEvent(event);
|
||||||
const actions: ActionEntry[] = [];
|
const actions: trace.ActionTraceEvent[] = [];
|
||||||
for (const page of this.contextEntry!.pages)
|
for (const page of this.contextEntry!.pages)
|
||||||
actions.push(...page.actions);
|
actions.push(...page.actions);
|
||||||
|
this.contextEntry!.resources = snapshotStorage.resources();
|
||||||
const resources = snapshotStorage.resources().reverse();
|
|
||||||
actions.reverse();
|
|
||||||
|
|
||||||
for (const action of actions) {
|
|
||||||
while (resources.length && resources[0].timestamp > action.timestamp)
|
|
||||||
action.resources.push(resources.shift()!);
|
|
||||||
action.resources.reverse();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
appendEvent(event: trace.TraceEvent) {
|
appendEvent(event: trace.TraceEvent) {
|
||||||
|
|
@ -56,6 +48,7 @@ export class TraceModel {
|
||||||
endTime: Number.MIN_VALUE,
|
endTime: Number.MIN_VALUE,
|
||||||
created: event,
|
created: event,
|
||||||
pages: [],
|
pages: [],
|
||||||
|
resources: []
|
||||||
};
|
};
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -64,7 +57,7 @@ export class TraceModel {
|
||||||
created: event,
|
created: event,
|
||||||
destroyed: undefined as any,
|
destroyed: undefined as any,
|
||||||
actions: [],
|
actions: [],
|
||||||
interestingEvents: [],
|
events: [],
|
||||||
screencastFrames: [],
|
screencastFrames: [],
|
||||||
};
|
};
|
||||||
this.pageEntries.set(event.pageId, pageEntry);
|
this.pageEntries.set(event.pageId, pageEntry);
|
||||||
|
|
@ -82,20 +75,14 @@ export class TraceModel {
|
||||||
case 'action': {
|
case 'action': {
|
||||||
const metadata = event.metadata;
|
const metadata = event.metadata;
|
||||||
const pageEntry = this.pageEntries.get(metadata.pageId!)!;
|
const pageEntry = this.pageEntries.get(metadata.pageId!)!;
|
||||||
const action: ActionEntry = {
|
pageEntry.actions.push(event);
|
||||||
actionId: metadata.id,
|
|
||||||
resources: [],
|
|
||||||
...event,
|
|
||||||
};
|
|
||||||
pageEntry.actions.push(action);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'dialog-opened':
|
case 'event': {
|
||||||
case 'dialog-closed':
|
const metadata = event.metadata;
|
||||||
case 'navigation':
|
const pageEntry = this.pageEntries.get(metadata.pageId!);
|
||||||
case 'load': {
|
if (pageEntry)
|
||||||
const pageEntry = this.pageEntries.get(event.pageId)!;
|
pageEntry.events.push(event);
|
||||||
pageEntry.interestingEvents.push(event);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'resource-snapshot':
|
case 'resource-snapshot':
|
||||||
|
|
@ -105,8 +92,10 @@ export class TraceModel {
|
||||||
this._snapshotStorage.addFrameSnapshot(event.snapshot);
|
this._snapshotStorage.addFrameSnapshot(event.snapshot);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
this.contextEntry!.startTime = Math.min(this.contextEntry!.startTime, event.timestamp);
|
if (event.type === 'action' || event.type === 'event') {
|
||||||
this.contextEntry!.endTime = Math.max(this.contextEntry!.endTime, event.timestamp);
|
this.contextEntry!.startTime = Math.min(this.contextEntry!.startTime, event.metadata.startTime);
|
||||||
|
this.contextEntry!.endTime = Math.max(this.contextEntry!.endTime, event.metadata.endTime);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -115,15 +104,14 @@ export type ContextEntry = {
|
||||||
endTime: number;
|
endTime: number;
|
||||||
created: trace.ContextCreatedTraceEvent;
|
created: trace.ContextCreatedTraceEvent;
|
||||||
pages: PageEntry[];
|
pages: PageEntry[];
|
||||||
|
resources: ResourceSnapshot[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export type InterestingPageEvent = trace.DialogOpenedEvent | trace.DialogClosedEvent | trace.NavigationEvent | trace.LoadEvent;
|
|
||||||
|
|
||||||
export type PageEntry = {
|
export type PageEntry = {
|
||||||
created: trace.PageCreatedTraceEvent;
|
created: trace.PageCreatedTraceEvent;
|
||||||
destroyed: trace.PageDestroyedTraceEvent;
|
destroyed: trace.PageDestroyedTraceEvent;
|
||||||
actions: ActionEntry[];
|
actions: trace.ActionTraceEvent[];
|
||||||
interestingEvents: InterestingPageEvent[];
|
events: trace.ActionTraceEvent[];
|
||||||
screencastFrames: {
|
screencastFrames: {
|
||||||
sha1: string,
|
sha1: string,
|
||||||
timestamp: number,
|
timestamp: number,
|
||||||
|
|
@ -132,11 +120,6 @@ export type PageEntry = {
|
||||||
}[]
|
}[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ActionEntry = trace.ActionTraceEvent & {
|
|
||||||
actionId: string;
|
|
||||||
resources: ResourceSnapshot[]
|
|
||||||
};
|
|
||||||
|
|
||||||
export class PersistentSnapshotStorage extends BaseSnapshotStorage {
|
export class PersistentSnapshotStorage extends BaseSnapshotStorage {
|
||||||
private _resourcesDir: string;
|
private _resourcesDir: string;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,17 +14,17 @@
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { ActionEntry } from '../../../server/trace/viewer/traceModel';
|
|
||||||
import './actionList.css';
|
import './actionList.css';
|
||||||
import './tabbedPane.css';
|
import './tabbedPane.css';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
|
import { ActionTraceEvent } from '../../../server/trace/common/traceEvents';
|
||||||
|
|
||||||
export interface ActionListProps {
|
export interface ActionListProps {
|
||||||
actions: ActionEntry[],
|
actions: ActionTraceEvent[],
|
||||||
selectedAction: ActionEntry | undefined,
|
selectedAction: ActionTraceEvent | undefined,
|
||||||
highlightedAction: ActionEntry | undefined,
|
highlightedAction: ActionTraceEvent | undefined,
|
||||||
onSelected: (action: ActionEntry) => void,
|
onSelected: (action: ActionTraceEvent) => void,
|
||||||
onHighlighted: (action: ActionEntry | undefined) => void,
|
onHighlighted: (action: ActionTraceEvent | undefined) => void,
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ActionList: React.FC<ActionListProps> = ({
|
export const ActionList: React.FC<ActionListProps> = ({
|
||||||
|
|
@ -68,16 +68,16 @@ export const ActionList: React.FC<ActionListProps> = ({
|
||||||
}}
|
}}
|
||||||
ref={actionListRef}
|
ref={actionListRef}
|
||||||
>
|
>
|
||||||
{actions.map(actionEntry => {
|
{actions.map(action => {
|
||||||
const { metadata, actionId } = actionEntry;
|
const { metadata } = action;
|
||||||
const selectedSuffix = actionEntry === selectedAction ? ' selected' : '';
|
const selectedSuffix = action === selectedAction ? ' selected' : '';
|
||||||
const highlightedSuffix = actionEntry === highlightedAction ? ' highlighted' : '';
|
const highlightedSuffix = action === highlightedAction ? ' highlighted' : '';
|
||||||
return <div
|
return <div
|
||||||
className={'action-entry' + selectedSuffix + highlightedSuffix}
|
className={'action-entry' + selectedSuffix + highlightedSuffix}
|
||||||
key={actionId}
|
key={metadata.id}
|
||||||
onClick={() => onSelected(actionEntry)}
|
onClick={() => onSelected(action)}
|
||||||
onMouseEnter={() => onHighlighted(actionEntry)}
|
onMouseEnter={() => onHighlighted(action)}
|
||||||
onMouseLeave={() => (highlightedAction === actionEntry) && onHighlighted(undefined)}
|
onMouseLeave={() => (highlightedAction === action) && onHighlighted(undefined)}
|
||||||
>
|
>
|
||||||
<div className={'action-error codicon codicon-issues'} hidden={!metadata.error} />
|
<div className={'action-error codicon codicon-issues'} hidden={!metadata.error} />
|
||||||
<div className='action-title'>{metadata.apiName || metadata.method}</div>
|
<div className='action-title'>{metadata.apiName || metadata.method}</div>
|
||||||
|
|
|
||||||
|
|
@ -14,18 +14,18 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { ActionEntry } from '../../../server/trace/viewer/traceModel';
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import './logsTab.css';
|
import './logsTab.css';
|
||||||
|
import { ActionTraceEvent } from '../../../server/trace/common/traceEvents';
|
||||||
|
|
||||||
export const LogsTab: React.FunctionComponent<{
|
export const LogsTab: React.FunctionComponent<{
|
||||||
actionEntry: ActionEntry | undefined,
|
action: ActionTraceEvent | undefined,
|
||||||
}> = ({ actionEntry }) => {
|
}> = ({ action }) => {
|
||||||
let logs: string[] = [];
|
let logs: string[] = [];
|
||||||
if (actionEntry) {
|
if (action) {
|
||||||
logs = actionEntry.metadata.log || [];
|
logs = action.metadata.log || [];
|
||||||
if (actionEntry.metadata.error)
|
if (action.metadata.error)
|
||||||
logs = [actionEntry.metadata.error, ...logs];
|
logs = [action.metadata.error, ...logs];
|
||||||
}
|
}
|
||||||
return <div className='logs-tab'>{
|
return <div className='logs-tab'>{
|
||||||
logs.map((logLine, index) => {
|
logs.map((logLine, index) => {
|
||||||
|
|
|
||||||
|
|
@ -14,18 +14,25 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { ActionEntry } from '../../../server/trace/viewer/traceModel';
|
import { ActionTraceEvent } from '../../../server/trace/common/traceEvents';
|
||||||
|
import { ContextEntry } from '../../../server/trace/viewer/traceModel';
|
||||||
import './networkTab.css';
|
import './networkTab.css';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { NetworkResourceDetails } from './networkResourceDetails';
|
import { NetworkResourceDetails } from './networkResourceDetails';
|
||||||
|
import { ResourceSnapshot } from '../../../server/snapshot/snapshotTypes';
|
||||||
|
|
||||||
export const NetworkTab: React.FunctionComponent<{
|
export const NetworkTab: React.FunctionComponent<{
|
||||||
actionEntry: ActionEntry | undefined,
|
context: ContextEntry,
|
||||||
}> = ({ actionEntry }) => {
|
action: ActionTraceEvent | undefined,
|
||||||
|
nextAction: ActionTraceEvent | undefined,
|
||||||
|
}> = ({ context, action, nextAction }) => {
|
||||||
const [selected, setSelected] = React.useState(0);
|
const [selected, setSelected] = React.useState(0);
|
||||||
|
|
||||||
|
const resources: ResourceSnapshot[] = context.resources.filter(resource => {
|
||||||
|
return action && resource.timestamp > action.metadata.startTime && (!nextAction || resource.timestamp < nextAction.metadata.startTime);
|
||||||
|
});
|
||||||
return <div className='network-tab'>{
|
return <div className='network-tab'>{
|
||||||
(actionEntry ? actionEntry.resources : []).map((resource, index) => {
|
resources.map((resource, index) => {
|
||||||
return <NetworkResourceDetails resource={resource} key={index} index={index} selected={selected === index} setSelected={setSelected} />;
|
return <NetworkResourceDetails resource={resource} key={index} index={index} selected={selected === index} setSelected={setSelected} />;
|
||||||
})
|
})
|
||||||
}</div>;
|
}</div>;
|
||||||
|
|
|
||||||
|
|
@ -14,23 +14,23 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { ActionEntry } from '../../../server/trace/viewer/traceModel';
|
|
||||||
import { Size } from '../geometry';
|
import { Size } from '../geometry';
|
||||||
import './snapshotTab.css';
|
import './snapshotTab.css';
|
||||||
import './tabbedPane.css';
|
import './tabbedPane.css';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { useMeasure } from './helpers';
|
import { useMeasure } from './helpers';
|
||||||
import type { Point } from '../../../common/types';
|
import type { Point } from '../../../common/types';
|
||||||
|
import { ActionTraceEvent } from '../../../server/trace/common/traceEvents';
|
||||||
|
|
||||||
export const SnapshotTab: React.FunctionComponent<{
|
export const SnapshotTab: React.FunctionComponent<{
|
||||||
actionEntry: ActionEntry | undefined,
|
action: ActionTraceEvent | undefined,
|
||||||
snapshotSize: Size,
|
snapshotSize: Size,
|
||||||
}> = ({ actionEntry, snapshotSize }) => {
|
}> = ({ action, snapshotSize }) => {
|
||||||
const [measure, ref] = useMeasure<HTMLDivElement>();
|
const [measure, ref] = useMeasure<HTMLDivElement>();
|
||||||
let [snapshotIndex, setSnapshotIndex] = React.useState(0);
|
let [snapshotIndex, setSnapshotIndex] = React.useState(0);
|
||||||
|
|
||||||
const snapshotMap = new Map<string, { title: string, snapshotName: string }>();
|
const snapshotMap = new Map<string, { title: string, snapshotName: string }>();
|
||||||
for (const snapshot of actionEntry?.metadata.snapshots || [])
|
for (const snapshot of action?.metadata.snapshots || [])
|
||||||
snapshotMap.set(snapshot.title, snapshot);
|
snapshotMap.set(snapshot.title, snapshot);
|
||||||
const actionSnapshot = snapshotMap.get('action') || snapshotMap.get('after');
|
const actionSnapshot = snapshotMap.get('action') || snapshotMap.get('after');
|
||||||
const snapshots = [actionSnapshot ? { ...actionSnapshot, title: 'action' } : undefined, snapshotMap.get('before'), snapshotMap.get('after')].filter(Boolean) as { title: string, snapshotName: string }[];
|
const snapshots = [actionSnapshot ? { ...actionSnapshot, title: 'action' } : undefined, snapshotMap.get('before'), snapshotMap.get('after')].filter(Boolean) as { title: string, snapshotName: string }[];
|
||||||
|
|
@ -44,12 +44,12 @@ export const SnapshotTab: React.FunctionComponent<{
|
||||||
return;
|
return;
|
||||||
let snapshotUri = undefined;
|
let snapshotUri = undefined;
|
||||||
let point: Point | undefined = undefined;
|
let point: Point | undefined = undefined;
|
||||||
if (actionEntry) {
|
if (action) {
|
||||||
const snapshot = snapshots[snapshotIndex];
|
const snapshot = snapshots[snapshotIndex];
|
||||||
if (snapshot && snapshot.snapshotName) {
|
if (snapshot && snapshot.snapshotName) {
|
||||||
snapshotUri = `${actionEntry.metadata.pageId}?name=${snapshot.snapshotName}`;
|
snapshotUri = `${action.metadata.pageId}?name=${snapshot.snapshotName}`;
|
||||||
if (snapshot.snapshotName.includes('action'))
|
if (snapshot.snapshotName.includes('action'))
|
||||||
point = actionEntry.metadata.point;
|
point = action.metadata.point;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const snapshotUrl = snapshotUri ? `${window.location.origin}/snapshot/${snapshotUri}` : 'data:text/html,<body style="background: #ddd"></body>';
|
const snapshotUrl = snapshotUri ? `${window.location.origin}/snapshot/${snapshotUri}` : 'data:text/html,<body style="background: #ddd"></body>';
|
||||||
|
|
@ -57,7 +57,7 @@ export const SnapshotTab: React.FunctionComponent<{
|
||||||
(iframeRef.current.contentWindow as any).showSnapshot(snapshotUrl, { point });
|
(iframeRef.current.contentWindow as any).showSnapshot(snapshotUrl, { point });
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
}
|
}
|
||||||
}, [actionEntry, snapshotIndex]);
|
}, [action, snapshotIndex]);
|
||||||
|
|
||||||
const scale = Math.min(measure.width / snapshotSize.width, measure.height / snapshotSize.height);
|
const scale = Math.min(measure.width / snapshotSize.width, measure.height / snapshotSize.height);
|
||||||
const scaledSize = {
|
const scaledSize = {
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,6 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { ActionEntry } from '../../../server/trace/viewer/traceModel';
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { useAsyncMemo } from './helpers';
|
import { useAsyncMemo } from './helpers';
|
||||||
import './sourceTab.css';
|
import './sourceTab.css';
|
||||||
|
|
@ -23,6 +22,7 @@ import { StackFrame } from '../../../common/types';
|
||||||
import { Source as SourceView } from '../../components/source';
|
import { Source as SourceView } from '../../components/source';
|
||||||
import { StackTraceView } from './stackTrace';
|
import { StackTraceView } from './stackTrace';
|
||||||
import { SplitView } from '../../components/splitView';
|
import { SplitView } from '../../components/splitView';
|
||||||
|
import { ActionTraceEvent } from '../../../server/trace/common/traceEvents';
|
||||||
|
|
||||||
type StackInfo = string | {
|
type StackInfo = string | {
|
||||||
frames: StackFrame[];
|
frames: StackFrame[];
|
||||||
|
|
@ -30,22 +30,22 @@ type StackInfo = string | {
|
||||||
};
|
};
|
||||||
|
|
||||||
export const SourceTab: React.FunctionComponent<{
|
export const SourceTab: React.FunctionComponent<{
|
||||||
actionEntry: ActionEntry | undefined,
|
action: ActionTraceEvent | undefined,
|
||||||
}> = ({ actionEntry }) => {
|
}> = ({ action }) => {
|
||||||
const [lastAction, setLastAction] = React.useState<ActionEntry | undefined>();
|
const [lastAction, setLastAction] = React.useState<ActionTraceEvent | undefined>();
|
||||||
const [selectedFrame, setSelectedFrame] = React.useState<number>(0);
|
const [selectedFrame, setSelectedFrame] = React.useState<number>(0);
|
||||||
const [needReveal, setNeedReveal] = React.useState<boolean>(false);
|
const [needReveal, setNeedReveal] = React.useState<boolean>(false);
|
||||||
|
|
||||||
if (lastAction !== actionEntry) {
|
if (lastAction !== action) {
|
||||||
setLastAction(actionEntry);
|
setLastAction(action);
|
||||||
setSelectedFrame(0);
|
setSelectedFrame(0);
|
||||||
setNeedReveal(true);
|
setNeedReveal(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
const stackInfo = React.useMemo<StackInfo>(() => {
|
const stackInfo = React.useMemo<StackInfo>(() => {
|
||||||
if (!actionEntry)
|
if (!action)
|
||||||
return '';
|
return '';
|
||||||
const { metadata } = actionEntry;
|
const { metadata } = action;
|
||||||
if (!metadata.stack)
|
if (!metadata.stack)
|
||||||
return '';
|
return '';
|
||||||
const frames = metadata.stack;
|
const frames = metadata.stack;
|
||||||
|
|
@ -53,7 +53,7 @@ export const SourceTab: React.FunctionComponent<{
|
||||||
frames,
|
frames,
|
||||||
fileContent: new Map(),
|
fileContent: new Map(),
|
||||||
};
|
};
|
||||||
}, [actionEntry]);
|
}, [action]);
|
||||||
|
|
||||||
const content = useAsyncMemo<string>(async () => {
|
const content = useAsyncMemo<string>(async () => {
|
||||||
let value: string;
|
let value: string;
|
||||||
|
|
@ -80,6 +80,6 @@ export const SourceTab: React.FunctionComponent<{
|
||||||
|
|
||||||
return <SplitView sidebarSize={100} orientation='vertical'>
|
return <SplitView sidebarSize={100} orientation='vertical'>
|
||||||
<SourceView text={content} language='javascript' highlight={[{ line: targetLine, type: 'running' }]} revealLine={targetLine}></SourceView>
|
<SourceView text={content} language='javascript' highlight={[{ line: targetLine, type: 'running' }]} revealLine={targetLine}></SourceView>
|
||||||
<StackTraceView actionEntry={actionEntry} selectedFrame={selectedFrame} setSelectedFrame={setSelectedFrame}></StackTraceView>
|
<StackTraceView action={action} selectedFrame={selectedFrame} setSelectedFrame={setSelectedFrame}></StackTraceView>
|
||||||
</SplitView>;
|
</SplitView>;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -14,16 +14,16 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { ActionEntry } from '../../../server/trace/viewer/traceModel';
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import './stackTrace.css';
|
import './stackTrace.css';
|
||||||
|
import { ActionTraceEvent } from '../../../server/trace/common/traceEvents';
|
||||||
|
|
||||||
export const StackTraceView: React.FunctionComponent<{
|
export const StackTraceView: React.FunctionComponent<{
|
||||||
actionEntry: ActionEntry | undefined,
|
action: ActionTraceEvent | undefined,
|
||||||
selectedFrame: number,
|
selectedFrame: number,
|
||||||
setSelectedFrame: (index: number) => void
|
setSelectedFrame: (index: number) => void
|
||||||
}> = ({ actionEntry, setSelectedFrame, selectedFrame }) => {
|
}> = ({ action, setSelectedFrame, selectedFrame }) => {
|
||||||
const frames = actionEntry?.metadata.stack || [];
|
const frames = action?.metadata.stack || [];
|
||||||
return <div className='stack-trace'>{
|
return <div className='stack-trace'>{
|
||||||
frames.map((frame, index) => {
|
frames.map((frame, index) => {
|
||||||
return <div
|
return <div
|
||||||
|
|
|
||||||
|
|
@ -71,10 +71,9 @@
|
||||||
position: absolute;
|
position: absolute;
|
||||||
height: 9px;
|
height: 9px;
|
||||||
top: 11px;
|
top: 11px;
|
||||||
background-color: red;
|
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
min-width: 3px;
|
min-width: 3px;
|
||||||
--action-color: 'transparent';
|
--action-color: gray;
|
||||||
background-color: var(--action-color);
|
background-color: var(--action-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -83,46 +82,54 @@
|
||||||
box-shadow: 0 0 0 1px var(--action-color);
|
box-shadow: 0 0 0 1px var(--action-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.timeline-bar.click,
|
.timeline-bar.frame_click,
|
||||||
.timeline-bar.dblclick,
|
.timeline-bar.frame_dblclick,
|
||||||
.timeline-bar.hover,
|
.timeline-bar.frame_hover,
|
||||||
.timeline-bar.check,
|
.timeline-bar.frame_check,
|
||||||
.timeline-bar.uncheck,
|
.timeline-bar.frame_uncheck,
|
||||||
.timeline-bar.tap {
|
.timeline-bar.frame_tap {
|
||||||
--action-color: var(--green);
|
--action-color: var(--green);
|
||||||
}
|
}
|
||||||
|
|
||||||
.timeline-bar.fill,
|
.timeline-bar.page_load,
|
||||||
.timeline-bar.press,
|
.timeline-bar.page_domcontentloaded,
|
||||||
.timeline-bar.type,
|
.timeline-bar.frame_fill,
|
||||||
.timeline-bar.selectOption,
|
.timeline-bar.frame_press,
|
||||||
.timeline-bar.setInputFiles {
|
.timeline-bar.frame_type,
|
||||||
|
.timeline-bar.frame_selectoption,
|
||||||
|
.timeline-bar.frame_setinputfiles {
|
||||||
--action-color: var(--orange);
|
--action-color: var(--orange);
|
||||||
}
|
}
|
||||||
|
|
||||||
.timeline-bar.goto,
|
.timeline-bar.frame_loadstate {
|
||||||
.timeline-bar.setContent,
|
display: none;
|
||||||
.timeline-bar.goBack,
|
}
|
||||||
.timeline-bar.goForward,
|
|
||||||
|
.timeline-bar.frame_goto,
|
||||||
|
.timeline-bar.frame_setcontent,
|
||||||
|
.timeline-bar.frame_goback,
|
||||||
|
.timeline-bar.frame_goforward,
|
||||||
.timeline-bar.reload {
|
.timeline-bar.reload {
|
||||||
--action-color: var(--blue);
|
--action-color: var(--blue);
|
||||||
}
|
}
|
||||||
|
|
||||||
.timeline-bar.evaluateExpression {
|
.timeline-bar.frame_evaluateexpression {
|
||||||
--action-color: var(--yellow);
|
--action-color: var(--yellow);
|
||||||
}
|
}
|
||||||
|
|
||||||
.timeline-bar.dialog {
|
.timeline-bar.frame_dialog {
|
||||||
top: 22px;
|
|
||||||
--action-color: var(--transparent-blue);
|
--action-color: var(--transparent-blue);
|
||||||
}
|
}
|
||||||
|
|
||||||
.timeline-bar.navigation {
|
.timeline-bar.frame_navigated {
|
||||||
top: 22px;
|
|
||||||
--action-color: var(--blue);
|
--action-color: var(--blue);
|
||||||
}
|
}
|
||||||
|
|
||||||
.timeline-bar.waitForEventInfo {
|
.timeline-bar.event {
|
||||||
|
top: 22px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.timeline-bar.frame_waitforeventinfo {
|
||||||
bottom: inherit;
|
bottom: inherit;
|
||||||
top: 0;
|
top: 0;
|
||||||
--action-color: var(--gray);
|
--action-color: var(--gray);
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,8 @@
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { ContextEntry, InterestingPageEvent, ActionEntry, trace } from '../../../server/trace/viewer/traceModel';
|
import { ActionTraceEvent } from '../../../server/trace/common/traceEvents';
|
||||||
|
import { ContextEntry } from '../../../server/trace/viewer/traceModel';
|
||||||
import './timeline.css';
|
import './timeline.css';
|
||||||
import { Boundaries } from '../geometry';
|
import { Boundaries } from '../geometry';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
|
|
@ -24,24 +25,24 @@ import { msToString } from '../../uiUtils';
|
||||||
import { FilmStrip } from './filmStrip';
|
import { FilmStrip } from './filmStrip';
|
||||||
|
|
||||||
type TimelineBar = {
|
type TimelineBar = {
|
||||||
entry?: ActionEntry;
|
action?: ActionTraceEvent;
|
||||||
event?: InterestingPageEvent;
|
event?: ActionTraceEvent;
|
||||||
leftPosition: number;
|
leftPosition: number;
|
||||||
rightPosition: number;
|
rightPosition: number;
|
||||||
leftTime: number;
|
leftTime: number;
|
||||||
rightTime: number;
|
rightTime: number;
|
||||||
type: string;
|
type: string;
|
||||||
label: string;
|
label: string;
|
||||||
priority: number;
|
className: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const Timeline: React.FunctionComponent<{
|
export const Timeline: React.FunctionComponent<{
|
||||||
context: ContextEntry,
|
context: ContextEntry,
|
||||||
boundaries: Boundaries,
|
boundaries: Boundaries,
|
||||||
selectedAction: ActionEntry | undefined,
|
selectedAction: ActionTraceEvent | undefined,
|
||||||
highlightedAction: ActionEntry | undefined,
|
highlightedAction: ActionTraceEvent | undefined,
|
||||||
onSelected: (action: ActionEntry) => void,
|
onSelected: (action: ActionTraceEvent) => void,
|
||||||
onHighlighted: (action: ActionEntry | undefined) => void,
|
onHighlighted: (action: ActionTraceEvent | undefined) => void,
|
||||||
}> = ({ context, boundaries, selectedAction, highlightedAction, onSelected, onHighlighted }) => {
|
}> = ({ context, boundaries, selectedAction, highlightedAction, onSelected, onHighlighted }) => {
|
||||||
const [measure, ref] = useMeasure<HTMLDivElement>();
|
const [measure, ref] = useMeasure<HTMLDivElement>();
|
||||||
const [previewPoint, setPreviewPoint] = React.useState<{ x: number, clientY: number } | undefined>();
|
const [previewPoint, setPreviewPoint] = React.useState<{ x: number, clientY: number } | undefined>();
|
||||||
|
|
@ -61,64 +62,36 @@ export const Timeline: React.FunctionComponent<{
|
||||||
if (entry.metadata.method === 'goto')
|
if (entry.metadata.method === 'goto')
|
||||||
detail = entry.metadata.params.url || '';
|
detail = entry.metadata.params.url || '';
|
||||||
bars.push({
|
bars.push({
|
||||||
entry,
|
action: entry,
|
||||||
leftTime: entry.metadata.startTime,
|
leftTime: entry.metadata.startTime,
|
||||||
rightTime: entry.metadata.endTime,
|
rightTime: entry.metadata.endTime,
|
||||||
leftPosition: timeToPosition(measure.width, boundaries, entry.metadata.startTime),
|
leftPosition: timeToPosition(measure.width, boundaries, entry.metadata.startTime),
|
||||||
rightPosition: timeToPosition(measure.width, boundaries, entry.metadata.endTime),
|
rightPosition: timeToPosition(measure.width, boundaries, entry.metadata.endTime),
|
||||||
label: entry.metadata.apiName + ' ' + detail,
|
label: entry.metadata.apiName + ' ' + detail,
|
||||||
type: entry.metadata.method,
|
type: entry.metadata.type + '.' + entry.metadata.method,
|
||||||
priority: 0,
|
className: `${entry.metadata.type}_${entry.metadata.method}`.toLowerCase()
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
let lastDialogOpened: trace.DialogOpenedEvent | undefined;
|
|
||||||
for (const event of page.interestingEvents) {
|
for (const event of page.events) {
|
||||||
if (event.type === 'dialog-opened') {
|
const startTime = event.metadata.startTime;
|
||||||
lastDialogOpened = event;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (event.type === 'dialog-closed' && lastDialogOpened) {
|
|
||||||
bars.push({
|
bars.push({
|
||||||
event,
|
event,
|
||||||
leftTime: lastDialogOpened.timestamp,
|
leftTime: startTime,
|
||||||
rightTime: event.timestamp,
|
rightTime: startTime,
|
||||||
leftPosition: timeToPosition(measure.width, boundaries, lastDialogOpened.timestamp),
|
leftPosition: timeToPosition(measure.width, boundaries, startTime),
|
||||||
rightPosition: timeToPosition(measure.width, boundaries, event.timestamp),
|
rightPosition: timeToPosition(measure.width, boundaries, startTime),
|
||||||
label: lastDialogOpened.message ? `${event.dialogType} "${lastDialogOpened.message}"` : event.dialogType,
|
label: event.metadata.method,
|
||||||
type: 'dialog',
|
type: event.metadata.type + '.' + event.metadata.method,
|
||||||
priority: -1,
|
className: `${event.metadata.type}_${event.metadata.method}`.toLowerCase()
|
||||||
});
|
|
||||||
} else if (event.type === 'navigation') {
|
|
||||||
bars.push({
|
|
||||||
event,
|
|
||||||
leftTime: event.timestamp,
|
|
||||||
rightTime: event.timestamp,
|
|
||||||
leftPosition: timeToPosition(measure.width, boundaries, event.timestamp),
|
|
||||||
rightPosition: timeToPosition(measure.width, boundaries, event.timestamp),
|
|
||||||
label: `navigated to ${event.url}`,
|
|
||||||
type: event.type,
|
|
||||||
priority: 1,
|
|
||||||
});
|
|
||||||
} else if (event.type === 'load') {
|
|
||||||
bars.push({
|
|
||||||
event,
|
|
||||||
leftTime: event.timestamp,
|
|
||||||
rightTime: event.timestamp,
|
|
||||||
leftPosition: timeToPosition(measure.width, boundaries, event.timestamp),
|
|
||||||
rightPosition: timeToPosition(measure.width, boundaries, event.timestamp),
|
|
||||||
label: `load`,
|
|
||||||
type: event.type,
|
|
||||||
priority: 1,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
bars.sort((a, b) => a.priority - b.priority);
|
|
||||||
return bars;
|
return bars;
|
||||||
}, [context, boundaries, measure.width]);
|
}, [context, boundaries, measure.width]);
|
||||||
|
|
||||||
const hoveredBar = hoveredBarIndex !== undefined ? bars[hoveredBarIndex] : undefined;
|
const hoveredBar = hoveredBarIndex !== undefined ? bars[hoveredBarIndex] : undefined;
|
||||||
let targetBar: TimelineBar | undefined = bars.find(bar => bar.entry === (highlightedAction || selectedAction));
|
let targetBar: TimelineBar | undefined = bars.find(bar => bar.action === (highlightedAction || selectedAction));
|
||||||
targetBar = hoveredBar || targetBar;
|
targetBar = hoveredBar || targetBar;
|
||||||
|
|
||||||
const findHoveredBarIndex = (x: number) => {
|
const findHoveredBarIndex = (x: number) => {
|
||||||
|
|
@ -150,7 +123,7 @@ export const Timeline: React.FunctionComponent<{
|
||||||
setPreviewPoint({ x, clientY: event.clientY });
|
setPreviewPoint({ x, clientY: event.clientY });
|
||||||
setHoveredBarIndex(index);
|
setHoveredBarIndex(index);
|
||||||
if (typeof index === 'number')
|
if (typeof index === 'number')
|
||||||
onHighlighted(bars[index].entry);
|
onHighlighted(bars[index].action);
|
||||||
};
|
};
|
||||||
|
|
||||||
const onMouseLeave = () => {
|
const onMouseLeave = () => {
|
||||||
|
|
@ -167,7 +140,7 @@ export const Timeline: React.FunctionComponent<{
|
||||||
const index = findHoveredBarIndex(x);
|
const index = findHoveredBarIndex(x);
|
||||||
if (index === undefined)
|
if (index === undefined)
|
||||||
return;
|
return;
|
||||||
const entry = bars[index].entry;
|
const entry = bars[index].action;
|
||||||
if (entry)
|
if (entry)
|
||||||
onSelected(entry);
|
onSelected(entry);
|
||||||
};
|
};
|
||||||
|
|
@ -183,7 +156,7 @@ export const Timeline: React.FunctionComponent<{
|
||||||
<div className='timeline-lane timeline-labels'>{
|
<div className='timeline-lane timeline-labels'>{
|
||||||
bars.map((bar, index) => {
|
bars.map((bar, index) => {
|
||||||
return <div key={index}
|
return <div key={index}
|
||||||
className={'timeline-label ' + bar.type + (targetBar === bar ? ' selected' : '')}
|
className={'timeline-label ' + bar.className + (targetBar === bar ? ' selected' : '')}
|
||||||
style={{
|
style={{
|
||||||
left: bar.leftPosition + 'px',
|
left: bar.leftPosition + 'px',
|
||||||
width: Math.max(1, bar.rightPosition - bar.leftPosition) + 'px',
|
width: Math.max(1, bar.rightPosition - bar.leftPosition) + 'px',
|
||||||
|
|
@ -196,7 +169,7 @@ export const Timeline: React.FunctionComponent<{
|
||||||
<div className='timeline-lane timeline-bars'>{
|
<div className='timeline-lane timeline-bars'>{
|
||||||
bars.map((bar, index) => {
|
bars.map((bar, index) => {
|
||||||
return <div key={index}
|
return <div key={index}
|
||||||
className={'timeline-bar ' + bar.type + (targetBar === bar ? ' selected' : '')}
|
className={'timeline-bar ' + (bar.action ? 'action ' : '') + (bar.event ? 'event ' : '') + bar.className + (targetBar === bar ? ' selected' : '')}
|
||||||
style={{
|
style={{
|
||||||
left: bar.leftPosition + 'px',
|
left: bar.leftPosition + 'px',
|
||||||
width: Math.max(1, bar.rightPosition - bar.leftPosition) + 'px',
|
width: Math.max(1, bar.rightPosition - bar.leftPosition) + 'px',
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,8 @@
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { ActionEntry, ContextEntry } from '../../../server/trace/viewer/traceModel';
|
import { ActionTraceEvent } from '../../../server/trace/common/traceEvents';
|
||||||
|
import { ContextEntry } from '../../../server/trace/viewer/traceModel';
|
||||||
import { ActionList } from './actionList';
|
import { ActionList } from './actionList';
|
||||||
import { TabbedPane } from './tabbedPane';
|
import { TabbedPane } from './tabbedPane';
|
||||||
import { Timeline } from './timeline';
|
import { Timeline } from './timeline';
|
||||||
|
|
@ -33,20 +34,21 @@ export const Workbench: React.FunctionComponent<{
|
||||||
debugNames: string[],
|
debugNames: string[],
|
||||||
}> = ({ debugNames }) => {
|
}> = ({ debugNames }) => {
|
||||||
const [debugName, setDebugName] = React.useState(debugNames[0]);
|
const [debugName, setDebugName] = React.useState(debugNames[0]);
|
||||||
const [selectedAction, setSelectedAction] = React.useState<ActionEntry | undefined>();
|
const [selectedAction, setSelectedAction] = React.useState<ActionTraceEvent | undefined>();
|
||||||
const [highlightedAction, setHighlightedAction] = React.useState<ActionEntry | undefined>();
|
const [highlightedAction, setHighlightedAction] = React.useState<ActionTraceEvent | undefined>();
|
||||||
|
|
||||||
let context = useAsyncMemo(async () => {
|
let context = useAsyncMemo(async () => {
|
||||||
return (await fetch(`/context/${debugName}`).then(response => response.json())) as ContextEntry;
|
return (await fetch(`/context/${debugName}`).then(response => response.json())) as ContextEntry;
|
||||||
}, [debugName], emptyContext);
|
}, [debugName], emptyContext);
|
||||||
|
|
||||||
const actions = React.useMemo(() => {
|
const { actions, nextAction } = React.useMemo(() => {
|
||||||
const actions: ActionEntry[] = [];
|
const actions: ActionTraceEvent[] = [];
|
||||||
for (const page of context.pages)
|
for (const page of context.pages)
|
||||||
actions.push(...page.actions);
|
actions.push(...page.actions);
|
||||||
actions.sort((a, b) => a.timestamp - b.timestamp);
|
actions.sort((a, b) => a.timestamp - b.timestamp);
|
||||||
return actions;
|
const nextAction = selectedAction ? actions[actions.indexOf(selectedAction) + 1] : undefined;
|
||||||
}, [context]);
|
return { actions, nextAction };
|
||||||
|
}, [context, selectedAction]);
|
||||||
|
|
||||||
const snapshotSize = context.created.viewportSize || { width: 1280, height: 720 };
|
const snapshotSize = context.created.viewportSize || { width: 1280, height: 720 };
|
||||||
const boundaries = { minimum: context.startTime, maximum: context.endTime };
|
const boundaries = { minimum: context.startTime, maximum: context.endTime };
|
||||||
|
|
@ -77,11 +79,11 @@ export const Workbench: React.FunctionComponent<{
|
||||||
</div>
|
</div>
|
||||||
<SplitView sidebarSize={300} orientation='horizontal' sidebarIsFirst={true}>
|
<SplitView sidebarSize={300} orientation='horizontal' sidebarIsFirst={true}>
|
||||||
<SplitView sidebarSize={300} orientation='horizontal'>
|
<SplitView sidebarSize={300} orientation='horizontal'>
|
||||||
<SnapshotTab actionEntry={selectedAction} snapshotSize={snapshotSize} />
|
<SnapshotTab action={selectedAction} snapshotSize={snapshotSize} />
|
||||||
<TabbedPane tabs={[
|
<TabbedPane tabs={[
|
||||||
{ id: 'logs', title: 'Log', render: () => <LogsTab actionEntry={selectedAction} /> },
|
{ id: 'logs', title: 'Log', render: () => <LogsTab action={selectedAction} /> },
|
||||||
{ id: 'source', title: 'Source', render: () => <SourceTab actionEntry={selectedAction} /> },
|
{ id: 'source', title: 'Source', render: () => <SourceTab action={selectedAction} /> },
|
||||||
{ id: 'network', title: 'Network', render: () => <NetworkTab actionEntry={selectedAction} /> },
|
{ id: 'network', title: 'Network', render: () => <NetworkTab context={context} action={selectedAction} nextAction={nextAction}/> },
|
||||||
]}/>
|
]}/>
|
||||||
</SplitView>
|
</SplitView>
|
||||||
<ActionList
|
<ActionList
|
||||||
|
|
@ -110,5 +112,6 @@ const emptyContext: ContextEntry = {
|
||||||
viewportSize: { width: 1280, height: 800 },
|
viewportSize: { width: 1280, height: 800 },
|
||||||
debugName: '<empty>',
|
debugName: '<empty>',
|
||||||
},
|
},
|
||||||
pages: []
|
pages: [],
|
||||||
|
resources: []
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -147,7 +147,7 @@ DEPS['src/cli/driver.ts'] = DEPS['src/inprocess.ts'] = DEPS['src/browserServerIm
|
||||||
// Tracing is a client/server plugin, nothing should depend on it.
|
// Tracing is a client/server plugin, nothing should depend on it.
|
||||||
DEPS['src/web/recorder/'] = ['src/common/', 'src/web/', 'src/web/components/', 'src/server/supplements/recorder/recorderTypes.ts'];
|
DEPS['src/web/recorder/'] = ['src/common/', 'src/web/', 'src/web/components/', 'src/server/supplements/recorder/recorderTypes.ts'];
|
||||||
DEPS['src/web/traceViewer/'] = ['src/common/', 'src/web/'];
|
DEPS['src/web/traceViewer/'] = ['src/common/', 'src/web/'];
|
||||||
DEPS['src/web/traceViewer/ui/'] = ['src/common/', 'src/web/traceViewer/', 'src/web/', 'src/server/trace/viewer/', 'src/server/trace/', 'src/server/trace/common/', 'src/server/snapshot/snapshotTypes.ts'];
|
DEPS['src/web/traceViewer/ui/'] = ['src/common/', 'src/web/traceViewer/', 'src/web/', 'src/server/trace/viewer/', 'src/server/trace/', 'src/server/trace/common/', 'src/server/snapshot/snapshotTypes.ts', 'src/protocol/channels.ts'];
|
||||||
// The service is a cross-cutting feature, and so it depends on a bunch of things.
|
// The service is a cross-cutting feature, and so it depends on a bunch of things.
|
||||||
DEPS['src/remote/'] = ['src/client/', 'src/debug/', 'src/dispatchers/', 'src/server/', 'src/server/supplements/', 'src/server/electron/', 'src/server/trace/'];
|
DEPS['src/remote/'] = ['src/client/', 'src/debug/', 'src/dispatchers/', 'src/server/', 'src/server/supplements/', 'src/server/electron/', 'src/server/trace/'];
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue