Skip to content

Commit

Permalink
new_audit: bf-cache (#14465)
Browse files Browse the repository at this point in the history
  • Loading branch information
adamraine committed Jan 11, 2023
1 parent 437eb4d commit 40e8195
Show file tree
Hide file tree
Showing 29 changed files with 934 additions and 279 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/devtools.yml
Expand Up @@ -51,8 +51,8 @@ jobs:
# 3) every change to file in Lighthouse repo important to running these tests.
#
# The number is how many times this hash key was manually updated to break the cache.
key: ${{ runner.os }}-10-${{ env.WEEK_OF_THE_YEAR }}-${{ hashFiles('cdt-test-hash.txt') }}
restore-keys: ${{ runner.os }}-10
key: ${{ runner.os }}-11-${{ env.WEEK_OF_THE_YEAR }}-${{ hashFiles('cdt-test-hash.txt') }}
restore-keys: ${{ runner.os }}-11
- name: Set GHA_DEVTOOLS_CACHE_HIT
if: steps.devtools-cache.outputs.cache-hit == 'true'
run: echo "GHA_DEVTOOLS_CACHE_HIT=1" >> $GITHUB_ENV
Expand Down
5 changes: 3 additions & 2 deletions build/build-cdt-strings.js
Expand Up @@ -94,11 +94,11 @@ export {
fs.writeFileSync(outFile, createStringsModule(uiStringsDeclare, extraCode));
}

// core/lib/bfcache-strings.js
// core/lib/bf-cache-strings.js
{
// eslint-disable-next-line max-len
const inFile = `${LH_ROOT}/node_modules/chrome-devtools-frontend/front_end/panels/application/components/BackForwardCacheStrings.ts`;
const outFile = `${LH_ROOT}/core/lib/bfcache-strings.js`;
const outFile = `${LH_ROOT}/core/lib/bf-cache-strings.js`;

const input = fs.readFileSync(inFile, 'utf-8');

Expand All @@ -109,6 +109,7 @@ export {
]);

const extraCode = `
/** @type {Record<string, {name: LH.IcuMessage} | undefined>} */
${notRestoredReasonDescriptionDeclare}
export {
Expand Down
28 changes: 14 additions & 14 deletions cli/test/smokehouse/core-tests.js
Expand Up @@ -14,6 +14,8 @@ import errorsExpiredSsl from './test-definitions/errors-expired-ssl.js';
import errorsIframeExpiredSsl from './test-definitions/errors-iframe-expired-ssl.js';
import errorsInfiniteLoop from './test-definitions/errors-infinite-loop.js';
import formsAutoComplete from './test-definitions/forms-autocomplete.js';
import fpsMax from './test-definitions/fps-max.js';
import fpsScaled from './test-definitions/fps-scaled.js';
import issuesMixedContent from './test-definitions/issues-mixed-content.js';
import lanternFetch from './test-definitions/lantern-fetch.js';
import lanternIdleCallbackLong from './test-definitions/lantern-idle-callback-long.js';
Expand Down Expand Up @@ -51,19 +53,17 @@ import pwaSvgomg from './test-definitions/pwa-svgomg.js';
import redirectsClientPaintServer from './test-definitions/redirects-client-paint-server.js';
import redirectsHistoryPushState from './test-definitions/redirects-history-push-state.js';
import redirectsMultipleServer from './test-definitions/redirects-multiple-server.js';
import redirectScripts from './test-definitions/redirects-scripts.js';
import redirectsScripts from './test-definitions/redirects-scripts.js';
import redirectsSelf from './test-definitions/redirects-self.js';
import redirectsSingleClient from './test-definitions/redirects-single-client.js';
import redirectsSingleServer from './test-definitions/redirects-single-server.js';
import redirectsSelf from './test-definitions/redirects-self.js';
import screenshot from './test-definitions/screenshot.js';
import seoFailing from './test-definitions/seo-failing.js';
import seoPassing from './test-definitions/seo-passing.js';
import seoStatus403 from './test-definitions/seo-status-403.js';
import seoTapTargets from './test-definitions/seo-tap-targets.js';
import sourceMaps from './test-definitions/source-maps.js';
import timing from './test-definitions/timing.js';
import fpsScaled from './test-definitions/fps-scaled.js';
import fpsMax from './test-definitions/fps-max.js';

/** @type {ReadonlyArray<Smokehouse.TestDfn>} */
const smokeTests = [
Expand All @@ -77,19 +77,21 @@ const smokeTests = [
errorsIframeExpiredSsl,
errorsInfiniteLoop,
formsAutoComplete,
fpsMax,
fpsScaled,
issuesMixedContent,
lanternFetch,
lanternIdleCallbackLong,
lanternIdleCallbackShort,
lanternOnline,
lanternSetTimeout,
lanternFetch,
lanternXhr,
lanternIdleCallbackShort,
lanternIdleCallbackLong,
legacyJavascript,
metricsDebugger,
metricsDelayedFcp,
metricsDelayedLcp,
metricsTrickyTtiLateFcp,
metricsTrickyTti,
metricsTrickyTtiLateFcp,
offlineOnlineOnly,
offlineReady,
offlineSwBroken,
Expand All @@ -107,26 +109,24 @@ const smokeTests = [
perfTraceElements,
pubads,
pwaAirhorner,
pwaChromestatus,
pwaSvgomg,
pwaCaltrain,
pwaChromestatus,
pwaRocks,
pwaSvgomg,
redirectsClientPaintServer,
redirectsHistoryPushState,
redirectsMultipleServer,
redirectScripts,
redirectsScripts,
redirectsSelf,
redirectsSingleClient,
redirectsSingleServer,
redirectsSelf,
screenshot,
seoFailing,
seoPassing,
seoStatus403,
seoTapTargets,
sourceMaps,
timing,
fpsScaled,
fpsMax,
];

export default smokeTests;
55 changes: 55 additions & 0 deletions cli/test/smokehouse/test-definitions/dobetterweb.js
Expand Up @@ -443,6 +443,61 @@ const expectations = {
}],
},
},
'bf-cache': {
details: {
items: [
{
reason: 'The page has an unload handler in the main frame.',
failureType: 'Actionable',
subItems: {
items: [{
frameUrl: 'http://localhost:10200/dobetterweb/dbw_tester.html',
}],
},
},
{
// Support for this was added in M109
// https://crbug.com/1350944
_maxChromiumVersion: '108',
reason: 'Pages that have requested notifications permissions are not currently eligible for back/forward cache.',
failureType: 'Pending browser support',
subItems: {
items: [{
frameUrl: 'http://localhost:10200/dobetterweb/dbw_tester.html',
}],
},
},
{
// This issue only appears in the DevTools runner for some reason.
// TODO: Investigate why this doesn't happen on the CLI runner.
_runner: 'devtools',
reason: 'There were permission requests upon navigating away.',
failureType: 'Pending browser support',
subItems: {
items: [{
frameUrl: 'http://localhost:10200/dobetterweb/dbw_tester.html',
}],
},
},
{
// The DevTools runner uses Puppeteer to launch Chrome which disables BFCache by default.
// https://github.com/puppeteer/puppeteer/issues/8197
//
// If we ignore the Puppeteer args and force BFCache to be enabled, it causes thew viewport to be sized incorrectly for other tests.
// These viewport issues are not present when Lighthouse is run from DevTools manually.
// TODO: Investigate why BFCache causes viewport issues only in our DevTools smoke tests.
_runner: 'devtools',
reason: 'Back/forward cache is disabled by flags. Visit chrome://flags/#back-forward-cache to enable it locally on this device.',
failureType: 'Not actionable',
subItems: {
items: [{
frameUrl: 'http://localhost:10200/dobetterweb/dbw_tester.html',
}],
},
},
],
},
},
'full-page-screenshot': {
score: null,
details: {
Expand Down
4 changes: 4 additions & 0 deletions cli/test/smokehouse/test-definitions/perf-budgets.js
Expand Up @@ -13,6 +13,10 @@ const config = {
// webpages present here, hence the inclusion of 'best-practices'.
onlyCategories: ['performance', 'best-practices'],

// BF cache will request the page again, initiating additional network requests.
// Disable the audit so we only detect requests from the normal page load.
skipAudits: ['bf-cache'],

// A mixture of under, over, and meeting budget to exercise all paths.
budgets: [{
path: '/',
Expand Down
4 changes: 4 additions & 0 deletions cli/test/smokehouse/test-definitions/perf-fonts.js
Expand Up @@ -13,6 +13,10 @@ const config = {
// webpages present here, hence the inclusion of 'best-practices'.
onlyCategories: ['performance', 'best-practices'],

// BF cache will request the page again, initiating additional network requests.
// Disable the audit so we only detect requests from the normal page load.
skipAudits: ['bf-cache'],

// A mixture of under, over, and meeting budget to exercise all paths.
budgets: [{
path: '/',
Expand Down
4 changes: 4 additions & 0 deletions cli/test/smokehouse/test-definitions/perf-frame-metrics.js
Expand Up @@ -13,6 +13,10 @@ const config = {
// webpages present here, hence the inclusion of 'best-practices'.
onlyCategories: ['performance', 'best-practices'],

// BF cache will request the page again, initiating additional network requests.
// Disable the audit so we only detect requests from the normal page load.
skipAudits: ['bf-cache'],

// A mixture of under, over, and meeting budget to exercise all paths.
budgets: [{
path: '/',
Expand Down
4 changes: 4 additions & 0 deletions cli/test/smokehouse/test-definitions/perf-preload.js
Expand Up @@ -13,6 +13,10 @@ const config = {
// webpages present here, hence the inclusion of 'best-practices'.
onlyCategories: ['performance', 'best-practices'],

// BF cache will request the page again, initiating additional network requests.
// Disable the audit so we only detect requests from the normal page load.
skipAudits: ['bf-cache'],

// A mixture of under, over, and meeting budget to exercise all paths.
budgets: [{
path: '/',
Expand Down
4 changes: 4 additions & 0 deletions cli/test/smokehouse/test-definitions/perf-trace-elements.js
Expand Up @@ -13,6 +13,10 @@ const config = {
// webpages present here, hence the inclusion of 'best-practices'.
onlyCategories: ['performance', 'best-practices'],

// BF cache will request the page again, initiating additional network requests.
// Disable the audit so we only detect requests from the normal page load.
skipAudits: ['bf-cache'],

// A mixture of under, over, and meeting budget to exercise all paths.
budgets: [{
path: '/',
Expand Down
120 changes: 120 additions & 0 deletions core/audits/bf-cache.js
@@ -0,0 +1,120 @@
/**
* @license Copyright 2022 The Lighthouse Authors. All Rights Reserved.
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
*/

import {Audit} from './audit.js';
import * as i18n from '../lib/i18n/i18n.js';
import {NotRestoredReasonDescription} from '../lib/bf-cache-strings.js';

/* eslint-disable max-len */
const UIStrings = {
/** Title of a diagnostic Lighthouse audit that identifies when the back/forward cache is being used. "back/forward" refers to the back and forward buttons found in modern browsers. This title is shown to users if the back/forward cache was used, or if the there was no attempt to restore the page from the back/forward cache. */
title: `Page didn't prevent back/forward cache restoration`,
/** Title of a diagnostic Lighthouse audit that identifies when the back/forward cache is being used. "back/forward" refers to the back and forward buttons found in modern browsers. This title is shown to users if the page attempted to restore from the back/forward cache but the back/forward cache was not used. */
failureTitle: 'Page prevented back/forward cache restoration',
/** Description of a diagnostic Lighthouse audit that identifies when the back/forward cache is being used. "back/forward" refers to the back and forward buttons found in modern browsers. */
description: 'Many navigations are performed by going back to a previous page, or forwards again. The back/forward cache (bfcache) can speed up these return navigations. [Learn more about the bfcache](https://web.dev/bfcache/)',
/** Failure type for an error that the user should be able to address themselves. Shown in a table column with other failure types. */
actionableFailureType: 'Actionable',
/** Failure type for an error that the user cannot address in the page's code. Shown in a table column with other failure types. */
notActionableFailureType: 'Not actionable',
/** Failure type for an error caused by missing browser support. Shown in a table column with other failure types. */
supportPendingFailureType: 'Pending browser support',
/** Label for a column in a data table; entries in the column will be a string explaining why a failure occurred. */
failureReasonColumn: 'Failure reason',
/** Label for a column in a data table; entries in the column will be a string representing the type of failure preventing the back/forward cache from being used. */
failureTypeColumn: 'Failure type',
/**
* @description [ICU Syntax] Label for an audit identifying the number of back/forward cache failure reasons found in the page.
*/
displayValue: `{itemCount, plural,
=1 {1 failure reason}
other {# failure reasons}
}`,
};
/* eslint-enable max-len */

