UNPKG

chrome-devtools-frontend

Version:
174 lines (148 loc) • 8.32 kB
// 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 Timeline from './timeline.js'; describeWithEnvironment('Initiators', () => { describe('initiator-initiated event relationships', () => { let requestIdleCallbackCall: Trace.Types.Events.SyntheticProfileCall; let functionCallByrequestIdleCallback: Trace.Types.Events.Event; let setTimeoutCall: Trace.Types.Events.SyntheticProfileCall; let functionCallBySetTimeout: Trace.Types.Events.Event; let rAFCall: Trace.Types.Events.SyntheticProfileCall; let functionCallByRAF: Trace.Types.Events.Event; let parsedTrace: Trace.Handlers.Types.ParsedTrace; beforeEach(async function() { parsedTrace = (await TraceLoader.traceEngine(this, 'async-js-calls.json.gz')).parsedTrace; setTimeoutCall = parsedTrace.Renderer.allTraceEntries .filter(e => Trace.Types.Events.isProfileCall(e) && e.callFrame.functionName === 'setTimeout') .at(-1) as Trace.Types.Events.SyntheticProfileCall; assert.exists(setTimeoutCall); assert.isTrue(Trace.Types.Events.isProfileCall(setTimeoutCall)); functionCallBySetTimeout = parsedTrace.Renderer.allTraceEntries.find( e => Trace.Types.Events.isFunctionCall(e) && e.ts > setTimeoutCall.ts) as Trace.Types.Events.Event; assert.exists(functionCallBySetTimeout); rAFCall = parsedTrace.Renderer.allTraceEntries .filter(e => Trace.Types.Events.isProfileCall(e) && e.callFrame.functionName === 'requestAnimationFrame') .at(-1) as Trace.Types.Events.SyntheticProfileCall; assert.exists(rAFCall); assert.isTrue(Trace.Types.Events.isProfileCall(rAFCall)); functionCallByRAF = parsedTrace.Renderer.allTraceEntries.find(e => Trace.Types.Events.isFunctionCall(e) && e.ts > rAFCall.ts) as Trace.Types.Events.Event; assert.exists(functionCallByRAF); requestIdleCallbackCall = parsedTrace.Renderer.allTraceEntries .filter(e => Trace.Types.Events.isProfileCall(e) && e.callFrame.functionName === 'requestIdleCallback') .at(-1) as Trace.Types.Events.SyntheticProfileCall; assert.exists(requestIdleCallbackCall); assert.isTrue(Trace.Types.Events.isProfileCall(requestIdleCallbackCall)); functionCallByrequestIdleCallback = parsedTrace.Renderer.allTraceEntries.find( e => Trace.Types.Events.isFunctionCall(e) && e.ts > requestIdleCallbackCall.ts) as Trace.Types.Events.Event; assert.exists(functionCallByrequestIdleCallback); }); it('returns the initiator data', async function() { const initiatorData = Timeline.Initiators.initiatorsDataToDraw(parsedTrace, functionCallBySetTimeout, [], []); assert.deepEqual(initiatorData[0], { event: functionCallBySetTimeout, initiator: setTimeoutCall, }); }); it('can walk up the tree to find the first parent with an initiator', async function() { // Find any of the bar() calls; they have a parent event // (FunctionCall) that has an initiator. const barCall = parsedTrace.Renderer.allTraceEntries.find( e => Trace.Types.Events.isProfileCall(e) && e.callFrame.functionName === 'bar'); assert.exists(barCall); // Find the initator data but starting at the fibonacci() // call. const initiatorsData = Timeline.Initiators.initiatorsDataToDraw(parsedTrace, barCall, [], []); assert.deepEqual(initiatorsData[0], { event: functionCallBySetTimeout, initiator: setTimeoutCall, }); }); it('will walk back through the initiators to find the entire chain', async function() { // Find any of the baz() calls; they have a parent event // (FunctionCall) that has an initiator. const bazCall = parsedTrace.Renderer.allTraceEntries.find( e => Trace.Types.Events.isProfileCall(e) && e.callFrame.functionName === 'baz'); assert.exists(bazCall); // Find the initators data but starting at the baz() // call. We expect to find 3 initiatorData objects here: // 1. baz() ===> FunctionCall caused by requestIdleCallback // 2. The requestIdleCallback from (1), caused by a prior setTimeout. // 3. The setTimeout from (2), caused by a prior requestAnimationFrame. const initiatorsData = Timeline.Initiators.initiatorsDataToDraw(parsedTrace, bazCall, [], []); assert.deepEqual(initiatorsData, [ { event: functionCallByrequestIdleCallback, initiator: requestIdleCallbackCall, }, { event: functionCallBySetTimeout, initiator: setTimeoutCall, }, { event: functionCallByRAF, initiator: rAFCall, }, ]); }); it('will walk forward to find the events initiated by the selected entry', async function() { const initatorsData = Timeline.Initiators.initiatorsDataToDraw(parsedTrace, rAFCall, [], []); assert.lengthOf(initatorsData, 1); assert.strictEqual(initatorsData[0].event, functionCallByRAF); assert.strictEqual(initatorsData[0].initiator, rAFCall); }); it('will return the closest expandable ancestor as an initiator in a pair if the initiator itself is hidden', async function() { // Get the parent of rAF to add to the expandable events array. // When we add rAF to hidden entries list, it will be the // closest expandable parent and the initiator should point to it. const rAFParent = parsedTrace.Renderer.entryToNode.get(rAFCall)?.parent; assert.exists(rAFParent); // Find the initatorData objects starting at the rAF // call. We expect to find one initatorData here: // rAF callback initiated by rAF -> Parent of rAF because rAF is hidden const initiatorsData = Timeline.Initiators.initiatorsDataToDraw(parsedTrace, rAFCall, [rAFCall], [rAFParent?.entry]); assert.lengthOf(initiatorsData, 1); assert.strictEqual(initiatorsData[0].event, functionCallByRAF); assert.strictEqual(initiatorsData[0].initiator, rAFParent.entry); // Ensure the expandable entry is marked as hidden assert.isTrue(initiatorsData[0].isInitiatorHidden); }); it('will return the closest expandable ancestor as an initiated event in a pair if the event itself is hidden', async function() { const functionCallByRAFParent = parsedTrace.Renderer.entryToNode.get(functionCallByRAF)?.parent; assert.exists(functionCallByRAFParent); const initiatorsData = Timeline.Initiators.initiatorsDataToDraw( parsedTrace, rAFCall, [functionCallByRAF], [functionCallByRAFParent?.entry]); assert.lengthOf(initiatorsData, 1); assert.strictEqual(initiatorsData[0].event, functionCallByRAFParent?.entry); assert.strictEqual(initiatorsData[0].initiator, rAFCall); // Ensure the expandable entry is marked as hidden assert.isTrue(initiatorsData[0].isEntryHidden); }); }); describe('Network Requests', function() { it('returns the initiator data for network requests', async function() { const {parsedTrace} = await TraceLoader.traceEngine(this, 'network-requests-initiators.json.gz'); // Find the network request to test, it is initiated by `youtube.com`. const event = parsedTrace.NetworkRequests.byTime.find(event => event.ts === 1491680762420); assert.exists(event); // Find the `youtube.com` network request. const initiator = parsedTrace.NetworkRequests.byTime.find(event => event.ts === 1491680629144); assert.exists(initiator); const initiatorData = Timeline.Initiators.initiatorsDataToDrawForNetwork(parsedTrace, event); assert.deepEqual(initiatorData, [{event, initiator}]); }); }); });