lighthouse 9.5.0-dev.20220815 → 9.5.0-dev.20220816
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/commands/list-locales.js +1 -1
- package/core/config/config-helpers.js +0 -15
- package/core/config/config.js +1 -1
- package/core/config/constants.js +1 -0
- package/core/config/default-config.js +0 -2
- package/core/fraggle-rock/api.js +1 -1
- package/core/fraggle-rock/config/config.js +12 -13
- package/core/fraggle-rock/gather/navigation-runner.js +17 -26
- package/core/fraggle-rock/gather/snapshot-runner.js +4 -5
- package/core/fraggle-rock/gather/timespan-runner.js +4 -5
- package/core/fraggle-rock/replay/stringify-extension.js +105 -0
- package/core/fraggle-rock/user-flow.js +10 -12
- package/core/gather/driver/execution-context.js +19 -2
- package/core/gather/gather-runner.js +1 -1
- package/core/index.js +2 -5
- package/core/lib/asset-saver.js +2 -2
- package/core/lib/page-functions.js +15 -3
- package/core/lib/traces/{pwmetrics-events.js → metric-trace-events.js} +3 -4
- package/core/runner.js +2 -2
- package/dist/report/bundle.esm.js +1 -14
- package/dist/report/flow.js +28 -27
- package/dist/report/standalone.js +4 -4
- package/flow-report/test/flow-report-pptr-test.ts +1 -1
- package/package.json +2 -1
- package/report/generator/file-namer.js +5 -1
- package/report/generator/flow-report-assets.js +10 -6
- package/report/generator/report-assets.js +8 -5
- package/report/generator/report-generator.js +7 -7
- package/report/generator/tsconfig.json +1 -0
- package/report/test/generator/file-namer-test.js +2 -2
- package/report/test/generator/report-generator-test.js +8 -9
- package/report/test/renderer/report-renderer-axe-test.js +2 -2
- package/report/test/renderer/report-ui-features-test.js +1 -1
- package/shared/localization/format.js +11 -13
- package/shared/localization/i18n-module.js +7 -2
- package/shared/localization/locales/ar-XB.json +0 -21
- package/shared/localization/locales/ar.json +0 -21
- package/shared/localization/locales/bg.json +0 -21
- package/shared/localization/locales/ca.json +0 -21
- package/shared/localization/locales/cs.json +0 -21
- package/shared/localization/locales/da.json +0 -21
- package/shared/localization/locales/de.json +0 -21
- package/shared/localization/locales/el.json +0 -21
- package/shared/localization/locales/en-GB.json +0 -21
- package/shared/localization/locales/en-US.json +0 -21
- package/shared/localization/locales/en-XA.json +0 -21
- package/shared/localization/locales/en-XL.json +0 -21
- package/shared/localization/locales/es-419.json +0 -21
- package/shared/localization/locales/es.json +0 -21
- package/shared/localization/locales/fi.json +0 -21
- package/shared/localization/locales/fil.json +0 -21
- package/shared/localization/locales/fr.json +0 -21
- package/shared/localization/locales/he.json +0 -21
- package/shared/localization/locales/hi.json +0 -21
- package/shared/localization/locales/hr.json +0 -21
- package/shared/localization/locales/hu.json +0 -21
- package/shared/localization/locales/id.json +0 -21
- package/shared/localization/locales/it.json +0 -21
- package/shared/localization/locales/ja.json +0 -21
- package/shared/localization/locales/ko.json +0 -21
- package/shared/localization/locales/lt.json +0 -21
- package/shared/localization/locales/lv.json +0 -21
- package/shared/localization/locales/nl.json +0 -21
- package/shared/localization/locales/no.json +0 -21
- package/shared/localization/locales/pl.json +0 -21
- package/shared/localization/locales/pt-PT.json +0 -21
- package/shared/localization/locales/pt.json +0 -21
- package/shared/localization/locales/ro.json +0 -21
- package/shared/localization/locales/ru.json +0 -21
- package/shared/localization/locales/sk.json +0 -21
- package/shared/localization/locales/sl.json +0 -21
- package/shared/localization/locales/sr-Latn.json +0 -21
- package/shared/localization/locales/sr.json +0 -21
- package/shared/localization/locales/sv.json +0 -21
- package/shared/localization/locales/ta.json +0 -21
- package/shared/localization/locales/te.json +0 -21
- package/shared/localization/locales/th.json +0 -21
- package/shared/localization/locales/tr.json +0 -21
- package/shared/localization/locales/uk.json +0 -21
- package/shared/localization/locales/vi.json +0 -21
- package/shared/localization/locales/zh-HK.json +0 -21
- package/shared/localization/locales/zh-TW.json +0 -21
- package/shared/localization/locales/zh.json +0 -21
- package/shared/localization/locales.js +53 -50
- package/shared/localization/swap-flow-locale.js +6 -2
- package/shared/localization/swap-locale.js +4 -4
- package/shared/test/localization/format-test.js +17 -20
- package/shared/test/localization/locales-test.js +2 -2
- package/shared/test/localization/swap-locale-test.js +3 -2
- package/shared/tsconfig.json +1 -0
- package/shared/type-verifiers.js +1 -1
- package/tsconfig.json +1 -5
- package/types/config.d.ts +0 -13
- package/types/externs.d.ts +1 -1
- package/types/lhr/settings.d.ts +2 -0
- package/types/user-flow.d.ts +1 -1
- package/core/audits/dobetterweb/no-vulnerable-libraries.js +0 -229
- package/report/generator/package.json +0 -4
- package/report/test/generator/package.json +0 -4
- package/shared/package.json +0 -4
- package/third-party/snyk/snapshot.json +0 -182
|
@@ -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
|
-
import locales from '../../shared/localization/locales.js';
|
|
7
|
+
import {locales} from '../../shared/localization/locales.js';
|
|
8
8
|
|
|
9
9
|
function listLocales() {
|
|
10
10
|
const localesList = Object.keys(locales);
|
|
@@ -606,20 +606,6 @@ function deepCloneConfigJson(json) {
|
|
|
606
606
|
return cloned;
|
|
607
607
|
}
|
|
608
608
|
|
|
609
|
-
/**
|
|
610
|
-
* @param {LH.Flags} flags
|
|
611
|
-
* @return {LH.Config.FRContext}
|
|
612
|
-
*/
|
|
613
|
-
function flagsToFRContext(flags) {
|
|
614
|
-
return {
|
|
615
|
-
configPath: flags?.configPath,
|
|
616
|
-
settingsOverrides: flags,
|
|
617
|
-
logLevel: flags?.logLevel,
|
|
618
|
-
hostname: flags?.hostname,
|
|
619
|
-
port: flags?.port,
|
|
620
|
-
};
|
|
621
|
-
}
|
|
622
|
-
|
|
623
609
|
export {
|
|
624
610
|
deepClone,
|
|
625
611
|
deepCloneConfigJson,
|
|
@@ -631,5 +617,4 @@ export {
|
|
|
631
617
|
resolveGathererToDefn,
|
|
632
618
|
resolveModulePath,
|
|
633
619
|
resolveSettings,
|
|
634
|
-
flagsToFRContext,
|
|
635
620
|
};
|
package/core/config/config.js
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
import legacyDefaultConfig from './legacy-default-config.js';
|
|
9
9
|
import * as constants from './constants.js';
|
|
10
|
-
import format from '../../shared/localization/format.js';
|
|
10
|
+
import * as format from '../../shared/localization/format.js';
|
|
11
11
|
import * as validation from './../fraggle-rock/config/validation.js';
|
|
12
12
|
import log from 'lighthouse-logger';
|
|
13
13
|
import path from 'path';
|
package/core/config/constants.js
CHANGED
|
@@ -413,7 +413,6 @@ const defaultConfig = {
|
|
|
413
413
|
'dobetterweb/geolocation-on-start',
|
|
414
414
|
'dobetterweb/inspector-issues',
|
|
415
415
|
'dobetterweb/no-document-write',
|
|
416
|
-
'dobetterweb/no-vulnerable-libraries',
|
|
417
416
|
'dobetterweb/js-libraries',
|
|
418
417
|
'dobetterweb/notification-on-start',
|
|
419
418
|
'dobetterweb/password-inputs-can-be-pasted-into',
|
|
@@ -666,7 +665,6 @@ const defaultConfig = {
|
|
|
666
665
|
{id: 'is-on-https', weight: 1, group: 'best-practices-trust-safety'},
|
|
667
666
|
{id: 'geolocation-on-start', weight: 1, group: 'best-practices-trust-safety'},
|
|
668
667
|
{id: 'notification-on-start', weight: 1, group: 'best-practices-trust-safety'},
|
|
669
|
-
{id: 'no-vulnerable-libraries', weight: 1, group: 'best-practices-trust-safety'},
|
|
670
668
|
{id: 'csp-xss', weight: 0, group: 'best-practices-trust-safety'},
|
|
671
669
|
// User Experience
|
|
672
670
|
{id: 'password-inputs-can-be-pasted-into', weight: 1, group: 'best-practices-ux'},
|
package/core/fraggle-rock/api.js
CHANGED
|
@@ -9,7 +9,7 @@ import {UserFlow, auditGatherSteps} from './user-flow.js';
|
|
|
9
9
|
import {snapshotGather} from './gather/snapshot-runner.js';
|
|
10
10
|
import {startTimespanGather} from './gather/timespan-runner.js';
|
|
11
11
|
import {navigationGather} from './gather/navigation-runner.js';
|
|
12
|
-
import ReportGenerator from '../../report/generator/report-generator.js';
|
|
12
|
+
import {ReportGenerator} from '../../report/generator/report-generator.js';
|
|
13
13
|
import {Runner} from '../runner.js';
|
|
14
14
|
|
|
15
15
|
/**
|
|
@@ -40,8 +40,6 @@ const defaultConfigPath = path.join(
|
|
|
40
40
|
'../../config/default-config.js'
|
|
41
41
|
);
|
|
42
42
|
|
|
43
|
-
/** @typedef {LH.Config.FRContext & {gatherMode: LH.Gatherer.GatherMode}} ConfigContext */
|
|
44
|
-
|
|
45
43
|
/**
|
|
46
44
|
* @param {LH.Config.Json|undefined} configJSON
|
|
47
45
|
* @param {{configPath?: string}} context
|
|
@@ -176,10 +174,10 @@ async function resolveArtifactsToDefns(artifacts, configDir) {
|
|
|
176
174
|
* Overrides the settings that may not apply to the chosen gather mode.
|
|
177
175
|
*
|
|
178
176
|
* @param {LH.Config.Settings} settings
|
|
179
|
-
* @param {
|
|
177
|
+
* @param {LH.Gatherer.GatherMode} gatherMode
|
|
180
178
|
*/
|
|
181
|
-
function overrideSettingsForGatherMode(settings,
|
|
182
|
-
if (
|
|
179
|
+
function overrideSettingsForGatherMode(settings, gatherMode) {
|
|
180
|
+
if (gatherMode === 'timespan') {
|
|
183
181
|
if (settings.throttlingMethod === 'simulate') {
|
|
184
182
|
settings.throttlingMethod = 'devtools';
|
|
185
183
|
}
|
|
@@ -251,21 +249,22 @@ function resolveNavigationsToDefns(navigations, artifactDefns, settings) {
|
|
|
251
249
|
}
|
|
252
250
|
|
|
253
251
|
/**
|
|
254
|
-
* @param {LH.
|
|
255
|
-
* @param {
|
|
252
|
+
* @param {LH.Gatherer.GatherMode} gatherMode
|
|
253
|
+
* @param {LH.Config.Json=} configJSON
|
|
254
|
+
* @param {LH.Flags=} flags
|
|
256
255
|
* @return {Promise<{config: LH.Config.FRConfig, warnings: string[]}>}
|
|
257
256
|
*/
|
|
258
|
-
async function initializeConfig(configJSON,
|
|
257
|
+
async function initializeConfig(gatherMode, configJSON, flags = {}) {
|
|
259
258
|
const status = {msg: 'Initialize config', id: 'lh:config'};
|
|
260
259
|
log.time(status, 'verbose');
|
|
261
260
|
|
|
262
|
-
let {configWorkingCopy, configDir} = resolveWorkingCopy(configJSON,
|
|
261
|
+
let {configWorkingCopy, configDir} = resolveWorkingCopy(configJSON, flags);
|
|
263
262
|
|
|
264
263
|
configWorkingCopy = resolveExtensions(configWorkingCopy);
|
|
265
|
-
configWorkingCopy = await mergePlugins(configWorkingCopy, configDir,
|
|
264
|
+
configWorkingCopy = await mergePlugins(configWorkingCopy, configDir, flags);
|
|
266
265
|
|
|
267
|
-
const settings = resolveSettings(configWorkingCopy.settings || {},
|
|
268
|
-
overrideSettingsForGatherMode(settings,
|
|
266
|
+
const settings = resolveSettings(configWorkingCopy.settings || {}, flags);
|
|
267
|
+
overrideSettingsForGatherMode(settings, gatherMode);
|
|
269
268
|
|
|
270
269
|
const artifacts = await resolveArtifactsToDefns(configWorkingCopy.artifacts, configDir);
|
|
271
270
|
const navigations = resolveNavigationsToDefns(configWorkingCopy.navigations, artifacts, settings);
|
|
@@ -282,7 +281,7 @@ async function initializeConfig(configJSON, context) {
|
|
|
282
281
|
|
|
283
282
|
const {warnings} = assertValidConfig(config);
|
|
284
283
|
|
|
285
|
-
config = filterConfigByGatherMode(config,
|
|
284
|
+
config = filterConfigByGatherMode(config, gatherMode);
|
|
286
285
|
config = filterConfigByExplicitFilters(config, settings);
|
|
287
286
|
|
|
288
287
|
log.timeEnd(status);
|
|
@@ -17,7 +17,7 @@ import * as emulation from '../../lib/emulation.js';
|
|
|
17
17
|
import {defaultNavigationConfig} from '../../config/constants.js';
|
|
18
18
|
import {initializeConfig} from '../config/config.js';
|
|
19
19
|
import {getBaseArtifacts, finalizeArtifacts} from './base-artifacts.js';
|
|
20
|
-
import format from '../../../shared/localization/format.js';
|
|
20
|
+
import * as format from '../../../shared/localization/format.js';
|
|
21
21
|
import {LighthouseError} from '../../lib/lh-error.js';
|
|
22
22
|
import URL from '../../lib/url-shim.js';
|
|
23
23
|
import {getPageLoadError} from '../../lib/navigation-error.js';
|
|
@@ -25,8 +25,6 @@ import Trace from '../../gather/gatherers/trace.js';
|
|
|
25
25
|
import DevtoolsLog from '../../gather/gatherers/devtools-log.js';
|
|
26
26
|
import NetworkRecords from '../../computed/network-records.js';
|
|
27
27
|
|
|
28
|
-
/** @typedef {{skipAboutBlank?: boolean}} InternalOptions */
|
|
29
|
-
|
|
30
28
|
/**
|
|
31
29
|
* @typedef NavigationContext
|
|
32
30
|
* @property {Driver} driver
|
|
@@ -35,7 +33,6 @@ import NetworkRecords from '../../computed/network-records.js';
|
|
|
35
33
|
* @property {LH.NavigationRequestor} requestor
|
|
36
34
|
* @property {LH.FRBaseArtifacts} baseArtifacts
|
|
37
35
|
* @property {Map<string, LH.ArbitraryEqualityMap>} computedCache
|
|
38
|
-
* @property {InternalOptions} [options]
|
|
39
36
|
*/
|
|
40
37
|
|
|
41
38
|
/** @typedef {Omit<Parameters<typeof collectPhaseArtifacts>[0], 'phase'>} PhaseState */
|
|
@@ -44,12 +41,14 @@ const DEFAULT_HOSTNAME = '127.0.0.1';
|
|
|
44
41
|
const DEFAULT_PORT = 9222;
|
|
45
42
|
|
|
46
43
|
/**
|
|
47
|
-
* @param {{driver: Driver, config: LH.Config.FRConfig,
|
|
44
|
+
* @param {{driver: Driver, config: LH.Config.FRConfig, requestor: LH.NavigationRequestor}} args
|
|
48
45
|
* @return {Promise<{baseArtifacts: LH.FRBaseArtifacts}>}
|
|
49
46
|
*/
|
|
50
|
-
async function _setup({driver, config,
|
|
47
|
+
async function _setup({driver, config, requestor}) {
|
|
51
48
|
await driver.connect();
|
|
52
|
-
|
|
49
|
+
|
|
50
|
+
// We can't trigger the navigation through user interaction if we reset the page before starting.
|
|
51
|
+
if (typeof requestor === 'string' && !config.settings.skipAboutBlank) {
|
|
53
52
|
await gotoURL(driver, defaultNavigationConfig.blankPage, {waitUntil: ['navigated']});
|
|
54
53
|
}
|
|
55
54
|
|
|
@@ -64,10 +63,12 @@ async function _setup({driver, config, options}) {
|
|
|
64
63
|
* @param {NavigationContext} navigationContext
|
|
65
64
|
* @return {Promise<{warnings: Array<LH.IcuMessage>}>}
|
|
66
65
|
*/
|
|
67
|
-
async function _setupNavigation({requestor, driver, navigation, config
|
|
68
|
-
if
|
|
66
|
+
async function _setupNavigation({requestor, driver, navigation, config}) {
|
|
67
|
+
// We can't trigger the navigation through user interaction if we reset the page before starting.
|
|
68
|
+
if (typeof requestor === 'string' && !config.settings.skipAboutBlank) {
|
|
69
69
|
await gotoURL(driver, navigation.blankPage, {...navigation, waitUntil: ['navigated']});
|
|
70
70
|
}
|
|
71
|
+
|
|
71
72
|
const {warnings} = await prepare.prepareTargetForIndividualNavigation(
|
|
72
73
|
driver.defaultSession,
|
|
73
74
|
config.settings,
|
|
@@ -249,10 +250,10 @@ async function _navigation(navigationContext) {
|
|
|
249
250
|
}
|
|
250
251
|
|
|
251
252
|
/**
|
|
252
|
-
* @param {{driver: Driver, config: LH.Config.FRConfig, requestor: LH.NavigationRequestor; baseArtifacts: LH.FRBaseArtifacts, computedCache: NavigationContext['computedCache']
|
|
253
|
+
* @param {{driver: Driver, config: LH.Config.FRConfig, requestor: LH.NavigationRequestor; baseArtifacts: LH.FRBaseArtifacts, computedCache: NavigationContext['computedCache']}} args
|
|
253
254
|
* @return {Promise<{artifacts: Partial<LH.FRArtifacts & LH.FRBaseArtifacts>}>}
|
|
254
255
|
*/
|
|
255
|
-
async function _navigations({driver, config, requestor, baseArtifacts, computedCache
|
|
256
|
+
async function _navigations({driver, config, requestor, baseArtifacts, computedCache}) {
|
|
256
257
|
if (!config.navigations) throw new Error('No navigations configured');
|
|
257
258
|
|
|
258
259
|
/** @type {Partial<LH.FRArtifacts & LH.FRBaseArtifacts>} */
|
|
@@ -268,7 +269,6 @@ async function _navigations({driver, config, requestor, baseArtifacts, computedC
|
|
|
268
269
|
config,
|
|
269
270
|
baseArtifacts,
|
|
270
271
|
computedCache,
|
|
271
|
-
options,
|
|
272
272
|
};
|
|
273
273
|
|
|
274
274
|
let shouldHaltNavigations = false;
|
|
@@ -300,25 +300,17 @@ async function _cleanup({requestedUrl, driver, config}) {
|
|
|
300
300
|
|
|
301
301
|
/**
|
|
302
302
|
* @param {LH.NavigationRequestor|undefined} requestor
|
|
303
|
-
* @param {{page?: LH.Puppeteer.Page, config?: LH.Config.Json,
|
|
303
|
+
* @param {{page?: LH.Puppeteer.Page, config?: LH.Config.Json, flags?: LH.Flags}} options
|
|
304
304
|
* @return {Promise<LH.Gatherer.FRGatherResult>}
|
|
305
305
|
*/
|
|
306
306
|
async function navigationGather(requestor, options) {
|
|
307
|
-
const {
|
|
308
|
-
log.setLevel(
|
|
307
|
+
const {flags = {}} = options;
|
|
308
|
+
log.setLevel(flags.logLevel || 'error');
|
|
309
309
|
|
|
310
|
-
const {config} =
|
|
311
|
-
await initializeConfig(options.config, {...configContext, gatherMode: 'navigation'});
|
|
310
|
+
const {config} = await initializeConfig('navigation', options.config, flags);
|
|
312
311
|
const computedCache = new Map();
|
|
313
|
-
const internalOptions = {
|
|
314
|
-
skipAboutBlank: configContext.skipAboutBlank,
|
|
315
|
-
};
|
|
316
312
|
|
|
317
|
-
// We can't trigger the navigation through user interaction if we reset the page before starting.
|
|
318
313
|
const isCallback = typeof requestor === 'function';
|
|
319
|
-
if (isCallback) {
|
|
320
|
-
internalOptions.skipAboutBlank = true;
|
|
321
|
-
}
|
|
322
314
|
|
|
323
315
|
const runnerOptions = {config, computedCache};
|
|
324
316
|
const artifacts = await Runner.gather(
|
|
@@ -329,7 +321,7 @@ async function navigationGather(requestor, options) {
|
|
|
329
321
|
// For navigation mode, we shouldn't connect to a browser in audit mode,
|
|
330
322
|
// therefore we connect to the browser in the gatherFn callback.
|
|
331
323
|
if (!page) {
|
|
332
|
-
const {hostname = DEFAULT_HOSTNAME, port = DEFAULT_PORT} =
|
|
324
|
+
const {hostname = DEFAULT_HOSTNAME, port = DEFAULT_PORT} = flags;
|
|
333
325
|
const browser = await puppeteer.connect({browserURL: `http://${hostname}:${port}`});
|
|
334
326
|
page = await browser.newPage();
|
|
335
327
|
}
|
|
@@ -339,7 +331,6 @@ async function navigationGather(requestor, options) {
|
|
|
339
331
|
driver,
|
|
340
332
|
config,
|
|
341
333
|
requestor: normalizedRequestor,
|
|
342
|
-
options: internalOptions,
|
|
343
334
|
};
|
|
344
335
|
const {baseArtifacts} = await _setup(context);
|
|
345
336
|
const {artifacts} = await _navigations({...context, baseArtifacts, computedCache});
|
|
@@ -13,15 +13,14 @@ import {initializeConfig} from '../config/config.js';
|
|
|
13
13
|
import {getBaseArtifacts, finalizeArtifacts} from './base-artifacts.js';
|
|
14
14
|
|
|
15
15
|
/**
|
|
16
|
-
* @param {{page: LH.Puppeteer.Page, config?: LH.Config.Json,
|
|
16
|
+
* @param {{page: LH.Puppeteer.Page, config?: LH.Config.Json, flags?: LH.Flags}} options
|
|
17
17
|
* @return {Promise<LH.Gatherer.FRGatherResult>}
|
|
18
18
|
*/
|
|
19
19
|
async function snapshotGather(options) {
|
|
20
|
-
const {
|
|
21
|
-
log.setLevel(
|
|
20
|
+
const {flags = {}} = options;
|
|
21
|
+
log.setLevel(flags.logLevel || 'error');
|
|
22
22
|
|
|
23
|
-
const {config} =
|
|
24
|
-
await initializeConfig(options.config, {...configContext, gatherMode: 'snapshot'});
|
|
23
|
+
const {config} = await initializeConfig('snapshot', options.config, flags);
|
|
25
24
|
const driver = new Driver(options.page);
|
|
26
25
|
await driver.connect();
|
|
27
26
|
|
|
@@ -14,15 +14,14 @@ import {initializeConfig} from '../config/config.js';
|
|
|
14
14
|
import {getBaseArtifacts, finalizeArtifacts} from './base-artifacts.js';
|
|
15
15
|
|
|
16
16
|
/**
|
|
17
|
-
* @param {{page: LH.Puppeteer.Page, config?: LH.Config.Json,
|
|
17
|
+
* @param {{page: LH.Puppeteer.Page, config?: LH.Config.Json, flags?: LH.Flags}} options
|
|
18
18
|
* @return {Promise<{endTimespanGather(): Promise<LH.Gatherer.FRGatherResult>}>}
|
|
19
19
|
*/
|
|
20
20
|
async function startTimespanGather(options) {
|
|
21
|
-
const {
|
|
22
|
-
log.setLevel(
|
|
21
|
+
const {flags = {}} = options;
|
|
22
|
+
log.setLevel(flags.logLevel || 'error');
|
|
23
23
|
|
|
24
|
-
const {config} =
|
|
25
|
-
await initializeConfig(options.config, {...configContext, gatherMode: 'timespan'});
|
|
24
|
+
const {config} = await initializeConfig('timespan', options.config, flags);
|
|
26
25
|
const driver = new Driver(options.page);
|
|
27
26
|
await driver.connect();
|
|
28
27
|
|
|
@@ -0,0 +1,105 @@
|
|
|
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
|
+
'use strict';
|
|
7
|
+
|
|
8
|
+
import * as PuppeteerReplay from '@puppeteer/replay';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* @param {PuppeteerReplay.Schema.Step} step
|
|
12
|
+
* @return {boolean}
|
|
13
|
+
*/
|
|
14
|
+
function isNavigationStep(step) {
|
|
15
|
+
return Boolean(
|
|
16
|
+
step.type === 'navigate' ||
|
|
17
|
+
step.assertedEvents?.some(event => event.type === 'navigation')
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
class LighthouseStringifyExtension extends PuppeteerReplay.PuppeteerStringifyExtension {
|
|
22
|
+
#isProcessingTimespan = false;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* @override
|
|
26
|
+
* @param {PuppeteerReplay.LineWriter} out
|
|
27
|
+
* @param {PuppeteerReplay.Schema.UserFlow} flow
|
|
28
|
+
*/
|
|
29
|
+
async beforeAllSteps(out, flow) {
|
|
30
|
+
out.appendLine(`const fs = require('fs');`);
|
|
31
|
+
|
|
32
|
+
let isMobile = true;
|
|
33
|
+
for (const step of flow.steps) {
|
|
34
|
+
if (step.type !== 'setViewport') continue;
|
|
35
|
+
isMobile = step.isMobile;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
await super.beforeAllSteps(out, flow);
|
|
39
|
+
|
|
40
|
+
const flags = {
|
|
41
|
+
screenEmulation: {
|
|
42
|
+
disabled: true,
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
out.appendLine(`const flags = ${JSON.stringify(flags)}`);
|
|
46
|
+
if (isMobile) {
|
|
47
|
+
out.appendLine(`const config = undefined;`);
|
|
48
|
+
} else {
|
|
49
|
+
// eslint-disable-next-line max-len
|
|
50
|
+
out.appendLine(`const config = (await import('lighthouse/core/config/desktop-config.js')).default;`);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
out.appendLine(`const lhApi = await import('lighthouse/core/fraggle-rock/api.js');`);
|
|
54
|
+
// eslint-disable-next-line max-len
|
|
55
|
+
out.appendLine(`const lhFlow = await lhApi.startFlow(page, {name: ${JSON.stringify(flow.title)}, config, flags});`);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* @override
|
|
60
|
+
* @param {PuppeteerReplay.LineWriter} out
|
|
61
|
+
* @param {PuppeteerReplay.Schema.Step} step
|
|
62
|
+
* @param {PuppeteerReplay.Schema.UserFlow} flow
|
|
63
|
+
*/
|
|
64
|
+
async stringifyStep(out, step, flow) {
|
|
65
|
+
if (step.type === 'setViewport') {
|
|
66
|
+
await super.stringifyStep(out, step, flow);
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const isNavigation = isNavigationStep(step);
|
|
71
|
+
|
|
72
|
+
if (isNavigation) {
|
|
73
|
+
if (this.#isProcessingTimespan) {
|
|
74
|
+
out.appendLine(`await lhFlow.endTimespan();`);
|
|
75
|
+
this.#isProcessingTimespan = false;
|
|
76
|
+
}
|
|
77
|
+
out.appendLine(`await lhFlow.startNavigation();`);
|
|
78
|
+
} else if (!this.#isProcessingTimespan) {
|
|
79
|
+
out.appendLine(`await lhFlow.startTimespan();`);
|
|
80
|
+
this.#isProcessingTimespan = true;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
await super.stringifyStep(out, step, flow);
|
|
84
|
+
|
|
85
|
+
if (isNavigation) {
|
|
86
|
+
out.appendLine(`await lhFlow.endNavigation();`);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* @override
|
|
92
|
+
* @param {PuppeteerReplay.LineWriter} out
|
|
93
|
+
* @param {PuppeteerReplay.Schema.UserFlow} flow
|
|
94
|
+
*/
|
|
95
|
+
async afterAllSteps(out, flow) {
|
|
96
|
+
if (this.#isProcessingTimespan) {
|
|
97
|
+
out.appendLine(`await lhFlow.endTimespan();`);
|
|
98
|
+
}
|
|
99
|
+
out.appendLine(`const lhFlowReport = await lhFlow.generateReport();`);
|
|
100
|
+
out.appendLine(`fs.writeFileSync(__dirname + '/flow.report.html', lhFlowReport)`);
|
|
101
|
+
await super.afterAllSteps(out, flow);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export default LighthouseStringifyExtension;
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
'use strict';
|
|
7
7
|
|
|
8
|
-
import ReportGenerator from '../../report/generator/report-generator.js';
|
|
8
|
+
import {ReportGenerator} from '../../report/generator/report-generator.js';
|
|
9
9
|
import {snapshotGather} from './gather/snapshot-runner.js';
|
|
10
10
|
import {startTimespanGather} from './gather/timespan-runner.js';
|
|
11
11
|
import {navigationGather} from './gather/navigation-runner.js';
|
|
@@ -63,24 +63,22 @@ class UserFlow {
|
|
|
63
63
|
*/
|
|
64
64
|
_getNextNavigationOptions(stepOptions) {
|
|
65
65
|
const options = {...this.options, ...stepOptions};
|
|
66
|
-
const
|
|
67
|
-
const settingsOverrides = {...configContext.settingsOverrides};
|
|
66
|
+
const flags = {...options.flags};
|
|
68
67
|
|
|
69
|
-
if (
|
|
70
|
-
|
|
68
|
+
if (flags.skipAboutBlank === undefined) {
|
|
69
|
+
flags.skipAboutBlank = true;
|
|
71
70
|
}
|
|
72
71
|
|
|
73
72
|
// On repeat navigations, we want to disable storage reset by default (i.e. it's not a cold load).
|
|
74
73
|
const isSubsequentNavigation = this._gatherSteps
|
|
75
74
|
.some(step => step.artifacts.GatherContext.gatherMode === 'navigation');
|
|
76
75
|
if (isSubsequentNavigation) {
|
|
77
|
-
if (
|
|
78
|
-
|
|
76
|
+
if (flags.disableStorageReset === undefined) {
|
|
77
|
+
flags.disableStorageReset = true;
|
|
79
78
|
}
|
|
80
79
|
}
|
|
81
80
|
|
|
82
|
-
|
|
83
|
-
options.configContext = configContext;
|
|
81
|
+
options.flags = flags;
|
|
84
82
|
|
|
85
83
|
return options;
|
|
86
84
|
}
|
|
@@ -96,7 +94,7 @@ class UserFlow {
|
|
|
96
94
|
artifacts: gatherResult.artifacts,
|
|
97
95
|
name: providedName || this._getDefaultStepName(gatherResult.artifacts),
|
|
98
96
|
config: options.config,
|
|
99
|
-
|
|
97
|
+
flags: options.flags,
|
|
100
98
|
};
|
|
101
99
|
this._gatherSteps.push(gatherStep);
|
|
102
100
|
this._gatherStepRunnerOptions.set(gatherStep, gatherResult.runnerOptions);
|
|
@@ -246,7 +244,7 @@ async function auditGatherSteps(gatherSteps, options) {
|
|
|
246
244
|
/** @type {LH.FlowResult['steps']} */
|
|
247
245
|
const steps = [];
|
|
248
246
|
for (const gatherStep of gatherSteps) {
|
|
249
|
-
const {artifacts, name,
|
|
247
|
+
const {artifacts, name, flags} = gatherStep;
|
|
250
248
|
|
|
251
249
|
let runnerOptions = options.gatherStepRunnerOptions?.get(gatherStep);
|
|
252
250
|
|
|
@@ -255,7 +253,7 @@ async function auditGatherSteps(gatherSteps, options) {
|
|
|
255
253
|
// Step specific configs take precedence over a config for the entire flow.
|
|
256
254
|
const configJson = gatherStep.config || options.config;
|
|
257
255
|
const {gatherMode} = artifacts.GatherContext;
|
|
258
|
-
const {config} = await initializeConfig(
|
|
256
|
+
const {config} = await initializeConfig(gatherMode, configJson, flags);
|
|
259
257
|
runnerOptions = {
|
|
260
258
|
config,
|
|
261
259
|
computedCache: new Map(),
|
|
@@ -16,6 +16,13 @@ class ExecutionContext {
|
|
|
16
16
|
|
|
17
17
|
/** @type {number|undefined} */
|
|
18
18
|
this._executionContextId = undefined;
|
|
19
|
+
/**
|
|
20
|
+
* Marks how many execution context ids have been created, for purposes of having a unique
|
|
21
|
+
* value (that doesn't expose the actual execution context id) to
|
|
22
|
+
* use for __lighthouseExecutionContextUniqueIdentifier.
|
|
23
|
+
* @type {number}
|
|
24
|
+
*/
|
|
25
|
+
this._executionContextIdentifiersCreated = 0;
|
|
19
26
|
|
|
20
27
|
// We use isolated execution contexts for `evaluateAsync` that can be destroyed through navigation
|
|
21
28
|
// and other page actions. Cleanup our relevant bookkeeping as we see those events.
|
|
@@ -64,10 +71,10 @@ class ExecutionContext {
|
|
|
64
71
|
});
|
|
65
72
|
|
|
66
73
|
this._executionContextId = isolatedWorldResponse.executionContextId;
|
|
74
|
+
this._executionContextIdentifiersCreated++;
|
|
67
75
|
return isolatedWorldResponse.executionContextId;
|
|
68
76
|
}
|
|
69
77
|
|
|
70
|
-
|
|
71
78
|
/**
|
|
72
79
|
* Evaluate an expression in the given execution context; an undefined contextId implies the main
|
|
73
80
|
* page without isolation.
|
|
@@ -82,15 +89,25 @@ class ExecutionContext {
|
|
|
82
89
|
this._session.getNextProtocolTimeout() :
|
|
83
90
|
60000;
|
|
84
91
|
|
|
92
|
+
// `__lighthouseExecutionContextUniqueIdentifier` is only used by the FullPageScreenshot gatherer.
|
|
93
|
+
// See `getNodeDetails` in page-functions.
|
|
94
|
+
const uniqueExecutionContextIdentifier = contextId === undefined ?
|
|
95
|
+
undefined :
|
|
96
|
+
this._executionContextIdentifiersCreated;
|
|
97
|
+
|
|
85
98
|
const evaluationParams = {
|
|
86
99
|
// We need to explicitly wrap the raw expression for several purposes:
|
|
87
100
|
// 1. Ensure that the expression will be a native Promise and not a polyfill/non-Promise.
|
|
88
101
|
// 2. Ensure that errors in the expression are captured by the Promise.
|
|
89
102
|
// 3. Ensure that errors captured in the Promise are converted into plain-old JS Objects
|
|
90
103
|
// so that they can be serialized properly b/c JSON.stringify(new Error('foo')) === '{}'
|
|
104
|
+
//
|
|
105
|
+
// `__lighthouseExecutionContextUniqueIdentifier` is only used by the FullPageScreenshot gatherer.
|
|
106
|
+
// See `getNodeDetails` in page-functions.
|
|
91
107
|
expression: `(function wrapInNativePromise() {
|
|
92
108
|
${ExecutionContext._cachedNativesPreamble};
|
|
93
|
-
globalThis.
|
|
109
|
+
globalThis.__lighthouseExecutionContextUniqueIdentifier =
|
|
110
|
+
${uniqueExecutionContextIdentifier};
|
|
94
111
|
return new Promise(function (resolve) {
|
|
95
112
|
return Promise.resolve()
|
|
96
113
|
.then(_ => ${expression})
|
|
@@ -10,7 +10,7 @@ import NetworkRecords from '../computed/network-records.js';
|
|
|
10
10
|
import {getPageLoadError} from '../lib/navigation-error.js';
|
|
11
11
|
import * as emulation from '../lib/emulation.js';
|
|
12
12
|
import * as constants from '../config/constants.js';
|
|
13
|
-
import format from '../../shared/localization/format.js';
|
|
13
|
+
import * as format from '../../shared/localization/format.js';
|
|
14
14
|
import {getBenchmarkIndex, getEnvironmentWarnings} from './driver/environment.js';
|
|
15
15
|
import * as prepare from './driver/prepare.js';
|
|
16
16
|
import * as storage from './driver/storage.js';
|
package/core/index.js
CHANGED
|
@@ -12,7 +12,6 @@ import {Config} from './config/config.js';
|
|
|
12
12
|
import URL from './lib/url-shim.js';
|
|
13
13
|
import * as fraggleRock from './fraggle-rock/api.js';
|
|
14
14
|
import {Driver} from './gather/driver.js';
|
|
15
|
-
import {flagsToFRContext} from './config/config-helpers.js';
|
|
16
15
|
import {initializeConfig} from './fraggle-rock/config/config.js';
|
|
17
16
|
|
|
18
17
|
/** @typedef {import('./gather/connections/connection.js').Connection} Connection */
|
|
@@ -41,8 +40,7 @@ import {initializeConfig} from './fraggle-rock/config/config.js';
|
|
|
41
40
|
* @return {Promise<LH.RunnerResult|undefined>}
|
|
42
41
|
*/
|
|
43
42
|
async function lighthouse(url, flags = {}, configJSON, page) {
|
|
44
|
-
|
|
45
|
-
return fraggleRock.navigation(url, {page, config: configJSON, configContext});
|
|
43
|
+
return fraggleRock.navigation(url, {page, config: configJSON, flags});
|
|
46
44
|
}
|
|
47
45
|
|
|
48
46
|
/**
|
|
@@ -86,8 +84,7 @@ async function legacyNavigation(url, flags = {}, configJSON, userConnection) {
|
|
|
86
84
|
* @return {Promise<LH.Config.FRConfig>}
|
|
87
85
|
*/
|
|
88
86
|
async function generateConfig(configJson, flags = {}, gatherMode = 'navigation') {
|
|
89
|
-
const
|
|
90
|
-
const {config} = await initializeConfig(configJson, {...configContext, gatherMode});
|
|
87
|
+
const {config} = await initializeConfig(gatherMode, configJson, flags);
|
|
91
88
|
return config;
|
|
92
89
|
}
|
|
93
90
|
|
package/core/lib/asset-saver.js
CHANGED
|
@@ -12,7 +12,7 @@ import stream from 'stream';
|
|
|
12
12
|
import util from 'util';
|
|
13
13
|
import {Simulator} from './dependency-graph/simulator/simulator.js';
|
|
14
14
|
import lanternTraceSaver from './lantern-trace-saver.js';
|
|
15
|
-
import {
|
|
15
|
+
import {MetricTraceEvents} from './traces/metric-trace-events.js';
|
|
16
16
|
import NetworkAnalysisComputed from '../computed/network-analysis.js';
|
|
17
17
|
import LoadSimulatorComputed from '../computed/load-simulator.js';
|
|
18
18
|
import {LighthouseError} from '../lib/lh-error.js';
|
|
@@ -164,7 +164,7 @@ async function prepareAssets(artifacts, audits) {
|
|
|
164
164
|
|
|
165
165
|
const traceData = Object.assign({}, trace);
|
|
166
166
|
if (audits) {
|
|
167
|
-
const evts = new
|
|
167
|
+
const evts = new MetricTraceEvents(traceData.traceEvents, audits).generateFakeEvents();
|
|
168
168
|
traceData.traceEvents = traceData.traceEvents.concat(evts);
|
|
169
169
|
}
|
|
170
170
|
|
|
@@ -498,6 +498,18 @@ function getNodeDetails(element) {
|
|
|
498
498
|
const selector = getNodeSelector(element);
|
|
499
499
|
|
|
500
500
|
// Create an id that will be unique across all execution contexts.
|
|
501
|
+
//
|
|
502
|
+
// Made up of 3 components:
|
|
503
|
+
// - prefix unique to specific execution context
|
|
504
|
+
// - nth unique node seen by this function for this execution context
|
|
505
|
+
// - node tagName
|
|
506
|
+
//
|
|
507
|
+
// Every page load only has up to two associated contexts - the page context
|
|
508
|
+
// (denoted as `__lighthouseExecutionContextUniqueIdentifier` being undefined)
|
|
509
|
+
// and the isolated context. The id must be unique to distinguish gatherers running
|
|
510
|
+
// on different page loads that identify the same logical element, for purposes
|
|
511
|
+
// of the full page screenshot node lookup; hence the prefix.
|
|
512
|
+
//
|
|
501
513
|
// The id could be any arbitrary string, the exact value is not important.
|
|
502
514
|
// For example, tagName is added only because it might be useful for debugging.
|
|
503
515
|
// But execution id and map size are added to ensure uniqueness.
|
|
@@ -507,9 +519,9 @@ function getNodeDetails(element) {
|
|
|
507
519
|
let lhId = window.__lighthouseNodesDontTouchOrAllVarianceGoesAway.get(element);
|
|
508
520
|
if (!lhId) {
|
|
509
521
|
lhId = [
|
|
510
|
-
window.
|
|
511
|
-
|
|
512
|
-
|
|
522
|
+
window.__lighthouseExecutionContextUniqueIdentifier === undefined ?
|
|
523
|
+
'page' :
|
|
524
|
+
window.__lighthouseExecutionContextUniqueIdentifier,
|
|
513
525
|
window.__lighthouseNodesDontTouchOrAllVarianceGoesAway.size,
|
|
514
526
|
element.tagName,
|
|
515
527
|
].join('-');
|
|
@@ -19,7 +19,7 @@ function getUberMetrics(auditResults) {
|
|
|
19
19
|
return metricsAudit.details.items[0];
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
class
|
|
22
|
+
class MetricTraceEvents {
|
|
23
23
|
/**
|
|
24
24
|
* @param {Array<LH.TraceEvent>} traceEvents
|
|
25
25
|
* @param {LH.Result['audits']} auditResults
|
|
@@ -100,7 +100,7 @@ class Metrics {
|
|
|
100
100
|
|
|
101
101
|
/** @type {Array<{ts: number, id: string, name: string}>} */
|
|
102
102
|
const resolvedMetrics = [];
|
|
103
|
-
|
|
103
|
+
MetricTraceEvents.metricsDefinitions.forEach(metric => {
|
|
104
104
|
// Skip if auditResults is missing a particular audit result
|
|
105
105
|
const ts = uberMetrics[metric.tsKey];
|
|
106
106
|
if (ts === undefined) {
|
|
@@ -197,5 +197,4 @@ class Metrics {
|
|
|
197
197
|
}
|
|
198
198
|
}
|
|
199
199
|
|
|
200
|
-
|
|
201
|
-
export {Metrics};
|
|
200
|
+
export {MetricTraceEvents};
|