Time
{!!model.wallTime &&
start time:{new Date(model.wallTime).toLocaleString()}
}
duration:{msToString(model.endTime - model.startTime)}
diff --git a/packages/trace-viewer/src/ui/watchMode.css b/packages/trace-viewer/src/ui/watchMode.css
index 66eb8ce0e4..00093916b8 100644
--- a/packages/trace-viewer/src/ui/watchMode.css
+++ b/packages/trace-viewer/src/ui/watchMode.css
@@ -44,7 +44,7 @@
margin: 0;
}
-.watch-mode-sidebar .section-title {
+.watch-mode .section-title {
font-size: 11px;
text-transform: uppercase;
font-weight: bold;
diff --git a/packages/trace-viewer/src/ui/watchMode.tsx b/packages/trace-viewer/src/ui/watchMode.tsx
index e08fc8e433..89c1e473f0 100644
--- a/packages/trace-viewer/src/ui/watchMode.tsx
+++ b/packages/trace-viewer/src/ui/watchMode.tsx
@@ -62,6 +62,7 @@ export const WatchModeView: React.FC<{}> = ({
const [visibleTestIds, setVisibleTestIds] = React.useState
([]);
const [filterText, setFilterText] = React.useState('');
const [filterExpanded, setFilterExpanded] = React.useState(false);
+ const [isShowingOutput, setIsShowingOutput] = React.useState(false);
const inputRef = React.useRef(null);
React.useEffect(() => {
@@ -125,9 +126,24 @@ export const WatchModeView: React.FC<{}> = ({
};
const result = selectedTest?.results[0];
- return
+ const isFinished = result && result.duration >= 0;
+ return
- {(result && result.duration >= 0) ? : }
+
+
+
+ Output
+ xtermDataSource.clear()}>
+
+ setIsShowingOutput(false)}>
+
+
;
+
+
+ {isFinished && }
+ {!isFinished && }
+
+
setSettingsVisible(false)}>Tests
@@ -137,6 +153,7 @@ export const WatchModeView: React.FC<{}> = ({
setIsWatchingFiles(!isWatchingFiles)}>
{ setSettingsVisible(!settingsVisible); }}>
+ { setIsShowingOutput(!isShowingOutput); }}>
{!settingsVisible &&
;
+ return
;
};
export const FinishedTraceView: React.FC<{
@@ -337,16 +354,7 @@ export const FinishedTraceView: React.FC<{
loadSingleTraceFile(attachment.path).then(setModel);
}, [testResult]);
- return
;
-};
-
-export const TraceView: React.FC<{
- model: MultiTraceModel | undefined,
-}> = ({ model }) => {
- const xterm =
;
- return
xtermDataSource.clear()}>,
- ]} hideTimelineBars={true} hideStackFrames={true} />;
+ return ;
};
declare global {
diff --git a/packages/trace-viewer/src/ui/workbench.tsx b/packages/trace-viewer/src/ui/workbench.tsx
index 2f50123aa9..daa224aeeb 100644
--- a/packages/trace-viewer/src/ui/workbench.tsx
+++ b/packages/trace-viewer/src/ui/workbench.tsx
@@ -33,42 +33,65 @@ import { MetadataView } from './metadataView';
export const Workbench: React.FunctionComponent<{
model?: MultiTraceModel,
- output?: React.ReactElement,
- rightToolbar?: React.ReactElement[],
hideTimelineBars?: boolean,
hideStackFrames?: boolean,
-}> = ({ model, output, rightToolbar, hideTimelineBars, hideStackFrames }) => {
+ showSourcesFirst?: boolean,
+}> = ({ model, hideTimelineBars, hideStackFrames, showSourcesFirst }) => {
const [selectedAction, setSelectedAction] = React.useState(undefined);
const [highlightedAction, setHighlightedAction] = React.useState();
const [selectedNavigatorTab, setSelectedNavigatorTab] = React.useState('actions');
- const [selectedPropertiesTab, setSelectedPropertiesTab] = React.useState(output ? 'output' : 'call');
+ const [selectedPropertiesTab, setSelectedPropertiesTab] = React.useState(showSourcesFirst ? 'source' : 'call');
const activeAction = model ? highlightedAction || selectedAction : undefined;
React.useEffect(() => {
- if (selectedAction)
+ if (selectedAction && model?.actions.includes(selectedAction))
return;
const failedAction = model?.actions.find(a => a.error);
if (failedAction)
setSelectedAction(failedAction);
- // In the UI mode, selecting the first error should reveal source.
- if (failedAction && output)
- setSelectedPropertiesTab('source');
- }, [model, output, selectedAction, setSelectedAction, setSelectedPropertiesTab]);
+ else if (model?.actions.length)
+ setSelectedAction(model.actions[model.actions.length - 1]);
+ }, [model, selectedAction, setSelectedAction, setSelectedPropertiesTab]);
const { errors, warnings } = activeAction ? modelUtil.stats(activeAction) : { errors: 0, warnings: 0 };
const consoleCount = errors + warnings;
const networkCount = activeAction ? modelUtil.resourcesForAction(activeAction).length : 0;
const sdkLanguage = model?.sdkLanguage || 'javascript';
- const tabs: TabbedPaneTabModel[] = [
- { id: 'call', title: 'Call', render: () => },
- { id: 'source', title: 'Source', count: 0, render: () => },
- { id: 'console', title: 'Console', count: consoleCount, render: () => },
- { id: 'network', title: 'Network', count: networkCount, render: () => },
- ];
+ const callTab: TabbedPaneTabModel = {
+ id: 'call',
+ title: showSourcesFirst ? 'Log' : 'Call',
+ render: () =>
+ };
+ const sourceTab: TabbedPaneTabModel = {
+ id: 'source',
+ title: 'Source',
+ render: () =>
+ };
+ const consoleTab: TabbedPaneTabModel = {
+ id: 'console',
+ title: 'Console',
+ count: consoleCount,
+ render: () =>
+ };
+ const networkTab: TabbedPaneTabModel = {
+ id: 'network',
+ title: 'Network',
+ count: networkCount,
+ render: () =>
+ };
- if (output)
- tabs.unshift({ id: 'output', title: 'Output', component: output });
+ const tabs: TabbedPaneTabModel[] = showSourcesFirst ? [
+ sourceTab,
+ consoleTab,
+ networkTab,
+ callTab,
+ ] : [
+ callTab,
+ consoleTab,
+ networkTab,
+ sourceTab,
+ ];
return
setSelectedAction(action)}
hideTimelineBars={hideTimelineBars}
/>
-
+
-
+
;
};
diff --git a/packages/web/src/common.css b/packages/web/src/common.css
index e1a640f2a4..01e443375b 100644
--- a/packages/web/src/common.css
+++ b/packages/web/src/common.css
@@ -72,7 +72,8 @@ body {
min-height: 0;
}
-*[hidden] {
+*[hidden],
+.hidden {
display: none !important;
}
diff --git a/packages/web/src/components/codeMirrorWrapper.tsx b/packages/web/src/components/codeMirrorWrapper.tsx
index dae38cf131..38558f1bf3 100644
--- a/packages/web/src/components/codeMirrorWrapper.tsx
+++ b/packages/web/src/components/codeMirrorWrapper.tsx
@@ -146,7 +146,7 @@ export const CodeMirrorWrapper: React.FC = ({
}
codemirrorRef.current!.widgets = widgets;
- if (revealLine)
+ if (revealLine && codemirrorRef.current!.cm.lineCount() >= revealLine)
codemirror.scrollIntoView({ line: revealLine - 1, ch: 0 }, 50);
}, [codemirror, text, highlight, revealLine, focusOnChange, onChange]);
diff --git a/tests/library/trace-viewer.spec.ts b/tests/library/trace-viewer.spec.ts
index ff8b7123ee..a101ab7845 100644
--- a/tests/library/trace-viewer.spec.ts
+++ b/tests/library/trace-viewer.spec.ts
@@ -633,7 +633,7 @@ test('should follow redirects', async ({ page, runAndTrace, server, asset }) =>
test('should include metainfo', async ({ showTraceViewer, browserName }) => {
const traceViewer = await showTraceViewer([traceFile]);
await traceViewer.page.locator('text=Metadata').click();
- const callLine = traceViewer.page.locator('.call-line');
+ const callLine = traceViewer.page.locator('.metadata-view .call-line');
await expect(callLine.getByText('start time')).toHaveText(/start time:[\d/,: ]+/);
await expect(callLine.getByText('duration')).toHaveText(/duration:[\dms]+/);
await expect(callLine.getByText('engine')).toHaveText(/engine:[\w]+/);
diff --git a/tests/playwright-test/reporter-html.spec.ts b/tests/playwright-test/reporter-html.spec.ts
index 755c5f9ab1..6628a1a39e 100644
--- a/tests/playwright-test/reporter-html.spec.ts
+++ b/tests/playwright-test/reporter-html.spec.ts
@@ -37,6 +37,8 @@ const test = baseTest.extend<{ showReport: (reportFolder?: string) => Promise {
await runInlineTest({
'playwright.config.ts': `
@@ -473,7 +475,6 @@ test('should warn user when viewing via file:// protocol', async ({ runInlineTes
await test.step('view via server', async () => {
await showReport();
await page.locator('[title="View trace"]').click();
- await expect(page.locator('body')).toContainText('Action does not have snapshots', { useInnerText: true });
await expect(page.locator('dialog')).toBeHidden();
});