[ve] Instrument and test performance panel

Bug: 308381366, 348173254
Change-Id: Ie1e2d4a757adaea70ede3ab5298183ceb238384c
Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/5741263
Reviewed-by: Jack Franklin <jacktfranklin@chromium.org>
Auto-Submit: Danil Somsikov <dsv@chromium.org>
Commit-Queue: Danil Somsikov <dsv@chromium.org>
diff --git a/front_end/panels/mobile_throttling/ThrottlingManager.ts b/front_end/panels/mobile_throttling/ThrottlingManager.ts
index cf87b0b..acd99ca 100644
--- a/front_end/panels/mobile_throttling/ThrottlingManager.ts
+++ b/front_end/panels/mobile_throttling/ThrottlingManager.ts
@@ -299,7 +299,7 @@
   createCPUThrottlingSelector(): UI.Toolbar.ToolbarComboBox {
     const control = new UI.Toolbar.ToolbarComboBox(
         event => this.setCPUThrottlingRate(this.cpuThrottlingRates[(event.target as HTMLSelectElement).selectedIndex]),
-        i18nString(UIStrings.cpuThrottling), '', 'cpu-throttling-selector');
+        i18nString(UIStrings.cpuThrottling), '', 'cpu-throttling');
     this.cpuThrottlingControls.add(control);
     const currentRate = this.cpuThrottlingManager.cpuThrottlingRate();
 
@@ -322,8 +322,8 @@
     warning: UI.Toolbar.ToolbarItem,
     toggle: UI.Toolbar.ToolbarItem,
   } {
-    const input = new UI.Toolbar.ToolbarItem(
-        UI.UIUtils.createInput('devtools-text-input', 'number', 'hardware-concurrency-selector'));
+    const input =
+        new UI.Toolbar.ToolbarItem(UI.UIUtils.createInput('devtools-text-input', 'number', 'hardware-concurrency'));
     input.setTitle(i18nString(UIStrings.hardwareConcurrencySettingTooltip));
     const inputElement = input.element as HTMLInputElement;
     inputElement.min = '1';
@@ -331,7 +331,7 @@
 
     const toggle = new UI.Toolbar.ToolbarCheckbox(
         i18nString(UIStrings.hardwareConcurrency), i18nString(UIStrings.hardwareConcurrencySettingTooltip), undefined,
-        'hardware-concurrency-toggle');
+        'hardware-concurrency');
     const reset = new UI.Toolbar.ToolbarButton('Reset concurrency', 'undo', undefined, 'hardware-concurrency-reset');
     reset.setTitle(i18nString(UIStrings.resetConcurrency));
     const icon = new IconButton.Icon.Icon();
diff --git a/front_end/panels/timeline/EventsTimelineTreeView.ts b/front_end/panels/timeline/EventsTimelineTreeView.ts
index 9b7a949..b8fc103 100644
--- a/front_end/panels/timeline/EventsTimelineTreeView.ts
+++ b/front_end/panels/timeline/EventsTimelineTreeView.ts
@@ -8,6 +8,7 @@
 import * as TraceEngine from '../../models/trace/trace.js';
 import * as DataGrid from '../../ui/legacy/components/data_grid/data_grid.js';
 import * as UI from '../../ui/legacy/legacy.js';
+import * as VisualLogging from '../../ui/visual_logging/visual_logging.js';
 
 import {type EventCategory, getCategoryStyles} from './EventUICategory.js';
 import {Category, IsLong} from './TimelineFilters.js';
