chrome-devtools-frontend
Version:
Chrome DevTools UI
126 lines (113 loc) • 6.06 kB
text/typescript
// Copyright 2023 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import * as Trace from '../../../models/trace/trace.js';
import {describeWithEnvironment} from '../../../testing/EnvironmentHelpers.js';
import {TraceLoader} from '../../../testing/TraceLoader.js';
import * as PerfUI from '../../../ui/legacy/components/perf_ui/perf_ui.js';
import * as Timeline from '../timeline.js';
function initTrackAppender(
flameChartData: PerfUI.FlameChart.FlameChartTimelineData,
parsedTrace: Trace.Handlers.Types.ParsedTrace,
entryData: Trace.Types.Events.Event[],
entryTypeByLevel: Timeline.TimelineFlameChartDataProvider.EntryType[],
): Timeline.InteractionsTrackAppender.InteractionsTrackAppender {
const entityMapper = new Timeline.Utils.EntityMapper.EntityMapper(parsedTrace);
const compatibilityTracksAppender = new Timeline.CompatibilityTracksAppender.CompatibilityTracksAppender(
flameChartData, parsedTrace, entryData, entryTypeByLevel, entityMapper);
return compatibilityTracksAppender.interactionsTrackAppender();
}
describeWithEnvironment('InteractionsTrackAppender', function() {
async function renderTrackAppender(context: Mocha.Suite|Mocha.Context, trace: string): Promise<{
entryTypeByLevel: Timeline.TimelineFlameChartDataProvider.EntryType[],
flameChartData: PerfUI.FlameChart.FlameChartTimelineData,
interactionsTrackAppender: Timeline.InteractionsTrackAppender.InteractionsTrackAppender,
entryData: Trace.Types.Events.Event[],
parsedTrace: Readonly<Trace.Handlers.Types.ParsedTrace>,
}> {
const entryTypeByLevel: Timeline.TimelineFlameChartDataProvider.EntryType[] = [];
const entryData: Trace.Types.Events.Event[] = [];
const flameChartData = PerfUI.FlameChart.FlameChartTimelineData.createEmpty();
const {parsedTrace} = await TraceLoader.traceEngine(context, trace);
const interactionsTrackAppender = initTrackAppender(flameChartData, parsedTrace, entryData, entryTypeByLevel);
interactionsTrackAppender.appendTrackAtLevel(0);
return {
entryTypeByLevel,
parsedTrace,
flameChartData,
interactionsTrackAppender,
entryData,
};
}
describe('appendTrackAtLevel', function() {
it('marks all levels used by the track with the `TrackAppender` type', async function() {
const {entryTypeByLevel} = await renderTrackAppender(this, 'slow-interaction-button-click.json.gz');
// All events fit on the top level
assert.lengthOf(entryTypeByLevel, 1);
assert.deepEqual(entryTypeByLevel, [
Timeline.TimelineFlameChartDataProvider.EntryType.TRACK_APPENDER,
]);
});
it('takes over no levels if there are no interactions', async function() {
// animation trace has no interactions in it.
const {entryTypeByLevel} = await renderTrackAppender(this, 'animation.json.gz');
assert.lengthOf(entryTypeByLevel, 0);
});
it('only shows the top level interactions', async function() {
const {entryData, parsedTrace} = await renderTrackAppender(this, 'nested-interactions.json.gz');
assert.strictEqual(entryData.length, parsedTrace.UserInteractions.interactionEventsWithNoNesting.length);
});
it('creates a flamechart group', async function() {
const {flameChartData} = await renderTrackAppender(this, 'slow-interaction-button-click.json.gz');
assert.lengthOf(flameChartData.groups, 1);
assert.strictEqual(flameChartData.groups[0].name, 'Interactions');
});
it('adds all interactions with the correct start times', async function() {
const {flameChartData, parsedTrace, entryData} =
await renderTrackAppender(this, 'slow-interaction-button-click.json.gz');
const events = parsedTrace.UserInteractions.interactionEventsWithNoNesting;
for (const event of events) {
const markerIndex = entryData.indexOf(event);
assert.exists(markerIndex);
assert.strictEqual(flameChartData.entryStartTimes[markerIndex], Trace.Helpers.Timing.microToMilli(event.ts));
}
});
it('adds total times correctly', async function() {
const {flameChartData, parsedTrace, entryData} =
await renderTrackAppender(this, 'slow-interaction-button-click.json.gz');
const events = parsedTrace.UserInteractions.interactionEventsWithNoNesting;
for (const event of events) {
const markerIndex = entryData.indexOf(event);
assert.exists(markerIndex);
const expectedTotalTimeForEvent =
Trace.Helpers.Timing.microToMilli((event.dur || 0) as Trace.Types.Timing.Micro);
assert.strictEqual(flameChartData.entryTotalTimes[markerIndex], expectedTotalTimeForEvent);
}
});
});
it('candy-stripes and adds warning triangles to long interactions', async function() {
const {parsedTrace, flameChartData, entryData} = await renderTrackAppender(this, 'one-second-interaction.json.gz');
const longInteraction = parsedTrace.UserInteractions.longestInteractionEvent;
if (!longInteraction) {
throw new Error('Could not find longest interaction');
}
const entryIndex = entryData.indexOf(longInteraction);
const decorationsForEntry = flameChartData.entryDecorations[entryIndex];
assert.deepEqual(decorationsForEntry, [
{
type: PerfUI.FlameChart.FlameChartDecorationType.CANDY,
startAtTime: Trace.Types.Timing.Micro(200_000),
endAtTime: longInteraction.processingEnd,
},
{
type: PerfUI.FlameChart.FlameChartDecorationType.WARNING_TRIANGLE,
customEndTime: longInteraction.processingEnd,
},
]);
});
it('does not candy-stripe interactions less than 200ms', async function() {
const {flameChartData} = await renderTrackAppender(this, 'slow-interaction-button-click.json.gz');
// None of the interactions are over 200ms, so we do not expect to see any decorations
assert.lengthOf(flameChartData.entryDecorations, 0);
});
});