lighthouse 9.5.0-dev.20230111 → 9.5.0-dev.20230113

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 (63) hide show
  1. package/cli/bin.js +6 -6
  2. package/cli/cli-flags.js +5 -1
  3. package/cli/run.js +1 -1
  4. package/cli/test/smokehouse/core-tests.js +2 -0
  5. package/cli/test/smokehouse/lighthouse-runners/bundle.js +9 -9
  6. package/cli/test/smokehouse/lighthouse-runners/cli.js +7 -7
  7. package/cli/test/smokehouse/lighthouse-runners/devtools.js +3 -3
  8. package/cli/test/smokehouse/readme.md +1 -1
  9. package/cli/test/smokehouse/report-assert.js +11 -7
  10. package/cli/test/smokehouse/smokehouse.js +13 -13
  11. package/core/audits/seo/is-crawlable.js +114 -42
  12. package/core/config/config-helpers.js +11 -11
  13. package/core/config/config-plugin.js +2 -2
  14. package/core/config/config.js +15 -15
  15. package/core/config/constants.js +1 -0
  16. package/core/config/default-config.js +2 -3
  17. package/core/config/desktop-config.js +1 -1
  18. package/core/config/experimental-config.js +1 -1
  19. package/core/config/filters.js +8 -4
  20. package/core/config/full-config.js +1 -1
  21. package/core/config/lr-desktop-config.js +1 -1
  22. package/core/config/lr-mobile-config.js +1 -1
  23. package/core/config/perf-config.js +1 -1
  24. package/core/config/validation.js +4 -4
  25. package/core/gather/gatherers/accessibility.js +15 -1
  26. package/core/gather/gatherers/full-page-screenshot.js +51 -40
  27. package/core/gather/gatherers/seo/tap-targets.js +39 -19
  28. package/core/gather/navigation-runner.js +1 -1
  29. package/core/gather/snapshot-runner.js +1 -1
  30. package/core/gather/timespan-runner.js +1 -1
  31. package/core/index.js +10 -10
  32. package/core/legacy/config/config.js +31 -33
  33. package/core/legacy/config/legacy-default-config.js +1 -1
  34. package/core/legacy/gather/driver.js +0 -17
  35. package/core/legacy/gather/gather-runner.js +0 -6
  36. package/core/runner.js +2 -0
  37. package/core/user-flow.js +3 -3
  38. package/core/util.cjs +43 -3
  39. package/dist/report/bundle.esm.js +53 -22
  40. package/dist/report/flow.js +5 -5
  41. package/dist/report/standalone.js +4 -4
  42. package/flow-report/src/common.tsx +3 -3
  43. package/flow-report/src/util.ts +0 -11
  44. package/flow-report/test/common-test.tsx +4 -7
  45. package/flow-report/test/summary/summary-test.tsx +1 -1
  46. package/package.json +1 -1
  47. package/readme.md +1 -0
  48. package/report/renderer/details-renderer.js +1 -2
  49. package/report/renderer/element-screenshot-renderer.js +4 -4
  50. package/report/renderer/report-renderer.js +3 -7
  51. package/report/renderer/report-ui-features.js +3 -7
  52. package/report/renderer/util.js +43 -3
  53. package/report/test/generator/report-generator-test.js +1 -1
  54. package/report/test/renderer/util-test.js +22 -0
  55. package/report/test-assets/faux-psi.js +3 -3
  56. package/types/artifacts.d.ts +1 -11
  57. package/types/config.d.ts +18 -18
  58. package/types/lhr/audit-details.d.ts +0 -17
  59. package/types/lhr/lhr.d.ts +13 -0
  60. package/types/lhr/settings.d.ts +2 -0
  61. package/types/smokehouse.d.ts +4 -3
  62. package/types/user-flow.d.ts +1 -1
  63. package/core/audits/full-page-screenshot.js +0 -42
@@ -38,19 +38,19 @@ const defaultConfigPath = path.join(
38
38
  );
39
39
 
40
40
  /**
41
- * @param {LH.Config.Json|undefined} configJSON
41
+ * @param {LH.Config|undefined} config
42
42
  * @param {{configPath?: string}} context
43
- * @return {{configWorkingCopy: LH.Config.Json, configDir?: string, configPath?: string}}
43
+ * @return {{configWorkingCopy: LH.Config, configDir?: string, configPath?: string}}
44
44
  */
