lighthouse 11.2.0 → 11.3.0-dev.20231102

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.
Files changed (106) hide show
  1. package/cli/cli-flags.d.ts +5 -1
  2. package/cli/cli-flags.js +5 -1
  3. package/cli/test/smokehouse/__snapshots__/report-assert-test.js.snap +2 -2
  4. package/cli/test/smokehouse/frontends/smokehouse-bin.js +18 -7
  5. package/cli/test/smokehouse/lighthouse-runners/bundle.d.ts +2 -4
  6. package/cli/test/smokehouse/lighthouse-runners/bundle.js +8 -4
  7. package/cli/test/smokehouse/lighthouse-runners/cli.d.ts +2 -4
  8. package/cli/test/smokehouse/lighthouse-runners/cli.js +5 -3
  9. package/cli/test/smokehouse/lighthouse-runners/devtools.d.ts +2 -1
  10. package/cli/test/smokehouse/lighthouse-runners/devtools.js +3 -1
  11. package/cli/test/smokehouse/smokehouse.d.ts +4 -2
  12. package/cli/test/smokehouse/smokehouse.js +10 -7
  13. package/core/audits/byte-efficiency/legacy-javascript.js +2 -2
  14. package/core/audits/image-size-responsive.js +1 -1
  15. package/core/audits/layout-shift-elements.js +1 -1
  16. package/core/audits/lcp-lazy-loaded.js +2 -2
  17. package/core/audits/long-tasks.js +1 -1
  18. package/core/audits/metrics/cumulative-layout-shift.js +2 -2
  19. package/core/audits/metrics/interaction-to-next-paint.js +2 -2
  20. package/core/audits/metrics/largest-contentful-paint.js +1 -1
  21. package/core/audits/no-unload-listeners.js +1 -1
  22. package/core/audits/preload-fonts.js +1 -1
  23. package/core/audits/prioritize-lcp-image.js +1 -1
  24. package/core/audits/unsized-images.js +1 -1
  25. package/core/audits/work-during-interaction.js +1 -1
  26. package/core/config/config.d.ts +1 -2
  27. package/core/config/config.js +18 -62
  28. package/core/config/constants.d.ts +1 -3
  29. package/core/config/constants.js +4 -18
  30. package/core/config/default-config.js +8 -8
  31. package/core/config/filters.d.ts +0 -8
  32. package/core/config/filters.js +0 -28
  33. package/core/config/validation.d.ts +4 -15
  34. package/core/config/validation.js +18 -63
  35. package/core/gather/base-artifacts.d.ts +2 -2
  36. package/core/gather/base-artifacts.js +5 -5
  37. package/core/gather/driver/navigation.d.ts +1 -1
  38. package/core/gather/driver/navigation.js +1 -1
  39. package/core/gather/driver/prepare.d.ts +3 -18
  40. package/core/gather/driver/prepare.js +21 -43
  41. package/core/gather/driver/storage.d.ts +2 -1
  42. package/core/gather/driver/storage.js +3 -12
  43. package/core/gather/gatherers/inspector-issues.js +1 -0
  44. package/core/gather/navigation-runner.d.ts +3 -31
  45. package/core/gather/navigation-runner.js +46 -113
  46. package/core/lib/navigation-error.d.ts +8 -3
  47. package/core/lib/navigation-error.js +19 -11
  48. package/dist/report/bundle.esm.js +4 -4
  49. package/dist/report/flow.js +7 -7
  50. package/dist/report/standalone.js +5 -5
  51. package/package.json +8 -8
  52. package/report/renderer/performance-category-renderer.js +2 -1
  53. package/shared/localization/locales/ar-XB.json +78 -75
  54. package/shared/localization/locales/ar.json +78 -75
  55. package/shared/localization/locales/bg.json +78 -75
  56. package/shared/localization/locales/ca.json +78 -75
  57. package/shared/localization/locales/cs.json +78 -75
  58. package/shared/localization/locales/da.json +78 -75
  59. package/shared/localization/locales/de.json +81 -78
  60. package/shared/localization/locales/el.json +84 -81
  61. package/shared/localization/locales/en-GB.json +78 -75
  62. package/shared/localization/locales/en-US.json +26 -23
  63. package/shared/localization/locales/en-XA.json +78 -75
  64. package/shared/localization/locales/en-XL.json +26 -23
  65. package/shared/localization/locales/es-419.json +78 -75
  66. package/shared/localization/locales/es.json +78 -75
  67. package/shared/localization/locales/fi.json +79 -76
  68. package/shared/localization/locales/fil.json +78 -75
  69. package/shared/localization/locales/fr.json +78 -75
  70. package/shared/localization/locales/he.json +78 -75
  71. package/shared/localization/locales/hi.json +78 -75
  72. package/shared/localization/locales/hr.json +78 -75
  73. package/shared/localization/locales/hu.json +78 -75
  74. package/shared/localization/locales/id.json +78 -75
  75. package/shared/localization/locales/it.json +79 -76
  76. package/shared/localization/locales/ja.json +78 -75
  77. package/shared/localization/locales/ko.json +78 -75
  78. package/shared/localization/locales/lt.json +78 -75
  79. package/shared/localization/locales/lv.json +78 -75
  80. package/shared/localization/locales/nl.json +78 -75
  81. package/shared/localization/locales/no.json +78 -75
  82. package/shared/localization/locales/pl.json +78 -75
  83. package/shared/localization/locales/pt-PT.json +78 -75
  84. package/shared/localization/locales/pt.json +79 -76
  85. package/shared/localization/locales/ro.json +78 -75
  86. package/shared/localization/locales/ru.json +78 -75
  87. package/shared/localization/locales/sk.json +78 -75
  88. package/shared/localization/locales/sl.json +78 -75
  89. package/shared/localization/locales/sr-Latn.json +78 -75
  90. package/shared/localization/locales/sr.json +78 -75
  91. package/shared/localization/locales/sv.json +78 -75
  92. package/shared/localization/locales/ta.json +78 -75
  93. package/shared/localization/locales/te.json +78 -75
  94. package/shared/localization/locales/th.json +78 -75
  95. package/shared/localization/locales/tr.json +78 -75
  96. package/shared/localization/locales/uk.json +78 -75
  97. package/shared/localization/locales/vi.json +78 -75
  98. package/shared/localization/locales/zh-HK.json +78 -75
  99. package/shared/localization/locales/zh-TW.json +79 -76
  100. package/shared/localization/locales/zh.json +78 -75
  101. package/third-party/chromium-synchronization/inspector-issueAdded-types-test.js +1 -0
  102. package/types/artifacts.d.ts +1 -0
  103. package/types/config.d.ts +0 -38
  104. package/types/internal/rxjs.d.ts +4 -0
  105. package/types/internal/smokehouse.d.ts +10 -5
  106. package/types/lhr/settings.d.ts +13 -0
