diff --git a/packages/trace-viewer/src/ui/logTab.css b/packages/trace-viewer/src/ui/logTab.css new file mode 100644 index 0000000000000..413da150b0514 --- /dev/null +++ b/packages/trace-viewer/src/ui/logTab.css @@ -0,0 +1,31 @@ +/** + * 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. + */ + +.log-list-duration { + display: flex; + flex: none; + align-items: center; + color: var(--vscode-editorCodeLens-foreground); + float: right; + margin-right: 5px; + user-select: none; +} + +.log-list-item { + text-wrap: wrap; + user-select: text; + width: 100%; +} diff --git a/packages/trace-viewer/src/ui/logTab.tsx b/packages/trace-viewer/src/ui/logTab.tsx index 98bd5bc3a4580..45b7ebe989279 100644 --- a/packages/trace-viewer/src/ui/logTab.tsx +++ b/packages/trace-viewer/src/ui/logTab.tsx @@ -18,17 +18,51 @@ import type { ActionTraceEventInContext } from './modelUtil'; import * as React from 'react'; import { ListView } from '@web/components/listView'; import { PlaceholderPanel } from './placeholderPanel'; +import { msToString } from '@web/uiUtils'; +import './logTab.css'; -const LogList = ListView<{ message: string, time: number }>; +const LogList = ListView<{ message: string, time: string }>; export const LogTab: React.FunctionComponent<{ action: ActionTraceEventInContext | undefined, -}> = ({ action }) => { - if (!action?.log.length) + isLive: boolean | undefined, +}> = ({ action, isLive }) => { + const entries = React.useMemo(() => { + if (!action || !action.log.length) + return []; + const log = action.log; + const wallTimeOffset = action.wallTime - action.startTime; + const entries: { message: string, time: string }[] = []; + for (let i = 0; i < log.length; ++i) { + let time = ''; + if (log[i].time !== -1) { + const timeStart = log[i]?.time; + if (i + 1 < log.length) + time = msToString(log[i + 1].time - timeStart); + else if (action.endTime > 0) + time = msToString(action.endTime - timeStart); + else if (isLive) + time = msToString(Date.now() - wallTimeOffset - timeStart); + else + time = '-'; + } + entries.push({ + message: log[i].message, + time, + }); + } + return entries; + }, [action]); + if (!entries.length) return ; + return logLine.message} + items={entries} + render={entry =>
+ {entry.time} + {entry.message} +
} + noHighlightOnHover={true} />; }; diff --git a/packages/trace-viewer/src/ui/workbench.tsx b/packages/trace-viewer/src/ui/workbench.tsx index 703ff72c503aa..46d8bbfbd4073 100644 --- a/packages/trace-viewer/src/ui/workbench.tsx +++ b/packages/trace-viewer/src/ui/workbench.tsx @@ -131,7 +131,7 @@ export const Workbench: React.FunctionComponent<{ const logTab: TabbedPaneTabModel = { id: 'log', title: 'Log', - render: () => + render: () => }; const errorsTab: TabbedPaneTabModel = { id: 'errors', @@ -199,7 +199,7 @@ export const Workbench: React.FunctionComponent<{ }, [model]); let time: number = 0; - if (model && model.endTime >= 0) + if (!isLive && model && model.endTime >= 0) time = model.endTime - model.startTime; else if (model && model.wallTime) time = Date.now() - model.wallTime; diff --git a/packages/web/src/components/listView.tsx b/packages/web/src/components/listView.tsx index 28bd4e4dd7dfc..3722337f145d1 100644 --- a/packages/web/src/components/listView.tsx +++ b/packages/web/src/components/listView.tsx @@ -35,6 +35,7 @@ export type ListViewProps = { onIconClicked?: (item: T, index: number) => void, noItemsMessage?: string, dataTestId?: string, + noHighlightOnHover?: boolean, }; const scrollPositions = new Map(); @@ -57,6 +58,7 @@ export function ListView({ onIconClicked, noItemsMessage, dataTestId, + noHighlightOnHover, }: ListViewProps) { const itemListRef = React.useRef(null); const [highlightedItem, setHighlightedItem] = React.useState(); @@ -131,7 +133,7 @@ export function ListView({ {noItemsMessage && items.length === 0 &&
{noItemsMessage}
} {items.map((item, index) => { const selectedSuffix = selectedItem === item ? ' selected' : ''; - const highlightedSuffix = highlightedItem === item ? ' highlighted' : ''; + const highlightedSuffix = !noHighlightOnHover && highlightedItem === item ? ' highlighted' : ''; const errorSuffix = isError?.(item, index) ? ' error' : ''; const warningSuffix = isWarning?.(item, index) ? ' warning' : ''; const indentation = indent?.(item, index) || 0; diff --git a/tests/library/trace-viewer.spec.ts b/tests/library/trace-viewer.spec.ts index 34244125ce389..123a1fd70fdf5 100644 --- a/tests/library/trace-viewer.spec.ts +++ b/tests/library/trace-viewer.spec.ts @@ -123,10 +123,10 @@ test('should contain action info', async ({ showTraceViewer }) => { const traceViewer = await showTraceViewer([traceFile]); await traceViewer.selectAction('locator.click'); await traceViewer.page.getByText('Log', { exact: true }).click(); - const logLines = await traceViewer.logLines.allTextContents(); - expect(logLines.length).toBeGreaterThan(10); - expect(logLines).toContain('attempting click action'); - expect(logLines).toContain(' click action done'); + await expect(traceViewer.logLines).toContainText([ + /\d+m?sattempting click action/, + /\d+m?s click action done/, + ]); }); test('should render network bars', async ({ page, runAndTrace, server }) => {