chrome-devtools-frontend
Version:
Chrome DevTools UI
186 lines (175 loc) • 6.67 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 type * as Protocol from '../../../generated/protocol.js';
import {TraceLoader} from '../../../testing/TraceLoader.js';
import * as Trace from '../trace.js';
function invalidationDataForTestAssertion(invalidation: Trace.Types.Events.InvalidationTrackingEvent): {
nodeId: Protocol.DOM.BackendNodeId,
nodeName?: string,
reason?: string,
stackTrace?: Trace.Types.Events.CallFrame[],
} {
return {
nodeId: invalidation.args.data.nodeId,
nodeName: invalidation.args.data.nodeName,
reason: invalidation.args.data.reason,
stackTrace: invalidation.args.data.stackTrace,
};
}
describe('InvalidationsHandler', () => {
beforeEach(() => {
Trace.Handlers.ModelHandlers.Invalidations.reset();
});
it('finds the right invalidators for a layout where attributes have been changed', async function() {
const events = await TraceLoader.rawEvents(this, 'style-invalidation-change-attribute.json.gz');
for (const event of events) {
Trace.Handlers.ModelHandlers.Invalidations.handleEvent(event);
}
await Trace.Handlers.ModelHandlers.Invalidations.finalize();
const data = Trace.Handlers.ModelHandlers.Invalidations.data();
// Find the Layout event that we want to test - we are testing
// the layout that happens after button click that happened in
// the trace.
const updateLayoutTreeEvent = events.find(event => {
return Trace.Types.Events.isUpdateLayoutTree(event) &&
event.args.beginData?.stackTrace?.[0].functionName === 'testFuncs.changeAttributeAndDisplay';
});
if (!updateLayoutTreeEvent) {
throw new Error('Could not find UpdateLayoutTree event.');
}
const invalidations =
data.invalidationsForEvent.get(updateLayoutTreeEvent)?.map(invalidationDataForTestAssertion) ?? [];
assert.deepEqual(invalidations, [
{
nodeId: 107 as Protocol.DOM.BackendNodeId,
nodeName: 'BUTTON id=\'changeAttributeAndDisplay\'',
reason: 'PseudoClass',
stackTrace: undefined,
},
{
nodeId: 110 as Protocol.DOM.BackendNodeId,
nodeName: 'DIV id=\'testElementFour\'',
reason: undefined,
stackTrace: [
{
columnNumber: 46,
functionName: 'testFuncs.changeAttributeAndDisplay',
lineNumber: 45,
scriptId: '86',
url: 'https://chromedevtools.github.io/performance-stories/style-invalidations/app.js',
},
],
},
{
nodeId: 110 as Protocol.DOM.BackendNodeId,
nodeName: 'DIV id=\'testElementFour\'',
reason: 'StyleInvalidator',
stackTrace: [
{
columnNumber: 46,
functionName: 'testFuncs.changeAttributeAndDisplay',
lineNumber: 45,
scriptId: '86',
url: 'https://chromedevtools.github.io/performance-stories/style-invalidations/app.js',
},
],
},
{
nodeId: 110 as Protocol.DOM.BackendNodeId,
nodeName: 'DIV id=\'testElementFour\'',
reason: 'Attribute',
stackTrace: [
{
columnNumber: 46,
functionName: 'testFuncs.changeAttributeAndDisplay',
lineNumber: 45,
scriptId: '86',
url: 'https://chromedevtools.github.io/performance-stories/style-invalidations/app.js',
},
],
},
{
nodeId: 111 as Protocol.DOM.BackendNodeId,
nodeName: 'DIV id=\'testElementFive\'',
reason: undefined,
stackTrace: [
{
columnNumber: 46,
functionName: 'testFuncs.changeAttributeAndDisplay',
lineNumber: 46,
scriptId: '86',
url: 'https://chromedevtools.github.io/performance-stories/style-invalidations/app.js',
},
],
},
{
nodeId: 111 as Protocol.DOM.BackendNodeId,
nodeName: 'DIV id=\'testElementFive\'',
reason: 'StyleInvalidator',
stackTrace: [
{
columnNumber: 46,
functionName: 'testFuncs.changeAttributeAndDisplay',
lineNumber: 46,
scriptId: '86',
url: 'https://chromedevtools.github.io/performance-stories/style-invalidations/app.js',
},
],
},
{
nodeId: 111 as Protocol.DOM.BackendNodeId,
nodeName: 'DIV id=\'testElementFive\'',
reason: 'Attribute',
stackTrace: [
{
columnNumber: 46,
functionName: 'testFuncs.changeAttributeAndDisplay',
lineNumber: 46,
scriptId: '86',
url: 'https://chromedevtools.github.io/performance-stories/style-invalidations/app.js',
},
],
},
{
nodeId: 110 as Protocol.DOM.BackendNodeId,
nodeName: 'DIV id=\'testElementFour\'',
reason: 'Element has pending invalidation list',
stackTrace: undefined,
},
{
nodeId: 111 as Protocol.DOM.BackendNodeId,
nodeName: 'DIV id=\'testElementFive\'',
reason: 'Element has pending invalidation list',
stackTrace: undefined,
},
]);
});
it('limits the number of kept invalidations per event', async function() {
const events = await TraceLoader.rawEvents(this, 'over-20-invalidations-per-event.json.gz');
Trace.Handlers.ModelHandlers.Invalidations.handleUserConfig(
{
...Trace.Types.Configuration.defaults(),
maxInvalidationEventsPerEvent: 5,
},
);
for (const event of events) {
Trace.Handlers.ModelHandlers.Invalidations.handleEvent(event);
}
await Trace.Handlers.ModelHandlers.Invalidations.finalize();
const data = Trace.Handlers.ModelHandlers.Invalidations.data();
// Find the UpdateLayoutEvent that had 26 invalidations
const layoutEvent = Array.from(data.invalidationCountForEvent.entries())
.filter(entry => {
const [, count] = entry;
return count === 26;
})
.map(entry => entry[0])
.at(0);
assert.isOk(layoutEvent);
const invalidations = data.invalidationsForEvent.get(layoutEvent);
assert.isOk(invalidations);
// We know there are 26 invalidation events, but the handler only kept the last 5 as per the config we passed in.
assert.lengthOf(invalidations, 5);
});
});