diff --git a/packages/playwright-core/src/server/trace/common/traceEvents.ts b/packages/playwright-core/src/server/trace/common/traceEvents.ts index bea151955d..581b7c5fb1 100644 --- a/packages/playwright-core/src/server/trace/common/traceEvents.ts +++ b/packages/playwright-core/src/server/trace/common/traceEvents.ts @@ -24,12 +24,15 @@ export type BrowserContextEventOptions = { viewport?: Size, deviceScaleFactor?: number, isMobile?: boolean, + userAgent?: string, }; export type ContextCreatedTraceEvent = { version: number, type: 'context-options', browserName: string, + platform: string, + wallTime: number, title?: string, options: BrowserContextEventOptions }; diff --git a/packages/playwright-core/src/server/trace/recorder/tracing.ts b/packages/playwright-core/src/server/trace/recorder/tracing.ts index f9a445f9dd..9620bdfe3f 100644 --- a/packages/playwright-core/src/server/trace/recorder/tracing.ts +++ b/packages/playwright-core/src/server/trace/recorder/tracing.ts @@ -81,7 +81,9 @@ export class Tracing implements InstrumentationListener, SnapshotterDelegate, Ha version: VERSION, type: 'context-options', browserName: this._context._browser.options.name, - options: this._context._options + options: this._context._options, + platform: process.platform, + wallTime: 0, }; } @@ -124,7 +126,7 @@ export class Tracing implements InstrumentationListener, SnapshotterDelegate, Ha this._appendTraceOperation(async () => { await mkdirIfNeeded(state.traceFile); - await fs.promises.appendFile(state.traceFile, JSON.stringify({ ...this._contextCreatedEvent, title: options.title }) + '\n'); + await fs.promises.appendFile(state.traceFile, JSON.stringify({ ...this._contextCreatedEvent, title: options.title, wallTime: Date.now() }) + '\n'); }); this._context.instrumentation.addListener(this); diff --git a/packages/playwright-core/src/web/traceViewer/entries.ts b/packages/playwright-core/src/web/traceViewer/entries.ts index 900304de08..c731e2dd0a 100644 --- a/packages/playwright-core/src/web/traceViewer/entries.ts +++ b/packages/playwright-core/src/web/traceViewer/entries.ts @@ -21,6 +21,8 @@ export type ContextEntry = { startTime: number; endTime: number; browserName: string; + platform?: string; + wallTime?: number; title?: string; options: trace.BrowserContextEventOptions; pages: PageEntry[]; diff --git a/packages/playwright-core/src/web/traceViewer/traceModel.ts b/packages/playwright-core/src/web/traceViewer/traceModel.ts index 56edc1eeda..d32aa3fdc3 100644 --- a/packages/playwright-core/src/web/traceViewer/traceModel.ts +++ b/packages/playwright-core/src/web/traceViewer/traceModel.ts @@ -104,6 +104,8 @@ export class TraceModel { case 'context-options': { this.contextEntry.browserName = event.browserName; this.contextEntry.title = event.title; + this.contextEntry.platform = event.platform; + this.contextEntry.wallTime = event.wallTime; this.contextEntry.options = event.options; break; } diff --git a/packages/playwright-core/src/web/traceViewer/ui/actionList.css b/packages/playwright-core/src/web/traceViewer/ui/actionList.css index d1d606a5fa..8d1890fe1d 100644 --- a/packages/playwright-core/src/web/traceViewer/ui/actionList.css +++ b/packages/playwright-core/src/web/traceViewer/ui/actionList.css @@ -14,10 +14,6 @@ limitations under the License. */ -.action-list-title { - padding: 0 10px; -} - .action-list-content { display: flex; flex-direction: column; diff --git a/packages/playwright-core/src/web/traceViewer/ui/actionList.tsx b/packages/playwright-core/src/web/traceViewer/ui/actionList.tsx index 0e7e533257..a737bf9a78 100644 --- a/packages/playwright-core/src/web/traceViewer/ui/actionList.tsx +++ b/packages/playwright-core/src/web/traceViewer/ui/actionList.tsx @@ -45,11 +45,6 @@ export const ActionList: React.FC = ({ }, [selectedAction, actionListRef]); return
-
-
-
Actions
-
-
= () => { @@ -35,7 +36,8 @@ export const Workbench: React.FunctionComponent<{ const [contextEntry, setContextEntry] = React.useState(emptyContext); const [selectedAction, setSelectedAction] = React.useState(); const [highlightedAction, setHighlightedAction] = React.useState(); - const [selectedTab, setSelectedTab] = React.useState('logs'); + const [selectedNavigatorTab, setSelectedNavigatorTab] = React.useState('actions'); + const [selectedPropertiesTab, setSelectedPropertiesTab] = React.useState('logs'); const [progress, setProgress] = React.useState<{ done: number, total: number }>({ done: 0, total: 0 }); const [dragOver, setDragOver] = React.useState(false); @@ -122,32 +124,55 @@ export const Workbench: React.FunctionComponent<{ - + - { - setSelectedAction(action); - }} - onHighlighted={action => setHighlightedAction(action)} - setSelectedTab={setSelectedTab} - /> + { + setSelectedAction(action); + }} + onHighlighted={action => setHighlightedAction(action)} + setSelectedTab={setSelectedPropertiesTab} + /> }, + { id: 'metadata', title: 'Metadata', count: 0, render: () =>
+
Time
+ {contextEntry.wallTime &&
start time: {new Date(contextEntry.wallTime).toLocaleString()}
} +
duration: {msToString(contextEntry.endTime - contextEntry.startTime)}
+
Browser
+
engine: {contextEntry.browserName}
+ {contextEntry.platform &&
platform: {contextEntry.platform}
} + {contextEntry.options.userAgent &&
user agent: {contextEntry.options.userAgent}
} +
Viewport
+ {contextEntry.options.viewport &&
width: {contextEntry.options.viewport.width}
} + {contextEntry.options.viewport &&
height: {contextEntry.options.viewport.height}
} +
is mobile: {String(!!contextEntry.options.isMobile)}
+ {contextEntry.options.deviceScaleFactor &&
device scale: {String(contextEntry.options.deviceScaleFactor)}
} +
Counts
+
pages: {contextEntry.pages.length}
+
actions: {contextEntry.actions.length}
+
events: {contextEntry.events.length}
+
}, + ] + } selectedTab={selectedNavigatorTab} setSelectedTab={setSelectedNavigatorTab}/>
{!!progress.total &&
} {!dragOver && !traceURL &&
Drop Playwright Trace to load
+
or
-
Playwright Trace Viewer is a progressive web app, it does not send your trace anywhere, - it opens it locally instead.
+ }}>Select file +
Playwright Trace Viewer is a Progressive Web App, it does not send your trace anywhere, + it opens it locally.
} {dragOver &&
{ setDragOver(false); }} diff --git a/tests/trace-viewer/trace-viewer.spec.ts b/tests/trace-viewer/trace-viewer.spec.ts index 847f9efade..f5e0a6ba1b 100644 --- a/tests/trace-viewer/trace-viewer.spec.ts +++ b/tests/trace-viewer/trace-viewer.spec.ts @@ -579,3 +579,18 @@ test('should follow redirects', async ({ page, runAndTrace, server, asset }) => const snapshotFrame = await traceViewer.snapshotFrame('page.evaluate'); await expect(snapshotFrame.locator('img')).toHaveJSProperty('naturalWidth', 10); }); + +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'); + await expect(callLine.locator('text=start time')).toHaveText(/start time: [\d/,: ]+/); + await expect(callLine.locator('text=duration')).toHaveText(/duration: [\dms]+/); + await expect(callLine.locator('text=engine')).toHaveText(/engine: [\w]+/); + await expect(callLine.locator('text=platform')).toHaveText(/platform: [\w]+/); + await expect(callLine.locator('text=width')).toHaveText(/width: [\d]+/); + await expect(callLine.locator('text=height')).toHaveText(/height: [\d]+/); + await expect(callLine.locator('text=pages')).toHaveText(/pages: 1/); + await expect(callLine.locator('text=actions')).toHaveText(/actions: [\d]+/); + await expect(callLine.locator('text=events')).toHaveText(/events: [\d]+/); +});