lighthouse 9.5.0-dev.20220620 → 9.5.0-dev.20220623
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/lighthouse-cli/bin.js +8 -2
- package/lighthouse-cli/test/smokehouse/__snapshots__/report-assert-test.js.snap +71 -0
- package/lighthouse-cli/test/smokehouse/report-assert-test.js +264 -0
- package/lighthouse-cli/test/smokehouse/report-assert.js +39 -35
- package/lighthouse-core/audits/byte-efficiency/unused-javascript.js +1 -1
- package/lighthouse-core/config/config-helpers.js +15 -0
- package/lighthouse-core/fraggle-rock/config/config.js +45 -1
- package/lighthouse-core/fraggle-rock/gather/driver.js +13 -5
- package/lighthouse-core/fraggle-rock/gather/session.js +23 -67
- package/lighthouse-core/gather/driver/navigation.js +1 -1
- package/lighthouse-core/gather/driver/network-monitor.js +19 -81
- package/lighthouse-core/gather/driver/target-manager.js +110 -45
- package/lighthouse-core/gather/driver.js +24 -16
- package/lighthouse-core/gather/gatherers/devtools-log.js +7 -11
- package/lighthouse-core/gather/gatherers/full-page-screenshot.js +1 -1
- package/lighthouse-core/index.js +24 -9
- package/lighthouse-core/lib/cdt/Common.js +13 -0
- package/lighthouse-core/lib/cdt/Platform.js +3 -0
- package/lighthouse-core/lib/cdt/generated/ParsedURL.js +178 -0
- package/lighthouse-core/lib/cdt/generated/SourceMap.js +155 -62
- package/lighthouse-core/lib/network-recorder.js +9 -18
- package/package.json +2 -2
- package/third-party/devtools-tests/README.md +14 -0
- package/third-party/devtools-tests/e2e/lighthouse/generate-report_test.ts +29 -0
- package/third-party/devtools-tests/e2e/lighthouse/indexeddb-warning_test.ts +29 -0
- package/third-party/devtools-tests/e2e/resources/lighthouse/lighthouse-storage.html +11 -0
- package/third-party/download-content-shell/README.md +1 -0
- package/third-party/download-content-shell/download-content-shell.js +6 -1
- package/types/gatherer.d.ts +5 -2
- package/types/protocol.d.ts +26 -17
package/lighthouse-cli/bin.js
CHANGED
|
@@ -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
|
-
|
|
124
|
-
|
|
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
|
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
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
|
+
|
|
35
|
+
exports[`getAssertionReport works (trivial failing) 1`] = `
|
|
36
|
+
"X difference at cumulative-layout-shift audit.details.items.length
|
|
37
|
+
expected: []
|
|
38
|
+
found: [{\\"cumulativeLayoutShiftMainFrame\\":0.13570762803819444,\\"totalCumulativeLayoutShift\\":0.13570762803819444}]
|
|
39
|
+
|
|
40
|
+
found result:
|
|
41
|
+
{
|
|
42
|
+
\\"id\\": \\"cumulative-layout-shift\\",
|
|
43
|
+
\\"title\\": \\"Cumulative Layout Shift\\",
|
|
44
|
+
\\"description\\": \\"Cumulative Layout Shift measures the movement of visible elements within the viewport. [Learn more](https://web.dev/cls/).\\",
|
|
45
|
+
\\"score\\": 0.8,
|
|
46
|
+
\\"scoreDisplayMode\\": \\"numeric\\",
|
|
47
|
+
\\"numericValue\\": 0.13570762803819444,
|
|
48
|
+
\\"numericUnit\\": \\"unitless\\",
|
|
49
|
+
\\"displayValue\\": \\"0.136\\",
|
|
50
|
+
\\"details\\": {
|
|
51
|
+
\\"type\\": \\"debugdata\\",
|
|
52
|
+
\\"items\\": [
|
|
53
|
+
{
|
|
54
|
+
\\"cumulativeLayoutShiftMainFrame\\": 0.13570762803819444,
|
|
55
|
+
\\"totalCumulativeLayoutShift\\": 0.13570762803819444
|
|
56
|
+
}
|
|
57
|
+
]
|
|
58
|
+
}
|
|
59
|
+
}"
|
|
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
|
+
`;
|
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license Copyright 2022 The Lighthouse Authors. All Rights Reserved.
|
|
3
|
+
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
|
|
4
|
+
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/* eslint-disable no-control-regex */
|
|
8
|
+
|
|
9
|
+
import {findDifferences, getAssertionReport} from './report-assert.js';
|
|
10
|
+
import {readJson} from '../../../root.js';
|
|
11
|
+
|
|
12
|
+
describe('findDiffersences', () => {
|
|
13
|
+
const testCases = {
|
|
14
|
+
'works (trivial passing)': {
|
|
15
|
+
actual: {},
|
|
16
|
+
expected: {},
|
|
17
|
+
diffs: null,
|
|
18
|
+
},
|
|
19
|
+
'works (trivial fail)': {
|
|
20
|
+
actual: {},
|
|
21
|
+
expected: {a: 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}}],
|
|
28
|
+
},
|
|
29
|
+
'works (trivial fail, nested)': {
|
|
30
|
+
actual: {a: {b: 2}},
|
|
31
|
+
expected: {a: {b: 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
|
+
],
|
|
54
|
+
},
|
|
55
|
+
|
|
56
|
+
'range (1)': {
|
|
57
|
+
actual: {duration: 100},
|
|
58
|
+
expected: {duration: '>=100'},
|
|
59
|
+
diffs: null,
|
|
60
|
+
},
|
|
61
|
+
'range (2)': {
|
|
62
|
+
actual: {},
|
|
63
|
+
expected: {duration: '>=100'},
|
|
64
|
+
diffs: [{path: '.duration', actual: undefined, expected: '>=100'}],
|
|
65
|
+
},
|
|
66
|
+
'range (3)': {
|
|
67
|
+
actual: {duration: 100},
|
|
68
|
+
expected: {duration: '>100'},
|
|
69
|
+
diffs: [{path: '.duration', actual: 100, expected: '>100'}],
|
|
70
|
+
},
|
|
71
|
+
'range (4)': {
|
|
72
|
+
actual: {duration: 100},
|
|
73
|
+
expected: {duration: '<100'},
|
|
74
|
+
diffs: [{path: '.duration', actual: 100, expected: '<100'}],
|
|
75
|
+
},
|
|
76
|
+
|
|
77
|
+
'array (1)': {
|
|
78
|
+
actual: {prices: [0, 1, 2, 3, 4, 5]},
|
|
79
|
+
expected: {prices: {length: 6}},
|
|
80
|
+
diffs: null,
|
|
81
|
+
},
|
|
82
|
+
'array (2)': {
|
|
83
|
+
actual: {prices: [0, 1, 2, 3, 4, 5]},
|
|
84
|
+
expected: {prices: {length: '>0'}},
|
|
85
|
+
diffs: null,
|
|
86
|
+
},
|
|
87
|
+
'array (3)': {
|
|
88
|
+
actual: {prices: [0, 1, 2, 3, 4, 5]},
|
|
89
|
+
expected: {prices: [0, 1, 2, 3, 4, 5]},
|
|
90
|
+
diffs: null,
|
|
91
|
+
},
|
|
92
|
+
'array (4)': {
|
|
93
|
+
actual: {prices: [0, 1, 2, 3, 4, 5]},
|
|
94
|
+
expected: {prices: [0, 1, 2, 3, 4, 5, 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
|
+
],
|
|
99
|
+
},
|
|
100
|
+
'array (5)': {
|
|
101
|
+
actual: {prices: [0, 1, 2, 3, 4, 5]},
|
|
102
|
+
expected: {prices: []},
|
|
103
|
+
diffs: [{path: '.prices.length', actual: [0, 1, 2, 3, 4, 5], expected: []}],
|
|
104
|
+
},
|
|
105
|
+
'array (6)': {
|
|
106
|
+
actual: {prices: [0, 1, 2, 3, 4, 5]},
|
|
107
|
+
expected: {prices: {'3': '>=3'}},
|
|
108
|
+
diffs: null,
|
|
109
|
+
},
|
|
110
|
+
'array (7)': {
|
|
111
|
+
actual: {prices: [0, 1, 2, {nested: 3}, 4, 5]},
|
|
112
|
+
expected: {prices: {'3': {nested: '>3'}}},
|
|
113
|
+
diffs: [{path: '.prices[3].nested', actual: 3, expected: '>3'}],
|
|
114
|
+
},
|
|
115
|
+
|
|
116
|
+
'_includes (1)': {
|
|
117
|
+
actual: {prices: [0, 1, 2, 3, 4, 5]},
|
|
118
|
+
expected: {prices: {_includes: [4]}},
|
|
119
|
+
diffs: null,
|
|
120
|
+
},
|
|
121
|
+
'_includes (2)': {
|
|
122
|
+
actual: {prices: [0, 1, 2, 3, 4, 5]},
|
|
123
|
+
expected: {prices: {_includes: [4, 4]}},
|
|
124
|
+
diffs: [{path: '.prices', actual: 'Item not found in array', expected: 4}],
|
|
125
|
+
},
|
|
126
|
+
'_includes (3)': {
|
|
127
|
+
actual: {prices: [0, 1, 2, 3, 4, 5]},
|
|
128
|
+
expected: {prices: {_includes: [100]}},
|
|
129
|
+
diffs: [{path: '.prices', actual: 'Item not found in array', expected: 100}],
|
|
130
|
+
},
|
|
131
|
+
'_includes (4)': {
|
|
132
|
+
actual: {prices: ['0', '1', '2', '3', '4', '5']},
|
|
133
|
+
expected: {prices: {_includes: [/\d/, /\d/, /\d/, /\d/, /\d/, /\d/]}},
|
|
134
|
+
diffs: null,
|
|
135
|
+
},
|
|
136
|
+
|
|
137
|
+
'_excludes (1)': {
|
|
138
|
+
actual: {prices: [0, 1, 2, 3, 4, 5]},
|
|
139
|
+
expected: {prices: {_excludes: [100]}},
|
|
140
|
+
diffs: null,
|
|
141
|
+
},
|
|
142
|
+
'_excludes (2)': {
|
|
143
|
+
actual: {prices: [0, 1, 2, 3, 4, 5]},
|
|
144
|
+
expected: {prices: {_excludes: [2]}},
|
|
145
|
+
diffs: [{path: '.prices', actual: 2, expected: {
|
|
146
|
+
expectedExclusion: 2,
|
|
147
|
+
message: 'Expected to not find matching entry via _excludes',
|
|
148
|
+
}}],
|
|
149
|
+
},
|
|
150
|
+
|
|
151
|
+
'_includes and _excludes (1)': {
|
|
152
|
+
actual: {prices: [0, 1, 2, 3, 4, 5]},
|
|
153
|
+
expected: {prices: {_includes: [2], _excludes: [2]}},
|
|
154
|
+
diffs: null,
|
|
155
|
+
},
|
|
156
|
+
// Order matters.
|
|
157
|
+
'_includes and _excludes (2)': {
|
|
158
|
+
actual: {prices: [0, 1, 2, 3, 4, 5]},
|
|
159
|
+
expected: {prices: {_excludes: [2], _includes: [2]}},
|
|
160
|
+
diffs: [{path: '.prices', actual: 2, expected: {
|
|
161
|
+
expectedExclusion: 2,
|
|
162
|
+
message: 'Expected to not find matching entry via _excludes',
|
|
163
|
+
}}],
|
|
164
|
+
},
|
|
165
|
+
'_includes and _excludes (3)': {
|
|
166
|
+
actual: {prices: [0, 1, 2, 3, 4, 5]},
|
|
167
|
+
expected: {prices: {_includes: [2], _excludes: [2, 1]}},
|
|
168
|
+
diffs: [{path: '.prices', actual: 1, expected: {
|
|
169
|
+
expectedExclusion: 1,
|
|
170
|
+
message: 'Expected to not find matching entry via _excludes',
|
|
171
|
+
}}],
|
|
172
|
+
},
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
for (const [testName, {actual, expected, diffs}] of Object.entries(testCases)) {
|
|
176
|
+
it(testName, () => {
|
|
177
|
+
expect(findDifferences('', actual, expected)).toEqual(diffs);
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Removes ANSI codes.
|
|
184
|
+
* TODO: should make it so logger can disable these.
|
|
185
|
+
* @param {string} text
|
|
186
|
+
*/
|
|
187
|
+
function clean(text) {
|
|
188
|
+
return text
|
|
189
|
+
.replace(/\x1B.*?m/g, '')
|
|
190
|
+
.replace(/\x1b.*?m/g, '')
|
|
191
|
+
.replace(/[✘×]/g, 'X')
|
|
192
|
+
.trim();
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
describe('getAssertionReport', () => {
|
|
196
|
+
const lhr = readJson('lighthouse-core/test/results/sample_v2.json');
|
|
197
|
+
const artifacts = readJson('lighthouse-core/test/results/artifacts/artifacts.json');
|
|
198
|
+
|
|
199
|
+
it('works (trivial passing)', () => {
|
|
200
|
+
const report = getAssertionReport({lhr, artifacts}, {
|
|
201
|
+
lhr: {
|
|
202
|
+
audits: {},
|
|
203
|
+
requestedUrl: 'http://localhost:10200/dobetterweb/dbw_tester.html',
|
|
204
|
+
finalUrl: 'http://localhost:10200/dobetterweb/dbw_tester.html',
|
|
205
|
+
},
|
|
206
|
+
});
|
|
207
|
+
expect(report).toMatchObject({passed: 3, failed: 0, log: ''});
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
it('works (trivial failing)', () => {
|
|
211
|
+
const report = getAssertionReport({lhr, artifacts}, {
|
|
212
|
+
lhr: {
|
|
213
|
+
audits: {
|
|
214
|
+
'cumulative-layout-shift': {
|
|
215
|
+
details: {
|
|
216
|
+
items: [],
|
|
217
|
+
},
|
|
218
|
+
},
|
|
219
|
+
},
|
|
220
|
+
requestedUrl: 'http://localhost:10200/dobetterweb/dbw_tester.html',
|
|
221
|
+
finalUrl: 'http://localhost:10200/dobetterweb/dbw_tester.html',
|
|
222
|
+
},
|
|
223
|
+
});
|
|
224
|
+
expect(report).toMatchObject({passed: 3, failed: 1});
|
|
225
|
+
expect(clean(report.log)).toMatchSnapshot();
|
|
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
|
+
});
|
|
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}
|
|
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 {
|
|
89
|
+
* @return {Difference[]|null}
|
|
90
90
|
*/
|
|
91
|
-
function
|
|
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
|
-
|
|
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
|
-
!
|
|
133
|
+
!findDifferences(keyPath, actualEntry, expectedEntry));
|
|
132
134
|
if (matchingIndex !== -1) {
|
|
133
135
|
inclExclCopy.splice(matchingIndex, 1);
|
|
134
136
|
continue;
|
|
135
137
|
}
|
|
136
138
|
|
|
137
|
-
|
|
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
|
-
!
|
|
160
|
+
!findDifferences(keyPath, actualEntry, expectedExclusion));
|
|
159
161
|
if (matchingIndex !== -1) {
|
|
160
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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: !
|
|
212
|
-
|
|
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.
|
|
431
|
-
const diff
|
|
432
|
-
|
|
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
|
-
|
|
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)}
|
|
@@ -489,4 +490,7 @@ function getAssertionReport(actual, expected, reportOptions = {}) {
|
|
|
489
490
|
};
|
|
490
491
|
}
|
|
491
492
|
|
|
492
|
-
export {
|
|
493
|
+
export {
|
|
494
|
+
getAssertionReport,
|
|
495
|
+
findDifferences,
|
|
496
|
+
};
|
|
@@ -133,7 +133,7 @@ class UnusedJavaScript extends ByteEfficiencyAudit {
|
|
|
133
133
|
})
|
|
134
134
|
.filter(d => d.unused >= bundleSourceUnusedThreshold);
|
|
135
135
|
|
|
136
|
-
const commonSourcePrefix = commonPrefix(
|
|
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
|
-
|
|
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};
|