chrome-devtools-frontend
Version:
Chrome DevTools UI
752 lines (678 loc) • 36.2 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 * as Platform from '../../../../core/platform/platform.js';
import * as SDK from '../../../../core/sdk/sdk.js';
import * as Protocol from '../../../../generated/protocol.js';
import {assertGridContents} from '../../../../testing/DataGridHelpers.js';
import {
getElementsWithinComponent,
getElementWithinComponent,
renderElementIntoDOM,
} from '../../../../testing/DOMHelpers.js';
import {describeWithEnvironment} from '../../../../testing/EnvironmentHelpers.js';
import * as RenderCoordinator from '../../../../ui/components/render_coordinator/render_coordinator.js';
import * as ReportView from '../../../../ui/components/report_view/report_view.js';
import * as PreloadingComponents from './components.js';
const {urlString} = Platform.DevToolsPath;
async function renderUsedPreloadingView(data: PreloadingComponents.UsedPreloadingView.UsedPreloadingViewData):
Promise<HTMLElement> {
const component = new PreloadingComponents.UsedPreloadingView.UsedPreloadingView();
component.data = data;
renderElementIntoDOM(component);
assert.isNotNull(component.shadowRoot);
await RenderCoordinator.done();
return component;
}
describeWithEnvironment('UsedPreloadingView', () => {
it('renderes prefetch used', async () => {
const data: PreloadingComponents.UsedPreloadingView.UsedPreloadingViewData = {
pageURL: urlString`https://example.com/prefetched.html`,
previousAttempts: [
{
action: Protocol.Preload.SpeculationAction.Prefetch,
key: {
loaderId: 'loaderId:1' as Protocol.Network.LoaderId,
action: Protocol.Preload.SpeculationAction.Prefetch,
url: urlString`https://example.com/prefetched.html`,
},
pipelineId: 'pipelineId:1' as Protocol.Preload.PreloadPipelineId,
status: SDK.PreloadingModel.PreloadingStatus.SUCCESS,
prefetchStatus: null,
requestId: 'requestId:1' as Protocol.Network.RequestId,
ruleSetIds: ['ruleSetId:1'] as Protocol.Preload.RuleSetId[],
nodeIds: [1] as Protocol.DOM.BackendNodeId[],
},
{
action: Protocol.Preload.SpeculationAction.Prerender,
key: {
loaderId: 'loaderId:1' as Protocol.Network.LoaderId,
action: Protocol.Preload.SpeculationAction.Prerender,
url: urlString`https://example.com/prerendered.html`,
},
pipelineId: 'pipelineId:2' as Protocol.Preload.PreloadPipelineId,
status: SDK.PreloadingModel.PreloadingStatus.FAILURE,
prerenderStatus: Protocol.Preload.PrerenderFinalStatus.TriggerDestroyed,
disallowedMojoInterface: null,
mismatchedHeaders: null,
ruleSetIds: ['ruleSetId:1'] as Protocol.Preload.RuleSetId[],
nodeIds: [1] as Protocol.DOM.BackendNodeId[],
},
],
currentAttempts: [],
};
const component = await renderUsedPreloadingView(data);
assert.isNotNull(component.shadowRoot);
const headers = getElementsWithinComponent(
component, 'devtools-report devtools-report-section-header', ReportView.ReportView.ReportSectionHeader);
const sections = getElementsWithinComponent(
component, 'devtools-report devtools-report-section', ReportView.ReportView.ReportSection);
assert.lengthOf(headers, 2);
assert.lengthOf(sections, 3);
assert.include(headers[0]?.textContent, 'Speculative loading status');
assert.strictEqual(sections[0]?.querySelector('.status-badge span')?.textContent?.trim(), 'Success');
assert.include(sections[0]?.textContent, 'This page was successfully prefetched.');
assert.include(headers[1]?.textContent, 'Speculations initiated by this page');
const badges = sections[1]?.querySelectorAll('.status-badge span') || [];
assert.lengthOf(badges, 1);
assert.strictEqual(badges[0]?.textContent?.trim(), 'No speculative loads');
assert.include(sections[2]?.textContent, 'Learn more: Speculative loading on developer.chrome.com');
});
it('renderes prerender used', async () => {
const data: PreloadingComponents.UsedPreloadingView.UsedPreloadingViewData = {
pageURL: urlString`https://example.com/prerendered.html`,
previousAttempts: [
{
action: Protocol.Preload.SpeculationAction.Prefetch,
key: {
loaderId: 'loaderId:1' as Protocol.Network.LoaderId,
action: Protocol.Preload.SpeculationAction.Prefetch,
url: urlString`https://example.com/prefetched.html`,
},
pipelineId: 'pipelineId:1' as Protocol.Preload.PreloadPipelineId,
status: SDK.PreloadingModel.PreloadingStatus.READY,
prefetchStatus: null,
requestId: 'requestId:1' as Protocol.Network.RequestId,
ruleSetIds: ['ruleSetId:1'] as Protocol.Preload.RuleSetId[],
nodeIds: [1] as Protocol.DOM.BackendNodeId[],
},
{
action: Protocol.Preload.SpeculationAction.Prerender,
key: {
loaderId: 'loaderId:1' as Protocol.Network.LoaderId,
action: Protocol.Preload.SpeculationAction.Prerender,
url: urlString`https://example.com/prerendered.html`,
},
pipelineId: 'pipelineId:2' as Protocol.Preload.PreloadPipelineId,
status: SDK.PreloadingModel.PreloadingStatus.SUCCESS,
prerenderStatus: null,
disallowedMojoInterface: null,
mismatchedHeaders: null,
ruleSetIds: ['ruleSetId:1'] as Protocol.Preload.RuleSetId[],
nodeIds: [1] as Protocol.DOM.BackendNodeId[],
},
],
currentAttempts: [],
};
const component = await renderUsedPreloadingView(data);
assert.isNotNull(component.shadowRoot);
const headers = getElementsWithinComponent(
component, 'devtools-report devtools-report-section-header', ReportView.ReportView.ReportSectionHeader);
const sections = getElementsWithinComponent(
component, 'devtools-report devtools-report-section', ReportView.ReportView.ReportSection);
assert.lengthOf(headers, 2);
assert.lengthOf(sections, 3);
assert.include(headers[0]?.textContent, 'Speculative loading status');
assert.strictEqual(sections[0]?.querySelector('.status-badge span')?.textContent?.trim(), 'Success');
assert.include(sections[0]?.textContent, 'This page was successfully prerendered.');
assert.include(headers[1]?.textContent, 'Speculations initiated by this page');
const badges = sections[1]?.querySelectorAll('.status-badge span') || [];
assert.lengthOf(badges, 1);
assert.strictEqual(badges[0]?.textContent?.trim(), 'No speculative loads');
assert.include(sections[2]?.textContent, 'Learn more: Speculative loading on developer.chrome.com');
});
it('renderes prefetch failed', async () => {
const data: PreloadingComponents.UsedPreloadingView.UsedPreloadingViewData = {
pageURL: urlString`https://example.com/prefetched.html`,
previousAttempts: [
{
action: Protocol.Preload.SpeculationAction.Prefetch,
key: {
loaderId: 'loaderId:1' as Protocol.Network.LoaderId,
action: Protocol.Preload.SpeculationAction.Prefetch,
url: urlString`https://example.com/prefetched.html`,
},
pipelineId: 'pipelineId:1' as Protocol.Preload.PreloadPipelineId,
status: SDK.PreloadingModel.PreloadingStatus.FAILURE,
prefetchStatus: Protocol.Preload.PrefetchStatus.PrefetchFailedIneligibleRedirect,
requestId: 'requestId:1' as Protocol.Network.RequestId,
ruleSetIds: ['ruleSetId:1'] as Protocol.Preload.RuleSetId[],
nodeIds: [1] as Protocol.DOM.BackendNodeId[],
},
{
action: Protocol.Preload.SpeculationAction.Prerender,
key: {
loaderId: 'loaderId:1' as Protocol.Network.LoaderId,
action: Protocol.Preload.SpeculationAction.Prerender,
url: urlString`https://example.com/prerendered.html`,
},
pipelineId: 'pipelineId:2' as Protocol.Preload.PreloadPipelineId,
status: SDK.PreloadingModel.PreloadingStatus.FAILURE,
prerenderStatus: Protocol.Preload.PrerenderFinalStatus.TriggerDestroyed,
disallowedMojoInterface: null,
mismatchedHeaders: null,
ruleSetIds: ['ruleSetId:1'] as Protocol.Preload.RuleSetId[],
nodeIds: [1] as Protocol.DOM.BackendNodeId[],
},
],
currentAttempts: [],
};
const component = await renderUsedPreloadingView(data);
assert.isNotNull(component.shadowRoot);
const headers = getElementsWithinComponent(
component, 'devtools-report devtools-report-section-header', ReportView.ReportView.ReportSectionHeader);
const sections = getElementsWithinComponent(
component, 'devtools-report devtools-report-section', ReportView.ReportView.ReportSection);
assert.lengthOf(headers, 3);
assert.lengthOf(sections, 4);
assert.include(headers[0]?.textContent, 'Speculative loading status');
assert.strictEqual(sections[0]?.querySelector('.status-badge span')?.textContent?.trim(), 'Failure');
assert.include(
sections[0]?.textContent,
'The initiating page attempted to prefetch this page\'s URL, but the prefetch failed, so a full navigation was performed instead.');
assert.include(headers[1]?.textContent, 'Failure reason');
assert.include(
sections[1]?.textContent, 'The prefetch was redirected, but the redirect URL is not eligible for prefetch.');
assert.include(headers[2]?.textContent, 'Speculations initiated by this page');
const badges = sections[2]?.querySelectorAll('.status-badge span') || [];
assert.lengthOf(badges, 1);
assert.strictEqual(badges[0]?.textContent?.trim(), 'No speculative loads');
assert.include(sections[3]?.textContent, 'Learn more: Speculative loading on developer.chrome.com');
});
it('renderes prerender failed', async () => {
const data: PreloadingComponents.UsedPreloadingView.UsedPreloadingViewData = {
pageURL: urlString`https://example.com/prerendered.html`,
previousAttempts: [
{
action: Protocol.Preload.SpeculationAction.Prefetch,
key: {
loaderId: 'loaderId:1' as Protocol.Network.LoaderId,
action: Protocol.Preload.SpeculationAction.Prefetch,
url: urlString`https://example.com/prefetched.html`,
},
pipelineId: 'pipelineId:1' as Protocol.Preload.PreloadPipelineId,
status: SDK.PreloadingModel.PreloadingStatus.READY,
prefetchStatus: null,
requestId: 'requestId:1' as Protocol.Network.RequestId,
ruleSetIds: ['ruleSetId:1'] as Protocol.Preload.RuleSetId[],
nodeIds: [1] as Protocol.DOM.BackendNodeId[],
},
{
action: Protocol.Preload.SpeculationAction.Prerender,
key: {
loaderId: 'loaderId:1' as Protocol.Network.LoaderId,
action: Protocol.Preload.SpeculationAction.Prerender,
url: urlString`https://example.com/prerendered.html`,
},
pipelineId: 'pipelineId:2' as Protocol.Preload.PreloadPipelineId,
status: SDK.PreloadingModel.PreloadingStatus.FAILURE,
prerenderStatus: Protocol.Preload.PrerenderFinalStatus.MojoBinderPolicy,
disallowedMojoInterface: 'device.mojom.GamepadMonitor',
mismatchedHeaders: null,
ruleSetIds: ['ruleSetId:1'] as Protocol.Preload.RuleSetId[],
nodeIds: [1] as Protocol.DOM.BackendNodeId[],
},
],
currentAttempts: [],
};
const component = await renderUsedPreloadingView(data);
assert.isNotNull(component.shadowRoot);
const headers = getElementsWithinComponent(
component, 'devtools-report devtools-report-section-header', ReportView.ReportView.ReportSectionHeader);
const sections = getElementsWithinComponent(
component, 'devtools-report devtools-report-section', ReportView.ReportView.ReportSection);
assert.lengthOf(headers, 3);
assert.lengthOf(sections, 4);
assert.include(headers[0]?.textContent, 'Speculative loading status');
assert.strictEqual(sections[0]?.querySelector('.status-badge span')?.textContent?.trim(), 'Failure');
assert.include(
sections[0]?.textContent,
'The initiating page attempted to prerender this page\'s URL, but the prerender failed, so a full navigation was performed instead.');
assert.include(headers[1]?.textContent, 'Failure reason');
assert.include(
sections[1]?.textContent,
'The prerendered page used a forbidden JavaScript API that is currently not supported. (Internal Mojo interface: device.mojom.GamepadMonitor)');
assert.include(headers[2]?.textContent, 'Speculations initiated by this page');
const badges = sections[2]?.querySelectorAll('.status-badge span') || [];
assert.lengthOf(badges, 1);
assert.strictEqual(badges[0]?.textContent?.trim(), 'No speculative loads');
assert.include(sections[3]?.textContent, 'Learn more: Speculative loading on developer.chrome.com');
});
it('renderes prerender failed due to header mismatch', async () => {
const data: PreloadingComponents.UsedPreloadingView.UsedPreloadingViewData = {
pageURL: urlString`https://example.com/prerendered.html`,
previousAttempts: [
{
action: Protocol.Preload.SpeculationAction.Prefetch,
key: {
loaderId: 'loaderId:1' as Protocol.Network.LoaderId,
action: Protocol.Preload.SpeculationAction.Prefetch,
url: urlString`https://example.com/prefetched.html`,
},
pipelineId: 'pipelineId:1' as Protocol.Preload.PreloadPipelineId,
status: SDK.PreloadingModel.PreloadingStatus.READY,
prefetchStatus: null,
requestId: 'requestId:1' as Protocol.Network.RequestId,
ruleSetIds: ['ruleSetId:1'] as Protocol.Preload.RuleSetId[],
nodeIds: [1] as Protocol.DOM.BackendNodeId[],
},
{
action: Protocol.Preload.SpeculationAction.Prerender,
key: {
loaderId: 'loaderId:1' as Protocol.Network.LoaderId,
action: Protocol.Preload.SpeculationAction.Prerender,
url: urlString`https://example.com/prerendered.html`,
},
pipelineId: 'pipelineId:2' as Protocol.Preload.PreloadPipelineId,
status: SDK.PreloadingModel.PreloadingStatus.FAILURE,
prerenderStatus: Protocol.Preload.PrerenderFinalStatus.ActivationNavigationParameterMismatch,
disallowedMojoInterface: null,
mismatchedHeaders: [
{
headerName: 'sec-ch-ua-platform' as string,
initialValue: 'Linux' as string,
activationValue: 'Android' as string,
},
{
headerName: 'sec-ch-ua-mobile' as string,
initialValue: '?0' as string,
activationValue: '?1' as string,
},
] as Protocol.Preload.PrerenderMismatchedHeaders[],
ruleSetIds: ['ruleSetId:1'] as Protocol.Preload.RuleSetId[],
nodeIds: [1] as Protocol.DOM.BackendNodeId[],
},
],
currentAttempts: [],
};
const component = await renderUsedPreloadingView(data);
assert.isNotNull(component.shadowRoot);
const headers = getElementsWithinComponent(
component, 'devtools-report devtools-report-section-header', ReportView.ReportView.ReportSectionHeader);
const sections = getElementsWithinComponent(
component, 'devtools-report devtools-report-section', ReportView.ReportView.ReportSection);
const grid = getElementWithinComponent(
component, 'devtools-resources-preloading-mismatched-headers-grid',
PreloadingComponents.PreloadingMismatchedHeadersGrid.PreloadingMismatchedHeadersGrid);
assert.lengthOf(headers, 4);
assert.lengthOf(sections, 5);
assert.include(headers[0]?.textContent, 'Speculative loading status');
assert.include(
sections[0]?.textContent,
'The initiating page attempted to prerender this page\'s URL, but the prerender failed, so a full navigation was performed instead.');
assert.include(headers[1]?.textContent, 'Failure reason');
assert.include(
sections[1]?.textContent,
'The prerender was not used because during activation time, different navigation parameters (e.g., HTTP headers) were calculated than during the original prerendering navigation request.');
assert.include(headers[2]?.textContent, 'Mismatched HTTP request headers');
assertGridContents(
grid,
['Header name', 'Value in initial navigation', 'Value in activation navigation'],
[
['sec-ch-ua-platform', 'Linux', 'Android'],
['sec-ch-ua-mobile', '?0', '?1'],
],
);
assert.include(headers[3]?.textContent, 'Speculations initiated by this page');
const badges = sections[3]?.querySelectorAll('.status-badge span') || [];
assert.lengthOf(badges, 1);
assert.strictEqual(badges[0]?.textContent?.trim(), 'No speculative loads');
assert.include(sections[4]?.textContent, 'Learn more: Speculative loading on developer.chrome.com');
});
it('renderes prerender -> prefetch downgraded and used', async () => {
const data: PreloadingComponents.UsedPreloadingView.UsedPreloadingViewData = {
pageURL: urlString`https://example.com/downgraded.html`,
previousAttempts: [
{
action: Protocol.Preload.SpeculationAction.Prefetch,
key: {
loaderId: 'loaderId:1' as Protocol.Network.LoaderId,
action: Protocol.Preload.SpeculationAction.Prefetch,
url: urlString`https://example.com/downgraded.html`,
},
pipelineId: 'pipelineId:1' as Protocol.Preload.PreloadPipelineId,
status: SDK.PreloadingModel.PreloadingStatus.SUCCESS,
prefetchStatus: null,
requestId: 'requestId:1' as Protocol.Network.RequestId,
ruleSetIds: ['ruleSetId:1'] as Protocol.Preload.RuleSetId[],
nodeIds: [1] as Protocol.DOM.BackendNodeId[],
},
{
action: Protocol.Preload.SpeculationAction.Prerender,
key: {
loaderId: 'loaderId:1' as Protocol.Network.LoaderId,
action: Protocol.Preload.SpeculationAction.Prerender,
url: urlString`https://example.com/downgraded.html`,
},
pipelineId: 'pipelineId:2' as Protocol.Preload.PreloadPipelineId,
status: SDK.PreloadingModel.PreloadingStatus.FAILURE,
prerenderStatus: Protocol.Preload.PrerenderFinalStatus.MojoBinderPolicy,
disallowedMojoInterface: 'device.mojom.GamepadMonitor',
mismatchedHeaders: null,
ruleSetIds: ['ruleSetId:1'] as Protocol.Preload.RuleSetId[],
nodeIds: [1] as Protocol.DOM.BackendNodeId[],
},
],
currentAttempts: [],
};
const component = await renderUsedPreloadingView(data);
assert.isNotNull(component.shadowRoot);
const headers = getElementsWithinComponent(
component, 'devtools-report devtools-report-section-header', ReportView.ReportView.ReportSectionHeader);
const sections = getElementsWithinComponent(
component, 'devtools-report devtools-report-section', ReportView.ReportView.ReportSection);
assert.lengthOf(headers, 3);
assert.lengthOf(sections, 4);
assert.include(headers[0]?.textContent, 'Speculative loading status');
assert.strictEqual(sections[0]?.querySelector('.status-badge span')?.textContent?.trim(), 'Success');
assert.include(
sections[0]?.textContent,
'The initiating page attempted to prerender this page\'s URL. The prerender failed, but the resulting response body was still used as a prefetch.');
assert.include(headers[1]?.textContent, 'Failure reason');
assert.include(
sections[1]?.textContent,
'The prerendered page used a forbidden JavaScript API that is currently not supported. (Internal Mojo interface: device.mojom.GamepadMonitor)');
assert.include(headers[2]?.textContent, 'Speculations initiated by this page');
const badges = sections[2]?.querySelectorAll('.status-badge span') || [];
assert.lengthOf(badges, 1);
assert.strictEqual(badges[0]?.textContent?.trim(), 'No speculative loads');
assert.include(sections[3]?.textContent, 'Learn more: Speculative loading on developer.chrome.com');
});
it('renders no preloading attempts used', async () => {
const data: PreloadingComponents.UsedPreloadingView.UsedPreloadingViewData = {
pageURL: urlString`https://example.com/no-preloads.html`,
previousAttempts: [],
currentAttempts: [],
};
const component = await renderUsedPreloadingView(data);
assert.isNotNull(component.shadowRoot);
const headers = getElementsWithinComponent(
component, 'devtools-report devtools-report-section-header', ReportView.ReportView.ReportSectionHeader);
const sections = getElementsWithinComponent(
component, 'devtools-report devtools-report-section', ReportView.ReportView.ReportSection);
assert.lengthOf(headers, 2);
assert.lengthOf(sections, 3);
assert.include(headers[0]?.textContent, 'Speculative loading status');
assert.strictEqual(sections[0]?.querySelector('.status-badge span')?.textContent?.trim(), 'No speculative loads');
assert.include(
sections[0]?.textContent, 'The initiating page did not attempt to speculatively load this page\'s URL.');
assert.include(headers[1]?.textContent, 'Speculations initiated by this page');
const badges = sections[1]?.querySelectorAll('.status-badge span') || [];
assert.lengthOf(badges, 1);
assert.strictEqual(badges[0]?.textContent?.trim(), 'No speculative loads');
assert.include(sections[2]?.textContent, 'Learn more: Speculative loading on developer.chrome.com');
});
it('ignores hash part of URL for prefetch', async () => {
const data: PreloadingComponents.UsedPreloadingView.UsedPreloadingViewData = {
pageURL: urlString`https://example.com/prefetched.html#alpha`,
previousAttempts: [
{
action: Protocol.Preload.SpeculationAction.Prefetch,
key: {
loaderId: 'loaderId:1' as Protocol.Network.LoaderId,
action: Protocol.Preload.SpeculationAction.Prefetch,
url: urlString`https://example.com/prefetched.html#beta`,
},
pipelineId: 'pipelineId:1' as Protocol.Preload.PreloadPipelineId,
status: SDK.PreloadingModel.PreloadingStatus.SUCCESS,
prefetchStatus: null,
requestId: 'requestId:1' as Protocol.Network.RequestId,
ruleSetIds: ['ruleSetId:1'] as Protocol.Preload.RuleSetId[],
nodeIds: [1] as Protocol.DOM.BackendNodeId[],
},
],
currentAttempts: [],
};
const component = await renderUsedPreloadingView(data);
assert.isNotNull(component.shadowRoot);
const headers = getElementsWithinComponent(
component, 'devtools-report devtools-report-section-header', ReportView.ReportView.ReportSectionHeader);
const sections = getElementsWithinComponent(
component, 'devtools-report devtools-report-section', ReportView.ReportView.ReportSection);
assert.lengthOf(headers, 2);
assert.lengthOf(sections, 3);
assert.include(headers[0]?.textContent, 'Speculative loading status');
assert.strictEqual(sections[0]?.querySelector('.status-badge span')?.textContent?.trim(), 'Success');
assert.include(sections[0]?.textContent, 'This page was successfully prefetched.');
assert.include(headers[1]?.textContent, 'Speculations initiated by this page');
const badges = sections[1]?.querySelectorAll('.status-badge span') || [];
assert.lengthOf(badges, 1);
assert.strictEqual(badges[0]?.textContent?.trim(), 'No speculative loads');
assert.include(sections[2]?.textContent, 'Learn more: Speculative loading on developer.chrome.com');
});
it('doesn\'t ignore hash part of URL for prerender', async () => {
// Prerender uses more strict URL matcher and distinguish URLs by fragments.
const data: PreloadingComponents.UsedPreloadingView.UsedPreloadingViewData = {
pageURL: urlString`https://example.com/prerendered.html#alpha`,
previousAttempts: [
{
action: Protocol.Preload.SpeculationAction.Prerender,
key: {
loaderId: 'loaderId:1' as Protocol.Network.LoaderId,
action: Protocol.Preload.SpeculationAction.Prerender,
url: urlString`https://example.com/prerendered.html#beta`,
},
pipelineId: 'pipelineId:1' as Protocol.Preload.PreloadPipelineId,
status: SDK.PreloadingModel.PreloadingStatus.READY,
prerenderStatus: null,
disallowedMojoInterface: null,
mismatchedHeaders: null,
ruleSetIds: ['ruleSetId:1'] as Protocol.Preload.RuleSetId[],
nodeIds: [1] as Protocol.DOM.BackendNodeId[],
},
],
currentAttempts: [],
};
const component = await renderUsedPreloadingView(data);
assert.isNotNull(component.shadowRoot);
const headers = getElementsWithinComponent(
component, 'devtools-report devtools-report-section-header', ReportView.ReportView.ReportSectionHeader);
const sections = getElementsWithinComponent(
component, 'devtools-report devtools-report-section', ReportView.ReportView.ReportSection);
assert.lengthOf(headers, 4);
assert.lengthOf(sections, 5);
assert.include(headers[0]?.textContent, 'Speculative loading status');
assert.strictEqual(sections[0]?.querySelector('.status-badge span')?.textContent?.trim(), 'No speculative loads');
assert.include(
sections[0]?.textContent, 'The initiating page did not attempt to speculatively load this page\'s URL.');
assert.include(headers[1]?.textContent, 'Current URL');
assert.include(sections[1]?.textContent, 'https://example.com/prerendered.html#alpha');
assert.include(headers[2]?.textContent, 'URLs being speculatively loaded by the initiating page');
const grid = sections[2].querySelector('devtools-resources-mismatched-preloading-grid');
assert.instanceOf(grid, PreloadingComponents.MismatchedPreloadingGrid.MismatchedPreloadingGrid);
assertGridContents(
grid,
['URL', 'Action', 'Status'],
[
['https://example.com/prerendered.html#betalpha', 'Prerender', 'Ready'],
],
);
assert.include(headers[3]?.textContent, 'Speculations initiated by this page');
const badges = sections[3]?.querySelectorAll('.status-badge span') || [];
assert.lengthOf(badges, 1);
assert.strictEqual(badges[0]?.textContent?.trim(), 'No speculative loads');
assert.include(sections[4]?.textContent, 'Learn more: Speculative loading on developer.chrome.com');
});
it('renders no preloading attempts used with mismatch', async () => {
const data: PreloadingComponents.UsedPreloadingView.UsedPreloadingViewData = {
pageURL: urlString`https://example.com/no-preloads.html`,
previousAttempts: [
{
action: Protocol.Preload.SpeculationAction.Prefetch,
key: {
loaderId: 'loaderId:1' as Protocol.Network.LoaderId,
action: Protocol.Preload.SpeculationAction.Prefetch,
url: urlString`https://example.com/prefetched.html`,
},
pipelineId: 'pipelineId:1' as Protocol.Preload.PreloadPipelineId,
status: SDK.PreloadingModel.PreloadingStatus.READY,
prefetchStatus: null,
requestId: 'requestId:1' as Protocol.Network.RequestId,
ruleSetIds: ['ruleSetId:1'] as Protocol.Preload.RuleSetId[],
nodeIds: [1] as Protocol.DOM.BackendNodeId[],
},
{
action: Protocol.Preload.SpeculationAction.Prerender,
key: {
loaderId: 'loaderId:1' as Protocol.Network.LoaderId,
action: Protocol.Preload.SpeculationAction.Prerender,
url: urlString`https://example.com/prerendered.html`,
},
pipelineId: 'pipelineId:2' as Protocol.Preload.PreloadPipelineId,
status: SDK.PreloadingModel.PreloadingStatus.FAILURE,
prerenderStatus: Protocol.Preload.PrerenderFinalStatus.TriggerDestroyed,
disallowedMojoInterface: null,
mismatchedHeaders: null,
ruleSetIds: ['ruleSetId:1'] as Protocol.Preload.RuleSetId[],
nodeIds: [1] as Protocol.DOM.BackendNodeId[],
},
],
currentAttempts: [],
};
const component = await renderUsedPreloadingView(data);
assert.isNotNull(component.shadowRoot);
const headers = getElementsWithinComponent(
component, 'devtools-report devtools-report-section-header', ReportView.ReportView.ReportSectionHeader);
const sections = getElementsWithinComponent(
component, 'devtools-report devtools-report-section', ReportView.ReportView.ReportSection);
assert.lengthOf(headers, 4);
assert.lengthOf(sections, 5);
assert.include(headers[0]?.textContent, 'Speculative loading status');
assert.strictEqual(sections[0]?.querySelector('.status-badge span')?.textContent?.trim(), 'No speculative loads');
assert.include(
sections[0]?.textContent, 'The initiating page did not attempt to speculatively load this page\'s URL.');
assert.include(headers[1]?.textContent, 'Current URL');
assert.include(sections[1]?.textContent, 'https://example.com/no-preloads.html');
assert.include(headers[2]?.textContent, 'URLs being speculatively loaded by the initiating page');
assert.exists(sections[2].querySelector('devtools-resources-mismatched-preloading-grid'));
assert.include(headers[3]?.textContent, 'Speculations initiated by this page');
const badges = sections[3]?.querySelectorAll('.status-badge span') || [];
assert.lengthOf(badges, 1);
assert.strictEqual(badges[0]?.textContent?.trim(), 'No speculative loads');
assert.include(sections[4]?.textContent, 'Learn more: Speculative loading on developer.chrome.com');
});
it('renders preloads initialized by this page', async () => {
const data: PreloadingComponents.UsedPreloadingView.UsedPreloadingViewData = {
pageURL: urlString`https://example.com/no-preloads.html`,
previousAttempts: [],
currentAttempts: [
{
action: Protocol.Preload.SpeculationAction.Prefetch,
key: {
loaderId: 'loaderId:1' as Protocol.Network.LoaderId,
action: Protocol.Preload.SpeculationAction.Prefetch,
url: urlString`https://example.com/prefetch-not-triggered.html`,
},
pipelineId: null,
status: SDK.PreloadingModel.PreloadingStatus.NOT_TRIGGERED,
prefetchStatus: null,
requestId: 'requestId:1' as Protocol.Network.RequestId,
ruleSetIds: ['ruleSetId:1'] as Protocol.Preload.RuleSetId[],
nodeIds: [1] as Protocol.DOM.BackendNodeId[],
},
{
action: Protocol.Preload.SpeculationAction.Prefetch,
key: {
loaderId: 'loaderId:1' as Protocol.Network.LoaderId,
action: Protocol.Preload.SpeculationAction.Prefetch,
url: urlString`https://example.com/prefetch-running.html`,
},
pipelineId: 'pipelineId:2' as Protocol.Preload.PreloadPipelineId,
status: SDK.PreloadingModel.PreloadingStatus.RUNNING,
prefetchStatus: null,
requestId: 'requestId:2' as Protocol.Network.RequestId,
ruleSetIds: ['ruleSetId:1'] as Protocol.Preload.RuleSetId[],
nodeIds: [1] as Protocol.DOM.BackendNodeId[],
},
{
action: Protocol.Preload.SpeculationAction.Prefetch,
key: {
loaderId: 'loaderId:1' as Protocol.Network.LoaderId,
action: Protocol.Preload.SpeculationAction.Prefetch,
url: urlString`https://example.com/prefetch-ready.html`,
},
pipelineId: 'pipelineId:3' as Protocol.Preload.PreloadPipelineId,
status: SDK.PreloadingModel.PreloadingStatus.READY,
prefetchStatus: null,
requestId: 'requestId:3' as Protocol.Network.RequestId,
ruleSetIds: ['ruleSetId:1'] as Protocol.Preload.RuleSetId[],
nodeIds: [1] as Protocol.DOM.BackendNodeId[],
},
{
action: Protocol.Preload.SpeculationAction.Prefetch,
key: {
loaderId: 'loaderId:1' as Protocol.Network.LoaderId,
action: Protocol.Preload.SpeculationAction.Prefetch,
url: urlString`https://example.com/prefetch-failure.html`,
},
pipelineId: 'pipelineId:4' as Protocol.Preload.PreloadPipelineId,
status: SDK.PreloadingModel.PreloadingStatus.FAILURE,
prefetchStatus: null,
requestId: 'requestId:4' as Protocol.Network.RequestId,
ruleSetIds: ['ruleSetId:1'] as Protocol.Preload.RuleSetId[],
nodeIds: [1] as Protocol.DOM.BackendNodeId[],
},
{
action: Protocol.Preload.SpeculationAction.Prerender,
key: {
loaderId: 'loaderId:1' as Protocol.Network.LoaderId,
action: Protocol.Preload.SpeculationAction.Prerender,
url: urlString`https://example.com/prerender-pending.html`,
},
pipelineId: 'pipelineId:5' as Protocol.Preload.PreloadPipelineId,
status: SDK.PreloadingModel.PreloadingStatus.PENDING,
prerenderStatus: null,
disallowedMojoInterface: null,
mismatchedHeaders: null,
ruleSetIds: ['ruleSetId:1'] as Protocol.Preload.RuleSetId[],
nodeIds: [1] as Protocol.DOM.BackendNodeId[],
},
{
action: Protocol.Preload.SpeculationAction.Prerender,
key: {
loaderId: 'loaderId:1' as Protocol.Network.LoaderId,
action: Protocol.Preload.SpeculationAction.Prerender,
url: urlString`https://example.com/prerender-ready.html`,
},
pipelineId: 'pipelineId:6' as Protocol.Preload.PreloadPipelineId,
status: SDK.PreloadingModel.PreloadingStatus.READY,
prerenderStatus: null,
mismatchedHeaders: null,
disallowedMojoInterface: null,
ruleSetIds: ['ruleSetId:1'] as Protocol.Preload.RuleSetId[],
nodeIds: [1] as Protocol.DOM.BackendNodeId[],
},
],
};
const component = await renderUsedPreloadingView(data);
assert.isNotNull(component.shadowRoot);
const headers = getElementsWithinComponent(
component, 'devtools-report devtools-report-section-header', ReportView.ReportView.ReportSectionHeader);
const sections = getElementsWithinComponent(
component, 'devtools-report devtools-report-section', ReportView.ReportView.ReportSection);
assert.lengthOf(headers, 2);
assert.lengthOf(sections, 3);
assert.include(headers[0]?.textContent, 'Speculative loading status');
assert.strictEqual(sections[0]?.querySelector('.status-badge span')?.textContent?.trim(), 'No speculative loads');
assert.include(
sections[0]?.textContent, 'The initiating page did not attempt to speculatively load this page\'s URL.');
assert.include(headers[1]?.textContent, 'Speculations initiated by this page');
const badges = sections[1]?.querySelectorAll('.status-badge span') || [];
assert.lengthOf(badges, 4);
assert.strictEqual(badges[0]?.textContent?.trim(), '1 not triggered');
assert.strictEqual(badges[1]?.textContent?.trim(), '2 in progress');
assert.strictEqual(badges[2]?.textContent?.trim(), '2 success');
assert.strictEqual(badges[3]?.textContent?.trim(), '1 failure');
assert.include(sections[2]?.textContent, 'Learn more: Speculative loading on developer.chrome.com');
});
});