feat(trace-viewer): render wall time for each action (#9982)
This commit is contained in:
parent
ad4632935f
commit
6a30c90590
|
|
@ -154,6 +154,7 @@ export class DispatcherConnection {
|
||||||
objectId: sdkObject?.guid,
|
objectId: sdkObject?.guid,
|
||||||
pageId: sdkObject?.attribution?.page?.guid,
|
pageId: sdkObject?.attribution?.page?.guid,
|
||||||
frameId: sdkObject?.attribution?.frame?.guid,
|
frameId: sdkObject?.attribution?.frame?.guid,
|
||||||
|
wallTime: Date.now(),
|
||||||
startTime: monotonicTime(),
|
startTime: monotonicTime(),
|
||||||
endTime: 0,
|
endTime: 0,
|
||||||
type,
|
type,
|
||||||
|
|
@ -228,6 +229,7 @@ export class DispatcherConnection {
|
||||||
objectId: sdkObject?.guid,
|
objectId: sdkObject?.guid,
|
||||||
pageId: sdkObject?.attribution?.page?.guid,
|
pageId: sdkObject?.attribution?.page?.guid,
|
||||||
frameId: sdkObject?.attribution?.frame?.guid,
|
frameId: sdkObject?.attribution?.frame?.guid,
|
||||||
|
wallTime: Date.now(),
|
||||||
startTime: monotonicTime(),
|
startTime: monotonicTime(),
|
||||||
endTime: 0,
|
endTime: 0,
|
||||||
type: dispatcher._type,
|
type: dispatcher._type,
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ import { Point, StackFrame, SerializedError } from './channels';
|
||||||
|
|
||||||
export type CallMetadata = {
|
export type CallMetadata = {
|
||||||
id: string;
|
id: string;
|
||||||
|
wallTime: number;
|
||||||
startTime: number;
|
startTime: number;
|
||||||
endTime: number;
|
endTime: number;
|
||||||
pauseStartTime?: number;
|
pauseStartTime?: number;
|
||||||
|
|
|
||||||
|
|
@ -88,6 +88,7 @@ export function createInstrumentation(): Instrumentation {
|
||||||
export function internalCallMetadata(): CallMetadata {
|
export function internalCallMetadata(): CallMetadata {
|
||||||
return {
|
return {
|
||||||
id: '',
|
id: '',
|
||||||
|
wallTime: 0,
|
||||||
startTime: 0,
|
startTime: 0,
|
||||||
endTime: 0,
|
endTime: 0,
|
||||||
type: 'Internal',
|
type: 'Internal',
|
||||||
|
|
|
||||||
|
|
@ -440,6 +440,7 @@ class ContextRecorder extends EventEmitter {
|
||||||
objectId: frame.guid,
|
objectId: frame.guid,
|
||||||
pageId: frame._page.guid,
|
pageId: frame._page.guid,
|
||||||
frameId: frame.guid,
|
frameId: frame.guid,
|
||||||
|
wallTime: Date.now(),
|
||||||
startTime: monotonicTime(),
|
startTime: monotonicTime(),
|
||||||
endTime: 0,
|
endTime: 0,
|
||||||
type: 'Frame',
|
type: 'Frame',
|
||||||
|
|
|
||||||
|
|
@ -65,7 +65,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.action-duration {
|
.action-duration {
|
||||||
margin-left: 4px;
|
margin: 0 4px;
|
||||||
color: var(--gray);
|
color: var(--gray);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -90,8 +90,8 @@ export const ActionList: React.FC<ActionListProps> = ({
|
||||||
<span>{metadata.apiName}</span>
|
<span>{metadata.apiName}</span>
|
||||||
{metadata.params.selector && <div className='action-selector' title={metadata.params.selector}>{metadata.params.selector}</div>}
|
{metadata.params.selector && <div className='action-selector' title={metadata.params.selector}>{metadata.params.selector}</div>}
|
||||||
{metadata.method === 'goto' && metadata.params.url && <div className='action-url' title={metadata.params.url}>{metadata.params.url}</div>}
|
{metadata.method === 'goto' && metadata.params.url && <div className='action-url' title={metadata.params.url}>{metadata.params.url}</div>}
|
||||||
<span className='action-duration'>— {metadata.endTime ? msToString(metadata.endTime - metadata.startTime) : 'Timed Out'}</span>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div className='action-duration' style={{ flex: 'none' }}>{metadata.endTime ? msToString(metadata.endTime - metadata.startTime) : 'Timed Out'}</div>
|
||||||
<div className='action-icons' onClick={() => setSelectedTab('console')}>
|
<div className='action-icons' onClick={() => setSelectedTab('console')}>
|
||||||
{!!errors && <div className='action-icon'><span className={'codicon codicon-error'}></span><span className="action-icon-value">{errors}</span></div>}
|
{!!errors && <div className='action-icon'><span className={'codicon codicon-error'}></span><span className="action-icon-value">{errors}</span></div>}
|
||||||
{!!warnings && <div className='action-icon'><span className={'codicon codicon-warning'}></span><span className="action-icon-value">{warnings}</span></div>}
|
{!!warnings && <div className='action-icon'><span className={'codicon codicon-warning'}></span><span className="action-icon-value">{warnings}</span></div>}
|
||||||
|
|
|
||||||
|
|
@ -54,10 +54,7 @@
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.call-duration {
|
.call-line .datetime,
|
||||||
color: var(--gray);
|
|
||||||
}
|
|
||||||
|
|
||||||
.call-line .string {
|
.call-line .string {
|
||||||
color: var(--orange);
|
color: var(--orange);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -32,12 +32,19 @@ export const CallTab: React.FunctionComponent<{
|
||||||
// Strip down the waitForEventInfo data, we never need it.
|
// Strip down the waitForEventInfo data, we never need it.
|
||||||
delete params.info;
|
delete params.info;
|
||||||
const paramKeys = Object.keys(params);
|
const paramKeys = Object.keys(params);
|
||||||
|
const wallTime = new Date(action.metadata.wallTime).toLocaleString();
|
||||||
|
const duration = msToString(action.metadata.endTime - action.metadata.startTime);
|
||||||
return <div className='call-tab'>
|
return <div className='call-tab'>
|
||||||
<div className='call-error' key='error' hidden={!error}>
|
<div className='call-error' key='error' hidden={!error}>
|
||||||
<div className='codicon codicon-issues'/>
|
<div className='codicon codicon-issues'/>
|
||||||
{error}
|
{error}
|
||||||
</div>
|
</div>
|
||||||
<div className='call-line'>{action.metadata.apiName} <span className='call-duration'>— {msToString(action.metadata.endTime - action.metadata.startTime)}</span></div>
|
<div className='call-line'>{action.metadata.apiName}</div>
|
||||||
|
{<>
|
||||||
|
<div className='call-section'>Time</div>
|
||||||
|
<div className='call-line'>wall time: <span className='datetime' title={wallTime}>{wallTime}</span></div>
|
||||||
|
<div className='call-line'>duration: <span className='datetime' title={duration}>{duration}</span></div>
|
||||||
|
</>}
|
||||||
{ !!paramKeys.length && <div className='call-section'>Parameters</div> }
|
{ !!paramKeys.length && <div className='call-section'>Parameters</div> }
|
||||||
{
|
{
|
||||||
!!paramKeys.length && paramKeys.map((name, index) => renderLine(action.metadata, name, params[name], 'param-' + index))
|
!!paramKeys.length && paramKeys.map((name, index) => renderLine(action.metadata, name, params[name], 'param-' + index))
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,7 @@ class LineReporter extends BaseReporter {
|
||||||
const title = `[${++this._current}/${this._total}] ${formatTestTitle(this.config, test)}`.substring(0, width);
|
const title = `[${++this._current}/${this._total}] ${formatTestTitle(this.config, test)}`.substring(0, width);
|
||||||
process.stdout.write(`\u001B[1A\u001B[2K${title}\n`);
|
process.stdout.write(`\u001B[1A\u001B[2K${title}\n`);
|
||||||
|
|
||||||
if (!this._omitFailures && !this.willRetry(test) && (test.outcome() === 'flaky' || test.outcome() === 'unexpected')) {
|
if (!this.willRetry(test) && (test.outcome() === 'flaky' || test.outcome() === 'unexpected')) {
|
||||||
process.stdout.write(`\u001B[1A\u001B[2K`);
|
process.stdout.write(`\u001B[1A\u001B[2K`);
|
||||||
console.log(formatFailure(this.config, test, {
|
console.log(formatFailure(this.config, test, {
|
||||||
index: ++this._failures
|
index: ++this._failures
|
||||||
|
|
|
||||||
|
|
@ -180,19 +180,19 @@ test('should show empty trace viewer', async ({ showTraceViewer }, testInfo) =>
|
||||||
test('should open simple trace viewer', async ({ showTraceViewer }) => {
|
test('should open simple trace viewer', async ({ showTraceViewer }) => {
|
||||||
const traceViewer = await showTraceViewer(traceFile);
|
const traceViewer = await showTraceViewer(traceFile);
|
||||||
await expect(traceViewer.actionTitles).toHaveText([
|
await expect(traceViewer.actionTitles).toHaveText([
|
||||||
/browserContext.newPage— [\d.ms]+/,
|
/browserContext.newPage/,
|
||||||
/page.gotodata:text\/html,<html>Hello world<\/html>— [\d.ms]+/,
|
/page.gotodata:text\/html,<html>Hello world<\/html>/,
|
||||||
/page.setContent— [\d.ms]+/,
|
/page.setContent/,
|
||||||
/expect.toHaveTextbutton— [\d.ms]+/,
|
/expect.toHaveTextbutton/,
|
||||||
/page.evaluate— [\d.ms]+/,
|
/page.evaluate/,
|
||||||
/page.click"Click"— [\d.ms]+/,
|
/page.click"Click"/,
|
||||||
/page.waitForEvent— [\d.ms]+/,
|
/page.waitForEvent/,
|
||||||
/page.route— [\d.ms]+/,
|
/page.route/,
|
||||||
/page.waitForNavigation— [\d.ms]+/,
|
/page.waitForNavigation/,
|
||||||
/page.waitForTimeout— [\d.ms]+/,
|
/page.waitForTimeout/,
|
||||||
/page.gotohttp:\/\/localhost:\d+\/frames\/frame.html— [\d.ms]+/,
|
/page.gotohttp:\/\/localhost:\d+\/frames\/frame.html/,
|
||||||
/route.continue— [\d.ms]+/,
|
/route.continue/,
|
||||||
/page.setViewportSize— [\d.ms]+/,
|
/page.setViewportSize/,
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -235,7 +235,9 @@ test('should show params and return value', async ({ showTraceViewer, browserNam
|
||||||
const traceViewer = await showTraceViewer(traceFile);
|
const traceViewer = await showTraceViewer(traceFile);
|
||||||
await traceViewer.selectAction('page.evaluate');
|
await traceViewer.selectAction('page.evaluate');
|
||||||
await expect(traceViewer.callLines).toHaveText([
|
await expect(traceViewer.callLines).toHaveText([
|
||||||
/page.evaluate — [\d.ms]+/,
|
/page.evaluate/,
|
||||||
|
/wall time: [0-9/:,APM ]+/,
|
||||||
|
/duration: [\d]+ms/,
|
||||||
'expression: "({↵ a↵ }) => {↵ console.log(\'Info\');↵ console.warn(\'Warning\');↵ con…"',
|
'expression: "({↵ a↵ }) => {↵ console.log(\'Info\');↵ console.warn(\'Warning\');↵ con…"',
|
||||||
'isFunction: true',
|
'isFunction: true',
|
||||||
'arg: {"a":"paramA","b":4}',
|
'arg: {"a":"paramA","b":4}',
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue