lighthouse 9.5.0-dev.20220621 → 9.5.0-dev.20220622

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.
Files changed (29) hide show
  1. package/lighthouse-cli/bin.js +8 -2
  2. package/lighthouse-cli/test/smokehouse/__snapshots__/report-assert-test.js.snap +43 -0
  3. package/lighthouse-cli/test/smokehouse/report-assert-test.js +96 -30
  4. package/lighthouse-cli/test/smokehouse/report-assert.js +36 -35
  5. package/lighthouse-core/audits/byte-efficiency/unused-javascript.js +1 -1
  6. package/lighthouse-core/config/config-helpers.js +15 -0
  7. package/lighthouse-core/fraggle-rock/config/config.js +45 -1
  8. package/lighthouse-core/fraggle-rock/gather/driver.js +13 -5
  9. package/lighthouse-core/fraggle-rock/gather/session.js +23 -67
  10. package/lighthouse-core/gather/driver/navigation.js +1 -1
  11. package/lighthouse-core/gather/driver/network-monitor.js +14 -59
  12. package/lighthouse-core/gather/driver/target-manager.js +110 -45
  13. package/lighthouse-core/gather/driver.js +24 -16
  14. package/lighthouse-core/gather/gatherers/devtools-log.js +7 -11
  15. package/lighthouse-core/gather/gatherers/full-page-screenshot.js +1 -1
  16. package/lighthouse-core/index.js +24 -9
  17. package/lighthouse-core/lib/cdt/Common.js +13 -0
  18. package/lighthouse-core/lib/cdt/Platform.js +3 -0
  19. package/lighthouse-core/lib/cdt/generated/ParsedURL.js +178 -0
  20. package/lighthouse-core/lib/cdt/generated/SourceMap.js +155 -62
  21. package/package.json +2 -2
  22. package/third-party/devtools-tests/README.md +14 -0
  23. package/third-party/devtools-tests/e2e/lighthouse/generate-report_test.ts +29 -0
  24. package/third-party/devtools-tests/e2e/lighthouse/indexeddb-warning_test.ts +29 -0
  25. package/third-party/devtools-tests/e2e/resources/lighthouse/lighthouse-storage.html +11 -0
  26. package/third-party/download-content-shell/README.md +1 -0
  27. package/third-party/download-content-shell/download-content-shell.js +6 -1
  28. package/types/gatherer.d.ts +5 -2
  29. package/types/protocol.d.ts +19 -17
@@ -31,6 +31,7 @@ import {runLighthouse} from './run.js';
31
31
  import lighthouse from '../lighthouse-core/index.js';
32
32
  import {askPermission} from './sentry-prompt.js';
33
33
  import {LH_ROOT} from '../root.js';
34
+ import {getConfigDisplayString} from '../lighthouse-core/fraggle-rock/config/config.js';
34
35
 
35
36
  const pkg = JSON.parse(fs.readFileSync(LH_ROOT + '/package.json', 'utf-8'));
36
37
 
@@ -120,8 +121,13 @@ async function begin() {
120
121
  }
121
122
 
122
123
  if (cliFlags.printConfig) {
123
- const config = await lighthouse.generateConfig(configJson, cliFlags);
124
- process.stdout.write(config.getPrintString());
124
+ if (cliFlags.legacyNavigation) {
125
+ const config = await lighthouse.generateLegacyConfig(configJson, cliFlags);
126
+ process.stdout.write(config.getPrintString());
127
+ } else {
128
+ const config = await lighthouse.generateConfig(configJson, cliFlags);
129
+ process.stdout.write(getConfigDisplayString(config));
130
+ }
125
131
  return;
126
132
  }
127
133
 
@@ -1,5 +1,37 @@
1
1
  // Jest Snapshot v1, https://goo.gl/fbAQLP
2
2
 
