lighthouse 9.4.0 → 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 +5902 -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/run.js +5 -1
- package/lighthouse-cli/test/smokehouse/core-tests.js +4 -0
- package/lighthouse-cli/test/smokehouse/frontends/lib.js +3 -1
- package/lighthouse-cli/test/smokehouse/frontends/smokehouse-bin.js +4 -1
- package/lighthouse-cli/test/smokehouse/lighthouse-runners/devtools.js +4 -1
- package/lighthouse-cli/test/smokehouse/readme.md +14 -0
- package/lighthouse-cli/test/smokehouse/report-assert.js +64 -28
- package/lighthouse-cli/test/smokehouse/smokehouse.js +5 -2
- package/lighthouse-core/audits/dobetterweb/inspector-issues.js +6 -6
- package/lighthouse-core/audits/script-treemap-data.js +1 -1
- package/lighthouse-core/config/config-helpers.js +1 -1
- package/lighthouse-core/fraggle-rock/api.js +56 -5
- package/lighthouse-core/fraggle-rock/config/config.js +1 -1
- package/lighthouse-core/fraggle-rock/gather/base-artifacts.js +1 -1
- package/lighthouse-core/fraggle-rock/gather/navigation-runner.js +8 -4
- package/lighthouse-core/fraggle-rock/gather/runner-helpers.js +3 -1
- package/lighthouse-core/fraggle-rock/gather/snapshot-runner.js +12 -5
- package/lighthouse-core/fraggle-rock/gather/timespan-runner.js +13 -7
- package/lighthouse-core/fraggle-rock/user-flow.js +114 -55
- package/lighthouse-core/gather/gatherers/full-page-screenshot.js +33 -25
- package/lighthouse-core/gather/gatherers/inspector-issues.js +1 -1
- package/lighthouse-core/lib/arbitrary-equality-map.js +1 -1
- package/lighthouse-core/lib/asset-saver.js +15 -0
- package/lighthouse-core/runner.js +1 -1
- package/lighthouse-core/scripts/package.json +4 -0
- package/package.json +7 -12
- package/report/assets/templates.html +2 -1
- package/report/renderer/components.js +1 -1
- package/report/renderer/topbar-features.js +2 -1
- package/shared/localization/swap-locale.js +1 -2
- package/third-party/chromium-synchronization/inspector-issueAdded-types-test.js +1 -1
- package/third-party/snyk/snapshot.json +4 -3
- package/types/artifacts.d.ts +1 -1
- package/types/config.d.ts +1 -1
- package/types/gatherer.d.ts +8 -0
- package/types/global-lh.d.ts +3 -0
- package/types/smokehouse.d.ts +1 -1
- package/types/user-flow.d.ts +19 -0
package/lighthouse-cli/run.js
CHANGED
|
@@ -207,7 +207,11 @@ async function runLighthouseWithFraggleRock(url, flags, config, launchedChrome)
|
|
|
207
207
|
const browser = await puppeteer.connect({browserURL: `http://localhost:${launchedChrome.port}`});
|
|
208
208
|
const page = await browser.newPage();
|
|
209
209
|
flags.channel = 'fraggle-rock-cli';
|
|
210
|
-
const configContext = {
|
|
210
|
+
const configContext = {
|
|
211
|
+
configPath: flags.configPath,
|
|
212
|
+
settingsOverrides: flags,
|
|
213
|
+
logLevel: flags.logLevel,
|
|
214
|
+
};
|
|
211
215
|
return fraggleRock.navigation(url, {page, config, configContext});
|
|
212
216
|
}
|
|
213
217
|
|
|
@@ -33,6 +33,7 @@ import offlineReady from './test-definitions/offline-ready.js';
|
|
|
33
33
|
import offlineSwBroken from './test-definitions/offline-sw-broken.js';
|
|
34
34
|
import offlineSwSlow from './test-definitions/offline-sw-slow.js';
|
|
35
35
|
import oopifRequests from './test-definitions/oopif-requests.js';
|
|
36
|
+
import oopifScripts from './test-definitions/oopif-scripts.js';
|
|
36
37
|
import perfBudgets from './test-definitions/perf-budgets.js';
|
|
37
38
|
import perfDebug from './test-definitions/perf-debug.js';
|
|
38
39
|
import perfDiagnosticsAnimations from './test-definitions/perf-diagnostics-animations.js';
|
|
@@ -53,6 +54,7 @@ import redirectsHistoryPushState from './test-definitions/redirects-history-push
|
|
|
53
54
|
import redirectsMultipleServer from './test-definitions/redirects-multiple-server.js';
|
|
54
55
|
import redirectsSingleClient from './test-definitions/redirects-single-client.js';
|
|
55
56
|
import redirectsSingleServer from './test-definitions/redirects-single-server.js';
|
|
57
|
+
import redirectsSelf from './test-definitions/redirects-self.js';
|
|
56
58
|
import screenshot from './test-definitions/screenshot.js';
|
|
57
59
|
import seoFailing from './test-definitions/seo-failing.js';
|
|
58
60
|
import seoPassing from './test-definitions/seo-passing.js';
|
|
@@ -91,6 +93,7 @@ const smokeTests = [
|
|
|
91
93
|
offlineSwBroken,
|
|
92
94
|
offlineSwSlow,
|
|
93
95
|
oopifRequests,
|
|
96
|
+
oopifScripts,
|
|
94
97
|
perfBudgets,
|
|
95
98
|
perfDebug,
|
|
96
99
|
perfDiagnosticsAnimations,
|
|
@@ -111,6 +114,7 @@ const smokeTests = [
|
|
|
111
114
|
redirectsMultipleServer,
|
|
112
115
|
redirectsSingleClient,
|
|
113
116
|
redirectsSingleServer,
|
|
117
|
+
redirectsSelf,
|
|
114
118
|
screenshot,
|
|
115
119
|
seoFailing,
|
|
116
120
|
seoPassing,
|
|
@@ -13,11 +13,13 @@
|
|
|
13
13
|
|
|
14
14
|
/* eslint-disable no-console */
|
|
15
15
|
|
|
16
|
-
import
|
|
16
|
+
import _ from 'lodash';
|
|
17
17
|
|
|
18
18
|
import smokeTests from '../core-tests.js';
|
|
19
19
|
import {runSmokehouse, getShardedDefinitions} from '../smokehouse.js';
|
|
20
20
|
|
|
21
|
+
const {cloneDeep} = _;
|
|
22
|
+
|
|
21
23
|
/**
|
|
22
24
|
* @param {Smokehouse.SmokehouseLibOptions} options
|
|
23
25
|
*/
|
|
@@ -17,7 +17,7 @@ import path from 'path';
|
|
|
17
17
|
import fs from 'fs';
|
|
18
18
|
import url from 'url';
|
|
19
19
|
|
|
20
|
-
import
|
|
20
|
+
import _ from 'lodash';
|
|
21
21
|
import yargs from 'yargs';
|
|
22
22
|
import * as yargsHelpers from 'yargs/helpers';
|
|
23
23
|
import log from 'lighthouse-logger';
|
|
@@ -26,6 +26,8 @@ import {runSmokehouse, getShardedDefinitions} from '../smokehouse.js';
|
|
|
26
26
|
import {updateTestDefnFormat} from './back-compat-util.js';
|
|
27
27
|
import {LH_ROOT} from '../../../../root.js';
|
|
28
28
|
|
|
29
|
+
const {cloneDeep} = _;
|
|
30
|
+
|
|
29
31
|
const coreTestDefnsPath =
|
|
30
32
|
path.join(LH_ROOT, 'lighthouse-cli/test/smokehouse/core-tests.js');
|
|
31
33
|
|
|
@@ -186,6 +188,7 @@ async function begin() {
|
|
|
186
188
|
console.log('\n✨ Be sure to have recently run this: yarn build-all');
|
|
187
189
|
}
|
|
188
190
|
const {runLighthouse} = await import(runnerPath);
|
|
191
|
+
runLighthouse.runnerName = argv.runner;
|
|
189
192
|
|
|
190
193
|
// Find test definition file and filter by requestedTestIds.
|
|
191
194
|
let testDefnPath = argv.testsPath || coreTestDefnsPath;
|
|
@@ -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) {
|
|
@@ -66,6 +66,8 @@ However, if an array literal is used as the expectation, an extra condition is e
|
|
|
66
66
|
|
|
67
67
|
Arrays can be checked against a subset of elements using the special `_includes` property. The value of `_includes` _must_ be an array. Each assertion in `_includes` will remove the matching item from consideration for the rest.
|
|
68
68
|
|
|
69
|
+
Arrays can be asserted to not match any elements using the special `_excludes` property. The value of `_excludes` _must_ be an array. If an `_includes` check is defined before an `_excludes` check, only the element not matched under the previous will be considered.
|
|
70
|
+
|
|
69
71
|
**Examples**:
|
|
70
72
|
| Actual | Expected | Result |
|
|
71
73
|
| -- | -- | -- |
|
|
@@ -73,6 +75,9 @@ Arrays can be checked against a subset of elements using the special `_includes`
|
|
|
73
75
|
| `[{timeInMs: 5}, {timeInMs: 15}]` | `{length: 2}` | ✅ PASS |
|
|
74
76
|
| `[{timeInMs: 5}, {timeInMs: 15}]` | `{_includes: [{timeInMs: 5}]}` | ✅ PASS |
|
|
75
77
|
| `[{timeInMs: 5}, {timeInMs: 15}]` | `{_includes: [{timeInMs: 5}, {timeInMs: 5}]}` | ❌ FAIL |
|
|
78
|
+
| `[{timeInMs: 5}, {timeInMs: 15}]` | `{_includes: [{timeInMs: 5}], _excludes: [{timeInMs: 5}]}` | ✅ PASS |
|
|
79
|
+
| `[{timeInMs: 5}, {timeInMs: 15}]` | `{_includes: [{timeInMs: 5}], _excludes: [{timeInMs: 15}]}` | ❌ FAIL |
|
|
80
|
+
| `[{timeInMs: 5}, {timeInMs: 15}]` | `{_includes: [{timeInMs: 5}], _excludes: [{}]}` | ❌ FAIL |
|
|
76
81
|
| `[{timeInMs: 5}, {timeInMs: 15}]` | `[{timeInMs: 5}]` | ❌ FAIL |
|
|
77
82
|
|
|
78
83
|
### Special environment checks
|
|
@@ -104,6 +109,15 @@ If an expectation requires a minimum version of Chromium, use `_minChromiumMiles
|
|
|
104
109
|
},
|
|
105
110
|
```
|
|
106
111
|
|
|
112
|
+
All pruning checks:
|
|
113
|
+
|
|
114
|
+
- `_minChromiumMilestone`
|
|
115
|
+
- `_maxChromiumMilestone`
|
|
116
|
+
- `_legacyOnly`
|
|
117
|
+
- `_fraggleRockOnly`
|
|
118
|
+
- `_skipInBundled`
|
|
119
|
+
- `_runner` (set to same value provided to CLI --runner flag, ex: `'devtools'`)
|
|
120
|
+
|
|
107
121
|
## Pipeline
|
|
108
122
|
|
|
109
123
|
The different frontends launch smokehouse with a set of tests to run. Smokehouse then coordinates the tests using a particular method of running Lighthouse (CLI, as a bundle, etc).
|
|
@@ -10,11 +10,13 @@
|
|
|
10
10
|
* against the results actually collected from Lighthouse.
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
-
import
|
|
13
|
+
import _ from 'lodash';
|
|
14
14
|
import log from 'lighthouse-logger';
|
|
15
15
|
|
|
16
16
|
import {LocalConsole} from './lib/local-console.js';
|
|
17
17
|
|
|
18
|
+
const {cloneDeep} = _;
|
|
19
|
+
|
|
18
20
|
/**
|
|
19
21
|
* @typedef Difference
|
|
20
22
|
* @property {string} path
|
|
@@ -103,6 +105,8 @@ function findDifference(path, actual, expected) {
|
|
|
103
105
|
};
|
|
104
106
|
}
|
|
105
107
|
|
|
108
|
+
let inclExclCopy;
|
|
109
|
+
|
|
106
110
|
// We only care that all expected's own properties are on actual (and not the other way around).
|
|
107
111
|
// Note an expected `undefined` can match an actual that is either `undefined` or not defined.
|
|
108
112
|
for (const key of Object.keys(expected)) {
|
|
@@ -112,6 +116,8 @@ function findDifference(path, actual, expected) {
|
|
|
112
116
|
const expectedValue = expected[key];
|
|
113
117
|
|
|
114
118
|
if (key === '_includes') {
|
|
119
|
+
inclExclCopy = [...actual];
|
|
120
|
+
|
|
115
121
|
if (!Array.isArray(expectedValue)) throw new Error('Array subset must be array');
|
|
116
122
|
if (!Array.isArray(actual)) {
|
|
117
123
|
return {
|
|
@@ -121,12 +127,12 @@ function findDifference(path, actual, expected) {
|
|
|
121
127
|
};
|
|
122
128
|
}
|
|
123
129
|
|
|
124
|
-
const actualCopy = [...actual];
|
|
125
130
|
for (const expectedEntry of expectedValue) {
|
|
126
131
|
const matchingIndex =
|
|
127
|
-
|
|
132
|
+
inclExclCopy.findIndex(actualEntry =>
|
|
133
|
+
!findDifference(keyPath, actualEntry, expectedEntry));
|
|
128
134
|
if (matchingIndex !== -1) {
|
|
129
|
-
|
|
135
|
+
inclExclCopy.splice(matchingIndex, 1);
|
|
130
136
|
continue;
|
|
131
137
|
}
|
|
132
138
|
|
|
@@ -140,6 +146,33 @@ function findDifference(path, actual, expected) {
|
|
|
140
146
|
continue;
|
|
141
147
|
}
|
|
142
148
|
|
|
149
|
+
if (key === '_excludes') {
|
|
150
|
+
// Re-use state from `_includes` check, if there was one.
|
|
151
|
+
/** @type {any[]} */
|
|
152
|
+
const arrToCheckAgainst = inclExclCopy || actual;
|
|
153
|
+
|
|
154
|
+
if (!Array.isArray(expectedValue)) throw new Error('Array subset must be array');
|
|
155
|
+
if (!Array.isArray(actual)) continue;
|
|
156
|
+
|
|
157
|
+
const expectedExclusions = expectedValue;
|
|
158
|
+
for (const expectedExclusion of expectedExclusions) {
|
|
159
|
+
const matchingIndex = arrToCheckAgainst.findIndex(actualEntry =>
|
|
160
|
+
!findDifference(keyPath, actualEntry, expectedExclusion));
|
|
161
|
+
if (matchingIndex !== -1) {
|
|
162
|
+
return {
|
|
163
|
+
path,
|
|
164
|
+
actual: arrToCheckAgainst[matchingIndex],
|
|
165
|
+
expected: {
|
|
166
|
+
message: 'Expected to not find matching entry via _excludes',
|
|
167
|
+
expectedExclusion,
|
|
168
|
+
},
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
continue;
|
|
174
|
+
}
|
|
175
|
+
|
|
143
176
|
const actualValue = actual[key];
|
|
144
177
|
const subDifference = findDifference(keyPath, actualValue, expectedValue);
|
|
145
178
|
|
|
@@ -187,7 +220,7 @@ function makeComparison(name, actualResult, expectedResult) {
|
|
|
187
220
|
* @param {LocalConsole} localConsole
|
|
188
221
|
* @param {LH.Result} lhr
|
|
189
222
|
* @param {Smokehouse.ExpectedRunnerResult} expected
|
|
190
|
-
* @param {{isBundled?: boolean}=} reportOptions
|
|
223
|
+
* @param {{runner?: string, isBundled?: boolean}=} reportOptions
|
|
191
224
|
*/
|
|
192
225
|
function pruneExpectations(localConsole, lhr, expected, reportOptions) {
|
|
193
226
|
const isFraggleRock = lhr.configSettings.channel === 'fraggle-rock-cli';
|
|
@@ -217,8 +250,20 @@ function pruneExpectations(localConsole, lhr, expected, reportOptions) {
|
|
|
217
250
|
* @param {*} obj
|
|
218
251
|
*/
|
|
219
252
|
function pruneRecursively(obj) {
|
|
220
|
-
|
|
221
|
-
|
|
253
|
+
/**
|
|
254
|
+
* @param {string} key
|
|
255
|
+
*/
|
|
256
|
+
const remove = (key) => {
|
|
257
|
+
if (Array.isArray(obj)) {
|
|
258
|
+
obj.splice(Number(key), 1);
|
|
259
|
+
} else {
|
|
260
|
+
delete obj[key];
|
|
261
|
+
}
|
|
262
|
+
};
|
|
263
|
+
|
|
264
|
+
// Because we may be deleting keys, we should iterate the keys backwards
|
|
265
|
+
// otherwise arrays with multiple pruning checks will skip elements.
|
|
266
|
+
for (const [key, value] of Object.entries(obj).reverse()) {
|
|
222
267
|
if (!value || typeof value !== 'object') {
|
|
223
268
|
continue;
|
|
224
269
|
}
|
|
@@ -229,42 +274,32 @@ function pruneExpectations(localConsole, lhr, expected, reportOptions) {
|
|
|
229
274
|
JSON.stringify(value, null, 2),
|
|
230
275
|
`Actual Chromium version: ${getChromeVersion()}`,
|
|
231
276
|
].join(' '));
|
|
232
|
-
|
|
233
|
-
obj.splice(Number(key), 1);
|
|
234
|
-
} else {
|
|
235
|
-
delete obj[key];
|
|
236
|
-
}
|
|
277
|
+
remove(key);
|
|
237
278
|
} else if (value._legacyOnly && isFraggleRock) {
|
|
238
279
|
localConsole.log([
|
|
239
280
|
`[${key}] marked legacy only but run is Fraggle Rock, pruning expectation:`,
|
|
240
281
|
JSON.stringify(value, null, 2),
|
|
241
282
|
].join(' '));
|
|
242
|
-
|
|
243
|
-
obj.splice(Number(key), 1);
|
|
244
|
-
} else {
|
|
245
|
-
delete obj[key];
|
|
246
|
-
}
|
|
283
|
+
remove(key);
|
|
247
284
|
} else if (value._fraggleRockOnly && !isFraggleRock) {
|
|
248
285
|
localConsole.log([
|
|
249
286
|
`[${key}] marked Fraggle Rock only but run is legacy, pruning expectation:`,
|
|
250
287
|
JSON.stringify(value, null, 2),
|
|
251
288
|
`Actual channel: ${lhr.configSettings.channel}`,
|
|
252
289
|
].join(' '));
|
|
253
|
-
|
|
254
|
-
obj.splice(Number(key), 1);
|
|
255
|
-
} else {
|
|
256
|
-
delete obj[key];
|
|
257
|
-
}
|
|
290
|
+
remove(key);
|
|
258
291
|
} else if (value._skipInBundled && !isBundled) {
|
|
259
292
|
localConsole.log([
|
|
260
293
|
`[${key}] marked as skip in bundled and runner is bundled, pruning expectation:`,
|
|
261
294
|
JSON.stringify(value, null, 2),
|
|
262
295
|
].join(' '));
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
296
|
+
remove(key);
|
|
297
|
+
} else if (value._runner && reportOptions?.runner !== value._runner) {
|
|
298
|
+
localConsole.log([
|
|
299
|
+
`[${key}] is only for runner ${value._runner}, pruning expectation:`,
|
|
300
|
+
JSON.stringify(value, null, 2),
|
|
301
|
+
].join(' '));
|
|
302
|
+
remove(key);
|
|
268
303
|
} else {
|
|
269
304
|
pruneRecursively(value);
|
|
270
305
|
}
|
|
@@ -275,6 +310,7 @@ function pruneExpectations(localConsole, lhr, expected, reportOptions) {
|
|
|
275
310
|
delete obj._skipInBundled;
|
|
276
311
|
delete obj._minChromiumMilestone;
|
|
277
312
|
delete obj._maxChromiumMilestone;
|
|
313
|
+
delete obj._runner;
|
|
278
314
|
}
|
|
279
315
|
|
|
280
316
|
const cloned = cloneDeep(expected);
|
|
@@ -420,7 +456,7 @@ function reportAssertion(localConsole, assertion) {
|
|
|
420
456
|
* summary. Returns count of passed and failed tests.
|
|
421
457
|
* @param {{lhr: LH.Result, artifacts: LH.Artifacts, networkRequests?: string[]}} actual
|
|
422
458
|
* @param {Smokehouse.ExpectedRunnerResult} expected
|
|
423
|
-
* @param {{isDebug?: boolean, isBundled?: boolean}=} reportOptions
|
|
459
|
+
* @param {{runner?: string, isDebug?: boolean, isBundled?: boolean}=} reportOptions
|
|
424
460
|
* @return {{passed: number, failed: number, log: string}}
|
|
425
461
|
*/
|
|
426
462
|
function getAssertionReport(actual, expected, reportOptions = {}) {
|
|
@@ -57,7 +57,7 @@ async function runSmokehouse(smokeTestDefns, smokehouseOptions) {
|
|
|
57
57
|
useFraggleRock,
|
|
58
58
|
jobs = DEFAULT_CONCURRENT_RUNS,
|
|
59
59
|
retries = DEFAULT_RETRIES,
|
|
60
|
-
lighthouseRunner = cliLighthouseRunner,
|
|
60
|
+
lighthouseRunner = Object.assign(cliLighthouseRunner, {runnerName: 'cli'}),
|
|
61
61
|
takeNetworkRequestUrls,
|
|
62
62
|
} = smokehouseOptions;
|
|
63
63
|
assertPositiveInteger('jobs', jobs);
|
|
@@ -159,7 +159,10 @@ async function runSmokeTest(smokeTestDefn, testOptions) {
|
|
|
159
159
|
}
|
|
160
160
|
|
|
161
161
|
// Assert result.
|
|
162
|
-
report = getAssertionReport(result, expectations, {
|
|
162
|
+
report = getAssertionReport(result, expectations, {
|
|
163
|
+
runner: lighthouseRunner.runnerName,
|
|
164
|
+
isDebug,
|
|
165
|
+
});
|
|
163
166
|
|
|
164
167
|
runs.push({
|
|
165
168
|
...result,
|
|
@@ -71,19 +71,19 @@ class IssuesPanelEntries extends Audit {
|
|
|
71
71
|
}
|
|
72
72
|
|
|
73
73
|
/**
|
|
74
|
-
* @param {Array<LH.Crdp.Audits.
|
|
74
|
+
* @param {Array<LH.Crdp.Audits.CookieIssueDetails>} CookieIssues
|
|
75
75
|
* @return {LH.Audit.Details.TableItem}
|
|
76
76
|
*/
|
|
77
|
-
static
|
|
77
|
+
static getCookieRow(CookieIssues) {
|
|
78
78
|
const requestUrls = new Set();
|
|
79
|
-
for (const issue of
|
|
79
|
+
for (const issue of CookieIssues) {
|
|
80
80
|
const requestUrl = (issue.request?.url) || issue.cookieUrl;
|
|
81
81
|
if (requestUrl) {
|
|
82
82
|
requestUrls.add(requestUrl);
|
|
83
83
|
}
|
|
84
84
|
}
|
|
85
85
|
return {
|
|
86
|
-
issueType: '
|
|
86
|
+
issueType: 'Cookie',
|
|
87
87
|
subItems: {
|
|
88
88
|
type: 'subitems',
|
|
89
89
|
items: Array.from(requestUrls).map(url => {
|
|
@@ -164,8 +164,8 @@ class IssuesPanelEntries extends Audit {
|
|
|
164
164
|
if (issues.mixedContentIssue.length) {
|
|
165
165
|
items.push(this.getMixedContentRow(issues.mixedContentIssue));
|
|
166
166
|
}
|
|
167
|
-
if (issues.
|
|
168
|
-
items.push(this.
|
|
167
|
+
if (issues.cookieIssue.length) {
|
|
168
|
+
items.push(this.getCookieRow(issues.cookieIssue));
|
|
169
169
|
}
|
|
170
170
|
if (issues.blockedByResponseIssue.length) {
|
|
171
171
|
items.push(this.getBlockedByResponseRow(issues.blockedByResponseIssue));
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
'use strict';
|
|
7
7
|
|
|
8
8
|
const path = require('path');
|
|
9
|
-
const isDeepEqual = require('lodash
|
|
9
|
+
const {isEqual: isDeepEqual} = require('lodash');
|
|
10
10
|
const constants = require('./constants.js');
|
|
11
11
|
const Budget = require('./budget.js');
|
|
12
12
|
const ConfigPlugin = require('./config-plugin.js');
|
|
@@ -5,22 +5,73 @@
|
|
|
5
5
|
*/
|
|
6
6
|
'use strict';
|
|
7
7
|
|
|
8
|
-
const {
|
|
9
|
-
const {
|
|
10
|
-
const {
|
|
11
|
-
const
|
|
8
|
+
const {UserFlow, auditGatherSteps} = require('./user-flow.js');
|
|
9
|
+
const {snapshotGather} = require('./gather/snapshot-runner.js');
|
|
10
|
+
const {startTimespanGather} = require('./gather/timespan-runner.js');
|
|
11
|
+
const {navigationGather} = require('./gather/navigation-runner.js');
|
|
12
|
+
const {generateFlowReportHtml} = require('../../report/generator/report-generator.js');
|
|
13
|
+
const Runner = require('../runner.js');
|
|
12
14
|
|
|
13
15
|
/**
|
|
14
16
|
* @param {import('puppeteer').Page} page
|
|
15
|
-
* @param {UserFlow
|
|
17
|
+
* @param {ConstructorParameters<LH.UserFlow>[1]} [options]
|
|
16
18
|
*/
|
|
17
19
|
async function startFlow(page, options) {
|
|
18
20
|
return new UserFlow(page, options);
|
|
19
21
|
}
|
|
20
22
|
|
|
23
|
+
/**
|
|
24
|
+
* @param {Parameters<navigationGather>} params
|
|
25
|
+
* @return {Promise<LH.RunnerResult|undefined>}
|
|
26
|
+
*/
|
|
27
|
+
async function navigation(...params) {
|
|
28
|
+
const gatherResult = await navigationGather(...params);
|
|
29
|
+
return Runner.audit(gatherResult.artifacts, gatherResult.runnerOptions);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* @param {Parameters<snapshotGather>} params
|
|
34
|
+
* @return {Promise<LH.RunnerResult|undefined>}
|
|
35
|
+
*/
|
|
36
|
+
async function snapshot(...params) {
|
|
37
|
+
const gatherResult = await snapshotGather(...params);
|
|
38
|
+
return Runner.audit(gatherResult.artifacts, gatherResult.runnerOptions);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* @param {Parameters<startTimespanGather>} params
|
|
43
|
+
* @return {Promise<{endTimespan: () => Promise<LH.RunnerResult|undefined>}>}
|
|
44
|
+
*/
|
|
45
|
+
async function startTimespan(...params) {
|
|
46
|
+
const {endTimespanGather} = await startTimespanGather(...params);
|
|
47
|
+
const endTimespan = async () => {
|
|
48
|
+
const gatherResult = await endTimespanGather();
|
|
49
|
+
return Runner.audit(gatherResult.artifacts, gatherResult.runnerOptions);
|
|
50
|
+
};
|
|
51
|
+
return {endTimespan};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* @param {LH.FlowResult} flowResult
|
|
56
|
+
*/
|
|
57
|
+
async function generateFlowReport(flowResult) {
|
|
58
|
+
return generateFlowReportHtml(flowResult);
|
|
59
|
+
}
|
|
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
|
+
|
|
21
70
|
module.exports = {
|
|
22
71
|
snapshot,
|
|
23
72
|
startTimespan,
|
|
24
73
|
navigation,
|
|
25
74
|
startFlow,
|
|
75
|
+
generateFlowReport,
|
|
76
|
+
auditFlowArtifacts,
|
|
26
77
|
};
|
|
@@ -30,7 +30,7 @@ const {
|
|
|
30
30
|
} = require('../../config/config-helpers.js');
|
|
31
31
|
const defaultConfigPath = path.join(__dirname, './default-config.js');
|
|
32
32
|
|
|
33
|
-
/** @typedef {
|
|
33
|
+
/** @typedef {LH.Config.FRContext & {gatherMode: LH.Gatherer.GatherMode}} ConfigContext */
|
|
34
34
|
|
|
35
35
|
/**
|
|
36
36
|
* @param {LH.Config.Json|undefined} configJSON
|
|
@@ -210,6 +210,7 @@ async function _computeNavigationResult(
|
|
|
210
210
|
async function _navigation(navigationContext) {
|
|
211
211
|
const artifactState = getEmptyArtifactState();
|
|
212
212
|
const phaseState = {
|
|
213
|
+
url: await navigationContext.driver.url(),
|
|
213
214
|
gatherMode: /** @type {const} */ ('navigation'),
|
|
214
215
|
driver: navigationContext.driver,
|
|
215
216
|
computedCache: navigationContext.computedCache,
|
|
@@ -223,6 +224,7 @@ async function _navigation(navigationContext) {
|
|
|
223
224
|
await collectPhaseArtifacts({phase: 'startInstrumentation', ...phaseState});
|
|
224
225
|
await collectPhaseArtifacts({phase: 'startSensitiveInstrumentation', ...phaseState});
|
|
225
226
|
const navigateResult = await _navigate(navigationContext);
|
|
227
|
+
phaseState.url = navigateResult.finalUrl;
|
|
226
228
|
await collectPhaseArtifacts({phase: 'stopSensitiveInstrumentation', ...phaseState});
|
|
227
229
|
await collectPhaseArtifacts({phase: 'stopInstrumentation', ...phaseState});
|
|
228
230
|
await _cleanupNavigation(navigationContext);
|
|
@@ -288,10 +290,12 @@ async function _cleanup({requestedUrl, driver, config}) {
|
|
|
288
290
|
/**
|
|
289
291
|
* @param {LH.NavigationRequestor} requestor
|
|
290
292
|
* @param {{page: import('puppeteer').Page, config?: LH.Config.Json, configContext?: LH.Config.FRContext}} options
|
|
291
|
-
* @return {Promise<LH.
|
|
293
|
+
* @return {Promise<LH.Gatherer.FRGatherResult>}
|
|
292
294
|
*/
|
|
293
|
-
async function
|
|
295
|
+
async function navigationGather(requestor, options) {
|
|
294
296
|
const {page, configContext = {}} = options;
|
|
297
|
+
log.setLevel(configContext.logLevel || 'error');
|
|
298
|
+
|
|
295
299
|
const {config} = initializeConfig(options.config, {...configContext, gatherMode: 'navigation'});
|
|
296
300
|
const computedCache = new Map();
|
|
297
301
|
const internalOptions = {
|
|
@@ -321,11 +325,11 @@ async function navigation(requestor, options) {
|
|
|
321
325
|
},
|
|
322
326
|
runnerOptions
|
|
323
327
|
);
|
|
324
|
-
return
|
|
328
|
+
return {artifacts, runnerOptions};
|
|
325
329
|
}
|
|
326
330
|
|
|
327
331
|
module.exports = {
|
|
328
|
-
|
|
332
|
+
navigationGather,
|
|
329
333
|
_setup,
|
|
330
334
|
_setupNavigation,
|
|
331
335
|
_navigate,
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
* @property {LH.Gatherer.GatherMode} gatherMode
|
|
16
16
|
* @property {Map<string, LH.ArbitraryEqualityMap>} computedCache
|
|
17
17
|
* @property {LH.Config.Settings} settings
|
|
18
|
+
* @property {string} url
|
|
18
19
|
*/
|
|
19
20
|
|
|
20
21
|
/** @typedef {Record<string, Promise<any>>} IntermediateArtifacts */
|
|
@@ -74,6 +75,7 @@ async function collectPhaseArtifacts(options) {
|
|
|
74
75
|
gatherMode,
|
|
75
76
|
computedCache,
|
|
76
77
|
settings,
|
|
78
|
+
url,
|
|
77
79
|
} = options;
|
|
78
80
|
const priorPhase = phaseToPriorPhase[phase];
|
|
79
81
|
const priorPhaseArtifacts = (priorPhase && artifactState[priorPhase]) || {};
|
|
@@ -90,7 +92,7 @@ async function collectPhaseArtifacts(options) {
|
|
|
90
92
|
: /** @type {Dependencies} */ ({});
|
|
91
93
|
|
|
92
94
|
return gatherer[phase]({
|
|
93
|
-
url
|
|
95
|
+
url,
|
|
94
96
|
gatherMode,
|
|
95
97
|
driver,
|
|
96
98
|
baseArtifacts,
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
'use strict';
|
|
7
7
|
|
|
8
|
+
const log = require('lighthouse-logger');
|
|
8
9
|
const Driver = require('./driver.js');
|
|
9
10
|
const Runner = require('../../runner.js');
|
|
10
11
|
const {
|
|
@@ -15,16 +16,21 @@ const {
|
|
|
15
16
|
const {initializeConfig} = require('../config/config.js');
|
|
16
17
|
const {getBaseArtifacts, finalizeArtifacts} = require('./base-artifacts.js');
|
|
17
18
|
|
|
18
|
-
/**
|
|
19
|
-
|
|
19
|
+
/**
|
|
20
|
+
* @param {{page: import('puppeteer').Page, config?: LH.Config.Json, configContext?: LH.Config.FRContext}} options
|
|
21
|
+
* @return {Promise<LH.Gatherer.FRGatherResult>}
|
|
22
|
+
*/
|
|
23
|
+
async function snapshotGather(options) {
|
|
20
24
|
const {configContext = {}} = options;
|
|
25
|
+
log.setLevel(configContext.logLevel || 'error');
|
|
26
|
+
|
|
21
27
|
const {config} = initializeConfig(options.config, {...configContext, gatherMode: 'snapshot'});
|
|
22
28
|
const driver = new Driver(options.page);
|
|
23
29
|
await driver.connect();
|
|
24
30
|
|
|
25
31
|
/** @type {Map<string, LH.ArbitraryEqualityMap>} */
|
|
26
32
|
const computedCache = new Map();
|
|
27
|
-
const url = await
|
|
33
|
+
const url = await driver.url();
|
|
28
34
|
|
|
29
35
|
const runnerOptions = {config, computedCache};
|
|
30
36
|
const artifacts = await Runner.gather(
|
|
@@ -36,6 +42,7 @@ async function snapshot(options) {
|
|
|
36
42
|
const artifactDefinitions = config.artifacts || [];
|
|
37
43
|
const artifactState = getEmptyArtifactState();
|
|
38
44
|
await collectPhaseArtifacts({
|
|
45
|
+
url,
|
|
39
46
|
phase: 'getArtifact',
|
|
40
47
|
gatherMode: 'snapshot',
|
|
41
48
|
driver,
|
|
@@ -53,9 +60,9 @@ async function snapshot(options) {
|
|
|
53
60
|
},
|
|
54
61
|
runnerOptions
|
|
55
62
|
);
|
|
56
|
-
return
|
|
63
|
+
return {artifacts, runnerOptions};
|
|
57
64
|
}
|
|
58
65
|
|
|
59
66
|
module.exports = {
|
|
60
|
-
|
|
67
|
+
snapshotGather,
|
|
61
68
|
};
|