chrome-devtools-frontend
Version:
Chrome DevTools UI
173 lines (148 loc) • 9.22 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 Platform from '../../core/platform/platform.js';
import * as SDK from '../../core/sdk/sdk.js';
import * as Protocol from '../../generated/protocol.js';
import {getCleanTextContentFromElements, renderElementIntoDOM} from '../../testing/DOMHelpers.js';
import {describeWithLocale} from '../../testing/EnvironmentHelpers.js';
import * as Network from './NetworkTimeCalculator.js';
import * as RequestTimingView from './RequestTimingView.js';
const {urlString} = Platform.DevToolsPath;
function createNetworkRequest(
matchedSource: Protocol.Network.ServiceWorkerRouterSource,
actualSource: Protocol.Network.ServiceWorkerRouterSource): SDK.NetworkRequest.NetworkRequest {
const request = SDK.NetworkRequest.NetworkRequest.create(
'requestId' as Protocol.Network.RequestId, urlString`http://devtools-frontend.test`, urlString``, null, null,
null);
request.mimeType = 'application/wasm';
request.finished = true;
const timingInfo: Protocol.Network.ResourceTiming = {
requestTime: 500,
proxyStart: 0,
proxyEnd: 0,
dnsStart: 0,
dnsEnd: 0,
connectStart: 0,
connectEnd: 0,
sslStart: 0,
sslEnd: 0,
workerReady: 400,
workerStart: 500,
workerRouterEvaluationStart: -200,
workerFetchStart: 600,
workerRespondWithSettled: 700,
sendStart: 800,
sendEnd: 900,
pushStart: 0,
pushEnd: 0,
receiveHeadersStart: 1000,
receiveHeadersEnd: 0,
};
if (matchedSource === Protocol.Network.ServiceWorkerRouterSource.Cache) {
timingInfo.workerCacheLookupStart = -100;
}
request.timing = timingInfo;
request.serviceWorkerRouterInfo = {
ruleIdMatched: 1,
matchedSourceType: matchedSource,
actualSourceType: actualSource,
};
return request;
}
describeWithLocale('ResourceTimingView', () => {
it('RequestTimeRanges has router evaluation field with SW router source as network', async () => {
const request = createNetworkRequest(
Protocol.Network.ServiceWorkerRouterSource.Network, Protocol.Network.ServiceWorkerRouterSource.Network);
const timingInfo = request.timing as Protocol.Network.ResourceTiming;
const timeRanges = RequestTimingView.RequestTimingView.calculateRequestTimeRanges(request, 100);
const routerEvaluationTime = timingInfo.workerRouterEvaluationStart as number;
const sendStart = timingInfo.sendStart;
const routerEvaluation = timeRanges.find(
timeRange => timeRange.name === RequestTimingView.RequestTimeRangeNames.SERVICE_WORKER_ROUTER_EVALUATION);
assert.isTrue(Boolean(routerEvaluation), 'worker router evaluation exists');
assert.strictEqual(routerEvaluation?.start, timingInfo.requestTime + routerEvaluationTime / 1000);
assert.strictEqual(routerEvaluation?.end, timingInfo.requestTime + sendStart / 1000);
const cacheLookup = timeRanges.find(
timeRange => timeRange.name === RequestTimingView.RequestTimeRangeNames.SERVICE_WORKER_CACHE_LOOKUP);
assert.isFalse(Boolean(cacheLookup), 'worker cache lookup does not exist');
});
it('RequestTimeRanges has router evaluation field with SW router source as fetch-event', async () => {
const request = createNetworkRequest(
Protocol.Network.ServiceWorkerRouterSource.FetchEvent, Protocol.Network.ServiceWorkerRouterSource.FetchEvent);
const timingInfo = request.timing as Protocol.Network.ResourceTiming;
const timeRanges = RequestTimingView.RequestTimingView.calculateRequestTimeRanges(request, 100);
const routerEvaluationTime = timingInfo.workerRouterEvaluationStart as number;
const workerStart = timingInfo.workerStart;
const routerEvaluation = timeRanges.find(
timeRange => timeRange.name === RequestTimingView.RequestTimeRangeNames.SERVICE_WORKER_ROUTER_EVALUATION);
assert.isTrue(Boolean(routerEvaluation), 'worker router evaluation exists');
assert.strictEqual(routerEvaluation?.start, timingInfo.requestTime + routerEvaluationTime / 1000);
assert.strictEqual(routerEvaluation?.end, timingInfo.requestTime + workerStart / 1000);
const cacheLookup = timeRanges.find(
timeRange => timeRange.name === RequestTimingView.RequestTimeRangeNames.SERVICE_WORKER_CACHE_LOOKUP);
assert.isFalse(Boolean(cacheLookup), 'worker cache lookup does not exist');
});
it('RequestTimeRanges has router evaluation field with SW router source as cache hit', async () => {
const request = createNetworkRequest(
Protocol.Network.ServiceWorkerRouterSource.Cache, Protocol.Network.ServiceWorkerRouterSource.Cache);
const timingInfo = request.timing as Protocol.Network.ResourceTiming;
const timeRanges = RequestTimingView.RequestTimingView.calculateRequestTimeRanges(request, 100);
const routerEvaluationTime = timingInfo.workerRouterEvaluationStart as number;
const cacheLookupStart = timingInfo.workerCacheLookupStart as number;
const routerEvaluation = timeRanges.find(
timeRange => timeRange.name === RequestTimingView.RequestTimeRangeNames.SERVICE_WORKER_ROUTER_EVALUATION);
assert.isTrue(Boolean(routerEvaluation), 'worker router evaluation exists');
assert.strictEqual(routerEvaluation?.start, timingInfo.requestTime + routerEvaluationTime / 1000);
assert.strictEqual(routerEvaluation?.end, timingInfo.requestTime + cacheLookupStart / 1000);
const cacheLookup = timeRanges.find(
timeRange => timeRange.name === RequestTimingView.RequestTimeRangeNames.SERVICE_WORKER_CACHE_LOOKUP);
assert.isTrue(Boolean(cacheLookup), 'worker cache lookup does not exist');
assert.strictEqual(cacheLookup?.start, timingInfo.requestTime + cacheLookupStart / 1000);
assert.strictEqual(cacheLookup?.end, timingInfo.requestTime + timingInfo.receiveHeadersStart / 1000);
});
it('RequestTimeRanges has router evaluation field with SW router source as cache miss', async () => {
const request = createNetworkRequest(
Protocol.Network.ServiceWorkerRouterSource.Cache, Protocol.Network.ServiceWorkerRouterSource.Network);
const timingInfo = request.timing as Protocol.Network.ResourceTiming;
const timeRanges = RequestTimingView.RequestTimingView.calculateRequestTimeRanges(request, 100);
const routerEvaluationTime = timingInfo.workerRouterEvaluationStart as number;
const cacheLookupStart = timingInfo.workerCacheLookupStart as number;
const routerEvaluation = timeRanges.find(
timeRange => timeRange.name === RequestTimingView.RequestTimeRangeNames.SERVICE_WORKER_ROUTER_EVALUATION);
assert.isTrue(Boolean(routerEvaluation), 'worker router evaluation exists');
assert.strictEqual(routerEvaluation?.start, timingInfo.requestTime + routerEvaluationTime / 1000);
assert.strictEqual(routerEvaluation?.end, timingInfo.requestTime + cacheLookupStart / 1000);
const cacheLookup = timeRanges.find(
timeRange => timeRange.name === RequestTimingView.RequestTimeRangeNames.SERVICE_WORKER_CACHE_LOOKUP);
assert.isTrue(Boolean(cacheLookup), 'worker cache lookup does not exist');
assert.strictEqual(cacheLookup?.start, timingInfo.requestTime + cacheLookupStart / 1000);
assert.strictEqual(cacheLookup?.end, timingInfo.requestTime + timingInfo.sendStart / 1000);
});
it('Timing table has router evaluation field with detail tabs', async () => {
const request = createNetworkRequest(
Protocol.Network.ServiceWorkerRouterSource.Network, Protocol.Network.ServiceWorkerRouterSource.Network);
const component = new RequestTimingView.RequestTimingView(request, new Network.NetworkTimeCalculator(true));
const div = document.createElement('div');
renderElementIntoDOM(div);
component.markAsRoot();
component.show(div);
// Test if we correctly set details element
const routerEvaluationDetailsElement = document.querySelector('.router-evaluation-timing-bar-details');
assert.isNotNull(routerEvaluationDetailsElement, 'router evaluation details does not exist');
assert.strictEqual(
routerEvaluationDetailsElement.childElementCount, 1,
'router evaluation details child element count does not match');
assert.isNotNull(routerEvaluationDetailsElement.firstElementChild, 'router evaluation first element is non null');
// Test if we correctly set the tree item inside shadow root
const shadowElement = routerEvaluationDetailsElement.firstElementChild.shadowRoot;
assert.isNotNull(shadowElement, 'shadow element does not exist');
const content = getCleanTextContentFromElements(shadowElement, '.network-fetch-details-treeitem');
assert.lengthOf(content, 2, 'does not match the tree item');
// Check the content of the view. Since the value is set from matched to actual,
// the order should be the same.
const networkString = String(Protocol.Network.ServiceWorkerRouterSource.Network);
assert.strictEqual(content[0], `Matched source: ${networkString}`, 'matched source does not match');
assert.strictEqual(content[1], `Actual source: ${networkString}`, 'actual source does not match');
});
});