lighthouse 10.0.2-dev.20230305 → 10.0.2-dev.20230307

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 (37) hide show
  1. package/cli/test/smokehouse/config/exclusions.js +0 -2
  2. package/core/audits/audit.d.ts +6 -0
  3. package/core/audits/audit.js +14 -2
  4. package/core/audits/bootup-time.js +2 -3
  5. package/core/audits/byte-efficiency/byte-efficiency-audit.d.ts +2 -0
  6. package/core/audits/byte-efficiency/byte-efficiency-audit.js +3 -1
  7. package/core/audits/byte-efficiency/total-byte-weight.js +1 -1
  8. package/core/audits/byte-efficiency/uses-long-cache-ttl.js +2 -2
  9. package/core/audits/long-tasks.js +2 -1
  10. package/core/audits/mainthread-work-breakdown.js +2 -1
  11. package/core/audits/network-rtt.js +1 -1
  12. package/core/audits/network-server-latency.js +2 -1
  13. package/core/audits/performance-budget.js +1 -1
  14. package/core/audits/prioritize-lcp-image.js +1 -1
  15. package/core/audits/third-party-summary.js +4 -1
  16. package/core/audits/timing-budget.js +2 -1
  17. package/core/audits/uses-rel-preconnect.js +1 -1
  18. package/core/audits/uses-rel-preload.js +1 -1
  19. package/core/audits/work-during-interaction.js +1 -1
  20. package/core/lib/lighthouse-compatibility.js +17 -0
  21. package/core/runner.js +1 -0
  22. package/dist/report/bundle.esm.js +222 -42
  23. package/dist/report/flow.js +17 -17
  24. package/dist/report/standalone.js +16 -16
  25. package/flow-report/src/i18n/i18n.d.ts +6 -0
  26. package/package.json +3 -5
  27. package/report/assets/styles.css +86 -4
  28. package/report/renderer/components.js +1 -1
  29. package/report/renderer/details-renderer.d.ts +26 -6
  30. package/report/renderer/details-renderer.js +144 -21
  31. package/report/renderer/report-renderer.js +1 -0
  32. package/report/renderer/report-ui-features.js +18 -9
  33. package/report/renderer/report-utils.d.ts +13 -4
  34. package/report/renderer/report-utils.js +43 -11
  35. package/shared/localization/locales/en-US.json +9 -0
  36. package/shared/localization/locales/en-XL.json +9 -0
  37. package/types/lhr/audit-details.d.ts +18 -0
@@ -25,8 +25,6 @@ const exclusions = {
25
25
  'metrics-tricky-tti', 'metrics-tricky-tti-late-fcp', 'screenshot',
26
26
  // Disabled because of differences that need further investigation
27
27
  'byte-efficiency', 'byte-gzip', 'perf-preload',
28
- // https://bugs.chromium.org/p/chromium/issues/detail?id=1420418
29
- 'issues-mixed-content',
30
28
  ],
31
29
  };
32
30
 
@@ -1,10 +1,16 @@
1
1
  export type TableOptions = {
2
2
  wastedMs?: number | undefined;
3
3
  wastedBytes?: number | undefined;
4
+ sortedBy?: LH.Audit.Details.Table['sortedBy'] | undefined;
5
+ skipSumming?: LH.Audit.Details.Table['skipSumming'] | undefined;
6
+ isEntityGrouped?: LH.Audit.Details.Table['isEntityGrouped'] | undefined;
4
7
  };
5
8
  export type OpportunityOptions = {
6
9
  overallSavingsMs: number;
7
10
  overallSavingsBytes?: number | undefined;
11
+ sortedBy?: LH.Audit.Details.Opportunity['sortedBy'] | undefined;
12
+ skipSumming?: LH.Audit.Details.Opportunity['skipSumming'] | undefined;
13
+ isEntityGrouped?: LH.Audit.Details.Opportunity['isEntityGrouped'] | undefined;
8
14
  };
