lighthouse 8.4.0 → 8.5.0-dev.20210921
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/changelog.md +79 -0
- package/flow-report/assets/styles.css +85 -61
- package/flow-report/src/app.tsx +6 -2
- package/flow-report/src/common.tsx +0 -41
- package/flow-report/src/icons.tsx +11 -0
- package/flow-report/src/summary/summary.tsx +32 -25
- package/flow-report/src/topbar.tsx +60 -0
- package/flow-report/src/util.ts +0 -1
- package/flow-report/src/wrappers/{gauge.tsx → category-score.tsx} +11 -5
- package/flow-report/test/summary/summary-test.tsx +3 -3
- package/flow-report/tsconfig.json +6 -22
- package/{lighthouse-core/gather/gatherers/gather-context.js → lighthouse-cli/.eslintrc.cjs} +20 -18
- package/lighthouse-cli/bin.js +31 -21
- package/lighthouse-cli/cli-flags.js +12 -7
- package/lighthouse-cli/commands/commands.js +2 -7
- package/lighthouse-cli/commands/list-audits.js +2 -2
- package/lighthouse-cli/commands/list-trace-categories.js +2 -2
- package/lighthouse-cli/index.js +3 -1
- package/lighthouse-cli/package.json +4 -0
- package/lighthouse-cli/printer.js +4 -3
- package/lighthouse-cli/run.js +14 -15
- package/lighthouse-cli/sentry-prompt.js +5 -6
- package/lighthouse-cli/test/smokehouse/frontends/back-compat-util.js +1 -1
- package/lighthouse-cli/test/smokehouse/frontends/lib.js +5 -4
- package/lighthouse-cli/test/smokehouse/frontends/node.js +1 -1
- package/lighthouse-cli/test/smokehouse/frontends/smokehouse-bin.js +93 -14
- package/lighthouse-cli/test/smokehouse/lib/child-process-error.js +1 -1
- package/lighthouse-cli/test/smokehouse/lib/concurrent-mapper.js +1 -1
- package/lighthouse-cli/test/smokehouse/lib/local-console.js +1 -1
- package/lighthouse-cli/test/smokehouse/lighthouse-runners/bundle.js +20 -16
- package/lighthouse-cli/test/smokehouse/lighthouse-runners/cli.js +15 -12
- package/lighthouse-cli/test/smokehouse/report-assert.js +23 -13
- package/lighthouse-cli/test/smokehouse/smokehouse.js +32 -11
- package/lighthouse-core/audits/byte-efficiency/byte-efficiency-audit.js +0 -2
- package/lighthouse-core/audits/byte-efficiency/uses-responsive-images-snapshot.js +6 -1
- package/lighthouse-core/audits/dobetterweb/js-libraries.js +5 -0
- package/lighthouse-core/audits/dobetterweb/uses-http2.js +1 -0
- package/lighthouse-core/audits/lcp-lazy-loaded.js +1 -0
- package/lighthouse-core/audits/oopif-iframe-test-audit.js +30 -0
- package/lighthouse-core/computed/metrics/metric.js +2 -1
- package/lighthouse-core/config/config-helpers.js +47 -3
- package/lighthouse-core/config/config.js +3 -46
- package/lighthouse-core/config/default-config.js +0 -1
- package/lighthouse-core/fraggle-rock/config/config.js +22 -4
- package/lighthouse-core/fraggle-rock/config/default-config.js +0 -9
- package/lighthouse-core/fraggle-rock/config/filters.js +3 -0
- package/lighthouse-core/fraggle-rock/config/validation.js +19 -1
- package/lighthouse-core/fraggle-rock/gather/base-artifacts.js +8 -3
- package/lighthouse-core/fraggle-rock/gather/driver.js +10 -0
- package/lighthouse-core/fraggle-rock/gather/navigation-runner.js +40 -19
- package/lighthouse-core/fraggle-rock/gather/runner-helpers.js +3 -0
- package/lighthouse-core/fraggle-rock/gather/session.js +44 -1
- package/lighthouse-core/fraggle-rock/gather/snapshot-runner.js +4 -1
- package/lighthouse-core/fraggle-rock/gather/timespan-runner.js +5 -1
- package/lighthouse-core/gather/devtools-log.js +9 -3
- package/lighthouse-core/gather/driver/network-monitor.js +57 -13
- package/lighthouse-core/gather/driver/prepare.js +43 -17
- package/lighthouse-core/gather/driver/target-manager.js +125 -0
- package/lighthouse-core/gather/driver.js +20 -0
- package/lighthouse-core/gather/fetcher.js +2 -2
- package/lighthouse-core/gather/gather-runner.js +1 -1
- package/lighthouse-core/gather/gatherers/css-usage.js +0 -2
- package/lighthouse-core/gather/gatherers/devtools-log.js +11 -9
- package/lighthouse-core/gather/gatherers/js-usage.js +0 -1
- package/lighthouse-core/gather/gatherers/script-elements.js +4 -5
- package/lighthouse-core/gather/gatherers/seo/robots-txt.js +3 -3
- package/lighthouse-core/gather/gatherers/trace.js +4 -5
- package/lighthouse-core/index.js +4 -0
- package/lighthouse-core/lib/emulation.js +0 -1
- package/lighthouse-core/lib/i18n/locales/en-US.json +6 -0
- package/lighthouse-core/lib/i18n/locales/en-XL.json +6 -0
- package/lighthouse-core/lib/network-recorder.js +8 -0
- package/lighthouse-core/lib/proto-preprocessor.js +5 -10
- package/lighthouse-core/runner.js +1 -3
- package/package.json +12 -9
- package/readme.md +2 -0
- package/report/assets/styles.css +120 -12
- package/report/assets/templates.html +13 -0
- package/report/clients/psi.js +5 -1
- package/report/generator/tsconfig.json +5 -16
- package/report/renderer/category-renderer.js +58 -5
- package/report/renderer/components.js +25 -5
- package/report/renderer/performance-category-renderer.js +5 -5
- package/report/renderer/pwa-category-renderer.js +10 -0
- package/report/renderer/report-renderer.js +12 -3
- package/report/renderer/report-ui-features.js +1 -4
- package/report/test/renderer/category-renderer-test.js +19 -0
- package/report/tsconfig.json +6 -21
- package/report/types/report-result.d.ts +1 -1
- package/root.js +3 -1
- package/third-party/snyk/snapshot.json +2 -2
- package/tsconfig-all.json +15 -0
- package/tsconfig-base.json +24 -0
- package/tsconfig.json +8 -18
- package/types/artifacts.d.ts +7 -7
- package/types/config.d.ts +1 -1
- package/types/gatherer.d.ts +7 -1
- package/types/lhr/flow.d.ts +0 -1
- package/types/lhr/lhr.d.ts +5 -1
- package/types/lhr/tsconfig.json +4 -14
- package/types/protocol.d.ts +2 -0
- package/types/smokehouse.d.ts +2 -1
- package/dist/report/flow.js +0 -149
- package/dist/report/standalone.js +0 -228
- package/flow-report/test/common-test.tsx +0 -131
- package/lighthouse-core/gather/gatherers/host-form-factor.js +0 -31
- package/lighthouse-core/gather/gatherers/host-user-agent.js +0 -29
|
@@ -11,8 +11,9 @@
|
|
|
11
11
|
* Currently uses `lighthouse-dt-bundle.js`.
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
import ChromeLauncher from 'chrome-launcher';
|
|
15
|
+
|
|
16
|
+
import ChromeProtocol from '../../../../lighthouse-core/gather/connections/cri.js';
|
|
16
17
|
|
|
17
18
|
const originalRequire = global.require;
|
|
18
19
|
if (typeof globalThis === 'undefined') {
|
|
@@ -22,7 +23,7 @@ if (typeof globalThis === 'undefined') {
|
|
|
22
23
|
|
|
23
24
|
// Load bundle, which creates a `global.runBundledLighthouse`.
|
|
24
25
|
// @ts-ignore - file exists if `yarn build-all` is run, but not used for types anyways.
|
|
25
|
-
|
|
26
|
+
import '../../../../dist/lighthouse-dt-bundle.js'; // eslint-disable-line
|
|
26
27
|
|
|
27
28
|
global.require = originalRequire;
|
|
28
29
|
|
|
@@ -43,20 +44,23 @@ async function runLighthouse(url, configJson, testRunnerOptions = {}) {
|
|
|
43
44
|
const port = launchedChrome.port;
|
|
44
45
|
const connection = new ChromeProtocol(port);
|
|
45
46
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
}
|
|
47
|
+
try {
|
|
48
|
+
// Run Lighthouse.
|
|
49
|
+
const logLevel = testRunnerOptions.isDebug ? 'info' : undefined;
|
|
50
|
+
const runnerResult = await lighthouse(url, {port, logLevel}, configJson, connection);
|
|
51
|
+
if (!runnerResult) throw new Error('No runnerResult');
|
|
52
|
+
|
|
53
|
+
return {
|
|
54
|
+
lhr: runnerResult.lhr,
|
|
55
|
+
artifacts: runnerResult.artifacts,
|
|
56
|
+
log: '', // TODO: if want to run in parallel, need to capture lighthouse-logger output.
|
|
57
|
+
};
|
|
58
|
+
} finally {
|
|
59
|
+
// Clean up and return results.
|
|
60
|
+
await launchedChrome.kill();
|
|
61
|
+
}
|
|
58
62
|
}
|
|
59
63
|
|
|
60
|
-
|
|
64
|
+
export {
|
|
61
65
|
runLighthouse,
|
|
62
66
|
};
|
|
@@ -12,16 +12,18 @@
|
|
|
12
12
|
* them.
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
const execFileAsync = promisify(require('child_process').execFile);
|
|
15
|
+
import {promises as fs} from 'fs';
|
|
16
|
+
import {promisify} from 'util';
|
|
17
|
+
import {execFile} from 'child_process';
|
|
19
18
|
|
|
20
|
-
|
|
19
|
+
import log from 'lighthouse-logger';
|
|
21
20
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
21
|
+
import assetSaver from '../../../../lighthouse-core/lib/asset-saver.js';
|
|
22
|
+
import {LocalConsole} from '../lib/local-console.js';
|
|
23
|
+
import {ChildProcessError} from '../lib/child-process-error.js';
|
|
24
|
+
import {LH_ROOT} from '../../../../root.js';
|
|
25
|
+
|
|
26
|
+
const execFileAsync = promisify(execFile);
|
|
25
27
|
|
|
26
28
|
/**
|
|
27
29
|
* Launch Chrome and do a full Lighthouse run via the Lighthouse CLI.
|
|
@@ -31,9 +33,10 @@ const ChildProcessError = require('../lib/child-process-error.js');
|
|
|
31
33
|
* @return {Promise<{lhr: LH.Result, artifacts: LH.Artifacts, log: string}>}
|
|
32
34
|
*/
|
|
33
35
|
async function runLighthouse(url, configJson, testRunnerOptions = {}) {
|
|
34
|
-
const tmpPath = await fs.mkdtemp(`${os.tmpdir()}/smokehouse-`);
|
|
35
|
-
|
|
36
36
|
const {isDebug} = testRunnerOptions;
|
|
37
|
+
const tmpDir = `${LH_ROOT}/.tmp/smokehouse`;
|
|
38
|
+
await fs.mkdir(tmpDir, {recursive: true});
|
|
39
|
+
const tmpPath = await fs.mkdtemp(`${tmpDir}/smokehouse-`);
|
|
37
40
|
return internalRun(url, tmpPath, configJson, testRunnerOptions)
|
|
38
41
|
// Wait for internalRun() before removing scratch directory.
|
|
39
42
|
.finally(() => !isDebug && fs.rmdir(tmpPath, {recursive: true}));
|
|
@@ -69,7 +72,7 @@ async function internalRun(url, tmpPath, configJson, options) {
|
|
|
69
72
|
const artifactsDirectory = `${tmpPath}/artifacts/`;
|
|
70
73
|
|
|
71
74
|
const args = [
|
|
72
|
-
`${
|
|
75
|
+
`${LH_ROOT}/lighthouse-cli/index.js`,
|
|
73
76
|
`${url}`,
|
|
74
77
|
`--output-path=${outputPath}`,
|
|
75
78
|
'--output=json',
|
|
@@ -142,6 +145,6 @@ async function internalRun(url, tmpPath, configJson, options) {
|
|
|
142
145
|
};
|
|
143
146
|
}
|
|
144
147
|
|
|
145
|
-
|
|
148
|
+
export {
|
|
146
149
|
runLighthouse,
|
|
147
150
|
};
|
|
@@ -10,15 +10,10 @@
|
|
|
10
10
|
* against the results actually collected from Lighthouse.
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
const LocalConsole = require('./lib/local-console.js');
|
|
13
|
+
import cloneDeep from 'lodash.clonedeep';
|
|
14
|
+
import log from 'lighthouse-logger';
|
|
16
15
|
|
|
17
|
-
|
|
18
|
-
const OPS_REGEXP = /<=?|>=?|\+\/-|±/.source;
|
|
19
|
-
// An optional number, optional whitespace, an operator, optional whitespace, a number.
|
|
20
|
-
const NUMERICAL_EXPECTATION_REGEXP =
|
|
21
|
-
new RegExp(`^(${NUMBER_REGEXP})?\\s*(${OPS_REGEXP})\\s*(${NUMBER_REGEXP})$`);
|
|
16
|
+
import {LocalConsole} from './lib/local-console.js';
|
|
22
17
|
|
|
23
18
|
/**
|
|
24
19
|
* @typedef Difference
|
|
@@ -36,6 +31,12 @@ const NUMERICAL_EXPECTATION_REGEXP =
|
|
|
36
31
|
* @property {Difference|null} [diff]
|
|
37
32
|
*/
|
|
38
33
|
|
|
34
|
+
const NUMBER_REGEXP = /(?:\d|\.)+/.source;
|
|
35
|
+
const OPS_REGEXP = /<=?|>=?|\+\/-|±/.source;
|
|
36
|
+
// An optional number, optional whitespace, an operator, optional whitespace, a number.
|
|
37
|
+
const NUMERICAL_EXPECTATION_REGEXP =
|
|
38
|
+
new RegExp(`^(${NUMBER_REGEXP})?\\s*(${OPS_REGEXP})\\s*(${NUMBER_REGEXP})$`);
|
|
39
|
+
|
|
39
40
|
/**
|
|
40
41
|
* Checks if the actual value matches the expectation. Does not recursively search. This supports
|
|
41
42
|
* - Greater than/less than operators, e.g. "<100", ">90"
|
|
@@ -157,9 +158,11 @@ function makeComparison(name, actualResult, expectedResult) {
|
|
|
157
158
|
* @param {LocalConsole} localConsole
|
|
158
159
|
* @param {LH.Result} lhr
|
|
159
160
|
* @param {Smokehouse.ExpectedRunnerResult} expected
|
|
161
|
+
* @param {{isBundled?: boolean}=} reportOptions
|
|
160
162
|
*/
|
|
161
|
-
function pruneExpectations(localConsole, lhr, expected) {
|
|
163
|
+
function pruneExpectations(localConsole, lhr, expected, reportOptions) {
|
|
162
164
|
const isFraggleRock = lhr.configSettings.channel === 'fraggle-rock-cli';
|
|
165
|
+
const isBundled = reportOptions && reportOptions.isBundled;
|
|
163
166
|
|
|
164
167
|
/**
|
|
165
168
|
* Lazily compute the Chrome version because some reports are explicitly asserting error conditions.
|
|
@@ -211,6 +214,12 @@ function pruneExpectations(localConsole, lhr, expected) {
|
|
|
211
214
|
`Actual channel: ${lhr.configSettings.channel}`,
|
|
212
215
|
].join(' '));
|
|
213
216
|
delete obj[key];
|
|
217
|
+
} else if (value._skipInBundled && !isBundled) {
|
|
218
|
+
localConsole.log([
|
|
219
|
+
`[${key}] marked as skip in bundled and runner is bundled, pruning expectation:`,
|
|
220
|
+
JSON.stringify(value, null, 2),
|
|
221
|
+
].join(' '));
|
|
222
|
+
delete obj[key];
|
|
214
223
|
} else {
|
|
215
224
|
pruneRecursively(value);
|
|
216
225
|
}
|
|
@@ -218,6 +227,7 @@ function pruneExpectations(localConsole, lhr, expected) {
|
|
|
218
227
|
|
|
219
228
|
delete obj._legacyOnly;
|
|
220
229
|
delete obj._fraggleRockOnly;
|
|
230
|
+
delete obj._skipInBundled;
|
|
221
231
|
delete obj._minChromiumMilestone;
|
|
222
232
|
delete obj._maxChromiumMilestone;
|
|
223
233
|
}
|
|
@@ -358,13 +368,13 @@ function reportAssertion(localConsole, assertion) {
|
|
|
358
368
|
* summary. Returns count of passed and failed tests.
|
|
359
369
|
* @param {{lhr: LH.Result, artifacts: LH.Artifacts, networkRequests?: string[]}} actual
|
|
360
370
|
* @param {Smokehouse.ExpectedRunnerResult} expected
|
|
361
|
-
* @param {{isDebug?: boolean}=} reportOptions
|
|
371
|
+
* @param {{isDebug?: boolean, isBundled?: boolean}=} reportOptions
|
|
362
372
|
* @return {{passed: number, failed: number, log: string}}
|
|
363
373
|
*/
|
|
364
|
-
function
|
|
374
|
+
function getAssertionReport(actual, expected, reportOptions = {}) {
|
|
365
375
|
const localConsole = new LocalConsole();
|
|
366
376
|
|
|
367
|
-
expected = pruneExpectations(localConsole, actual.lhr, expected);
|
|
377
|
+
expected = pruneExpectations(localConsole, actual.lhr, expected, reportOptions);
|
|
368
378
|
const comparisons = collateResults(localConsole, actual, expected);
|
|
369
379
|
|
|
370
380
|
let correctCount = 0;
|
|
@@ -389,4 +399,4 @@ function report(actual, expected, reportOptions = {}) {
|
|
|
389
399
|
};
|
|
390
400
|
}
|
|
391
401
|
|
|
392
|
-
|
|
402
|
+
export {getAssertionReport};
|
|
@@ -11,27 +11,38 @@
|
|
|
11
11
|
* smoke tests passed.
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
|
-
const log = require('lighthouse-logger');
|
|
15
|
-
const cliLighthouseRunner = require('./lighthouse-runners/cli.js').runLighthouse;
|
|
16
|
-
const getAssertionReport = require('./report-assert.js');
|
|
17
|
-
const LocalConsole = require('./lib/local-console.js');
|
|
18
|
-
const ConcurrentMapper = require('./lib/concurrent-mapper.js');
|
|
19
|
-
|
|
20
14
|
/* eslint-disable no-console */
|
|
21
15
|
|
|
22
|
-
/** @typedef {import('./lib/child-process-error.js')} ChildProcessError */
|
|
16
|
+
/** @typedef {import('./lib/child-process-error.js').ChildProcessError} ChildProcessError */
|
|
23
17
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
18
|
+
/**
|
|
19
|
+
* @typedef Run
|
|
20
|
+
* @property {string[] | undefined} networkRequests
|
|
21
|
+
* @property {LH.Result} lhr
|
|
22
|
+
* @property {LH.Artifacts} artifacts
|
|
23
|
+
* @property {string} lighthouseLog
|
|
24
|
+
* @property {string} assertionLog
|
|
25
|
+
*/
|
|
27
26
|
|
|
28
27
|
/**
|
|
29
28
|
* @typedef SmokehouseResult
|
|
30
29
|
* @property {string} id
|
|
31
30
|
* @property {number} passed
|
|
32
31
|
* @property {number} failed
|
|
32
|
+
* @property {Run[]} runs
|
|
33
33
|
*/
|
|
34
34
|
|
|
35
|
+
import log from 'lighthouse-logger';
|
|
36
|
+
|
|
37
|
+
import {runLighthouse as cliLighthouseRunner} from './lighthouse-runners/cli.js';
|
|
38
|
+
import {getAssertionReport} from './report-assert.js';
|
|
39
|
+
import {LocalConsole} from './lib/local-console.js';
|
|
40
|
+
import {ConcurrentMapper} from './lib/concurrent-mapper.js';
|
|
41
|
+
|
|
42
|
+
// The number of concurrent (`!runSerially`) tests to run if `jobs` isn't set.
|
|
43
|
+
const DEFAULT_CONCURRENT_RUNS = 5;
|
|
44
|
+
const DEFAULT_RETRIES = 0;
|
|
45
|
+
|
|
35
46
|
/**
|
|
36
47
|
* Runs the selected smoke tests. Returns whether all assertions pass.
|
|
37
48
|
* @param {Array<Smokehouse.TestDfn>} smokeTestDefns
|
|
@@ -120,6 +131,8 @@ async function runSmokeTest(smokeTestDefn, testOptions) {
|
|
|
120
131
|
console.log(`${purpleify(id)} smoketest starting…`);
|
|
121
132
|
|
|
122
133
|
// Rerun test until there's a passing result or retries are exhausted to prevent flakes.
|
|
134
|
+
/** @type {Run[]} */
|
|
135
|
+
const runs = [];
|
|
123
136
|
let result;
|
|
124
137
|
let report;
|
|
125
138
|
const bufferedConsole = new LocalConsole();
|
|
@@ -145,6 +158,13 @@ async function runSmokeTest(smokeTestDefn, testOptions) {
|
|
|
145
158
|
|
|
146
159
|
// Assert result.
|
|
147
160
|
report = getAssertionReport(result, expectations, {isDebug});
|
|
161
|
+
|
|
162
|
+
runs.push({
|
|
163
|
+
...result,
|
|
164
|
+
lighthouseLog: result.log,
|
|
165
|
+
assertionLog: report.log,
|
|
166
|
+
});
|
|
167
|
+
|
|
148
168
|
if (report.failed) {
|
|
149
169
|
bufferedConsole.log(` ${getAssertionLog(report.failed)} failed.`);
|
|
150
170
|
continue; // Retry, if possible.
|
|
@@ -180,6 +200,7 @@ async function runSmokeTest(smokeTestDefn, testOptions) {
|
|
|
180
200
|
id,
|
|
181
201
|
passed,
|
|
182
202
|
failed,
|
|
203
|
+
runs,
|
|
183
204
|
};
|
|
184
205
|
}
|
|
185
206
|
|
|
@@ -206,6 +227,6 @@ function getAssertionLog(count) {
|
|
|
206
227
|
return `${count} assertion${plural}`;
|
|
207
228
|
}
|
|
208
229
|
|
|
209
|
-
|
|
230
|
+
export {
|
|
210
231
|
runSmokehouse,
|
|
211
232
|
};
|
|
@@ -129,7 +129,6 @@ class UnusedBytes extends Audit {
|
|
|
129
129
|
const [result, graph, simulator] = await Promise.all([
|
|
130
130
|
this.audit_(artifacts, networkRecords, context),
|
|
131
131
|
// Page dependency graph is only used in navigation mode.
|
|
132
|
-
// TODO(FR-COMPAT): Use dependency graph in timespan mode.
|
|
133
132
|
gatherContext.gatherMode === 'navigation' ?
|
|
134
133
|
PageDependencyGraph.request({trace, devtoolsLog}, context) :
|
|
135
134
|
null,
|
|
@@ -201,7 +200,6 @@ class UnusedBytes extends Audit {
|
|
|
201
200
|
}
|
|
202
201
|
|
|
203
202
|
/**
|
|
204
|
-
* TODO(FR-COMPAT): Rework opportunities to remove emphasis on `wastedMs`
|
|
205
203
|
* @param {number} wastedBytes
|
|
206
204
|
* @param {Simulator} simulator
|
|
207
205
|
*/
|
|
@@ -17,6 +17,10 @@ const URL = require('../../lib/url-shim.js');
|
|
|
17
17
|
const i18n = require('../../lib/i18n/i18n.js');
|
|
18
18
|
|
|
19
19
|
const UIStrings = {
|
|
20
|
+
/** Descriptive title of a Lighthouse audit that checks if images match their displayed dimensions. This is displayed when the audit is passing. */
|
|
21
|
+
title: 'Images were appropriate for their displayed size',
|
|
22
|
+
/** Descriptive title of a Lighthouse audit that checks if images match their displayed dimensions. This is displayed when the audit is failing. */
|
|
23
|
+
failureTitle: 'Images were larger than their displayed size',
|
|
20
24
|
/** Label for a column in a data table; entries will be the dimensions of an image as it appears on the page. */
|
|
21
25
|
columnDisplayedDimensions: 'Displayed dimensions',
|
|
22
26
|
/** Label for a column in a data table; entries will be the dimensions of an image from it's source file. */
|
|
@@ -35,7 +39,8 @@ class UsesResponsiveImagesSnapshot extends Audit {
|
|
|
35
39
|
static get meta() {
|
|
36
40
|
return {
|
|
37
41
|
id: 'uses-responsive-images-snapshot',
|
|
38
|
-
title:
|
|
42
|
+
title: str_(UIStrings.title),
|
|
43
|
+
failureTitle: str_(UIStrings.failureTitle),
|
|
39
44
|
description: UsesResponsiveImages.str_(UsesResponsiveImages.UIStrings.description),
|
|
40
45
|
supportedModes: ['snapshot'],
|
|
41
46
|
requiredArtifacts: ['ImageElements', 'ViewportDimensions'],
|
|
@@ -32,6 +32,7 @@ class JsLibrariesAudit extends Audit {
|
|
|
32
32
|
return {
|
|
33
33
|
id: 'js-libraries',
|
|
34
34
|
title: str_(UIStrings.title),
|
|
35
|
+
scoreDisplayMode: Audit.SCORING_MODES.INFORMATIVE,
|
|
35
36
|
description: str_(UIStrings.description),
|
|
36
37
|
requiredArtifacts: ['Stacks'],
|
|
37
38
|
};
|
|
@@ -69,6 +70,10 @@ class JsLibrariesAudit extends Audit {
|
|
|
69
70
|
}),
|
|
70
71
|
};
|
|
71
72
|
|
|
73
|
+
if (!libDetails.length) {
|
|
74
|
+
return {score: null, notApplicable: true};
|
|
75
|
+
}
|
|
76
|
+
|
|
72
77
|
return {
|
|
73
78
|
score: 1, // Always pass for now.
|
|
74
79
|
details: {
|
|
@@ -60,6 +60,7 @@ class UsesHTTP2Audit extends Audit {
|
|
|
60
60
|
id: 'uses-http2',
|
|
61
61
|
title: str_(UIStrings.title),
|
|
62
62
|
description: str_(UIStrings.description),
|
|
63
|
+
scoreDisplayMode: Audit.SCORING_MODES.NUMERIC,
|
|
63
64
|
supportedModes: ['navigation'],
|
|
64
65
|
requiredArtifacts: ['URL', 'devtoolsLogs', 'traces'],
|
|
65
66
|
};
|
|
@@ -27,6 +27,7 @@ class LargestContentfulPaintLazyLoaded extends Audit {
|
|
|
27
27
|
return {
|
|
28
28
|
id: 'lcp-lazy-loaded',
|
|
29
29
|
title: str_(UIStrings.title),
|
|
30
|
+
failureTitle: str_(UIStrings.failureTitle),
|
|
30
31
|
description: str_(UIStrings.description),
|
|
31
32
|
supportedModes: ['navigation'],
|
|
32
33
|
requiredArtifacts: ['TraceElements', 'ViewportDimensions', 'ImageElements'],
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license Copyright 2021 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
|
+
/**
|
|
9
|
+
* @fileoverview This is a fake audit used exclusively in smoke tests to force inclusion of IFrameElements artifact.
|
|
10
|
+
* It is included here for complex reasons in the way the bundled smoketests work.
|
|
11
|
+
*
|
|
12
|
+
* The smokehouse configs are evaluated first in the node CLI side (which requires an absolute path using LH_ROOT).
|
|
13
|
+
* The smokehouse configs are then *re-evaluated* in the bundled context for execution by Lighthouse (which *cannot* use an absolute path using LH_ROOT).
|
|
14
|
+
*
|
|
15
|
+
* This mismatch in environment demands that the audit path in the config must be context-aware,
|
|
16
|
+
* yet the require-graph for the config is included before even the CLI knows that it will be using
|
|
17
|
+
* a bundled runner. Rather than force a massive smoketest architecture change, we include a harmless,
|
|
18
|
+
* test-only audit in our core list instead.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
module.exports = {
|
|
22
|
+
meta: {
|
|
23
|
+
id: 'oopif-iframe-test-audit',
|
|
24
|
+
title: 'IFrame Elements',
|
|
25
|
+
failureTitle: 'IFrame Elements',
|
|
26
|
+
description: 'Audit to force the inclusion of IFrameElements artifact',
|
|
27
|
+
requiredArtifacts: ['IFrameElements'],
|
|
28
|
+
},
|
|
29
|
+
audit: () => ({score: 1}),
|
|
30
|
+
};
|
|
@@ -69,7 +69,8 @@ class Metric {
|
|
|
69
69
|
}
|
|
70
70
|
|
|
71
71
|
const processedTrace = await ProcessedTrace.request(trace, context);
|
|
72
|
-
const processedNavigation =
|
|
72
|
+
const processedNavigation = gatherContext.gatherMode === 'timespan' ?
|
|
73
|
+
undefined : await ProcessedNavigation.request(processedTrace, context);
|
|
73
74
|
|
|
74
75
|
const augmentedData = Object.assign({
|
|
75
76
|
networkRecords: await NetworkRecords.request(devtoolsLog, context),
|
|
@@ -9,6 +9,7 @@ const path = require('path');
|
|
|
9
9
|
const isDeepEqual = require('lodash.isequal');
|
|
10
10
|
const constants = require('./constants.js');
|
|
11
11
|
const Budget = require('./budget.js');
|
|
12
|
+
const ConfigPlugin = require('./config-plugin.js');
|
|
12
13
|
const Runner = require('../runner.js');
|
|
13
14
|
const i18n = require('../lib/i18n/i18n.js');
|
|
14
15
|
const validation = require('../fraggle-rock/config/validation.js');
|
|
@@ -17,6 +18,21 @@ const validation = require('../fraggle-rock/config/validation.js');
|
|
|
17
18
|
/** @typedef {typeof import('../audits/audit.js')} Audit */
|
|
18
19
|
/** @typedef {InstanceType<GathererConstructor>} Gatherer */
|
|
19
20
|
|
|
21
|
+
function isBundledEnvironment() {
|
|
22
|
+
// If we're in DevTools or LightRider, we are definitely bundled.
|
|
23
|
+
// TODO: refactor and delete `global.isDevtools`.
|
|
24
|
+
if (global.isDevtools || global.isLightrider) return true;
|
|
25
|
+
|
|
26
|
+
try {
|
|
27
|
+
// Not foolproof, but `lighthouse-logger` is a dependency of lighthouse that should always be resolvable.
|
|
28
|
+
// `require.resolve` will only throw in atypical/bundled environments.
|
|
29
|
+
require.resolve('lighthouse-logger');
|
|
30
|
+
return false;
|
|
31
|
+
} catch (err) {
|
|
32
|
+
return true;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
20
36
|
/**
|
|
21
37
|
* If any items with identical `path` properties are found in the input array,
|
|
22
38
|
* merge their `options` properties into the first instance and then discard any
|
|
@@ -224,9 +240,8 @@ function requireAudit(auditPath, coreAuditList, configDir) {
|
|
|
224
240
|
const coreAudit = coreAuditList.find(a => a === auditPathJs);
|
|
225
241
|
let requirePath = `../audits/${auditPath}`;
|
|
226
242
|
if (!coreAudit) {
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
// This is for pubads bundling.
|
|
243
|
+
if (isBundledEnvironment()) {
|
|
244
|
+
// This is for pubads bundling.
|
|
230
245
|
requirePath = auditPath;
|
|
231
246
|
} else {
|
|
232
247
|
// Otherwise, attempt to find it elsewhere. This throws if not found.
|
|
@@ -298,6 +313,34 @@ function resolveSettings(settingsJson = {}, overrides = undefined) {
|
|
|
298
313
|
}
|
|
299
314
|
|
|
300
315
|
|
|
316
|
+
/**
|
|
317
|
+
* @param {LH.Config.Json} configJSON
|
|
318
|
+
* @param {string | undefined} configDir
|
|
319
|
+
* @param {{plugins?: string[]} | undefined} flags
|
|
320
|
+
* @return {LH.Config.Json}
|
|
321
|
+
*/
|
|
322
|
+
function mergePlugins(configJSON, configDir, flags) {
|
|
323
|
+
const configPlugins = configJSON.plugins || [];
|
|
324
|
+
const flagPlugins = (flags && flags.plugins) || [];
|
|
325
|
+
const pluginNames = new Set([...configPlugins, ...flagPlugins]);
|
|
326
|
+
|
|
327
|
+
for (const pluginName of pluginNames) {
|
|
328
|
+
validation.assertValidPluginName(configJSON, pluginName);
|
|
329
|
+
|
|
330
|
+
// In bundled contexts, `resolveModulePath` will fail, so use the raw pluginName directly.
|
|
331
|
+
const pluginPath = isBundledEnvironment() ?
|
|
332
|
+
pluginName :
|
|
333
|
+
resolveModulePath(pluginName, configDir, 'plugin');
|
|
334
|
+
const rawPluginJson = require(pluginPath);
|
|
335
|
+
const pluginJson = ConfigPlugin.parsePlugin(rawPluginJson, pluginName);
|
|
336
|
+
|
|
337
|
+
configJSON = mergeConfigFragment(configJSON, pluginJson);
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
return configJSON;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
|
|
301
344
|
/**
|
|
302
345
|
* Turns a GathererJson into a GathererDefn which involves a few main steps:
|
|
303
346
|
* - Expanding the JSON shorthand the full definition format.
|
|
@@ -528,6 +571,7 @@ module.exports = {
|
|
|
528
571
|
mergeOptionsOfItems,
|
|
529
572
|
mergeConfigFragment,
|
|
530
573
|
mergeConfigFragmentArrayByKey,
|
|
574
|
+
mergePlugins,
|
|
531
575
|
resolveSettings,
|
|
532
576
|
resolveGathererToDefn,
|
|
533
577
|
resolveAuditsToDefns,
|
|
@@ -14,13 +14,12 @@ const validation = require('./../fraggle-rock/config/validation.js');
|
|
|
14
14
|
const log = require('lighthouse-logger');
|
|
15
15
|
const path = require('path');
|
|
16
16
|
const Runner = require('../runner.js');
|
|
17
|
-
const ConfigPlugin = require('./config-plugin.js');
|
|
18
17
|
const {
|
|
18
|
+
mergePlugins,
|
|
19
19
|
mergeConfigFragment,
|
|
20
20
|
resolveSettings,
|
|
21
21
|
resolveAuditsToDefns,
|
|
22
22
|
resolveGathererToDefn,
|
|
23
|
-
resolveModulePath,
|
|
24
23
|
deepClone,
|
|
25
24
|
deepCloneConfigJson,
|
|
26
25
|
} = require('./config-helpers.js');
|
|
@@ -40,6 +39,7 @@ const BASE_ARTIFACT_BLANKS = {
|
|
|
40
39
|
NetworkUserAgent: '',
|
|
41
40
|
BenchmarkIndex: '',
|
|
42
41
|
WebAppManifest: '',
|
|
42
|
+
GatherContext: '',
|
|
43
43
|
InstallabilityErrors: '',
|
|
44
44
|
Stacks: '',
|
|
45
45
|
traces: '',
|
|
@@ -148,22 +148,6 @@ function assertValidFlags(flags) {
|
|
|
148
148
|
}
|
|
149
149
|
}
|
|
150
150
|
|
|
151
|
-
/**
|
|
152
|
-
* Throws if pluginName is invalid or (somehow) collides with a category in the
|
|
153
|
-
* configJSON being added to.
|
|
154
|
-
* @param {LH.Config.Json} configJSON
|
|
155
|
-
* @param {string} pluginName
|
|
156
|
-
*/
|
|
157
|
-
function assertValidPluginName(configJSON, pluginName) {
|
|
158
|
-
if (!pluginName.startsWith('lighthouse-plugin-')) {
|
|
159
|
-
throw new Error(`plugin name '${pluginName}' does not start with 'lighthouse-plugin-'`);
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
if (configJSON.categories && configJSON.categories[pluginName]) {
|
|
163
|
-
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
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
|
|
167
151
|
|
|
168
152
|
/**
|
|
169
153
|
* @implements {LH.Config.Config}
|
|
@@ -203,7 +187,7 @@ class Config {
|
|
|
203
187
|
const configDir = configPath ? path.dirname(configPath) : undefined;
|
|
204
188
|
|
|
205
189
|
// Validate and merge in plugins (if any).
|
|
206
|
-
configJSON =
|
|
190
|
+
configJSON = mergePlugins(configJSON, configDir, flags);
|
|
207
191
|
|
|
208
192
|
if (flags) {
|
|
209
193
|
assertValidFlags(flags);
|
|
@@ -294,33 +278,6 @@ class Config {
|
|
|
294
278
|
return mergeConfigFragment(baseJSON, extendJSON);
|
|
295
279
|
}
|
|
296
280
|
|
|
297
|
-
/**
|
|
298
|
-
* @param {LH.Config.Json} configJSON
|
|
299
|
-
* @param {LH.Flags=} flags
|
|
300
|
-
* @param {string=} configDir
|
|
301
|
-
* @return {LH.Config.Json}
|
|
302
|
-
*/
|
|
303
|
-
static mergePlugins(configJSON, flags, configDir) {
|
|
304
|
-
const configPlugins = configJSON.plugins || [];
|
|
305
|
-
const flagPlugins = (flags && flags.plugins) || [];
|
|
306
|
-
const pluginNames = new Set([...configPlugins, ...flagPlugins]);
|
|
307
|
-
|
|
308
|
-
for (const pluginName of pluginNames) {
|
|
309
|
-
assertValidPluginName(configJSON, pluginName);
|
|
310
|
-
|
|
311
|
-
// TODO: refactor and delete `global.isDevtools`.
|
|
312
|
-
const pluginPath = global.isDevtools || global.isLightrider ?
|
|
313
|
-
pluginName :
|
|
314
|
-
resolveModulePath(pluginName, configDir, 'plugin');
|
|
315
|
-
const rawPluginJson = require(pluginPath);
|
|
316
|
-
const pluginJson = ConfigPlugin.parsePlugin(rawPluginJson, pluginName);
|
|
317
|
-
|
|
318
|
-
configJSON = Config.extendConfigJSON(configJSON, pluginJson);
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
return configJSON;
|
|
322
|
-
}
|
|
323
|
-
|
|
324
281
|
/**
|
|
325
282
|
* @param {LH.Config.Json['passes']} passes
|
|
326
283
|
* @return {?Array<Required<LH.Config.PassJson>>}
|
|
@@ -24,11 +24,14 @@ const {
|
|
|
24
24
|
resolveSettings,
|
|
25
25
|
resolveAuditsToDefns,
|
|
26
26
|
resolveGathererToDefn,
|
|
27
|
+
mergePlugins,
|
|
27
28
|
mergeConfigFragment,
|
|
28
29
|
mergeConfigFragmentArrayByKey,
|
|
29
30
|
} = require('../../config/config-helpers.js');
|
|
30
31
|
const defaultConfigPath = path.join(__dirname, './default-config.js');
|
|
31
32
|
|
|
33
|
+
/** @typedef {Omit<LH.Config.FRContext, 'gatherMode'> & {gatherMode: LH.Gatherer.GatherMode}} ConfigContext */
|
|
34
|
+
|
|
32
35
|
/**
|
|
33
36
|
* @param {LH.Config.Json|undefined} configJSON
|
|
34
37
|
* @param {{configPath?: string}} context
|
|
@@ -132,7 +135,7 @@ function resolveArtifactsToDefns(artifacts, configDir) {
|
|
|
132
135
|
const coreGathererList = Runner.getGathererList();
|
|
133
136
|
const artifactDefns = artifacts.map(artifactJson => {
|
|
134
137
|
/** @type {LH.Config.GathererJson} */
|
|
135
|
-
// @ts-expect-error
|
|
138
|
+
// @ts-expect-error - remove when legacy runner path is removed.
|
|
136
139
|
const gathererJson = artifactJson.gatherer;
|
|
137
140
|
|
|
138
141
|
const gatherer = resolveGathererToDefn(gathererJson, coreGathererList, configDir);
|
|
@@ -158,6 +161,20 @@ function resolveArtifactsToDefns(artifacts, configDir) {
|
|
|
158
161
|
return artifactDefns;
|
|
159
162
|
}
|
|
160
163
|
|
|
164
|
+
/**
|
|
165
|
+
* Overrides the settings that may not apply to the chosen gather mode.
|
|
166
|
+
*
|
|
167
|
+
* @param {LH.Config.Settings} settings
|
|
168
|
+
* @param {ConfigContext} context
|
|
169
|
+
*/
|
|
170
|
+
function overrideSettingsForGatherMode(settings, context) {
|
|
171
|
+
if (context.gatherMode === 'timespan') {
|
|
172
|
+
if (settings.throttlingMethod === 'simulate') {
|
|
173
|
+
settings.throttlingMethod = 'devtools';
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
161
178
|
/**
|
|
162
179
|
* Overrides the quiet windows when throttlingMethod requires observation.
|
|
163
180
|
*
|
|
@@ -224,7 +241,7 @@ function resolveNavigationsToDefns(navigations, artifactDefns, settings) {
|
|
|
224
241
|
|
|
225
242
|
/**
|
|
226
243
|
* @param {LH.Config.Json|undefined} configJSON
|
|
227
|
-
* @param {
|
|
244
|
+
* @param {ConfigContext} context
|
|
228
245
|
* @return {{config: LH.Config.FRConfig, warnings: string[]}}
|
|
229
246
|
*/
|
|
230
247
|
function initializeConfig(configJSON, context) {
|
|
@@ -234,10 +251,11 @@ function initializeConfig(configJSON, context) {
|
|
|
234
251
|
let {configWorkingCopy, configDir} = resolveWorkingCopy(configJSON, context); // eslint-disable-line prefer-const
|
|
235
252
|
|
|
236
253
|
configWorkingCopy = resolveExtensions(configWorkingCopy);
|
|
237
|
-
|
|
238
|
-
// TODO(FR-COMPAT): handle config plugins
|
|
254
|
+
configWorkingCopy = mergePlugins(configWorkingCopy, configDir, context.settingsOverrides);
|
|
239
255
|
|
|
240
256
|
const settings = resolveSettings(configWorkingCopy.settings || {}, context.settingsOverrides);
|
|
257
|
+
overrideSettingsForGatherMode(settings, context);
|
|
258
|
+
|
|
241
259
|
const artifacts = resolveArtifactsToDefns(configWorkingCopy.artifacts, configDir);
|
|
242
260
|
const navigations = resolveNavigationsToDefns(configWorkingCopy.navigations, artifacts, settings);
|
|
243
261
|
|