From 9618a8477b33d5a6b69ec1a3abe43b193d1d8bc0 Mon Sep 17 00:00:00 2001 From: Dmitry Gozman Date: Wed, 1 Sep 2021 13:41:47 -0700 Subject: [PATCH] feat(trace viewer): show call duration (#8634) --- src/web/traceViewer/ui/actionList.css | 5 +++ src/web/traceViewer/ui/actionList.tsx | 2 ++ src/web/traceViewer/ui/callTab.css | 4 +++ src/web/traceViewer/ui/callTab.tsx | 3 +- tests/trace-viewer/trace-viewer.spec.ts | 44 ++++++++++++++----------- 5 files changed, 38 insertions(+), 20 deletions(-) diff --git a/src/web/traceViewer/ui/actionList.css b/src/web/traceViewer/ui/actionList.css index e256d5390c..f1bb3b9889 100644 --- a/src/web/traceViewer/ui/actionList.css +++ b/src/web/traceViewer/ui/actionList.css @@ -64,6 +64,11 @@ text-overflow: ellipsis; } +.action-duration { + margin-left: 4px; + color: var(--gray); +} + .action-icon { flex: none; display: flex; diff --git a/src/web/traceViewer/ui/actionList.tsx b/src/web/traceViewer/ui/actionList.tsx index 6cfae0fd35..69522616d4 100644 --- a/src/web/traceViewer/ui/actionList.tsx +++ b/src/web/traceViewer/ui/actionList.tsx @@ -19,6 +19,7 @@ import './tabbedPane.css'; import * as React from 'react'; import * as modelUtil from './modelUtil'; import { ActionTraceEvent } from '../../../server/trace/common/traceEvents'; +import { msToString } from '../../uiUtils'; export interface ActionListProps { actions: ActionTraceEvent[], @@ -88,6 +89,7 @@ export const ActionList: React.FC = ({ {metadata.apiName} {metadata.params.selector &&
{metadata.params.selector}
} {metadata.method === 'goto' && metadata.params.url &&
{metadata.params.url}
} + — {msToString(metadata.endTime - metadata.startTime)}
setSelectedTab('console')}> {!!errors &&
{errors}
} diff --git a/src/web/traceViewer/ui/callTab.css b/src/web/traceViewer/ui/callTab.css index 2912238f3c..87def44160 100644 --- a/src/web/traceViewer/ui/callTab.css +++ b/src/web/traceViewer/ui/callTab.css @@ -54,6 +54,10 @@ overflow: hidden; } +.call-duration { + color: var(--gray); +} + .call-line .string { color: var(--orange); } diff --git a/src/web/traceViewer/ui/callTab.tsx b/src/web/traceViewer/ui/callTab.tsx index fb0fb14a19..40672ab85b 100644 --- a/src/web/traceViewer/ui/callTab.tsx +++ b/src/web/traceViewer/ui/callTab.tsx @@ -19,6 +19,7 @@ import './callTab.css'; import type { ActionTraceEvent } from '../../../server/trace/common/traceEvents'; import { CallMetadata } from '../../../protocol/callMetadata'; import { parseSerializedValue } from '../../../protocol/serializers'; +import { msToString } from '../../uiUtils'; export const CallTab: React.FunctionComponent<{ action: ActionTraceEvent | undefined, @@ -36,7 +37,7 @@ export const CallTab: React.FunctionComponent<{
{error}
-
{action.metadata.apiName}
+
{action.metadata.apiName} — {msToString(action.metadata.endTime - action.metadata.startTime)}
{ !!paramKeys.length &&
Parameters
} { !!paramKeys.length && paramKeys.map((name, index) => renderLine(action.metadata, name, params[name], 'param-' + index)) diff --git a/tests/trace-viewer/trace-viewer.spec.ts b/tests/trace-viewer/trace-viewer.spec.ts index 1bad4f274d..8f3e7289bb 100644 --- a/tests/trace-viewer/trace-viewer.spec.ts +++ b/tests/trace-viewer/trace-viewer.spec.ts @@ -148,15 +148,16 @@ test('should show empty trace viewer', async ({ showTraceViewer }, testInfo) => test('should open simple trace viewer', async ({ showTraceViewer }) => { const traceViewer = await showTraceViewer(traceFile); - await expect(traceViewer.actionTitles).toHaveText([ - 'page.gotodata:text/html,Hello world', - 'page.setContent', - 'page.evaluate', - 'page.click\"Click\"', - 'page.waitForNavigation', - 'page.gotodata:text/html,Hello world 2', - 'page.setViewportSize', - 'page.hoverbody', + const actionTitles = await traceViewer.actionTitles.allTextContents(); + expect(sanitize(actionTitles)).toEqual([ + 'page.gotodata:text/html,Hello world— Xms', + 'page.setContent— Xms', + 'page.evaluate— Xms', + 'page.click\"Click\"— Xms', + 'page.waitForNavigation— Xms', + 'page.gotodata:text/html,Hello world 2— Xms', + 'page.setViewportSize— Xms', + 'page.hoverbody— Xms', ]); }); @@ -198,9 +199,10 @@ test('should open console errors on click', async ({ showTraceViewer, browserNam test('should show params and return value', async ({ showTraceViewer, browserName }) => { const traceViewer = await showTraceViewer(traceFile); await traceViewer.selectAction('page.evaluate'); - await expect(traceViewer.callLines).toHaveText([ - 'page.evaluate', - 'expression: "({↵ a↵ }) => {↵ console.log(\'Info\');↵ console.warn(\'Warning\');↵ con…"', + const callLines = await traceViewer.callLines.allTextContents(); + expect(sanitize(callLines)).toEqual([ + 'page.evaluate — Xms', + 'expression: "({↵ a↵ }) => {↵ console.log(\'Info\');↵ console.warn(\'Warning\');↵ con…"', 'isFunction: true', 'arg: {"a":"paramA","b":4}', 'value: "return paramA"' @@ -221,16 +223,20 @@ test('should have correct stack trace', async ({ showTraceViewer }) => { await traceViewer.selectAction('page.click'); await traceViewer.showSourceTab(); - const stack1 = (await traceViewer.stackFrames.allInnerTexts()).map(s => s.replace(/\s+/g, ' ').replace(/[0-9]/g, 'X')); - expect(stack1.slice(0, 2)).toEqual([ - 'doClick trace-viewer.spec.ts :XXX', - 'recordTrace trace-viewer.spec.ts :XXX', + const stack1 = await traceViewer.stackFrames.allInnerTexts(); + expect(sanitize(stack1).slice(0, 2)).toEqual([ + 'doClick trace-viewer.spec.ts :X', + 'recordTrace trace-viewer.spec.ts :X', ]); await traceViewer.selectAction('page.hover'); await traceViewer.showSourceTab(); - const stack2 = (await traceViewer.stackFrames.allInnerTexts()).map(s => s.replace(/\s+/g, ' ').replace(/[0-9]/g, 'X')); - expect(stack2.slice(0, 1)).toEqual([ - 'BrowserType.browserType._onWillCloseContext trace-viewer.spec.ts :XXX', + const stack2 = await traceViewer.stackFrames.allInnerTexts(); + expect(sanitize(stack2).slice(0, 1)).toEqual([ + 'BrowserType.browserType._onWillCloseContext trace-viewer.spec.ts :X', ]); }); + +function sanitize(texts: string[]): string[] { + return texts.map(s => s.replace(/\s+/g, ' ').replace(/\.ts :[0-9]+/g, '.ts :X').replace(/[0-9]+ms/g, 'Xms')); +}