const str_ = i18n.createIcuMessageFn(import.meta.url, UIStrings);

/** @type {LH.Crdp.Page.BackForwardCacheNotRestoredReasonType[]} */
const ORDERED_FAILURE_TYPES = ['PageSupportNeeded', 'SupportPending', 'Circumstantial'];

/** @type {Record<LH.Crdp.Page.BackForwardCacheNotRestoredReasonType, string | LH.IcuMessage>} */
const FAILURE_TYPE_TO_STRING = {
PageSupportNeeded: str_(UIStrings.actionableFailureType),
Circumstantial: str_(UIStrings.notActionableFailureType),
SupportPending: str_(UIStrings.supportPendingFailureType),
};

class BFCache extends Audit {
/**
* @return {LH.Audit.Meta}
*/
static get meta() {
return {
id: 'bf-cache',
title: str_(UIStrings.title),
failureTitle: str_(UIStrings.failureTitle),
description: str_(UIStrings.description),
supportedModes: ['navigation', 'timespan'],
requiredArtifacts: ['BFCacheFailures'],
};
}

/**
* @param {LH.Artifacts} artifacts
* @return {Promise<LH.Audit.Product>}
*/
static async audit(artifacts) {
const failures = artifacts.BFCacheFailures;
if (!failures.length) return {score: 1};

// TODO: Analyze more than one bf cache failure.
const {notRestoredReasonsTree} = failures[0];

/** @type {LH.Audit.Details.TableItem[]} */
const results = [];

for (const failureType of ORDERED_FAILURE_TYPES) {
const reasonsMap = notRestoredReasonsTree[failureType];

for (const [reason, frameUrls] of Object.entries(reasonsMap)) {
results.push({
reason: NotRestoredReasonDescription[reason]?.name ?? reason,
failureType: FAILURE_TYPE_TO_STRING[failureType],
subItems: {
type: 'subitems',
items: frameUrls.map(frameUrl => ({frameUrl})),
},
// Include hidden protocol reason code for debugging.
protocolReason: reason,
});
}
}

/** @type {LH.Audit.Details.Table['headings']} */
const headings = [
/* eslint-disable max-len */
{key: 'reason', valueType: 'text', subItemsHeading: {key: 'frameUrl', valueType: 'url'}, label: str_(UIStrings.failureReasonColumn)},
{key: 'failureType', valueType: 'text', label: str_(UIStrings.failureTypeColumn)},
/* eslint-enable max-len */
];

const details = Audit.makeTableDetails(headings, results);

const displayValue = results.length ?
str_(UIStrings.displayValue, {itemCount: results.length}) :
undefined;

return {
score: results.length ? 0 : 1,
displayValue,
details,
};
}
}

