diff --git a/packages/trace-viewer/src/traceModel.ts b/packages/trace-viewer/src/traceModel.ts index 3cd9bebaef..005f625492 100644 --- a/packages/trace-viewer/src/traceModel.ts +++ b/packages/trace-viewer/src/traceModel.ts @@ -76,13 +76,6 @@ export class TraceModel { unzipProgress(++done, total); contextEntry.actions = [...actionMap.values()].sort((a1, a2) => a1.startTime - a2.startTime); - if (!backend.isLive()) { - for (const action of contextEntry.actions) { - if (!action.endTime && !action.error) - action.error = { name: 'Error', message: 'Timed out' }; - } - } - const stacks = await this._backend.readText(ordinal + '.stacks'); if (stacks) { const callMetadata = parseClientSideCallMetadata(JSON.parse(stacks)); diff --git a/packages/trace-viewer/src/ui/actionList.tsx b/packages/trace-viewer/src/ui/actionList.tsx index 22fe0e9eb4..f883e55e5d 100644 --- a/packages/trace-viewer/src/ui/actionList.tsx +++ b/packages/trace-viewer/src/ui/actionList.tsx @@ -31,6 +31,7 @@ export interface ActionListProps { onSelected: (action: ActionTraceEvent) => void, onHighlighted: (action: ActionTraceEvent | undefined) => void, revealConsole: () => void, + isLive?: boolean, } type ActionTreeItem = { @@ -49,6 +50,7 @@ export const ActionList: React.FC = ({ onSelected, onHighlighted, revealConsole, + isLive, }) => { const [treeState, setTreeState] = React.useState({ expandedItems: new Map() }); const { rootItem, itemMap } = React.useMemo(() => { @@ -86,14 +88,15 @@ export const ActionList: React.FC = ({ onSelected={item => onSelected(item.action!)} onHighlighted={item => onHighlighted(item?.action)} isError={item => !!item.action?.error?.message} - render={item => renderAction(item.action!, sdkLanguage, revealConsole)} + render={item => renderAction(item.action!, sdkLanguage, revealConsole, isLive || false)} />; }; const renderAction = ( action: ActionTraceEvent, sdkLanguage: Language | undefined, - revealConsole: () => void + revealConsole: () => void, + isLive: boolean, ) => { const { errors, warnings } = modelUtil.stats(action); const locator = action.params.selector ? asLocator(sdkLanguage || 'javascript', action.params.selector, false /* isFrameLocator */, true /* playSafe */) : undefined; @@ -103,6 +106,8 @@ const renderAction = ( time = msToString(action.endTime - action.startTime); else if (action.error) time = 'Timed out'; + else if (!isLive) + time = '-'; return <>
{action.apiName} diff --git a/packages/trace-viewer/src/ui/uiModeView.tsx b/packages/trace-viewer/src/ui/uiModeView.tsx index 6cd85c514e..8582e39934 100644 --- a/packages/trace-viewer/src/ui/uiModeView.tsx +++ b/packages/trace-viewer/src/ui/uiModeView.tsx @@ -492,7 +492,7 @@ const TraceView: React.FC<{ item: { testFile?: SourceLocation, testCase?: TestCase }, rootDir?: string, }> = ({ item, rootDir }) => { - const [model, setModel] = React.useState(); + const [model, setModel] = React.useState<{ model: MultiTraceModel, isLive: boolean } | undefined>(); const [counter, setCounter] = React.useState(0); const pollTimer = React.useRef(null); @@ -505,7 +505,7 @@ const TraceView: React.FC<{ // This avoids auto-selection of the last action every time we reload the model. const [selectedActionId, setSelectedActionId] = React.useState(); const onSelectionChanged = React.useCallback((action: ActionTraceEvent) => setSelectedActionId(idForAction(action)), [setSelectedActionId]); - const initialSelection = selectedActionId ? model?.actions.find(a => idForAction(a) === selectedActionId) : undefined; + const initialSelection = selectedActionId ? model?.model.actions.find(a => idForAction(a) === selectedActionId) : undefined; React.useEffect(() => { if (pollTimer.current) @@ -520,7 +520,7 @@ const TraceView: React.FC<{ // Test finished. const attachment = result && result.duration >= 0 && result.attachments.find(a => a.name === 'trace'); if (attachment && attachment.path) { - loadSingleTraceFile(attachment.path).then(model => setModel(model)); + loadSingleTraceFile(attachment.path).then(model => setModel({ model, isLive: false })); return; } @@ -534,7 +534,7 @@ const TraceView: React.FC<{ pollTimer.current = setTimeout(async () => { try { const model = await loadSingleTraceFile(traceLocation); - setModel(model); + setModel({ model, isLive: true }); } catch { setModel(undefined); } finally { @@ -549,14 +549,15 @@ const TraceView: React.FC<{ return ; + fallbackLocation={item.testFile} + isLive={model?.isLive} />; }; declare global { diff --git a/packages/trace-viewer/src/ui/workbench.tsx b/packages/trace-viewer/src/ui/workbench.tsx index 883058054f..c044d8d712 100644 --- a/packages/trace-viewer/src/ui/workbench.tsx +++ b/packages/trace-viewer/src/ui/workbench.tsx @@ -41,7 +41,8 @@ export const Workbench: React.FunctionComponent<{ fallbackLocation?: modelUtil.SourceLocation, initialSelection?: ActionTraceEvent, onSelectionChanged?: (action: ActionTraceEvent) => void, -}> = ({ model, hideTimelineBars, hideStackFrames, showSourcesFirst, rootDir, fallbackLocation, initialSelection, onSelectionChanged }) => { + isLive?: boolean, +}> = ({ model, hideTimelineBars, hideStackFrames, showSourcesFirst, rootDir, fallbackLocation, initialSelection, onSelectionChanged, isLive }) => { const [selectedAction, setSelectedAction] = React.useState(undefined); const [highlightedAction, setHighlightedAction] = React.useState(); const [selectedNavigatorTab, setSelectedNavigatorTab] = React.useState('actions'); @@ -142,6 +143,7 @@ export const Workbench: React.FunctionComponent<{ onSelected={onActionSelected} onHighlighted={setHighlightedAction} revealConsole={() => setSelectedPropertiesTab('console')} + isLive={isLive} /> }, { diff --git a/tests/playwright-test/ui-mode-trace.spec.ts b/tests/playwright-test/ui-mode-trace.spec.ts index 9194e74add..2e07f7464b 100644 --- a/tests/playwright-test/ui-mode-trace.spec.ts +++ b/tests/playwright-test/ui-mode-trace.spec.ts @@ -98,9 +98,12 @@ test('should merge screenshot assertions', async ({ runUITest }, testInfo) => { 'action list' ).toHaveText([ /Before Hooks[\d.]+m?s/, - /page\.setContent[\d.]+m?s/, - /expect\.toHaveScreenshot[\d.]+m?s/, - /After Hooks/, + /page.setContent[\d.]+m?s/, + /expect.toHaveScreenshot[\d.]+m?s/, + /After Hooks-/, + /fixture: page[\d.]+m?s/, + /fixture: context[\d.]+m?s/, + /fixture: browser[\d.]+m?s/, ]); });