@@ -43,6 +44,7 @@
   private currentTree!: TimelineModel.TimelineProfileTree.Node;
   constructor(delegate: TimelineModeViewDelegate) {
     super();
+    this.element.setAttribute('jslog', `${VisualLogging.pane('event-log').track({resize: true})}`);
     this.filtersControl = new Filters();
     this.filtersControl.addEventListener(Events.FilterChanged, this.onFilterChanged, this);
     this.init();
@@ -166,8 +168,8 @@
   }
 
   populateToolbar(toolbar: UI.Toolbar.Toolbar): void {
-    const durationFilterUI =
-        new UI.Toolbar.ToolbarComboBox(durationFilterChanged.bind(this), i18nString(UIStrings.durationFilter));
+    const durationFilterUI = new UI.Toolbar.ToolbarComboBox(
+        durationFilterChanged.bind(this), i18nString(UIStrings.durationFilter), undefined, 'duration');
     for (const durationMs of Filters.durationFilterPresetsMs) {
       durationFilterUI.addOption(durationFilterUI.createOption(
           durationMs ? `≥ ${i18nString(UIStrings.Dms, {PH1: durationMs})}` : i18nString(UIStrings.all),
@@ -183,7 +185,7 @@
         continue;
       }
       const checkbox = new UI.Toolbar.ToolbarCheckbox(
-          category.title, undefined, categoriesFilterChanged.bind(this, categoryName as EventCategory));
+          category.title, undefined, categoriesFilterChanged.bind(this, categoryName as EventCategory), categoryName);
       checkbox.setChecked(true);
       checkbox.inputElement.style.backgroundColor = category.color;
       categoryFiltersUI.set(category.name, checkbox);
diff --git a/front_end/panels/timeline/TimelineDetailsView.ts b/front_end/panels/timeline/TimelineDetailsView.ts
index 60e404c..7b6142c 100644
--- a/front_end/panels/timeline/TimelineDetailsView.ts
+++ b/front_end/panels/timeline/TimelineDetailsView.ts
@@ -10,6 +10,7 @@
 import * as TraceBounds from '../../services/trace_bounds/trace_bounds.js';
 import * as Components from '../../ui/legacy/components/utils/utils.js';
 import * as UI from '../../ui/legacy/legacy.js';
+import * as VisualLogging from '../../ui/visual_logging/visual_logging.js';
 
 import * as TimelineComponents from './components/components.js';
 import {EventsTimelineTreeView} from './EventsTimelineTreeView.js';
@@ -86,9 +87,13 @@
 
     this.tabbedPane = new UI.TabbedPane.TabbedPane();
     this.tabbedPane.show(this.element);
+    this.tabbedPane.headerElement().setAttribute(
+        'jslog',
+        `${VisualLogging.toolbar('sidebar').track({keydown: 'ArrowUp|ArrowLeft|ArrowDown|ArrowRight|Enter|Space'})}`);
 
     this.defaultDetailsWidget = new UI.Widget.VBox();
     this.defaultDetailsWidget.element.classList.add('timeline-details-view');
+    this.defaultDetailsWidget.element.setAttribute('jslog', `${VisualLogging.pane('details').track({resize: true})}`);
     this.defaultDetailsContentElement =
         this.defaultDetailsWidget.element.createChild('div', 'timeline-details-view-body vbox');
     this.appendTab(Tab.Details, i18nString(UIStrings.summary), this.defaultDetailsWidget);
diff --git a/front_end/panels/timeline/TimelineHistoryManager.ts b/front_end/panels/timeline/TimelineHistoryManager.ts
index 900d99c..7c03424 100644
--- a/front_end/panels/timeline/TimelineHistoryManager.ts
+++ b/front_end/panels/timeline/TimelineHistoryManager.ts
@@ -671,6 +671,7 @@
   constructor(action: UI.ActionRegistration.Action) {
     const element = document.createElement('button');
     element.classList.add('history-dropdown-button');
+    element.setAttribute('jslog', `${VisualLogging.dropDown('history')}`);
     super(element);
     this.contentElement = this.element.createChild('span', 'content');
     this.element.addEventListener('click', () => void action.execute(), false);
diff --git a/front_end/panels/timeline/TimelineLandingPage.ts b/front_end/panels/timeline/TimelineLandingPage.ts
index ef61de4..e46460e 100644
--- a/front_end/panels/timeline/TimelineLandingPage.ts
+++ b/front_end/panels/timeline/TimelineLandingPage.ts
@@ -90,7 +90,8 @@
     this.contentElement.classList.add('legacy');
     const centered = this.contentElement.createChild('div');
 
-    const recordButton = UI.UIUtils.createInlineButton(UI.Toolbar.Toolbar.createActionButton(this.toggleRecordAction));
+    const recordButton = UI.UIUtils.createInlineButton(
+        UI.Toolbar.Toolbar.createActionButton(this.toggleRecordAction, {showLabel: false, ignoreToggleable: true}));
     const reloadButton =
         UI.UIUtils.createInlineButton(UI.Toolbar.Toolbar.createActionButtonForId('timeline.record-reload'));
 
diff --git a/front_end/panels/timeline/TimelinePanel.ts b/front_end/panels/timeline/TimelinePanel.ts
index 606e454..e74a5c7 100644
--- a/front_end/panels/timeline/TimelinePanel.ts
+++ b/front_end/panels/timeline/TimelinePanel.ts
@@ -838,7 +838,7 @@
     // Record
     this.panelToolbar.appendToolbarItem(UI.Toolbar.Toolbar.createActionButton(this.toggleRecordAction));
     this.panelToolbar.appendToolbarItem(UI.Toolbar.Toolbar.createActionButton(this.recordReloadAction));
-    this.clearButton = new UI.Toolbar.ToolbarButton(i18nString(UIStrings.clear), 'clear');
+    this.clearButton = new UI.Toolbar.ToolbarButton(i18nString(UIStrings.clear), 'clear', undefined, 'timeline.clear');
     this.clearButton.addEventListener(UI.Toolbar.ToolbarButton.Events.Click, () => this.onClearButton());
     this.panelToolbar.appendToolbarItem(this.clearButton);
 
@@ -2119,6 +2119,7 @@
     super(true);
 
     this.contentElement.classList.add('timeline-status-dialog');
+    this.contentElement.setAttribute('jslog', `${VisualLogging.dialog('timeline-status').track({resize: true})}`);
 
     const statusLine = this.contentElement.createChild('div', 'status-dialog-line status');
     statusLine.createChild('div', 'label').textContent = i18nString(UIStrings.status);
diff --git a/front_end/panels/timeline/TimelineSelectorStatsView.ts b/front_end/panels/timeline/TimelineSelectorStatsView.ts
index bc362f7..c43e478 100644
--- a/front_end/panels/timeline/TimelineSelectorStatsView.ts
+++ b/front_end/panels/timeline/TimelineSelectorStatsView.ts
@@ -11,6 +11,7 @@
 import * as Linkifier from '../../ui/components/linkifier/linkifier.js';
 import * as UI from '../../ui/legacy/legacy.js';
 import * as LitHtml from '../../ui/lit-html/lit-html.js';
+import * as VisualLogging from '../../ui/visual_logging/visual_logging.js';
 
 const UIStrings = {
   /**
@@ -107,6 +108,7 @@
     super();
 
     this.#datagrid = new DataGrid.DataGridController.DataGridController();
+    this.element.setAttribute('jslog', `${VisualLogging.pane('selector-stats').track({resize: true})}`);
     this.#selectorLocations = new Map<string, Protocol.CSS.SourceRange[]>();
     this.#traceParsedData = traceParsedData;
 
diff --git a/front_end/panels/timeline/TimelineTreeView.ts b/front_end/panels/timeline/TimelineTreeView.ts
index a1d5e4e..09470de 100644
--- a/front_end/panels/timeline/TimelineTreeView.ts
+++ b/front_end/panels/timeline/TimelineTreeView.ts
@@ -11,6 +11,7 @@
 import * as DataGrid from '../../ui/legacy/components/data_grid/data_grid.js';
 import * as Components from '../../ui/legacy/components/utils/utils.js';
 import * as UI from '../../ui/legacy/legacy.js';
+import * as VisualLogging from '../../ui/visual_logging/visual_logging.js';
 
 import {ActiveFilters} from './ActiveFilters.js';
 import {getCategoryStyles, stringIsEventCategory} from './EventUICategory.js';
@@ -219,6 +220,7 @@
     this.splitWidget = new UI.SplitWidget.SplitWidget(true, true, 'timeline-tree-view-details-split-widget');
     const mainView = new UI.Widget.VBox();
     const toolbar = new UI.Toolbar.Toolbar('', mainView.element);
+    toolbar.element.setAttribute('jslog', `${VisualLogging.toolbar()}`);
     toolbar.makeWrappable(true);
     this.populateToolbar(toolbar);
 
@@ -278,19 +280,22 @@
   }
 
   populateToolbar(toolbar: UI.Toolbar.Toolbar): void {
-    this.caseSensitiveButton = new UI.Toolbar.ToolbarToggle(i18nString(UIStrings.matchCase), 'match-case');
+    this.caseSensitiveButton =
+        new UI.Toolbar.ToolbarToggle(i18nString(UIStrings.matchCase), 'match-case', undefined, 'match-case');
     this.caseSensitiveButton.addEventListener(UI.Toolbar.ToolbarButton.Events.Click, () => {
       this.#filterChanged();
     }, this);
     toolbar.appendToolbarItem(this.caseSensitiveButton);
 
-    this.regexButton = new UI.Toolbar.ToolbarToggle(i18nString(UIStrings.useRegularExpression), 'regular-expression');
+    this.regexButton = new UI.Toolbar.ToolbarToggle(
+        i18nString(UIStrings.useRegularExpression), 'regular-expression', undefined, 'regular-expression');
     this.regexButton.addEventListener(UI.Toolbar.ToolbarButton.Events.Click, () => {
       this.#filterChanged();
     }, this);
     toolbar.appendToolbarItem(this.regexButton);
 
-    this.matchWholeWord = new UI.Toolbar.ToolbarToggle(i18nString(UIStrings.matchWholeWord), 'match-whole-word');
+    this.matchWholeWord = new UI.Toolbar.ToolbarToggle(
+        i18nString(UIStrings.matchWholeWord), 'match-whole-word', undefined, 'match-whole-word');
     this.matchWholeWord.addEventListener(UI.Toolbar.ToolbarButton.Events.Click, () => {
       this.#filterChanged();
     }, this);
@@ -973,6 +978,7 @@
 export class CallTreeTimelineTreeView extends AggregatedTimelineTreeView {
   constructor() {
     super();
+    this.element.setAttribute('jslog', `${VisualLogging.pane('call-tree').track({resize: true})}`);
     this.dataGrid.markColumnAsSortedBy('total', DataGrid.DataGrid.Order.Descending);
   }
 
@@ -985,6 +991,7 @@
 export class BottomUpTimelineTreeView extends AggregatedTimelineTreeView {
   constructor() {
     super();
+    this.element.setAttribute('jslog', `${VisualLogging.pane('bottom-up').track({resize: true})}`);
     this.dataGrid.markColumnAsSortedBy('self', DataGrid.DataGrid.Order.Descending);
   }
 
diff --git a/front_end/panels/timeline/components/BreadcrumbsUI.ts b/front_end/panels/timeline/components/BreadcrumbsUI.ts
index 92302d0..05f49d4 100644
--- a/front_end/panels/timeline/components/BreadcrumbsUI.ts
+++ b/front_end/panels/timeline/components/BreadcrumbsUI.ts
@@ -74,7 +74,7 @@
     // clang-format off
     return html`
           <div class="breadcrumb" @click=${() => this.#removeBreadcrumb(breadcrumb)}
-          jslog=${VisualLogging.action('timeline.breadcrumb-select').track({click: true})}>
+          jslog=${VisualLogging.item('timeline.breadcrumb-select').track({click: true})}>
            <span class="${(index !== 0 && breadcrumb.child === null) ? 'last-breadcrumb' : ''} range">
             ${(index === 0) ?
               `Full range (${i18n.TimeUtilities.preciseMillisToString(breadcrumbRange, 2)})` :
@@ -97,7 +97,7 @@
   #render(): void {
     // clang-format off
     const output = html`
-      ${this.#breadcrumb === null ? html`` : html`<div class="breadcrumbs">
+      ${this.#breadcrumb === null ? html`` : html`<div class="breadcrumbs" jslog=${VisualLogging.section('breadcrumbs')}>
         ${flattenBreadcrumbs(this.#breadcrumb).map((breadcrumb, index) => this.#renderElement(breadcrumb, index))}
       </div>`}
     `;
diff --git a/front_end/panels/timeline/components/CPUThrottlingSelector.ts b/front_end/panels/timeline/components/CPUThrottlingSelector.ts
index e69bff2..22a729c 100644
--- a/front_end/panels/timeline/components/CPUThrottlingSelector.ts
+++ b/front_end/panels/timeline/components/CPUThrottlingSelector.ts
@@ -80,6 +80,7 @@
             .sideButton=${false}
             .showSelectedItem=${true}
             .showConnector=${false}
+            .jslogContext=${'cpu-throttling'}
             .buttonTitle=${i18nString(UIStrings.cpu, {PH1: selectionTitle})}
             aria-label=${i18nString(UIStrings.cpuThrottling, {PH1: selectionTitle})}
           >
diff --git a/front_end/panels/timeline/components/FieldSettingsDialog.ts b/front_end/panels/timeline/components/FieldSettingsDialog.ts
index dd66c3f..ea1c423 100644
--- a/front_end/panels/timeline/components/FieldSettingsDialog.ts
+++ b/front_end/panels/timeline/components/FieldSettingsDialog.ts
@@ -252,8 +252,8 @@
           .data=${{
             variant: Buttons.Button.Variant.OUTLINED,
             title: i18nString(UIStrings.configure),
+            jslogContext: 'field-data-configure',
           } as Buttons.Button.ButtonData}
-          jslogContext=${'field-data-configure'}
         >${i18nString(UIStrings.configure)}</${Buttons.Button.Button.litTagName}>
       `;
       // clang-format on
diff --git a/front_end/ui/legacy/Toolbar.ts b/front_end/ui/legacy/Toolbar.ts
index 74697f9..6dad617 100644
--- a/front_end/ui/legacy/Toolbar.ts
+++ b/front_end/ui/legacy/Toolbar.ts
@@ -216,7 +216,7 @@
 
   static createActionButton(action: Action, options: ToolbarButtonOptions|undefined = TOOLBAR_BUTTON_DEFAULT_OPTIONS):
       ToolbarButton {
-    const button = action.toggleable() ? makeToggle() : makeButton();
+    const button = (action.toggleable() && !options?.ignoreToggleable) ? makeToggle() : makeButton();
 
     if (options.showLabel) {
       button.setText(options.label?.() || action.title());
@@ -441,6 +441,7 @@
   label?: () => Platform.UIString.LocalizedString;
   showLabel: boolean;
   userActionCode?: Host.UserMetrics.Action;
+  ignoreToggleable?: boolean;
 }
 
 const TOOLBAR_BUTTON_DEFAULT_OPTIONS: ToolbarButtonOptions = {
@@ -1189,7 +1190,7 @@
   private readonly setting: Common.Settings.Setting<string>;
   private muteSettingListener?: boolean;
   constructor(options: Option[], setting: Common.Settings.Setting<string>, accessibleName: string) {
-    super(null, accessibleName);
+    super(null, accessibleName, undefined, setting.name);
     this.optionsInternal = options;
     this.setting = setting;
     this.selectElementInternal.addEventListener('change', this.valueChanged.bind(this), false);
diff --git a/front_end/ui/visual_logging/KnownContextValues.ts b/front_end/ui/visual_logging/KnownContextValues.ts
index 81860a8..c4930d6 100644
--- a/front_end/ui/visual_logging/KnownContextValues.ts
+++ b/front_end/ui/visual_logging/KnownContextValues.ts
@@ -409,6 +409,7 @@
   'br',
   'brand-name',
   'brand-version',
+  'breadcrumbs',
   'break-after',
   'break-before',
   'break-inside',
@@ -700,6 +701,7 @@
   'cpu-throttled-20',
   'cpu-throttled-4',
   'cpu-throttled-6',
+  'cpu-throttling',
   'cpu-throttling-selector',
   'create-new-snippet',
   'create-recording',
@@ -1080,6 +1082,7 @@
   'fetch-and-xhr',
   'fi',
   'field-data',
+  'field-data-configure',
   'field-data-settings',
   'field-sizing',
   'field-url-override-enabled',
@@ -1234,6 +1237,7 @@
   'gu',
   'gutter',
   'gzip',
+  'hardware-concurrency',
   'hardware-concurrency-reset',
   'hardware-concurrency-selector',
   'hardware-concurrency-toggle',
@@ -1283,6 +1287,7 @@
   'hide-repeating-children',
   'highlight-errors-elements-panel',
   'highlight-node-on-hover-in-overlay',
+  'history',
   'historyRedo',
   'historyUndo',
   'hover',
@@ -1660,6 +1665,7 @@
   'mask-size',
   'mask-type',
   'match-case',
+  'match-whole-word',
   'match_attempts',
   'match_count',
   'matched-address-item',
@@ -1684,6 +1690,7 @@
   'messageURLFilters',
   'messageerror',
   'messages',
+  'messaging',
   'metadata',
   'metadata-allowed-sites-details',
   'method',
@@ -1914,6 +1921,7 @@
   'page',
   'page-orientation',
   'paint-order',
+  'painting',
   'palatino',
   'palette-panel',
   'palette-switcher',
@@ -2232,6 +2240,7 @@
   'script-snippets-last-identifier',
   'script-source-url',
   'script-text-node',
+  'scripting',
   'scroll',
   'scroll-behavior',
   'scroll-into-view',
@@ -2667,12 +2676,14 @@
   'timeline-show-postmessage-events',
   'timeline-show-screenshots',
   'timeline-show-settings-toolbar',
+  'timeline-status',
   'timeline-tree-current-thread',
   'timeline-tree-group-by',
   'timeline-v8-runtime-call-stats',
   'timeline.animations',
   'timeline.annotations-tab',
   'timeline.breadcrumb-select',
+  'timeline.clear',
   'timeline.create-breadcrumb',
   'timeline.download-after-error',
   'timeline.extension',
@@ -2769,6 +2780,7 @@
   'uk',
   'unavailable',
   'unblock',
+  'undefined',
   'undo',
   'unicode-bidi',
   'unit',
diff --git a/test/e2e/cross_tool_integration/workflow_test.ts b/test/e2e/cross_tool_integration/workflow_test.ts
index 1d76df8..2335c7c 100644
--- a/test/e2e/cross_tool_integration/workflow_test.ts
+++ b/test/e2e/cross_tool_integration/workflow_test.ts
@@ -17,7 +17,7 @@
 import {LAYERS_TAB_SELECTOR} from '../helpers/layers-helpers.js';
 import {MEMORY_TAB_ID, navigateToMemoryTab} from '../helpers/memory-helpers.js';
 import {
-  navigateToPerformanceSidebarTab,
+  navigateToBottomUpTab,
   navigateToPerformanceTab,
   startRecording,
   stopRecording,
@@ -75,7 +75,7 @@
 
     await stopRecording();
 
-    await navigateToPerformanceSidebarTab('Bottom-Up');
+    await navigateToBottomUpTab();
 
     await click('.devtools-link[title*="default.html"]');
     await waitFor('.panel[aria-label="sources"]');
diff --git a/test/e2e/helpers/cross-tool-helper.ts b/test/e2e/helpers/cross-tool-helper.ts
index acd41b3..53b4ed3 100644
--- a/test/e2e/helpers/cross-tool-helper.ts
+++ b/test/e2e/helpers/cross-tool-helper.ts
@@ -82,6 +82,7 @@
   await waitFor(`.panel.${selectedPanel}`);
   const expectClosedPanels = options?.expectClosedPanels;
   const newFilterBar = enableExperiments.includes('network-panel-filter-bar-redesign');
+  const timelineObservationLandingPage = enableExperiments.includes('timeline-observations');
   const dockable = options?.canDock;
   const panelImpression = selectedPanel === 'elements' ? veImpressionForElementsPanel({dockable}) :
       selectedPanel === 'animations'                   ? veImpressionForAnimationsPanel() :
@@ -89,12 +90,12 @@
       selectedPanel === 'layers'                       ? veImpressionForLayersPanel() :
       selectedPanel === 'network'                      ? veImpressionForNetworkPanel({newFilterBar}) :
       selectedPanel === 'console'                      ? veImpressionForConsolePanel() :
-      selectedPanel === 'timeline'                     ? veImpressionForPerformancePanel() :
-      selectedPanel === 'sources'                      ? veImpressionForSourcesPanel() :
-      selectedPanel === 'animations'                   ? veImpressionForSourcesPanel() :
-      selectedPanel === 'changes'                      ? veImpressionForChangesPanel() :
-      selectedPanel === 'resources'                    ? veImpressionForApplicationPanel() :
-                                                         veImpression('Panel', selectedPanel);
+      selectedPanel === 'timeline'   ? veImpressionForPerformancePanel({timelineObservationLandingPage}) :
+      selectedPanel === 'sources'    ? veImpressionForSourcesPanel() :
+      selectedPanel === 'animations' ? veImpressionForSourcesPanel() :
+      selectedPanel === 'changes'    ? veImpressionForChangesPanel() :
+      selectedPanel === 'resources'  ? veImpressionForApplicationPanel() :
+                                       veImpression('Panel', selectedPanel);
   const expectedVeEvents = [veImpressionForMainToolbar({selectedPanel, expectClosedPanels, dockable}), panelImpression];
   if (options?.drawerShown) {
     expectedVeEvents.push(veImpression('Drawer', undefined, [
diff --git a/test/e2e/helpers/performance-helpers.ts b/test/e2e/helpers/performance-helpers.ts
index eb1973d..ca3d666 100644
--- a/test/e2e/helpers/performance-helpers.ts
+++ b/test/e2e/helpers/performance-helpers.ts
@@ -9,7 +9,7 @@
   $,
   click,
   goToResource,
-  platform,
+  summonSearchBox,
   waitFor,
   waitForAria,
   waitForElementWithTextContent,
@@ -17,7 +17,15 @@
   waitForMany,
 } from '../../shared/helper.js';
 
-import {veImpression} from './visual-logging-helpers.js';
+import {
+  expectVeEvents,
+  veChange,
+  veClick,
+  veImpression,
+  veImpressionsUnder,
+  veKeyDown,
+  veResize,
+} from './visual-logging-helpers.js';
 
 export const FILTER_TEXTBOX_SELECTOR = '[aria-label="Filter"]';
 export const RECORD_BUTTON_SELECTOR = '[aria-label="Record"]';
@@ -44,52 +52,144 @@
 
   // Make sure the landing page is shown.
   await waitFor('.timeline-landing-page');
+  await expectVeEvents([veClick('Toolbar: main > PanelTabHeader: timeline'), veImpressionForPerformancePanel()]);
 }
 
 export async function openCaptureSettings(sectionClassName: string) {
   const captureSettingsButton = await waitForAria('Capture settings');
   await captureSettingsButton.click();
-  return await waitFor(sectionClassName);
+  await waitFor(sectionClassName);
+  await expectVeEvents(
+      [
+        veClick('Toolbar > Toggle: timeline-settings-toggle'),
+        veImpression(
+            'Pane', 'timeline-settings-pane',
+            [
+              veImpression('Toggle', 'timeline-capture-layers-and-pictures'),
+              veImpression('Toggle', 'timeline-capture-selector-stats'),
+              veImpression('Toggle', 'timeline-disable-js-sampling'),
+              veImpression('DropDown', 'cpu-throttling'),
+              veImpression('DropDown', 'preferred-network-condition'),
+              veImpression('Toggle', 'hardware-concurrency'),
+              veImpression('TextField', 'hardware-concurrency'),
+              veImpression('Action', 'hardware-concurrency-reset'),
+              veImpression('Toggle', 'timeline-show-extension-data'),
+            ]),
+      ],
+      'Panel: timeline');
 }
 
 export async function searchForComponent(frontend: puppeteer.Page, searchEntry: string) {
-  const modifierKey = platform === 'mac' ? 'Meta' : 'Control';
-  await frontend.keyboard.down(modifierKey);
-  await frontend.keyboard.press('KeyF');
-  await frontend.keyboard.up(modifierKey);
+  await waitFor('.timeline-details-chip-body');
+  await summonSearchBox();
+  await waitFor('.search-bar');
   await frontend.keyboard.type(searchEntry);
-}
-
-export async function navigateToSummaryTab() {
-  await click(SUMMARY_TAB_SELECTOR);
+  await frontend.keyboard.press('Tab');
+  await expectVeEvents([
+    veKeyDown(''),
+    veImpressionsUnder('Panel: timeline', [veImpression(
+                                              'Toolbar', 'search',
+                                              [
+                                                veImpression('TextField', 'search'),
+                                                veImpression('Action', 'regular-expression'),
+                                                veImpression('Action', 'match-case'),
+                                                veImpression('Action', 'select-previous'),
+                                                veImpression('Action', 'select-next'),
+                                                veImpression('Action', 'close-search'),
+                                              ])]),
+    veChange('Panel: timeline > Toolbar: search > TextField: search'),
+  ]);
 }
 
 export async function navigateToBottomUpTab() {
   await click(BOTTOM_UP_SELECTOR);
+  await expectVeEvents(
+      [
+        veClick('Toolbar: sidebar > PanelTabHeader: bottom-up'),
+        veImpression(
+            'Pane', 'bottom-up',
+            [
+              veImpression(
+                  'Toolbar', undefined,
+                  [
+                    veImpression('Toggle', 'match-case'),
+                    veImpression('Toggle', 'regular-expression'),
+                    veImpression('Toggle', 'match-whole-word'),
+                    veImpression('TextField', 'filter'),
+                    veImpression('DropDown', 'timeline-tree-group-by'),
+                  ]),
+              veImpression('TableHeader', 'self'),
+              veImpression('TableHeader', 'total'),
+              veImpression('TableHeader', 'activity'),
+              veImpression(
+                  'TableRow', undefined,
+                  [
+                    veImpression('TableCell', 'self'),
+                    veImpression('TableCell', 'total'),
+                    veImpression('TableCell', 'activity', [veImpression('Link', 'url')]),
+                  ]),
+            ]),
+
+      ],
+      'Panel: timeline');
 }
 
 export async function navigateToCallTreeTab() {
   await click(CALL_TREE_SELECTOR);
+  await expectVeEvents(
+      [
+        veClick('Toolbar: sidebar > PanelTabHeader: call-tree'),
+        veImpression(
+            'Pane', 'call-tree',
+            [
+              veImpression(
+                  'Toolbar', undefined,
+                  [
+                    veImpression('Toggle', 'match-case'),
+                    veImpression('Toggle', 'regular-expression'),
+                    veImpression('Toggle', 'match-whole-word'),
+                    veImpression('TextField', 'filter'),
+                    veImpression('DropDown', 'timeline-tree-group-by'),
+                  ]),
+              veImpression('TableHeader: self'),
+              veImpression('TableHeader: total'),
+              veImpression('TableHeader: activity'),
+              veImpression(
+                  'TableRow', undefined,
+                  [
+                    veImpression('TableCell: self'),
+                    veImpression('TableCell: total'),
+                    veImpression('TableCell: activity'),
+                  ]),
+            ]),
+      ],
+      'Panel: timeline');
 }
 
 export async function setFilter(filter: string) {
   const filterBoxElement = await click(FILTER_TEXTBOX_SELECTOR);
   await filterBoxElement.type(filter);
+  await expectVeEvents(
+      [veChange(''), veImpression('Action', 'clear')],
+      'Panel: timeline > Pane: bottom-up > Toolbar > TextField: filter');
 }
 
 export async function toggleCaseSensitive() {
   const matchCaseButton = await waitForAria('Match Case');
   await matchCaseButton.click();
+  await expectVeEvents([veClick('Panel: timeline > Pane: bottom-up > Toolbar > Toggle: match-case')]);
 }
 
 export async function toggleRegExButtonBottomUp() {
   const regexButton = await waitFor('[aria-label="Use Regular Expression"]');
   await regexButton.click();
+  await expectVeEvents([veClick('Panel: timeline > Pane: bottom-up > Toolbar > Toggle: regular-expression')]);
 }
 
 export async function toggleMatchWholeWordButtonBottomUp() {
   const wholeWordButton = await waitForAria('Match whole word');
   await wholeWordButton.click();
+  await expectVeEvents([veClick('Panel: timeline > Pane: bottom-up > Toolbar > Toggle: match-whole-word')]);
 }
 
 export async function startRecording() {
@@ -97,6 +197,8 @@
 
   // Wait for the button to turn to its stop state.
   await waitFor(STOP_BUTTON_SELECTOR);
+  await expectVeEvents(
+      [veClick('Toolbar > Toggle: timeline.toggle-recording'), veImpressionForStatusDialog()], 'Panel: timeline');
 }
 
 export async function reloadAndRecord() {
@@ -105,6 +207,8 @@
   // that a recording is actually displayed as some of the other elements in
   // the timeline remain in the DOM even after the recording has been cleared.
   await waitFor('.timeline-details-chip-body');
+  await expectVeEvents(
+      [veClick('Toolbar > Action: timeline.record-reload'), veImpressionForStatusDialog()], 'Panel: timeline');
 }
 
 export async function stopRecording() {
@@ -114,6 +218,12 @@
   // that a recording is actually displayed as some of the other elements in
   // the timeline remain in the DOM even after the recording has been cleared.
   await waitFor('.timeline-details-chip-body');
+  await expectVeEvents(
+      [
+        veClick('Toolbar > Toggle: timeline.toggle-recording'),
+        veResize('Dialog: timeline-status'),
+      ],
+      'Panel: timeline');
 }
 
 export async function getTotalTimeFromSummary(): Promise<number> {
@@ -143,16 +253,24 @@
   return tree;
 }
 
-export async function navigateToPerformanceSidebarTab(tabName: string) {
-  await click(`[aria-label="${tabName}"]`);
-}
-
-export async function clickOnFunctionLink() {
-  await click('.timeline-details.devtools-link');
-}
-
 export async function navigateToSelectorStatsTab() {
   await click(SELECTOR_STATS_SELECTOR);
+  await expectVeEvents(
+      [
+        veClick('Toolbar: sidebar > PanelTabHeader: selector-stats'),
+        veImpression(
+            'Pane', 'selector-stats',
+            [
+              veImpression('TableHeader', 'elapsed(us)'),
+              veImpression('TableHeader', 'match_attempts'),
+              veImpression('TableHeader', 'match_count'),
+              veImpression('TableHeader', 'reject_percentage'),
+              veImpression('TableHeader', 'selector'),
+              veImpression('TableHeader', 'style_sheet_id'),
+              veImpression('TableRow', undefined, [veImpression('TableCell')]),
+            ]),
+      ],
+      'Panel: timeline');
 }
 
 export async function selectRecalculateStylesEvent() {
@@ -187,6 +305,8 @@
     }
     return true;
   }));
+  await expectVeEvents(
+      [veChange('Panel: timeline > Pane: timeline-settings-pane > Toggle: timeline-capture-selector-stats')]);
 }
 
 export async function disableCSSSelectorStats() {
@@ -207,24 +327,35 @@
     }
     return true;
   }));
+  await expectVeEvents(
+      [veChange('Panel: timeline > Pane: timeline-settings-pane > Toggle: timeline-capture-selector-stats')]);
 }
 
-export function veImpressionForPerformancePanel() {
+export function veImpressionForPerformancePanel(options?: {timelineObservationLandingPage?: boolean}) {
   return veImpression('Panel', 'timeline', [
     veImpression(
         'Toolbar', undefined,
         [
           veImpression('Toggle', 'timeline.toggle-recording'),
           veImpression('Action', 'timeline.record-reload'),
+          veImpression('Action', 'timeline.clear'),
           veImpression('Action', 'timeline.load-from-file'),
           veImpression('Action', 'timeline.save-to-file'),
-          veImpression('Action', 'components.collect-garbage'),
+          veImpression('DropDown', 'history'),
           veImpression('Toggle', 'timeline-show-screenshots'),
           veImpression('Toggle', 'timeline-show-memory'),
+          veImpression('Action', 'components.collect-garbage'),
         ]),
-    // veImpression('Pane', 'timeline-settings-pane', {optional: true}),
-    veImpression('Link', 'learn-more'),
-    veImpression('Toggle', 'timeline.toggle-recording'),
+    veImpression('Action', 'timeline.toggle-recording'),
     veImpression('Action', 'timeline.record-reload'),
+    ...(options?.timelineObservationLandingPage ?
+            [veImpression('DropDown', 'cpu-throttling'), veImpression('DropDown', 'network-conditions')] :
+            [veImpression('Link', 'learn-more')]),
   ]);
 }
+
+function veImpressionForStatusDialog() {
+  return veImpression(
+      'Dialog', 'timeline-status',
+      [veImpression('Action', 'timeline.download-after-error'), veImpression('Action', 'timeline.stop-recording')]);
+}
diff --git a/test/e2e/performance/landing-page_test.ts b/test/e2e/performance/landing-page_test.ts
index fcc5a48..fda4467 100644
--- a/test/e2e/performance/landing-page_test.ts
+++ b/test/e2e/performance/landing-page_test.ts
@@ -7,7 +7,6 @@
 
 import {
   $$,
-  enableExperiment,
   getBrowserAndPages,
   getResourcesPath,
   goTo,
@@ -19,9 +18,7 @@
   waitForVisible,
 } from '../../shared/helper.js';
 import {describe, it} from '../../shared/mocha-extensions.js';
-import {
-  navigateToPerformanceTab,
-} from '../helpers/performance-helpers.js';
+import {reloadDevTools} from '../helpers/cross-tool-helper.js';
 
 const READY_LOCAL_METRIC_SELECTOR = '#local-value .metric-value:not(.waiting)';
 const READY_FIELD_METRIC_SELECTOR = '#field-value .metric-value:not(.waiting)';
@@ -61,14 +58,12 @@
 
 describe('The Performance panel landing page', () => {
   beforeEach(async () => {
-    await enableExperiment('timeline-observations');
+    await reloadDevTools({selectedPanel: {name: 'timeline'}, enableExperiments: ['timeline-observations']});
   });
 
   it('displays live metrics', async () => {
     const {target, frontend} = await getBrowserAndPages();
 
-    await navigateToPerformanceTab();
-
     await target.bringToFront();
 
     const targetSession = await target.createCDPSession();
@@ -129,7 +124,6 @@
       await executionContextPromise;
 
       await frontend.bringToFront();
-      await navigateToPerformanceTab();
 
       const [lcpValueElem, clsValueElem, inpValueElem] = await waitForMany(READY_LOCAL_METRIC_SELECTOR, 3);
       const interactions = await $$<HTMLElement>(INTERACTION_SELECTOR);
@@ -156,8 +150,6 @@
   it('treats bfcache restoration like a regular navigation', async () => {
     const {target, frontend} = await getBrowserAndPages();
 
-    await navigateToPerformanceTab();
-
     await target.bringToFront();
 
     const targetSession = await target.createCDPSession();
@@ -215,8 +207,6 @@
   });
 
   it('gets field data automatically', async () => {
-    await navigateToPerformanceTab();
-
     await setCruxRawResponse('performance/crux-none.rawresponse');
     await goToResource('performance/fake-website.html');
 
@@ -271,8 +261,6 @@
   });
 
   it('uses URL override for field data', async () => {
-    await navigateToPerformanceTab();
-
     await setCruxRawResponse('performance/crux-valid.rawresponse');
     await goToResource('performance/fake-website.html');