lighthouse 9.5.0-dev.20220628 → 9.5.0-dev.20220701
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/CONTRIBUTING.md +1 -1
- package/dist/report/bundle.esm.js +7 -6
- package/dist/report/flow.js +24 -24
- package/dist/report/standalone.js +19 -19
- package/{eslint-local-rules.js → eslint-local-rules.cjs} +0 -0
- package/flow-report/test/common-test.tsx +5 -4
- package/flow-report/test/flow-report-pptr-test.ts +4 -7
- package/flow-report/test/run-flow-report-tests.sh +20 -0
- package/flow-report/test/setup/env-setup.ts +44 -24
- package/flow-report/test/sidebar/sidebar-test.tsx +0 -1
- package/flow-report/test/topbar-test.tsx +5 -5
- package/flow-report/test/util-test.tsx +3 -3
- package/flow-report/tsconfig.json +4 -1
- package/lighthouse-cli/cli-flags.js +313 -305
- package/lighthouse-cli/run.js +1 -1
- package/lighthouse-cli/test/smokehouse/report-assert-test.js +1 -1
- package/lighthouse-core/audits/byte-efficiency/uses-responsive-images.js +30 -5
- package/lighthouse-core/audits/final-screenshot.js +2 -2
- package/lighthouse-core/audits/screenshot-thumbnails.js +6 -6
- package/lighthouse-core/audits/work-during-interaction.js +3 -3
- package/lighthouse-core/computed/metrics/cumulative-layout-shift.js +3 -3
- package/lighthouse-core/computed/metrics/first-meaningful-paint.js +2 -2
- package/lighthouse-core/computed/metrics/interactive.js +5 -5
- package/lighthouse-core/computed/metrics/lantern-first-meaningful-paint.js +3 -3
- package/lighthouse-core/computed/metrics/lantern-largest-contentful-paint.js +3 -3
- package/lighthouse-core/computed/metrics/largest-contentful-paint-all-frames.js +2 -2
- package/lighthouse-core/computed/metrics/largest-contentful-paint.js +2 -2
- package/lighthouse-core/computed/speedline.js +4 -4
- package/lighthouse-core/config/config-plugin.js +26 -0
- package/lighthouse-core/fraggle-rock/gather/navigation-runner.js +1 -0
- package/lighthouse-core/fraggle-rock/gather/session.js +3 -2
- package/lighthouse-core/gather/connections/connection.js +2 -2
- package/lighthouse-core/gather/driver/wait-for-condition.js +3 -3
- package/lighthouse-core/gather/driver.js +3 -2
- package/lighthouse-core/gather/gather-runner.js +2 -1
- package/lighthouse-core/gather/gatherers/main-document-content.js +0 -2
- package/lighthouse-core/lib/asset-saver.js +6 -6
- package/lighthouse-core/lib/lh-env.js +1 -1
- package/lighthouse-core/lib/lh-error.js +5 -5
- package/lighthouse-core/lib/lh-trace-processor.js +6 -6
- package/lighthouse-core/lib/navigation-error.js +34 -14
- package/lighthouse-core/lib/sentry.js +3 -3
- package/lighthouse-core/lib/url-shim.js +2 -2
- package/lighthouse-core/runner.js +6 -4
- package/lighthouse-core/util-commonjs.js +7 -6
- package/package.json +24 -19
- package/report/renderer/util.js +7 -6
- package/report/test/clients/bundle-test.js +4 -4
- package/report/test/generator/report-generator-test.js +3 -1
- package/report/test/renderer/category-renderer-test.js +3 -3
- package/report/test/renderer/components-test.js +36 -34
- package/report/test/renderer/crc-details-renderer-test.js +2 -2
- package/report/test/renderer/details-renderer-test.js +2 -2
- package/report/test/renderer/dom-test.js +4 -4
- package/report/test/renderer/element-screenshot-renderer-test.js +3 -2
- package/report/test/renderer/performance-category-renderer-test.js +4 -4
- package/report/test/renderer/pwa-category-renderer-test.js +3 -3
- package/report/test/renderer/report-renderer-axe-test.js +6 -8
- package/report/test/renderer/report-renderer-test.js +5 -5
- package/report/test/renderer/report-ui-features-test.js +8 -8
- package/report/test/renderer/snippet-renderer-test.js +2 -2
- package/report/test/renderer/text-encoding-test.js +2 -2
- package/report/test/renderer/util-test.js +1 -1
- package/root.js +0 -18
- package/shared/localization/format.js +1 -1
- package/shared/localization/locales/en-US.json +3 -0
- package/shared/localization/locales/en-XL.json +3 -0
- package/third-party/chromium-synchronization/inspector-issueAdded-types-test.js +1 -1
- package/third-party/chromium-synchronization/installability-errors-test.js +1 -3
- package/third-party/devtools-tests/README.md +2 -2
- package/third-party/devtools-tests/e2e/lighthouse/BUILD.gn +17 -0
- package/third-party/devtools-tests/e2e/resources/lighthouse/BUILD.gn +9 -0
- package/tsconfig.json +1 -1
- package/types/global-lh.d.ts +2 -2
- package/types/test.d.ts +53 -0
- package/flow-report/.eslintrc.cjs +0 -51
- package/flow-report/jest.config.js +0 -24
- package/flow-report/test/setup/global-setup.ts +0 -11
- package/jest.config.js +0 -30
- package/lighthouse-cli/.eslintrc.cjs +0 -28
- package/report/.eslintrc.cjs +0 -37
- package/report/test/.eslintrc.cjs +0 -11
- package/shared/test/localization/.eslintrc.cjs +0 -11
- package/types/jest.d.ts +0 -25
package/lighthouse-cli/run.js
CHANGED
|
@@ -250,7 +250,7 @@ async function runLighthouse(url, flags, config) {
|
|
|
250
250
|
if (runnerResult?.lhr.runtimeError) {
|
|
251
251
|
const {runtimeError} = runnerResult.lhr;
|
|
252
252
|
return printErrorAndExit({
|
|
253
|
-
name: '
|
|
253
|
+
name: 'LighthouseError',
|
|
254
254
|
friendlyMessage: runtimeError.message,
|
|
255
255
|
code: runtimeError.code,
|
|
256
256
|
message: runtimeError.message,
|
|
@@ -6,8 +6,8 @@
|
|
|
6
6
|
|
|
7
7
|
/* eslint-disable no-control-regex */
|
|
8
8
|
|
|
9
|
+
import {readJson} from '../../../lighthouse-core/test/test-utils.js';
|
|
9
10
|
import {findDifferences, getAssertionReport} from './report-assert.js';
|
|
10
|
-
import {readJson} from '../../../root.js';
|
|
11
11
|
|
|
12
12
|
describe('findDiffersences', () => {
|
|
13
13
|
const testCases = {
|
|
@@ -33,6 +33,9 @@ const str_ = i18n.createMessageInstanceIdFn(__filename, UIStrings);
|
|
|
33
33
|
|
|
34
34
|
const IGNORE_THRESHOLD_IN_BYTES = 4096;
|
|
35
35
|
|
|
36
|
+
// Ignore up to 12KB of waste if an effort was made with breakpoints.
|
|
37
|
+
const IGNORE_THRESHOLD_IN_BYTES_BREAKPOINTS_PRESENT = 12288;
|
|
38
|
+
|
|
36
39
|
class UsesResponsiveImages extends ByteEfficiencyAudit {
|
|
37
40
|
/**
|
|
38
41
|
* @return {LH.Audit.Meta}
|
|
@@ -112,6 +115,18 @@ class UsesResponsiveImages extends ByteEfficiencyAudit {
|
|
|
112
115
|
};
|
|
113
116
|
}
|
|
114
117
|
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* @param {LH.Artifacts.ImageElement} image
|
|
121
|
+
* @return {number};
|
|
122
|
+
*/
|
|
123
|
+
static determineAllowableWaste(image) {
|
|
124
|
+
if (image.srcset || image.isPicture) {
|
|
125
|
+
return IGNORE_THRESHOLD_IN_BYTES_BREAKPOINTS_PRESENT;
|
|
126
|
+
}
|
|
127
|
+
return IGNORE_THRESHOLD_IN_BYTES;
|
|
128
|
+
}
|
|
129
|
+
|
|
115
130
|
/**
|
|
116
131
|
* @param {LH.Artifacts} artifacts
|
|
117
132
|
* @param {Array<LH.Artifacts.NetworkRequest>} networkRecords
|
|
@@ -126,6 +141,8 @@ class UsesResponsiveImages extends ByteEfficiencyAudit {
|
|
|
126
141
|
const ViewportDimensions = artifacts.ViewportDimensions;
|
|
127
142
|
/** @type {Map<string, LH.Audit.ByteEfficiencyItem>} */
|
|
128
143
|
const resultsMap = new Map();
|
|
144
|
+
/** @type {Array<string>} */
|
|
145
|
+
const passedImageList = [];
|
|
129
146
|
for (const image of images) {
|
|
130
147
|
// Give SVG a free pass because creating a "responsive" SVG is of questionable value.
|
|
131
148
|
// Ignore CSS images because it's difficult to determine what is a spritesheet,
|
|
@@ -147,15 +164,23 @@ class UsesResponsiveImages extends ByteEfficiencyAudit {
|
|
|
147
164
|
);
|
|
148
165
|
if (!processed) continue;
|
|
149
166
|
|
|
150
|
-
//
|
|
167
|
+
// Verify the image wastes more than the minimum.
|
|
168
|
+
const exceedsAllowableWaste = processed.wastedBytes > this.determineAllowableWaste(image);
|
|
169
|
+
|
|
151
170
|
const existing = resultsMap.get(processed.url);
|
|
152
|
-
|
|
153
|
-
|
|
171
|
+
// Don't warn about an image that was later used appropriately, or wastes a trivial amount of data.
|
|
172
|
+
if (exceedsAllowableWaste && !passedImageList.includes(processed.url)) {
|
|
173
|
+
if ((!existing || existing.wastedBytes > processed.wastedBytes)) {
|
|
174
|
+
resultsMap.set(processed.url, processed);
|
|
175
|
+
}
|
|
176
|
+
} else {
|
|
177
|
+
// Ensure this url passes for future tests.
|
|
178
|
+
resultsMap.delete(processed.url);
|
|
179
|
+
passedImageList.push(processed.url);
|
|
154
180
|
}
|
|
155
181
|
}
|
|
156
182
|
|
|
157
|
-
const items = Array.from(resultsMap.values())
|
|
158
|
-
.filter(item => item.wastedBytes > IGNORE_THRESHOLD_IN_BYTES);
|
|
183
|
+
const items = Array.from(resultsMap.values());
|
|
159
184
|
|
|
160
185
|
/** @type {LH.Audit.Details.Opportunity['headings']} */
|
|
161
186
|
const headings = [
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
'use strict';
|
|
7
7
|
|
|
8
8
|
const Audit = require('./audit.js');
|
|
9
|
-
const
|
|
9
|
+
const LighthouseError = require('../lib/lh-error.js');
|
|
10
10
|
const ProcessedTrace = require('../computed/processed-trace.js');
|
|
11
11
|
const Screenshots = require('../computed/screenshots.js');
|
|
12
12
|
|
|
@@ -41,7 +41,7 @@ class FinalScreenshot extends Audit {
|
|
|
41
41
|
if (artifacts.GatherContext.gatherMode === 'timespan') return {notApplicable: true, score: 1};
|
|
42
42
|
|
|
43
43
|
// If it was another mode, that's a fatal error.
|
|
44
|
-
throw new
|
|
44
|
+
throw new LighthouseError(LighthouseError.errors.NO_SCREENSHOTS);
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
return {
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
'use strict';
|
|
7
7
|
|
|
8
8
|
const Audit = require('./audit.js');
|
|
9
|
-
const
|
|
9
|
+
const LighthouseError = require('../lib/lh-error.js');
|
|
10
10
|
const jpeg = require('jpeg-js');
|
|
11
11
|
const Speedline = require('../computed/speedline.js');
|
|
12
12
|
|
|
@@ -88,7 +88,7 @@ class ScreenshotThumbnails extends Audit {
|
|
|
88
88
|
const timelineEnd = Math.max(maxFrameTime, minimumTimelineDuration);
|
|
89
89
|
|
|
90
90
|
if (!analyzedFrames.length || !Number.isFinite(timelineEnd)) {
|
|
91
|
-
throw new
|
|
91
|
+
throw new LighthouseError(LighthouseError.errors.INVALID_SPEEDLINE);
|
|
92
92
|
}
|
|
93
93
|
|
|
94
94
|
for (let i = 1; i <= NUMBER_OF_THUMBNAILS; i++) {
|
|
@@ -144,10 +144,10 @@ class ScreenshotThumbnails extends Audit {
|
|
|
144
144
|
return await this._audit(artifacts, context);
|
|
145
145
|
} catch (err) {
|
|
146
146
|
const noFramesErrors = new Set([
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
147
|
+
LighthouseError.errors.NO_SCREENSHOTS.code,
|
|
148
|
+
LighthouseError.errors.SPEEDINDEX_OF_ZERO.code,
|
|
149
|
+
LighthouseError.errors.NO_SPEEDLINE_FRAMES.code,
|
|
150
|
+
LighthouseError.errors.INVALID_SPEEDLINE.code,
|
|
151
151
|
]);
|
|
152
152
|
|
|
153
153
|
// If a timespan didn't happen to contain frames, that's fine. Just mark not applicable.
|
|
@@ -15,7 +15,7 @@ const {taskGroups} = require('../lib/tracehouse/task-groups.js');
|
|
|
15
15
|
const TraceProcessor = require('../lib/tracehouse/trace-processor.js');
|
|
16
16
|
const {getExecutionTimingsByURL} = require('../lib/tracehouse/task-summary.js');
|
|
17
17
|
const inpThresholds = require('./metrics/experimental-interaction-to-next-paint.js').defaultOptions;
|
|
18
|
-
const
|
|
18
|
+
const LighthouseError = require('../lib/lh-error.js');
|
|
19
19
|
|
|
20
20
|
/** @typedef {import('../computed/metrics/responsiveness.js').EventTimingEvent} EventTimingEvent */
|
|
21
21
|
/** @typedef {import('../lib/tracehouse/main-thread-tasks.js').TaskNode} TaskNode */
|
|
@@ -237,8 +237,8 @@ class WorkDuringInteraction extends Audit {
|
|
|
237
237
|
}
|
|
238
238
|
// TODO: remove workaround once 103.0.5052.0 is sufficiently released.
|
|
239
239
|
if (interactionEvent.name === 'FallbackTiming') {
|
|
240
|
-
throw new
|
|
241
|
-
|
|
240
|
+
throw new LighthouseError(
|
|
241
|
+
LighthouseError.errors.UNSUPPORTED_OLD_CHROME,
|
|
242
242
|
{featureName: 'detailed EventTiming trace events'}
|
|
243
243
|
);
|
|
244
244
|
}
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
const makeComputedArtifact = require('../computed-artifact.js');
|
|
9
9
|
const ProcessedTrace = require('../processed-trace.js');
|
|
10
|
-
const
|
|
10
|
+
const LighthouseError = require('../../lib/lh-error.js');
|
|
11
11
|
|
|
12
12
|
/** @typedef {{ts: number, isMainFrame: boolean, weightedScore: number}} LayoutShiftEvent */
|
|
13
13
|
|
|
@@ -40,8 +40,8 @@ class CumulativeLayoutShift {
|
|
|
40
40
|
// For all-frames CLS calculation, we rely on `weighted_score_delta`, which
|
|
41
41
|
// was added in Chrome 90: https://crbug.com/1173139
|
|
42
42
|
if (event.args.data.weighted_score_delta === undefined) {
|
|
43
|
-
throw new
|
|
44
|
-
|
|
43
|
+
throw new LighthouseError(
|
|
44
|
+
LighthouseError.errors.UNSUPPORTED_OLD_CHROME,
|
|
45
45
|
{featureName: 'Cumulative Layout Shift'}
|
|
46
46
|
);
|
|
47
47
|
}
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
const makeComputedArtifact = require('../computed-artifact.js');
|
|
9
9
|
const NavigationMetric = require('./navigation-metric.js');
|
|
10
|
-
const
|
|
10
|
+
const LighthouseError = require('../../lib/lh-error.js');
|
|
11
11
|
const LanternFirstMeaningfulPaint = require('./lantern-first-meaningful-paint.js');
|
|
12
12
|
|
|
13
13
|
class FirstMeaningfulPaint extends NavigationMetric {
|
|
@@ -28,7 +28,7 @@ class FirstMeaningfulPaint extends NavigationMetric {
|
|
|
28
28
|
static async computeObservedMetric(data) {
|
|
29
29
|
const {processedNavigation} = data;
|
|
30
30
|
if (processedNavigation.timings.firstMeaningfulPaint === undefined) {
|
|
31
|
-
throw new
|
|
31
|
+
throw new LighthouseError(LighthouseError.errors.NO_FMP);
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
return {
|
|
@@ -11,7 +11,7 @@ const LanternInteractive = require('./lantern-interactive.js');
|
|
|
11
11
|
|
|
12
12
|
const NetworkMonitor = require('../../gather/driver/network-monitor.js');
|
|
13
13
|
const TracingProcessor = require('../../lib/tracehouse/trace-processor.js');
|
|
14
|
-
const
|
|
14
|
+
const LighthouseError = require('../../lib/lh-error.js');
|
|
15
15
|
|
|
16
16
|
const REQUIRED_QUIET_WINDOW = 5000;
|
|
17
17
|
const ALLOWED_CONCURRENT_REQUESTS = 2;
|
|
@@ -133,10 +133,10 @@ class Interactive extends NavigationMetric {
|
|
|
133
133
|
}
|
|
134
134
|
}
|
|
135
135
|
|
|
136
|
-
throw new
|
|
136
|
+
throw new LighthouseError(
|
|
137
137
|
cpuCandidate
|
|
138
|
-
?
|
|
139
|
-
:
|
|
138
|
+
? LighthouseError.errors.NO_TTI_NETWORK_IDLE_PERIOD
|
|
139
|
+
: LighthouseError.errors.NO_TTI_CPU_IDLE_PERIOD
|
|
140
140
|
);
|
|
141
141
|
}
|
|
142
142
|
|
|
@@ -158,7 +158,7 @@ class Interactive extends NavigationMetric {
|
|
|
158
158
|
const {processedTrace, processedNavigation, networkRecords} = data;
|
|
159
159
|
|
|
160
160
|
if (!processedNavigation.timestamps.domContentLoaded) {
|
|
161
|
-
throw new
|
|
161
|
+
throw new LighthouseError(LighthouseError.errors.NO_DCL);
|
|
162
162
|
}
|
|
163
163
|
|
|
164
164
|
const longTasks = TracingProcessor.getMainThreadTopLevelEvents(processedTrace)
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
const makeComputedArtifact = require('../computed-artifact.js');
|
|
9
9
|
const LanternMetric = require('./lantern-metric.js');
|
|
10
|
-
const
|
|
10
|
+
const LighthouseError = require('../../lib/lh-error.js');
|
|
11
11
|
const LanternFirstContentfulPaint = require('./lantern-first-contentful-paint.js');
|
|
12
12
|
|
|
13
13
|
/** @typedef {import('../../lib/dependency-graph/base-node.js').Node} Node */
|
|
@@ -32,7 +32,7 @@ class LanternFirstMeaningfulPaint extends LanternMetric {
|
|
|
32
32
|
static getOptimisticGraph(dependencyGraph, processedNavigation) {
|
|
33
33
|
const fmp = processedNavigation.timestamps.firstMeaningfulPaint;
|
|
34
34
|
if (!fmp) {
|
|
35
|
-
throw new
|
|
35
|
+
throw new LighthouseError(LighthouseError.errors.NO_FMP);
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
return LanternFirstContentfulPaint.getFirstPaintBasedGraph(
|
|
@@ -52,7 +52,7 @@ class LanternFirstMeaningfulPaint extends LanternMetric {
|
|
|
52
52
|
static getPessimisticGraph(dependencyGraph, processedNavigation) {
|
|
53
53
|
const fmp = processedNavigation.timestamps.firstMeaningfulPaint;
|
|
54
54
|
if (!fmp) {
|
|
55
|
-
throw new
|
|
55
|
+
throw new LighthouseError(LighthouseError.errors.NO_FMP);
|
|
56
56
|
}
|
|
57
57
|
|
|
58
58
|
return LanternFirstContentfulPaint.getFirstPaintBasedGraph(
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
const makeComputedArtifact = require('../computed-artifact.js');
|
|
9
9
|
const LanternMetric = require('./lantern-metric.js');
|
|
10
|
-
const
|
|
10
|
+
const LighthouseError = require('../../lib/lh-error.js');
|
|
11
11
|
const LanternFirstContentfulPaint = require('./lantern-first-contentful-paint.js');
|
|
12
12
|
|
|
13
13
|
/** @typedef {import('../../lib/dependency-graph/base-node.js').Node} Node */
|
|
@@ -47,7 +47,7 @@ class LanternLargestContentfulPaint extends LanternMetric {
|
|
|
47
47
|
static getOptimisticGraph(dependencyGraph, processedNavigation) {
|
|
48
48
|
const lcp = processedNavigation.timestamps.largestContentfulPaint;
|
|
49
49
|
if (!lcp) {
|
|
50
|
-
throw new
|
|
50
|
+
throw new LighthouseError(LighthouseError.errors.NO_LCP);
|
|
51
51
|
}
|
|
52
52
|
|
|
53
53
|
return LanternFirstContentfulPaint.getFirstPaintBasedGraph(
|
|
@@ -65,7 +65,7 @@ class LanternLargestContentfulPaint extends LanternMetric {
|
|
|
65
65
|
static getPessimisticGraph(dependencyGraph, processedNavigation) {
|
|
66
66
|
const lcp = processedNavigation.timestamps.largestContentfulPaint;
|
|
67
67
|
if (!lcp) {
|
|
68
|
-
throw new
|
|
68
|
+
throw new LighthouseError(LighthouseError.errors.NO_LCP);
|
|
69
69
|
}
|
|
70
70
|
|
|
71
71
|
return LanternFirstContentfulPaint.getFirstPaintBasedGraph(
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
|
|
12
12
|
const makeComputedArtifact = require('../computed-artifact.js');
|
|
13
13
|
const NavigationMetric = require('./navigation-metric.js');
|
|
14
|
-
const
|
|
14
|
+
const LighthouseError = require('../../lib/lh-error.js');
|
|
15
15
|
|
|
16
16
|
class LargestContentfulPaintAllFrames extends NavigationMetric {
|
|
17
17
|
/**
|
|
@@ -29,7 +29,7 @@ class LargestContentfulPaintAllFrames extends NavigationMetric {
|
|
|
29
29
|
static async computeObservedMetric(data) {
|
|
30
30
|
const {processedNavigation} = data;
|
|
31
31
|
if (processedNavigation.timings.largestContentfulPaintAllFrames === undefined) {
|
|
32
|
-
throw new
|
|
32
|
+
throw new LighthouseError(LighthouseError.errors.NO_LCP_ALL_FRAMES);
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
return {
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
|
|
16
16
|
const makeComputedArtifact = require('../computed-artifact.js');
|
|
17
17
|
const NavigationMetric = require('./navigation-metric.js');
|
|
18
|
-
const
|
|
18
|
+
const LighthouseError = require('../../lib/lh-error.js');
|
|
19
19
|
const LanternLargestContentfulPaint = require('./lantern-largest-contentful-paint.js');
|
|
20
20
|
|
|
21
21
|
class LargestContentfulPaint extends NavigationMetric {
|
|
@@ -36,7 +36,7 @@ class LargestContentfulPaint extends NavigationMetric {
|
|
|
36
36
|
static async computeObservedMetric(data) {
|
|
37
37
|
const {processedNavigation} = data;
|
|
38
38
|
if (processedNavigation.timings.largestContentfulPaint === undefined) {
|
|
39
|
-
throw new
|
|
39
|
+
throw new LighthouseError(LighthouseError.errors.NO_LCP);
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
return {
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
const makeComputedArtifact = require('./computed-artifact.js');
|
|
9
9
|
const speedline = require('speedline-core');
|
|
10
|
-
const
|
|
10
|
+
const LighthouseError = require('../lib/lh-error.js');
|
|
11
11
|
const ProcessedTrace = require('./processed-trace.js');
|
|
12
12
|
|
|
13
13
|
class Speedline {
|
|
@@ -33,17 +33,17 @@ class Speedline {
|
|
|
33
33
|
});
|
|
34
34
|
}).catch(err => {
|
|
35
35
|
if (/No screenshots found in trace/.test(err.message)) {
|
|
36
|
-
throw new
|
|
36
|
+
throw new LighthouseError(LighthouseError.errors.NO_SCREENSHOTS);
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
throw err;
|
|
40
40
|
}).then(speedline => {
|
|
41
41
|
if (speedline.frames.length === 0) {
|
|
42
|
-
throw new
|
|
42
|
+
throw new LighthouseError(LighthouseError.errors.NO_SPEEDLINE_FRAMES);
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
if (speedline.speedIndex === 0) {
|
|
46
|
-
throw new
|
|
46
|
+
throw new LighthouseError(LighthouseError.errors.SPEEDINDEX_OF_ZERO);
|
|
47
47
|
}
|
|
48
48
|
|
|
49
49
|
return speedline;
|
|
@@ -23,6 +23,24 @@ function isObjectOfUnknownProperties(val) {
|
|
|
23
23
|
return typeof val === 'object' && val !== null && !Array.isArray(val);
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
+
/**
|
|
27
|
+
* @param {unknown} str
|
|
28
|
+
* @return {str is LH.Gatherer.GatherMode}
|
|
29
|
+
*/
|
|
30
|
+
function objectIsGatherMode(str) {
|
|
31
|
+
if (typeof str !== 'string') return false;
|
|
32
|
+
return str === 'navigation' || str === 'timespan' || str === 'snapshot';
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* @param {unknown} arr
|
|
37
|
+
* @return {arr is Array<LH.Gatherer.GatherMode>}
|
|
38
|
+
*/
|
|
39
|
+
function isArrayOfGatherModes(arr) {
|
|
40
|
+
if (!Array.isArray(arr)) return false;
|
|
41
|
+
return arr.every(objectIsGatherMode);
|
|
42
|
+
}
|
|
43
|
+
|
|
26
44
|
/**
|
|
27
45
|
* Asserts that obj has no own properties, throwing a nice error message if it does.
|
|
28
46
|
* Plugin and object name are included for nicer logging.
|
|
@@ -124,6 +142,7 @@ class ConfigPlugin {
|
|
|
124
142
|
description,
|
|
125
143
|
manualDescription,
|
|
126
144
|
auditRefs: auditRefsJson,
|
|
145
|
+
supportedModes,
|
|
127
146
|
...invalidRest
|
|
128
147
|
} = categoryJson;
|
|
129
148
|
|
|
@@ -138,6 +157,12 @@ class ConfigPlugin {
|
|
|
138
157
|
if (!i18n.isStringOrIcuMessage(manualDescription) && manualDescription !== undefined) {
|
|
139
158
|
throw new Error(`${pluginName} has an invalid category manualDescription.`);
|
|
140
159
|
}
|
|
160
|
+
if (!isArrayOfGatherModes(supportedModes) && supportedModes !== undefined) {
|
|
161
|
+
throw new Error(
|
|
162
|
+
`${pluginName} supportedModes must be an array, ` +
|
|
163
|
+
`valid array values are "navigation", "timespan", and "snapshot".`
|
|
164
|
+
);
|
|
165
|
+
}
|
|
141
166
|
const auditRefs = ConfigPlugin._parseAuditRefsList(auditRefsJson, pluginName);
|
|
142
167
|
|
|
143
168
|
return {
|
|
@@ -145,6 +170,7 @@ class ConfigPlugin {
|
|
|
145
170
|
auditRefs,
|
|
146
171
|
description: description,
|
|
147
172
|
manualDescription: manualDescription,
|
|
173
|
+
supportedModes,
|
|
148
174
|
};
|
|
149
175
|
}
|
|
150
176
|
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
'use strict';
|
|
7
7
|
|
|
8
8
|
const EventEmitter = require('events').EventEmitter;
|
|
9
|
-
const
|
|
9
|
+
const LighthouseError = require('../../lib/lh-error.js');
|
|
10
10
|
|
|
11
11
|
// Controls how long to wait for a response after sending a DevTools protocol command.
|
|
12
12
|
const DEFAULT_PROTOCOL_TIMEOUT = 30000;
|
|
@@ -87,7 +87,8 @@ class ProtocolSession extends CrdpEventEmitter {
|
|
|
87
87
|
const timeoutPromise = new Promise((resolve, reject) => {
|
|
88
88
|
if (timeoutMs === Infinity) return;
|
|
89
89
|
|
|
90
|
-
|
|
90
|
+
// eslint-disable-next-line max-len
|
|
91
|
+
timeout = setTimeout(reject, timeoutMs, new LighthouseError(LighthouseError.errors.PROTOCOL_TIMEOUT, {
|
|
91
92
|
protocolMethod: method,
|
|
92
93
|
}));
|
|
93
94
|
});
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
const EventEmitter = require('events').EventEmitter;
|
|
9
9
|
const log = require('lighthouse-logger');
|
|
10
|
-
const
|
|
10
|
+
const LighthouseError = require('../../lib/lh-error.js');
|
|
11
11
|
|
|
12
12
|
// TODO(bckenny): CommandCallback properties should be tied by command type after
|
|
13
13
|
// https://github.com/Microsoft/TypeScript/pull/22348. See driver.js TODO.
|
|
@@ -138,7 +138,7 @@ class Connection {
|
|
|
138
138
|
callback.resolve(Promise.resolve().then(_ => {
|
|
139
139
|
if (object.error) {
|
|
140
140
|
log.formatProtocol('method <= browser ERR', {method: callback.method}, 'error');
|
|
141
|
-
throw
|
|
141
|
+
throw LighthouseError.fromProtocolMessage(callback.method, object.error);
|
|
142
142
|
}
|
|
143
143
|
|
|
144
144
|
log.formatProtocol('method <= browser OK',
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
/* global window */
|
|
9
9
|
|
|
10
10
|
const log = require('lighthouse-logger');
|
|
11
|
-
const
|
|
11
|
+
const LighthouseError = require('../../lib/lh-error.js');
|
|
12
12
|
const ExecutionContext = require('./execution-context.js');
|
|
13
13
|
|
|
14
14
|
/** @typedef {import('./network-monitor.js')} NetworkMonitor */
|
|
@@ -75,7 +75,7 @@ function waitForFcp(session, pauseAfterFcpMs, maxWaitForFcpMs) {
|
|
|
75
75
|
/** @type {Promise<void>} */
|
|
76
76
|
const promise = new Promise((resolve, reject) => {
|
|
77
77
|
const maxWaitTimeout = setTimeout(() => {
|
|
78
|
-
reject(new
|
|
78
|
+
reject(new LighthouseError(LighthouseError.errors.NO_FCP));
|
|
79
79
|
}, maxWaitForFcpMs);
|
|
80
80
|
/** @type {NodeJS.Timeout|undefined} */
|
|
81
81
|
let loadTimeout;
|
|
@@ -476,7 +476,7 @@ async function waitForFullyLoaded(session, networkMonitor, options) {
|
|
|
476
476
|
log.warn('waitFor', 'Page appears to be hung, killing JavaScript...');
|
|
477
477
|
await session.sendCommand('Emulation.setScriptExecutionDisabled', {value: true});
|
|
478
478
|
await session.sendCommand('Runtime.terminateExecution');
|
|
479
|
-
throw new
|
|
479
|
+
throw new LighthouseError(LighthouseError.errors.PAGE_HUNG);
|
|
480
480
|
}
|
|
481
481
|
|
|
482
482
|
return {timedOut: true};
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
const Fetcher = require('./fetcher.js');
|
|
9
9
|
const ExecutionContext = require('./driver/execution-context.js');
|
|
10
|
-
const
|
|
10
|
+
const LighthouseError = require('../lib/lh-error.js');
|
|
11
11
|
const {fetchResponseBodyFromCache} = require('../gather/driver/network.js');
|
|
12
12
|
const EventEmitter = require('events').EventEmitter;
|
|
13
13
|
|
|
@@ -343,7 +343,8 @@ class Driver {
|
|
|
343
343
|
let asyncTimeout;
|
|
344
344
|
const timeoutPromise = new Promise((resolve, reject) => {
|
|
345
345
|
if (timeout === Infinity) return;
|
|
346
|
-
|
|
346
|
+
// eslint-disable-next-line max-len
|
|
347
|
+
asyncTimeout = setTimeout(reject, timeout, new LighthouseError(LighthouseError.errors.PROTOCOL_TIMEOUT, {
|
|
347
348
|
protocolMethod: method,
|
|
348
349
|
}));
|
|
349
350
|
});
|
|
@@ -90,7 +90,7 @@ class GatherRunner {
|
|
|
90
90
|
passContext.LighthouseRunWarnings.push(...warnings);
|
|
91
91
|
}
|
|
92
92
|
} catch (err) {
|
|
93
|
-
// If it's one of our loading-based
|
|
93
|
+
// If it's one of our loading-based LighthouseErrors, we'll treat it as a page load error.
|
|
94
94
|
if (err.code === 'NO_FCP' || err.code === 'PAGE_HUNG') {
|
|
95
95
|
return {navigationError: err};
|
|
96
96
|
}
|
|
@@ -604,6 +604,7 @@ class GatherRunner {
|
|
|
604
604
|
url: passContext.url,
|
|
605
605
|
loadFailureMode: passConfig.loadFailureMode,
|
|
606
606
|
networkRecords: loadData.networkRecords,
|
|
607
|
+
warnings: passContext.LighthouseRunWarnings,
|
|
607
608
|
});
|
|
608
609
|
if (pageLoadError) {
|
|
609
610
|
const localizedMessage = format.getFormatted(pageLoadError.friendlyMessage,
|
|
@@ -21,7 +21,6 @@ class MainDocumentContent extends FRGatherer {
|
|
|
21
21
|
};
|
|
22
22
|
|
|
23
23
|
/**
|
|
24
|
-
*
|
|
25
24
|
* @param {LH.Gatherer.FRTransitionalContext} context
|
|
26
25
|
* @param {LH.Artifacts['DevtoolsLog']} devtoolsLog
|
|
27
26
|
* @return {Promise<LH.Artifacts['MainDocumentContent']>}
|
|
@@ -34,7 +33,6 @@ class MainDocumentContent extends FRGatherer {
|
|
|
34
33
|
}
|
|
35
34
|
|
|
36
35
|
/**
|
|
37
|
-
*
|
|
38
36
|
* @param {LH.Gatherer.FRTransitionalContext<'DevtoolsLog'>} context
|
|
39
37
|
* @return {Promise<LH.Artifacts['MainDocumentContent']>}
|
|
40
38
|
*/
|
|
@@ -15,7 +15,7 @@ const lanternTraceSaver = require('./lantern-trace-saver.js');
|
|
|
15
15
|
const Metrics = require('./traces/pwmetrics-events.js');
|
|
16
16
|
const NetworkAnalysisComputed = require('../computed/network-analysis.js');
|
|
17
17
|
const LoadSimulatorComputed = require('../computed/load-simulator.js');
|
|
18
|
-
const
|
|
18
|
+
const LighthouseError = require('../lib/lh-error.js');
|
|
19
19
|
// TODO(esmodules): Rollup does not support `promisfy` or `stream.pipeline`. Bundled files
|
|
20
20
|
// don't need anything in this file except for `stringifyReplacer`, so a check for
|
|
21
21
|
// truthiness before using is enough.
|
|
@@ -48,10 +48,10 @@ function loadArtifacts(basePath) {
|
|
|
48
48
|
throw new Error('No saved artifacts found at ' + basePath);
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
-
// load artifacts.json using a reviver to deserialize any
|
|
51
|
+
// load artifacts.json using a reviver to deserialize any LighthouseErrors in artifacts.
|
|
52
52
|
const artifactsStr = fs.readFileSync(path.join(basePath, artifactsFilename), 'utf8');
|
|
53
53
|
/** @type {LH.Artifacts} */
|
|
54
|
-
const artifacts = JSON.parse(artifactsStr,
|
|
54
|
+
const artifacts = JSON.parse(artifactsStr, LighthouseError.parseReviver);
|
|
55
55
|
|
|
56
56
|
const filenames = fs.readdirSync(basePath);
|
|
57
57
|
|
|
@@ -87,9 +87,9 @@ function loadArtifacts(basePath) {
|
|
|
87
87
|
* @param {any} value
|
|
88
88
|
*/
|
|
89
89
|
function stringifyReplacer(key, value) {
|
|
90
|
-
// Currently only handle
|
|
90
|
+
// Currently only handle LighthouseError and other Error types.
|
|
91
91
|
if (value instanceof Error) {
|
|
92
|
-
return
|
|
92
|
+
return LighthouseError.stringifyReplacer(value);
|
|
93
93
|
}
|
|
94
94
|
|
|
95
95
|
return value;
|
|
@@ -128,7 +128,7 @@ async function saveArtifacts(artifacts, basePath) {
|
|
|
128
128
|
await saveDevtoolsLog(devtoolsLog, `${basePath}/${passName}${devtoolsLogSuffix}`);
|
|
129
129
|
}
|
|
130
130
|
|
|
131
|
-
// save everything else, using a replacer to serialize
|
|
131
|
+
// save everything else, using a replacer to serialize LighthouseErrors in the artifacts.
|
|
132
132
|
const restArtifactsString = JSON.stringify(restArtifacts, stringifyReplacer, 2) + '\n';
|
|
133
133
|
fs.writeFileSync(`${basePath}/${artifactsFilename}`, restArtifactsString, 'utf8');
|
|
134
134
|
log.log('Artifacts saved to disk in folder:', basePath);
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
const process = require('process');
|
|
9
9
|
|
|
10
10
|
module.exports = {
|
|
11
|
-
// NODE_ENV is set to test by
|
|
11
|
+
// NODE_ENV is set to test by mocha-setup.js and the smokehouse CLI runner
|
|
12
12
|
// CI as a catchall for everything we do in GitHub Actions
|
|
13
13
|
isUnderTest: !!process.env.CI || process.env.NODE_ENV === 'test',
|
|
14
14
|
};
|
|
@@ -114,7 +114,7 @@ class LighthouseError extends Error {
|
|
|
114
114
|
*/
|
|
115
115
|
constructor(errorDefinition, properties) {
|
|
116
116
|
super(errorDefinition.code);
|
|
117
|
-
this.name = '
|
|
117
|
+
this.name = 'LighthouseError';
|
|
118
118
|
this.code = errorDefinition.code;
|
|
119
119
|
// Add additional properties to be ICU replacements in the error string.
|
|
120
120
|
// `code` is always added as `errorCode` so callers don't need to specify the code multiple times.
|
|
@@ -148,7 +148,7 @@ class LighthouseError extends Error {
|
|
|
148
148
|
}
|
|
149
149
|
|
|
150
150
|
/**
|
|
151
|
-
* A JSON.stringify replacer to serialize
|
|
151
|
+
* A JSON.stringify replacer to serialize LighthouseErrors and (as a fallback) Errors.
|
|
152
152
|
* Returns a simplified version of the error object that can be reconstituted
|
|
153
153
|
* as a copy of the original error at parse time.
|
|
154
154
|
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#The_replacer_parameter
|
|
@@ -169,7 +169,7 @@ class LighthouseError extends Error {
|
|
|
169
169
|
};
|
|
170
170
|
}
|
|
171
171
|
|
|
172
|
-
// Unexpected errors won't be
|
|
172
|
+
// Unexpected errors won't be LighthouseErrors, but we want them serialized as well.
|
|
173
173
|
if (err instanceof Error) {
|
|
174
174
|
const {message, stack} = err;
|
|
175
175
|
// @ts-expect-error - code can be helpful for e.g. node errors, so preserve it if it's present.
|
|
@@ -182,12 +182,12 @@ class LighthouseError extends Error {
|
|
|
182
182
|
};
|
|
183
183
|
}
|
|
184
184
|
|
|
185
|
-
throw new Error('Invalid value for
|
|
185
|
+
throw new Error('Invalid value for LighthouseError stringification');
|
|
186
186
|
}
|
|
187
187
|
|
|
188
188
|
/**
|
|
189
189
|
* A JSON.parse reviver. If any value passed in is a serialized Error or
|
|
190
|
-
*
|
|
190
|
+
* LighthouseError, the error is recreated as the original object. Otherwise, the
|
|
191
191
|
* value is passed through unchanged.
|
|
192
192
|
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse#Using_the_reviver_parameter
|
|
193
193
|
* @param {string} key
|