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 }) => {