3
+ exports[`getAssertionReport works (multiple failing) 1`] = `
4
+ "X difference at cumulative-layout-shift audit.details.items.length
5
+ expected: []
6
+ found: [{\\"cumulativeLayoutShiftMainFrame\\":0.13570762803819444,\\"totalCumulativeLayoutShift\\":0.13570762803819444}]
7
+
8
+
9
+ X difference at cumulative-layout-shift audit.details.blah
10
+ expected: 123
11
+ found: undefined
12
+
13
+ found result:
14
+ {
15
+ \\"id\\": \\"cumulative-layout-shift\\",
16
+ \\"title\\": \\"Cumulative Layout Shift\\",
17
+ \\"description\\": \\"Cumulative Layout Shift measures the movement of visible elements within the viewport. [Learn more](https://web.dev/cls/).\\",
18
+ \\"score\\": 0.8,
19
+ \\"scoreDisplayMode\\": \\"numeric\\",
20
+ \\"numericValue\\": 0.13570762803819444,
21
+ \\"numericUnit\\": \\"unitless\\",
22
+ \\"displayValue\\": \\"0.136\\",
23
+ \\"details\\": {
24
+ \\"type\\": \\"debugdata\\",
25
+ \\"items\\": [
26
+ {
27
+ \\"cumulativeLayoutShiftMainFrame\\": 0.13570762803819444,
28
+ \\"totalCumulativeLayoutShift\\": 0.13570762803819444
29
+ }
30
+ ]
31
+ }
32
+ }"
33
+ `;
34
+
3
35
  exports[`getAssertionReport works (trivial failing) 1`] = `
4
36
  "X difference at cumulative-layout-shift audit.details.items.length
5
37
  expected: []
@@ -26,3 +58,14 @@ exports[`getAssertionReport works (trivial failing) 1`] = `
26
58
  }
27
59
  }"
