lighthouse 11.1.0-dev.20231002 → 11.1.0-dev.20231004
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/test/smokehouse/__snapshots__/report-assert-test.js.snap +8 -0
- package/core/audits/audit.js +21 -11
- package/core/audits/bf-cache.js +1 -0
- package/core/audits/bootup-time.js +4 -1
- package/core/audits/byte-efficiency/byte-efficiency-audit.js +1 -1
- package/core/audits/byte-efficiency/duplicated-javascript.js +1 -1
- package/core/audits/byte-efficiency/efficient-animated-content.js +1 -1
- package/core/audits/byte-efficiency/legacy-javascript.js +1 -1
- package/core/audits/byte-efficiency/modern-image-formats.js +1 -1
- package/core/audits/byte-efficiency/offscreen-images.js +1 -1
- package/core/audits/byte-efficiency/render-blocking-resources.js +2 -3
- package/core/audits/byte-efficiency/total-byte-weight.js +3 -1
- package/core/audits/byte-efficiency/unminified-css.js +1 -1
- package/core/audits/byte-efficiency/unminified-javascript.js +1 -1
- package/core/audits/byte-efficiency/unused-css-rules.js +1 -1
- package/core/audits/byte-efficiency/unused-javascript.js +1 -1
- package/core/audits/byte-efficiency/uses-long-cache-ttl.js +3 -8
- package/core/audits/byte-efficiency/uses-optimized-images.js +1 -1
- package/core/audits/byte-efficiency/uses-responsive-images-snapshot.js +1 -0
- package/core/audits/byte-efficiency/uses-responsive-images.js +1 -1
- package/core/audits/byte-efficiency/uses-text-compression.js +1 -1
- package/core/audits/csp-xss.js +1 -1
- package/core/audits/dobetterweb/dom-size.js +3 -1
- package/core/audits/dobetterweb/no-document-write.js +1 -0
- package/core/audits/dobetterweb/uses-http2.js +2 -3
- package/core/audits/dobetterweb/uses-passive-event-listeners.js +1 -0
- package/core/audits/font-display.js +1 -0
- package/core/audits/installable-manifest.js +2 -2
- package/core/audits/largest-contentful-paint-element.js +3 -2
- package/core/audits/layout-shift-elements.js +8 -4
- package/core/audits/lcp-lazy-loaded.js +1 -0
- package/core/audits/long-tasks.js +8 -1
- package/core/audits/mainthread-work-breakdown.js +3 -1
- package/core/audits/metrics/cumulative-layout-shift.js +4 -1
- package/core/audits/metrics/first-contentful-paint.js +1 -0
- package/core/audits/metrics/largest-contentful-paint.js +1 -0
- package/core/audits/metrics/speed-index.js +1 -0
- package/core/audits/metrics/total-blocking-time.js +1 -0
- package/core/audits/prioritize-lcp-image.js +2 -3
- package/core/audits/redirects.js +2 -3
- package/core/audits/server-response-time.js +2 -0
- package/core/audits/third-party-facades.js +1 -0
- package/core/audits/third-party-summary.js +5 -1
- package/core/audits/unsized-images.js +1 -1
- package/core/audits/uses-rel-preconnect.js +2 -3
- package/core/audits/uses-rel-preload.js +2 -3
- package/core/audits/viewport.js +1 -0
- package/core/audits/work-during-interaction.js +6 -2
- package/core/gather/session.js +2 -0
- package/core/lib/csp-evaluator.js +13 -13
- package/dist/report/bundle.esm.js +12 -89
- package/dist/report/flow.js +17 -94
- package/dist/report/standalone.js +13 -90
- package/package.json +4 -4
- package/report/assets/styles.css +1 -78
- package/report/assets/templates.html +0 -35
- package/report/renderer/components.d.ts +2 -2
- package/report/renderer/components.js +2 -51
- package/report/renderer/performance-category-renderer.d.ts +16 -18
- package/report/renderer/performance-category-renderer.js +90 -102
- package/shared/localization/locales/en-US.json +14 -14
- package/shared/localization/locales/en-XL.json +14 -14
- package/shared/util.d.ts +22 -0
- package/shared/util.js +31 -0
- package/types/audit.d.ts +5 -1
- package/types/internal/rxjs.d.ts +3 -0
- package/types/lhr/audit-result.d.ts +16 -0
- /package/{core/lib → shared}/statistics.d.ts +0 -0
- /package/{core/lib → shared}/statistics.js +0 -0
|
@@ -20,6 +20,10 @@ exports[`getAssertionReport works (multiple failing) 1`] = `
|
|
|
20
20
|
\\"numericValue\\": 0.13570762803819444,
|
|
21
21
|
\\"numericUnit\\": \\"unitless\\",
|
|
22
22
|
\\"displayValue\\": \\"0.136\\",
|
|
23
|
+
\\"scoringOptions\\": {
|
|
24
|
+
\\"p10\\": 0.1,
|
|
25
|
+
\\"median\\": 0.25
|
|
26
|
+
},
|
|
23
27
|
\\"details\\": {
|
|
24
28
|
\\"type\\": \\"debugdata\\",
|
|
25
29
|
\\"items\\": [
|
|
@@ -46,6 +50,10 @@ exports[`getAssertionReport works (trivial failing) 1`] = `
|
|
|
46
50
|
\\"numericValue\\": 0.13570762803819444,
|
|
47
51
|
\\"numericUnit\\": \\"unitless\\",
|
|
48
52
|
\\"displayValue\\": \\"0.136\\",
|
|
53
|
+
\\"scoringOptions\\": {
|
|
54
|
+
\\"p10\\": 0.1,
|
|
55
|
+
\\"median\\": 0.25
|
|
56
|
+
},
|
|
49
57
|
\\"details\\": {
|
|
50
58
|
\\"type\\": \\"debugdata\\",
|
|
51
59
|
\\"items\\": [
|
package/core/audits/audit.js
CHANGED
|
@@ -6,7 +6,6 @@
|
|
|
6
6
|
|
|
7
7
|
import * as LH from '../../types/lh.js';
|
|
8
8
|
import {isUnderTest} from '../lib/lh-env.js';
|
|
9
|
-
import * as statistics from '../lib/statistics.js';
|
|
10
9
|
import {Util} from '../../shared/util.js';
|
|
11
10
|
|
|
12
11
|
const DEFAULT_PASS = 'defaultPass';
|
|
@@ -50,6 +49,7 @@ class Audit {
|
|
|
50
49
|
static get SCORING_MODES() {
|
|
51
50
|
return {
|
|
52
51
|
NUMERIC: 'numeric',
|
|
52
|
+
METRIC_SAVINGS: 'metricSavings',
|
|
53
53
|
BINARY: 'binary',
|
|
54
54
|
MANUAL: 'manual',
|
|
55
55
|
INFORMATIVE: 'informative',
|
|
@@ -105,14 +105,7 @@ class Audit {
|
|
|
105
105
|
* @return {number}
|
|
106
106
|
*/
|
|
107
107
|
static computeLogNormalScore(controlPoints, value) {
|
|
108
|
-
|
|
109
|
-
// Add a boost to scores of 90+, linearly ramping from 0 at 0.9 to half a
|
|
110
|
-
// point (0.005) at 1. Expands scores in (0.9, 1] to (0.9, 1.005], so more top
|
|
111
|
-
// scores will be a perfect 1 after the two-digit `Math.floor()` rounding below.
|
|
112
|
-
if (percentile > 0.9) { // getLogNormalScore ensures `percentile` can't exceed 1.
|
|
113
|
-
percentile += 0.05 * (percentile - 0.9);
|
|
114
|
-
}
|
|
115
|
-
return Math.floor(percentile * 100) / 100;
|
|
108
|
+
return Util.computeLogNormalScore(controlPoints, value);
|
|
116
109
|
}
|
|
117
110
|
|
|
118
111
|
/**
|
|
@@ -329,7 +322,8 @@ class Audit {
|
|
|
329
322
|
*/
|
|
330
323
|
static _normalizeAuditScore(score, scoreDisplayMode, auditId) {
|
|
331
324
|
if (scoreDisplayMode !== Audit.SCORING_MODES.BINARY &&
|
|
332
|
-
scoreDisplayMode !== Audit.SCORING_MODES.NUMERIC
|
|
325
|
+
scoreDisplayMode !== Audit.SCORING_MODES.NUMERIC &&
|
|
326
|
+
scoreDisplayMode !== Audit.SCORING_MODES.METRIC_SAVINGS) {
|
|
333
327
|
return null;
|
|
334
328
|
}
|
|
335
329
|
|
|
@@ -371,6 +365,7 @@ class Audit {
|
|
|
371
365
|
|
|
372
366
|
// Default to binary scoring.
|
|
373
367
|
let scoreDisplayMode = audit.meta.scoreDisplayMode || Audit.SCORING_MODES.BINARY;
|
|
368
|
+
let score = product.score;
|
|
374
369
|
|
|
375
370
|
// But override if product contents require it.
|
|
376
371
|
if (product.errorMessage !== undefined) {
|
|
@@ -379,9 +374,21 @@ class Audit {
|
|
|
379
374
|
} else if (product.notApplicable) {
|
|
380
375
|
// Audit was determined to not apply to the page.
|
|
381
376
|
scoreDisplayMode = Audit.SCORING_MODES.NOT_APPLICABLE;
|
|
377
|
+
} else if (product.scoreDisplayMode) {
|
|
378
|
+
scoreDisplayMode = product.scoreDisplayMode;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
if (scoreDisplayMode === Audit.SCORING_MODES.METRIC_SAVINGS) {
|
|
382
|
+
if (score && score >= Util.PASS_THRESHOLD) {
|
|
383
|
+
score = 1;
|
|
384
|
+
} else if (Object.values(product.metricSavings || {}).some(v => v)) {
|
|
385
|
+
score = 0;
|
|
386
|
+
} else {
|
|
387
|
+
score = 0.5;
|
|
388
|
+
}
|
|
382
389
|
}
|
|
383
390
|
|
|
384
|
-
|
|
391
|
+
score = Audit._normalizeAuditScore(score, scoreDisplayMode, audit.meta.id);
|
|
385
392
|
|
|
386
393
|
let auditTitle = audit.meta.title;
|
|
387
394
|
if (audit.meta.failureTitle) {
|
|
@@ -411,8 +418,11 @@ class Audit {
|
|
|
411
418
|
errorMessage: product.errorMessage,
|
|
412
419
|
errorStack: product.errorStack,
|
|
413
420
|
warnings: product.warnings,
|
|
421
|
+
scoringOptions: product.scoringOptions,
|
|
422
|
+
metricSavings: product.metricSavings,
|
|
414
423
|
|
|
415
424
|
details: product.details,
|
|
425
|
+
guidanceLevel: audit.meta.guidanceLevel,
|
|
416
426
|
};
|
|
417
427
|
}
|
|
418
428
|
|
package/core/audits/bf-cache.js
CHANGED
|
@@ -14,6 +14,7 @@ import {MainThreadTasks} from '../computed/main-thread-tasks.js';
|
|
|
14
14
|
import {getExecutionTimingsByURL} from '../lib/tracehouse/task-summary.js';
|
|
15
15
|
import {TBTImpactTasks} from '../computed/tbt-impact-tasks.js';
|
|
16
16
|
import {Sentry} from '../lib/sentry.js';
|
|
17
|
+
import {Util} from '../../shared/util.js';
|
|
17
18
|
|
|
18
19
|
const UIStrings = {
|
|
19
20
|
/** Title of a diagnostic audit that provides detail on the time spent executing javascript files during the load. This descriptive title is shown to users when the amount is acceptable and no user action is required. */
|
|
@@ -47,7 +48,7 @@ class BootupTime extends Audit {
|
|
|
47
48
|
title: str_(UIStrings.title),
|
|
48
49
|
failureTitle: str_(UIStrings.failureTitle),
|
|
49
50
|
description: str_(UIStrings.description),
|
|
50
|
-
scoreDisplayMode: Audit.SCORING_MODES.
|
|
51
|
+
scoreDisplayMode: Audit.SCORING_MODES.METRIC_SAVINGS,
|
|
51
52
|
guidanceLevel: 1,
|
|
52
53
|
requiredArtifacts: ['traces', 'devtoolsLogs', 'URL', 'GatherContext'],
|
|
53
54
|
};
|
|
@@ -169,6 +170,8 @@ class BootupTime extends Audit {
|
|
|
169
170
|
|
|
170
171
|
return {
|
|
171
172
|
score,
|
|
173
|
+
scoreDisplayMode: score >= Util.PASS_THRESHOLD ? Audit.SCORING_MODES.INFORMATIVE : undefined,
|
|
174
|
+
notApplicable: !results.length,
|
|
172
175
|
numericValue: totalBootupTime,
|
|
173
176
|
numericUnit: 'millisecond',
|
|
174
177
|
displayValue: totalBootupTime > 0 ?
|
|
@@ -309,7 +309,7 @@ class ByteEfficiencyAudit extends Audit {
|
|
|
309
309
|
displayValue,
|
|
310
310
|
numericValue: wastedMs,
|
|
311
311
|
numericUnit: 'millisecond',
|
|
312
|
-
score:
|
|
312
|
+
score: results.length ? 0 : 1,
|
|
313
313
|
details,
|
|
314
314
|
metricSavings,
|
|
315
315
|
};
|
|
@@ -46,7 +46,7 @@ class DuplicatedJavascript extends ByteEfficiencyAudit {
|
|
|
46
46
|
id: 'duplicated-javascript',
|
|
47
47
|
title: str_(UIStrings.title),
|
|
48
48
|
description: str_(UIStrings.description),
|
|
49
|
-
scoreDisplayMode: ByteEfficiencyAudit.SCORING_MODES.
|
|
49
|
+
scoreDisplayMode: ByteEfficiencyAudit.SCORING_MODES.METRIC_SAVINGS,
|
|
50
50
|
guidanceLevel: 2,
|
|
51
51
|
requiredArtifacts: ['devtoolsLogs', 'traces', 'SourceMaps', 'Scripts',
|
|
52
52
|
'GatherContext', 'URL'],
|
|
@@ -36,7 +36,7 @@ class EfficientAnimatedContent extends ByteEfficiencyAudit {
|
|
|
36
36
|
id: 'efficient-animated-content',
|
|
37
37
|
title: str_(UIStrings.title),
|
|
38
38
|
description: str_(UIStrings.description),
|
|
39
|
-
scoreDisplayMode: ByteEfficiencyAudit.SCORING_MODES.
|
|
39
|
+
scoreDisplayMode: ByteEfficiencyAudit.SCORING_MODES.METRIC_SAVINGS,
|
|
40
40
|
guidanceLevel: 3,
|
|
41
41
|
requiredArtifacts: ['devtoolsLogs', 'traces', 'GatherContext', 'URL'],
|
|
42
42
|
};
|
|
@@ -115,7 +115,7 @@ class LegacyJavascript extends ByteEfficiencyAudit {
|
|
|
115
115
|
static get meta() {
|
|
116
116
|
return {
|
|
117
117
|
id: 'legacy-javascript',
|
|
118
|
-
scoreDisplayMode: ByteEfficiencyAudit.SCORING_MODES.
|
|
118
|
+
scoreDisplayMode: ByteEfficiencyAudit.SCORING_MODES.METRIC_SAVINGS,
|
|
119
119
|
description: str_(UIStrings.description),
|
|
120
120
|
title: str_(UIStrings.title),
|
|
121
121
|
guidanceLevel: 2,
|
|
@@ -34,7 +34,7 @@ class ModernImageFormats extends ByteEfficiencyAudit {
|
|
|
34
34
|
id: 'modern-image-formats',
|
|
35
35
|
title: str_(UIStrings.title),
|
|
36
36
|
description: str_(UIStrings.description),
|
|
37
|
-
scoreDisplayMode: ByteEfficiencyAudit.SCORING_MODES.
|
|
37
|
+
scoreDisplayMode: ByteEfficiencyAudit.SCORING_MODES.METRIC_SAVINGS,
|
|
38
38
|
guidanceLevel: 3,
|
|
39
39
|
requiredArtifacts: ['OptimizedImages', 'devtoolsLogs', 'traces', 'URL', 'GatherContext',
|
|
40
40
|
'ImageElements'],
|
|
@@ -48,7 +48,7 @@ class OffscreenImages extends ByteEfficiencyAudit {
|
|
|
48
48
|
id: 'offscreen-images',
|
|
49
49
|
title: str_(UIStrings.title),
|
|
50
50
|
description: str_(UIStrings.description),
|
|
51
|
-
scoreDisplayMode: ByteEfficiencyAudit.SCORING_MODES.
|
|
51
|
+
scoreDisplayMode: ByteEfficiencyAudit.SCORING_MODES.METRIC_SAVINGS,
|
|
52
52
|
supportedModes: ['navigation'],
|
|
53
53
|
guidanceLevel: 2,
|
|
54
54
|
requiredArtifacts: ['ImageElements', 'ViewportDimensions', 'GatherContext', 'devtoolsLogs',
|
|
@@ -12,7 +12,6 @@
|
|
|
12
12
|
import {Audit} from '../audit.js';
|
|
13
13
|
import * as i18n from '../../lib/i18n/i18n.js';
|
|
14
14
|
import {BaseNode} from '../../lib/dependency-graph/base-node.js';
|
|
15
|
-
import {ByteEfficiencyAudit} from './byte-efficiency-audit.js';
|
|
16
15
|
import {UnusedCSS} from '../../computed/unused-css.js';
|
|
17
16
|
import {NetworkRequest} from '../../lib/network-request.js';
|
|
18
17
|
import {ProcessedNavigation} from '../../computed/processed-navigation.js';
|
|
@@ -113,7 +112,7 @@ class RenderBlockingResources extends Audit {
|
|
|
113
112
|
id: 'render-blocking-resources',
|
|
114
113
|
title: str_(UIStrings.title),
|
|
115
114
|
supportedModes: ['navigation'],
|
|
116
|
-
scoreDisplayMode: Audit.SCORING_MODES.
|
|
115
|
+
scoreDisplayMode: Audit.SCORING_MODES.METRIC_SAVINGS,
|
|
117
116
|
description: str_(UIStrings.description),
|
|
118
117
|
guidanceLevel: 2,
|
|
119
118
|
// TODO: look into adding an `optionalArtifacts` property that captures the non-required nature
|
|
@@ -296,7 +295,7 @@ class RenderBlockingResources extends Audit {
|
|
|
296
295
|
|
|
297
296
|
return {
|
|
298
297
|
displayValue,
|
|
299
|
-
score:
|
|
298
|
+
score: results.length ? 0 : 1,
|
|
300
299
|
numericValue: wastedMs,
|
|
301
300
|
numericUnit: 'millisecond',
|
|
302
301
|
details,
|
|
@@ -8,6 +8,7 @@ import {Audit} from '../audit.js';
|
|
|
8
8
|
import * as i18n from '../../lib/i18n/i18n.js';
|
|
9
9
|
import {NetworkRequest} from '../../lib/network-request.js';
|
|
10
10
|
import {NetworkRecords} from '../../computed/network-records.js';
|
|
11
|
+
import {Util} from '../../../shared/util.js';
|
|
11
12
|
|
|
12
13
|
const UIStrings = {
|
|
13
14
|
/** Title of a diagnostic audit that provides detail on large network resources required during page load. 'Payloads' is roughly equivalent to 'resources'. This descriptive title is shown to users when the amount is acceptable and no user action is required. */
|
|
@@ -34,7 +35,7 @@ class TotalByteWeight extends Audit {
|
|
|
34
35
|
title: str_(UIStrings.title),
|
|
35
36
|
failureTitle: str_(UIStrings.failureTitle),
|
|
36
37
|
description: str_(UIStrings.description),
|
|
37
|
-
scoreDisplayMode: Audit.SCORING_MODES.
|
|
38
|
+
scoreDisplayMode: Audit.SCORING_MODES.METRIC_SAVINGS,
|
|
38
39
|
guidanceLevel: 1,
|
|
39
40
|
requiredArtifacts: ['devtoolsLogs'],
|
|
40
41
|
};
|
|
@@ -98,6 +99,7 @@ class TotalByteWeight extends Audit {
|
|
|
98
99
|
|
|
99
100
|
return {
|
|
100
101
|
score,
|
|
102
|
+
scoreDisplayMode: score >= Util.PASS_THRESHOLD ? Audit.SCORING_MODES.INFORMATIVE : undefined,
|
|
101
103
|
numericValue: totalBytes,
|
|
102
104
|
numericUnit: 'byte',
|
|
103
105
|
displayValue: str_(UIStrings.displayValue, {totalBytes}),
|
|
@@ -34,7 +34,7 @@ class UnminifiedCSS extends ByteEfficiencyAudit {
|
|
|
34
34
|
id: 'unminified-css',
|
|
35
35
|
title: str_(UIStrings.title),
|
|
36
36
|
description: str_(UIStrings.description),
|
|
37
|
-
scoreDisplayMode: ByteEfficiencyAudit.SCORING_MODES.
|
|
37
|
+
scoreDisplayMode: ByteEfficiencyAudit.SCORING_MODES.METRIC_SAVINGS,
|
|
38
38
|
guidanceLevel: 3,
|
|
39
39
|
requiredArtifacts: ['CSSUsage', 'devtoolsLogs', 'traces', 'URL', 'GatherContext'],
|
|
40
40
|
};
|
|
@@ -42,7 +42,7 @@ class UnminifiedJavaScript extends ByteEfficiencyAudit {
|
|
|
42
42
|
id: 'unminified-javascript',
|
|
43
43
|
title: str_(UIStrings.title),
|
|
44
44
|
description: str_(UIStrings.description),
|
|
45
|
-
scoreDisplayMode: ByteEfficiencyAudit.SCORING_MODES.
|
|
45
|
+
scoreDisplayMode: ByteEfficiencyAudit.SCORING_MODES.METRIC_SAVINGS,
|
|
46
46
|
guidanceLevel: 3,
|
|
47
47
|
requiredArtifacts: ['Scripts', 'devtoolsLogs', 'traces', 'GatherContext', 'URL'],
|
|
48
48
|
};
|
|
@@ -32,7 +32,7 @@ class UnusedCSSRules extends ByteEfficiencyAudit {
|
|
|
32
32
|
id: 'unused-css-rules',
|
|
33
33
|
title: str_(UIStrings.title),
|
|
34
34
|
description: str_(UIStrings.description),
|
|
35
|
-
scoreDisplayMode: ByteEfficiencyAudit.SCORING_MODES.
|
|
35
|
+
scoreDisplayMode: ByteEfficiencyAudit.SCORING_MODES.METRIC_SAVINGS,
|
|
36
36
|
guidanceLevel: 2,
|
|
37
37
|
requiredArtifacts: ['CSSUsage', 'URL', 'devtoolsLogs', 'traces', 'GatherContext'],
|
|
38
38
|
};
|
|
@@ -66,7 +66,7 @@ class UnusedJavaScript extends ByteEfficiencyAudit {
|
|
|
66
66
|
id: 'unused-javascript',
|
|
67
67
|
title: str_(UIStrings.title),
|
|
68
68
|
description: str_(UIStrings.description),
|
|
69
|
-
scoreDisplayMode: ByteEfficiencyAudit.SCORING_MODES.
|
|
69
|
+
scoreDisplayMode: ByteEfficiencyAudit.SCORING_MODES.METRIC_SAVINGS,
|
|
70
70
|
guidanceLevel: 2,
|
|
71
71
|
requiredArtifacts: ['JsUsage', 'Scripts', 'SourceMaps', 'GatherContext',
|
|
72
72
|
'devtoolsLogs', 'traces', 'URL'],
|
|
@@ -9,7 +9,7 @@ import parseCacheControl from 'parse-cache-control';
|
|
|
9
9
|
import {Audit} from '../audit.js';
|
|
10
10
|
import {NetworkRequest} from '../../lib/network-request.js';
|
|
11
11
|
import UrlUtils from '../../lib/url-utils.js';
|
|
12
|
-
import {linearInterpolation} from '
|
|
12
|
+
import {linearInterpolation} from '../../../shared/statistics.js';
|
|
13
13
|
import * as i18n from '../../lib/i18n/i18n.js';
|
|
14
14
|
import {NetworkRecords} from '../../computed/network-records.js';
|
|
15
15
|
|
|
@@ -44,7 +44,7 @@ class CacheHeaders extends Audit {
|
|
|
44
44
|
title: str_(UIStrings.title),
|
|
45
45
|
failureTitle: str_(UIStrings.failureTitle),
|
|
46
46
|
description: str_(UIStrings.description),
|
|
47
|
-
scoreDisplayMode: Audit.SCORING_MODES.
|
|
47
|
+
scoreDisplayMode: Audit.SCORING_MODES.METRIC_SAVINGS,
|
|
48
48
|
guidanceLevel: 3,
|
|
49
49
|
requiredArtifacts: ['devtoolsLogs'],
|
|
50
50
|
};
|
|
@@ -266,11 +266,6 @@ class CacheHeaders extends Audit {
|
|
|
266
266
|
a.url.localeCompare(b.url);
|
|
267
267
|
});
|
|
268
268
|
|
|
269
|
-
const score = Audit.computeLogNormalScore(
|
|
270
|
-
{p10: context.options.p10, median: context.options.median},
|
|
271
|
-
totalWastedBytes
|
|
272
|
-
);
|
|
273
|
-
|
|
274
269
|
/** @type {LH.Audit.Details.Table['headings']} */
|
|
275
270
|
const headings = [
|
|
276
271
|
{key: 'url', valueType: 'url', label: str_(i18n.UIStrings.columnURL)},
|
|
@@ -285,7 +280,7 @@ class CacheHeaders extends Audit {
|
|
|
285
280
|
{wastedBytes: totalWastedBytes, sortedBy: ['totalBytes'], skipSumming: ['cacheLifetimeMs']});
|
|
286
281
|
|
|
287
282
|
return {
|
|
288
|
-
score,
|
|
283
|
+
score: results.length ? 0 : 1,
|
|
289
284
|
numericValue: totalWastedBytes,
|
|
290
285
|
numericUnit: 'byte',
|
|
291
286
|
displayValue: str_(UIStrings.displayValue, {itemCount: results.length}),
|
|
@@ -34,7 +34,7 @@ class UsesOptimizedImages extends ByteEfficiencyAudit {
|
|
|
34
34
|
id: 'uses-optimized-images',
|
|
35
35
|
title: str_(UIStrings.title),
|
|
36
36
|
description: str_(UIStrings.description),
|
|
37
|
-
scoreDisplayMode: ByteEfficiencyAudit.SCORING_MODES.
|
|
37
|
+
scoreDisplayMode: ByteEfficiencyAudit.SCORING_MODES.METRIC_SAVINGS,
|
|
38
38
|
guidanceLevel: 2,
|
|
39
39
|
requiredArtifacts: ['OptimizedImages', 'ImageElements', 'GatherContext', 'devtoolsLogs',
|
|
40
40
|
'traces', 'URL'],
|
|
@@ -42,6 +42,7 @@ class UsesResponsiveImagesSnapshot extends Audit {
|
|
|
42
42
|
title: str_(UIStrings.title),
|
|
43
43
|
failureTitle: str_(UIStrings.failureTitle),
|
|
44
44
|
description: UsesResponsiveImages.str_(UsesResponsiveImages.UIStrings.description),
|
|
45
|
+
scoreDisplayMode: Audit.SCORING_MODES.METRIC_SAVINGS,
|
|
45
46
|
supportedModes: ['snapshot'],
|
|
46
47
|
guidanceLevel: 2,
|
|
47
48
|
requiredArtifacts: ['ImageElements', 'ViewportDimensions'],
|
|
@@ -45,7 +45,7 @@ class UsesResponsiveImages extends ByteEfficiencyAudit {
|
|
|
45
45
|
id: 'uses-responsive-images',
|
|
46
46
|
title: str_(UIStrings.title),
|
|
47
47
|
description: str_(UIStrings.description),
|
|
48
|
-
scoreDisplayMode: ByteEfficiencyAudit.SCORING_MODES.
|
|
48
|
+
scoreDisplayMode: ByteEfficiencyAudit.SCORING_MODES.METRIC_SAVINGS,
|
|
49
49
|
guidanceLevel: 2,
|
|
50
50
|
requiredArtifacts: ['ImageElements', 'ViewportDimensions', 'GatherContext',
|
|
51
51
|
'devtoolsLogs', 'traces', 'URL'],
|
|
@@ -36,7 +36,7 @@ class ResponsesAreCompressed extends ByteEfficiencyAudit {
|
|
|
36
36
|
id: 'uses-text-compression',
|
|
37
37
|
title: str_(UIStrings.title),
|
|
38
38
|
description: str_(UIStrings.description),
|
|
39
|
-
scoreDisplayMode: ByteEfficiencyAudit.SCORING_MODES.
|
|
39
|
+
scoreDisplayMode: ByteEfficiencyAudit.SCORING_MODES.METRIC_SAVINGS,
|
|
40
40
|
guidanceLevel: 3,
|
|
41
41
|
requiredArtifacts: ['ResponseCompression', 'GatherContext', 'devtoolsLogs', 'traces', 'URL'],
|
|
42
42
|
};
|
package/core/audits/csp-xss.js
CHANGED
|
@@ -21,7 +21,7 @@ const UIStrings = {
|
|
|
21
21
|
/** Summary text for the results of a Lighthouse audit that evaluates the security of a page's CSP. This is displayed if no CSP is being enforced. "CSP" stands for "Content Security Policy". "CSP" does not need to be translated. */
|
|
22
22
|
noCsp: 'No CSP found in enforcement mode',
|
|
23
23
|
/** Message shown when one or more CSPs are defined in a <meta> tag. Shown in a table with a list of other CSP bypasses and warnings. "CSP" stands for "Content Security Policy". "CSP" and "HTTP" do not need to be translated. */
|
|
24
|
-
metaTagMessage: 'The page contains a CSP defined in a
|
|
24
|
+
metaTagMessage: 'The page contains a CSP defined in a `<meta>` tag. ' +
|
|
25
25
|
'Consider moving the CSP to an HTTP header or ' +
|
|
26
26
|
'defining another strict CSP in an HTTP header.',
|
|
27
27
|
/** Label for a column in a data table; entries will be a directive of a CSP. "CSP" stands for "Content Security Policy". */
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
import {Audit} from '../audit.js';
|
|
15
15
|
import * as i18n from '../../lib/i18n/i18n.js';
|
|
16
16
|
import {TBTImpactTasks} from '../../computed/tbt-impact-tasks.js';
|
|
17
|
+
import {Util} from '../../../shared/util.js';
|
|
17
18
|
|
|
18
19
|
const UIStrings = {
|
|
19
20
|
/** Title of a diagnostic audit that provides detail on the size of the web page's DOM. The size of a DOM is characterized by the total number of DOM elements and greatest DOM depth. This descriptive title is shown to users when the amount is acceptable and no user action is required. */
|
|
@@ -53,7 +54,7 @@ class DOMSize extends Audit {
|
|
|
53
54
|
title: str_(UIStrings.title),
|
|
54
55
|
failureTitle: str_(UIStrings.failureTitle),
|
|
55
56
|
description: str_(UIStrings.description),
|
|
56
|
-
scoreDisplayMode: Audit.SCORING_MODES.
|
|
57
|
+
scoreDisplayMode: Audit.SCORING_MODES.METRIC_SAVINGS,
|
|
57
58
|
guidanceLevel: 1,
|
|
58
59
|
requiredArtifacts: ['DOMStats', 'URL', 'GatherContext'],
|
|
59
60
|
__internalOptionalArtifacts: ['traces', 'devtoolsLogs'],
|
|
@@ -167,6 +168,7 @@ class DOMSize extends Audit {
|
|
|
167
168
|
|
|
168
169
|
return {
|
|
169
170
|
score,
|
|
171
|
+
scoreDisplayMode: score >= Util.PASS_THRESHOLD ? Audit.SCORING_MODES.INFORMATIVE : undefined,
|
|
170
172
|
numericValue: stats.totalBodyElements,
|
|
171
173
|
numericUnit: 'element',
|
|
172
174
|
displayValue: str_(UIStrings.displayValue, {itemCount: stats.totalBodyElements}),
|
|
@@ -15,7 +15,6 @@
|
|
|
15
15
|
import {Audit} from '../audit.js';
|
|
16
16
|
import {EntityClassification} from '../../computed/entity-classification.js';
|
|
17
17
|
import UrlUtils from '../../lib/url-utils.js';
|
|
18
|
-
import {ByteEfficiencyAudit} from '../byte-efficiency/byte-efficiency-audit.js';
|
|
19
18
|
import {LanternInteractive} from '../../computed/metrics/lantern-interactive.js';
|
|
20
19
|
import {NetworkRequest} from '../../lib/network-request.js';
|
|
21
20
|
import {NetworkRecords} from '../../computed/network-records.js';
|
|
@@ -61,7 +60,7 @@ class UsesHTTP2Audit extends Audit {
|
|
|
61
60
|
id: 'uses-http2',
|
|
62
61
|
title: str_(UIStrings.title),
|
|
63
62
|
description: str_(UIStrings.description),
|
|
64
|
-
scoreDisplayMode: Audit.SCORING_MODES.
|
|
63
|
+
scoreDisplayMode: Audit.SCORING_MODES.METRIC_SAVINGS,
|
|
65
64
|
guidanceLevel: 3,
|
|
66
65
|
supportedModes: ['timespan', 'navigation'],
|
|
67
66
|
requiredArtifacts: ['URL', 'devtoolsLogs', 'traces', 'GatherContext'],
|
|
@@ -300,7 +299,7 @@ class UsesHTTP2Audit extends Audit {
|
|
|
300
299
|
displayValue,
|
|
301
300
|
numericValue: wastedMsTti,
|
|
302
301
|
numericUnit: 'millisecond',
|
|
303
|
-
score:
|
|
302
|
+
score: resources.length ? 0 : 1,
|
|
304
303
|
details,
|
|
305
304
|
metricSavings: {LCP: wasteLcp.savings, FCP: wasteFcp.savings},
|
|
306
305
|
};
|
|
@@ -38,6 +38,7 @@ class PassiveEventsAudit extends ViolationAudit {
|
|
|
38
38
|
description: str_(UIStrings.description),
|
|
39
39
|
guidanceLevel: 3,
|
|
40
40
|
requiredArtifacts: ['ConsoleMessages', 'SourceMaps', 'Scripts'],
|
|
41
|
+
scoreDisplayMode: ViolationAudit.SCORING_MODES.METRIC_SAVINGS,
|
|
41
42
|
};
|
|
42
43
|
}
|
|
43
44
|
|
|
@@ -39,9 +39,9 @@ const UIStrings = {
|
|
|
39
39
|
/** Error message explaining that the provided manifest URL is invalid. */
|
|
40
40
|
'start-url-not-valid': `Manifest start URL is not valid`,
|
|
41
41
|
/** Error message explaining that the provided manifest does not contain a name or short_name field. */
|
|
42
|
-
'manifest-missing-name-or-short-name':
|
|
42
|
+
'manifest-missing-name-or-short-name': 'Manifest does not contain a `name` or `short_name` field',
|
|
43
43
|
/** Error message explaining that the manifest display property must be one of 'standalone', 'fullscreen', or 'minimal-ui'. */
|
|
44
|
-
'manifest-display-not-supported':
|
|
44
|
+
'manifest-display-not-supported': 'Manifest `display` property must be one of `standalone`, `fullscreen`, or `minimal-ui`',
|
|
45
45
|
/** Error message explaining that the manifest could not be fetched, might be empty, or could not be parsed. */
|
|
46
46
|
'manifest-empty': `Manifest could not be fetched, is empty, or could not be parsed`,
|
|
47
47
|
/**
|
|
@@ -46,7 +46,7 @@ class LargestContentfulPaintElement extends Audit {
|
|
|
46
46
|
id: 'largest-contentful-paint-element',
|
|
47
47
|
title: str_(UIStrings.title),
|
|
48
48
|
description: str_(UIStrings.description),
|
|
49
|
-
scoreDisplayMode: Audit.SCORING_MODES.
|
|
49
|
+
scoreDisplayMode: Audit.SCORING_MODES.METRIC_SAVINGS,
|
|
50
50
|
guidanceLevel: 1,
|
|
51
51
|
supportedModes: ['navigation'],
|
|
52
52
|
requiredArtifacts:
|
|
@@ -162,7 +162,8 @@ class LargestContentfulPaintElement extends Audit {
|
|
|
162
162
|
const lcpSavings = Math.max(0, metricLcp - idealLcp);
|
|
163
163
|
|
|
164
164
|
return {
|
|
165
|
-
score: 1,
|
|
165
|
+
score: lcpSavings ? 0 : 1,
|
|
166
|
+
scoreDisplayMode: lcpSavings ? undefined : Audit.SCORING_MODES.INFORMATIVE,
|
|
166
167
|
displayValue,
|
|
167
168
|
details,
|
|
168
169
|
metricSavings: {
|
|
@@ -5,7 +5,8 @@
|
|
|
5
5
|
|
|
6
6
|
import {Audit} from './audit.js';
|
|
7
7
|
import * as i18n from '../lib/i18n/i18n.js';
|
|
8
|
-
import {CumulativeLayoutShift} from '../computed/metrics/cumulative-layout-shift.js';
|
|
8
|
+
import {CumulativeLayoutShift as CumulativeLayoutShiftComputed} from '../computed/metrics/cumulative-layout-shift.js';
|
|
9
|
+
import CumulativeLayoutShift from './metrics/cumulative-layout-shift.js';
|
|
9
10
|
|
|
10
11
|
const UIStrings = {
|
|
11
12
|
/** Descriptive title of a diagnostic audit that provides up to the top five elements contributing to Cumulative Layout Shift. */
|
|
@@ -27,7 +28,7 @@ class LayoutShiftElements extends Audit {
|
|
|
27
28
|
id: 'layout-shift-elements',
|
|
28
29
|
title: str_(UIStrings.title),
|
|
29
30
|
description: str_(UIStrings.description),
|
|
30
|
-
scoreDisplayMode: Audit.SCORING_MODES.
|
|
31
|
+
scoreDisplayMode: Audit.SCORING_MODES.METRIC_SAVINGS,
|
|
31
32
|
guidanceLevel: 2,
|
|
32
33
|
requiredArtifacts: ['traces', 'TraceElements'],
|
|
33
34
|
};
|
|
@@ -64,10 +65,13 @@ class LayoutShiftElements extends Audit {
|
|
|
64
65
|
}
|
|
65
66
|
|
|
66
67
|
const {cumulativeLayoutShift: clsSavings} =
|
|
67
|
-
await
|
|
68
|
+
await CumulativeLayoutShiftComputed.request(artifacts.traces[Audit.DEFAULT_PASS], context);
|
|
69
|
+
|
|
70
|
+
const passed = clsSavings <= CumulativeLayoutShift.defaultOptions.p10;
|
|
68
71
|
|
|
69
72
|
return {
|
|
70
|
-
score: 1,
|
|
73
|
+
score: passed ? 1 : 0,
|
|
74
|
+
scoreDisplayMode: passed ? Audit.SCORING_MODES.INFORMATIVE : undefined,
|
|
71
75
|
metricSavings: {
|
|
72
76
|
CLS: clsSavings,
|
|
73
77
|
},
|
|
@@ -33,6 +33,7 @@ class LargestContentfulPaintLazyLoaded extends Audit {
|
|
|
33
33
|
failureTitle: str_(UIStrings.failureTitle),
|
|
34
34
|
description: str_(UIStrings.description),
|
|
35
35
|
supportedModes: ['navigation'],
|
|
36
|
+
scoreDisplayMode: Audit.SCORING_MODES.METRIC_SAVINGS,
|
|
36
37
|
guidanceLevel: 3,
|
|
37
38
|
requiredArtifacts: ['TraceElements', 'ViewportDimensions', 'ImageElements',
|
|
38
39
|
'traces', 'devtoolsLogs', 'GatherContext', 'URL'],
|
|
@@ -11,6 +11,7 @@ import {MainThreadTasks} from '../computed/main-thread-tasks.js';
|
|
|
11
11
|
import {PageDependencyGraph} from '../computed/page-dependency-graph.js';
|
|
12
12
|
import {LoadSimulator} from '../computed/load-simulator.js';
|
|
13
13
|
import {getJavaScriptURLs, getAttributableURLForTask} from '../lib/tracehouse/task-summary.js';
|
|
14
|
+
import {TotalBlockingTime} from '../computed/metrics/total-blocking-time.js';
|
|
14
15
|
|
|
15
16
|
/** We don't always have timing data for short tasks, if we're missing timing data. Treat it as though it were 0ms. */
|
|
16
17
|
const DEFAULT_TIMING = {startTime: 0, endTime: 0, duration: 0};
|
|
@@ -66,8 +67,8 @@ class LongTasks extends Audit {
|
|
|
66
67
|
scoreDisplayMode: Audit.SCORING_MODES.INFORMATIVE,
|
|
67
68
|
title: str_(UIStrings.title),
|
|
68
69
|
description: str_(UIStrings.description),
|
|
70
|
+
requiredArtifacts: ['traces', 'devtoolsLogs', 'URL', 'GatherContext'],
|
|
69
71
|
guidanceLevel: 1,
|
|
70
|
-
requiredArtifacts: ['traces', 'devtoolsLogs', 'URL'],
|
|
71
72
|
};
|
|
72
73
|
}
|
|
73
74
|
|
|
@@ -180,6 +181,9 @@ class LongTasks extends Audit {
|
|
|
180
181
|
const devtoolsLog = artifacts.devtoolsLogs[LongTasks.DEFAULT_PASS];
|
|
181
182
|
const networkRecords = await NetworkRecords.request(devtoolsLog, context);
|
|
182
183
|
|
|
184
|
+
const metricComputationData = Audit.makeMetricComputationDataInput(artifacts, context);
|
|
185
|
+
const tbtResult = await TotalBlockingTime.request(metricComputationData, context);
|
|
186
|
+
|
|
183
187
|
/** @type {Map<LH.TraceEvent, LH.Gatherer.Simulation.NodeTiming>|undefined} */
|
|
184
188
|
let taskTimingsByEvent;
|
|
185
189
|
|
|
@@ -245,6 +249,9 @@ class LongTasks extends Audit {
|
|
|
245
249
|
notApplicable: results.length === 0,
|
|
246
250
|
details: tableDetails,
|
|
247
251
|
displayValue,
|
|
252
|
+
metricSavings: {
|
|
253
|
+
TBT: tbtResult.timing,
|
|
254
|
+
},
|
|
248
255
|
};
|
|
249
256
|
}
|
|
250
257
|
}
|
|
@@ -16,6 +16,7 @@ import * as i18n from '../lib/i18n/i18n.js';
|
|
|
16
16
|
import {MainThreadTasks} from '../computed/main-thread-tasks.js';
|
|
17
17
|
import {TotalBlockingTime} from '../computed/metrics/total-blocking-time.js';
|
|
18
18
|
import {Sentry} from '../lib/sentry.js';
|
|
19
|
+
import {Util} from '../../shared/util.js';
|
|
19
20
|
|
|
20
21
|
const UIStrings = {
|
|
21
22
|
/** Title of a diagnostic audit that provides detail on the main thread work the browser did to load the page. This descriptive title is shown to users when the amount is acceptable and no user action is required. */
|
|
@@ -44,7 +45,7 @@ class MainThreadWorkBreakdown extends Audit {
|
|
|
44
45
|
title: str_(UIStrings.title),
|
|
45
46
|
failureTitle: str_(UIStrings.failureTitle),
|
|
46
47
|
description: str_(UIStrings.description),
|
|
47
|
-
scoreDisplayMode: Audit.SCORING_MODES.
|
|
48
|
+
scoreDisplayMode: Audit.SCORING_MODES.METRIC_SAVINGS,
|
|
48
49
|
guidanceLevel: 1,
|
|
49
50
|
requiredArtifacts: ['traces', 'devtoolsLogs', 'URL', 'GatherContext'],
|
|
50
51
|
};
|
|
@@ -141,6 +142,7 @@ class MainThreadWorkBreakdown extends Audit {
|
|
|
141
142
|
|
|
142
143
|
return {
|
|
143
144
|
score,
|
|
145
|
+
scoreDisplayMode: score >= Util.PASS_THRESHOLD ? Audit.SCORING_MODES.INFORMATIVE : undefined,
|
|
144
146
|
numericValue: totalExecutionTime,
|
|
145
147
|
numericUnit: 'millisecond',
|
|
146
148
|
displayValue: str_(i18n.UIStrings.seconds, {timeInMs: totalExecutionTime}),
|
|
@@ -62,11 +62,14 @@ class CumulativeLayoutShift extends Audit {
|
|
|
62
62
|
items: [rest],
|
|
63
63
|
};
|
|
64
64
|
|
|
65
|
+
const scoringOptions = {p10: context.options.p10, median: context.options.median};
|
|
66
|
+
|
|
65
67
|
return {
|
|
66
68
|
score: Audit.computeLogNormalScore(
|
|
67
|
-
|
|
69
|
+
scoringOptions,
|
|
68
70
|
cumulativeLayoutShift
|
|
69
71
|
),
|
|
72
|
+
scoringOptions,
|
|
70
73
|
numericValue: cumulativeLayoutShift,
|
|
71
74
|
numericUnit: 'unitless',
|
|
72
75
|
displayValue: cumulativeLayoutShift.toLocaleString(context.settings.locale),
|
|
@@ -74,6 +74,7 @@ class FirstContentfulPaint extends Audit {
|
|
|
74
74
|
options.scoring,
|
|
75
75
|
metricResult.timing
|
|
76
76
|
),
|
|
77
|
+
scoringOptions: options.scoring,
|
|
77
78
|
numericValue: metricResult.timing,
|
|
78
79
|
numericUnit: 'millisecond',
|
|
79
80
|
displayValue: str_(i18n.UIStrings.seconds, {timeInMs: metricResult.timing}),
|
|
@@ -83,6 +83,7 @@ class LargestContentfulPaint extends Audit {
|
|
|
83
83
|
options.scoring,
|
|
84
84
|
metricResult.timing
|
|
85
85
|
),
|
|
86
|
+
scoringOptions: options.scoring,
|
|
86
87
|
numericValue: metricResult.timing,
|
|
87
88
|
numericUnit: 'millisecond',
|
|
88
89
|
displayValue: str_(i18n.UIStrings.seconds, {timeInMs: metricResult.timing}),
|