chore(trace-viewer): improve progress indicator when loading trace (#36678)
Co-authored-by: Simon Knott <info@simonknott.de>
This commit is contained in:
parent
1592353d47
commit
248d29ed78
|
@ -20,7 +20,8 @@ export interface DialogProps {
|
|||
className?: string;
|
||||
style?: React.CSSProperties;
|
||||
open: boolean;
|
||||
width: number;
|
||||
isModal?: boolean;
|
||||
width?: number;
|
||||
verticalOffset?: number;
|
||||
requestClose?: () => void;
|
||||
anchor?: React.RefObject<HTMLElement>;
|
||||
|
@ -31,6 +32,7 @@ export const Dialog: React.FC<React.PropsWithChildren<DialogProps>> = ({
|
|||
className,
|
||||
style: externalStyle,
|
||||
open,
|
||||
isModal,
|
||||
width,
|
||||
verticalOffset,
|
||||
requestClose,
|
||||
|
@ -52,7 +54,7 @@ export const Dialog: React.FC<React.PropsWithChildren<DialogProps>> = ({
|
|||
position: 'fixed',
|
||||
margin: 0,
|
||||
top: bounds.bottom + (verticalOffset ?? 0),
|
||||
left: buildTopLeftCoord(bounds, width),
|
||||
left: buildTopLeftCoord(bounds, width ?? 0),
|
||||
width,
|
||||
zIndex: 1,
|
||||
...externalStyle
|
||||
|
@ -96,12 +98,24 @@ export const Dialog: React.FC<React.PropsWithChildren<DialogProps>> = ({
|
|||
};
|
||||
}, []);
|
||||
|
||||
React.useLayoutEffect(() => {
|
||||
if (!dialogRef.current)
|
||||
return;
|
||||
|
||||
if (open) {
|
||||
if (isModal)
|
||||
dialogRef.current.showModal();
|
||||
else
|
||||
dialogRef.current.show();
|
||||
} else {
|
||||
dialogRef.current.close();
|
||||
}
|
||||
}, [open, isModal]);
|
||||
|
||||
return (
|
||||
open && (
|
||||
<dialog ref={dialogRef} style={style} className={className} data-testid={dataTestId} open>
|
||||
{children}
|
||||
</dialog>
|
||||
)
|
||||
<dialog ref={dialogRef} style={style} className={className} data-testid={dataTestId}>
|
||||
{children}
|
||||
</dialog>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -65,17 +65,40 @@ body.dark-mode .drop-target {
|
|||
cursor: pointer;
|
||||
}
|
||||
|
||||
.progress {
|
||||
flex: none;
|
||||
.progress-dialog {
|
||||
width: 400px;
|
||||
inset: 0;
|
||||
border: none;
|
||||
outline: none;
|
||||
background-color: var(--vscode-sideBar-background);
|
||||
}
|
||||
|
||||
.progress-dialog::backdrop {
|
||||
background-color: rgba(0, 0, 0, 0.4);
|
||||
}
|
||||
|
||||
.progress-content {
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.progress-content .title {
|
||||
/* This is set in common.css */
|
||||
background-color: unset;
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.progress-wrapper {
|
||||
background-color: var(--vscode-commandCenter-activeBackground);
|
||||
width: 100%;
|
||||
height: 3px;
|
||||
margin-top: -3px;
|
||||
z-index: 10;
|
||||
margin-top: 16px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.inner-progress {
|
||||
background-color: var(--vscode-progressBar-background);
|
||||
height: 100%;
|
||||
height: 4px;
|
||||
}
|
||||
|
||||
.header {
|
||||
|
|
|
@ -21,6 +21,7 @@ import './workbenchLoader.css';
|
|||
import { Workbench } from './workbench';
|
||||
import { TestServerConnection, WebSocketTestServerTransport } from '@testIsomorphic/testServerConnection';
|
||||
import { SettingsToolbarButton } from './settingsToolbarButton';
|
||||
import { Dialog } from './shared/dialog';
|
||||
|
||||
export const WorkbenchLoader: React.FunctionComponent<{
|
||||
}> = () => {
|
||||
|
@ -32,6 +33,7 @@ export const WorkbenchLoader: React.FunctionComponent<{
|
|||
const [dragOver, setDragOver] = React.useState<boolean>(false);
|
||||
const [processingErrorMessage, setProcessingErrorMessage] = React.useState<string | null>(null);
|
||||
const [fileForLocalModeError, setFileForLocalModeError] = React.useState<string | null>(null);
|
||||
const [showProgressDialog, setShowProgressDialog] = React.useState<boolean>(false);
|
||||
|
||||
const processTraceFiles = React.useCallback((files: FileList) => {
|
||||
const blobUrls = [];
|
||||
|
@ -167,6 +169,20 @@ export const WorkbenchLoader: React.FunctionComponent<{
|
|||
})();
|
||||
}, [isServer, traceURLs, uploadedTraceNames]);
|
||||
|
||||
const showLoading = progress.done !== progress.total && progress.total !== 0;
|
||||
|
||||
React.useEffect(() => {
|
||||
if (showLoading) {
|
||||
const timeout = setTimeout(() => {
|
||||
setShowProgressDialog(true);
|
||||
}, 200);
|
||||
|
||||
return () => clearTimeout(timeout);
|
||||
} else {
|
||||
setShowProgressDialog(false);
|
||||
}
|
||||
}, [showLoading]);
|
||||
|
||||
const showFileUploadDropArea = !!(!isServer && !dragOver && !fileForLocalModeError && (!traceURLs.length || processingErrorMessage));
|
||||
|
||||
return <div className='vbox workbench-loader' onDragOver={event => { event.preventDefault(); setDragOver(true); }}>
|
||||
|
@ -179,9 +195,6 @@ export const WorkbenchLoader: React.FunctionComponent<{
|
|||
<div className='spacer'></div>
|
||||
<SettingsToolbarButton />
|
||||
</div>
|
||||
<div className='progress'>
|
||||
<div className='inner-progress' style={{ width: progress.total ? (100 * progress.done / progress.total) + '%' : 0 }}></div>
|
||||
</div>
|
||||
<Workbench model={model} inert={showFileUploadDropArea} />
|
||||
{fileForLocalModeError && <div className='drop-target'>
|
||||
<div>Trace Viewer uses Service Workers to show traces. To view trace:</div>
|
||||
|
@ -191,6 +204,14 @@ export const WorkbenchLoader: React.FunctionComponent<{
|
|||
<div>3. Drop the trace from the download shelf into the page</div>
|
||||
</div>
|
||||
</div>}
|
||||
<Dialog open={showProgressDialog} isModal={true} className='progress-dialog'>
|
||||
<div className='progress-content'>
|
||||
<div className='title' role='heading' aria-level={1}>Loading Playwright Trace...</div>
|
||||
<div className='progress-wrapper'>
|
||||
<div className='inner-progress' style={{ width: progress.total ? (100 * progress.done / progress.total) + '%' : 0 }}></div>
|
||||
</div>
|
||||
</div>
|
||||
</Dialog>
|
||||
{showFileUploadDropArea && <div className='drop-target'>
|
||||
<div className='processing-error' role='alert'>{processingErrorMessage}</div>
|
||||
<div className='title' role='heading' aria-level={1}>Drop Playwright Trace to load</div>
|
||||
|
|
|
@ -21,6 +21,7 @@ import type { TraceViewerFixtures } from '../config/traceViewerFixtures';
|
|||
import { traceViewerFixtures } from '../config/traceViewerFixtures';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import type http from 'http';
|
||||
import { pathToFileURL } from 'url';
|
||||
import { expect, playwrightTest } from '../config/browserTest';
|
||||
import type { FrameLocator } from '@playwright/test';
|
||||
|
@ -1914,3 +1915,27 @@ test('should render locator descriptions', async ({ runAndTrace, page }) => {
|
|||
- treeitem /Click.*input.*first/
|
||||
`);
|
||||
});
|
||||
|
||||
test('should load trace from HTTP with progress indicator', async ({ showTraceViewer, server }) => {
|
||||
const [traceViewer, res] = await Promise.all([
|
||||
showTraceViewer([server.PREFIX]),
|
||||
new Promise<http.ServerResponse>(resolve => {
|
||||
server.setRoute('/', (req, res) => resolve(res));
|
||||
}),
|
||||
]);
|
||||
|
||||
const file = await fs.promises.readFile(traceFile);
|
||||
|
||||
const dialog = traceViewer.page.locator('dialog', { hasText: 'Loading' });
|
||||
|
||||
res.setHeader('Access-Control-Allow-Origin', '*');
|
||||
res.setHeader('Content-Length', file.byteLength);
|
||||
res.writeHead(200);
|
||||
await expect(dialog).not.toBeVisible({ timeout: 100 });
|
||||
// Should become visible after ~200ms
|
||||
await expect(dialog).toBeVisible();
|
||||
|
||||
res.end(file);
|
||||
await expect(dialog).not.toBeVisible();
|
||||
await expect(traceViewer.actionTitles).toContainText([/Create page/]);
|
||||
});
|
||||
|
|
|
@ -620,7 +620,8 @@ for (const useIntermediateMergeReport of [true, false] as const) {
|
|||
await showReport();
|
||||
await page.getByRole('link', { name: 'passes' }).click();
|
||||
await page.click('img');
|
||||
await expect(page.locator('.workbench-loader .title')).toHaveText('a.test.js:3 › passes');
|
||||
await expect(page.locator('.progress-dialog')).toBeHidden();
|
||||
await expect(page.locator('.workbench-loader > .header > .title')).toHaveText('a.test.js:3 › passes');
|
||||
});
|
||||
|
||||
test('should show multi trace source', async ({ runInlineTest, page, server, showReport }) => {
|
||||
|
|
Loading…
Reference in New Issue