lighthouse 9.4.0-dev.20220307 → 9.5.0
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 +54 -0
- package/dist/report/bundle.esm.js +3 -2
- package/dist/report/flow.js +2 -2
- package/dist/report/standalone.js +2 -2
- package/lighthouse-cli/test/smokehouse/lighthouse-runners/devtools.js +4 -1
- package/lighthouse-core/fraggle-rock/api.js +12 -2
- package/lighthouse-core/fraggle-rock/user-flow.js +88 -40
- package/lighthouse-core/gather/gatherers/full-page-screenshot.js +33 -25
- package/lighthouse-core/lib/asset-saver.js +15 -0
- package/package.json +2 -1
- package/report/assets/templates.html +2 -1
- package/report/renderer/components.js +1 -1
- package/report/renderer/topbar-features.js +2 -1
- package/third-party/snyk/snapshot.json +1 -0
- package/types/global-lh.d.ts +3 -0
- package/types/user-flow.d.ts +19 -0
|
@@ -75,10 +75,13 @@ async function runLighthouse(url, configJson, testRunnerOptions = {}) {
|
|
|
75
75
|
await buildDevtoolsPromise;
|
|
76
76
|
|
|
77
77
|
const outputDir = fs.mkdtempSync(os.tmpdir() + '/lh-smoke-cdt-runner-');
|
|
78
|
+
const chromeFlags = [
|
|
79
|
+
`--custom-devtools-frontend=file://${devtoolsDir}/out/Default/gen/front_end`,
|
|
80
|
+
];
|
|
78
81
|
const args = [
|
|
79
82
|
'run-devtools',
|
|
80
83
|
url,
|
|
81
|
-
`--
|
|
84
|
+
`--chrome-flags=${chromeFlags.join(' ')}`,
|
|
82
85
|
'--output-dir', outputDir,
|
|
83
86
|
];
|
|
84
87
|
if (configJson) {
|
|
@@ -5,16 +5,16 @@
|
|
|
5
5
|
*/
|
|
6
6
|
'use strict';
|
|
7
7
|
|
|
8
|
+
const {UserFlow, auditGatherSteps} = require('./user-flow.js');
|
|
8
9
|
const {snapshotGather} = require('./gather/snapshot-runner.js');
|
|
9
10
|
const {startTimespanGather} = require('./gather/timespan-runner.js');
|
|
10
11
|
const {navigationGather} = require('./gather/navigation-runner.js');
|
|
11
12
|
const {generateFlowReportHtml} = require('../../report/generator/report-generator.js');
|
|
12
13
|
const Runner = require('../runner.js');
|
|
13
|
-
const UserFlow = require('./user-flow.js');
|
|
14
14
|
|
|
15
15
|
/**
|
|
16
16
|
* @param {import('puppeteer').Page} page
|
|
17
|
-
* @param {UserFlow
|
|
17
|
+
* @param {ConstructorParameters<LH.UserFlow>[1]} [options]
|
|
18
18
|
*/
|
|
19
19
|
async function startFlow(page, options) {
|
|
20
20
|
return new UserFlow(page, options);
|
|
@@ -58,10 +58,20 @@ async function generateFlowReport(flowResult) {
|
|
|
58
58
|
return generateFlowReportHtml(flowResult);
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
+
/**
|
|
62
|
+
* @param {LH.UserFlow.FlowArtifacts} flowArtifacts
|
|
63
|
+
* @param {LH.Config.Json} [config]
|
|
64
|
+
*/
|
|
65
|
+
async function auditFlowArtifacts(flowArtifacts, config) {
|
|
66
|
+
const {gatherSteps, name} = flowArtifacts;
|
|
67
|
+
return await auditGatherSteps(gatherSteps, {name, config});
|
|
68
|
+
}
|
|
69
|
+
|
|
61
70
|
module.exports = {
|
|
62
71
|
snapshot,
|
|
63
72
|
startTimespan,
|
|
64
73
|
navigation,
|
|
65
74
|
startFlow,
|
|
66
75
|
generateFlowReport,
|
|
76
|
+
auditFlowArtifacts,
|
|
67
77
|
};
|
|
@@ -10,11 +10,12 @@ const {snapshotGather} = require('./gather/snapshot-runner.js');
|
|
|
10
10
|
const {startTimespanGather} = require('./gather/timespan-runner.js');
|
|
11
11
|
const {navigationGather} = require('./gather/navigation-runner.js');
|
|
12
12
|
const Runner = require('../runner.js');
|
|
13
|
+
const {initializeConfig} = require('./config/config.js');
|
|
13
14
|
|
|
14
15
|
/** @typedef {Parameters<snapshotGather>[0]} FrOptions */
|
|
15
16
|
/** @typedef {Omit<FrOptions, 'page'> & {name?: string}} UserFlowOptions */
|
|
16
17
|
/** @typedef {Omit<FrOptions, 'page'> & {stepName?: string}} StepOptions */
|
|
17
|
-
/** @typedef {
|
|
18
|
+
/** @typedef {WeakMap<LH.UserFlow.GatherStep, LH.Gatherer.FRGatherResult['runnerOptions']>} GatherStepRunnerOptions */
|
|
18
19
|
|
|
19
20
|
class UserFlow {
|
|
20
21
|
/**
|
|
@@ -26,8 +27,10 @@ class UserFlow {
|
|
|
26
27
|
this.options = {page, ...options};
|
|
27
28
|
/** @type {string|undefined} */
|
|
28
29
|
this.name = options?.name;
|
|
29
|
-
/** @type {
|
|
30
|
-
this.
|
|
30
|
+
/** @type {LH.UserFlow.GatherStep[]} */
|
|
31
|
+
this._gatherSteps = [];
|
|
32
|
+
/** @type {GatherStepRunnerOptions} */
|
|
33
|
+
this._gatherStepRunnerOptions = new WeakMap();
|
|
31
34
|
}
|
|
32
35
|
|
|
33
36
|
/**
|
|
@@ -68,8 +71,8 @@ class UserFlow {
|
|
|
68
71
|
}
|
|
69
72
|
|
|
70
73
|
// On repeat navigations, we want to disable storage reset by default (i.e. it's not a cold load).
|
|
71
|
-
const isSubsequentNavigation = this.
|
|
72
|
-
.some(step => step.
|
|
74
|
+
const isSubsequentNavigation = this._gatherSteps
|
|
75
|
+
.some(step => step.artifacts.GatherContext.gatherMode === 'navigation');
|
|
73
76
|
if (isSubsequentNavigation) {
|
|
74
77
|
if (settingsOverrides.disableStorageReset === undefined) {
|
|
75
78
|
settingsOverrides.disableStorageReset = true;
|
|
@@ -82,6 +85,23 @@ class UserFlow {
|
|
|
82
85
|
return options;
|
|
83
86
|
}
|
|
84
87
|
|
|
88
|
+
/**
|
|
89
|
+
*
|
|
90
|
+
* @param {LH.Gatherer.FRGatherResult} gatherResult
|
|
91
|
+
* @param {StepOptions} options
|
|
92
|
+
*/
|
|
93
|
+
_addGatherStep(gatherResult, options) {
|
|
94
|
+
const providedName = options?.stepName;
|
|
95
|
+
const gatherStep = {
|
|
96
|
+
artifacts: gatherResult.artifacts,
|
|
97
|
+
name: providedName || this._getDefaultStepName(gatherResult.artifacts),
|
|
98
|
+
config: options.config,
|
|
99
|
+
configContext: options.configContext,
|
|
100
|
+
};
|
|
101
|
+
this._gatherSteps.push(gatherStep);
|
|
102
|
+
this._gatherStepRunnerOptions.set(gatherStep, gatherResult.runnerOptions);
|
|
103
|
+
}
|
|
104
|
+
|
|
85
105
|
/**
|
|
86
106
|
* @param {LH.NavigationRequestor} requestor
|
|
87
107
|
* @param {StepOptions=} stepOptions
|
|
@@ -89,16 +109,10 @@ class UserFlow {
|
|
|
89
109
|
async navigate(requestor, stepOptions) {
|
|
90
110
|
if (this.currentTimespan) throw new Error('Timespan already in progress');
|
|
91
111
|
|
|
92
|
-
const
|
|
93
|
-
|
|
94
|
-
this._getNextNavigationOptions(stepOptions)
|
|
95
|
-
);
|
|
112
|
+
const options = this._getNextNavigationOptions(stepOptions);
|
|
113
|
+
const gatherResult = await navigationGather(requestor, options);
|
|
96
114
|
|
|
97
|
-
|
|
98
|
-
this.stepArtifacts.push({
|
|
99
|
-
gatherResult,
|
|
100
|
-
name: providedName || this._getDefaultStepName(gatherResult.artifacts),
|
|
101
|
-
});
|
|
115
|
+
this._addGatherStep(gatherResult, options);
|
|
102
116
|
|
|
103
117
|
return gatherResult;
|
|
104
118
|
}
|
|
@@ -121,11 +135,7 @@ class UserFlow {
|
|
|
121
135
|
const gatherResult = await timespan.endTimespanGather();
|
|
122
136
|
this.currentTimespan = undefined;
|
|
123
137
|
|
|
124
|
-
|
|
125
|
-
this.stepArtifacts.push({
|
|
126
|
-
gatherResult,
|
|
127
|
-
name: providedName || this._getDefaultStepName(gatherResult.artifacts),
|
|
128
|
-
});
|
|
138
|
+
this._addGatherStep(gatherResult, options);
|
|
129
139
|
|
|
130
140
|
return gatherResult;
|
|
131
141
|
}
|
|
@@ -139,11 +149,7 @@ class UserFlow {
|
|
|
139
149
|
const options = {...this.options, ...stepOptions};
|
|
140
150
|
const gatherResult = await snapshotGather(options);
|
|
141
151
|
|
|
142
|
-
|
|
143
|
-
this.stepArtifacts.push({
|
|
144
|
-
gatherResult,
|
|
145
|
-
name: providedName || this._getDefaultStepName(gatherResult.artifacts),
|
|
146
|
-
});
|
|
152
|
+
this._addGatherStep(gatherResult, options);
|
|
147
153
|
|
|
148
154
|
return gatherResult;
|
|
149
155
|
}
|
|
@@ -152,21 +158,11 @@ class UserFlow {
|
|
|
152
158
|
* @returns {Promise<LH.FlowResult>}
|
|
153
159
|
*/
|
|
154
160
|
async createFlowResult() {
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
/** @type {LH.FlowResult['steps']} */
|
|
162
|
-
const steps = [];
|
|
163
|
-
for (const {gatherResult, name} of this.stepArtifacts) {
|
|
164
|
-
const result = await Runner.audit(gatherResult.artifacts, gatherResult.runnerOptions);
|
|
165
|
-
if (!result) throw new Error(`Step "${name}" did not return a result`);
|
|
166
|
-
steps.push({lhr: result.lhr, name});
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
return {steps, name: flowName};
|
|
161
|
+
return auditGatherSteps(this._gatherSteps, {
|
|
162
|
+
name: this.name,
|
|
163
|
+
config: this.options.config,
|
|
164
|
+
gatherStepRunnerOptions: this._gatherStepRunnerOptions,
|
|
165
|
+
});
|
|
170
166
|
}
|
|
171
167
|
|
|
172
168
|
/**
|
|
@@ -176,6 +172,58 @@ class UserFlow {
|
|
|
176
172
|
const flowResult = await this.createFlowResult();
|
|
177
173
|
return generateFlowReportHtml(flowResult);
|
|
178
174
|
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* @return {LH.UserFlow.FlowArtifacts}
|
|
178
|
+
*/
|
|
179
|
+
createArtifactsJson() {
|
|
180
|
+
return {
|
|
181
|
+
gatherSteps: this._gatherSteps,
|
|
182
|
+
name: this.name,
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* @param {Array<LH.UserFlow.GatherStep>} gatherSteps
|
|
189
|
+
* @param {{name?: string, config?: LH.Config.Json, gatherStepRunnerOptions?: GatherStepRunnerOptions}} options
|
|
190
|
+
*/
|
|
191
|
+
async function auditGatherSteps(gatherSteps, options) {
|
|
192
|
+
if (!gatherSteps.length) {
|
|
193
|
+
throw new Error('Need at least one step before getting the result');
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/** @type {LH.FlowResult['steps']} */
|
|
197
|
+
const steps = [];
|
|
198
|
+
for (const gatherStep of gatherSteps) {
|
|
199
|
+
const {artifacts, name, configContext} = gatherStep;
|
|
200
|
+
|
|
201
|
+
let runnerOptions = options.gatherStepRunnerOptions?.get(gatherStep);
|
|
202
|
+
|
|
203
|
+
// If the gather step is not active, we must recreate the runner options.
|
|
204
|
+
if (!runnerOptions) {
|
|
205
|
+
// Step specific configs take precedence over a config for the entire flow.
|
|
206
|
+
const configJson = gatherStep.config || options.config;
|
|
207
|
+
const {gatherMode} = artifacts.GatherContext;
|
|
208
|
+
const {config} = initializeConfig(configJson, {...configContext, gatherMode});
|
|
209
|
+
runnerOptions = {
|
|
210
|
+
config,
|
|
211
|
+
computedCache: new Map(),
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
const result = await Runner.audit(artifacts, runnerOptions);
|
|
216
|
+
if (!result) throw new Error(`Step "${name}" did not return a result`);
|
|
217
|
+
steps.push({lhr: result.lhr, name});
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
const url = new URL(gatherSteps[0].artifacts.URL.finalUrl);
|
|
221
|
+
const flowName = options.name || `User flow (${url.hostname})`;
|
|
222
|
+
return {steps, name: flowName};
|
|
179
223
|
}
|
|
180
224
|
|
|
181
|
-
|
|
225
|
+
|
|
226
|
+
module.exports = {
|
|
227
|
+
UserFlow,
|
|
228
|
+
auditGatherSteps,
|
|
229
|
+
};
|
|
@@ -70,21 +70,22 @@ class FullPageScreenshot extends FRGatherer {
|
|
|
70
70
|
|
|
71
71
|
/**
|
|
72
72
|
* @param {LH.Gatherer.FRTransitionalContext} context
|
|
73
|
+
* @param {{height: number, width: number, mobile: boolean}} deviceMetrics
|
|
73
74
|
* @return {Promise<LH.Artifacts.FullPageScreenshot['screenshot']>}
|
|
74
75
|
*/
|
|
75
|
-
async _takeScreenshot(context) {
|
|
76
|
+
async _takeScreenshot(context, deviceMetrics) {
|
|
76
77
|
const session = context.driver.defaultSession;
|
|
77
78
|
const maxTextureSize = await this.getMaxTextureSize(context);
|
|
78
79
|
const metrics = await session.sendCommand('Page.getLayoutMetrics');
|
|
79
80
|
|
|
80
|
-
//
|
|
81
|
-
//
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
const height = Math.min(
|
|
81
|
+
// Height should be as tall as the content.
|
|
82
|
+
// Scale the emulated height to reach the content height.
|
|
83
|
+
const fullHeight = Math.round(
|
|
84
|
+
deviceMetrics.height *
|
|
85
|
+
metrics.contentSize.height /
|
|
86
|
+
metrics.layoutViewport.clientHeight
|
|
87
|
+
);
|
|
88
|
+
const height = Math.min(fullHeight, maxTextureSize);
|
|
88
89
|
|
|
89
90
|
// Setup network monitor before we change the viewport.
|
|
90
91
|
const networkMonitor = new NetworkMonitor(session);
|
|
@@ -98,13 +99,10 @@ class FullPageScreenshot extends FRGatherer {
|
|
|
98
99
|
await networkMonitor.enable();
|
|
99
100
|
|
|
100
101
|
await session.sendCommand('Emulation.setDeviceMetricsOverride', {
|
|
101
|
-
|
|
102
|
-
mobile: context.settings.screenEmulation.mobile,
|
|
103
|
-
height,
|
|
104
|
-
width,
|
|
102
|
+
mobile: deviceMetrics.mobile,
|
|
105
103
|
deviceScaleFactor: 1,
|
|
106
|
-
|
|
107
|
-
|
|
104
|
+
height,
|
|
105
|
+
width: 0, // Leave width unchanged
|
|
108
106
|
});
|
|
109
107
|
|
|
110
108
|
// Now that the viewport is taller, give the page some time to fetch new resources that
|
|
@@ -127,7 +125,7 @@ class FullPageScreenshot extends FRGatherer {
|
|
|
127
125
|
|
|
128
126
|
return {
|
|
129
127
|
data,
|
|
130
|
-
width,
|
|
128
|
+
width: deviceMetrics.width,
|
|
131
129
|
height,
|
|
132
130
|
};
|
|
133
131
|
}
|
|
@@ -185,29 +183,37 @@ class FullPageScreenshot extends FRGatherer {
|
|
|
185
183
|
const session = context.driver.defaultSession;
|
|
186
184
|
const executionContext = context.driver.executionContext;
|
|
187
185
|
const settings = context.settings;
|
|
188
|
-
|
|
189
|
-
// In case some other program is controlling emulation, remember what the device looks
|
|
190
|
-
// like now and reset after gatherer is done.
|
|
191
|
-
let observedDeviceMetrics;
|
|
192
186
|
const lighthouseControlsEmulation = !settings.screenEmulation.disabled;
|
|
187
|
+
|
|
188
|
+
// Make a copy so we don't modify the config settings.
|
|
189
|
+
/** @type {{width: number, height: number, deviceScaleFactor: number, mobile: boolean}} */
|
|
190
|
+
const deviceMetrics = {...settings.screenEmulation};
|
|
191
|
+
|
|
192
|
+
// In case some other program is controlling emulation, remember what the device looks like now and reset after gatherer is done.
|
|
193
|
+
// If we're gathering with mobile screenEmulation on (overlay scrollbars, etc), continue to use that for this screenshot.
|
|
193
194
|
if (!lighthouseControlsEmulation) {
|
|
194
|
-
observedDeviceMetrics = await executionContext.evaluate(getObservedDeviceMetrics, {
|
|
195
|
+
const observedDeviceMetrics = await executionContext.evaluate(getObservedDeviceMetrics, {
|
|
195
196
|
args: [],
|
|
196
197
|
useIsolation: true,
|
|
197
198
|
deps: [kebabCaseToCamelCase],
|
|
198
199
|
});
|
|
200
|
+
deviceMetrics.height = observedDeviceMetrics.height;
|
|
201
|
+
deviceMetrics.width = observedDeviceMetrics.width;
|
|
202
|
+
deviceMetrics.deviceScaleFactor = observedDeviceMetrics.deviceScaleFactor;
|
|
203
|
+
// If screen emulation is disabled, use formFactor to determine if we are on mobile.
|
|
204
|
+
deviceMetrics.mobile = settings.formFactor === 'mobile';
|
|
199
205
|
}
|
|
200
206
|
|
|
201
207
|
try {
|
|
202
208
|
return {
|
|
203
|
-
screenshot: await this._takeScreenshot(context),
|
|
209
|
+
screenshot: await this._takeScreenshot(context, deviceMetrics),
|
|
204
210
|
nodes: await this._resolveNodes(context),
|
|
205
211
|
};
|
|
206
212
|
} finally {
|
|
207
213
|
// Revert resized page.
|
|
208
214
|
if (lighthouseControlsEmulation) {
|
|
209
215
|
await emulation.emulate(session, settings);
|
|
210
|
-
} else
|
|
216
|
+
} else {
|
|
211
217
|
// Best effort to reset emulation to what it was.
|
|
212
218
|
// https://github.com/GoogleChrome/lighthouse/pull/10716#discussion_r428970681
|
|
213
219
|
// TODO: seems like this would be brittle. Should at least work for devtools, but what
|
|
@@ -216,8 +222,10 @@ class FullPageScreenshot extends FRGatherer {
|
|
|
216
222
|
// and then just call that to reset?
|
|
217
223
|
// https://github.com/GoogleChrome/lighthouse/issues/11122
|
|
218
224
|
await session.sendCommand('Emulation.setDeviceMetricsOverride', {
|
|
219
|
-
mobile:
|
|
220
|
-
|
|
225
|
+
mobile: deviceMetrics.mobile,
|
|
226
|
+
deviceScaleFactor: deviceMetrics.deviceScaleFactor,
|
|
227
|
+
height: deviceMetrics.height,
|
|
228
|
+
width: 0, // Leave width unchanged
|
|
221
229
|
});
|
|
222
230
|
}
|
|
223
231
|
}
|
|
@@ -313,6 +313,20 @@ async function saveLanternNetworkData(devtoolsLog, outputPath) {
|
|
|
313
313
|
fs.writeFileSync(outputPath, JSON.stringify(lanternData));
|
|
314
314
|
}
|
|
315
315
|
|
|
316
|
+
/**
|
|
317
|
+
* Normalize timing data so it doesn't change every update.
|
|
318
|
+
* @param {LH.Result.MeasureEntry[]} timings
|
|
319
|
+
*/
|
|
320
|
+
function normalizeTimingEntries(timings) {
|
|
321
|
+
let baseTime = 0;
|
|
322
|
+
for (const timing of timings) {
|
|
323
|
+
// @ts-expect-error: Value actually is writeable at this point.
|
|
324
|
+
timing.startTime = baseTime++;
|
|
325
|
+
// @ts-expect-error: Value actually is writeable at this point.
|
|
326
|
+
timing.duration = 1;
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
|
|
316
330
|
module.exports = {
|
|
317
331
|
saveArtifacts,
|
|
318
332
|
saveLhr,
|
|
@@ -323,4 +337,5 @@ module.exports = {
|
|
|
323
337
|
saveDevtoolsLog,
|
|
324
338
|
saveLanternNetworkData,
|
|
325
339
|
stringifyReplacer,
|
|
340
|
+
normalizeTimingEntries,
|
|
326
341
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lighthouse",
|
|
3
|
-
"version": "9.
|
|
3
|
+
"version": "9.5.0",
|
|
4
4
|
"description": "Automated auditing, performance metrics, and best practices for the web.",
|
|
5
5
|
"main": "./lighthouse-core/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -76,6 +76,7 @@
|
|
|
76
76
|
"update:sample-artifacts": "node lighthouse-core/scripts/update-report-fixtures.js",
|
|
77
77
|
"update:sample-json": "yarn i18n:collect-strings && node ./lighthouse-cli -A=./lighthouse-core/test/results/artifacts --config-path=./lighthouse-core/test/results/sample-config.js --output=json --output-path=./lighthouse-core/test/results/sample_v2.json && node lighthouse-core/scripts/cleanup-LHR-for-diff.js ./lighthouse-core/test/results/sample_v2.json --only-remove-timing",
|
|
78
78
|
"update:flow-sample-json": "node ./lighthouse-core/scripts/update-flow-fixtures.js",
|
|
79
|
+
"update:snapshot-sample-json": "node ./lighthouse-core/scripts/update-snapshot-sample.js",
|
|
79
80
|
"update:test-devtools": "bash lighthouse-core/test/chromium-web-tests/test-locally.sh --reset-results",
|
|
80
81
|
"test-devtools": "bash lighthouse-core/test/chromium-web-tests/test-locally.sh",
|
|
81
82
|
"open-devtools": "bash lighthouse-core/scripts/open-devtools.sh",
|
|
@@ -445,7 +445,7 @@ function createScorescaleComponent(dom) {
|
|
|
445
445
|
function createScoresWrapperComponent(dom) {
|
|
446
446
|
const el0 = dom.createFragment();
|
|
447
447
|
const el1 = dom.createElement('style');
|
|
448
|
-
el1.append('\n .lh-scores-container {\n display: flex;\n flex-direction: column;\n padding: var(--default-padding) 0;\n position: relative;\n width: 100%;\n }\n\n .lh-sticky-header {\n --gauge-circle-size: var(--gauge-circle-size-sm);\n --plugin-badge-size: 16px;\n --plugin-icon-size: 75%;\n --gauge-wrapper-width: 60px;\n --gauge-percentage-font-size: 13px;\n position: fixed;\n left: 0;\n right: 0;\n top: var(--topbar-height);\n font-weight: 500;\n display: none;\n justify-content: center;\n background-color: var(--sticky-header-background-color);\n border-bottom: 1px solid var(--color-gray-200);\n padding-top: var(--score-container-padding);\n padding-bottom: 4px;\n z-index: 1;\n pointer-events: none;\n }\n\n .lh-devtools .lh-sticky-header {\n /* The report within DevTools is placed in a container with overflow, which changes the placement of this header unless we change `position` to `sticky.` */\n position: sticky;\n }\n\n .lh-sticky-header--visible {\n display: grid;\n grid-auto-flow: column;\n pointer-events: auto;\n }\n\n /* Disable the gauge arc animation for the sticky header, so toggling display: none\n does not play the animation. */\n .lh-sticky-header .lh-gauge-arc {\n animation: none;\n }\n\n .lh-sticky-header .lh-gauge__label {\n display: none;\n }\n\n .lh-highlighter {\n width: var(--gauge-wrapper-width);\n height: 1px;\n background-color: var(--highlighter-background-color);\n /* Position at bottom of first gauge in sticky header. */\n position: absolute;\n grid-column: 1;\n bottom: -1px;\n }\n\n .lh-gauge__wrapper:first-of-type {\n contain: none;\n }\n ');
|
|
448
|
+
el1.append('\n .lh-scores-container {\n display: flex;\n flex-direction: column;\n padding: var(--default-padding) 0;\n position: relative;\n width: 100%;\n }\n\n .lh-sticky-header {\n --gauge-circle-size: var(--gauge-circle-size-sm);\n --plugin-badge-size: 16px;\n --plugin-icon-size: 75%;\n --gauge-wrapper-width: 60px;\n --gauge-percentage-font-size: 13px;\n position: fixed;\n left: 0;\n right: 0;\n top: var(--topbar-height);\n font-weight: 500;\n display: none;\n justify-content: center;\n background-color: var(--sticky-header-background-color);\n border-bottom: 1px solid var(--color-gray-200);\n padding-top: var(--score-container-padding);\n padding-bottom: 4px;\n z-index: 1;\n pointer-events: none;\n }\n\n .lh-devtools .lh-sticky-header {\n /* The report within DevTools is placed in a container with overflow, which changes the placement of this header unless we change `position` to `sticky.` */\n position: sticky;\n }\n\n .lh-sticky-header--visible {\n display: grid;\n grid-auto-flow: column;\n pointer-events: auto;\n }\n\n /* Disable the gauge arc animation for the sticky header, so toggling display: none\n does not play the animation. */\n .lh-sticky-header .lh-gauge-arc {\n animation: none;\n }\n\n .lh-sticky-header .lh-gauge__label,\n .lh-sticky-header .lh-fraction__label {\n display: none;\n }\n\n .lh-highlighter {\n width: var(--gauge-wrapper-width);\n height: 1px;\n background-color: var(--highlighter-background-color);\n /* Position at bottom of first gauge in sticky header. */\n position: absolute;\n grid-column: 1;\n bottom: -1px;\n }\n\n .lh-gauge__wrapper:first-of-type {\n contain: none;\n }\n ');
|
|
449
449
|
el0.append(el1);
|
|
450
450
|
const el2 = dom.createElement('div', 'lh-scores-wrapper');
|
|
451
451
|
const el3 = dom.createElement('div', 'lh-scores-container');
|
|
@@ -306,7 +306,8 @@ export class TopbarFeatures {
|
|
|
306
306
|
categoriesAboveTheMiddle.length > 0 ? categoriesAboveTheMiddle.length - 1 : 0;
|
|
307
307
|
|
|
308
308
|
// Category order matches gauge order in sticky header.
|
|
309
|
-
const gaugeWrapperEls =
|
|
309
|
+
const gaugeWrapperEls =
|
|
310
|
+
this.stickyHeaderEl.querySelectorAll('.lh-gauge__wrapper, .lh-fraction__wrapper');
|
|
310
311
|
const gaugeToHighlight = gaugeWrapperEls[highlightIndex];
|
|
311
312
|
const origin = gaugeWrapperEls[0].getBoundingClientRect().left;
|
|
312
313
|
const offset = gaugeToHighlight.getBoundingClientRect().left - origin;
|
|
@@ -133,6 +133,7 @@
|
|
|
133
133
|
{"id":"npm:mustache:20110814","severity":"medium","semver":{"vulnerable":["< 0.3.1"]}}
|
|
134
134
|
],
|
|
135
135
|
"next":[
|
|
136
|
+
{"id":"SNYK-JS-NEXT-2405694","severity":"medium","semver":{"vulnerable":[">=10.0.0 <12.1.0"]}},
|
|
136
137
|
{"id":"SNYK-JS-NEXT-2388583","severity":"medium","semver":{"vulnerable":[">=12.0.0 <12.0.9"]}},
|
|
137
138
|
{"id":"SNYK-JS-NEXT-2312745","severity":"high","semver":{"vulnerable":[">=12.0.0 <12.0.5",">=11.1.0 <11.1.3"]}},
|
|
138
139
|
{"id":"SNYK-JS-NEXT-1577139","severity":"medium","semver":{"vulnerable":[">=10.0.0 <11.1.1"]}},
|
package/types/global-lh.d.ts
CHANGED
|
@@ -21,6 +21,7 @@ import Protocol_ from './protocol';
|
|
|
21
21
|
import * as Settings from './lhr/settings';
|
|
22
22
|
import StructuredData_ from './structured-data';
|
|
23
23
|
import Treemap_ from './lhr/treemap';
|
|
24
|
+
import UserFlow_ from './user-flow';
|
|
24
25
|
|
|
25
26
|
// Construct hierarchy of global types under the LH namespace.
|
|
26
27
|
declare global {
|
|
@@ -46,6 +47,8 @@ declare global {
|
|
|
46
47
|
export import CrdpEvents = _CrdpMappings.Events;
|
|
47
48
|
export import CrdpCommands = _CrdpMappings.Commands;
|
|
48
49
|
|
|
50
|
+
export import UserFlow = UserFlow_;
|
|
51
|
+
|
|
49
52
|
// externs.d.ts
|
|
50
53
|
export import Flags = Externs.Flags;
|
|
51
54
|
export import CliFlags = Externs.CliFlags;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import {UserFlow as UserFlow_} from '../lighthouse-core/fraggle-rock/user-flow';
|
|
2
|
+
|
|
3
|
+
declare module UserFlow {
|
|
4
|
+
export interface FlowArtifacts {
|
|
5
|
+
gatherSteps: GatherStep[];
|
|
6
|
+
name?: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface GatherStep {
|
|
10
|
+
artifacts: LH.Artifacts;
|
|
11
|
+
name: string;
|
|
12
|
+
config?: LH.Config.Json;
|
|
13
|
+
configContext?: LH.Config.FRContext;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
type UserFlow = typeof UserFlow_;
|
|
18
|
+
|
|
19
|
+
export default UserFlow;
|