22// Use of this source code is governed by a BSD-style license that can be
33// found in the LICENSE file.
44
5- import type * as Trace from '../../models/trace/trace.js' ;
5+ import * as Root from '../../core/root/root.js' ;
6+ import * as Trace from '../../models/trace/trace.js' ;
67import { doubleRaf } from '../../testing/DOMHelpers.js' ;
78import { describeWithEnvironment } from '../../testing/EnvironmentHelpers.js' ;
89import { TraceLoader } from '../../testing/TraceLoader.js' ;
@@ -21,6 +22,7 @@ class MockViewDelegate implements Timeline.TimelinePanel.TimelineModeViewDelegat
2122
2223describeWithEnvironment ( 'TimelineDetailsView' , function ( ) {
2324 const mockViewDelegate = new MockViewDelegate ( ) ;
25+
2426 it ( 'displays the details of a network request event correctly' , async function ( ) {
2527 const { parsedTrace, insights} = await TraceLoader . traceEngine ( this , 'lcp-web-font.json.gz' ) ;
2628 const detailsView = new Timeline . TimelineDetailsView . TimelineDetailsView ( mockViewDelegate ) ;
@@ -63,4 +65,64 @@ describeWithEnvironment('TimelineDetailsView', function() {
6365 assert . isOk ( duration ) ;
6466 assert . strictEqual ( duration . innerText , 'Duration37.85 ms (at 109.82 ms)' ) ;
6567 } ) ;
68+
69+ it ( 'renders the layout shift component for a single layout shift' , async function ( ) {
70+ Root . Runtime . experiments . enableForTest ( Root . Runtime . ExperimentName . TIMELINE_INSIGHTS ) ;
71+
72+ const { parsedTrace} = await TraceLoader . traceEngine ( this , 'shift-attribution.json.gz' ) ;
73+ const detailsView = new Timeline . TimelineDetailsView . TimelineDetailsView ( mockViewDelegate ) ;
74+ await detailsView . setModel (
75+ { parsedTrace, selectedEvents : null , traceInsightsSets : null , eventToRelatedInsightsMap : null } ) ;
76+
77+ const layoutShift = parsedTrace . LayoutShifts . clusters . at ( 0 ) ?. events . at ( 0 ) ;
78+ assert . isOk ( layoutShift ) ;
79+ const selection = Timeline . TimelineSelection . TimelineSelection . fromTraceEvent ( layoutShift ) ;
80+ await detailsView . setSelection ( selection ) ;
81+ const detailsContentElement = detailsView . getDetailsContentElementForTest ( ) ;
82+ // Assert that the right component is rendered. This component has its own
83+ // tests for its contents so no need to duplicate those here.
84+ const layoutShiftDetails = detailsContentElement . querySelector ( 'devtools-performance-layout-shift-details' ) ;
85+ assert . isNotNull ( layoutShiftDetails ) ;
86+ } ) ;
87+
88+ it ( 'renders the layout shift component for a selected cluster' , async function ( ) {
89+ Root . Runtime . experiments . enableForTest ( Root . Runtime . ExperimentName . TIMELINE_INSIGHTS ) ;
90+
91+ const { parsedTrace} = await TraceLoader . traceEngine ( this , 'shift-attribution.json.gz' ) ;
92+ const detailsView = new Timeline . TimelineDetailsView . TimelineDetailsView ( mockViewDelegate ) ;
93+ await detailsView . setModel (
94+ { parsedTrace, selectedEvents : null , traceInsightsSets : null , eventToRelatedInsightsMap : null } ) ;
95+
96+ const layoutShiftCluster = parsedTrace . LayoutShifts . clusters . at ( 0 ) ;
97+ assert . isOk ( layoutShiftCluster ) ;
98+ const selection = Timeline . TimelineSelection . TimelineSelection . fromTraceEvent ( layoutShiftCluster ) ;
99+ await detailsView . setSelection ( selection ) ;
100+ const detailsContentElement = detailsView . getDetailsContentElementForTest ( ) ;
101+ // Assert that the right component is rendered. This component has its own
102+ // tests for its contents so no need to duplicate those here.
103+ const layoutShiftDetails = detailsContentElement . querySelector ( 'devtools-performance-layout-shift-details' ) ;
104+ assert . isNotNull ( layoutShiftDetails ) ;
105+ } ) ;
106+
107+ it ( 'updates the range details when the user has a range selected' , async function ( ) {
108+ const { parsedTrace} = await TraceLoader . traceEngine ( this , 'web-dev-with-commit.json.gz' ) ;
109+ const detailsView = new Timeline . TimelineDetailsView . TimelineDetailsView ( mockViewDelegate ) ;
110+ await detailsView . setModel ( {
111+ parsedTrace,
112+ // We have to set selected events for the range selection UI to be drawn
113+ // (without the set of events we can't generate the range stats)
114+ selectedEvents : parsedTrace . Renderer . allTraceEntries ,
115+ traceInsightsSets : null ,
116+ eventToRelatedInsightsMap : null ,
117+ } ) ;
118+ const bounds = Trace . Helpers . Timing . traceWindowMilliSeconds ( parsedTrace . Meta . traceBounds ) ;
119+ const selection = Timeline . TimelineSelection . TimelineSelection . fromRange (
120+ bounds . min ,
121+ bounds . max ,
122+ ) ;
123+ await detailsView . setSelection ( selection ) ;
124+ const detailsContentElement = detailsView . getDetailsContentElementForTest ( ) ;
125+ const title = detailsContentElement . querySelector < HTMLElement > ( '.timeline-details-chip-title' ) ;
126+ assert . strictEqual ( title ?. innerText , 'Range: 0 ms – 5.39 s' ) ;
127+ } ) ;
66128} ) ;
0 commit comments