9
15
  export class Audit {
10
16
  /**
@@ -15,12 +15,18 @@ const DEFAULT_PASS = 'defaultPass';
15
15
  * @typedef TableOptions
16
16
  * @property {number=} wastedMs
17
17
  * @property {number=} wastedBytes
18
+ * @property {LH.Audit.Details.Table['sortedBy']=} sortedBy
19
+ * @property {LH.Audit.Details.Table['skipSumming']=} skipSumming
20
+ * @property {LH.Audit.Details.Table['isEntityGrouped']=} isEntityGrouped
18
21
  */
19
22
 
20
23
  /**
21
24
  * @typedef OpportunityOptions
22
25
  * @property {number} overallSavingsMs
23
26
  * @property {number=} overallSavingsBytes
27
+ * @property {LH.Audit.Details.Opportunity['sortedBy']=} sortedBy
28
+ * @property {LH.Audit.Details.Opportunity['skipSumming']=} skipSumming
29
+ * @property {LH.Audit.Details.Opportunity['isEntityGrouped']=} isEntityGrouped
24
30
  */
25
31
 
26
32
  /**
@@ -139,7 +145,7 @@ class Audit {
139
145
  * @return {LH.Audit.Details.Table}
140
146
  */
141
147
  static makeTableDetails(headings, results, options = {}) {
142
- const {wastedBytes, wastedMs} = options;
148
+ const {wastedBytes, wastedMs, sortedBy, skipSumming, isEntityGrouped} = options;
143
149
  const summary = (wastedBytes || wastedMs) ? {wastedBytes, wastedMs} : undefined;
144
150
  if (results.length === 0) {
145
151
  return {
@@ -157,6 +163,9 @@ class Audit {
157
163
  headings: headings,
158
164
  items: results,
159
165
  summary,
166
+ sortedBy,
167
+ skipSumming,
168
+ isEntityGrouped,
160
169
  };
161
170
  }
162
171
 
@@ -234,7 +243,7 @@ class Audit {
234
243
  */
235
244
  static makeOpportunityDetails(headings, items, options) {
236
245
  Audit.assertHeadingKeysExist(headings, items);
237
- const {overallSavingsMs, overallSavingsBytes} = options;
246
+ const {overallSavingsMs, overallSavingsBytes, sortedBy, skipSumming, isEntityGrouped} = options;
238
247
 
239
248
  return {
240
249
  type: 'opportunity',
@@ -242,6 +251,9 @@ class Audit {
242
251
  items,
243
252
  overallSavingsMs,
244
253
  overallSavingsBytes,
254
+ sortedBy,
255
+ skipSumming,
256
+ isEntityGrouped,
245
257
  };
246
258
  }
247
259
 
@@ -117,8 +117,6 @@ class BootupTime extends Audit {
117
117
  runWarnings = [str_(UIStrings.chromeExtensionsWarning)];
118
118
  }
119
119
 
120
- const summary = {wastedMs: totalBootupTime};
121
-
122
120
  /** @type {LH.Audit.Details.Table['headings']} */
123
121
  const headings = [
124
122
  {key: 'url', valueType: 'url', label: str_(i18n.UIStrings.columnURL)},
@@ -128,7 +126,8 @@ class BootupTime extends Audit {
128
126
  label: str_(UIStrings.columnScriptParse)},
129
127
  ];
130
128
 
131
- const details = BootupTime.makeTableDetails(headings, results, summary);
129
+ const details = BootupTime.makeTableDetails(headings, results,
130
+ {wastedMs: totalBootupTime, sortedBy: ['total']});
132
131
 
133
132
  const score = Audit.computeLogNormalScore(
134
133
  {p10: context.options.p10, median: context.options.median},
@@ -7,6 +7,7 @@ export type ByteEfficiencyProduct = {
7
7
  displayValue?: import("../../index.js").IcuMessage | undefined;
8
8
  explanation?: import("../../index.js").IcuMessage | undefined;
9
9
  warnings?: (string | import("../../index.js").IcuMessage)[] | undefined;
10
+ sortedBy?: string[] | undefined;
10
11
  };
11
12
  /**
12
13
  * @typedef {object} ByteEfficiencyProduct
@@ -16,6 +17,7 @@ export type ByteEfficiencyProduct = {
16
17
  * @property {LH.IcuMessage} [displayValue]
17
18
  * @property {LH.IcuMessage} [explanation]
18
19
  * @property {Array<string | LH.IcuMessage>} [warnings]
20
+ * @property {Array<string>} [sortedBy]
19
21
  */
20
22
  /**
21
23
  * @overview Used as the base for all byte efficiency audits. Computes total bytes
@@ -29,6 +29,7 @@ const WASTED_MS_FOR_SCORE_OF_ZERO = 5000;
29
29
  * @property {LH.IcuMessage} [displayValue]
30
30
  * @property {LH.IcuMessage} [explanation]
31
31
  * @property {Array<string | LH.IcuMessage>} [warnings]
32
+ * @property {Array<string>} [sortedBy]
32
33
  */
33
34
 
34
35
  /**
@@ -228,8 +229,9 @@ class ByteEfficiencyAudit extends Audit {
228
229
  displayValue = str_(i18n.UIStrings.displayValueByteSavings, {wastedBytes});
229
230
  }
230
231
 
232
+ const sortedBy = result.sortedBy || ['wastedBytes'];
231
233
  const details = Audit.makeOpportunityDetails(result.headings, results,
232
- {overallSavingsMs: wastedMs, overallSavingsBytes: wastedBytes});
234
+ {overallSavingsMs: wastedMs, overallSavingsBytes: wastedBytes, sortedBy});
233
235
 
234
236
  return {
235
237
  explanation: result.explanation,
@@ -93,7 +93,7 @@ class TotalByteWeight extends Audit {
93
93
  {key: 'totalBytes', valueType: 'bytes', label: str_(i18n.UIStrings.columnTransferSize)},
94
94
  ];
95
95
 
96
- const tableDetails = Audit.makeTableDetails(headings, results);
96
+ const tableDetails = Audit.makeTableDetails(headings, results, {sortedBy: ['totalBytes']});
97
97
 
98
98
  return {
99
99
  score,
@@ -280,8 +280,8 @@ class CacheHeaders extends Audit {
280
280
  displayUnit: 'kb', granularity: 1},
281
281
  ];
282
282
 
283
- const summary = {wastedBytes: totalWastedBytes};
284
- const details = Audit.makeTableDetails(headings, results, summary);
283
+ const details = Audit.makeTableDetails(headings, results,
284
+ {wastedBytes: totalWastedBytes, sortedBy: ['totalBytes'], skipSumming: ['cacheLifetimeMs']});
285
285
 
286
286
  return {
287
287
  score,
@@ -104,7 +104,8 @@ class LongTasks extends Audit {
104
104
  /* eslint-enable max-len */
105
105
  ];
106
106
 
107
- const tableDetails = Audit.makeTableDetails(headings, results);
107
+ const tableDetails = Audit.makeTableDetails(headings, results,
108
+ {sortedBy: ['duration'], skipSumming: ['startTime']});
108
109
 
109
110
  let displayValue;
110
111
  if (results.length > 0) {
@@ -114,7 +114,8 @@ class MainThreadWorkBreakdown extends Audit {
114
114
  ];
115
115
 
116
116
  results.sort((a, b) => categoryTotals[b.group] - categoryTotals[a.group]);
117
- const tableDetails = MainThreadWorkBreakdown.makeTableDetails(headings, results);
117
+ const tableDetails = MainThreadWorkBreakdown.makeTableDetails(headings, results,
118
+ {sortedBy: ['duration']});
118
119
 
119
120
  const score = Audit.computeLogNormalScore(
120
121
  {p10: context.options.p10, median: context.options.median},
@@ -73,7 +73,7 @@ class NetworkRTT extends Audit {
73
73
  {key: 'rtt', valueType: 'ms', granularity: 1, label: str_(i18n.UIStrings.columnTimeSpent)},
74
74
  ];
75
75
 
76
- const tableDetails = Audit.makeTableDetails(headings, results);
76
+ const tableDetails = Audit.makeTableDetails(headings, results, {sortedBy: ['rtt']});
77
77
 
78
78
  return {
79
79
  score: 1,
@@ -72,7 +72,8 @@ class NetworkServerLatency extends Audit {
72
72
  label: str_(i18n.UIStrings.columnTimeSpent)},
73
73
  ];
74
74
 
75
- const tableDetails = Audit.makeTableDetails(headings, results);
75
+ const tableDetails = Audit.makeTableDetails(headings, results,
76
+ {sortedBy: ['serverResponseTime']});
76
77
 
77
78
  return {
78
79
  score: Math.max(1 - (maxLatency / 500), 0),
@@ -144,7 +144,7 @@ class ResourceBudget extends Audit {
144
144
 
145
145
  return {
146
146
  details: Audit.makeTableDetails(headers,
147
- this.tableItems(budget, summary)),
147
+ this.tableItems(budget, summary), {sortedBy: ['sizeOverBudget']}),
148
148
  score: 1,
149
149
  };
150
150
  }
@@ -319,7 +319,7 @@ class PrioritizeLcpImage extends Audit {
319
319
  {key: 'wastedMs', valueType: 'timespanMs', label: str_(i18n.UIStrings.columnWastedMs)},
320
320
  ];
321
321
  const details = Audit.makeOpportunityDetails(headings, results,
322
- {overallSavingsMs: wastedMs});
322
+ {overallSavingsMs: wastedMs, sortedBy: ['wastedMs']});
323
323
 
324
324
  // If LCP element was an image and had valid network records (regardless of
325
325
  // if it should be preloaded), it will be found first in the `initiatorPath`.
@@ -243,12 +243,15 @@ class ThirdPartySummary extends Audit {
243
243
  };
244
244
  }
245
245
 
246
+ const details = Audit.makeTableDetails(headings, results,
247
+ {...overallSummary, isEntityGrouped: true});
248
+
246
249
  return {
247
250
  score: Number(overallSummary.wastedMs <= PASS_THRESHOLD_IN_MS),
248
251
  displayValue: str_(UIStrings.displayValue, {
249
252
  timeInMs: overallSummary.wastedMs,
250
253
  }),
251
- details: Audit.makeTableDetails(headings, results, overallSummary),
254
+ details,
252
255
  };
253
256
  }
254
257
  }
@@ -164,7 +164,8 @@ class TimingBudget extends Audit {
164
164
  ];
165
165
 
166
166
  return {
167
- details: Audit.makeTableDetails(headers, this.tableItems(budget, summary)),
167
+ details: Audit.makeTableDetails(headers, this.tableItems(budget, summary),
168
+ {sortedBy: ['overBudget']}),
168
169
  score: 1,
169
170
  };
170
171
  }
@@ -234,7 +234,7 @@ class UsesRelPreconnectAudit extends Audit {
234
234
  ];
235
235
 
236
236
  const details = Audit.makeOpportunityDetails(headings, results,
237
- {overallSavingsMs: maxWasted});
237
+ {overallSavingsMs: maxWasted, sortedBy: ['wastedMs']});
238
238
 
239
239
  return {
240
240
  score: ByteEfficiencyAudit.scoreForWastedMs(maxWasted),
@@ -240,7 +240,7 @@ class UsesRelPreloadAudit extends Audit {
240
240
  {key: 'wastedMs', valueType: 'timespanMs', label: str_(i18n.UIStrings.columnWastedMs)},
241
241
  ];
242
242
  const details = Audit.makeOpportunityDetails(headings, results,
243
- {overallSavingsMs: wastedMs});
243
+ {overallSavingsMs: wastedMs, sortedBy: ['wastedMs']});
244
244
 
245
245
  return {
246
246
  score: ByteEfficiencyAudit.scoreForWastedMs(wastedMs),
@@ -193,7 +193,7 @@ class WorkDuringInteraction extends Audit {
193
193
  ];
194
194
 
195
195
  return {
196
- table: Audit.makeTableDetails(headings, items),
196
+ table: Audit.makeTableDetails(headings, items, {sortedBy: ['total']}),
197
197
  phases,
198
198
  };
199
199
  }
@@ -81,6 +81,23 @@ function upgradeLhrForCompatibility(lhr) {
81
81
  }
82
82
  }
