chrome-devtools-frontend
Version:
Chrome DevTools UI
345 lines (303 loc) • 13.7 kB
text/typescript
// Copyright 2024 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 Timeline from './timeline.js';
describeWithEnvironment('ModificationsManager', () => {
it('applies modifications when present in a trace file', async function() {
await TraceLoader.traceEngine(this, 'web-dev-modifications.json.gz');
const modificationsManager = Timeline.ModificationsManager.ModificationsManager.activeManager();
if (!modificationsManager) {
throw new Error('Modifications manager does not exist.');
}
const entriesFilter = modificationsManager.getEntriesFilter();
assert.lengthOf(entriesFilter.expandableEntries(), 1);
assert.lengthOf(entriesFilter.invisibleEntries(), 108);
assert.deepEqual(modificationsManager.getTimelineBreadcrumbs().initialBreadcrumb, {
window: {min: 1020034823047, max: 1020036087961, range: 1264914},
child: {window: {min: 1020034823047, max: 1020035228006.5569, range: 404959.5568847656}, child: null},
} as Trace.Types.File.Breadcrumb);
// Make sure the saved Label Annotation is applied
const labelAnnotation = modificationsManager.getAnnotations()[0];
const label = (labelAnnotation.type === 'ENTRY_LABEL') ? labelAnnotation.label : '';
assert.deepEqual(labelAnnotation.type, 'ENTRY_LABEL');
assert.deepEqual(label, 'Initialize App');
// Make sure the saved Range Annotation is applied
const timeRangeAnnotation = modificationsManager.getAnnotations()[1];
const rangeLabel = (timeRangeAnnotation.type === 'TIME_RANGE') ? timeRangeAnnotation.label : '';
assert.deepEqual(modificationsManager.getAnnotations()[1].type, 'TIME_RANGE');
assert.deepEqual(rangeLabel, 'Visibility change 1');
});
it('generates a serializable modifications json ', async function() {
await TraceLoader.traceEngine(this, 'web-dev-modifications.json.gz');
const modificationsManager = Timeline.ModificationsManager.ModificationsManager.activeManager();
if (!modificationsManager) {
throw new Error('Modifications manager does not exist.');
}
const entriesFilter = modificationsManager.getEntriesFilter();
const modifications = modificationsManager.toJSON();
assert.lengthOf(entriesFilter.expandableEntries(), 1);
assert.lengthOf(modifications.entriesModifications.expandableEntries, 1);
assert.lengthOf(modifications.entriesModifications.hiddenEntries, 108);
assert.deepEqual(modifications.initialBreadcrumb, {
window: {min: 1020034823047, max: 1020036087961, range: 1264914},
child: {window: {min: 1020034823047, max: 1020035228006.5569, range: 404959.5568847656}, child: null},
} as Trace.Types.File.Breadcrumb);
assert.deepEqual(modifications.annotations.entryLabels, [
{entry: 'p-73704-775-2151-457', label: 'Initialize App'},
]);
assert.deepEqual(modifications.annotations.labelledTimeRanges, [
{
bounds: {min: 1020034870460.4769, max: 1020034880507.9258, range: 10047.448852539062},
label: 'Visibility change 1',
},
]);
});
it('creates annotations and generates correct json for annotations', async function() {
const parsedTrace = (await TraceLoader.traceEngine(this, 'web-dev-with-commit.json.gz')).parsedTrace;
// Get any entres to create a label and a link with.
const entry = parsedTrace.Renderer.allTraceEntries[0];
const entry2 = parsedTrace.Renderer.allTraceEntries[1];
const modificationsManager = Timeline.ModificationsManager.ModificationsManager.activeManager();
assert.isOk(modificationsManager);
modificationsManager.createAnnotation({
type: 'ENTRY_LABEL',
entry,
label: 'entry label',
});
modificationsManager.createAnnotation({
type: 'ENTRIES_LINK',
state: Trace.Types.File.EntriesLinkState.CONNECTED,
entryFrom: entry,
entryTo: entry2,
});
modificationsManager.createAnnotation({
type: 'TIME_RANGE',
bounds: {
min: Trace.Types.Timing.Micro(0),
max: Trace.Types.Timing.Micro(10),
range: Trace.Types.Timing.Micro(10),
},
label: 'range label',
});
const modifications = modificationsManager.toJSON().annotations;
assert.deepEqual(modifications, {
entryLabels: [{
entry: 'r-38',
label: 'entry label',
}],
labelledTimeRanges: [{
bounds: {
min: Trace.Types.Timing.Micro(0),
max: Trace.Types.Timing.Micro(10),
range: Trace.Types.Timing.Micro(10),
},
label: 'range label',
}],
linksBetweenEntries: [{
entryFrom: 'r-38',
entryTo: 'r-39',
}],
});
});
it('does not add the annotation link between entries into the json saved into metadata if `entryTo` does not exist',
async function() {
const parsedTrace = (await TraceLoader.traceEngine(this, 'web-dev-with-commit.json.gz')).parsedTrace;
// Get any entry to create links with.
const entry = parsedTrace.Renderer.allTraceEntries[0];
const entry2 = parsedTrace.Renderer.allTraceEntries[1];
const modificationsManager = Timeline.ModificationsManager.ModificationsManager.activeManager();
assert.isOk(modificationsManager);
modificationsManager.createAnnotation({
type: 'ENTRIES_LINK',
state: Trace.Types.File.EntriesLinkState.CONNECTED,
entryFrom: entry,
entryTo: entry2,
});
modificationsManager.createAnnotation({
type: 'ENTRIES_LINK',
state: Trace.Types.File.EntriesLinkState.PENDING_TO_EVENT,
entryFrom: entry2,
});
// Make sure only the link with both 'to' and 'from' entries in in the generated JSON
const modifications = modificationsManager.toJSON().annotations;
assert.deepEqual(modifications, {
entryLabels: [],
labelledTimeRanges: [],
linksBetweenEntries: [{
entryFrom: 'r-38',
entryTo: 'r-39',
}],
});
});
it('correctly identifies if a connection between entries already exists', async function() {
const parsedTrace = (await TraceLoader.traceEngine(this, 'web-dev-with-commit.json.gz')).parsedTrace;
// Get any entry to create links with.
const entry1 = parsedTrace.Renderer.allTraceEntries[0];
const entry2 = parsedTrace.Renderer.allTraceEntries[1];
const entry3 = parsedTrace.Renderer.allTraceEntries[2];
const modificationsManager = Timeline.ModificationsManager.ModificationsManager.activeManager();
assert.isOk(modificationsManager);
// Create a connection between entry 1 and entry 2
modificationsManager.createAnnotation({
type: 'ENTRIES_LINK',
state: Trace.Types.File.EntriesLinkState.CONNECTED,
entryFrom: entry1,
entryTo: entry2,
});
// Chech if a connection between entries 1 and 3 exists
const existsBetween1And3 = modificationsManager.linkAnnotationBetweenEntriesExists(entry1, entry3);
// Make sure the link does not exists
assert.isFalse(existsBetween1And3);
// Chech if a connection between entries 1 and 2 exists
const existsBetween1And2 = modificationsManager.linkAnnotationBetweenEntriesExists(entry1, entry2);
// Make sure the link exists
assert.isTrue(existsBetween1And2);
// Chech if a connection between entries 2 and 1 exists. It should since the order of entries does not matter.
const existsBetween2And1 = modificationsManager.linkAnnotationBetweenEntriesExists(entry1, entry2);
// Make sure the link exists
assert.isTrue(existsBetween2And1);
});
it('deletes time ranges with an empty label from the annotations list', async function() {
await TraceLoader.traceEngine(this, 'web-dev-with-commit.json.gz');
const modificationsManager = Timeline.ModificationsManager.ModificationsManager.activeManager();
assert.isOk(modificationsManager);
modificationsManager.createAnnotation({
type: 'TIME_RANGE',
bounds: {
min: Trace.Types.Timing.Micro(0),
max: Trace.Types.Timing.Micro(10),
range: Trace.Types.Timing.Micro(10),
},
label: 'label',
});
// Create time range with empty label that shoud be removed
modificationsManager.createAnnotation({
type: 'TIME_RANGE',
bounds: {
min: Trace.Types.Timing.Micro(3),
max: Trace.Types.Timing.Micro(10),
range: Trace.Types.Timing.Micro(7),
},
label: '',
});
// Create time range with empty label that shoud be removed
modificationsManager.createAnnotation({
type: 'TIME_RANGE',
bounds: {
min: Trace.Types.Timing.Micro(5),
max: Trace.Types.Timing.Micro(10),
range: Trace.Types.Timing.Micro(5),
},
label: '',
});
modificationsManager.deleteEmptyRangeAnnotations();
const modifications = modificationsManager.toJSON().annotations;
// Make sure that the annotations with an empty label were deleted
assert.deepEqual(modifications.labelledTimeRanges, [{
bounds: {
min: Trace.Types.Timing.Micro(0),
max: Trace.Types.Timing.Micro(10),
range: Trace.Types.Timing.Micro(10),
},
label: 'label',
}]);
});
it('correctly gets all annotations associated with an entry', async function() {
const parsedTrace = (await TraceLoader.traceEngine(this, 'web-dev-with-commit.json.gz')).parsedTrace;
const modificationsManager = Timeline.ModificationsManager.ModificationsManager.activeManager();
assert.isOk(modificationsManager);
// Get any entry to create annotations with.
const entryToFindAnnotationsFor = parsedTrace.Renderer.allTraceEntries[0];
const entry2 = parsedTrace.Renderer.allTraceEntries[1];
const entry3 = parsedTrace.Renderer.allTraceEntries[2];
// Create a connection between entry we are looking for annotations for and another entry.
// This link should be a part of associated with the entry annotations.
modificationsManager.createAnnotation({
type: 'ENTRIES_LINK',
state: Trace.Types.File.EntriesLinkState.CONNECTED,
entryFrom: entry2,
entryTo: entryToFindAnnotationsFor,
});
// Create a link between random entries
modificationsManager.createAnnotation({
type: 'ENTRIES_LINK',
state: Trace.Types.File.EntriesLinkState.CONNECTED,
entryFrom: entry3,
entryTo: entry2,
});
// Label for the entry we are looking for annotations for.
// This label should be a part of associated with the entry annotations.
modificationsManager.createAnnotation({
type: 'ENTRY_LABEL',
entry: entryToFindAnnotationsFor,
label: 'entry label',
});
const annotationsForEntry = modificationsManager.annotationsForEntry(entryToFindAnnotationsFor);
// Make sure the method returns annotations that `entryToFindAnnotationsFor` is a part of
assert.deepEqual(
annotationsForEntry,
[
{
type: 'ENTRIES_LINK',
state: Trace.Types.File.EntriesLinkState.CONNECTED,
entryFrom: entry2,
entryTo: entryToFindAnnotationsFor,
},
{
type: 'ENTRY_LABEL',
entry: entryToFindAnnotationsFor,
label: 'entry label',
},
],
);
});
it('deletes all annotations associated with an entry', async function() {
const parsedTrace = (await TraceLoader.traceEngine(this, 'web-dev-with-commit.json.gz')).parsedTrace;
const modificationsManager = Timeline.ModificationsManager.ModificationsManager.activeManager();
assert.isOk(modificationsManager);
// Get any entry to create annotations with.
const entryToFindAnnotationsFor = parsedTrace.Renderer.allTraceEntries[0];
const entry2 = parsedTrace.Renderer.allTraceEntries[1];
const entry3 = parsedTrace.Renderer.allTraceEntries[2];
// Create a connection between entry we are looking for annotations for and another entry.
// This link should be deleted.
modificationsManager.createAnnotation({
type: 'ENTRIES_LINK',
state: Trace.Types.File.EntriesLinkState.CONNECTED,
entryFrom: entry2,
entryTo: entryToFindAnnotationsFor,
});
// Create a link between random entries.
// This annotation should not be deleted/
modificationsManager.createAnnotation({
type: 'ENTRIES_LINK',
state: Trace.Types.File.EntriesLinkState.CONNECTED,
entryFrom: entry3,
entryTo: entry2,
});
// Label for the entry we are looking for annotations for.
// This link should be deleted.
modificationsManager.createAnnotation({
type: 'ENTRY_LABEL',
entry: entryToFindAnnotationsFor,
label: 'entry label',
});
modificationsManager.deleteEntryAnnotations(entryToFindAnnotationsFor);
const annotationsForEntry = modificationsManager.getAnnotations();
// Make sure the method deleted all annotations that `entryToFindAnnotationsFor` is a part of
assert.deepEqual(
annotationsForEntry,
[
{
type: 'ENTRIES_LINK',
state: Trace.Types.File.EntriesLinkState.CONNECTED,
entryFrom: entry3,
entryTo: entry2,
},
],
);
});
});