lighthouse 9.4.0-dev.20220227 → 9.4.0-dev.20220228

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.
@@ -186,6 +186,7 @@ async function begin() {
186
186
  console.log('\n✨ Be sure to have recently run this: yarn build-all');
187
187
  }
188
188
  const {runLighthouse} = await import(runnerPath);
189
+ runLighthouse.runnerName = argv.runner;
189
190
 
190
191
  // Find test definition file and filter by requestedTestIds.
191
192
  let testDefnPath = argv.testsPath || coreTestDefnsPath;
@@ -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).
@@ -103,6 +103,8 @@ function findDifference(path, actual, expected) {
103
103
  };
104
104
  }
105
105
 
106
+ let inclExclCopy;
107
+
106
108
  // We only care that all expected's own properties are on actual (and not the other way around).
107
109
  // Note an expected `undefined` can match an actual that is either `undefined` or not defined.
108
110
  for (const key of Object.keys(expected)) {
@@ -112,6 +114,8 @@ function findDifference(path, actual, expected) {
112
114
  const expectedValue = expected[key];
113
115
 
114
116
  if (key === '_includes') {
117
+ inclExclCopy = [...actual];
118
+
115
119
  if (!Array.isArray(expectedValue)) throw new Error('Array subset must be array');
116
120
  if (!Array.isArray(actual)) {
117
121
  return {
@@ -121,12 +125,12 @@ function findDifference(path, actual, expected) {
121
125
  };
122
126
  }
123
127
 
124
- const actualCopy = [...actual];
125
128
  for (const expectedEntry of expectedValue) {
126
129
  const matchingIndex =
127
- actualCopy.findIndex(actualEntry => !findDifference(keyPath, actualEntry, expectedEntry));
130
+ inclExclCopy.findIndex(actualEntry =>
131
+ !findDifference(keyPath, actualEntry, expectedEntry));
128
132
  if (matchingIndex !== -1) {
129
- actualCopy.splice(matchingIndex, 1);
133
+ inclExclCopy.splice(matchingIndex, 1);
130
134
  continue;
131
135
  }
132
136
 
@@ -140,6 +144,33 @@ function findDifference(path, actual, expected) {
140
144
  continue;
141
145
  }
142
146
 
147
+ if (key === '_excludes') {
148
+ // Re-use state from `_includes` check, if there was one.
149
+ /** @type {any[]} */
150
+ const arrToCheckAgainst = inclExclCopy || actual;
151
+
152
+ if (!Array.isArray(expectedValue)) throw new Error('Array subset must be array');
153
+ if (!Array.isArray(actual)) continue;
154
+
155
+ const expectedExclusions = expectedValue;
156
+ for (const expectedExclusion of expectedExclusions) {
157
+ const matchingIndex = arrToCheckAgainst.findIndex(actualEntry =>
158
+ !findDifference(keyPath, actualEntry, expectedExclusion));
159
+ if (matchingIndex !== -1) {
160
+ return {
161
+ path,
162
+ actual: arrToCheckAgainst[matchingIndex],
163
+ expected: {
164
+ message: 'Expected to not find matching entry via _excludes',
165
+ expectedExclusion,
166
+ },
167
+ };
168
+ }
169
+ }
170
+
171
+ continue;
172
+ }
173
+
143
174
  const actualValue = actual[key];
144
175
  const subDifference = findDifference(keyPath, actualValue, expectedValue);
145
176
 
@@ -187,7 +218,7 @@ function makeComparison(name, actualResult, expectedResult) {
187
218
  * @param {LocalConsole} localConsole
188
219
  * @param {LH.Result} lhr
189
220
  * @param {Smokehouse.ExpectedRunnerResult} expected
190
- * @param {{isBundled?: boolean}=} reportOptions
221
+ * @param {{runner?: string, isBundled?: boolean}=} reportOptions
191
222
  */
192
223
  function pruneExpectations(localConsole, lhr, expected, reportOptions) {
193
224
  const isFraggleRock = lhr.configSettings.channel === 'fraggle-rock-cli';
@@ -217,8 +248,20 @@ function pruneExpectations(localConsole, lhr, expected, reportOptions) {
217
248
  * @param {*} obj
218
249
  */
219
250
  function pruneRecursively(obj) {
220
- for (const key of Object.keys(obj)) {
221
- const value = obj[key];
251
+ /**
252
+ * @param {string} key
253
+ */
254
+ const remove = (key) => {
255
+ if (Array.isArray(obj)) {
256
+ obj.splice(Number(key), 1);
257
+ } else {
258
+ delete obj[key];
259
+ }
260
+ };
261
+
262
+ // Because we may be deleting keys, we should iterate the keys backwards
263
+ // otherwise arrays with multiple pruning checks will skip elements.
264
+ for (const [key, value] of Object.entries(obj).reverse()) {
222
265
  if (!value || typeof value !== 'object') {
223
266
  continue;
224
267
  }
@@ -229,42 +272,32 @@ function pruneExpectations(localConsole, lhr, expected, reportOptions) {
229
272
  JSON.stringify(value, null, 2),
230
273
  `Actual Chromium version: ${getChromeVersion()}`,
231
274
  ].join(' '));
232
- if (Array.isArray(obj)) {
233
- obj.splice(Number(key), 1);
234
- } else {
235
- delete obj[key];
236
- }
275
+ remove(key);
237
276
  } else if (value._legacyOnly && isFraggleRock) {
238
277
  localConsole.log([
239
278
  `[${key}] marked legacy only but run is Fraggle Rock, pruning expectation:`,
240
279
  JSON.stringify(value, null, 2),
241
280
  ].join(' '));
242
- if (Array.isArray(obj)) {
243
- obj.splice(Number(key), 1);
244
- } else {
245
- delete obj[key];
246
- }
281
+ remove(key);
247
282
  } else if (value._fraggleRockOnly && !isFraggleRock) {
248
283
  localConsole.log([
249
284
  `[${key}] marked Fraggle Rock only but run is legacy, pruning expectation:`,
250
285
  JSON.stringify(value, null, 2),
251
286
  `Actual channel: ${lhr.configSettings.channel}`,
252
287
  ].join(' '));
253
- if (Array.isArray(obj)) {
254
- obj.splice(Number(key), 1);
255
- } else {
256
- delete obj[key];
257
- }
288
+ remove(key);
258
289
  } else if (value._skipInBundled && !isBundled) {
259
290
  localConsole.log([
260
291
  `[${key}] marked as skip in bundled and runner is bundled, pruning expectation:`,
261
292
  JSON.stringify(value, null, 2),
262
293
  ].join(' '));
263
- if (Array.isArray(obj)) {
264
- obj.splice(Number(key), 1);
265
- } else {
266
- delete obj[key];
267
- }
294
+ remove(key);
295
+ } else if (value._runner && reportOptions?.runner !== value._runner) {
296
+ localConsole.log([
297
+ `[${key}] is only for runner ${value._runner}, pruning expectation:`,
298
+ JSON.stringify(value, null, 2),
299
+ ].join(' '));
300
+ remove(key);
268
301
  } else {
269
302
  pruneRecursively(value);
270
303
  }
@@ -275,6 +308,7 @@ function pruneExpectations(localConsole, lhr, expected, reportOptions) {
275
308
  delete obj._skipInBundled;
276
309
  delete obj._minChromiumMilestone;
277
310
  delete obj._maxChromiumMilestone;
311
+ delete obj._runner;
278
312
  }
279
313
 
280
314
  const cloned = cloneDeep(expected);
@@ -420,7 +454,7 @@ function reportAssertion(localConsole, assertion) {
420
454
  * summary. Returns count of passed and failed tests.
421
455
  * @param {{lhr: LH.Result, artifacts: LH.Artifacts, networkRequests?: string[]}} actual
422
456
  * @param {Smokehouse.ExpectedRunnerResult} expected
423
- * @param {{isDebug?: boolean, isBundled?: boolean}=} reportOptions
457
+ * @param {{runner?: string, isDebug?: boolean, isBundled?: boolean}=} reportOptions
424
458
  * @return {{passed: number, failed: number, log: string}}
425
459
  */
426
460
  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, {isDebug});
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.SameSiteCookieIssueDetails>} sameSiteCookieIssues
74
+ * @param {Array<LH.Crdp.Audits.CookieIssueDetails>} CookieIssues
75
75
  * @return {LH.Audit.Details.TableItem}
76
76
  */
77
- static getSameSiteCookieRow(sameSiteCookieIssues) {
77
+ static getCookieRow(CookieIssues) {
78
78
  const requestUrls = new Set();
79
- for (const issue of sameSiteCookieIssues) {
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: 'SameSite cookie',
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.sameSiteCookieIssue.length) {
168
- items.push(this.getSameSiteCookieRow(issues.sameSiteCookieIssue));
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));
@@ -73,7 +73,7 @@ class InspectorIssues extends FRGatherer {
73
73
  mixedContentIssue: [],
74
74
  navigatorUserAgentIssue: [],
75
75
  quirksModeIssue: [],
76
- sameSiteCookieIssue: [],
76
+ cookieIssue: [],
77
77
  sharedArrayBufferIssue: [],
78
78
  twaQualityEnforcement: [],
79
79
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lighthouse",
3
- "version": "9.4.0-dev.20220227",
3
+ "version": "9.4.0-dev.20220228",
4
4
  "description": "Automated auditing, performance metrics, and best practices for the web.",
5
5
  "main": "./lighthouse-core/index.js",
6
6
  "bin": {
@@ -140,7 +140,7 @@
140
140
  "cpy": "^8.1.2",
141
141
  "cross-env": "^7.0.2",
142
142
  "csv-validator": "^0.0.3",
143
- "devtools-protocol": "0.0.964215",
143
+ "devtools-protocol": "0.0.975298",
144
144
  "es-main": "^1.0.2",
145
145
  "eslint": "^8.4.1",
146
146
  "eslint-config-google": "^0.14.0",
@@ -213,7 +213,7 @@
213
213
  "yargs-parser": "^21.0.0"
214
214
  },
215
215
  "resolutions": {
216
- "puppeteer/**/devtools-protocol": "0.0.964215"
216
+ "puppeteer/**/devtools-protocol": "0.0.975298"
217
217
  },
218
218
  "repository": "GoogleChrome/lighthouse",
219
219
  "keywords": [
@@ -38,6 +38,7 @@ Array [
38
38
  "blockedByResponseIssueDetails",
39
39
  "clientHintIssueDetails",
40
40
  "contentSecurityPolicyIssueDetails",
41
+ "cookieIssueDetails",
41
42
  "corsIssueDetails",
42
43
  "deprecationIssueDetails",
43
44
  "federatedAuthRequestIssueDetails",
@@ -47,7 +48,6 @@ Array [
47
48
  "mixedContentIssueDetails",
48
49
  "navigatorUserAgentIssueDetails",
49
50
  "quirksModeIssueDetails",
50
- "sameSiteCookieIssueDetails",
51
51
  "sharedArrayBufferIssueDetails",
52
52
  "twaQualityEnforcementDetails",
53
53
  ]
@@ -566,7 +566,7 @@ declare module Artifacts {
566
566
  mixedContentIssue: LH.Crdp.Audits.MixedContentIssueDetails[];
567
567
  navigatorUserAgentIssue: LH.Crdp.Audits.NavigatorUserAgentIssueDetails[];
568
568
  quirksModeIssue: LH.Crdp.Audits.QuirksModeIssueDetails[];
569
- sameSiteCookieIssue: LH.Crdp.Audits.SameSiteCookieIssueDetails[];
569
+ cookieIssue: LH.Crdp.Audits.CookieIssueDetails[];
570
570
  sharedArrayBufferIssue: LH.Crdp.Audits.SharedArrayBufferIssueDetails[];
571
571
  twaQualityEnforcement: LH.Crdp.Audits.TrustedWebActivityIssueDetails[];
572
572
  }
@@ -52,7 +52,7 @@ declare global {
52
52
  {expectations: Smokehouse.ExpectedRunnerResult | Array<Smokehouse.ExpectedRunnerResult>}
53
53
 
54
54
  export type LighthouseRunner =
55
- (url: string, configJson?: Config.Json, runnerOptions?: {isDebug?: boolean; useFraggleRock?: boolean}) => Promise<{lhr: LHResult, artifacts: Artifacts, log: string}>;
55
+ {runnerName?: string} & ((url: string, configJson?: Config.Json, runnerOptions?: {isDebug?: boolean; useFraggleRock?: boolean}) => Promise<{lhr: LHResult, artifacts: Artifacts, log: string}>);
56
56
 
57
57
  export interface SmokehouseOptions {
58
58
  /** If true, performs extra logging from the test runs. */