83
83
 
84
+ // In 10.0, third-party-summary deprecated entity: LinkValue and switched to entity name string
85
+ if (audit.id === 'third-party-summary') {
86
+ if (audit.details.type === 'opportunity' || audit.details.type === 'table') {
87
+ const {headings, items} = audit.details;
88
+ if (headings[0].valueType === 'link') {
89
+ // Apply upgrade only if we are dealing with an older version (valueType=link marker).
90
+ headings[0].valueType = 'text';
91
+ for (const item of items) {
92
+ if (typeof item.entity === 'object' && item.entity.type === 'link') {
93
+ item.entity = item.entity.text;
94
+ }
95
+ }
96
+ audit.details.isEntityGrouped = true;
97
+ }
98
+ }
99
+ }
100
+
84
101
  // TODO: convert printf-style displayValue.
85
102
  // Added: #5099, v3
86
103
  // Removed: #6767, v4
package/core/runner.js CHANGED
@@ -170,6 +170,7 @@ class Runner {
170
170
  // Reduce payload size in LHR JSON by omitting whats falsy.
171
171
  if (entity === classifiedEntities.firstParty) shortEntity.isFirstParty = true;
172
172
  if (entity.isUnrecognized) shortEntity.isUnrecognized = true;
173
+ if (entity.category) shortEntity.category = entity.category;
173
174
  entities.push(shortEntity);
174
175
  }
175
176