export default BFCache;
export {UIStrings};
2 changes: 1 addition & 1 deletion core/audits/installable-manifest.js
Expand Up @@ -16,7 +16,7 @@ const UIStrings = {
'failureTitle': 'Web app manifest or service worker do not meet the installability requirements',
/** Description of a Lighthouse audit that tells the user why installability is important for webapps. This is displayed after a user expands the section to see more. No character length limits. The last sentence starting with 'Learn' becomes link text to additional documentation. */
'description': `Service worker is the technology that enables your app to use many Progressive Web App features, such as offline, add to homescreen, and push notifications. With proper service worker and manifest implementations, browsers can proactively prompt users to add your app to their homescreen, which can lead to higher engagement. [Learn more about manifest installability requirements](https://developer.chrome.com/docs/lighthouse/pwa/installable-manifest/).`,
/** Description Table column header for the observed value of the Installability failure reason statistic. */
/** Label for a column in a data table; entries in the column will be a string explaining why a failure occurred. */
'columnValue': 'Failure reason',
/**
* @description [ICU Syntax] Label for an audit identifying the number of installability errors found in the page.
Expand Down
7 changes: 6 additions & 1 deletion core/config/default-config.js
Expand Up @@ -217,8 +217,11 @@ const defaultConfig = {
{id: artifacts.devtoolsLogs, gatherer: 'devtools-log-compat'},
{id: artifacts.traces, gatherer: 'trace-compat'},

// FullPageScreenshot comes at the very end so all other node analysis is captured.
// FullPageScreenshot comes at the end so all other node analysis is captured.
{id: artifacts.FullPageScreenshot, gatherer: 'full-page-screenshot'},

// BFCacheErrors comes at the very end because it can perform a page navigation.
{id: artifacts.BFCacheFailures, gatherer: 'bf-cache-failures'},
],
audits: [
'is-on-https',
Expand Down Expand Up @@ -374,6 +377,7 @@ const defaultConfig = {
'seo/canonical',
'seo/manual/structured-data',
'work-during-interaction',
'bf-cache',
],
groups: {
'metrics': {
Expand Down Expand Up @@ -516,6 +520,7 @@ const defaultConfig = {
{id: 'no-unload-listeners', weight: 0},
{id: 'uses-responsive-images-snapshot', weight: 0},
{id: 'work-during-interaction', weight: 0},
{id: 'bf-cache', weight: 0},

// Budget audits.
{id: 'performance-budget', weight: 0, group: 'budgets'},
Expand Down

0 comments on commit 40e8195

Please sign in to comment.