UNPKG

chrome-devtools-frontend

Version:
348 lines (330 loc) • 12.8 kB
// Copyright 2025 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 {describeWithEnvironment} from '../../../testing/EnvironmentHelpers.js'; import {getFirstOrError, getInsightOrError, processTrace} from '../../../testing/InsightHelpers.js'; import * as Trace from '../trace.js'; import * as ModernHTTP from './ModernHTTP.js'; const {determineNonHttp2Resources} = ModernHTTP; describeWithEnvironment('Cache', function() { describe('determineNonHttp2Resources', () => { function createNRequests(cb: (i: number) => Trace.Types.Events.SyntheticNetworkRequest, num: number) { const reqs: Trace.Types.Events.SyntheticNetworkRequest[] = []; for (let i = 0; i < num; ++i) { reqs.push(cb(i)); } return reqs; } const generic1PEntity: Trace.Handlers.Helpers.Entity = { name: 'first-party', company: 'First Party', category: 'cat', categories: [], domains: ['first.party'], averageExecutionTime: 100, totalExecutionTime: 100, totalOccurrences: 1, }; function createEntityMappings(events: Trace.Types.Events.Event[]): Trace.Handlers.Helpers.EntityMappings { const entityMappings: Trace.Handlers.Helpers.EntityMappings = { createdEntityCache: new Map(), entityByEvent: new Map(), eventsByEntity: new Map(), }; for (const event of events) { Trace.Handlers.Helpers.addEventToEntityMapping(event, entityMappings); } return entityMappings; } it('returns requests affected by an old HTTP version', () => { const requests: Trace.Types.Events.SyntheticNetworkRequest[] = [ ...createNRequests( i => ({ args: { data: { url: `https://example.com/req${i}`, protocol: 'http/2', resourceType: 'Image', decodedBodyLength: 1000, }, }, } as Trace.Types.Events.SyntheticNetworkRequest), 6), { args: { data: { url: 'https://example.com/bad', protocol: 'http/1.1', resourceType: 'Image', decodedBodyLength: 1000, }, }, } as Trace.Types.Events.SyntheticNetworkRequest, ]; const result = determineNonHttp2Resources(requests, createEntityMappings(requests), generic1PEntity); assert.deepEqual(result.map(r => r.args.data.url), ['https://example.com/bad']); }); it('ignores requests fetched from service workers', () => { const requests: Trace.Types.Events.SyntheticNetworkRequest[] = [ ...createNRequests( i => ({ args: { data: { url: `https://example.com/req${i}`, protocol: 'http/2', resourceType: 'Image', decodedBodyLength: 1000, }, }, } as Trace.Types.Events.SyntheticNetworkRequest), 6), { args: { data: { url: 'https://example.com/bad', protocol: 'http/1.1', resourceType: 'Image', decodedBodyLength: 1000, fromServiceWorker: true, }, }, } as Trace.Types.Events.SyntheticNetworkRequest, ]; const result = determineNonHttp2Resources(requests, createEntityMappings(requests), generic1PEntity); assert.deepEqual(result.map(r => r.args.data.url), []); }); it('ignores requests fetched using http2 or higher', () => { const requests: Trace.Types.Events.SyntheticNetworkRequest[] = [ ...createNRequests( i => ({ args: { data: { url: `https://example.com/req${i}`, protocol: 'http/2', resourceType: 'Image', decodedBodyLength: 1000, }, }, } as Trace.Types.Events.SyntheticNetworkRequest), 6), { args: { data: { url: 'https://example.com/bad', protocol: 'http/3', resourceType: 'Image', decodedBodyLength: 1000, }, }, } as Trace.Types.Events.SyntheticNetworkRequest, ]; const result = determineNonHttp2Resources(requests, createEntityMappings(requests), generic1PEntity); assert.deepEqual(result.map(r => r.args.data.url), []); }); it('shows requests if the origin had 6 static resource requests', () => { const requests: Trace.Types.Events.SyntheticNetworkRequest[] = [ ...createNRequests( i => ({ args: { data: { url: `https://example.com/req${i}`, protocol: 'http/2', resourceType: 'Image', decodedBodyLength: 1000, }, }, } as Trace.Types.Events.SyntheticNetworkRequest), 6), { args: { data: { url: 'https://example.com/fetch', protocol: 'http/1.1', resourceType: 'Fetch', decodedBodyLength: 1000, }, }, } as Trace.Types.Events.SyntheticNetworkRequest, ]; const result = determineNonHttp2Resources(requests, createEntityMappings(requests), generic1PEntity); assert.deepEqual(result.map(r => r.args.data.url), ['https://example.com/fetch']); }); it('ignores requests if the origin did not have 6 requests', () => { const requests: Trace.Types.Events.SyntheticNetworkRequest[] = [ { args: { data: { url: 'https://example.com/fetch', protocol: 'http/1.1', resourceType: 'Fetch', decodedBodyLength: 1000, }, }, } as Trace.Types.Events.SyntheticNetworkRequest, ]; const result = determineNonHttp2Resources(requests, createEntityMappings(requests), generic1PEntity); assert.deepEqual(result.map(r => r.args.data.url), []); }); it('ignores requests if the 6+ origin requests were not static resources', () => { const requests: Trace.Types.Events.SyntheticNetworkRequest[] = [ ...createNRequests( i => ({ args: { data: { url: `https://example.com/req${i}`, protocol: 'http/2', resourceType: 'Fetch', decodedBodyLength: 1000, }, }, } as Trace.Types.Events.SyntheticNetworkRequest), 6), { args: { data: { url: 'https://example.com/fetch', protocol: 'http/1.1', resourceType: 'Fetch', decodedBodyLength: 1000, }, }, } as Trace.Types.Events.SyntheticNetworkRequest, ]; const result = determineNonHttp2Resources(requests, createEntityMappings(requests), generic1PEntity); assert.deepEqual(result.map(r => r.args.data.url), []); }); it('ignores requests if the 6+ origin requests were served on localhost', () => { const requests: Trace.Types.Events.SyntheticNetworkRequest[] = [ ...createNRequests( i => ({ args: { data: { url: `https://localhost:8080/req${i}`, protocol: 'http/2', resourceType: 'Image', decodedBodyLength: 1000, }, }, } as Trace.Types.Events.SyntheticNetworkRequest), 6), { args: { data: { url: 'https://localhost:8080/fetch', protocol: 'http/1.1', resourceType: 'Fetch', decodedBodyLength: 1000, }, }, } as Trace.Types.Events.SyntheticNetworkRequest, ]; const result = determineNonHttp2Resources(requests, createEntityMappings(requests), generic1PEntity); assert.deepEqual(result.map(r => r.args.data.url), []); }); it('ignores requests if the 6+ origin requests were too small on a known entity', () => { const requests: Trace.Types.Events.SyntheticNetworkRequest[] = [ ...createNRequests( i => ({ args: { data: { url: `https://www.google.com/req${i}`, protocol: 'http/2', resourceType: 'Image', decodedBodyLength: 10, }, }, } as Trace.Types.Events.SyntheticNetworkRequest), 6), { args: { data: { url: 'https://www.google.com/fetch', protocol: 'http/1.1', resourceType: 'Fetch', decodedBodyLength: 1000, }, }, } as Trace.Types.Events.SyntheticNetworkRequest, ]; const result = determineNonHttp2Resources(requests, createEntityMappings(requests), generic1PEntity); assert.deepEqual(result.map(r => r.args.data.url), []); }); it('shows requests if the 6+ origin requests were too small on an unknown entity', () => { const requests: Trace.Types.Events.SyntheticNetworkRequest[] = [ ...createNRequests( i => ({ args: { data: { url: `https://example.com/req${i}`, protocol: 'http/2', resourceType: 'Image', decodedBodyLength: 10, }, }, } as Trace.Types.Events.SyntheticNetworkRequest), 6), { args: { data: { url: 'https://example.com/fetch', protocol: 'http/1.1', resourceType: 'Fetch', decodedBodyLength: 1000, }, }, } as Trace.Types.Events.SyntheticNetworkRequest, ]; const result = determineNonHttp2Resources(requests, createEntityMappings(requests), generic1PEntity); assert.deepEqual(result.map(r => r.args.data.url), ['https://example.com/fetch']); }); it('shows requests if the 6+ origin requests were too small on the first party entity', () => { const requests: Trace.Types.Events.SyntheticNetworkRequest[] = [ ...createNRequests( i => ({ args: { data: { url: `https://example.com/req${i}`, protocol: 'http/2', resourceType: 'Image', decodedBodyLength: 10, }, }, } as Trace.Types.Events.SyntheticNetworkRequest), 6), { args: { data: { url: 'https://example.com/fetch', protocol: 'http/1.1', resourceType: 'Fetch', decodedBodyLength: 1000, }, }, } as Trace.Types.Events.SyntheticNetworkRequest, ]; const entityMappings = createEntityMappings(requests); const firstPartyEntity = entityMappings.entityByEvent.get(requests[0]); const result = determineNonHttp2Resources(requests, createEntityMappings(requests), firstPartyEntity!); assert.deepEqual(result.map(r => r.args.data.url), ['https://example.com/fetch']); }); }); it('identifies HTTP/1.1 requests in a real trace', async () => { const {data, insights} = await processTrace(this, 'http1.1.json.gz'); const insight = getInsightOrError('ModernHTTP', insights, getFirstOrError(data.Meta.navigationsByNavigationId.values())); assert.deepEqual(insight.requests.map(r => r.args.data.url), [ 'https://ads.jetpackdigital.com/sites/_uploads/1742278386bg_opt_640x350-avif.avif', 'https://ads.jetpackdigital.com/sites/_uploads/1583540859Play.png', 'https://ads.jetpackdigital.com/sites/_uploads/1583540859Muted.png', 'https://ads.jetpackdigital.com/h5media/sites/_uploads/1742363510mm_allthefeels_20_mob.mp4', 'https://ads.jetpackdigital.com/sites/_uploads/1583540860Pause.png', 'https://ads.jetpackdigital.com/tracking_pixel.gif?8852762616', 'https://ads.jetpackdigital.com/tracking_pixel.gif?7753243273', ]); // All the above were preload requests. None should affect FCP or LCP. assert.deepEqual(insight.metricSavings, { FCP: 0 as Trace.Types.Timing.Milli, LCP: 0 as Trace.Types.Timing.Milli, }); }); });