From 1c3d31ac9cc97c1df33e389b5c306924c8f5faf7 Mon Sep 17 00:00:00 2001 From: Adam Gastineau Date: Thu, 9 Jan 2025 12:54:31 -0800 Subject: [PATCH 01/17] Beginning implementation of commandContextString --- packages/trace-viewer/src/ui/actionList.tsx | 3 +- packages/trace-viewer/src/ui/string.ts | 48 +++++++++++++++++++++ 2 files changed, 50 insertions(+), 1 deletion(-) create mode 100644 packages/trace-viewer/src/ui/string.ts diff --git a/packages/trace-viewer/src/ui/actionList.tsx b/packages/trace-viewer/src/ui/actionList.tsx index 1deb8ecd88c9a..c740fe9c33ec1 100644 --- a/packages/trace-viewer/src/ui/actionList.tsx +++ b/packages/trace-viewer/src/ui/actionList.tsx @@ -26,6 +26,7 @@ import { TreeView } from '@web/components/treeView'; import type { ActionTraceEventInContext, ActionTreeItem } from './modelUtil'; import type { Boundaries } from './geometry'; import { ToolbarButton } from '@web/components/toolbarButton'; +import { commandContextString } from './string'; export interface ActionListProps { actions: ActionTraceEventInContext[], @@ -116,7 +117,7 @@ export const renderAction = ( }) => { const { sdkLanguage, revealConsole, revealAttachment, isLive, showDuration, showBadges } = options; const { errors, warnings } = modelUtil.stats(action); - const locator = action.params.selector ? asLocator(sdkLanguage || 'javascript', action.params.selector) : undefined; + const locator = commandContextString(action, sdkLanguage || 'javascript'); const showAttachments = !!action.attachments?.length && !!revealAttachment; let time: string = ''; diff --git a/packages/trace-viewer/src/ui/string.ts b/packages/trace-viewer/src/ui/string.ts new file mode 100644 index 0000000000000..e515868eb3f01 --- /dev/null +++ b/packages/trace-viewer/src/ui/string.ts @@ -0,0 +1,48 @@ +import type { ActionTraceEvent } from '@trace/trace'; +import { asLocator, type Language } from '@isomorphic/locatorGenerators'; + +export const commandContextString = (action: ActionTraceEvent, sdkLanguage: Language): string | undefined => { + const params = action.params; + + if (action.apiName.startsWith('clock')) { + if ('ticksNumber' in params) { + // clock.fastForward/runFor + return `${params.ticksNumber}ms`; + } else if (params.ticksString) { + // clock.fastForward/runFor + return params.ticksString; + } else if ('timeNumber' in params) { + // clock.pauseAt/setFixedTime/setSystemTime + try { + return new Date(params.timeNumber).toLocaleString(); + } catch (e) { + return undefined; + } + } + } else if (action.apiName.startsWith('keyboard')) { + if (params.key) { + // keyboard.press/down/up + return params.key; + } else if (params.text) { + // keyboard.type/insertText + return `"${params.text}"`; + } + } else if (action.apiName.startsWith('locator')) { + return asLocator(sdkLanguage, params.selector); + } else if (action.apiName.startsWith('mouse')) { + if ('x' in params && 'y' in params) { + // mouse.click/dblclick/move + return `(${params.x}, ${params.y})`; + } else if ('deltaX' in params && 'deltaY' in params) { + // mouse.wheel + return `(${params.deltaX}, ${params.deltaY})`; + } + } else if (action.apiName.startsWith('touchscreen')) { + if ('x' in params && 'y' in params) { + // touchscreen.tap + return `(${params.x}, ${params.y})`; + } + } + + return undefined; +}; \ No newline at end of file From 366dad572fca529134f45202b4bdb28b426a078d Mon Sep 17 00:00:00 2001 From: Adam Gastineau Date: Fri, 10 Jan 2025 06:03:13 -0800 Subject: [PATCH 02/17] Cleaned up formatting logic --- packages/trace-viewer/src/ui/string.ts | 152 ++++++++++++++++++------- 1 file changed, 110 insertions(+), 42 deletions(-) diff --git a/packages/trace-viewer/src/ui/string.ts b/packages/trace-viewer/src/ui/string.ts index e515868eb3f01..4227880d8e9cc 100644 --- a/packages/trace-viewer/src/ui/string.ts +++ b/packages/trace-viewer/src/ui/string.ts @@ -1,48 +1,116 @@ +/** + * Copyright (c) Microsoft Corporation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + import type { ActionTraceEvent } from '@trace/trace'; import { asLocator, type Language } from '@isomorphic/locatorGenerators'; -export const commandContextString = (action: ActionTraceEvent, sdkLanguage: Language): string | undefined => { - const params = action.params; - - if (action.apiName.startsWith('clock')) { - if ('ticksNumber' in params) { - // clock.fastForward/runFor - return `${params.ticksNumber}ms`; - } else if (params.ticksString) { - // clock.fastForward/runFor - return params.ticksString; - } else if ('timeNumber' in params) { - // clock.pauseAt/setFixedTime/setSystemTime - try { - return new Date(params.timeNumber).toLocaleString(); - } catch (e) { - return undefined; - } - } - } else if (action.apiName.startsWith('keyboard')) { - if (params.key) { - // keyboard.press/down/up - return params.key; - } else if (params.text) { - // keyboard.type/insertText - return `"${params.text}"`; - } - } else if (action.apiName.startsWith('locator')) { - return asLocator(sdkLanguage, params.selector); - } else if (action.apiName.startsWith('mouse')) { - if ('x' in params && 'y' in params) { - // mouse.click/dblclick/move - return `(${params.x}, ${params.y})`; - } else if ('deltaX' in params && 'deltaY' in params) { - // mouse.wheel - return `(${params.deltaX}, ${params.deltaY})`; - } - } else if (action.apiName.startsWith('touchscreen')) { - if ('x' in params && 'y' in params) { - // touchscreen.tap - return `(${params.x}, ${params.y})`; +const formatClockParams = (params: { + ticksNumber?: number; + ticksString?: string; + timeNumber?: number; +}): string | undefined => { + if (params.ticksNumber !== undefined) { + // clock.fastForward/runFor + return `${params.ticksNumber}ms`; + } else if (params.ticksString !== undefined) { + // clock.fastForward/runFor + return params.ticksString; + } else if (params.timeNumber !== undefined) { + // clock.pauseAt/setFixedTime/setSystemTime + try { + return new Date(params.timeNumber).toLocaleString(); + } catch (e) { + return undefined; } } - + + return undefined; +}; + +const formatLocatorParams = ( + sdkLanguage: Language, + params: { selector?: string }, +): string | undefined => + params.selector !== undefined + ? asLocator(sdkLanguage, params.selector) + : undefined; + +const formatKeyboardParams = (params: { + key?: string; + text?: string; +}): string | undefined => { + if (params.key !== undefined) { + // keyboard.press/down/up + return params.key; + } else if (params.text !== undefined) { + // keyboard.type/insertText + return `"${params.text}"`; + } + + return undefined; +}; + +const formatMouseParams = (params: { + x?: number; + y?: number; + deltaX?: number; + deltaY?: number; +}): string | undefined => { + if (params.x !== undefined && params.y !== undefined) { + // mouse.click/dblclick/move + return `(${params.x}, ${params.y})`; + } else if (params.deltaX !== undefined && params.deltaY !== undefined) { + // mouse.wheel + return `(${params.deltaX}, ${params.deltaY})`; + } + + return undefined; +}; + +const formatTouchscreenParams = (params: { + x?: number; + y?: number; +}): string | undefined => { + if (params.x && params.y) { + // touchscreen.tap + return `(${params.x}, ${params.y})`; + } + return undefined; -}; \ No newline at end of file +}; + +export const commandContextString = ( + action: ActionTraceEvent, + sdkLanguage: Language, +): string | undefined => { + const params = action.params; + const apiName = action.apiName; + + switch (true) { + case apiName.startsWith('clock'): + return formatClockParams(params); + case apiName.startsWith('keyboard'): + return formatKeyboardParams(params); + case apiName.startsWith('locator'): + return formatLocatorParams(sdkLanguage, params); + case apiName.startsWith('mouse'): + return formatMouseParams(params); + case apiName.startsWith('touchscreen'): + return formatTouchscreenParams(params); + default: + return undefined; + } +}; From 6a9e976978dff4bdf004931bedf98aaed8ff06e6 Mon Sep 17 00:00:00 2001 From: Adam Gastineau Date: Fri, 10 Jan 2025 07:47:54 -0800 Subject: [PATCH 03/17] Separate locator display from other action contexts --- packages/trace-viewer/src/ui/actionList.css | 9 +++++- packages/trace-viewer/src/ui/actionList.tsx | 31 +++++++++++++++++---- packages/trace-viewer/src/ui/string.ts | 2 +- 3 files changed, 35 insertions(+), 7 deletions(-) diff --git a/packages/trace-viewer/src/ui/actionList.css b/packages/trace-viewer/src/ui/actionList.css index 10e3c39f98522..0d246b7d45ed1 100644 --- a/packages/trace-viewer/src/ui/actionList.css +++ b/packages/trace-viewer/src/ui/actionList.css @@ -70,13 +70,20 @@ flex: none; } -.action-selector { +.action-context { display: inline; flex: none; padding-left: 5px; +} + +.action-locator-context { color: var(--vscode-charts-orange); } +.action-generic-context { + color: var(--vscode-charts-purple); +} + .action-url { display: inline; flex: none; diff --git a/packages/trace-viewer/src/ui/actionList.tsx b/packages/trace-viewer/src/ui/actionList.tsx index c740fe9c33ec1..9ab1bf147fd95 100644 --- a/packages/trace-viewer/src/ui/actionList.tsx +++ b/packages/trace-viewer/src/ui/actionList.tsx @@ -15,18 +15,17 @@ */ import type { ActionTraceEvent, AfterActionTraceEventAttachment } from '@trace/trace'; -import { msToString } from '@web/uiUtils'; +import { clsx, msToString } from '@web/uiUtils'; import * as React from 'react'; import './actionList.css'; import * as modelUtil from './modelUtil'; -import { asLocator } from '@isomorphic/locatorGenerators'; import type { Language } from '@isomorphic/locatorGenerators'; import type { TreeState } from '@web/components/treeView'; import { TreeView } from '@web/components/treeView'; import type { ActionTraceEventInContext, ActionTreeItem } from './modelUtil'; import type { Boundaries } from './geometry'; import { ToolbarButton } from '@web/components/toolbarButton'; -import { commandContextString } from './string'; +import { actionContextString } from './string'; export interface ActionListProps { actions: ActionTraceEventInContext[], @@ -105,6 +104,29 @@ export const ActionList: React.FC = ({ ; }; +const ActionContext: React.FC<{ + action: ActionTraceEvent; + sdkLanguage: Language; +}> = ({ action, sdkLanguage }) => { + const contextString = actionContextString(action, sdkLanguage); + + if (contextString === undefined) + return null; + + return ( +
+ {contextString} +
+ ); +}; + export const renderAction = ( action: ActionTraceEvent, options: { @@ -117,7 +139,6 @@ export const renderAction = ( }) => { const { sdkLanguage, revealConsole, revealAttachment, isLive, showDuration, showBadges } = options; const { errors, warnings } = modelUtil.stats(action); - const locator = commandContextString(action, sdkLanguage || 'javascript'); const showAttachments = !!action.attachments?.length && !!revealAttachment; let time: string = ''; @@ -130,7 +151,7 @@ export const renderAction = ( return <>
{action.apiName} - {locator &&
{locator}
} + {action.method === 'goto' && action.params.url &&
{action.params.url}
} {action.class === 'APIRequestContext' && action.params.url &&
{excludeOrigin(action.params.url)}
}
diff --git a/packages/trace-viewer/src/ui/string.ts b/packages/trace-viewer/src/ui/string.ts index 4227880d8e9cc..785930a32afea 100644 --- a/packages/trace-viewer/src/ui/string.ts +++ b/packages/trace-viewer/src/ui/string.ts @@ -92,7 +92,7 @@ const formatTouchscreenParams = (params: { return undefined; }; -export const commandContextString = ( +export const actionContextString = ( action: ActionTraceEvent, sdkLanguage: Language, ): string | undefined => { From dfd58875b5b769f9e75b9afdfe51a48bdeeeb382 Mon Sep 17 00:00:00 2001 From: Adam Gastineau Date: Fri, 10 Jan 2025 09:27:02 -0800 Subject: [PATCH 04/17] Fix expect not being included in locator printing --- packages/trace-viewer/src/ui/string.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/trace-viewer/src/ui/string.ts b/packages/trace-viewer/src/ui/string.ts index 785930a32afea..05ff20dbc0749 100644 --- a/packages/trace-viewer/src/ui/string.ts +++ b/packages/trace-viewer/src/ui/string.ts @@ -105,6 +105,7 @@ export const actionContextString = ( case apiName.startsWith('keyboard'): return formatKeyboardParams(params); case apiName.startsWith('locator'): + case apiName.startsWith('expect'): return formatLocatorParams(sdkLanguage, params); case apiName.startsWith('mouse'): return formatMouseParams(params); From 258699cdb4fdbb4c82ef61248408b7c1ba116d79 Mon Sep 17 00:00:00 2001 From: Adam Gastineau Date: Fri, 10 Jan 2025 09:35:12 -0800 Subject: [PATCH 05/17] Add text for displaying action context --- tests/library/trace-viewer.spec.ts | 51 ++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/tests/library/trace-viewer.spec.ts b/tests/library/trace-viewer.spec.ts index 59441935cfcec..4e994bc2c57ab 100644 --- a/tests/library/trace-viewer.spec.ts +++ b/tests/library/trace-viewer.spec.ts @@ -166,6 +166,57 @@ test('should open simple trace viewer', async ({ showTraceViewer }) => { ]); }); +test('should show action context on locators and other common actions', async ({ + runAndTrace, + page, +}) => { + const traceViewer = await runAndTrace(async () => { + await page.setContent(''); + await page.locator('input').click({ button: 'right' }); + await page.getByRole('textbox').click(); + await page.keyboard.type( + 'Hello world this is a very long string what happens when it overflows?', + ); + await page.keyboard.press('Control+c'); + await page.keyboard.down('Shift'); + await page.keyboard.insertText('Hello world'); + await page.keyboard.up('Shift'); + await page.mouse.move(0, 0); + await page.mouse.down(); + await page.mouse.move(100, 200); + await page.mouse.wheel(5, 7); + await page.mouse.up(); + await page.clock.fastForward(1000); + await page.clock.fastForward('30:00'); + await page.clock.pauseAt(new Date('2020-02-02')); + await page.clock.runFor(10); + await page.clock.setFixedTime(new Date('2020-02-02')); + await page.clock.setSystemTime(new Date('2020-02-02')); + }); + + await expect(traceViewer.actionTitles).toHaveText([ + /page.setContent/, + /locator.clicklocator\('input'\)/, + /locator.clickgetByRole\('textbox'\)/, + /keyboard.type\"Hello world this is a very long string what happens when it overflows\?\"/, + /keyboard.pressControl\+c/, + /keyboard.downShift/, + /keyboard.insertText\"Hello world\"/, + /keyboard.upShift/, + /mouse.move\(0, 0\)/, + /mouse.down/, + /mouse.move\(100, 200\)/, + /mouse.wheel\(5, 7\)/, + /mouse.up/, + /clock.fastForward1000ms/, + /clock.fastForward30:00/, + /clock.pauseAt2\/1\/2020, 4:00:00 PM/, + /clock.runFor10ms/, + /clock.setFixedTime2\/1\/2020, 4:00:00 PM/, + /clock.setSystemTime2\/1\/2020, 4:00:00 PM/ + ]); +}); + test('should complain about newer version of trace in old viewer', async ({ showTraceViewer, asset }, testInfo) => { const traceViewer = await showTraceViewer([asset('trace-from-the-future.zip')]); await expect(traceViewer.page.getByText('The trace was created by a newer version of Playwright and is not supported by this version of the viewer.')).toBeVisible(); From 0100f87e1129a0ced93eeb14938c6ba57c043f97 Mon Sep 17 00:00:00 2001 From: Adam Gastineau Date: Fri, 10 Jan 2025 10:11:32 -0800 Subject: [PATCH 06/17] Switch to always using UTC date display --- packages/trace-viewer/src/ui/string.ts | 4 +++- tests/library/trace-viewer.spec.ts | 12 ++++++------ 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/packages/trace-viewer/src/ui/string.ts b/packages/trace-viewer/src/ui/string.ts index 05ff20dbc0749..bf6eccd8f3318 100644 --- a/packages/trace-viewer/src/ui/string.ts +++ b/packages/trace-viewer/src/ui/string.ts @@ -31,7 +31,9 @@ const formatClockParams = (params: { } else if (params.timeNumber !== undefined) { // clock.pauseAt/setFixedTime/setSystemTime try { - return new Date(params.timeNumber).toLocaleString(); + return new Date(params.timeNumber).toLocaleString(undefined, { + timeZone: 'UTC', + }); } catch (e) { return undefined; } diff --git a/tests/library/trace-viewer.spec.ts b/tests/library/trace-viewer.spec.ts index 4e994bc2c57ab..7ec43ca9902f0 100644 --- a/tests/library/trace-viewer.spec.ts +++ b/tests/library/trace-viewer.spec.ts @@ -188,10 +188,10 @@ test('should show action context on locators and other common actions', async ({ await page.mouse.up(); await page.clock.fastForward(1000); await page.clock.fastForward('30:00'); - await page.clock.pauseAt(new Date('2020-02-02')); + await page.clock.pauseAt(new Date('2020-02-02T00:00:00Z')); await page.clock.runFor(10); - await page.clock.setFixedTime(new Date('2020-02-02')); - await page.clock.setSystemTime(new Date('2020-02-02')); + await page.clock.setFixedTime(new Date('2020-02-02T00:00:00Z')); + await page.clock.setSystemTime(new Date('2020-02-02T00:00:00Z')); }); await expect(traceViewer.actionTitles).toHaveText([ @@ -210,10 +210,10 @@ test('should show action context on locators and other common actions', async ({ /mouse.up/, /clock.fastForward1000ms/, /clock.fastForward30:00/, - /clock.pauseAt2\/1\/2020, 4:00:00 PM/, + /clock.pauseAt2\/2\/2020, 12:00:00 AM/, /clock.runFor10ms/, - /clock.setFixedTime2\/1\/2020, 4:00:00 PM/, - /clock.setSystemTime2\/1\/2020, 4:00:00 PM/ + /clock.setFixedTime2\/2\/2020, 12:00:00 AM/, + /clock.setSystemTime2\/2\/2020, 12:00:00 AM/, ]); }); From 9cb8d55bea3e77c9c175275bc06239da21f15c7e Mon Sep 17 00:00:00 2001 From: Adam Gastineau Date: Mon, 13 Jan 2025 05:05:23 -0800 Subject: [PATCH 07/17] Remove "context" naming --- packages/trace-viewer/src/ui/actionList.css | 6 +++--- packages/trace-viewer/src/ui/actionList.tsx | 18 +++++++++--------- packages/trace-viewer/src/ui/string.ts | 2 +- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/packages/trace-viewer/src/ui/actionList.css b/packages/trace-viewer/src/ui/actionList.css index 0d246b7d45ed1..0cb3cb5f5469f 100644 --- a/packages/trace-viewer/src/ui/actionList.css +++ b/packages/trace-viewer/src/ui/actionList.css @@ -70,17 +70,17 @@ flex: none; } -.action-context { +.action-parameter { display: inline; flex: none; padding-left: 5px; } -.action-locator-context { +.action-locator-parameter { color: var(--vscode-charts-orange); } -.action-generic-context { +.action-generic-parameter { color: var(--vscode-charts-purple); } diff --git a/packages/trace-viewer/src/ui/actionList.tsx b/packages/trace-viewer/src/ui/actionList.tsx index 9ab1bf147fd95..f9aff122ca62d 100644 --- a/packages/trace-viewer/src/ui/actionList.tsx +++ b/packages/trace-viewer/src/ui/actionList.tsx @@ -25,7 +25,7 @@ import { TreeView } from '@web/components/treeView'; import type { ActionTraceEventInContext, ActionTreeItem } from './modelUtil'; import type { Boundaries } from './geometry'; import { ToolbarButton } from '@web/components/toolbarButton'; -import { actionContextString } from './string'; +import { actionParameterDisplayString } from './string'; export interface ActionListProps { actions: ActionTraceEventInContext[], @@ -104,25 +104,25 @@ export const ActionList: React.FC = ({ ; }; -const ActionContext: React.FC<{ +const ActionParameterContext: React.FC<{ action: ActionTraceEvent; sdkLanguage: Language; }> = ({ action, sdkLanguage }) => { - const contextString = actionContextString(action, sdkLanguage); + const parameterString = actionParameterDisplayString(action, sdkLanguage); - if (contextString === undefined) + if (parameterString === undefined) return null; return (
- {contextString} + {parameterString}
); }; @@ -151,7 +151,7 @@ export const renderAction = ( return <>
{action.apiName} - + {action.method === 'goto' && action.params.url &&
{action.params.url}
} {action.class === 'APIRequestContext' && action.params.url &&
{excludeOrigin(action.params.url)}
}
diff --git a/packages/trace-viewer/src/ui/string.ts b/packages/trace-viewer/src/ui/string.ts index bf6eccd8f3318..204772b30327e 100644 --- a/packages/trace-viewer/src/ui/string.ts +++ b/packages/trace-viewer/src/ui/string.ts @@ -94,7 +94,7 @@ const formatTouchscreenParams = (params: { return undefined; }; -export const actionContextString = ( +export const actionParameterDisplayString = ( action: ActionTraceEvent, sdkLanguage: Language, ): string | undefined => { From fa033bb9e67816c66af549b9015d81d955f6349d Mon Sep 17 00:00:00 2001 From: Adam Gastineau Date: Mon, 13 Jan 2025 08:02:17 -0800 Subject: [PATCH 08/17] Better handling for non-JS Playwright actions --- packages/trace-viewer/src/ui/actionList.tsx | 4 +- packages/trace-viewer/src/ui/string.ts | 78 +++++++++++++++------ 2 files changed, 60 insertions(+), 22 deletions(-) diff --git a/packages/trace-viewer/src/ui/actionList.tsx b/packages/trace-viewer/src/ui/actionList.tsx index f9aff122ca62d..5be5ff5b4b019 100644 --- a/packages/trace-viewer/src/ui/actionList.tsx +++ b/packages/trace-viewer/src/ui/actionList.tsx @@ -117,12 +117,12 @@ const ActionParameterContext: React.FC<{
- {parameterString} + {parameterString.value}
); }; diff --git a/packages/trace-viewer/src/ui/string.ts b/packages/trace-viewer/src/ui/string.ts index 204772b30327e..9607526ec1e6d 100644 --- a/packages/trace-viewer/src/ui/string.ts +++ b/packages/trace-viewer/src/ui/string.ts @@ -17,21 +17,28 @@ import type { ActionTraceEvent } from '@trace/trace'; import { asLocator, type Language } from '@isomorphic/locatorGenerators'; +export interface ActionParameterDisplayString { + type: 'generic' | 'locator'; + value: string; +} + const formatClockParams = (params: { ticksNumber?: number; ticksString?: string; timeNumber?: number; -}): string | undefined => { +}): ActionParameterDisplayString | undefined => { + let value: string | undefined = undefined; + if (params.ticksNumber !== undefined) { // clock.fastForward/runFor - return `${params.ticksNumber}ms`; + value = `${params.ticksNumber}ms`; } else if (params.ticksString !== undefined) { // clock.fastForward/runFor - return params.ticksString; + value = params.ticksString; } else if (params.timeNumber !== undefined) { // clock.pauseAt/setFixedTime/setSystemTime try { - return new Date(params.timeNumber).toLocaleString(undefined, { + value = new Date(params.timeNumber).toLocaleString(undefined, { timeZone: 'UTC', }); } catch (e) { @@ -39,30 +46,44 @@ const formatClockParams = (params: { } } - return undefined; + if (value === undefined) + return undefined; + + return { + type: 'generic', + value, + }; }; const formatLocatorParams = ( sdkLanguage: Language, params: { selector?: string }, -): string | undefined => +): ActionParameterDisplayString | undefined => params.selector !== undefined - ? asLocator(sdkLanguage, params.selector) + ? { type: 'locator', value: asLocator(sdkLanguage, params.selector) } : undefined; const formatKeyboardParams = (params: { key?: string; text?: string; -}): string | undefined => { +}): ActionParameterDisplayString | undefined => { + let value: string | undefined = undefined; + if (params.key !== undefined) { // keyboard.press/down/up - return params.key; + value = params.key; } else if (params.text !== undefined) { // keyboard.type/insertText - return `"${params.text}"`; + value = `"${params.text}"`; } - return undefined; + if (value === undefined) + return undefined; + + return { + type: 'generic', + value, + }; }; const formatMouseParams = (params: { @@ -70,36 +91,52 @@ const formatMouseParams = (params: { y?: number; deltaX?: number; deltaY?: number; -}): string | undefined => { +}): ActionParameterDisplayString | undefined => { + let value: string | undefined = undefined; + if (params.x !== undefined && params.y !== undefined) { // mouse.click/dblclick/move - return `(${params.x}, ${params.y})`; + value = `(${params.x}, ${params.y})`; } else if (params.deltaX !== undefined && params.deltaY !== undefined) { // mouse.wheel - return `(${params.deltaX}, ${params.deltaY})`; + value = `(${params.deltaX}, ${params.deltaY})`; } - return undefined; + if (value === undefined) + return undefined; + + return { + type: 'generic', + value, + }; }; const formatTouchscreenParams = (params: { x?: number; y?: number; -}): string | undefined => { +}): ActionParameterDisplayString | undefined => { + let value: string | undefined = undefined; + if (params.x && params.y) { // touchscreen.tap - return `(${params.x}, ${params.y})`; + value = `(${params.x}, ${params.y})`; } - return undefined; + if (value === undefined) + return undefined; + + return { + type: 'generic', + value, + }; }; export const actionParameterDisplayString = ( action: ActionTraceEvent, sdkLanguage: Language, -): string | undefined => { +): ActionParameterDisplayString | undefined => { const params = action.params; - const apiName = action.apiName; + const apiName = action.apiName.toLowerCase(); switch (true) { case apiName.startsWith('clock'): @@ -108,6 +145,7 @@ export const actionParameterDisplayString = ( return formatKeyboardParams(params); case apiName.startsWith('locator'): case apiName.startsWith('expect'): + case apiName.startsWith('frame'): return formatLocatorParams(sdkLanguage, params); case apiName.startsWith('mouse'): return formatMouseParams(params); From 742bcfc04a002ce0acfa478f895d954ddb57a260 Mon Sep 17 00:00:00 2001 From: Adam Gastineau Date: Tue, 14 Jan 2025 07:40:13 -0800 Subject: [PATCH 09/17] Flatten actionParameterDisplayString parsing as action class changes --- packages/trace-viewer/src/ui/string.ts | 111 ++++--------------------- 1 file changed, 14 insertions(+), 97 deletions(-) diff --git a/packages/trace-viewer/src/ui/string.ts b/packages/trace-viewer/src/ui/string.ts index 9607526ec1e6d..436ca827b594e 100644 --- a/packages/trace-viewer/src/ui/string.ts +++ b/packages/trace-viewer/src/ui/string.ts @@ -22,18 +22,21 @@ export interface ActionParameterDisplayString { value: string; } -const formatClockParams = (params: { - ticksNumber?: number; - ticksString?: string; - timeNumber?: number; -}): ActionParameterDisplayString | undefined => { +export const actionParameterDisplayString = ( + action: ActionTraceEvent, + sdkLanguage: Language, +): ActionParameterDisplayString | undefined => { + const params = action.params; + let value: string | undefined = undefined; - if (params.ticksNumber !== undefined) { - // clock.fastForward/runFor + if (params.selector !== undefined) { + return { type: 'locator', value: asLocator(sdkLanguage, params.selector) }; + } else if (params.ticksNumber !== undefined) { + // clock.fastForward/runFor number value = `${params.ticksNumber}ms`; } else if (params.ticksString !== undefined) { - // clock.fastForward/runFor + // clock.fastForward/runFor string value = params.ticksString; } else if (params.timeNumber !== undefined) { // clock.pauseAt/setFixedTime/setSystemTime @@ -44,80 +47,19 @@ const formatClockParams = (params: { } catch (e) { return undefined; } - } - - if (value === undefined) - return undefined; - - return { - type: 'generic', - value, - }; -}; - -const formatLocatorParams = ( - sdkLanguage: Language, - params: { selector?: string }, -): ActionParameterDisplayString | undefined => - params.selector !== undefined - ? { type: 'locator', value: asLocator(sdkLanguage, params.selector) } - : undefined; - -const formatKeyboardParams = (params: { - key?: string; - text?: string; -}): ActionParameterDisplayString | undefined => { - let value: string | undefined = undefined; - - if (params.key !== undefined) { + } else if (params.key !== undefined) { // keyboard.press/down/up value = params.key; } else if (params.text !== undefined) { // keyboard.type/insertText value = `"${params.text}"`; - } - - if (value === undefined) - return undefined; - - return { - type: 'generic', - value, - }; -}; - -const formatMouseParams = (params: { - x?: number; - y?: number; - deltaX?: number; - deltaY?: number; -}): ActionParameterDisplayString | undefined => { - let value: string | undefined = undefined; - - if (params.x !== undefined && params.y !== undefined) { + } else if (params.x !== undefined && params.y !== undefined) { // mouse.click/dblclick/move value = `(${params.x}, ${params.y})`; } else if (params.deltaX !== undefined && params.deltaY !== undefined) { // mouse.wheel value = `(${params.deltaX}, ${params.deltaY})`; - } - - if (value === undefined) - return undefined; - - return { - type: 'generic', - value, - }; -}; - -const formatTouchscreenParams = (params: { - x?: number; - y?: number; -}): ActionParameterDisplayString | undefined => { - let value: string | undefined = undefined; - - if (params.x && params.y) { + } else if (params.x && params.y) { // touchscreen.tap value = `(${params.x}, ${params.y})`; } @@ -130,28 +72,3 @@ const formatTouchscreenParams = (params: { value, }; }; - -export const actionParameterDisplayString = ( - action: ActionTraceEvent, - sdkLanguage: Language, -): ActionParameterDisplayString | undefined => { - const params = action.params; - const apiName = action.apiName.toLowerCase(); - - switch (true) { - case apiName.startsWith('clock'): - return formatClockParams(params); - case apiName.startsWith('keyboard'): - return formatKeyboardParams(params); - case apiName.startsWith('locator'): - case apiName.startsWith('expect'): - case apiName.startsWith('frame'): - return formatLocatorParams(sdkLanguage, params); - case apiName.startsWith('mouse'): - return formatMouseParams(params); - case apiName.startsWith('touchscreen'): - return formatTouchscreenParams(params); - default: - return undefined; - } -}; From b27fd7096a9c7698ab6240199d3d7600533c18be Mon Sep 17 00:00:00 2001 From: Adam Gastineau Date: Tue, 14 Jan 2025 07:44:14 -0800 Subject: [PATCH 10/17] Add expect locator test for display in Trace Viewer --- tests/library/trace-viewer.spec.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/library/trace-viewer.spec.ts b/tests/library/trace-viewer.spec.ts index 7ec43ca9902f0..4ba4876440b87 100644 --- a/tests/library/trace-viewer.spec.ts +++ b/tests/library/trace-viewer.spec.ts @@ -174,6 +174,7 @@ test('should show action context on locators and other common actions', async ({ await page.setContent(''); await page.locator('input').click({ button: 'right' }); await page.getByRole('textbox').click(); + await expect(page.locator('input')).toHaveText(''); await page.keyboard.type( 'Hello world this is a very long string what happens when it overflows?', ); @@ -198,6 +199,7 @@ test('should show action context on locators and other common actions', async ({ /page.setContent/, /locator.clicklocator\('input'\)/, /locator.clickgetByRole\('textbox'\)/, + /expect.toHaveTextlocator\('input'\)/, /keyboard.type\"Hello world this is a very long string what happens when it overflows\?\"/, /keyboard.pressControl\+c/, /keyboard.downShift/, From 8e29f4fca551b13cd8588030c2dc7b5935c3b8fd Mon Sep 17 00:00:00 2001 From: Adam Gastineau Date: Wed, 15 Jan 2025 12:34:35 -0800 Subject: [PATCH 11/17] Properly print setFixedTime values from other language clients --- packages/trace-viewer/src/ui/string.ts | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/packages/trace-viewer/src/ui/string.ts b/packages/trace-viewer/src/ui/string.ts index 436ca827b594e..572ff241bfce8 100644 --- a/packages/trace-viewer/src/ui/string.ts +++ b/packages/trace-viewer/src/ui/string.ts @@ -38,12 +38,18 @@ export const actionParameterDisplayString = ( } else if (params.ticksString !== undefined) { // clock.fastForward/runFor string value = params.ticksString; - } else if (params.timeNumber !== undefined) { + } else if ( + params.timeString !== undefined || + params.timeNumber !== undefined + ) { // clock.pauseAt/setFixedTime/setSystemTime try { - value = new Date(params.timeNumber).toLocaleString(undefined, { - timeZone: 'UTC', - }); + value = new Date(params.timeString ?? params.timeNumber).toLocaleString( + undefined, + { + timeZone: 'UTC', + }, + ); } catch (e) { return undefined; } From 1a31594b751eb66075760de5bf146e2d68310988 Mon Sep 17 00:00:00 2001 From: Adam Gastineau Date: Thu, 16 Jan 2025 05:43:40 -0800 Subject: [PATCH 12/17] Improved code style --- packages/trace-viewer/src/ui/actionList.tsx | 101 +++++++++++++++----- packages/trace-viewer/src/ui/string.ts | 80 ---------------- 2 files changed, 75 insertions(+), 106 deletions(-) delete mode 100644 packages/trace-viewer/src/ui/string.ts diff --git a/packages/trace-viewer/src/ui/actionList.tsx b/packages/trace-viewer/src/ui/actionList.tsx index 5be5ff5b4b019..3f5cadcc6118e 100644 --- a/packages/trace-viewer/src/ui/actionList.tsx +++ b/packages/trace-viewer/src/ui/actionList.tsx @@ -19,13 +19,12 @@ import { clsx, msToString } from '@web/uiUtils'; import * as React from 'react'; import './actionList.css'; import * as modelUtil from './modelUtil'; -import type { Language } from '@isomorphic/locatorGenerators'; +import { asLocator, type Language } from '@isomorphic/locatorGenerators'; import type { TreeState } from '@web/components/treeView'; import { TreeView } from '@web/components/treeView'; import type { ActionTraceEventInContext, ActionTreeItem } from './modelUtil'; import type { Boundaries } from './geometry'; import { ToolbarButton } from '@web/components/toolbarButton'; -import { actionParameterDisplayString } from './string'; export interface ActionListProps { actions: ActionTraceEventInContext[], @@ -104,29 +103,6 @@ export const ActionList: React.FC = ({ ; }; -const ActionParameterContext: React.FC<{ - action: ActionTraceEvent; - sdkLanguage: Language; -}> = ({ action, sdkLanguage }) => { - const parameterString = actionParameterDisplayString(action, sdkLanguage); - - if (parameterString === undefined) - return null; - - return ( -
- {parameterString.value} -
- ); -}; - export const renderAction = ( action: ActionTraceEvent, options: { @@ -141,6 +117,8 @@ export const renderAction = ( const { errors, warnings } = modelUtil.stats(action); const showAttachments = !!action.attachments?.length && !!revealAttachment; + const parameterString = actionParameterDisplayString(action, sdkLanguage || 'javascript'); + let time: string = ''; if (action.endTime) time = msToString(action.endTime - action.startTime); @@ -151,7 +129,16 @@ export const renderAction = ( return <>
{action.apiName} - + {parameterString &&
+ {parameterString.value} +
} {action.method === 'goto' && action.params.url &&
{action.params.url}
} {action.class === 'APIRequestContext' && action.params.url &&
{excludeOrigin(action.params.url)}
}
@@ -173,3 +160,65 @@ function excludeOrigin(url: string): string { return url; } } + +interface ActionParameterDisplayString { + type: 'generic' | 'locator'; + value: string; +} + +const actionParameterDisplayString = ( + action: ActionTraceEvent, + sdkLanguage: Language, +): ActionParameterDisplayString | undefined => { + const params = action.params; + + let value: string | undefined = undefined; + + if (params.selector !== undefined) { + return { type: 'locator', value: asLocator(sdkLanguage, params.selector) }; + } else if (params.ticksNumber !== undefined) { + // clock.fastForward/runFor number + value = `${params.ticksNumber}ms`; + } else if (params.ticksString !== undefined) { + // clock.fastForward/runFor string + value = params.ticksString; + } else if ( + params.timeString !== undefined || + params.timeNumber !== undefined + ) { + // clock.pauseAt/setFixedTime/setSystemTime + try { + value = new Date(params.timeString ?? params.timeNumber).toLocaleString( + undefined, + { + timeZone: 'UTC', + }, + ); + } catch (e) { + return undefined; + } + } else if (params.key !== undefined) { + // keyboard.press/down/up + value = params.key; + } else if (params.text !== undefined) { + // keyboard.type/insertText + value = `"${params.text}"`; + } else if (params.x !== undefined && params.y !== undefined) { + // mouse.click/dblclick/move + value = `(${params.x}, ${params.y})`; + } else if (params.deltaX !== undefined && params.deltaY !== undefined) { + // mouse.wheel + value = `(${params.deltaX}, ${params.deltaY})`; + } else if (params.x && params.y) { + // touchscreen.tap + value = `(${params.x}, ${params.y})`; + } + + if (value === undefined) + return undefined; + + return { + type: 'generic', + value, + }; +}; diff --git a/packages/trace-viewer/src/ui/string.ts b/packages/trace-viewer/src/ui/string.ts deleted file mode 100644 index 572ff241bfce8..0000000000000 --- a/packages/trace-viewer/src/ui/string.ts +++ /dev/null @@ -1,80 +0,0 @@ -/** - * Copyright (c) Microsoft Corporation. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import type { ActionTraceEvent } from '@trace/trace'; -import { asLocator, type Language } from '@isomorphic/locatorGenerators'; - -export interface ActionParameterDisplayString { - type: 'generic' | 'locator'; - value: string; -} - -export const actionParameterDisplayString = ( - action: ActionTraceEvent, - sdkLanguage: Language, -): ActionParameterDisplayString | undefined => { - const params = action.params; - - let value: string | undefined = undefined; - - if (params.selector !== undefined) { - return { type: 'locator', value: asLocator(sdkLanguage, params.selector) }; - } else if (params.ticksNumber !== undefined) { - // clock.fastForward/runFor number - value = `${params.ticksNumber}ms`; - } else if (params.ticksString !== undefined) { - // clock.fastForward/runFor string - value = params.ticksString; - } else if ( - params.timeString !== undefined || - params.timeNumber !== undefined - ) { - // clock.pauseAt/setFixedTime/setSystemTime - try { - value = new Date(params.timeString ?? params.timeNumber).toLocaleString( - undefined, - { - timeZone: 'UTC', - }, - ); - } catch (e) { - return undefined; - } - } else if (params.key !== undefined) { - // keyboard.press/down/up - value = params.key; - } else if (params.text !== undefined) { - // keyboard.type/insertText - value = `"${params.text}"`; - } else if (params.x !== undefined && params.y !== undefined) { - // mouse.click/dblclick/move - value = `(${params.x}, ${params.y})`; - } else if (params.deltaX !== undefined && params.deltaY !== undefined) { - // mouse.wheel - value = `(${params.deltaX}, ${params.deltaY})`; - } else if (params.x && params.y) { - // touchscreen.tap - value = `(${params.x}, ${params.y})`; - } - - if (value === undefined) - return undefined; - - return { - type: 'generic', - value, - }; -}; From 60bb08838b4b9036092986ee50d52b2391aaf741 Mon Sep 17 00:00:00 2001 From: Adam Gastineau Date: Thu, 16 Jan 2025 06:23:25 -0800 Subject: [PATCH 13/17] Group parameter display strings by class/method --- packages/trace-viewer/src/ui/actionList.tsx | 170 ++++++++++++++------ 1 file changed, 124 insertions(+), 46 deletions(-) diff --git a/packages/trace-viewer/src/ui/actionList.tsx b/packages/trace-viewer/src/ui/actionList.tsx index 3f5cadcc6118e..2d5241851c747 100644 --- a/packages/trace-viewer/src/ui/actionList.tsx +++ b/packages/trace-viewer/src/ui/actionList.tsx @@ -166,59 +166,137 @@ interface ActionParameterDisplayString { value: string; } +const clockDisplayString = ( + action: ActionTraceEvent, +): ActionParameterDisplayString | undefined => { + switch (action.method) { + case 'clockPauseAt': + case 'clockSetFixedTime': + case 'clockSetSystemTime': { + if ( + action.params.timeString === undefined && + action.params.timeNumber === undefined + ) + return undefined; + return { + type: 'generic', + value: new Date( + action.params.timeString ?? action.params.timeNumber, + ).toLocaleString(undefined, { timeZone: 'UTC' }), + }; + } + case 'clockFastForward': + case 'clockRunFor': { + if ( + action.params.ticksNumber === undefined && + action.params.ticksString === undefined + ) + return undefined; + return { + type: 'generic', + value: action.params.ticksString ?? `${action.params.ticksNumber}ms`, + }; + } + } + + return undefined; +}; + +const keyboardDisplayString = ( + action: ActionTraceEvent, +): ActionParameterDisplayString | undefined => { + switch (action.method) { + case 'press': + case 'keyboardPress': + case 'keyboardDown': + case 'keyboardUp': { + if (action.params.key === undefined) + return undefined; + return { type: 'generic', value: action.params.key }; + } + case 'type': + case 'fill': + case 'keyboardType': + case 'keyboardInsertText': { + if (action.params.text === undefined) + return undefined; + return { type: 'generic', value: `"${action.params.text}"` }; + } + } +}; + +const mouseDisplayString = ( + action: ActionTraceEvent, +): ActionParameterDisplayString | undefined => { + switch (action.method) { + case 'click': + case 'dblclick': + case 'mouseClick': + case 'mouseMove': { + if (action.params.x === undefined || action.params.y === undefined) + return undefined; + return { + type: 'generic', + value: `(${action.params.x}, ${action.params.y})`, + }; + } + case 'mouseWheel': { + if ( + action.params.deltaX === undefined || + action.params.deltaY === undefined + ) + return undefined; + return { + type: 'generic', + value: `(${action.params.deltaX}, ${action.params.deltaY})`, + }; + } + } +}; + +const touchscreenDisplayString = ( + action: ActionTraceEvent, +): ActionParameterDisplayString | undefined => { + switch (action.method) { + case 'tap': { + if (action.params.x === undefined || action.params.y === undefined) + return undefined; + return { + type: 'generic', + value: `(${action.params.x}, ${action.params.y})`, + }; + } + } +}; + const actionParameterDisplayString = ( action: ActionTraceEvent, sdkLanguage: Language, ): ActionParameterDisplayString | undefined => { const params = action.params; - let value: string | undefined = undefined; - - if (params.selector !== undefined) { + // Locators have many possible classes, so follow existing logic and use `selector` presence + if (params.selector !== undefined) return { type: 'locator', value: asLocator(sdkLanguage, params.selector) }; - } else if (params.ticksNumber !== undefined) { - // clock.fastForward/runFor number - value = `${params.ticksNumber}ms`; - } else if (params.ticksString !== undefined) { - // clock.fastForward/runFor string - value = params.ticksString; - } else if ( - params.timeString !== undefined || - params.timeNumber !== undefined - ) { - // clock.pauseAt/setFixedTime/setSystemTime - try { - value = new Date(params.timeString ?? params.timeNumber).toLocaleString( - undefined, - { - timeZone: 'UTC', - }, - ); - } catch (e) { - return undefined; - } - } else if (params.key !== undefined) { - // keyboard.press/down/up - value = params.key; - } else if (params.text !== undefined) { - // keyboard.type/insertText - value = `"${params.text}"`; - } else if (params.x !== undefined && params.y !== undefined) { - // mouse.click/dblclick/move - value = `(${params.x}, ${params.y})`; - } else if (params.deltaX !== undefined && params.deltaY !== undefined) { - // mouse.wheel - value = `(${params.deltaX}, ${params.deltaY})`; - } else if (params.x && params.y) { - // touchscreen.tap - value = `(${params.x}, ${params.y})`; - } - if (value === undefined) - return undefined; + switch (action.class.toLowerCase()) { + case 'browsercontext': + return clockDisplayString(action); + case 'page': + case 'frame': + case 'elementhandle': + let string = keyboardDisplayString(action); + + if (string !== undefined) + return string; + + string = mouseDisplayString(action); + + if (string !== undefined) + return string; + + return touchscreenDisplayString(action); + } - return { - type: 'generic', - value, - }; + return undefined; }; From 0fd80b09e1221438e6f35c1c9920969f0dbb2012 Mon Sep 17 00:00:00 2001 From: Adam Gastineau Date: Thu, 16 Jan 2025 06:31:06 -0800 Subject: [PATCH 14/17] Enable displaying locator and parameter context at the same time --- packages/trace-viewer/src/ui/actionList.tsx | 60 ++++++++++++++------- 1 file changed, 42 insertions(+), 18 deletions(-) diff --git a/packages/trace-viewer/src/ui/actionList.tsx b/packages/trace-viewer/src/ui/actionList.tsx index 2d5241851c747..8d673761ae554 100644 --- a/packages/trace-viewer/src/ui/actionList.tsx +++ b/packages/trace-viewer/src/ui/actionList.tsx @@ -129,16 +129,23 @@ export const renderAction = ( return <>
{action.apiName} - {parameterString &&
- {parameterString.value} -
} + {parameterString && + (parameterString.type === 'locator' ? ( + <> + + {parameterString.value} + + {parameterString.childDisplayString && ( + + {parameterString.childDisplayString.value} + + )} + + ) : ( + + {parameterString.value} + + ))} {action.method === 'goto' && action.params.url &&
{action.params.url}
} {action.class === 'APIRequestContext' && action.params.url &&
{excludeOrigin(action.params.url)}
}
@@ -161,10 +168,16 @@ function excludeOrigin(url: string): string { } } -interface ActionParameterDisplayString { - type: 'generic' | 'locator'; - value: string; -} +type ActionParameterDisplayString = + | { + type: 'generic'; + value: string; + } + | { + type: 'locator'; + value: string; + childDisplayString?: ActionParameterDisplayString; + }; const clockDisplayString = ( action: ActionTraceEvent, @@ -218,9 +231,10 @@ const keyboardDisplayString = ( case 'fill': case 'keyboardType': case 'keyboardInsertText': { - if (action.params.text === undefined) + const string = action.params.text ?? action.params.value; + if (string === undefined) return undefined; - return { type: 'generic', value: `"${action.params.text}"` }; + return { type: 'generic', value: `"${string}"` }; } } }; @@ -272,12 +286,22 @@ const touchscreenDisplayString = ( const actionParameterDisplayString = ( action: ActionTraceEvent, sdkLanguage: Language, + ignoreLocator: boolean = false, ): ActionParameterDisplayString | undefined => { const params = action.params; // Locators have many possible classes, so follow existing logic and use `selector` presence - if (params.selector !== undefined) - return { type: 'locator', value: asLocator(sdkLanguage, params.selector) }; + if (!ignoreLocator && params.selector !== undefined) { + return { + type: 'locator', + value: asLocator(sdkLanguage, params.selector), + childDisplayString: actionParameterDisplayString( + action, + sdkLanguage, + true, + ), + }; + } switch (action.class.toLowerCase()) { case 'browsercontext': From 57ed7d314bcec238ea711b894b8b835931d9e891 Mon Sep 17 00:00:00 2001 From: Adam Gastineau Date: Thu, 16 Jan 2025 06:35:26 -0800 Subject: [PATCH 15/17] Remove unnecessary import --- packages/trace-viewer/src/ui/actionList.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/trace-viewer/src/ui/actionList.tsx b/packages/trace-viewer/src/ui/actionList.tsx index 8d673761ae554..4ed76c61651f4 100644 --- a/packages/trace-viewer/src/ui/actionList.tsx +++ b/packages/trace-viewer/src/ui/actionList.tsx @@ -15,7 +15,7 @@ */ import type { ActionTraceEvent, AfterActionTraceEventAttachment } from '@trace/trace'; -import { clsx, msToString } from '@web/uiUtils'; +import { msToString } from '@web/uiUtils'; import * as React from 'react'; import './actionList.css'; import * as modelUtil from './modelUtil'; From e6c8812ff40eebd477b068dee01771045014380a Mon Sep 17 00:00:00 2001 From: Adam Gastineau Date: Thu, 16 Jan 2025 11:23:05 -0800 Subject: [PATCH 16/17] Style changes --- packages/trace-viewer/src/ui/actionList.tsx | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/packages/trace-viewer/src/ui/actionList.tsx b/packages/trace-viewer/src/ui/actionList.tsx index 4ed76c61651f4..87e78416b0fb0 100644 --- a/packages/trace-viewer/src/ui/actionList.tsx +++ b/packages/trace-viewer/src/ui/actionList.tsx @@ -309,17 +309,11 @@ const actionParameterDisplayString = ( case 'page': case 'frame': case 'elementhandle': - let string = keyboardDisplayString(action); - - if (string !== undefined) - return string; - - string = mouseDisplayString(action); - - if (string !== undefined) - return string; - - return touchscreenDisplayString(action); + return ( + keyboardDisplayString(action) ?? + mouseDisplayString(action) ?? + touchscreenDisplayString(action) + ); } return undefined; From 527b6254d577d2b0ad281326151f5e057fee9ab7 Mon Sep 17 00:00:00 2001 From: Adam Gastineau Date: Thu, 16 Jan 2025 11:25:20 -0800 Subject: [PATCH 17/17] Added test for simultaneous display of locator and parameter displayText --- tests/library/trace-viewer.spec.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/library/trace-viewer.spec.ts b/tests/library/trace-viewer.spec.ts index 4ba4876440b87..4761fd20d63a2 100644 --- a/tests/library/trace-viewer.spec.ts +++ b/tests/library/trace-viewer.spec.ts @@ -175,6 +175,7 @@ test('should show action context on locators and other common actions', async ({ await page.locator('input').click({ button: 'right' }); await page.getByRole('textbox').click(); await expect(page.locator('input')).toHaveText(''); + await page.locator('input').press('Enter'); await page.keyboard.type( 'Hello world this is a very long string what happens when it overflows?', ); @@ -200,6 +201,7 @@ test('should show action context on locators and other common actions', async ({ /locator.clicklocator\('input'\)/, /locator.clickgetByRole\('textbox'\)/, /expect.toHaveTextlocator\('input'\)/, + /locator.presslocator\('input'\)Enter/, /keyboard.type\"Hello world this is a very long string what happens when it overflows\?\"/, /keyboard.pressControl\+c/, /keyboard.downShift/,