diff --git a/packages/trace-viewer/src/ui/callTab.css b/packages/trace-viewer/src/ui/callTab.css index 23e8ae0bbb..0dd31c912b 100644 --- a/packages/trace-viewer/src/ui/callTab.css +++ b/packages/trace-viewer/src/ui/callTab.css @@ -77,12 +77,17 @@ } .call-line .datetime, -.call-line .string { +.call-line .string, +.call-line .locator { color: var(--orange); } .call-line .number, +.call-line .bigint, .call-line .boolean, +.call-line .symbol, +.call-line .undefined, +.call-line .function, .call-line .object { color: var(--blue); } diff --git a/packages/trace-viewer/src/ui/callTab.tsx b/packages/trace-viewer/src/ui/callTab.tsx index 841d1a5da4..01b2443dc5 100644 --- a/packages/trace-viewer/src/ui/callTab.tsx +++ b/packages/trace-viewer/src/ui/callTab.tsx @@ -21,10 +21,13 @@ import { msToString } from '@web/uiUtils'; import * as React from 'react'; import './callTab.css'; import { CopyToClipboard } from './copyToClipboard'; +import { asLocator } from '@isomorphic/locatorGenerators'; +import type { Language } from '@isomorphic/locatorGenerators'; export const CallTab: React.FunctionComponent<{ action: ActionTraceEvent | undefined, -}> = ({ action }) => { + sdkLanguage: Language | undefined, +}> = ({ action, sdkLanguage }) => { if (!action) return null; const logs = action.metadata.log; @@ -48,12 +51,12 @@ export const CallTab: React.FunctionComponent<{ } { !!paramKeys.length &&
Parameters
} { - !!paramKeys.length && paramKeys.map((name, index) => renderLine(action.metadata, name, params[name], 'param-' + index)) + !!paramKeys.length && paramKeys.map((name, index) => renderProperty(propertyToString(action.metadata, name, params[name], sdkLanguage), 'param-' + index)) } { !!action.metadata.result &&
Return value
} { !!action.metadata.result && Object.keys(action.metadata.result).map((name, index) => - renderLine(action.metadata, name, action.metadata.result[name], 'result-' + index) + renderProperty(propertyToString(action.metadata, name, action.metadata.result[name], sdkLanguage), 'result-' + index) ) }
Log
@@ -67,44 +70,42 @@ export const CallTab: React.FunctionComponent<{ ; }; -function shouldCopy(type: string): boolean { - return !!({ - 'string': true, - 'number': true, - 'object': true, - }[type]); -} +type Property = { + name: string; + type: 'string' | 'number' | 'object' | 'locator' | 'handle' | 'bigint' | 'boolean' | 'symbol' | 'undefined' | 'function'; + text: string; +}; -function renderLine(metadata: CallMetadata, name: string, value: any, key: string) { - const { title, type } = toString(metadata, name, value); - let text = title.replace(/\n/g, '↵'); - if (type === 'string') +function renderProperty(property: Property, key: string) { + let text = property.text.replace(/\n/g, '↵'); + if (property.type === 'string') text = `"${text}"`; return (
- {name}: {text} - { shouldCopy(type) && ( + {property.name}: {text} + { ['string', 'number', 'object', 'locator'].includes(property.type) && - + - )} + }
); } -function toString(metadata: CallMetadata, name: string, value: any): { title: string, type: string } { - if (metadata.method.includes('eval')) { - if (name === 'arg') - value = parseSerializedValue(value.value, new Array(10).fill({ handle: '' })); - if (name === 'value') - value = parseSerializedValue(value, new Array(10).fill({ handle: '' })); - } +function propertyToString(metadata: CallMetadata, name: string, value: any, sdkLanguage: Language | undefined): Property { + const isEval = metadata.method.includes('eval') || metadata.method === 'waitForFunction'; + if (name === 'eventInit' || name === 'expectedValue' || (name === 'arg' && isEval)) + value = parseSerializedValue(value.value, new Array(10).fill({ handle: '' })); + if ((name === 'value' && isEval) || (name === 'received' && metadata.method === 'expect')) + value = parseSerializedValue(value, new Array(10).fill({ handle: '' })); + if (name === 'selector') + return { text: asLocator(sdkLanguage || 'javascript', metadata.params.selector), type: 'locator', name: 'locator' }; const type = typeof value; if (type !== 'object' || value === null) - return { title: String(value), type }; + return { text: String(value), type, name }; if (value.guid) - return { title: '', type: 'handle' }; - return { title: JSON.stringify(value), type: 'object' }; + return { text: '', type: 'handle', name }; + return { text: JSON.stringify(value), type: 'object', name }; } function parseSerializedValue(value: SerializedValue, handles: any[] | undefined): any { diff --git a/packages/trace-viewer/src/ui/workbench.tsx b/packages/trace-viewer/src/ui/workbench.tsx index 2fdd7cbca7..4f436cf5d1 100644 --- a/packages/trace-viewer/src/ui/workbench.tsx +++ b/packages/trace-viewer/src/ui/workbench.tsx @@ -144,7 +144,7 @@ export const Workbench: React.FunctionComponent<{ const networkCount = selectedAction ? modelUtil.resourcesForAction(selectedAction).length : 0; const tabs = [ - { id: 'logs', title: 'Call', count: 0, render: () => }, + { id: 'logs', title: 'Call', count: 0, render: () => }, { id: 'console', title: 'Console', count: consoleCount, render: () => }, { id: 'network', title: 'Network', count: networkCount, render: () => }, ]; diff --git a/tests/library/trace-viewer.spec.ts b/tests/library/trace-viewer.spec.ts index b43c7e049d..df436862ad 100644 --- a/tests/library/trace-viewer.spec.ts +++ b/tests/library/trace-viewer.spec.ts @@ -151,6 +151,18 @@ test('should show params and return value', async ({ showTraceViewer, browserNam 'arg: {"a":"paramA","b":4}', 'value: "return paramA"' ]); + + await traceViewer.selectAction(`locator('button')`); + await expect(traceViewer.callLines).toContainText([ + /expect.toHaveText/, + /wall time: [0-9/:,APM ]+/, + /duration: [\d]+ms/, + /locator: locator\('button'\)/, + /expression: "to.have.text"/, + /timeout: 10000/, + /matches: true/, + /received: "Click"/, + ]); }); test('should show null as a param', async ({ showTraceViewer, browserName }) => {