28
60
  `;
61
+
62
+ exports[`getAssertionReport works (trivial failing, actual undefined) 1`] = `
63
+ "Error: Config did not trigger run of expected audit cumulative-layout-shift-no-exist
64
+
65
+ X difference at cumulative-layout-shift-no-exist audit
66
+ expected: {\\"details\\":{\\"items\\":[]}}
67
+ found: undefined
68
+
69
+ found result:
70
+ undefined"
71
+ `;
@@ -6,146 +6,175 @@
6
6
 
7
7
  /* eslint-disable no-control-regex */
8
8
 
9
- import {findDifference, getAssertionReport} from './report-assert.js';
9
+ import {findDifferences, getAssertionReport} from './report-assert.js';
10
10
  import {readJson} from '../../../root.js';
11
11
 
12
- describe('findDifference', () => {
12
+ describe('findDiffersences', () => {
13
13
  const testCases = {
14
14
  'works (trivial passing)': {
15
15
  actual: {},
16
16
  expected: {},
17
- diff: null,
17
+ diffs: null,
18
18
  },
19
19
  'works (trivial fail)': {
20
20
  actual: {},
21
21
  expected: {a: 1},
22
- diff: {path: '.a', actual: undefined, expected: 1},
22
+ diffs: [{path: '.a', actual: undefined, expected: 1}],
23
+ },
24
+ 'works (trivial fail, actual undefined)': {
25
+ actual: undefined,
26
+ expected: {a: 1},
27
+ diffs: [{path: '', actual: undefined, expected: {a: 1}}],
23
28
  },
24
29
  'works (trivial fail, nested)': {
25
30
  actual: {a: {b: 2}},
26
31
  expected: {a: {b: 1}},
27
- diff: {path: '.a.b', actual: 2, expected: 1},
32
+ diffs: [{path: '.a.b', actual: 2, expected: 1}],
33
+ },
34
+ 'works (trivial fail, nested actual undefined)': {
35
+ actual: {a: undefined},
36
+ expected: {a: {b: 1}},
37
+ diffs: [{path: '.a', actual: undefined, expected: {b: 1}}],
38
+ },
39
+ 'works (multiple fail 1)': {
40
+ actual: {},
41
+ expected: {a: 1, b: 2},
42
+ diffs: [
43
+ {path: '.a', actual: undefined, expected: 1},
44
+ {path: '.b', actual: undefined, expected: 2},
45
+ ],
46
+ },
47
+ 'works (multiple fail 2)': {
48
+ actual: {nested: {array: [0, 1, 2]}},
49
+ expected: {nested: {array: [2, 1, 0]}},
50
+ diffs: [
51
+ {path: '.nested.array[0]', actual: 0, expected: 2},
52
+ {path: '.nested.array[2]', actual: 2, expected: 0},
53
+ ],
28
54
  },
29
55
 
30
56
  'range (1)': {
31
57
  actual: {duration: 100},
32
58
  expected: {duration: '>=100'},
33
- diff: null,
59
+ diffs: null,
34
60
  },
35
61
  'range (2)': {
36
62
  actual: {},
37
63
  expected: {duration: '>=100'},
38
- diff: {path: '.duration', actual: undefined, expected: '>=100'},
64
+ diffs: [{path: '.duration', actual: undefined, expected: '>=100'}],
39
65
  },
40
66
  'range (3)': {
41
67
  actual: {duration: 100},
42
68
  expected: {duration: '>100'},
43
- diff: {path: '.duration', actual: 100, expected: '>100'},
69
+ diffs: [{path: '.duration', actual: 100, expected: '>100'}],
44
70
  },
45
71
  'range (4)': {
46
72
  actual: {duration: 100},
47
73
  expected: {duration: '<100'},
48
- diff: {path: '.duration', actual: 100, expected: '<100'},
74
+ diffs: [{path: '.duration', actual: 100, expected: '<100'}],
49
75
  },
50
76
 
51
77
  'array (1)': {
52
78
  actual: {prices: [0, 1, 2, 3, 4, 5]},
53
79
  expected: {prices: {length: 6}},
54
- diff: null,
80
+ diffs: null,
55
81
  },
56
82
  'array (2)': {
57
83
  actual: {prices: [0, 1, 2, 3, 4, 5]},
58
84
  expected: {prices: {length: '>0'}},
59
- diff: null,
85
+ diffs: null,
60
86
  },
61
87
  'array (3)': {
62
88
  actual: {prices: [0, 1, 2, 3, 4, 5]},
63
89
  expected: {prices: [0, 1, 2, 3, 4, 5]},
64
- diff: null,
90
+ diffs: null,
65
91
  },
66
92
  'array (4)': {
67
93
  actual: {prices: [0, 1, 2, 3, 4, 5]},
68
94
  expected: {prices: [0, 1, 2, 3, 4, 5, 6]},
69
- diff: {path: '.prices[6]', actual: undefined, expected: 6},
95
+ diffs: [
96
+ {path: '.prices[6]', actual: undefined, expected: 6},
97
+ {path: '.prices.length', actual: [0, 1, 2, 3, 4, 5], expected: [0, 1, 2, 3, 4, 5, 6]},
98
+ ],
70
99
  },
71
100
  'array (5)': {
72
101
  actual: {prices: [0, 1, 2, 3, 4, 5]},
73
102
  expected: {prices: []},
74
- diff: {path: '.prices.length', actual: [0, 1, 2, 3, 4, 5], expected: []},
103
+ diffs: [{path: '.prices.length', actual: [0, 1, 2, 3, 4, 5], expected: []}],
75
104
  },
76
105
  'array (6)': {
77
106
  actual: {prices: [0, 1, 2, 3, 4, 5]},
78
107
  expected: {prices: {'3': '>=3'}},
79
- diff: null,
108
+ diffs: null,
80
109
  },
81
110
  'array (7)': {
82
111
  actual: {prices: [0, 1, 2, {nested: 3}, 4, 5]},
83
112
  expected: {prices: {'3': {nested: '>3'}}},
84
- diff: {path: '.prices[3].nested', actual: 3, expected: '>3'},
113
+ diffs: [{path: '.prices[3].nested', actual: 3, expected: '>3'}],
85
114
  },
86
115
 
87
116
  '_includes (1)': {
88
117
  actual: {prices: [0, 1, 2, 3, 4, 5]},
89
118
  expected: {prices: {_includes: [4]}},
90
- diff: null,
119
+ diffs: null,
91
120
  },
92
121
  '_includes (2)': {
93
122
  actual: {prices: [0, 1, 2, 3, 4, 5]},
94
123
  expected: {prices: {_includes: [4, 4]}},
95
- diff: {path: '.prices', actual: 'Item not found in array', expected: 4},
124
+ diffs: [{path: '.prices', actual: 'Item not found in array', expected: 4}],
96
125
  },
97
126
  '_includes (3)': {
98
127
  actual: {prices: [0, 1, 2, 3, 4, 5]},
99
128
  expected: {prices: {_includes: [100]}},
100
- diff: {path: '.prices', actual: 'Item not found in array', expected: 100},
129
+ diffs: [{path: '.prices', actual: 'Item not found in array', expected: 100}],
101
130
  },
102
131
  '_includes (4)': {
103
132
  actual: {prices: ['0', '1', '2', '3', '4', '5']},
104
133
  expected: {prices: {_includes: [/\d/, /\d/, /\d/, /\d/, /\d/, /\d/]}},
105
- diff: null,
134
+ diffs: null,
106
135
  },
107
136
 
108
137
  '_excludes (1)': {
109
138
  actual: {prices: [0, 1, 2, 3, 4, 5]},
110
139
  expected: {prices: {_excludes: [100]}},
111
- diff: null,
140
+ diffs: null,
112
141
  },
113
142
  '_excludes (2)': {
114
143
  actual: {prices: [0, 1, 2, 3, 4, 5]},
115
144
  expected: {prices: {_excludes: [2]}},
116
- diff: {path: '.prices', actual: 2, expected: {
145
+ diffs: [{path: '.prices', actual: 2, expected: {
117
146
  expectedExclusion: 2,
118
147
  message: 'Expected to not find matching entry via _excludes',
119
- }},
148
+ }}],
120
149
  },
121
150
 
122
151
  '_includes and _excludes (1)': {
123
152
  actual: {prices: [0, 1, 2, 3, 4, 5]},
124
153
  expected: {prices: {_includes: [2], _excludes: [2]}},
125
- diff: null,
154
+ diffs: null,
126
155
  },
127
156
  // Order matters.
128
157
  '_includes and _excludes (2)': {
129
158
  actual: {prices: [0, 1, 2, 3, 4, 5]},
130
159
  expected: {prices: {_excludes: [2], _includes: [2]}},
131
- diff: {path: '.prices', actual: 2, expected: {
160
+ diffs: [{path: '.prices', actual: 2, expected: {
132
161
  expectedExclusion: 2,
133
162
  message: 'Expected to not find matching entry via _excludes',
134
- }},
163
+ }}],
135
164
  },
136
165
  '_includes and _excludes (3)': {
137
166
  actual: {prices: [0, 1, 2, 3, 4, 5]},
138
167
  expected: {prices: {_includes: [2], _excludes: [2, 1]}},
139
- diff: {path: '.prices', actual: 1, expected: {
168
+ diffs: [{path: '.prices', actual: 1, expected: {
140
169
  expectedExclusion: 1,
141
170
  message: 'Expected to not find matching entry via _excludes',
142
- }},
171
+ }}],
143
172
  },
144
173
  };
145
174
 
146
- for (const [testName, {actual, expected, diff}] of Object.entries(testCases)) {
175
+ for (const [testName, {actual, expected, diffs}] of Object.entries(testCases)) {
147
176
  it(testName, () => {
148
- expect(findDifference('', actual, expected)).toEqual(diff);
177
+ expect(findDifferences('', actual, expected)).toEqual(diffs);
149
178
  });
150
179
  }
151
180
  });
@@ -195,4 +224,41 @@ describe('getAssertionReport', () => {
195
224
  expect(report).toMatchObject({passed: 3, failed: 1});
196
225
  expect(clean(report.log)).toMatchSnapshot();
197
226
  });
227
+
228
+ it('works (trivial failing, actual undefined)', () => {
229
+ const report = getAssertionReport({lhr, artifacts}, {
230
+ lhr: {
231
+ audits: {
232
+ 'cumulative-layout-shift-no-exist': {
233
+ details: {
234
+ items: [],
235
+ },
236
+ },
237
+ },
238
+ requestedUrl: 'http://localhost:10200/dobetterweb/dbw_tester.html',
239
+ finalUrl: 'http://localhost:10200/dobetterweb/dbw_tester.html',
240
+ },
241
+ });
242
+ expect(report).toMatchObject({passed: 3, failed: 1});
243
+ expect(clean(report.log)).toMatchSnapshot();
244
+ });
245
+
246
+ it('works (multiple failing)', () => {
247
+ const report = getAssertionReport({lhr, artifacts}, {
248
+ lhr: {
249
+ audits: {
250
+ 'cumulative-layout-shift': {
251
+ details: {
252
+ items: [],
253
+ blah: 123,
254
+ },
255
+ },
256
+ },
257
+ requestedUrl: 'http://localhost:10200/dobetterweb/dbw_tester.html',
258
+ finalUrl: 'http://localhost:10200/dobetterweb/dbw_tester.html',
259
+ },
260
+ });
261
+ expect(report).toMatchObject({passed: 3, failed: 1});
262
+ expect(clean(report.log)).toMatchSnapshot();
263
+ });
198
264
  });
@@ -28,7 +28,7 @@ import {chromiumVersionCheck} from './version-check.js';
28
28
  * @property {any} actual
29
29
  * @property {any} expected
30
30
  * @property {boolean} equal
31
- * @property {Difference|null} [diff]
31
+ * @property {Difference[]|null} diffs
32
32
  */
33
33
 
34
34
  const NUMBER_REGEXP = /(?:\d|\.)+/.source;
@@ -86,9 +86,9 @@ function matchesExpectation(actual, expected) {
86
86
  * @param {string} path
87
87
  * @param {*} actual
88
88
  * @param {*} expected
89
- * @return {(Difference|null)}
89
+ * @return {Difference[]|null}
90
90
  */
91
- function findDifference(path, actual, expected) {
91
+ function findDifferences(path, actual, expected) {
92
92
  if (matchesExpectation(actual, expected)) {
93
93
  return null;
94
94
  }
@@ -96,13 +96,15 @@ function findDifference(path, actual, expected) {
96
96
  // If they aren't both an object we can't recurse further, so this is the difference.
97
97
  if (actual === null || expected === null || typeof actual !== 'object' ||
98
98
  typeof expected !== 'object' || expected instanceof RegExp) {
99
- return {
99
+ return [{
100
100
  path,
101
101
  actual,
102
102
  expected,
103
- };
103
+ }];
104
104
  }
105
105
 
106
+ /** @type {Difference[]} */
107
+ const diffs = [];
106
108
  let inclExclCopy;
107
109
 
108
110
  // We only care that all expected's own properties are on actual (and not the other way around).
@@ -118,27 +120,27 @@ function findDifference(path, actual, expected) {
118
120
 
119
121
  if (!Array.isArray(expectedValue)) throw new Error('Array subset must be array');
120
122
  if (!Array.isArray(actual)) {
121
- return {
123
+ diffs.push({
122
124
  path,
123
125
  actual: 'Actual value is not an array',
124
126
  expected,
125
- };
127
+ });
126
128
  }
127
129
 
128
130
  for (const expectedEntry of expectedValue) {
129
131
  const matchingIndex =
130
132
  inclExclCopy.findIndex(actualEntry =>
131
- !findDifference(keyPath, actualEntry, expectedEntry));
133
+ !findDifferences(keyPath, actualEntry, expectedEntry));
132
134
  if (matchingIndex !== -1) {
133
135
  inclExclCopy.splice(matchingIndex, 1);
134
136
  continue;
135
137
  }
136
138
 
137
- return {
139
+ diffs.push({
138
140
  path,
139
141
  actual: 'Item not found in array',
140
142
  expected: expectedEntry,
141
- };
143
+ });
142
144
  }
143
145
 
144
146
  continue;
@@ -155,16 +157,16 @@ function findDifference(path, actual, expected) {
155
157
  const expectedExclusions = expectedValue;
156
158
  for (const expectedExclusion of expectedExclusions) {
157
159
  const matchingIndex = arrToCheckAgainst.findIndex(actualEntry =>
158
- !findDifference(keyPath, actualEntry, expectedExclusion));
160
+ !findDifferences(keyPath, actualEntry, expectedExclusion));
159
161
  if (matchingIndex !== -1) {
160
- return {
162
+ diffs.push({
161
163
  path,
162
164
  actual: arrToCheckAgainst[matchingIndex],
163
165
  expected: {
164
166
  message: 'Expected to not find matching entry via _excludes',
165
167
  expectedExclusion,
166
168
  },
167
- };
169
+ });
168
170
  }
169
171
  }
170
172
 
@@ -172,12 +174,8 @@ function findDifference(path, actual, expected) {
172
174
  }
173
175
 
174
176
  const actualValue = actual[key];
175
- const subDifference = findDifference(keyPath, actualValue, expectedValue);
176
-
177
- // Break on first difference found.
178
- if (subDifference) {
179
- return subDifference;
180
- }
177
+ const subDifferences = findDifferences(keyPath, actualValue, expectedValue);
178
+ if (subDifferences) diffs.push(...subDifferences);
181
179
  }
182
180
 
183
181
  // If the expected value is an array, assert the length as well.
@@ -185,14 +183,15 @@ function findDifference(path, actual, expected) {
185
183
  // but requires using an object literal (ex: {0: x, 1: y, 2: z} matches [x, y, z, q, w, e] and
186
184
  // {0: x, 1: y, 2: z, length: 5} does not match [x, y, z].
187
185
  if (Array.isArray(expected) && actual.length !== expected.length) {
188
- return {
186
+ diffs.push({
189
187
  path: `${path}.length`,
190
188
  actual,
191
189
  expected,
192
- };
190
+ });
193
191
  }
194
192
 
195
- return null;
193
+ if (diffs.length === 0) return null;
194
+ return diffs;
196
195
  }
197
196
 
198
197
  /**
@@ -202,14 +201,14 @@ function findDifference(path, actual, expected) {
202
201
  * @return {Comparison}
203
202
  */
204
203
  function makeComparison(name, actualResult, expectedResult) {
205
- const diff = findDifference(name, actualResult, expectedResult);
204
+ const diffs = findDifferences(name, actualResult, expectedResult);
206
205
 
207
206
  return {
208
207
  name,
209
208
  actual: actualResult,
210
209
  expected: expectedResult,
211
- equal: !diff,
212
- diff,
210
+ equal: !diffs,
211
+ diffs,
213
212
  };
214
213
  }
215
214
 
@@ -427,19 +426,21 @@ function reportAssertion(localConsole, assertion) {
427
426
  log.greenify(assertion.actual));
428
427
  }
429
428
  } else {
430
- if (assertion.diff) {
431
- const diff = assertion.diff;
432
- const fullActual = String(JSON.stringify(assertion.actual, null, 2))
433
- .replace(/\n/g, '\n ');
434
- const msg = `
429
+ if (assertion.diffs?.length) {
430
+ for (const diff of assertion.diffs) {
431
+ const msg = `
435
432
  ${log.redify(log.cross)} difference at ${log.bold}${diff.path}${log.reset}
436
433
  expected: ${JSON.stringify(diff.expected)}
437
- found: ${JSON.stringify(diff.actual)}
434
+ found: ${JSON.stringify(diff.actual)}\n`;
435
+ localConsole.log(msg);
436
+ }
438
437
 
439
- found result:
438
+ const fullActual = assertion.actual !== undefined ?
439
+ JSON.stringify(assertion.actual, null, 2).replace(/\n/g, '\n ') :
440
+ 'undefined\n ';
441
+ localConsole.log(` found result:
440
442
  ${log.redify(fullActual)}
441
- `;
442
- localConsole.log(msg);
443
+ `);
443
444
  } else {
444
445
  localConsole.log(` ${log.redify(log.cross)} ${assertion.name}:
445
446
  expected: ${JSON.stringify(assertion.expected)}
@@ -491,5 +492,5 @@ function getAssertionReport(actual, expected, reportOptions = {}) {
491
492
 
492
493
  export {
493
494
  getAssertionReport,
494
- findDifference,
495
+ findDifferences,
495
496
  };
@@ -133,7 +133,7 @@ class UnusedJavaScript extends ByteEfficiencyAudit {
133
133
  })
134
134
  .filter(d => d.unused >= bundleSourceUnusedThreshold);
135
135
 
136
- const commonSourcePrefix = commonPrefix([...bundle.map.sourceInfos.keys()]);
136
+ const commonSourcePrefix = commonPrefix(bundle.map.sourceURLs());
137
137
  item.subItems = {
138
138
  type: 'subitems',
139
139
  items: topUnusedSourceSizes.map(({source, unused, total}) => {
@@ -578,6 +578,20 @@ function deepCloneConfigJson(json) {
578
578
  return cloned;
579
579
  }
580
580
 
581
+ /**
582
+ * @param {LH.Flags} flags
583
+ * @return {LH.Config.FRContext}
584
+ */
585
+ function flagsToFRContext(flags) {
586
+ return {
587
+ configPath: flags?.configPath,
588
+ settingsOverrides: flags,
589
+ logLevel: flags?.logLevel,
590
+ hostname: flags?.hostname,
591
+ port: flags?.port,
592
+ };
593
+ }
594
+
581
595
  module.exports = {
582
596
  deepClone,
583
597
  deepCloneConfigJson,
@@ -589,4 +603,5 @@ module.exports = {
589
603
  resolveGathererToDefn,
590
604
  resolveModulePath,
591
605
  resolveSettings,
606
+ flagsToFRContext,
592
607
  };
@@ -8,6 +8,7 @@
8
8
  const path = require('path');
9
9
  const log = require('lighthouse-logger');
10
10
  const Runner = require('../../runner.js');
11
+ const format = require('../../../shared/localization/format.js');
11
12
  const defaultConfig = require('./default-config.js');
12
13
  const {defaultNavigationConfig, nonSimulatedPassConfigOverrides} = require('../../config/constants.js'); // eslint-disable-line max-len
13
14
  const {
@@ -279,4 +280,47 @@ async function initializeConfig(configJSON, context) {
279
280
  return {config, warnings};
280
281
  }
281
282
 
282
- module.exports = {resolveWorkingCopy, initializeConfig};
283
+ /**
284
+ * @param {LH.Config.FRConfig} config
285
+ * @return {string}
286
+ */
287
+ function getConfigDisplayString(config) {
288
+ /** @type {LH.Config.FRConfig} */
289
+ const jsonConfig = JSON.parse(JSON.stringify(config));
290
+
291
+ if (jsonConfig.navigations) {
292
+ for (const navigation of jsonConfig.navigations) {
293
+ for (let i = 0; i < navigation.artifacts.length; ++i) {
294
+ // @ts-expect-error Breaking the Config.AnyArtifactDefn type.
295
+ navigation.artifacts[i] = navigation.artifacts[i].id;
296
+ }
297
+ }
298
+ }
299
+
300
+ if (jsonConfig.artifacts) {
301
+ for (const artifactDefn of jsonConfig.artifacts) {
302
+ // @ts-expect-error Breaking the Config.AnyArtifactDefn type.
303
+ artifactDefn.gatherer = artifactDefn.gatherer.path;
304
+ // Dependencies are not declared on Config JSON
305
+ artifactDefn.dependencies = undefined;
306
+ }
307
+ }
308
+
309
+ if (jsonConfig.audits) {
310
+ for (const auditDefn of jsonConfig.audits) {
311
+ // @ts-expect-error Breaking the Config.AuditDefn type.
312
+ auditDefn.implementation = undefined;
313
+ if (Object.keys(auditDefn.options).length === 0) {
314
+ // @ts-expect-error Breaking the Config.AuditDefn type.
315
+ auditDefn.options = undefined;
316
+ }
317
+ }
318
+ }
319
+
320
+ // Printed config is more useful with localized strings.
321
+ format.replaceIcuMessages(jsonConfig, jsonConfig.settings.locale);
322
+
323
+ return JSON.stringify(jsonConfig, null, 2);
324
+ }
325
+
326
+ module.exports = {resolveWorkingCopy, initializeConfig, getConfigDisplayString};
@@ -6,9 +6,9 @@
6
6
  'use strict';
7
7
 
8
8
  const log = require('lighthouse-logger');
9
- const ProtocolSession = require('./session.js');
10
9
  const ExecutionContext = require('../../gather/driver/execution-context.js');
11
10
  const Fetcher = require('../../gather/fetcher.js');
11
+ const TargetManager = require('../../gather/driver/target-manager.js');
12
12
 
13
13
  /** @return {*} */
14
14
  const throwNotConnectedFn = () => {
@@ -24,8 +24,6 @@ const throwingSession = {
24
24
  on: throwNotConnectedFn,
25
25
  once: throwNotConnectedFn,
26
26
  off: throwNotConnectedFn,
27
- addProtocolMessageListener: throwNotConnectedFn,
28
- removeProtocolMessageListener: throwNotConnectedFn,
29
27
  sendCommand: throwNotConnectedFn,
30
28
  dispose: throwNotConnectedFn,
31
29
  };
@@ -37,6 +35,8 @@ class Driver {
37
35
  */
38
36
  constructor(page) {
39
37
  this._page = page;
38
+ /** @type {TargetManager|undefined} */
39
+ this._targetManager = undefined;
40
40
  /** @type {ExecutionContext|undefined} */
41
41
  this._executionContext = undefined;
42
42
  /** @type {Fetcher|undefined} */
@@ -57,6 +57,11 @@ class Driver {
57
57
  return this._fetcher;
58
58
  }
59
59
 
60
+ get targetManager() {
61
+ if (!this._targetManager) return throwNotConnectedFn();
62
+ return this._targetManager;
63
+ }
64
+
60
65
  /** @return {Promise<string>} */
61
66
  async url() {
62
67
  return this._page.url();
@@ -67,8 +72,10 @@ class Driver {
67
72
  if (this.defaultSession !== throwingSession) return;
68
73
  const status = {msg: 'Connecting to browser', id: 'lh:driver:connect'};
69
74
  log.time(status);
70
- const session = await this._page.target().createCDPSession();
71
- this.defaultSession = new ProtocolSession(session);
75
+ const cdpSession = await this._page.target().createCDPSession();
76
+ this._targetManager = new TargetManager(cdpSession);
77
+ await this._targetManager.enable();
78
+ this.defaultSession = this._targetManager.rootSession();
72
79
  this._executionContext = new ExecutionContext(this.defaultSession);
73
80
  this._fetcher = new Fetcher(this.defaultSession);
74
81
  log.timeEnd(status);
@@ -77,6 +84,7 @@ class Driver {
77
84
  /** @return {Promise<void>} */
78
85
  async disconnect() {
79
86
  if (this.defaultSession === throwingSession) return;
87
+ this._targetManager?.disable();
80
88
  await this.defaultSession.dispose();
81
89
  }
82
90
  }