chore(trace): improve loading progress bar (#22201)
Fixes: https://github.com/microsoft/playwright/issues/22118
This commit is contained in:
parent
bc1de5f28d
commit
0d31d69d65
|
|
@ -26,6 +26,18 @@ import { SnapshotStorage } from './snapshotStorage';
|
||||||
|
|
||||||
const zipjs = zipImport as typeof zip;
|
const zipjs = zipImport as typeof zip;
|
||||||
|
|
||||||
|
type Progress = (done: number, total: number) => void;
|
||||||
|
|
||||||
|
const splitProgress = (progress: Progress, weights: number[]): Progress[] => {
|
||||||
|
const doneList = new Array(weights.length).fill(0);
|
||||||
|
return new Array(weights.length).fill(0).map((_, i) => {
|
||||||
|
return (done: number, total: number) => {
|
||||||
|
doneList[i] = done / total * weights[i] * 1000;
|
||||||
|
progress(doneList.reduce((a, b) => a + b, 0), 1000);
|
||||||
|
};
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
export class TraceModel {
|
export class TraceModel {
|
||||||
contextEntries: ContextEntry[] = [];
|
contextEntries: ContextEntry[] = [];
|
||||||
pageEntries = new Map<string, PageEntry>();
|
pageEntries = new Map<string, PageEntry>();
|
||||||
|
|
@ -38,7 +50,9 @@ export class TraceModel {
|
||||||
|
|
||||||
async load(traceURL: string, progress: (done: number, total: number) => void) {
|
async load(traceURL: string, progress: (done: number, total: number) => void) {
|
||||||
const isLive = traceURL.endsWith('json');
|
const isLive = traceURL.endsWith('json');
|
||||||
this._backend = isLive ? new FetchTraceModelBackend(traceURL) : new ZipTraceModelBackend(traceURL, progress);
|
// Allow 10% to hop from sw to page.
|
||||||
|
const [fetchProgress, unzipProgress] = splitProgress(progress, [0.5, 0.4, 0.1]);
|
||||||
|
this._backend = isLive ? new FetchTraceModelBackend(traceURL) : new ZipTraceModelBackend(traceURL, fetchProgress);
|
||||||
|
|
||||||
const ordinals: string[] = [];
|
const ordinals: string[] = [];
|
||||||
let hasSource = false;
|
let hasSource = false;
|
||||||
|
|
@ -54,6 +68,9 @@ export class TraceModel {
|
||||||
|
|
||||||
this._snapshotStorage = new SnapshotStorage();
|
this._snapshotStorage = new SnapshotStorage();
|
||||||
|
|
||||||
|
// 3 * ordinals progress increments below.
|
||||||
|
const total = ordinals.length * 3;
|
||||||
|
let done = 0;
|
||||||
for (const ordinal of ordinals) {
|
for (const ordinal of ordinals) {
|
||||||
const contextEntry = createEmptyContext();
|
const contextEntry = createEmptyContext();
|
||||||
const actionMap = new Map<string, trace.ActionTraceEvent>();
|
const actionMap = new Map<string, trace.ActionTraceEvent>();
|
||||||
|
|
@ -63,10 +80,12 @@ export class TraceModel {
|
||||||
const trace = await this._backend.readText(ordinal + 'trace.trace') || '';
|
const trace = await this._backend.readText(ordinal + 'trace.trace') || '';
|
||||||
for (const line of trace.split('\n'))
|
for (const line of trace.split('\n'))
|
||||||
this.appendEvent(contextEntry, actionMap, line);
|
this.appendEvent(contextEntry, actionMap, line);
|
||||||
|
unzipProgress(++done, total);
|
||||||
|
|
||||||
const network = await this._backend.readText(ordinal + 'trace.network') || '';
|
const network = await this._backend.readText(ordinal + 'trace.network') || '';
|
||||||
for (const line of network.split('\n'))
|
for (const line of network.split('\n'))
|
||||||
this.appendEvent(contextEntry, actionMap, line);
|
this.appendEvent(contextEntry, actionMap, line);
|
||||||
|
unzipProgress(++done, total);
|
||||||
|
|
||||||
contextEntry.actions = [...actionMap.values()].sort((a1, a2) => a1.startTime - a2.startTime);
|
contextEntry.actions = [...actionMap.values()].sort((a1, a2) => a1.startTime - a2.startTime);
|
||||||
if (!isLive) {
|
if (!isLive) {
|
||||||
|
|
@ -82,6 +101,7 @@ export class TraceModel {
|
||||||
for (const action of contextEntry.actions)
|
for (const action of contextEntry.actions)
|
||||||
action.stack = action.stack || callMetadata.get(action.callId);
|
action.stack = action.stack || callMetadata.get(action.callId);
|
||||||
}
|
}
|
||||||
|
unzipProgress(++done, total);
|
||||||
|
|
||||||
this.contextEntries.push(contextEntry);
|
this.contextEntries.push(contextEntry);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -90,6 +90,8 @@ function renderProperty(property: Property, key: string) {
|
||||||
|
|
||||||
function propertyToString(event: ActionTraceEvent, name: string, value: any, sdkLanguage: Language | undefined): Property {
|
function propertyToString(event: ActionTraceEvent, name: string, value: any, sdkLanguage: Language | undefined): Property {
|
||||||
const isEval = event.method.includes('eval') || event.method === 'waitForFunction';
|
const isEval = event.method.includes('eval') || event.method === 'waitForFunction';
|
||||||
|
if (name === 'files')
|
||||||
|
return { text: '<files>', type: 'string', name };
|
||||||
if (name === 'eventInit' || name === 'expectedValue' || (name === 'arg' && isEval))
|
if (name === 'eventInit' || name === 'expectedValue' || (name === 'arg' && isEval))
|
||||||
value = parseSerializedValue(value.value, new Array(10).fill({ handle: '<handle>' }));
|
value = parseSerializedValue(value.value, new Array(10).fill({ handle: '<handle>' }));
|
||||||
if ((name === 'value' && isEval) || (name === 'received' && event.method === 'expect'))
|
if ((name === 'value' && isEval) || (name === 'received' && event.method === 'expect'))
|
||||||
|
|
@ -101,7 +103,7 @@ function propertyToString(event: ActionTraceEvent, name: string, value: any, sdk
|
||||||
return { text: String(value), type, name };
|
return { text: String(value), type, name };
|
||||||
if (value.guid)
|
if (value.guid)
|
||||||
return { text: '<handle>', type: 'handle', name };
|
return { text: '<handle>', type: 'handle', name };
|
||||||
return { text: JSON.stringify(value), type: 'object', name };
|
return { text: JSON.stringify(value).slice(0, 1000), type: 'object', name };
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseSerializedValue(value: SerializedValue, handles: any[] | undefined): any {
|
function parseSerializedValue(value: SerializedValue, handles: any[] | undefined): any {
|
||||||
|
|
|
||||||
|
|
@ -59,15 +59,14 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.progress {
|
.progress {
|
||||||
position: relative;
|
width: 100%;
|
||||||
margin: auto;
|
height: 3px;
|
||||||
width: 400px;
|
margin-top: -3px;
|
||||||
height: 10px;
|
z-index: 10;
|
||||||
border: 1px solid #aaa;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.inner-progress {
|
.inner-progress {
|
||||||
background-color: #aaa;
|
background-color: var(--vscode-progressBar-background);
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -125,10 +125,10 @@ export const WorkbenchLoader: React.FunctionComponent<{
|
||||||
<div className='spacer'></div>
|
<div className='spacer'></div>
|
||||||
<ToolbarButton icon='color-mode' title='Toggle color mode' toggled={false} onClick={() => toggleTheme()}></ToolbarButton>
|
<ToolbarButton icon='color-mode' title='Toggle color mode' toggled={false} onClick={() => toggleTheme()}></ToolbarButton>
|
||||||
</div>
|
</div>
|
||||||
<Workbench model={model} />
|
|
||||||
{!!progress.total && <div className='progress'>
|
{!!progress.total && <div className='progress'>
|
||||||
<div className='inner-progress' style={{ width: (100 * progress.done / progress.total) + '%' }}></div>
|
<div className='inner-progress' style={{ width: (100 * progress.done / progress.total) + '%' }}></div>
|
||||||
</div>}
|
</div>}
|
||||||
|
<Workbench model={model} />
|
||||||
{fileForLocalModeError && <div className='drop-target'>
|
{fileForLocalModeError && <div className='drop-target'>
|
||||||
<div>Trace Viewer uses Service Workers to show traces. To view trace:</div>
|
<div>Trace Viewer uses Service Workers to show traces. To view trace:</div>
|
||||||
<div style={{ paddingTop: 20 }}>
|
<div style={{ paddingTop: 20 }}>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue