lighthouse 9.5.0-dev.20230110 → 9.5.0-dev.20230112
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cli/bin.js +6 -6
- package/cli/run.js +1 -1
- package/cli/test/smokehouse/config/exclusions.js +5 -0
- package/cli/test/smokehouse/core-tests.js +14 -14
- package/cli/test/smokehouse/lighthouse-runners/bundle.js +9 -9
- package/cli/test/smokehouse/lighthouse-runners/cli.js +7 -7
- package/cli/test/smokehouse/lighthouse-runners/devtools.js +3 -3
- package/cli/test/smokehouse/readme.md +1 -1
- package/cli/test/smokehouse/smokehouse.js +13 -13
- package/core/audits/bf-cache.js +120 -0
- package/core/audits/installable-manifest.js +1 -1
- package/core/audits/seo/is-crawlable.js +114 -42
- package/core/config/config-helpers.js +11 -11
- package/core/config/config-plugin.js +2 -2
- package/core/config/config.js +15 -15
- package/core/config/default-config.js +8 -3
- package/core/config/desktop-config.js +1 -1
- package/core/config/experimental-config.js +1 -1
- package/core/config/full-config.js +1 -1
- package/core/config/lr-desktop-config.js +1 -1
- package/core/config/lr-mobile-config.js +1 -1
- package/core/config/perf-config.js +1 -1
- package/core/config/validation.js +4 -4
- package/core/gather/driver/execution-context.js +2 -2
- package/core/gather/driver/network-monitor.js +2 -2
- package/core/gather/driver/wait-for-condition.js +9 -2
- package/core/gather/gatherers/bf-cache-failures.js +21 -3
- package/core/gather/navigation-runner.js +1 -1
- package/core/gather/snapshot-runner.js +1 -1
- package/core/gather/timespan-runner.js +1 -1
- package/core/index.js +10 -10
- package/core/legacy/config/config.js +23 -23
- package/core/legacy/config/legacy-default-config.js +1 -1
- package/core/lib/{bfcache-strings.js → bf-cache-strings.js} +1 -0
- package/core/user-flow.js +9 -9
- package/core/util.cjs +13 -3
- package/dist/report/bundle.esm.js +13 -3
- package/dist/report/flow.js +1 -1
- package/dist/report/standalone.js +1 -1
- package/package.json +1 -1
- package/report/renderer/util.js +13 -3
- package/report/test/generator/report-generator-test.js +1 -1
- package/report/test/renderer/util-test.js +2 -0
- package/shared/localization/locales/en-US.json +141 -114
- package/shared/localization/locales/en-XL.json +141 -114
- package/tsconfig.json +1 -0
- package/types/config.d.ts +18 -18
- package/types/smokehouse.d.ts +2 -2
- package/types/user-flow.d.ts +1 -1
package/cli/bin.js
CHANGED
|
@@ -63,20 +63,20 @@ async function begin() {
|
|
|
63
63
|
|
|
64
64
|
const urlUnderTest = cliFlags._[0];
|
|
65
65
|
|
|
66
|
-
/** @type {LH.Config
|
|
67
|
-
let
|
|
66
|
+
/** @type {LH.Config|undefined} */
|
|
67
|
+
let config;
|
|
68
68
|
if (cliFlags.configPath) {
|
|
69
69
|
// Resolve the config file path relative to where cli was called.
|
|
70
70
|
cliFlags.configPath = path.resolve(process.cwd(), cliFlags.configPath);
|
|
71
71
|
|
|
72
72
|
if (cliFlags.configPath.endsWith('.json')) {
|
|
73
|
-
|
|
73
|
+
config = JSON.parse(fs.readFileSync(cliFlags.configPath, 'utf-8'));
|
|
74
74
|
} else {
|
|
75
75
|
const configModuleUrl = url.pathToFileURL(cliFlags.configPath).href;
|
|
76
|
-
|
|
76
|
+
config = (await import(configModuleUrl)).default;
|
|
77
77
|
}
|
|
78
78
|
} else if (cliFlags.preset) {
|
|
79
|
-
|
|
79
|
+
config = (await import(`../core/config/${cliFlags.preset}-config.js`)).default;
|
|
80
80
|
}
|
|
81
81
|
|
|
82
82
|
if (cliFlags.budgetPath) {
|
|
@@ -132,7 +132,7 @@ async function begin() {
|
|
|
132
132
|
});
|
|
133
133
|
}
|
|
134
134
|
|
|
135
|
-
return runLighthouse(urlUnderTest, cliFlags,
|
|
135
|
+
return runLighthouse(urlUnderTest, cliFlags, config);
|
|
136
136
|
}
|
|
137
137
|
|
|
138
138
|
export {
|
package/cli/run.js
CHANGED
|
@@ -209,7 +209,7 @@ async function potentiallyKillChrome(launchedChrome) {
|
|
|
209
209
|
/**
|
|
210
210
|
* @param {string} url
|
|
211
211
|
* @param {LH.CliFlags} flags
|
|
212
|
-
* @param {LH.Config
|
|
212
|
+
* @param {LH.Config|undefined} config
|
|
213
213
|
* @return {Promise<LH.RunnerResult|undefined>}
|
|
214
214
|
*/
|
|
215
215
|
async function runLighthouse(url, flags, config) {
|
|
@@ -14,6 +14,8 @@ import errorsExpiredSsl from './test-definitions/errors-expired-ssl.js';
|
|
|
14
14
|
import errorsIframeExpiredSsl from './test-definitions/errors-iframe-expired-ssl.js';
|
|
15
15
|
import errorsInfiniteLoop from './test-definitions/errors-infinite-loop.js';
|
|
16
16
|
import formsAutoComplete from './test-definitions/forms-autocomplete.js';
|
|
17
|
+
import fpsMax from './test-definitions/fps-max.js';
|
|
18
|
+
import fpsScaled from './test-definitions/fps-scaled.js';
|
|
17
19
|
import issuesMixedContent from './test-definitions/issues-mixed-content.js';
|
|
18
20
|
import lanternFetch from './test-definitions/lantern-fetch.js';
|
|
19
21
|
import lanternIdleCallbackLong from './test-definitions/lantern-idle-callback-long.js';
|
|
@@ -51,10 +53,10 @@ import pwaSvgomg from './test-definitions/pwa-svgomg.js';
|
|
|
51
53
|
import redirectsClientPaintServer from './test-definitions/redirects-client-paint-server.js';
|
|
52
54
|
import redirectsHistoryPushState from './test-definitions/redirects-history-push-state.js';
|
|
53
55
|
import redirectsMultipleServer from './test-definitions/redirects-multiple-server.js';
|
|
54
|
-
import
|
|
56
|
+
import redirectsScripts from './test-definitions/redirects-scripts.js';
|
|
57
|
+
import redirectsSelf from './test-definitions/redirects-self.js';
|
|
55
58
|
import redirectsSingleClient from './test-definitions/redirects-single-client.js';
|
|
56
59
|
import redirectsSingleServer from './test-definitions/redirects-single-server.js';
|
|
57
|
-
import redirectsSelf from './test-definitions/redirects-self.js';
|
|
58
60
|
import screenshot from './test-definitions/screenshot.js';
|
|
59
61
|
import seoFailing from './test-definitions/seo-failing.js';
|
|
60
62
|
import seoPassing from './test-definitions/seo-passing.js';
|
|
@@ -62,8 +64,6 @@ import seoStatus403 from './test-definitions/seo-status-403.js';
|
|
|
62
64
|
import seoTapTargets from './test-definitions/seo-tap-targets.js';
|
|
63
65
|
import sourceMaps from './test-definitions/source-maps.js';
|
|
64
66
|
import timing from './test-definitions/timing.js';
|
|
65
|
-
import fpsScaled from './test-definitions/fps-scaled.js';
|
|
66
|
-
import fpsMax from './test-definitions/fps-max.js';
|
|
67
67
|
|
|
68
68
|
/** @type {ReadonlyArray<Smokehouse.TestDfn>} */
|
|
69
69
|
const smokeTests = [
|
|
@@ -77,19 +77,21 @@ const smokeTests = [
|
|
|
77
77
|
errorsIframeExpiredSsl,
|
|
78
78
|
errorsInfiniteLoop,
|
|
79
79
|
formsAutoComplete,
|
|
80
|
+
fpsMax,
|
|
81
|
+
fpsScaled,
|
|
80
82
|
issuesMixedContent,
|
|
83
|
+
lanternFetch,
|
|
84
|
+
lanternIdleCallbackLong,
|
|
85
|
+
lanternIdleCallbackShort,
|
|
81
86
|
lanternOnline,
|
|
82
87
|
lanternSetTimeout,
|
|
83
|
-
lanternFetch,
|
|
84
88
|
lanternXhr,
|
|
85
|
-
lanternIdleCallbackShort,
|
|
86
|
-
lanternIdleCallbackLong,
|
|
87
89
|
legacyJavascript,
|
|
88
90
|
metricsDebugger,
|
|
89
91
|
metricsDelayedFcp,
|
|
90
92
|
metricsDelayedLcp,
|
|
91
|
-
metricsTrickyTtiLateFcp,
|
|
92
93
|
metricsTrickyTti,
|
|
94
|
+
metricsTrickyTtiLateFcp,
|
|
93
95
|
offlineOnlineOnly,
|
|
94
96
|
offlineReady,
|
|
95
97
|
offlineSwBroken,
|
|
@@ -107,17 +109,17 @@ const smokeTests = [
|
|
|
107
109
|
perfTraceElements,
|
|
108
110
|
pubads,
|
|
109
111
|
pwaAirhorner,
|
|
110
|
-
pwaChromestatus,
|
|
111
|
-
pwaSvgomg,
|
|
112
112
|
pwaCaltrain,
|
|
113
|
+
pwaChromestatus,
|
|
113
114
|
pwaRocks,
|
|
115
|
+
pwaSvgomg,
|
|
114
116
|
redirectsClientPaintServer,
|
|
115
117
|
redirectsHistoryPushState,
|
|
116
118
|
redirectsMultipleServer,
|
|
117
|
-
|
|
119
|
+
redirectsScripts,
|
|
120
|
+
redirectsSelf,
|
|
118
121
|
redirectsSingleClient,
|
|
119
122
|
redirectsSingleServer,
|
|
120
|
-
redirectsSelf,
|
|
121
123
|
screenshot,
|
|
122
124
|
seoFailing,
|
|
123
125
|
seoPassing,
|
|
@@ -125,8 +127,6 @@ const smokeTests = [
|
|
|
125
127
|
seoTapTargets,
|
|
126
128
|
sourceMaps,
|
|
127
129
|
timing,
|
|
128
|
-
fpsScaled,
|
|
129
|
-
fpsMax,
|
|
130
130
|
];
|
|
131
131
|
|
|
132
132
|
export default smokeTests;
|
|
@@ -26,9 +26,9 @@ import {loadArtifacts, saveArtifacts} from '../../../../core/lib/asset-saver.js'
|
|
|
26
26
|
// This runs only in the worker. The rest runs on the main thread.
|
|
27
27
|
if (!isMainThread && parentPort) {
|
|
28
28
|
(async () => {
|
|
29
|
-
const {url,
|
|
29
|
+
const {url, config, testRunnerOptions} = workerData;
|
|
30
30
|
try {
|
|
31
|
-
const result = await runBundledLighthouse(url,
|
|
31
|
+
const result = await runBundledLighthouse(url, config, testRunnerOptions);
|
|
32
32
|
// Save to assets directory because LighthouseError won't survive postMessage.
|
|
33
33
|
const assetsDir = fs.mkdtempSync(os.tmpdir() + '/smoke-bundle-assets-');
|
|
34
34
|
await saveArtifacts(result.artifacts, assetsDir);
|
|
@@ -46,11 +46,11 @@ if (!isMainThread && parentPort) {
|
|
|
46
46
|
|
|
47
47
|
/**
|
|
48
48
|
* @param {string} url
|
|
49
|
-
* @param {LH.Config
|
|
49
|
+
* @param {LH.Config|undefined} config
|
|
50
50
|
* @param {{isDebug?: boolean, useLegacyNavigation?: boolean}} testRunnerOptions
|
|
51
51
|
* @return {Promise<{lhr: LH.Result, artifacts: LH.Artifacts}>}
|
|
52
52
|
*/
|
|
53
|
-
async function runBundledLighthouse(url,
|
|
53
|
+
async function runBundledLighthouse(url, config, testRunnerOptions) {
|
|
54
54
|
if (isMainThread || !parentPort) {
|
|
55
55
|
throw new Error('must be called in worker');
|
|
56
56
|
}
|
|
@@ -87,12 +87,12 @@ async function runBundledLighthouse(url, configJson, testRunnerOptions) {
|
|
|
87
87
|
if (testRunnerOptions.useLegacyNavigation) {
|
|
88
88
|
const connection = new CriConnection(port);
|
|
89
89
|
runnerResult =
|
|
90
|
-
await legacyNavigation(url, {port, logLevel},
|
|
90
|
+
await legacyNavigation(url, {port, logLevel}, config, connection);
|
|
91
91
|
} else {
|
|
92
92
|
// Puppeteer is not included in the bundle, we must create the page here.
|
|
93
93
|
const browser = await puppeteer.connect({browserURL: `http://localhost:${port}`});
|
|
94
94
|
const page = await browser.newPage();
|
|
95
|
-
runnerResult = await lighthouse(url, {port, logLevel},
|
|
95
|
+
runnerResult = await lighthouse(url, {port, logLevel}, config, page);
|
|
96
96
|
}
|
|
97
97
|
if (!runnerResult) throw new Error('No runnerResult');
|
|
98
98
|
|
|
@@ -109,17 +109,17 @@ async function runBundledLighthouse(url, configJson, testRunnerOptions) {
|
|
|
109
109
|
/**
|
|
110
110
|
* Launch Chrome and do a full Lighthouse run via the Lighthouse DevTools bundle.
|
|
111
111
|
* @param {string} url
|
|
112
|
-
* @param {LH.Config
|
|
112
|
+
* @param {LH.Config=} config
|
|
113
113
|
* @param {{isDebug?: boolean, useLegacyNavigation?: boolean}=} testRunnerOptions
|
|
114
114
|
* @return {Promise<{lhr: LH.Result, artifacts: LH.Artifacts, log: string}>}
|
|
115
115
|
*/
|
|
116
|
-
async function runLighthouse(url,
|
|
116
|
+
async function runLighthouse(url, config, testRunnerOptions = {}) {
|
|
117
117
|
/** @type {string[]} */
|
|
118
118
|
const logs = [];
|
|
119
119
|
const worker = new Worker(new URL(import.meta.url), {
|
|
120
120
|
stdout: true,
|
|
121
121
|
stderr: true,
|
|
122
|
-
workerData: {url,
|
|
122
|
+
workerData: {url, config, testRunnerOptions},
|
|
123
123
|
});
|
|
124
124
|
worker.stdout.setEncoding('utf8');
|
|
125
125
|
worker.stderr.setEncoding('utf8');
|
|
@@ -27,16 +27,16 @@ const execFileAsync = promisify(execFile);
|
|
|
27
27
|
/**
|
|
28
28
|
* Launch Chrome and do a full Lighthouse run via the Lighthouse CLI.
|
|
29
29
|
* @param {string} url
|
|
30
|
-
* @param {LH.Config
|
|
30
|
+
* @param {LH.Config=} config
|
|
31
31
|
* @param {{isDebug?: boolean, useFraggleRock?: boolean}=} testRunnerOptions
|
|
32
32
|
* @return {Promise<{lhr: LH.Result, artifacts: LH.Artifacts, log: string}>}
|
|
33
33
|
*/
|
|
34
|
-
async function runLighthouse(url,
|
|
34
|
+
async function runLighthouse(url, config, testRunnerOptions = {}) {
|
|
35
35
|
const {isDebug} = testRunnerOptions;
|
|
36
36
|
const tmpDir = `${LH_ROOT}/.tmp/smokehouse`;
|
|
37
37
|
await fs.mkdir(tmpDir, {recursive: true});
|
|
38
38
|
const tmpPath = await fs.mkdtemp(`${tmpDir}/smokehouse-`);
|
|
39
|
-
return internalRun(url, tmpPath,
|
|
39
|
+
return internalRun(url, tmpPath, config, testRunnerOptions)
|
|
40
40
|
// Wait for internalRun() before removing scratch directory.
|
|
41
41
|
.finally(() => !isDebug && fs.rm(tmpPath, {recursive: true, force: true}));
|
|
42
42
|
}
|
|
@@ -45,11 +45,11 @@ async function runLighthouse(url, configJson, testRunnerOptions = {}) {
|
|
|
45
45
|
* Internal runner.
|
|
46
46
|
* @param {string} url
|
|
47
47
|
* @param {string} tmpPath
|
|
48
|
-
* @param {LH.Config
|
|
48
|
+
* @param {LH.Config=} config
|
|
49
49
|
* @param {{isDebug?: boolean, useLegacyNavigation?: boolean}=} options
|
|
50
50
|
* @return {Promise<{lhr: LH.Result, artifacts: LH.Artifacts, log: string}>}
|
|
51
51
|
*/
|
|
52
|
-
async function internalRun(url, tmpPath,
|
|
52
|
+
async function internalRun(url, tmpPath, config, options) {
|
|
53
53
|
const {isDebug = false, useLegacyNavigation = false} = options || {};
|
|
54
54
|
const localConsole = new LocalConsole();
|
|
55
55
|
|
|
@@ -72,9 +72,9 @@ async function internalRun(url, tmpPath, configJson, options) {
|
|
|
72
72
|
}
|
|
73
73
|
|
|
74
74
|
// Config can be optionally provided.
|
|
75
|
-
if (
|
|
75
|
+
if (config) {
|
|
76
76
|
const configPath = `${tmpPath}/config.json`;
|
|
77
|
-
await fs.writeFile(configPath, JSON.stringify(
|
|
77
|
+
await fs.writeFile(configPath, JSON.stringify(config));
|
|
78
78
|
args.push(`--config-path=${configPath}`);
|
|
79
79
|
}
|
|
80
80
|
|
|
@@ -41,16 +41,16 @@ async function setup() {
|
|
|
41
41
|
* unless DEVTOOLS_PATH is set.
|
|
42
42
|
* CHROME_PATH determines which Chrome is used–otherwise the default is puppeteer's chrome binary.
|
|
43
43
|
* @param {string} url
|
|
44
|
-
* @param {LH.Config
|
|
44
|
+
* @param {LH.Config=} config
|
|
45
45
|
* @param {{isDebug?: boolean, useLegacyNavigation?: boolean}=} testRunnerOptions
|
|
46
46
|
* @return {Promise<{lhr: LH.Result, artifacts: LH.Artifacts, log: string}>}
|
|
47
47
|
*/
|
|
48
|
-
async function runLighthouse(url,
|
|
48
|
+
async function runLighthouse(url, config, testRunnerOptions = {}) {
|
|
49
49
|
const chromeFlags = [
|
|
50
50
|
`--custom-devtools-frontend=file://${devtoolsDir}/out/LighthouseIntegration/gen/front_end`,
|
|
51
51
|
];
|
|
52
52
|
const {lhr, artifacts, logs} = await testUrlFromDevtools(url, {
|
|
53
|
-
config
|
|
53
|
+
config,
|
|
54
54
|
chromeFlags,
|
|
55
55
|
useLegacyNavigation: testRunnerOptions.useLegacyNavigation,
|
|
56
56
|
});
|
|
@@ -14,7 +14,7 @@ See [`SmokehouseOptions`](https://github.com/GoogleChrome/lighthouse/blob/main/c
|
|
|
14
14
|
| -------------- | ---------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- |
|
|
15
15
|
| `id` | `string` | The string identifier of the test. |
|
|
16
16
|
| `expectations` | `{lhr: Object, artifacts: Object}` | See below. |
|
|
17
|
-
| `config` | `LH.Config
|
|
17
|
+
| `config` | `LH.Config` (optional) | An optional Lighthouse config. If not specified, the default config is used. |
|
|
18
18
|
| `runSerially` | `boolean` (optional) | An optional flag. If set to true, the test won't be run in parallel to other tests. Useful if the test is performance sensitive. |
|
|
19
19
|
|
|
20
20
|
### Expectations
|
|
@@ -134,21 +134,21 @@ function purpleify(str) {
|
|
|
134
134
|
}
|
|
135
135
|
|
|
136
136
|
/**
|
|
137
|
-
* @param {LH.Config
|
|
138
|
-
* @return {LH.Config
|
|
137
|
+
* @param {LH.Config=} config
|
|
138
|
+
* @return {LH.Config|undefined}
|
|
139
139
|
*/
|
|
140
|
-
function convertToLegacyConfig(
|
|
141
|
-
if (!
|
|
140
|
+
function convertToLegacyConfig(config) {
|
|
141
|
+
if (!config) return config;
|
|
142
142
|
|
|
143
143
|
return {
|
|
144
|
-
...
|
|
144
|
+
...config,
|
|
145
145
|
passes: [{
|
|
146
146
|
passName: 'defaultPass',
|
|
147
|
-
pauseAfterFcpMs:
|
|
148
|
-
pauseAfterLoadMs:
|
|
149
|
-
networkQuietThresholdMs:
|
|
150
|
-
cpuQuietThresholdMs:
|
|
151
|
-
blankPage:
|
|
147
|
+
pauseAfterFcpMs: config.settings?.pauseAfterFcpMs,
|
|
148
|
+
pauseAfterLoadMs: config.settings?.pauseAfterLoadMs,
|
|
149
|
+
networkQuietThresholdMs: config.settings?.networkQuietThresholdMs,
|
|
150
|
+
cpuQuietThresholdMs: config.settings?.cpuQuietThresholdMs,
|
|
151
|
+
blankPage: config.settings?.blankPage,
|
|
152
152
|
}],
|
|
153
153
|
};
|
|
154
154
|
}
|
|
@@ -184,15 +184,15 @@ async function runSmokeTest(smokeTestDefn, testOptions) {
|
|
|
184
184
|
bufferedConsole.log(` Retrying run (${i} out of ${retries} retries)…`);
|
|
185
185
|
}
|
|
186
186
|
|
|
187
|
-
let
|
|
187
|
+
let config = smokeTestDefn.config;
|
|
188
188
|
if (useLegacyNavigation) {
|
|
189
|
-
|
|
189
|
+
config = convertToLegacyConfig(config);
|
|
190
190
|
}
|
|
191
191
|
|
|
192
192
|
// Run Lighthouse.
|
|
193
193
|
try {
|
|
194
194
|
result = {
|
|
195
|
-
...await lighthouseRunner(requestedUrl,
|
|
195
|
+
...await lighthouseRunner(requestedUrl, config, {isDebug, useLegacyNavigation}),
|
|
196
196
|
networkRequests: takeNetworkRequestUrls ? takeNetworkRequestUrls() : undefined,
|
|
197
197
|
};
|
|
198
198
|
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license Copyright 2022 The Lighthouse Authors. All Rights Reserved.
|
|
3
|
+
* 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
|
|
4
|
+
* 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.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import {Audit} from './audit.js';
|
|
8
|
+
import * as i18n from '../lib/i18n/i18n.js';
|
|
9
|
+
import {NotRestoredReasonDescription} from '../lib/bf-cache-strings.js';
|
|
10
|
+
|
|
11
|
+
/* eslint-disable max-len */
|
|
12
|
+
const UIStrings = {
|
|
13
|
+
/** 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. */
|
|
14
|
+
title: `Page didn't prevent back/forward cache restoration`,
|
|
15
|
+
/** 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. */
|
|
16
|
+
failureTitle: 'Page prevented back/forward cache restoration',
|
|
17
|
+
/** 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. */
|
|
18
|
+
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/)',
|
|
19
|
+
/** Failure type for an error that the user should be able to address themselves. Shown in a table column with other failure types. */
|
|
20
|
+
actionableFailureType: 'Actionable',
|
|
21
|
+
/** Failure type for an error that the user cannot address in the page's code. Shown in a table column with other failure types. */
|
|
22
|
+
notActionableFailureType: 'Not actionable',
|
|
23
|
+
/** Failure type for an error caused by missing browser support. Shown in a table column with other failure types. */
|
|
24
|
+
supportPendingFailureType: 'Pending browser support',
|
|
25
|
+
/** Label for a column in a data table; entries in the column will be a string explaining why a failure occurred. */
|
|
26
|
+
failureReasonColumn: 'Failure reason',
|
|
27
|
+
/** 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. */
|
|
28
|
+
failureTypeColumn: 'Failure type',
|
|
29
|
+
/**
|
|
30
|
+
* @description [ICU Syntax] Label for an audit identifying the number of back/forward cache failure reasons found in the page.
|
|
31
|
+
*/
|
|
32
|
+
displayValue: `{itemCount, plural,
|
|
33
|
+
=1 {1 failure reason}
|
|
34
|
+
other {# failure reasons}
|
|
35
|
+
}`,
|
|
36
|
+
};
|
|
37
|
+
/* eslint-enable max-len */
|
|
38
|
+
|
|
39
|
+
const str_ = i18n.createIcuMessageFn(import.meta.url, UIStrings);
|
|
40
|
+
|
|
41
|
+
/** @type {LH.Crdp.Page.BackForwardCacheNotRestoredReasonType[]} */
|
|
42
|
+
const ORDERED_FAILURE_TYPES = ['PageSupportNeeded', 'SupportPending', 'Circumstantial'];
|
|
43
|
+
|
|
44
|
+
/** @type {Record<LH.Crdp.Page.BackForwardCacheNotRestoredReasonType, string | LH.IcuMessage>} */
|
|
45
|
+
const FAILURE_TYPE_TO_STRING = {
|
|
46
|
+
PageSupportNeeded: str_(UIStrings.actionableFailureType),
|
|
47
|
+
Circumstantial: str_(UIStrings.notActionableFailureType),
|
|
48
|
+
SupportPending: str_(UIStrings.supportPendingFailureType),
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
class BFCache extends Audit {
|
|
52
|
+
/**
|
|
53
|
+
* @return {LH.Audit.Meta}
|
|
54
|
+
*/
|
|
55
|
+
static get meta() {
|
|
56
|
+
return {
|
|
57
|
+
id: 'bf-cache',
|
|
58
|
+
title: str_(UIStrings.title),
|
|
59
|
+
failureTitle: str_(UIStrings.failureTitle),
|
|
60
|
+
description: str_(UIStrings.description),
|
|
61
|
+
supportedModes: ['navigation', 'timespan'],
|
|
62
|
+
requiredArtifacts: ['BFCacheFailures'],
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* @param {LH.Artifacts} artifacts
|
|
68
|
+
* @return {Promise<LH.Audit.Product>}
|
|
69
|
+
*/
|
|
70
|
+
static async audit(artifacts) {
|
|
71
|
+
const failures = artifacts.BFCacheFailures;
|
|
72
|
+
if (!failures.length) return {score: 1};
|
|
73
|
+
|
|
74
|
+
// TODO: Analyze more than one bf cache failure.
|
|
75
|
+
const {notRestoredReasonsTree} = failures[0];
|
|
76
|
+
|
|
77
|
+
/** @type {LH.Audit.Details.TableItem[]} */
|
|
78
|
+
const results = [];
|
|
79
|
+
|
|
80
|
+
for (const failureType of ORDERED_FAILURE_TYPES) {
|
|
81
|
+
const reasonsMap = notRestoredReasonsTree[failureType];
|
|
82
|
+
|
|
83
|
+
for (const [reason, frameUrls] of Object.entries(reasonsMap)) {
|
|
84
|
+
results.push({
|
|
85
|
+
reason: NotRestoredReasonDescription[reason]?.name ?? reason,
|
|
86
|
+
failureType: FAILURE_TYPE_TO_STRING[failureType],
|
|
87
|
+
subItems: {
|
|
88
|
+
type: 'subitems',
|
|
89
|
+
items: frameUrls.map(frameUrl => ({frameUrl})),
|
|
90
|
+
},
|
|
91
|
+
// Include hidden protocol reason code for debugging.
|
|
92
|
+
protocolReason: reason,
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/** @type {LH.Audit.Details.Table['headings']} */
|
|
98
|
+
const headings = [
|
|
99
|
+
/* eslint-disable max-len */
|
|
100
|
+
{key: 'reason', valueType: 'text', subItemsHeading: {key: 'frameUrl', valueType: 'url'}, label: str_(UIStrings.failureReasonColumn)},
|
|
101
|
+
{key: 'failureType', valueType: 'text', label: str_(UIStrings.failureTypeColumn)},
|
|
102
|
+
/* eslint-enable max-len */
|
|
103
|
+
];
|
|
104
|
+
|
|
105
|
+
const details = Audit.makeTableDetails(headings, results);
|
|
106
|
+
|
|
107
|
+
const displayValue = results.length ?
|
|
108
|
+
str_(UIStrings.displayValue, {itemCount: results.length}) :
|
|
109
|
+
undefined;
|
|
110
|
+
|
|
111
|
+
return {
|
|
112
|
+
score: results.length ? 0 : 1,
|
|
113
|
+
displayValue,
|
|
114
|
+
details,
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export default BFCache;
|
|
120
|
+
export {UIStrings};
|
|
@@ -16,7 +16,7 @@ const UIStrings = {
|
|
|
16
16
|
'failureTitle': 'Web app manifest or service worker do not meet the installability requirements',
|
|
17
17
|
/** 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. */
|
|
18
18
|
'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/).`,
|
|
19
|
-
/**
|
|
19
|
+
/** Label for a column in a data table; entries in the column will be a string explaining why a failure occurred. */
|
|
20
20
|
'columnValue': 'Failure reason',
|
|
21
21
|
/**
|
|
22
22
|
* @description [ICU Syntax] Label for an audit identifying the number of installability errors found in the page.
|