@@ -89,21 +89,22 @@ async function dismissJavaScriptDialogs(session) {
89
89
  * Reset the storage and warn if any stored data could be affecting the scores.
90
90
  * @param {LH.Gatherer.ProtocolSession} session
91
91
  * @param {string} url
92
+ * @param {LH.Config.Settings['clearStorageTypes']} clearStorageTypes
92
93
  * @return {Promise<{warnings: Array<LH.IcuMessage>}>}
93
94
  */
94
- async function resetStorageForUrl(session, url) {
95
+ async function resetStorageForUrl(session, url, clearStorageTypes) {
95
96
  /** @type {Array<LH.IcuMessage>} */
96
97
  const warnings = [];
97
98
 
98
- const importantStorageWarning = await storage.getImportantStorageWarning(session, url);
99
- if (importantStorageWarning) warnings.push(importantStorageWarning);
100
-
101
- const clearDataWarnings = await storage.clearDataForOrigin(session, url);
99
+ const clearDataWarnings = await storage.clearDataForOrigin(session, url, clearStorageTypes);
102
100
  warnings.push(...clearDataWarnings);
103
101
 
104
102
  const clearCacheWarnings = await storage.clearBrowserCaches(session);
105
103
  warnings.push(...clearCacheWarnings);
106
104
 
105
+ const importantStorageWarning = await storage.getImportantStorageWarning(session, url);
106
+ if (importantStorageWarning) warnings.push(importantStorageWarning);
107
+
107
108
  return {warnings};
108
109
  }
109
110
 
@@ -114,21 +115,17 @@ async function resetStorageForUrl(session, url) {
114
115
  *
115
116
  * @param {LH.Gatherer.ProtocolSession} session
116
117
  * @param {LH.Config.Settings} settings
117
- * @param {{disableThrottling: boolean, blockedUrlPatterns?: string[]}} options
118
118
  */
119
- async function prepareThrottlingAndNetwork(session, settings, options) {
119
+ async function prepareThrottlingAndNetwork(session, settings) {
120
120
  const status = {msg: 'Preparing network conditions', id: `lh:gather:prepareThrottlingAndNetwork`};
121
121
  log.time(status);
122
122
 
123
- if (options.disableThrottling) await emulation.clearThrottling(session);
124
- else await emulation.throttle(session, settings);
123
+ await emulation.throttle(session, settings);
125
124
 
126
125
  // Set request blocking before any network activity.
127
126
  // No "clearing" is done at the end of the recording since Network.setBlockedURLs([]) will unset all if
128
127
  // neccessary at the beginning of the next section.
129
- const blockedUrls = (options.blockedUrlPatterns || []).concat(
130
- settings.blockedUrlPatterns || []
131
- );
128
+ const blockedUrls = settings.blockedUrlPatterns || [];
132
129
  await session.sendCommand('Network.setBlockedURLs', {urls: blockedUrls});
133
130
 
134
131
  const headers = settings.extraHeaders;
@@ -163,10 +160,7 @@ async function prepareTargetForTimespanMode(driver, settings) {
163
160
  log.time(status);
164
161
 
165
162
  await prepareDeviceEmulation(driver, settings);
166
- await prepareThrottlingAndNetwork(driver.defaultSession, settings, {
167
- disableThrottling: false,
168
- blockedUrlPatterns: undefined,
169
- });
163
+ await prepareThrottlingAndNetwork(driver.defaultSession, settings);
170
164
  await warmUpIntlSegmenter(driver);
171
165
 
172
166
  log.timeEnd(status);
@@ -192,11 +186,16 @@ async function warmUpIntlSegmenter(driver) {
192
186
  *
193
187
  * @param {LH.Gatherer.Driver} driver
194
188
  * @param {LH.Config.Settings} settings
189
+ * @param {LH.NavigationRequestor} requestor
190
+ * @return {Promise<{warnings: Array<LH.IcuMessage>}>}
195
191
  */
196
- async function prepareTargetForNavigationMode(driver, settings) {
192
+ async function prepareTargetForNavigationMode(driver, settings, requestor) {
197
193
  const status = {msg: 'Preparing target for navigation mode', id: 'lh:prepare:navigationMode'};
198
194
  log.time(status);
199
195
 
196
+ /** @type {Array<LH.IcuMessage>} */
197
+ const warnings = [];
198
+
200
199
  await prepareDeviceEmulation(driver, settings);
201
200
 
202
201
  // Automatically handle any JavaScript dialogs to prevent a hung renderer.
@@ -212,41 +211,21 @@ async function prepareTargetForNavigationMode(driver, settings) {
212
211
 
213
212
  await warmUpIntlSegmenter(driver);
214
213
 
215
- log.timeEnd(status);
216
- }
217
-
218
- /**
219
- * Prepares a target for a particular navigation by resetting storage and setting network.
220
- *
221
- * This method assumes `prepareTargetForNavigationMode` has already been invoked.
222
- *
223
- * @param {LH.Gatherer.ProtocolSession} session
224
- * @param {LH.Config.Settings} settings
225
- * @param {Pick<LH.Config.NavigationDefn, 'disableThrottling'|'disableStorageReset'|'blockedUrlPatterns'> & {requestor: LH.NavigationRequestor}} navigation
226
- * @return {Promise<{warnings: Array<LH.IcuMessage>}>}
227
- */
228
- async function prepareTargetForIndividualNavigation(session, settings, navigation) {
229
- const status = {msg: 'Preparing target for navigation', id: 'lh:prepare:navigation'};
230
- log.time(status);
231
-
232
- /** @type {Array<LH.IcuMessage>} */
233
- const warnings = [];
234
-
235
- const {requestor} = navigation;
236
214
  const shouldResetStorage =
237
215
  !settings.disableStorageReset &&
238
- !navigation.disableStorageReset &&
239
216
  // Without prior knowledge of the destination, we cannot know which URL to clear storage for.
240
217
  typeof requestor === 'string';
241
218
  if (shouldResetStorage) {
242
- const requestedUrl = requestor;
243
- const {warnings: storageWarnings} = await resetStorageForUrl(session, requestedUrl);
219
+ const {warnings: storageWarnings} = await resetStorageForUrl(driver.defaultSession,
220
+ requestor,
221
+ settings.clearStorageTypes);
244
222
  warnings.push(...storageWarnings);
245
223
  }
246
224
 
247
- await prepareThrottlingAndNetwork(session, settings, navigation);
225
+ await prepareThrottlingAndNetwork(driver.defaultSession, settings);
248
226
 
249
227
  log.timeEnd(status);
228
+
250
229
  return {warnings};
251
230
  }
252
231
 
@@ -254,6 +233,5 @@ export {
254
233
  prepareThrottlingAndNetwork,
255
234
  prepareTargetForTimespanMode,
256
235
  prepareTargetForNavigationMode,
257
- prepareTargetForIndividualNavigation,
258
236
  enableAsyncStacks,
259
237
  };
@@ -1,9 +1,10 @@
1
1
  /**
2
2
  * @param {LH.Gatherer.ProtocolSession} session
3
3
  * @param {string} url
4
+ * @param {LH.Config.Settings['clearStorageTypes']} clearStorageTypes
4
5
  * @return {Promise<LH.IcuMessage[]>}
5
6
  */
6
- export function clearDataForOrigin(session: LH.Gatherer.ProtocolSession, url: string): Promise<LH.IcuMessage[]>;
7
+ export function clearDataForOrigin(session: LH.Gatherer.ProtocolSession, url: string, clearStorageTypes: LH.Config.Settings['clearStorageTypes']): Promise<LH.IcuMessage[]>;
7
8
  /**
8
9
  * Clear the network cache on disk and in memory.
9
10
  * @param {LH.Gatherer.ProtocolSession} session
@@ -36,9 +36,10 @@ const str_ = i18n.createIcuMessageFn(import.meta.url, UIStrings);
36
36
  /**
37
37
  * @param {LH.Gatherer.ProtocolSession} session
38
38
  * @param {string} url
39
+ * @param {LH.Config.Settings['clearStorageTypes']} clearStorageTypes
39
40
  * @return {Promise<LH.IcuMessage[]>}
40
41
  */
41
- async function clearDataForOrigin(session, url) {
42
+ async function clearDataForOrigin(session, url, clearStorageTypes) {
42
43
  const status = {msg: 'Cleaning origin data', id: 'lh:storage:clearDataForOrigin'};
43
44
  log.time(status);
44
45
 
@@ -46,17 +47,7 @@ async function clearDataForOrigin(session, url) {
46
47
 
47
48
  const origin = new URL(url).origin;
48
49
 
49
- // Clear some types of storage.
50
- // Cookies are not cleared, so the user isn't logged out.
51
- // indexeddb, websql, and localstorage are not cleared to prevent loss of potentially important data.
52
- // https://chromedevtools.github.io/debugger-protocol-viewer/tot/Storage/#type-StorageType
53
- const typesToClear = [
54
- // 'cookies',
55
- 'file_systems',
56
- 'shader_cache',
57
- 'service_workers',
58
- 'cache_storage',
59
- ].join(',');
50
+ const typesToClear = clearStorageTypes.join(',');
60
51
 
61
52
  // `Storage.clearDataForOrigin` is one of our PROTOCOL_TIMEOUT culprits and this command is also
62
53
  // run in the context of PAGE_HUNG to cleanup. We'll keep the timeout low and just warn if it fails.
@@ -67,6 +67,7 @@ class InspectorIssues extends BaseGatherer {
67
67
  bounceTrackingIssue: [],
68
68
  clientHintIssue: [],
69
69
  contentSecurityPolicyIssue: [],
70
+ cookieDeprecationMetadataIssue: [],
70
71
  corsIssue: [],
71
72
  deprecationIssue: [],
72
73
  federatedAuthRequestIssue: [],
@@ -2,7 +2,6 @@ export type NavigationContext = {
2
2
  driver: Driver;
3
3
  page: LH.Puppeteer.Page;
4
4
  resolvedConfig: LH.Config.ResolvedConfig;
5
- navigation: LH.Config.NavigationDefn;
6
5
  requestor: LH.NavigationRequestor;
7
6
  baseArtifacts: LH.BaseArtifacts;
8
7
  computedCache: Map<string, LH.ArbitraryEqualityMap>;
@@ -31,40 +30,18 @@ export function _setup({ driver, resolvedConfig, requestor }: {
31
30
  }>;
32
31
  /**
33
32
  * @param {NavigationContext} navigationContext
34
- * @return {Promise<{warnings: Array<LH.IcuMessage>}>}
35
- */
36
- export function _setupNavigation({ requestor, driver, navigation, resolvedConfig }: NavigationContext): Promise<{
37
- warnings: Array<LH.IcuMessage>;
38
- }>;
39
- /**
40
- * @param {NavigationContext} navigationContext
41
- * @return {Promise<{requestedUrl: string, mainDocumentUrl: string, navigationError: LH.LighthouseError | undefined, warnings: Array<LH.IcuMessage>}>}
33
+ * @return {Promise<{requestedUrl: string, mainDocumentUrl: string, navigationError: LH.LighthouseError | undefined}>}
42
34
  */
43
35
  export function _navigate(navigationContext: NavigationContext): Promise<{
44
36
  requestedUrl: string;
45
37
  mainDocumentUrl: string;
46
38
  navigationError: LH.LighthouseError | undefined;
47
- warnings: Array<LH.IcuMessage>;
48
39
  }>;
49
40
  /**
50
41
  * @param {NavigationContext} navigationContext
51
42
  * @return {ReturnType<typeof _computeNavigationResult>}
52
43
  */
53
44
  export function _navigation(navigationContext: NavigationContext): ReturnType<typeof _computeNavigationResult>;
54
- /**
55
- * @param {{driver: Driver, page: LH.Puppeteer.Page, resolvedConfig: LH.Config.ResolvedConfig, requestor: LH.NavigationRequestor; baseArtifacts: LH.BaseArtifacts, computedCache: NavigationContext['computedCache']}} args
56
- * @return {Promise<{artifacts: Partial<LH.Artifacts & LH.BaseArtifacts>}>}
57
- */
58
- export function _navigations(args: {
59
- driver: Driver;
60
- page: LH.Puppeteer.Page;
61
- resolvedConfig: LH.Config.ResolvedConfig;
62
- requestor: LH.NavigationRequestor;
63
- baseArtifacts: LH.BaseArtifacts;
64
- computedCache: NavigationContext['computedCache'];
65
- }): Promise<{
66
- artifacts: Partial<LH.Artifacts & LH.BaseArtifacts>;
67
- }>;
68
45
  /**
69
46
  * @param {{requestedUrl?: string, driver: Driver, resolvedConfig: LH.Config.ResolvedConfig, lhBrowser?: LH.Puppeteer.Browser, lhPage?: LH.Puppeteer.Page}} args
70
47
  */
@@ -81,14 +58,9 @@ import { LighthouseError } from '../lib/lh-error.js';
81
58
  /**
82
59
  * @param {NavigationContext} navigationContext
83
60
  * @param {PhaseState} phaseState
84
- * @param {Awaited<ReturnType<typeof _setupNavigation>>} setupResult
85
61
  * @param {Awaited<ReturnType<typeof _navigate>>} navigateResult
86
- * @return {Promise<{artifacts: Partial<LH.GathererArtifacts>, warnings: Array<LH.IcuMessage>, pageLoadError: LH.LighthouseError | undefined}>}
62
+ * @return {Promise<Partial<LH.GathererArtifacts>>}
87
63
  */
88
- declare function _computeNavigationResult(navigationContext: NavigationContext, phaseState: PhaseState, setupResult: Awaited<ReturnType<typeof _setupNavigation>>, navigateResult: Awaited<ReturnType<typeof _navigate>>): Promise<{
89
- artifacts: Partial<LH.GathererArtifacts>;
90
- warnings: Array<LH.IcuMessage>;
91
- pageLoadError: LH.LighthouseError | undefined;
92
- }>;
64
+ declare function _computeNavigationResult(navigationContext: NavigationContext, phaseState: PhaseState, navigateResult: Awaited<ReturnType<typeof _navigate>>): Promise<Partial<LH.GathererArtifacts>>;
93
65
  export {};
94
66
  //# sourceMappingURL=navigation-runner.d.ts.map
@@ -14,7 +14,6 @@ import * as prepare from './driver/prepare.js';
14
14
  import {gotoURL} from './driver/navigation.js';
15
15
  import * as storage from './driver/storage.js';
16
16
  import * as emulation from '../lib/emulation.js';
17
- import {defaultNavigationConfig} from '../config/constants.js';
18
17
  import {initializeConfig} from '../config/config.js';
19
18
  import {getBaseArtifacts, finalizeArtifacts} from './base-artifacts.js';
20
19
  import * as format from '../../shared/localization/format.js';
@@ -30,7 +29,6 @@ import {NetworkRecords} from '../computed/network-records.js';
30
29
  * @property {Driver} driver
31
30
  * @property {LH.Puppeteer.Page} page
32
31
  * @property {LH.Config.ResolvedConfig} resolvedConfig
33
- * @property {LH.Config.NavigationDefn} navigation
34
32
  * @property {LH.NavigationRequestor} requestor
35
33
  * @property {LH.BaseArtifacts} baseArtifacts
36
34
  * @property {Map<string, LH.ArbitraryEqualityMap>} computedCache
@@ -50,36 +48,23 @@ async function _setup({driver, resolvedConfig, requestor}) {
50
48
 
51
49
  // We can't trigger the navigation through user interaction if we reset the page before starting.
52
50
  if (typeof requestor === 'string' && !resolvedConfig.settings.skipAboutBlank) {
53
- await gotoURL(driver, defaultNavigationConfig.blankPage, {waitUntil: ['navigated']});
54
- }
51
+ // Disable network monitor on the blank page to prevent it from picking up network requests and
52
+ // frame navigated events before the run starts.
53
+ await driver._networkMonitor?.disable();
55
54
 
56
- const baseArtifacts = await getBaseArtifacts(resolvedConfig, driver, {gatherMode: 'navigation'});
55
+ await gotoURL(driver, resolvedConfig.settings.blankPage, {waitUntil: ['navigated']});
57
56
 
58
- await prepare.prepareTargetForNavigationMode(driver, resolvedConfig.settings);
57
+ await driver._networkMonitor?.enable();
58
+ }
59
59
 
60
- return {baseArtifacts};
61
- }
60
+ const baseArtifacts = await getBaseArtifacts(resolvedConfig, driver, {gatherMode: 'navigation'});
62
61
 
63
- /**
64
- * @param {NavigationContext} navigationContext
65
- * @return {Promise<{warnings: Array<LH.IcuMessage>}>}
66
- */
67
- async function _setupNavigation({requestor, driver, navigation, resolvedConfig}) {
68
- // We can't trigger the navigation through user interaction if we reset the page before starting.
69
- if (typeof requestor === 'string' && !resolvedConfig.settings.skipAboutBlank) {
70
- await gotoURL(driver, navigation.blankPage, {...navigation, waitUntil: ['navigated']});
71
- }
62
+ const {warnings} =
63
+ await prepare.prepareTargetForNavigationMode(driver, resolvedConfig.settings, requestor);
72
64
 
73
- const {warnings} = await prepare.prepareTargetForIndividualNavigation(
74
- driver.defaultSession,
75
- resolvedConfig.settings,
76
- {
77
- ...navigation,
78
- requestor,
79
- }
80
- );
65
+ baseArtifacts.LighthouseRunWarnings.push(...warnings);
81
66
 
82
- return {warnings};
67
+ return {baseArtifacts};
83
68
  }
84
69
 
85
70
  /**
@@ -91,20 +76,20 @@ async function _cleanupNavigation({driver}) {
91
76
 
92
77
  /**
93
78
  * @param {NavigationContext} navigationContext
94
- * @return {Promise<{requestedUrl: string, mainDocumentUrl: string, navigationError: LH.LighthouseError | undefined, warnings: Array<LH.IcuMessage>}>}
79
+ * @return {Promise<{requestedUrl: string, mainDocumentUrl: string, navigationError: LH.LighthouseError | undefined}>}
95
80
  */
96
81
  async function _navigate(navigationContext) {
97
82
  const {driver, resolvedConfig, requestor} = navigationContext;
98
83
 
99
84
  try {
100
85
  const {requestedUrl, mainDocumentUrl, warnings} = await gotoURL(driver, requestor, {
101
- ...navigationContext.navigation,
102
- debugNavigation: resolvedConfig.settings.debugNavigation,
103
- maxWaitForFcp: resolvedConfig.settings.maxWaitForFcp,
104
- maxWaitForLoad: resolvedConfig.settings.maxWaitForLoad,
105
- waitUntil: navigationContext.navigation.pauseAfterFcpMs ? ['fcp', 'load'] : ['load'],
86
+ ...resolvedConfig.settings,
87
+ waitUntil: resolvedConfig.settings.pauseAfterFcpMs ? ['fcp', 'load'] : ['load'],
106
88
  });
107
- return {requestedUrl, mainDocumentUrl, navigationError: undefined, warnings};
89
+
90
+ navigationContext.baseArtifacts.LighthouseRunWarnings.push(...warnings);
91
+
92
+ return {requestedUrl, mainDocumentUrl, navigationError: undefined};
108
93
  } catch (err) {
109
94
  if (!(err instanceof LighthouseError)) throw err;
110
95
  if (err.code !== 'NO_FCP' && err.code !== 'PAGE_HUNG') throw err;
@@ -115,7 +100,6 @@ async function _navigate(navigationContext) {
115
100
  requestedUrl: requestor,
116
101
  mainDocumentUrl: requestor,
117
102
  navigationError: err,
118
- warnings: [],
119
103
  };
120
104
  }
121
105
  }
@@ -158,36 +142,33 @@ async function _collectDebugData(navigationContext, phaseState) {
158
142
  /**
159
143
  * @param {NavigationContext} navigationContext
160
144
  * @param {PhaseState} phaseState
161
- * @param {Awaited<ReturnType<typeof _setupNavigation>>} setupResult
162
145
  * @param {Awaited<ReturnType<typeof _navigate>>} navigateResult
163
- * @return {Promise<{artifacts: Partial<LH.GathererArtifacts>, warnings: Array<LH.IcuMessage>, pageLoadError: LH.LighthouseError | undefined}>}
146
+ * @return {Promise<Partial<LH.GathererArtifacts>>}
164
147
  */
165
148
  async function _computeNavigationResult(
166
149
  navigationContext,
167
150
  phaseState,
168
- setupResult,
169
151
  navigateResult
170
152
  ) {
171
- const {navigationError, mainDocumentUrl} = navigateResult;
172
- const warnings = [...setupResult.warnings, ...navigateResult.warnings];
153
+ const {navigationError, requestedUrl, mainDocumentUrl} = navigateResult;
173
154
  const debugData = await _collectDebugData(navigationContext, phaseState);
174
155
  const pageLoadError = debugData.records
175
156
  ? getPageLoadError(navigationError, {
176
157
  url: mainDocumentUrl,
177
- loadFailureMode: navigationContext.navigation.loadFailureMode,
158
+ ignoreStatusCode: navigationContext.resolvedConfig.settings.ignoreStatusCode,
178
159
  networkRecords: debugData.records,
179
- warnings,
160
+ warnings: navigationContext.baseArtifacts.LighthouseRunWarnings,
180
161
  })
181
162
  : navigationError;
182
163
 
183
164
  if (pageLoadError) {
184
165
  const locale = navigationContext.resolvedConfig.settings.locale;
185
166
  const localizedMessage = format.getFormatted(pageLoadError.friendlyMessage, locale);
186
- log.error('NavigationRunner', localizedMessage, navigateResult.requestedUrl);
167
+ log.error('NavigationRunner', localizedMessage, requestedUrl);
187
168
 
188
169
  /** @type {Partial<LH.GathererArtifacts>} */
189
170
  const artifacts = {};
190
- const pageLoadErrorId = `pageLoadError-${navigationContext.navigation.id}`;
171
+ const pageLoadErrorId = 'pageLoadError-defaultPass';
191
172
  if (debugData.devtoolsLog) {
192
173
  artifacts.DevtoolsLogError = debugData.devtoolsLog;
193
174
  artifacts.devtoolsLogs = {[pageLoadErrorId]: debugData.devtoolsLog};
@@ -197,20 +178,14 @@ async function _computeNavigationResult(
197
178
  artifacts.traces = {[pageLoadErrorId]: debugData.trace};
198
179
  }
199
180
 
200
- return {
201
- pageLoadError,
202
- artifacts,
203
- warnings: [...warnings, pageLoadError.friendlyMessage],
204
- };
181
+ navigationContext.baseArtifacts.LighthouseRunWarnings.push(pageLoadError.friendlyMessage);
182
+ navigationContext.baseArtifacts.PageLoadError = pageLoadError;
183
+
184
+ return artifacts;
205
185
  } else {
206
186
  await collectPhaseArtifacts({phase: 'getArtifact', ...phaseState});
207
187
 
208
- const artifacts = await awaitArtifacts(phaseState.artifactState);
209
- return {
210
- artifacts,
211
- warnings,
212
- pageLoadError: undefined,
213
- };
188
+ return await awaitArtifacts(phaseState.artifactState);
214
189
  }
215
190
  }
216
191
 
@@ -219,6 +194,10 @@ async function _computeNavigationResult(
219
194
  * @return {ReturnType<typeof _computeNavigationResult>}
220
195
  */
221
196
  async function _navigation(navigationContext) {
197
+ if (!navigationContext.resolvedConfig.artifacts) {
198
+ throw new Error('No artifacts were defined on the config');
199
+ }
200
+
222
201
  const artifactState = getEmptyArtifactState();
223
202
  const phaseState = {
224
203
  url: await navigationContext.driver.url(),
@@ -226,14 +205,12 @@ async function _navigation(navigationContext) {
226
205
  driver: navigationContext.driver,
227
206
  page: navigationContext.page,
228
207
  computedCache: navigationContext.computedCache,
229
- artifactDefinitions: navigationContext.navigation.artifacts,
208
+ artifactDefinitions: navigationContext.resolvedConfig.artifacts,
230
209
  artifactState,
231
210
  baseArtifacts: navigationContext.baseArtifacts,
232
211
  settings: navigationContext.resolvedConfig.settings,
233
212
  };
234
213
 
235
- const setupResult = await _setupNavigation(navigationContext);
236
-
237
214
  const disableAsyncStacks =
238
215
  await prepare.enableAsyncStacks(navigationContext.driver.defaultSession);
239
216
 
@@ -262,58 +239,7 @@ async function _navigation(navigationContext) {
262
239
 
263
240
  await _cleanupNavigation(navigationContext);
264
241
 
265
- return _computeNavigationResult(navigationContext, phaseState, setupResult, navigateResult);
266
- }
267
-
268
- /**
269
- * @param {{driver: Driver, page: LH.Puppeteer.Page, resolvedConfig: LH.Config.ResolvedConfig, requestor: LH.NavigationRequestor; baseArtifacts: LH.BaseArtifacts, computedCache: NavigationContext['computedCache']}} args
270
- * @return {Promise<{artifacts: Partial<LH.Artifacts & LH.BaseArtifacts>}>}
271
- */
272
- async function _navigations(args) {
273
- const {
274
- driver,
275
- page,
276
- resolvedConfig,
277
- requestor,
278
- baseArtifacts,
279
- computedCache,
280
- } = args;
281
-
282
- if (!resolvedConfig.artifacts || !resolvedConfig.navigations) {
283
- throw new Error('No artifacts were defined on the config');
284
- }
285
-
286
- /** @type {Partial<LH.Artifacts & LH.BaseArtifacts>} */
287
- const artifacts = {};
288
- /** @type {Array<LH.IcuMessage>} */
289
- const LighthouseRunWarnings = [];
290
-
291
- for (const navigation of resolvedConfig.navigations) {
292
- const navigationContext = {
293
- driver,
294
- page,
295
- navigation,
296
- requestor,
297
- resolvedConfig,
298
- baseArtifacts,
299
- computedCache,
300
- };
301
-
302
- let shouldHaltNavigations = false;
303
- const navigationResult = await _navigation(navigationContext);
304
- if (navigation.loadFailureMode === 'fatal') {
305
- if (navigationResult.pageLoadError) {
306
- artifacts.PageLoadError = navigationResult.pageLoadError;
307
- shouldHaltNavigations = true;
308
- }
309
- }
310
-
311
- LighthouseRunWarnings.push(...navigationResult.warnings);
312
- Object.assign(artifacts, navigationResult.artifacts);
313
- if (shouldHaltNavigations) break;
314
- }
315
-
316
- return {artifacts: {...artifacts, LighthouseRunWarnings}};
242
+ return _computeNavigationResult(navigationContext, phaseState, navigateResult);
317
243
  }
318
244
 
319
245
  /**
@@ -321,7 +247,12 @@ async function _navigations(args) {
321
247
  */
322
248
  async function _cleanup({requestedUrl, driver, resolvedConfig, lhBrowser, lhPage}) {
323
249
  const didResetStorage = !resolvedConfig.settings.disableStorageReset && requestedUrl;
324
- if (didResetStorage) await storage.clearDataForOrigin(driver.defaultSession, requestedUrl);
250
+ if (didResetStorage) {
251
+ await storage.clearDataForOrigin(driver.defaultSession,
252
+ requestedUrl,
253
+ resolvedConfig.settings.clearStorageTypes
254
+ );
255
+ }
325
256
 
326
257
  await driver.disconnect();
327
258
 
@@ -369,11 +300,15 @@ async function navigationGather(page, requestor, options = {}) {
369
300
  driver,
370
301
  lhBrowser,
371
302
  lhPage,
303
+ page,
372
304
  resolvedConfig,
373
305
  requestor: normalizedRequestor,
306
+ computedCache: new Map(),
374
307
  };
375
308
  const {baseArtifacts} = await _setup(context);
376
- const {artifacts} = await _navigations({...context, page, baseArtifacts, computedCache});
309
+
310
+ const artifacts = await _navigation({...context, baseArtifacts});
311
+
377
312
  await _cleanup(context);
378
313
 
379
314
  return finalizeArtifacts(baseArtifacts, artifacts);
@@ -386,9 +321,7 @@ async function navigationGather(page, requestor, options = {}) {
386
321
  export {
387
322
  navigationGather,
388
323
  _setup,
389
- _setupNavigation,
390
324
  _navigate,
391
325
  _navigation,
392
- _navigations,
393
326
  _cleanup,
394
327
  };
@@ -1,9 +1,13 @@
1
1
  /**
2
2
  * Returns an error if the original network request failed or wasn't found.
3
3
  * @param {LH.Artifacts.NetworkRequest|undefined} mainRecord
4
+ * @param {{warnings: Array<string | LH.IcuMessage>, ignoreStatusCode?: LH.Config.Settings['ignoreStatusCode']}} context
4
5
  * @return {LH.LighthouseError|undefined}
5
6
  */
6
- export function getNetworkError(mainRecord: LH.Artifacts.NetworkRequest | undefined): LH.LighthouseError | undefined;
7
+ export function getNetworkError(mainRecord: LH.Artifacts.NetworkRequest | undefined, context: {
8
+ warnings: Array<string | LH.IcuMessage>;
9
+ ignoreStatusCode?: LH.Config.Settings['ignoreStatusCode'];
10
+ }): LH.LighthouseError | undefined;
7
11
  /**
8
12
  * Returns an error if we ended up on the `chrome-error` page and all other requests failed.
9
13
  * @param {LH.Artifacts.NetworkRequest|undefined} mainRecord
@@ -15,12 +19,12 @@ export function getInterstitialError(mainRecord: LH.Artifacts.NetworkRequest | u
15
19
  * Returns an error if the page load should be considered failed, e.g. from a
16
20
  * main document request failure, a security issue, etc.
17
21
  * @param {LH.LighthouseError|undefined} navigationError
18
- * @param {{url: string, loadFailureMode: LH.Config.SharedPassNavigationJson['loadFailureMode'], networkRecords: Array<LH.Artifacts.NetworkRequest>, warnings: Array<string | LH.IcuMessage>}} context
22
+ * @param {{url: string, ignoreStatusCode?: LH.Config.Settings['ignoreStatusCode'], networkRecords: Array<LH.Artifacts.NetworkRequest>, warnings: Array<string | LH.IcuMessage>}} context
19
23
  * @return {LH.LighthouseError|undefined}
20
24
  */
21
25
  export function getPageLoadError(navigationError: LH.LighthouseError | undefined, context: {
22
26
  url: string;
23
- loadFailureMode: LH.Config.SharedPassNavigationJson['loadFailureMode'];
27
+ ignoreStatusCode?: boolean | undefined;
24
28
  networkRecords: Array<LH.Artifacts.NetworkRequest>;
25
29
  warnings: Array<string | LH.IcuMessage>;
26
30
  }): LH.LighthouseError | undefined;
@@ -33,6 +37,7 @@ export function getPageLoadError(navigationError: LH.LighthouseError | undefined
33
37
  export function getNonHtmlError(finalRecord: LH.Artifacts.NetworkRequest | undefined): LH.LighthouseError | undefined;
34
38
  export namespace UIStrings {
35
39
  const warningXhtml: string;
40
+ const warningStatusCode: string;
36
41
  }
37
42
  import { NetworkRequest } from './network-request.js';
38
43
  import { LighthouseError } from './lh-error.js';
@@ -16,6 +16,13 @@ const UIStrings = {
16
16
  */
17
17
  warningXhtml:
18
18
  'The page MIME type is XHTML: Lighthouse does not explicitly support this document type',
19
+ /**
20
+ * @description Warning shown in report when the page under test returns an error code, which Lighthouse is not able to reliably load so we display a warning.
21
+ * @example {404} errorCode
22
+ */
23
+ warningStatusCode: 'Lighthouse was unable to reliably load the page you requested. Make sure' +
24
+ ' you are testing the correct URL and that the server is properly responding' +
25
+ ' to all requests. (Status code: {errorCode})',
19
26
  };
20
27
 
21
28
  const str_ = i18n.createIcuMessageFn(import.meta.url, UIStrings);
@@ -27,9 +34,10 @@ const XHTML_MIME_TYPE = 'application/xhtml+xml';
27
34
  /**
28
35
  * Returns an error if the original network request failed or wasn't found.
29
36
  * @param {LH.Artifacts.NetworkRequest|undefined} mainRecord
37
+ * @param {{warnings: Array<string | LH.IcuMessage>, ignoreStatusCode?: LH.Config.Settings['ignoreStatusCode']}} context
30
38
  * @return {LH.LighthouseError|undefined}
31
39
  */
32
- function getNetworkError(mainRecord) {
40
+ function getNetworkError(mainRecord, context) {
33
41
  if (!mainRecord) {
34
42
  return new LighthouseError(LighthouseError.errors.NO_DOCUMENT_REQUEST);
35
43
  } else if (mainRecord.failed) {
@@ -47,9 +55,13 @@ function getNetworkError(mainRecord) {
47
55
  LighthouseError.errors.FAILED_DOCUMENT_REQUEST, {errorDetails: netErr});
48
56
  }
49
57
  } else if (mainRecord.hasErrorStatusCode()) {
50
- return new LighthouseError(LighthouseError.errors.ERRORED_DOCUMENT_REQUEST, {
51
- statusCode: `${mainRecord.statusCode}`,
52
- });
58
+ if (context.ignoreStatusCode) {
59
+ context.warnings.push(str_(UIStrings.warningStatusCode, {errorCode: mainRecord.statusCode}));
60
+ } else {
61
+ return new LighthouseError(LighthouseError.errors.ERRORED_DOCUMENT_REQUEST, {
62
+ statusCode: `${mainRecord.statusCode}`,
63
+ });
64
+ }
53
65
  }
54
66
  }
55
67
 
@@ -113,11 +125,11 @@ function getNonHtmlError(finalRecord) {
113
125
  * Returns an error if the page load should be considered failed, e.g. from a
114
126
  * main document request failure, a security issue, etc.
115
127
  * @param {LH.LighthouseError|undefined} navigationError
116
- * @param {{url: string, loadFailureMode: LH.Config.SharedPassNavigationJson['loadFailureMode'], networkRecords: Array<LH.Artifacts.NetworkRequest>, warnings: Array<string | LH.IcuMessage>}} context
128
+ * @param {{url: string, ignoreStatusCode?: LH.Config.Settings['ignoreStatusCode'], networkRecords: Array<LH.Artifacts.NetworkRequest>, warnings: Array<string | LH.IcuMessage>}} context
117
129
  * @return {LH.LighthouseError|undefined}
118
130
  */
119
131
  function getPageLoadError(navigationError, context) {
120
- const {url, loadFailureMode, networkRecords} = context;
132
+ const {url, networkRecords} = context;
121
133
  /** @type {LH.Artifacts.NetworkRequest|undefined} */
122
134
  let mainRecord = NetworkAnalyzer.findResourceForUrl(networkRecords, url);
123
135
 
@@ -144,14 +156,10 @@ function getPageLoadError(navigationError, context) {
144
156
  context.warnings.push(str_(UIStrings.warningXhtml));
145
157
  }
146
158
 
147
- const networkError = getNetworkError(mainRecord);
159
+ const networkError = getNetworkError(mainRecord, context);
148
160
  const interstitialError = getInterstitialError(mainRecord, networkRecords);
149
161
  const nonHtmlError = getNonHtmlError(finalRecord);
150
162
 
151
- // Check to see if we need to ignore the page load failure.
152
- // e.g. When the driver is offline, the load will fail without page offline support.
153
- if (loadFailureMode === 'ignore') return;
154
-
155
163
  // We want to special-case the interstitial beyond FAILED_DOCUMENT_REQUEST. See https://github.com/GoogleChrome/lighthouse/pull/8865#issuecomment-497507618
156
164
  if (interstitialError) return interstitialError;
157
165