mirror of
https://github.com/microsoft/playwright.git
synced 2025-06-26 21:40:17 +00:00
feat(trace viewer): show call duration (#8634)
This commit is contained in:
parent
4f4cf448c2
commit
9618a8477b
@ -64,6 +64,11 @@
|
|||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.action-duration {
|
||||||
|
margin-left: 4px;
|
||||||
|
color: var(--gray);
|
||||||
|
}
|
||||||
|
|
||||||
.action-icon {
|
.action-icon {
|
||||||
flex: none;
|
flex: none;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
@ -19,6 +19,7 @@ import './tabbedPane.css';
|
|||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import * as modelUtil from './modelUtil';
|
import * as modelUtil from './modelUtil';
|
||||||
import { ActionTraceEvent } from '../../../server/trace/common/traceEvents';
|
import { ActionTraceEvent } from '../../../server/trace/common/traceEvents';
|
||||||
|
import { msToString } from '../../uiUtils';
|
||||||
|
|
||||||
export interface ActionListProps {
|
export interface ActionListProps {
|
||||||
actions: ActionTraceEvent[],
|
actions: ActionTraceEvent[],
|
||||||
@ -88,6 +89,7 @@ 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'>— {msToString(metadata.endTime - metadata.startTime)}</span>
|
||||||
</div>
|
</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>}
|
||||||
|
@ -54,6 +54,10 @@
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.call-duration {
|
||||||
|
color: var(--gray);
|
||||||
|
}
|
||||||
|
|
||||||
.call-line .string {
|
.call-line .string {
|
||||||
color: var(--orange);
|
color: var(--orange);
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,7 @@ import './callTab.css';
|
|||||||
import type { ActionTraceEvent } from '../../../server/trace/common/traceEvents';
|
import type { ActionTraceEvent } from '../../../server/trace/common/traceEvents';
|
||||||
import { CallMetadata } from '../../../protocol/callMetadata';
|
import { CallMetadata } from '../../../protocol/callMetadata';
|
||||||
import { parseSerializedValue } from '../../../protocol/serializers';
|
import { parseSerializedValue } from '../../../protocol/serializers';
|
||||||
|
import { msToString } from '../../uiUtils';
|
||||||
|
|
||||||
export const CallTab: React.FunctionComponent<{
|
export const CallTab: React.FunctionComponent<{
|
||||||
action: ActionTraceEvent | undefined,
|
action: ActionTraceEvent | undefined,
|
||||||
@ -36,7 +37,7 @@ export const CallTab: React.FunctionComponent<{
|
|||||||
<div className='codicon codicon-issues'/>
|
<div className='codicon codicon-issues'/>
|
||||||
{error}
|
{error}
|
||||||
</div>
|
</div>
|
||||||
<div className='call-line'>{action.metadata.apiName}</div>
|
<div className='call-line'>{action.metadata.apiName} <span className='call-duration'>— {msToString(action.metadata.endTime - action.metadata.startTime)}</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))
|
||||||
|
@ -148,15 +148,16 @@ 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([
|
const actionTitles = await traceViewer.actionTitles.allTextContents();
|
||||||
'page.gotodata:text/html,<html>Hello world</html>',
|
expect(sanitize(actionTitles)).toEqual([
|
||||||
'page.setContent',
|
'page.gotodata:text/html,<html>Hello world</html>— Xms',
|
||||||
'page.evaluate',
|
'page.setContent— Xms',
|
||||||
'page.click\"Click\"',
|
'page.evaluate— Xms',
|
||||||
'page.waitForNavigation',
|
'page.click\"Click\"— Xms',
|
||||||
'page.gotodata:text/html,<html>Hello world 2</html>',
|
'page.waitForNavigation— Xms',
|
||||||
'page.setViewportSize',
|
'page.gotodata:text/html,<html>Hello world 2</html>— Xms',
|
||||||
'page.hoverbody',
|
'page.setViewportSize— Xms',
|
||||||
|
'page.hoverbody— Xms',
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -198,8 +199,9 @@ test('should open console errors on click', async ({ showTraceViewer, browserNam
|
|||||||
test('should show params and return value', async ({ showTraceViewer, browserName }) => {
|
test('should show params and return value', async ({ showTraceViewer, browserName }) => {
|
||||||
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([
|
const callLines = await traceViewer.callLines.allTextContents();
|
||||||
'page.evaluate',
|
expect(sanitize(callLines)).toEqual([
|
||||||
|
'page.evaluate — Xms',
|
||||||
'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}',
|
||||||
@ -221,16 +223,20 @@ test('should have correct stack trace', async ({ showTraceViewer }) => {
|
|||||||
|
|
||||||
await traceViewer.selectAction('page.click');
|
await traceViewer.selectAction('page.click');
|
||||||
await traceViewer.showSourceTab();
|
await traceViewer.showSourceTab();
|
||||||
const stack1 = (await traceViewer.stackFrames.allInnerTexts()).map(s => s.replace(/\s+/g, ' ').replace(/[0-9]/g, 'X'));
|
const stack1 = await traceViewer.stackFrames.allInnerTexts();
|
||||||
expect(stack1.slice(0, 2)).toEqual([
|
expect(sanitize(stack1).slice(0, 2)).toEqual([
|
||||||
'doClick trace-viewer.spec.ts :XXX',
|
'doClick trace-viewer.spec.ts :X',
|
||||||
'recordTrace trace-viewer.spec.ts :XXX',
|
'recordTrace trace-viewer.spec.ts :X',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
await traceViewer.selectAction('page.hover');
|
await traceViewer.selectAction('page.hover');
|
||||||
await traceViewer.showSourceTab();
|
await traceViewer.showSourceTab();
|
||||||
const stack2 = (await traceViewer.stackFrames.allInnerTexts()).map(s => s.replace(/\s+/g, ' ').replace(/[0-9]/g, 'X'));
|
const stack2 = await traceViewer.stackFrames.allInnerTexts();
|
||||||
expect(stack2.slice(0, 1)).toEqual([
|
expect(sanitize(stack2).slice(0, 1)).toEqual([
|
||||||
'BrowserType.browserType._onWillCloseContext trace-viewer.spec.ts :XXX',
|
'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'));
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user