chore(trace): improve loading progress bar (#22201)

Fixes: https://github.com/microsoft/playwright/issues/22118
This commit is contained in:
Pavel Feldman 2023-04-05 13:03:25 -07:00 committed by GitHub
parent bc1de5f28d
commit 0d31d69d65
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 30 additions and 9 deletions

View file

@ -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);
} }

View file

@ -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 {

View file

@ -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%;
} }

View file

@ -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 }}>