45
- function resolveWorkingCopy(configJSON, context) {
45
+ function resolveWorkingCopy(config, context) {
46
46
  let {configPath} = context;
47
47
 
48
48
  if (configPath && !path.isAbsolute(configPath)) {
49
49
  throw new Error('configPath must be an absolute path');
50
50
  }
51
51
 
52
- if (!configJSON) {
53
- configJSON = defaultConfig;
52
+ if (!config) {
53
+ config = defaultConfig;
54
54
  configPath = defaultConfigPath;
55
55
  }
56
56
 
@@ -58,24 +58,24 @@ function resolveWorkingCopy(configJSON, context) {
58
58
  const configDir = configPath ? path.dirname(configPath) : undefined;
59
59
 
60
60
  return {
61
- configWorkingCopy: deepCloneConfigJson(configJSON),
61
+ configWorkingCopy: deepCloneConfigJson(config),
62
62
  configPath,
63
63
  configDir,
64
64
  };
65
65
  }
66
66
 
67
67
  /**
68
- * @param {LH.Config.Json} configJSON
69
- * @return {LH.Config.Json}
68
+ * @param {LH.Config} config
69
+ * @return {LH.Config}
70
70
  */
71
- function resolveExtensions(configJSON) {
72
- if (!configJSON.extends) return configJSON;
71
+ function resolveExtensions(config) {
72
+ if (!config.extends) return config;
73
73
 
74
- if (configJSON.extends !== 'lighthouse:default') {
74
+ if (config.extends !== 'lighthouse:default') {
75
75
  throw new Error('`lighthouse:default` is the only valid extension method.');
76
76
  }
77
77
 
78
- const {artifacts, ...extensionJSON} = configJSON;
78
+ const {artifacts, ...extensionJSON} = config;
79
79
  const defaultClone = deepCloneConfigJson(defaultConfig);
80
80
  const mergedConfig = mergeConfigFragment(defaultClone, extensionJSON);
81
81
 
@@ -236,15 +236,15 @@ function resolveFakeNavigations(artifactDefns, settings) {
236
236
 
237
237
  /**
238
238
  * @param {LH.Gatherer.GatherMode} gatherMode
239
- * @param {LH.Config.Json=} configJSON
239
+ * @param {LH.Config=} config
240
240
  * @param {LH.Flags=} flags
241
241
  * @return {Promise<{resolvedConfig: LH.Config.ResolvedConfig, warnings: string[]}>}
242
242
  */
243
- async function initializeConfig(gatherMode, configJSON, flags = {}) {
243
+ async function initializeConfig(gatherMode, config, flags = {}) {
244
244
  const status = {msg: 'Initialize config', id: 'lh:config'};
245
245
  log.time(status, 'verbose');
246
246
 
247
- let {configWorkingCopy, configDir} = resolveWorkingCopy(configJSON, flags);
247
+ let {configWorkingCopy, configDir} = resolveWorkingCopy(config, flags);
248
248
 
249
249
  configWorkingCopy = resolveExtensions(configWorkingCopy);
250
250
  configWorkingCopy = await mergePlugins(configWorkingCopy, configDir, flags);
@@ -111,6 +111,7 @@ const defaultSettings = {
111
111
  debugNavigation: false,
112
112
  channel: 'node',
113
113
  usePassiveGathering: false,
114
+ disableFullPageScreenshot: false,
114
115
  skipAboutBlank: false,
115
116
  blankPage: 'about:blank',
116
117
 
@@ -170,7 +170,7 @@ for (const key of Object.keys(artifacts)) {
170
170
  artifacts[/** @type {keyof typeof artifacts} */ (key)] = key;
171
171
  }
172
172
 
173
- /** @type {LH.Config.Json} */
173
+ /** @type {LH.Config} */
174
174
  const defaultConfig = {
175
175
  settings: constants.defaultSettings,
176
176
  artifacts: [
@@ -278,7 +278,6 @@ const defaultConfig = {
278
278
  'valid-source-maps',
279
279
  'preload-lcp-image',
280
280
  'csp-xss',
281
- 'full-page-screenshot',
282
281
  'script-treemap-data',
283
282
  'manual/pwa-cross-browser',
284
283
  'manual/pwa-page-transitions',
@@ -517,7 +516,6 @@ const defaultConfig = {
517
516
  {id: 'non-composited-animations', weight: 0},
518
517
  {id: 'unsized-images', weight: 0},
519
518
  {id: 'viewport', weight: 0},
520
- {id: 'no-unload-listeners', weight: 0},
521
519
  {id: 'uses-responsive-images-snapshot', weight: 0},
522
520
  {id: 'work-during-interaction', weight: 0},
523
521
  {id: 'bf-cache', weight: 0},
@@ -623,6 +621,7 @@ const defaultConfig = {
623
621
  {id: 'doctype', weight: 1, group: 'best-practices-browser-compat'},
624
622
  {id: 'charset', weight: 1, group: 'best-practices-browser-compat'},
625
623
  // General Group
624
+ {id: 'no-unload-listeners', weight: 1, group: 'best-practices-general'},
626
625
  {id: 'js-libraries', weight: 0, group: 'best-practices-general'},
627
626
  {id: 'deprecations', weight: 1, group: 'best-practices-general'},
628
627
  {id: 'errors-in-console', weight: 1, group: 'best-practices-general'},
@@ -6,7 +6,7 @@
6
6
 
7
7
  import * as constants from './constants.js';
8
8
 
9
- /** @type {LH.Config.Json} */
9
+ /** @type {LH.Config} */
10
10
  const config = {
11
11
  extends: 'lighthouse:default',
12
12
  settings: {
@@ -9,7 +9,7 @@
9
9
  * being enabled by default.
10
10
  */
11
11
 
12
- /** @type {LH.Config.Json} */
12
+ /** @type {LH.Config} */
13
13
  const config = {
14
14
  extends: 'lighthouse:default',
15
15
  audits: [
@@ -27,12 +27,13 @@ const baseArtifactKeys = Object.keys(baseArtifactKeySource);
27
27
 
28
28
  // Some audits are used by the report for additional information.
29
29
  // Keep these audits unless they are *directly* skipped with `skipAudits`.
30
- const filterResistantAuditIds = ['full-page-screenshot'];
30
+ /** @type {string[]} */
31
+ const filterResistantAuditIds = [];
31
32
 
32
33
  // Some artifacts are used by the report for additional information.
33
34
  // Always run these artifacts even if audits do not request them.
34
35
  // These are similar to base artifacts but they cannot be run in all 3 modes.
35
- const filterResistantArtifactIds = ['Stacks', 'NetworkUserAgent'];
36
+ const filterResistantArtifactIds = ['Stacks', 'NetworkUserAgent', 'FullPageScreenshot'];
36
37
 
37
38
  /**
38
39
  * Returns the set of audit IDs used in the list of categories.
@@ -302,7 +303,7 @@ function filterConfigByExplicitFilters(resolvedConfig, filters) {
302
303
  [
303
304
  ...baseAuditIds, // Start with our base audits.
304
305
  ...(onlyAudits || []), // Additionally include the opt-in audits from `onlyAudits`.
305
- ...filterResistantAuditIds, // Always include our filter-resistant audits (full-page-screenshot).
306
+ ...filterResistantAuditIds, // Always include any filter-resistant audits.
306
307
  ].filter(auditId => !skipAudits || !skipAudits.includes(auditId))
307
308
  );
308
309
 
@@ -313,7 +314,10 @@ function filterConfigByExplicitFilters(resolvedConfig, filters) {
313
314
  const availableCategories =
314
315
  filterCategoriesByAvailableAudits(resolvedConfig.categories, audits || []);
315
316
  const categories = filterCategoriesByExplicitFilters(availableCategories, onlyCategories);
316
- const artifacts = filterArtifactsByAvailableAudits(resolvedConfig.artifacts, audits);
317
+ let artifacts = filterArtifactsByAvailableAudits(resolvedConfig.artifacts, audits);
318
+ if (artifacts && resolvedConfig.settings.disableFullPageScreenshot) {
319
+ artifacts = artifacts.filter(({id}) => id !== 'FullPageScreenshot');
320
+ }
317
321
  const navigations =
318
322
  filterNavigationsByAvailableArtifacts(resolvedConfig.navigations, artifacts || []);
319
323
 
@@ -4,7 +4,7 @@
4
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
5
  */
6
6
 
7
- /** @type {LH.Config.Json} */
7
+ /** @type {LH.Config} */
8
8
  const fullConfig = {
9
9
  extends: 'lighthouse:default',
10
10
  settings: {},
@@ -6,7 +6,7 @@
6
6
 
7
7
  import * as constants from './constants.js';
8
8
 
9
- /** @type {LH.Config.Json} */
9
+ /** @type {LH.Config} */
10
10
  const config = {
11
11
  extends: 'lighthouse:default',
12
12
  settings: {
@@ -4,7 +4,7 @@
4
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
5
  */
6
6
 
7
- /** @type {LH.Config.Json} */
7
+ /** @type {LH.Config} */
8
8
  const config = {
9
9
  extends: 'lighthouse:default',
10
10
  settings: {
@@ -4,7 +4,7 @@
4
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
5
  */
6
6
 
7
- /** @type {LH.Config.Json} */
7
+ /** @type {LH.Config} */
8
8
  const perfConfig = {
9
9
  extends: 'lighthouse:default',
10
10
  settings: {
@@ -39,16 +39,16 @@ function isValidArtifactDependency(dependent, dependency) {
39
39
 
40
40
  /**
41
41
  * Throws if pluginName is invalid or (somehow) collides with a category in the
42
- * configJSON being added to.
43
- * @param {LH.Config.Json} configJSON
42
+ * config being added to.
43
+ * @param {LH.Config} config
44
44
  * @param {string} pluginName
45
45
  */
46
- function assertValidPluginName(configJSON, pluginName) {
46
+ function assertValidPluginName(config, pluginName) {
47
47
  if (!pluginName.startsWith('lighthouse-plugin-')) {
48
48
  throw new Error(`plugin name '${pluginName}' does not start with 'lighthouse-plugin-'`);
49
49
  }
50
50
 
51
- if (configJSON.categories?.[pluginName]) {
51
+ if (config.categories?.[pluginName]) {
52
52
  throw new Error(`plugin name '${pluginName}' not allowed because it is the id of a category already found in config`); // eslint-disable-line max-len
53
53
  }
54
54
  }
@@ -81,6 +81,19 @@ async function runA11yChecks() {
81
81
  };
82
82
  }
83
83
 
84
+ async function runA11yChecksAndResetScroll() {
85
+ const originalScrollPosition = {
86
+ x: window.scrollX,
87
+ y: window.scrollY,
88
+ };
89
+
90
+ try {
91
+ return await runA11yChecks();
92
+ } finally {
93
+ window.scrollTo(originalScrollPosition.x, originalScrollPosition.y);
94
+ }
95
+ }
96
+
84
97
  /**
85
98
  * @param {import('axe-core/axe').Result} result
86
99
  * @return {LH.Artifacts.AxeRuleResult}
@@ -168,13 +181,14 @@ class Accessibility extends FRGatherer {
168
181
  getArtifact(passContext) {
169
182
  const driver = passContext.driver;
170
183
 
171
- return driver.executionContext.evaluate(runA11yChecks, {
184
+ return driver.executionContext.evaluate(runA11yChecksAndResetScroll, {
172
185
  args: [],
173
186
  useIsolation: true,
174
187
  deps: [
175
188
  axeSource,
176
189
  pageFunctions.getNodeDetails,
177
190
  createAxeRuleResultArtifact,
191
+ runA11yChecks,
178
192
  ],
179
193
  });
180
194
  }
@@ -47,7 +47,7 @@ function getObservedDeviceMetrics() {
47
47
  }
48
48
 
49
49
  /**
50
- * The screenshot dimensions are sized to `window.outerHeight` / `window.innerWidth`,
50
+ * The screenshot dimensions are sized to `window.outerHeight` / `window.outerWidth`,
51
51
  * however the bounding boxes of the elements are relative to `window.innerHeight` / `window.innerWidth`.
52
52
  */
53
53
  function getScreenshotAreaSize() {
@@ -74,9 +74,8 @@ class FullPageScreenshot extends FRGatherer {
74
74
  /**
75
75
  * @param {LH.Gatherer.FRTransitionalContext} context
76
76
  * @param {{height: number, width: number, mobile: boolean}} deviceMetrics
77
- * @return {Promise<LH.Artifacts.FullPageScreenshot['screenshot']>}
78
77
  */
79
- async _takeScreenshot(context, deviceMetrics) {
78
+ async _resizeViewport(context, deviceMetrics) {
80
79
  const session = context.driver.defaultSession;
81
80
  const metrics = await session.sendCommand('Page.getLayoutMetrics');
82
81
 
@@ -118,8 +117,14 @@ class FullPageScreenshot extends FRGatherer {
118
117
 
119
118
  // Now that new resources are (probably) fetched, wait long enough for a layout.
120
119
  await context.driver.executionContext.evaluate(waitForDoubleRaf, {args: []});
120
+ }
121
121
 
122
- const result = await session.sendCommand('Page.captureScreenshot', {
122
+ /**
123
+ * @param {LH.Gatherer.FRTransitionalContext} context
124
+ * @return {Promise<LH.Result.FullPageScreenshot['screenshot']>}
125
+ */
126
+ async _takeScreenshot(context) {
127
+ const result = await context.driver.defaultSession.sendCommand('Page.captureScreenshot', {
123
128
  format: 'webp',
124
129
  quality: FULL_PAGE_SCREENSHOT_QUALITY,
125
130
  });
@@ -129,8 +134,8 @@ class FullPageScreenshot extends FRGatherer {
129
134
  await context.driver.executionContext.evaluate(getScreenshotAreaSize, {
130
135
  args: [],
131
136
  useIsolation: true,
132
- deps: [kebabCaseToCamelCase],
133
137
  });
138
+
134
139
  return {
135
140
  data,
136
141
  width: screenshotAreaSize.width,
@@ -146,11 +151,11 @@ class FullPageScreenshot extends FRGatherer {
146
151
  * to re-collect the bounding client rectangle.
147
152
  * @see pageFunctions.getNodeDetails
148
153
  * @param {LH.Gatherer.FRTransitionalContext} context
149
- * @return {Promise<LH.Artifacts.FullPageScreenshot['nodes']>}
154
+ * @return {Promise<LH.Result.FullPageScreenshot['nodes']>}
150
155
  */
151
156
  async _resolveNodes(context) {
152
157
  function resolveNodes() {
153
- /** @type {LH.Artifacts.FullPageScreenshot['nodes']} */
158
+ /** @type {LH.Result.FullPageScreenshot['nodes']} */
154
159
  const nodes = {};
155
160
  if (!window.__lighthouseNodesDontTouchOrAllVarianceGoesAway) return nodes;
156
161
 
@@ -197,44 +202,50 @@ class FullPageScreenshot extends FRGatherer {
197
202
  /** @type {{width: number, height: number, deviceScaleFactor: number, mobile: boolean}} */
198
203
  const deviceMetrics = {...settings.screenEmulation};
199
204
 
200
- // In case some other program is controlling emulation, remember what the device looks like now and reset after gatherer is done.
201
- // If we're gathering with mobile screenEmulation on (overlay scrollbars, etc), continue to use that for this screenshot.
202
- if (!lighthouseControlsEmulation) {
203
- const observedDeviceMetrics = await executionContext.evaluate(getObservedDeviceMetrics, {
204
- args: [],
205
- useIsolation: true,
206
- deps: [kebabCaseToCamelCase],
207
- });
208
- deviceMetrics.height = observedDeviceMetrics.height;
209
- deviceMetrics.width = observedDeviceMetrics.width;
210
- deviceMetrics.deviceScaleFactor = observedDeviceMetrics.deviceScaleFactor;
211
- // If screen emulation is disabled, use formFactor to determine if we are on mobile.
212
- deviceMetrics.mobile = settings.formFactor === 'mobile';
213
- }
214
-
215
205
  try {
206
+ if (!settings.usePassiveGathering) {
207
+ // In case some other program is controlling emulation, remember what the device looks like now and reset after gatherer is done.
208
+ // If we're gathering with mobile screenEmulation on (overlay scrollbars, etc), continue to use that for this screenshot.
209
+ if (!lighthouseControlsEmulation) {
210
+ const observedDeviceMetrics = await executionContext.evaluate(getObservedDeviceMetrics, {
211
+ args: [],
212
+ useIsolation: true,
213
+ deps: [kebabCaseToCamelCase],
214
+ });
215
+ deviceMetrics.height = observedDeviceMetrics.height;
216
+ deviceMetrics.width = observedDeviceMetrics.width;
217
+ deviceMetrics.deviceScaleFactor = observedDeviceMetrics.deviceScaleFactor;
218
+ // If screen emulation is disabled, use formFactor to determine if we are on mobile.
219
+ deviceMetrics.mobile = settings.formFactor === 'mobile';
220
+ }
221
+
222
+ await this._resizeViewport(context, deviceMetrics);
223
+ }
224
+
216
225
  return {
217
- screenshot: await this._takeScreenshot(context, deviceMetrics),
226
+ screenshot: await this._takeScreenshot(context),
218
227
  nodes: await this._resolveNodes(context),
219
228
  };
220
229
  } finally {
221
- // Revert resized page.
222
- if (lighthouseControlsEmulation) {
223
- await emulation.emulate(session, settings);
224
- } else {
225
- // Best effort to reset emulation to what it was.
226
- // https://github.com/GoogleChrome/lighthouse/pull/10716#discussion_r428970681
227
- // TODO: seems like this would be brittle. Should at least work for devtools, but what
228
- // about scripted puppeteer usages? Better to introduce a "setEmulation" callback
229
- // in the LH runner api, which for ex. puppeteer consumers would setup puppeteer emulation,
230
- // and then just call that to reset?
231
- // https://github.com/GoogleChrome/lighthouse/issues/11122
232
- await session.sendCommand('Emulation.setDeviceMetricsOverride', {
233
- mobile: deviceMetrics.mobile,
234
- deviceScaleFactor: deviceMetrics.deviceScaleFactor,
235
- height: deviceMetrics.height,
236
- width: 0, // Leave width unchanged
237
- });
230
+ if (!settings.usePassiveGathering) {
231
+ // Revert resized page.
232
+ if (lighthouseControlsEmulation) {
233
+ await emulation.emulate(session, settings);
234
+ } else {
235
+ // Best effort to reset emulation to what it was.
236
+ // https://github.com/GoogleChrome/lighthouse/pull/10716#discussion_r428970681
237
+ // TODO: seems like this would be brittle. Should at least work for devtools, but what
238
+ // about scripted puppeteer usages? Better to introduce a "setEmulation" callback
239
+ // in the LH runner api, which for ex. puppeteer consumers would setup puppeteer emulation,
240
+ // and then just call that to reset?
241
+ // https://github.com/GoogleChrome/lighthouse/issues/11122
242
+ await session.sendCommand('Emulation.setDeviceMetricsOverride', {
243
+ mobile: deviceMetrics.mobile,
244
+ deviceScaleFactor: deviceMetrics.deviceScaleFactor,
245
+ height: deviceMetrics.height,
246
+ width: 0, // Leave width unchanged
247
+ });
248
+ }
238
249
  }
239
250
  }
240
251
  }
@@ -281,6 +281,24 @@ function gatherTapTargets(tapTargetsSelector, className) {
281
281
 
282
282
  return targets;
283
283
  }
284
+
285
+ /**
286
+ * @param {string} tapTargetsSelector
287
+ * @param {string} className
288
+ * @return {LH.Artifacts.TapTarget[]}
289
+ */
290
+ function gatherTapTargetsAndResetScroll(tapTargetsSelector, className) {
291
+ const originalScrollPosition = {
292
+ x: window.scrollX,
293
+ y: window.scrollY,
294
+ };
295
+
296
+ try {
297
+ return gatherTapTargets(tapTargetsSelector, className);
298
+ } finally {
299
+ window.scrollTo(originalScrollPosition.x, originalScrollPosition.y);
300
+ }
301
+ }
284
302
  /* c8 ignore stop */
285
303
 
286
304
  class TapTargets extends FRGatherer {
@@ -337,25 +355,27 @@ class TapTargets extends FRGatherer {
337
355
  const className = 'lighthouse-disable-pointer-events';
338
356
  const styleSheetId = await this.addStyleRule(session, className);
339
357
 
340
- const tapTargets = await passContext.driver.executionContext.evaluate(gatherTapTargets, {
341
- args: [tapTargetsSelector, className],
342
- useIsolation: true,
343
- deps: [
344
- pageFunctions.getNodeDetails,
345
- pageFunctions.getElementsInDocument,
346
- disableFixedAndStickyElementPointerEvents,
347
- elementIsVisible,
348
- elementHasAncestorTapTarget,
349
- elementCenterIsAtZAxisTop,
350
- getClientRects,
351
- hasTextNodeSiblingsFormingTextBlock,
352
- elementIsInTextBlock,
353
- RectHelpers.getRectCenterPoint,
354
- pageFunctions.getNodePath,
355
- pageFunctions.getNodeSelector,
356
- pageFunctions.getNodeLabel,
357
- ],
358
- });
358
+ const tapTargets =
359
+ await passContext.driver.executionContext.evaluate(gatherTapTargetsAndResetScroll, {
360
+ args: [tapTargetsSelector, className],
361
+ useIsolation: true,
362
+ deps: [
363
+ pageFunctions.getNodeDetails,
364
+ pageFunctions.getElementsInDocument,
365
+ disableFixedAndStickyElementPointerEvents,
366
+ elementIsVisible,
367
+ elementHasAncestorTapTarget,
368
+ elementCenterIsAtZAxisTop,
369
+ getClientRects,
370
+ hasTextNodeSiblingsFormingTextBlock,
371
+ elementIsInTextBlock,
372
+ RectHelpers.getRectCenterPoint,
373
+ pageFunctions.getNodePath,
374
+ pageFunctions.getNodeSelector,
375
+ pageFunctions.getNodeLabel,
376
+ gatherTapTargets,
377
+ ],
378
+ });
359
379
 
360
380
  await this.removeStyleRule(session, styleSheetId);
361
381
 
@@ -311,7 +311,7 @@ async function _cleanup({requestedUrl, driver, resolvedConfig}) {
311
311
  /**
312
312
  * @param {LH.Puppeteer.Page|undefined} page
313
313
  * @param {LH.NavigationRequestor|undefined} requestor
314
- * @param {{config?: LH.Config.Json, flags?: LH.Flags}} [options]
314
+ * @param {{config?: LH.Config, flags?: LH.Flags}} [options]
315
315
  * @return {Promise<LH.Gatherer.FRGatherResult>}
316
316
  */
317
317
  async function navigationGather(page, requestor, options = {}) {
@@ -14,7 +14,7 @@ import {getBaseArtifacts, finalizeArtifacts} from './base-artifacts.js';
14
14
 
15
15
  /**
16
16
  * @param {LH.Puppeteer.Page} page
17
- * @param {{config?: LH.Config.Json, flags?: LH.Flags}} [options]
17
+ * @param {{config?: LH.Config, flags?: LH.Flags}} [options]
18
18
  * @return {Promise<LH.Gatherer.FRGatherResult>}
19
19
  */
20
20
  async function snapshotGather(page, options = {}) {
@@ -15,7 +15,7 @@ import {getBaseArtifacts, finalizeArtifacts} from './base-artifacts.js';
15
15
 
16
16
  /**
17
17
  * @param {LH.Puppeteer.Page} page
18
- * @param {{config?: LH.Config.Json, flags?: LH.Flags}} [options]
18
+ * @param {{config?: LH.Config, flags?: LH.Flags}} [options]
19
19
  * @return {Promise<{endTimespanGather(): Promise<LH.Gatherer.FRGatherResult>}>}
20
20
  */
21
21
  async function startTimespanGather(page, options = {}) {
package/core/index.js CHANGED
@@ -37,13 +37,13 @@ import {navigationGather} from './gather/navigation-runner.js';
37
37
  * @param {string=} url The URL to test. Optional if running in auditMode.
38
38
  * @param {LH.Flags=} flags Optional settings for the Lighthouse run. If present,
39
39
  * they will override any settings in the config.
40
- * @param {LH.Config.Json=} configJSON Configuration for the Lighthouse run. If
40
+ * @param {LH.Config=} config Configuration for the Lighthouse run. If
41
41
  * not present, the default config is used.
42
42
  * @param {LH.Puppeteer.Page=} page
43
43
  * @return {Promise<LH.RunnerResult|undefined>}
44
44
  */
45
- async function lighthouse(url, flags = {}, configJSON, page) {
46
- return navigation(page, url, {config: configJSON, flags});
45
+ async function lighthouse(url, flags = {}, config, page) {
46
+ return navigation(page, url, {config, flags});
47
47
  }
48
48
 
49
49
  /**
@@ -53,17 +53,17 @@ async function lighthouse(url, flags = {}, configJSON, page) {
53
53
  * @param {string=} url The URL to test. Optional if running in auditMode.
54
54
  * @param {LH.Flags=} flags Optional settings for the Lighthouse run. If present,
55
55
  * they will override any settings in the config.
56
- * @param {LH.Config.Json=} configJSON Configuration for the Lighthouse run. If
56
+ * @param {LH.Config=} config Configuration for the Lighthouse run. If
57
57
  * not present, the default config is used.
58
58
  * @param {Connection=} userConnection
59
59
  * @return {Promise<LH.RunnerResult|undefined>}
60
60
  */
61
- async function legacyNavigation(url, flags = {}, configJSON, userConnection) {
61
+ async function legacyNavigation(url, flags = {}, config, userConnection) {
62
62
  // set logging preferences, assume quiet
63
63
  flags.logLevel = flags.logLevel || 'error';
64
64
  log.setLevel(flags.logLevel);
65
65
 
66
- const resolvedConfig = await LegacyResolvedConfig.fromJson(configJSON, flags);
66
+ const resolvedConfig = await LegacyResolvedConfig.fromJson(config, flags);
67
67
  const computedCache = new Map();
68
68
  const options = {resolvedConfig, computedCache};
69
69
  const connection = userConnection || new CriConnection(flags.port, flags.hostname);
@@ -87,7 +87,7 @@ async function startFlow(page, options) {
87
87
  /**
88
88
  * @param {LH.Puppeteer.Page|undefined} page
89
89
  * @param {LH.NavigationRequestor|undefined} requestor
90
- * @param {{config?: LH.Config.Json, flags?: LH.Flags}} [options]
90
+ * @param {{config?: LH.Config, flags?: LH.Flags}} [options]
91
91
  * @return {Promise<LH.RunnerResult|undefined>}
92
92
  */
93
93
  async function navigation(page, requestor, options) {
@@ -97,7 +97,7 @@ async function navigation(page, requestor, options) {
97
97
 
98
98
  /**
99
99
  * @param {LH.Puppeteer.Page} page
100
- * @param {{config?: LH.Config.Json, flags?: LH.Flags}} [options]
100
+ * @param {{config?: LH.Config, flags?: LH.Flags}} [options]
101
101
  * @return {Promise<LH.RunnerResult|undefined>}
102
102
  */
103
103
  async function snapshot(page, options) {
@@ -107,7 +107,7 @@ async function snapshot(page, options) {
107
107
 
108
108
  /**
109
109
  * @param {LH.Puppeteer.Page} page
110
- * @param {{config?: LH.Config.Json, flags?: LH.Flags}} [options]
110
+ * @param {{config?: LH.Config, flags?: LH.Flags}} [options]
111
111
  * @return {Promise<{endTimespan: () => Promise<LH.RunnerResult|undefined>}>}
112
112
  */
113
113
  async function startTimespan(page, options) {
@@ -138,7 +138,7 @@ function generateReport(result, format = 'html') {
138
138
 
139
139
  /**
140
140
  * @param {LH.UserFlow.FlowArtifacts} flowArtifacts
141
- * @param {LH.Config.Json} [config]
141
+ * @param {LH.Config} [config]
142
142
  */
143
143
  async function auditFlowArtifacts(flowArtifacts, config) {
144
144
  const {gatherSteps, name} = flowArtifacts;