lighthouse 8.6.0-dev.20211031 → 8.6.0-dev.20211104
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/dist/report/flow.js +13 -13
- package/dist/report/standalone.js +41 -35
- package/flow-report/.eslintrc.cjs +2 -0
- package/flow-report/assets/standalone-flow-template.html +3 -2
- package/flow-report/assets/styles.css +1 -1
- package/flow-report/src/app.tsx +10 -11
- package/flow-report/src/common.tsx +22 -9
- package/flow-report/src/header.tsx +3 -3
- package/flow-report/src/i18n/i18n.tsx +10 -6
- package/flow-report/src/icons.tsx +30 -9
- package/flow-report/src/sidebar/flow.tsx +3 -3
- package/flow-report/src/sidebar/sidebar.tsx +30 -11
- package/flow-report/src/summary/category.tsx +9 -4
- package/flow-report/src/summary/summary.tsx +9 -3
- package/flow-report/src/util.ts +31 -19
- package/flow-report/src/wrappers/report-renderer.tsx +10 -3
- package/flow-report/src/wrappers/report.tsx +19 -10
- package/flow-report/standalone-flow.tsx +4 -3
- package/flow-report/test/common-test.tsx +25 -10
- package/flow-report/test/header-test.tsx +6 -6
- package/flow-report/test/setup/env-setup.ts +1 -1
- package/flow-report/test/sidebar/sidebar-test.tsx +43 -1
- package/flow-report/test/summary/category-test.tsx +5 -5
- package/flow-report/test/util-test.tsx +32 -11
- package/lighthouse-cli/.eslintrc.cjs +2 -0
- package/lighthouse-cli/cli-flags.js +1 -1
- package/lighthouse-cli/test/smokehouse/lighthouse-runners/bundle.js +4 -2
- package/lighthouse-cli/test/smokehouse/smokehouse.js +1 -1
- package/lighthouse-core/audits/audit.js +1 -1
- package/lighthouse-core/computed/metrics/lantern-total-blocking-time.js +3 -4
- package/lighthouse-core/computed/metrics/tbt-utils.js +57 -0
- package/lighthouse-core/computed/metrics/total-blocking-time.js +3 -53
- package/lighthouse-core/computed/resource-summary.js +1 -1
- package/lighthouse-core/config/config-helpers.js +20 -10
- package/lighthouse-core/config/default-config.js +59 -55
- package/lighthouse-core/fraggle-rock/config/default-config.js +1 -1
- package/lighthouse-core/fraggle-rock/gather/base-gatherer.js +6 -1
- package/lighthouse-core/gather/connections/cri.js +1 -1
- package/lighthouse-core/gather/gatherers/dobetterweb/response-compression.js +1 -0
- package/lighthouse-core/lib/asset-saver.js +4 -1
- package/lighthouse-core/lib/lh-env.js +2 -0
- package/lighthouse-core/lib/proto-preprocessor.js +14 -2
- package/lighthouse-core/lib/tappable-rects.js +8 -15
- package/lighthouse-core/lib/url-shim.js +1 -1
- package/lighthouse-core/runner.js +1 -1
- package/lighthouse-core/util-commonjs.js +25 -11
- package/package.json +11 -8
- package/report/.eslintrc.cjs +2 -0
- package/report/assets/standalone-template.html +2 -3
- package/report/assets/styles.css +227 -251
- package/report/assets/templates.html +19 -29
- package/report/clients/bundle.js +1 -0
- package/report/clients/standalone.js +6 -15
- package/report/renderer/api.js +32 -0
- package/report/renderer/category-renderer.js +22 -18
- package/report/renderer/components.js +46 -79
- package/report/renderer/crc-details-renderer.js +15 -12
- package/report/renderer/dom.js +16 -1
- package/report/renderer/drop-down-menu.js +2 -2
- package/report/renderer/element-screenshot-renderer.js +8 -8
- package/report/renderer/features-util.js +1 -1
- package/report/renderer/open-tab.js +9 -3
- package/report/renderer/performance-category-renderer.js +51 -24
- package/report/renderer/report-renderer.js +34 -10
- package/report/renderer/report-ui-features.js +36 -33
- package/report/renderer/swap-locale-feature.js +3 -3
- package/report/renderer/topbar-features.js +52 -48
- package/report/renderer/util.js +26 -11
- package/report/test/clients/bundle-test.js +8 -0
- package/report/test/renderer/category-renderer-test.js +2 -2
- package/report/test/renderer/performance-category-renderer-test.js +35 -9
- package/report/test/renderer/report-renderer-axe-test.js +5 -2
- package/report/test/renderer/report-renderer-test.js +14 -11
- package/report/test/renderer/report-ui-features-test.js +20 -10
- package/report/test/renderer/util-test.js +13 -31
- package/report/test-assets/faux-psi-template.html +2 -7
- package/report/test-assets/faux-psi.js +20 -39
- package/report/types/html-renderer.d.ts +2 -0
- package/report/types/report-renderer.d.ts +23 -0
- package/shared/localization/format.js +9 -4
- package/shared/localization/locales/ar-XB.json +0 -15
- package/shared/localization/locales/ar.json +0 -15
- package/shared/localization/locales/bg.json +0 -15
- package/shared/localization/locales/ca.json +0 -15
- package/shared/localization/locales/cs.json +0 -15
- package/shared/localization/locales/da.json +0 -15
- package/shared/localization/locales/de.json +0 -15
- package/shared/localization/locales/el.json +0 -15
- package/shared/localization/locales/en-GB.json +0 -15
- package/shared/localization/locales/en-US.json +19 -16
- package/shared/localization/locales/en-XA.json +0 -15
- package/shared/localization/locales/en-XL.json +19 -16
- package/shared/localization/locales/es-419.json +0 -15
- package/shared/localization/locales/es.json +0 -15
- package/shared/localization/locales/fi.json +0 -15
- package/shared/localization/locales/fil.json +0 -15
- package/shared/localization/locales/fr.json +0 -15
- package/shared/localization/locales/he.json +0 -15
- package/shared/localization/locales/hi.json +0 -15
- package/shared/localization/locales/hr.json +0 -15
- package/shared/localization/locales/hu.json +0 -15
- package/shared/localization/locales/id.json +0 -15
- package/shared/localization/locales/it.json +0 -15
- package/shared/localization/locales/ja.json +0 -15
- package/shared/localization/locales/ko.json +0 -15
- package/shared/localization/locales/lt.json +0 -15
- package/shared/localization/locales/lv.json +0 -15
- package/shared/localization/locales/nl.json +0 -15
- package/shared/localization/locales/no.json +0 -15
- package/shared/localization/locales/pl.json +0 -15
- package/shared/localization/locales/pt-PT.json +0 -15
- package/shared/localization/locales/pt.json +0 -15
- package/shared/localization/locales/ro.json +0 -15
- package/shared/localization/locales/ru.json +0 -15
- package/shared/localization/locales/sk.json +0 -15
- package/shared/localization/locales/sl.json +0 -15
- package/shared/localization/locales/sr-Latn.json +0 -15
- package/shared/localization/locales/sr.json +0 -15
- package/shared/localization/locales/sv.json +0 -15
- package/shared/localization/locales/ta.json +0 -15
- package/shared/localization/locales/te.json +0 -15
- package/shared/localization/locales/th.json +0 -15
- package/shared/localization/locales/tr.json +0 -15
- package/shared/localization/locales/uk.json +0 -15
- package/shared/localization/locales/vi.json +0 -15
- package/shared/localization/locales/zh-HK.json +0 -15
- package/shared/localization/locales/zh-TW.json +0 -15
- package/shared/localization/locales/zh.json +0 -15
- package/third-party/chromium-synchronization/installability-errors-test.js +2 -0
- package/types/lhr/flow.d.ts +3 -2
- package/types/rollup-plugin-postprocess.d.ts +10 -0
- package/lighthouse-core/audits/dobetterweb/external-anchors-use-rel-noopener.js +0 -96
|
@@ -24,15 +24,13 @@ export class TopbarFeatures {
|
|
|
24
24
|
this.lhr; // eslint-disable-line no-unused-expressions
|
|
25
25
|
this._reportUIFeatures = reportUIFeatures;
|
|
26
26
|
this._dom = dom;
|
|
27
|
-
/** @type {Document} */
|
|
28
|
-
this._document = this._dom.document();
|
|
29
27
|
this._dropDownMenu = new DropDownMenu(this._dom);
|
|
30
28
|
this._copyAttempt = false;
|
|
31
29
|
/** @type {HTMLElement} */
|
|
32
30
|
this.topbarEl; // eslint-disable-line no-unused-expressions
|
|
33
31
|
/** @type {HTMLElement} */
|
|
34
|
-
this.
|
|
35
|
-
/** @type {HTMLElement} */
|
|
32
|
+
this.categoriesEl; // eslint-disable-line no-unused-expressions
|
|
33
|
+
/** @type {HTMLElement?} */
|
|
36
34
|
this.stickyHeaderEl; // eslint-disable-line no-unused-expressions
|
|
37
35
|
/** @type {HTMLElement} */
|
|
38
36
|
this.highlightEl; // eslint-disable-line no-unused-expressions
|
|
@@ -40,7 +38,6 @@ export class TopbarFeatures {
|
|
|
40
38
|
this.onKeyUp = this.onKeyUp.bind(this);
|
|
41
39
|
this.onCopy = this.onCopy.bind(this);
|
|
42
40
|
this.collapseAllDetails = this.collapseAllDetails.bind(this);
|
|
43
|
-
this._updateStickyHeaderOnScroll = this._updateStickyHeaderOnScroll.bind(this);
|
|
44
41
|
}
|
|
45
42
|
|
|
46
43
|
/**
|
|
@@ -48,33 +45,15 @@ export class TopbarFeatures {
|
|
|
48
45
|
*/
|
|
49
46
|
enable(lhr) {
|
|
50
47
|
this.lhr = lhr;
|
|
51
|
-
this.
|
|
52
|
-
this.
|
|
48
|
+
this._dom.rootEl.addEventListener('keyup', this.onKeyUp);
|
|
49
|
+
this._dom.document().addEventListener('copy', this.onCopy);
|
|
53
50
|
this._dropDownMenu.setup(this.onDropDownMenuClick);
|
|
54
51
|
this._setUpCollapseDetailsAfterPrinting();
|
|
55
52
|
|
|
56
|
-
const topbarLogo = this._dom.find('.lh-topbar__logo', this.
|
|
53
|
+
const topbarLogo = this._dom.find('.lh-topbar__logo', this._dom.rootEl);
|
|
57
54
|
topbarLogo.addEventListener('click', () => toggleDarkTheme(this._dom));
|
|
58
55
|
|
|
59
|
-
|
|
60
|
-
if (Object.keys(this.lhr.categories).length >= 2) {
|
|
61
|
-
this._setupStickyHeaderElements();
|
|
62
|
-
const containerEl = this._dom.find('.lh-container', this._document);
|
|
63
|
-
const elToAddScrollListener = this._getScrollParent(containerEl);
|
|
64
|
-
elToAddScrollListener.addEventListener('scroll', this._updateStickyHeaderOnScroll);
|
|
65
|
-
|
|
66
|
-
// Use ResizeObserver where available.
|
|
67
|
-
// TODO: there is an issue with incorrect position numbers and, as a result, performance
|
|
68
|
-
// issues due to layout thrashing.
|
|
69
|
-
// See https://github.com/GoogleChrome/lighthouse/pull/9023/files#r288822287 for details.
|
|
70
|
-
// For now, limit to DevTools.
|
|
71
|
-
if (this._dom.isDevTools()) {
|
|
72
|
-
const resizeObserver = new window.ResizeObserver(this._updateStickyHeaderOnScroll);
|
|
73
|
-
resizeObserver.observe(containerEl);
|
|
74
|
-
} else {
|
|
75
|
-
window.addEventListener('resize', this._updateStickyHeaderOnScroll);
|
|
76
|
-
}
|
|
77
|
-
}
|
|
56
|
+
this._setupStickyHeader();
|
|
78
57
|
}
|
|
79
58
|
|
|
80
59
|
/**
|
|
@@ -112,7 +91,7 @@ export class TopbarFeatures {
|
|
|
112
91
|
try {
|
|
113
92
|
this._reportUIFeatures._saveFile(new Blob([htmlStr], {type: 'text/html'}));
|
|
114
93
|
} catch (e) {
|
|
115
|
-
this._dom.fireEventOn('lh-log', this.
|
|
94
|
+
this._dom.fireEventOn('lh-log', this._dom.document(), {
|
|
116
95
|
cmd: 'error', msg: 'Could not export as HTML. ' + e.message,
|
|
117
96
|
});
|
|
118
97
|
}
|
|
@@ -152,7 +131,7 @@ export class TopbarFeatures {
|
|
|
152
131
|
e.preventDefault();
|
|
153
132
|
e.clipboardData.setData('text/plain', JSON.stringify(this.lhr, null, 2));
|
|
154
133
|
|
|
155
|
-
this._dom.fireEventOn('lh-log', this.
|
|
134
|
+
this._dom.fireEventOn('lh-log', this._dom.document(), {
|
|
156
135
|
cmd: 'log', msg: 'Report JSON copied to clipboard',
|
|
157
136
|
});
|
|
158
137
|
}
|
|
@@ -164,28 +143,28 @@ export class TopbarFeatures {
|
|
|
164
143
|
* Copies the report JSON to the clipboard (if supported by the browser).
|
|
165
144
|
*/
|
|
166
145
|
onCopyButtonClick() {
|
|
167
|
-
this._dom.fireEventOn('lh-analytics', this.
|
|
146
|
+
this._dom.fireEventOn('lh-analytics', this._dom.document(), {
|
|
168
147
|
cmd: 'send',
|
|
169
148
|
fields: {hitType: 'event', eventCategory: 'report', eventAction: 'copy'},
|
|
170
149
|
});
|
|
171
150
|
|
|
172
151
|
try {
|
|
173
|
-
if (this.
|
|
152
|
+
if (this._dom.document().queryCommandSupported('copy')) {
|
|
174
153
|
this._copyAttempt = true;
|
|
175
154
|
|
|
176
155
|
// Note: In Safari 10.0.1, execCommand('copy') returns true if there's
|
|
177
156
|
// a valid text selection on the page. See http://caniuse.com/#feat=clipboard.
|
|
178
|
-
if (!this.
|
|
157
|
+
if (!this._dom.document().execCommand('copy')) {
|
|
179
158
|
this._copyAttempt = false; // Prevent event handler from seeing this as a copy attempt.
|
|
180
159
|
|
|
181
|
-
this._dom.fireEventOn('lh-log', this.
|
|
160
|
+
this._dom.fireEventOn('lh-log', this._dom.document(), {
|
|
182
161
|
cmd: 'warn', msg: 'Your browser does not support copy to clipboard.',
|
|
183
162
|
});
|
|
184
163
|
}
|
|
185
164
|
}
|
|
186
165
|
} catch (e) {
|
|
187
166
|
this._copyAttempt = false;
|
|
188
|
-
this._dom.fireEventOn('lh-log', this.
|
|
167
|
+
this._dom.fireEventOn('lh-log', this._dom.document(), {cmd: 'log', msg: e.message});
|
|
189
168
|
}
|
|
190
169
|
}
|
|
191
170
|
|
|
@@ -206,7 +185,7 @@ export class TopbarFeatures {
|
|
|
206
185
|
* open a `<details>` element.
|
|
207
186
|
*/
|
|
208
187
|
expandAllDetails() {
|
|
209
|
-
const details = this._dom.findAll('.lh-categories details', this.
|
|
188
|
+
const details = this._dom.findAll('.lh-categories details', this._dom.rootEl);
|
|
210
189
|
details.map(detail => detail.open = true);
|
|
211
190
|
}
|
|
212
191
|
|
|
@@ -215,7 +194,7 @@ export class TopbarFeatures {
|
|
|
215
194
|
* open a `<details>` element.
|
|
216
195
|
*/
|
|
217
196
|
collapseAllDetails() {
|
|
218
|
-
const details = this._dom.findAll('.lh-categories details', this.
|
|
197
|
+
const details = this._dom.findAll('.lh-categories details', this._dom.rootEl);
|
|
219
198
|
details.map(detail => detail.open = false);
|
|
220
199
|
}
|
|
221
200
|
|
|
@@ -235,7 +214,7 @@ export class TopbarFeatures {
|
|
|
235
214
|
/**
|
|
236
215
|
* Finds the first scrollable ancestor of `element`. Falls back to the document.
|
|
237
216
|
* @param {Element} element
|
|
238
|
-
* @return {
|
|
217
|
+
* @return {Element | Document}
|
|
239
218
|
*/
|
|
240
219
|
_getScrollParent(element) {
|
|
241
220
|
const {overflowY} = window.getComputedStyle(element);
|
|
@@ -273,24 +252,49 @@ export class TopbarFeatures {
|
|
|
273
252
|
}
|
|
274
253
|
}
|
|
275
254
|
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
this.
|
|
279
|
-
this.
|
|
255
|
+
_setupStickyHeader() {
|
|
256
|
+
// Cache these elements to avoid qSA on each onscroll.
|
|
257
|
+
this.topbarEl = this._dom.find('div.lh-topbar', this._dom.rootEl);
|
|
258
|
+
this.categoriesEl = this._dom.find('div.lh-categories', this._dom.rootEl);
|
|
259
|
+
|
|
260
|
+
// Defer behind rAF to avoid forcing layout.
|
|
261
|
+
window.requestAnimationFrame(() => window.requestAnimationFrame(() => {
|
|
262
|
+
// Only present in the DOM if it'll be used (>=2 categories)
|
|
263
|
+
try {
|
|
264
|
+
this.stickyHeaderEl = this._dom.find('div.lh-sticky-header', this._dom.rootEl);
|
|
265
|
+
} catch {
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
280
268
|
|
|
281
|
-
|
|
282
|
-
|
|
269
|
+
// Highlighter will be absolutely positioned at first gauge, then transformed on scroll.
|
|
270
|
+
this.highlightEl = this._dom.createChildOf(this.stickyHeaderEl, 'div', 'lh-highlighter');
|
|
271
|
+
|
|
272
|
+
// Update sticky header visibility and highlight when page scrolls/resizes.
|
|
273
|
+
const scrollParent = this._getScrollParent(this._dom.rootEl);
|
|
274
|
+
// The 'scroll' handler must be should be on {Element | Document}...
|
|
275
|
+
scrollParent.addEventListener('scroll', () => this._updateStickyHeader());
|
|
276
|
+
// However resizeObserver needs an element, *not* the document.
|
|
277
|
+
const resizeTarget = scrollParent instanceof window.Document
|
|
278
|
+
? document.documentElement
|
|
279
|
+
: scrollParent;
|
|
280
|
+
new window.ResizeObserver(() => this._updateStickyHeader()).observe(resizeTarget);
|
|
281
|
+
}));
|
|
283
282
|
}
|
|
284
283
|
|
|
285
|
-
|
|
286
|
-
|
|
284
|
+
/**
|
|
285
|
+
* Toggle visibility and update highlighter position
|
|
286
|
+
*/
|
|
287
|
+
_updateStickyHeader() {
|
|
288
|
+
if (!this.stickyHeaderEl) return;
|
|
289
|
+
|
|
290
|
+
// Show sticky header when the main 5 gauges clear the topbar.
|
|
287
291
|
const topbarBottom = this.topbarEl.getBoundingClientRect().bottom;
|
|
288
|
-
const
|
|
289
|
-
const showStickyHeader = topbarBottom >=
|
|
292
|
+
const categoriesTop = this.categoriesEl.getBoundingClientRect().top;
|
|
293
|
+
const showStickyHeader = topbarBottom >= categoriesTop;
|
|
290
294
|
|
|
291
295
|
// Highlight mini gauge when section is in view.
|
|
292
296
|
// In view = the last category that starts above the middle of the window.
|
|
293
|
-
const categoryEls = Array.from(this.
|
|
297
|
+
const categoryEls = Array.from(this._dom.rootEl.querySelectorAll('.lh-category'));
|
|
294
298
|
const categoriesAboveTheMiddle =
|
|
295
299
|
categoryEls.filter(el => el.getBoundingClientRect().top - window.innerHeight / 2 < 0);
|
|
296
300
|
const highlightIndex =
|
package/report/renderer/util.js
CHANGED
|
@@ -38,7 +38,11 @@ const listOfTlds = [
|
|
|
38
38
|
'web', 'spb', 'blog', 'jus', 'kiev', 'mil', 'wi', 'qc', 'ca', 'bel', 'on',
|
|
39
39
|
];
|
|
40
40
|
|
|
41
|
-
|
|
41
|
+
class Util {
|
|
42
|
+
/** @type {I18n<typeof UIStrings>} */
|
|
43
|
+
// @ts-expect-error: Is set in report renderer.
|
|
44
|
+
static i18n = null;
|
|
45
|
+
|
|
42
46
|
static get PASS_THRESHOLD() {
|
|
43
47
|
return PASS_THRESHOLD;
|
|
44
48
|
}
|
|
@@ -417,7 +421,7 @@ export class Util {
|
|
|
417
421
|
throttling.downloadThroughputKbps === 1.6 * 1024 * 0.9 &&
|
|
418
422
|
throttling.uploadThroughputKbps === 750 * 0.9;
|
|
419
423
|
};
|
|
420
|
-
summary =
|
|
424
|
+
summary = isSlow4G() ? Util.i18n.strings.runtimeSlow4g : Util.i18n.strings.runtimeCustom;
|
|
421
425
|
break;
|
|
422
426
|
}
|
|
423
427
|
case 'simulate': {
|
|
@@ -429,7 +433,7 @@ export class Util {
|
|
|
429
433
|
const isSlow4G = () => {
|
|
430
434
|
return rttMs === 150 && throughputKbps === 1.6 * 1024;
|
|
431
435
|
};
|
|
432
|
-
summary = isSlow4G() ?
|
|
436
|
+
summary = isSlow4G() ? Util.i18n.strings.runtimeSlow4g : Util.i18n.strings.runtimeCustom;
|
|
433
437
|
break;
|
|
434
438
|
}
|
|
435
439
|
default:
|
|
@@ -516,10 +520,9 @@ export class Util {
|
|
|
516
520
|
let totalWeight = 0;
|
|
517
521
|
for (const auditRef of category.auditRefs) {
|
|
518
522
|
const auditPassed = Util.showAsPassed(auditRef.result);
|
|
519
|
-
const notDisplayed = !auditRef.group && category.id === 'performance';
|
|
520
523
|
|
|
521
524
|
// Don't count the audit if it's manual, N/A, or isn't displayed.
|
|
522
|
-
if (
|
|
525
|
+
if (auditRef.group === 'hidden' ||
|
|
523
526
|
auditRef.result.scoreDisplayMode === 'manual' ||
|
|
524
527
|
auditRef.result.scoreDisplayMode === 'notApplicable') {
|
|
525
528
|
continue;
|
|
@@ -555,14 +558,10 @@ Util.getUniqueSuffix = (() => {
|
|
|
555
558
|
};
|
|
556
559
|
})();
|
|
557
560
|
|
|
558
|
-
/** @type {I18n<typeof Util['UIStrings']>} */
|
|
559
|
-
// @ts-expect-error: Is set in report renderer.
|
|
560
|
-
Util.i18n = null;
|
|
561
|
-
|
|
562
561
|
/**
|
|
563
562
|
* Report-renderer-specific strings.
|
|
564
563
|
*/
|
|
565
|
-
|
|
564
|
+
const UIStrings = {
|
|
566
565
|
/** Disclaimer shown to users below the metric values (First Contentful Paint, Time to Interactive, etc) to warn them that the numbers they see will likely change slightly the next time they run Lighthouse. */
|
|
567
566
|
varianceDisclaimer: 'Values are estimated and may vary. The [performance score is calculated](https://web.dev/performance-scoring/) directly from these metrics.',
|
|
568
567
|
/** Text link pointing to an interactive calculator that explains Lighthouse scoring. The link text should be fairly short. */
|
|
@@ -662,6 +661,22 @@ Util.UIStrings = {
|
|
|
662
661
|
|
|
663
662
|
/** Descriptive explanation for environment throttling that was provided by the runtime environment instead of provided by Lighthouse throttling. */
|
|
664
663
|
throttlingProvided: 'Provided by environment',
|
|
664
|
+
/** Label for an interactive control that will reveal or hide a group of content. This control toggles between the text 'Show' and 'Hide'. */
|
|
665
|
+
show: 'Show',
|
|
666
|
+
/** Label for an interactive control that will reveal or hide a group of content. This control toggles between the text 'Show' and 'Hide'. */
|
|
667
|
+
hide: 'Hide',
|
|
668
|
+
/** Label for an interactive control that will reveal or hide a group of content. This control toggles between the text 'Expand view' and 'Collapse view'. */
|
|
669
|
+
expandView: 'Expand view',
|
|
670
|
+
/** Label for an interactive control that will reveal or hide a group of content. This control toggles between the text 'Expand view' and 'Collapse view'. */
|
|
671
|
+
collapseView: 'Collapse view',
|
|
672
|
+
/** Label indicating that Lighthouse throttled the page to emulate a slow 4G network connection. */
|
|
673
|
+
runtimeSlow4g: 'Slow 4G throttling',
|
|
674
|
+
/** Label indicating that Lighthouse throttled the page using custom throttling settings. */
|
|
675
|
+
runtimeCustom: 'Custom throttling',
|
|
665
676
|
};
|
|
677
|
+
Util.UIStrings = UIStrings;
|
|
666
678
|
|
|
667
|
-
export
|
|
679
|
+
export {
|
|
680
|
+
Util,
|
|
681
|
+
UIStrings,
|
|
682
|
+
};
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
import fs from 'fs';
|
|
10
10
|
|
|
11
11
|
import jsdom from 'jsdom';
|
|
12
|
+
import {jest} from '@jest/globals';
|
|
12
13
|
|
|
13
14
|
import * as lighthouseRenderer from '../../clients/bundle.js';
|
|
14
15
|
import {LH_ROOT} from '../../../root.js';
|
|
@@ -22,16 +23,23 @@ const sampleResultsStr =
|
|
|
22
23
|
describe('lighthouseRenderer bundle', () => {
|
|
23
24
|
let document;
|
|
24
25
|
beforeAll(() => {
|
|
26
|
+
global.console.warn = jest.fn();
|
|
27
|
+
|
|
25
28
|
const {window} = new jsdom.JSDOM();
|
|
26
29
|
document = window.document;
|
|
27
30
|
|
|
28
31
|
global.window = global.self = window;
|
|
32
|
+
global.window.requestAnimationFrame = fn => fn();
|
|
29
33
|
// Stub out matchMedia for Node.
|
|
30
34
|
global.self.matchMedia = function() {
|
|
31
35
|
return {
|
|
32
36
|
addListener: function() {},
|
|
33
37
|
};
|
|
34
38
|
};
|
|
39
|
+
global.window.ResizeObserver = class ResizeObserver {
|
|
40
|
+
observe() { }
|
|
41
|
+
unobserve() { }
|
|
42
|
+
};
|
|
35
43
|
});
|
|
36
44
|
|
|
37
45
|
afterAll(() => {
|
|
@@ -197,8 +197,8 @@ describe('CategoryRenderer', () => {
|
|
|
197
197
|
'score shows informative and dash icon');
|
|
198
198
|
|
|
199
199
|
assert.ok(pwaCategory.manualDescription);
|
|
200
|
-
const description = categoryDOM
|
|
201
|
-
.querySelector('.lh-
|
|
200
|
+
const description = categoryDOM.querySelector('.lh-clump--manual').closest('.lh-audit-group')
|
|
201
|
+
.querySelector('.lh-audit-group__description').textContent;
|
|
202
202
|
// may need to be adjusted if description includes a link at the beginning
|
|
203
203
|
assert.ok(description.startsWith(pwaCategory.manualDescription.substring(0, 20)),
|
|
204
204
|
'no manual description');
|
|
@@ -96,11 +96,23 @@ describe('PerfCategoryRenderer', () => {
|
|
|
96
96
|
assert.strictEqual(new URL(calcLink.href).hostname, 'googlechrome.github.io');
|
|
97
97
|
});
|
|
98
98
|
|
|
99
|
+
it('ignores hidden audits', () => {
|
|
100
|
+
const categoryDOM = renderer.render(category, sampleResults.categoryGroups);
|
|
101
|
+
|
|
102
|
+
const hiddenAudits = category.auditRefs.filter(audit => audit.group === 'hidden');
|
|
103
|
+
const auditElements = [...categoryDOM.querySelectorAll('.lh-audit')];
|
|
104
|
+
const matchingElements = auditElements
|
|
105
|
+
.filter(el => hiddenAudits.some(audit => audit.id === el.id));
|
|
106
|
+
expect(matchingElements).toHaveLength(0);
|
|
107
|
+
});
|
|
108
|
+
|
|
99
109
|
it('renders the failing performance opportunities', () => {
|
|
100
110
|
const categoryDOM = renderer.render(category, sampleResults.categoryGroups);
|
|
101
111
|
|
|
102
|
-
const oppAudits = category.auditRefs.filter(audit =>
|
|
103
|
-
|
|
112
|
+
const oppAudits = category.auditRefs.filter(audit =>
|
|
113
|
+
audit.result.details &&
|
|
114
|
+
audit.result.details.type === 'opportunity' &&
|
|
115
|
+
!Util.showAsPassed(audit.result));
|
|
104
116
|
const oppElements = [...categoryDOM.querySelectorAll('.lh-audit--load-opportunity')];
|
|
105
117
|
expect(oppElements.map(e => e.id).sort()).toEqual(oppAudits.map(a => a.id).sort());
|
|
106
118
|
expect(oppElements.length).toBeGreaterThan(0);
|
|
@@ -120,10 +132,14 @@ describe('PerfCategoryRenderer', () => {
|
|
|
120
132
|
it('renders performance opportunities with an errorMessage', () => {
|
|
121
133
|
const auditWithError = {
|
|
122
134
|
score: 0,
|
|
123
|
-
group: 'load-opportunities',
|
|
124
135
|
result: {
|
|
125
136
|
score: null, scoreDisplayMode: 'error', errorMessage: 'Yikes!!', title: 'Bug #2',
|
|
126
137
|
description: '',
|
|
138
|
+
details: {
|
|
139
|
+
overallSavingsMs: 0,
|
|
140
|
+
items: [],
|
|
141
|
+
type: 'opportunity',
|
|
142
|
+
},
|
|
127
143
|
},
|
|
128
144
|
};
|
|
129
145
|
|
|
@@ -137,10 +153,14 @@ describe('PerfCategoryRenderer', () => {
|
|
|
137
153
|
it('renders performance opportunities\' explanation', () => {
|
|
138
154
|
const auditWithExplanation = {
|
|
139
155
|
score: 0,
|
|
140
|
-
group: 'load-opportunities',
|
|
141
156
|
result: {
|
|
142
157
|
score: 0, scoreDisplayMode: 'numeric',
|
|
143
158
|
numericValue: 100, explanation: 'Yikes!!', title: 'Bug #2', description: '',
|
|
159
|
+
details: {
|
|
160
|
+
overallSavingsMs: 0,
|
|
161
|
+
items: [],
|
|
162
|
+
type: 'opportunity',
|
|
163
|
+
},
|
|
144
164
|
},
|
|
145
165
|
};
|
|
146
166
|
|
|
@@ -159,7 +179,9 @@ describe('PerfCategoryRenderer', () => {
|
|
|
159
179
|
'.lh-category > .lh-audit-group.lh-audit-group--diagnostics');
|
|
160
180
|
|
|
161
181
|
const diagnosticAuditIds = category.auditRefs.filter(audit => {
|
|
162
|
-
return audit.group
|
|
182
|
+
return !audit.group &&
|
|
183
|
+
!(audit.result.details && audit.result.details.type === 'opportunity') &&
|
|
184
|
+
!Util.showAsPassed(audit.result);
|
|
163
185
|
}).map(audit => audit.id).sort();
|
|
164
186
|
assert.ok(diagnosticAuditIds.length > 0);
|
|
165
187
|
|
|
@@ -170,11 +192,11 @@ describe('PerfCategoryRenderer', () => {
|
|
|
170
192
|
|
|
171
193
|
it('renders the passed audits', () => {
|
|
172
194
|
const categoryDOM = renderer.render(category, sampleResults.categoryGroups);
|
|
173
|
-
const passedSection = categoryDOM.querySelector('.lh-
|
|
195
|
+
const passedSection = categoryDOM.querySelector('.lh-clump--passed');
|
|
174
196
|
|
|
175
197
|
const passedAudits = category.auditRefs.filter(audit =>
|
|
176
|
-
audit.group &&
|
|
177
|
-
|
|
198
|
+
!audit.group &&
|
|
199
|
+
Util.showAsPassed(audit.result));
|
|
178
200
|
const passedElements = passedSection.querySelectorAll('.lh-audit');
|
|
179
201
|
assert.equal(passedElements.length, passedAudits.length);
|
|
180
202
|
});
|
|
@@ -187,10 +209,14 @@ describe('PerfCategoryRenderer', () => {
|
|
|
187
209
|
it('handles erroring opportunities', () => {
|
|
188
210
|
const auditWithDebug = {
|
|
189
211
|
score: 0,
|
|
190
|
-
group: 'load-opportunities',
|
|
191
212
|
result: {
|
|
192
213
|
error: true, score: 0,
|
|
193
214
|
numericValue: 100, explanation: 'Yikes!!', title: 'Bug #2',
|
|
215
|
+
details: {
|
|
216
|
+
overallSavingsMs: 0,
|
|
217
|
+
items: [],
|
|
218
|
+
type: 'opportunity',
|
|
219
|
+
},
|
|
194
220
|
},
|
|
195
221
|
};
|
|
196
222
|
const wastedMs = renderer._getWastedMs(auditWithDebug);
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
/* eslint-env jest */
|
|
9
9
|
|
|
10
10
|
import jsdom from 'jsdom';
|
|
11
|
+
import {jest} from '@jest/globals';
|
|
11
12
|
|
|
12
13
|
import {Util} from '../../renderer/util.js';
|
|
13
14
|
import {DOM} from '../../renderer/dom.js';
|
|
@@ -23,6 +24,8 @@ describe('ReportRendererAxe', () => {
|
|
|
23
24
|
let sampleResults;
|
|
24
25
|
|
|
25
26
|
beforeAll(async () => {
|
|
27
|
+
global.console.warn = jest.fn();
|
|
28
|
+
|
|
26
29
|
const {window} = new jsdom.JSDOM();
|
|
27
30
|
const dom = new DOM(window.document);
|
|
28
31
|
const detailsRenderer = new DetailsRenderer(dom);
|
|
@@ -47,10 +50,10 @@ describe('ReportRendererAxe', () => {
|
|
|
47
50
|
});
|
|
48
51
|
|
|
49
52
|
it('renders without axe violations', async () => {
|
|
50
|
-
const container = renderer._dom.
|
|
53
|
+
const container = renderer._dom.document().createElement('main');
|
|
51
54
|
const output = renderer.renderReport(sampleResults, container);
|
|
52
55
|
|
|
53
|
-
renderer._dom.
|
|
56
|
+
renderer._dom.document().body.appendChild(container);
|
|
54
57
|
|
|
55
58
|
const config = {
|
|
56
59
|
rules: {
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
import {strict as assert} from 'assert';
|
|
11
11
|
|
|
12
12
|
import jsdom from 'jsdom';
|
|
13
|
+
import {jest} from '@jest/globals';
|
|
13
14
|
|
|
14
15
|
import {Util} from '../../renderer/util.js';
|
|
15
16
|
import URL from '../../../lighthouse-core/lib/url-shim.js';
|
|
@@ -26,6 +27,8 @@ describe('ReportRenderer', () => {
|
|
|
26
27
|
let sampleResults;
|
|
27
28
|
|
|
28
29
|
beforeAll(() => {
|
|
30
|
+
global.console.warn = jest.fn();
|
|
31
|
+
|
|
29
32
|
// Stub out matchMedia for Node.
|
|
30
33
|
global.matchMedia = function() {
|
|
31
34
|
return {
|
|
@@ -50,7 +53,7 @@ describe('ReportRenderer', () => {
|
|
|
50
53
|
|
|
51
54
|
describe('renderReport', () => {
|
|
52
55
|
it('should render a report', () => {
|
|
53
|
-
const container = renderer._dom.
|
|
56
|
+
const container = renderer._dom.document().body;
|
|
54
57
|
const output = renderer.renderReport(sampleResults, container);
|
|
55
58
|
assert.ok(output.querySelector('.lh-header-container'), 'has a header');
|
|
56
59
|
assert.ok(output.querySelector('.lh-report'), 'has report body');
|
|
@@ -60,7 +63,7 @@ describe('ReportRenderer', () => {
|
|
|
60
63
|
});
|
|
61
64
|
|
|
62
65
|
it('renders additional reports by replacing the existing one', () => {
|
|
63
|
-
const container = renderer._dom.
|
|
66
|
+
const container = renderer._dom.document().body;
|
|
64
67
|
const oldReport = Array.from(renderer.renderReport(sampleResults, container).children);
|
|
65
68
|
const newReport = Array.from(renderer.renderReport(sampleResults, container).children);
|
|
66
69
|
assert.ok(!oldReport.find(node => container.contains(node)), 'old report was removed');
|
|
@@ -86,11 +89,11 @@ describe('ReportRenderer', () => {
|
|
|
86
89
|
auditRefs: [],
|
|
87
90
|
};
|
|
88
91
|
|
|
89
|
-
const container = renderer._dom.
|
|
92
|
+
const container = renderer._dom.document().body;
|
|
90
93
|
const output = renderer.renderReport(sampleResultsCopy, container);
|
|
91
94
|
|
|
92
95
|
function isPWAGauge(el) {
|
|
93
|
-
return el.querySelector('.lh-gauge__label').textContent === '
|
|
96
|
+
return el.querySelector('.lh-gauge__label').textContent === 'PWA';
|
|
94
97
|
}
|
|
95
98
|
function isPluginGauge(el) {
|
|
96
99
|
return el.querySelector('.lh-gauge__label').textContent === 'Some Plugin';
|
|
@@ -126,7 +129,7 @@ describe('ReportRenderer', () => {
|
|
|
126
129
|
title: 'Some Plugin',
|
|
127
130
|
auditRefs: [],
|
|
128
131
|
};
|
|
129
|
-
const container = renderer._dom.
|
|
132
|
+
const container = renderer._dom.document().body;
|
|
130
133
|
const output = renderer.renderReport(sampleResultsCopy, container);
|
|
131
134
|
const scoresHeaderElem = output.querySelector('.lh-scores-header');
|
|
132
135
|
|
|
@@ -140,7 +143,7 @@ describe('ReportRenderer', () => {
|
|
|
140
143
|
});
|
|
141
144
|
|
|
142
145
|
it('should not mutate a report object', () => {
|
|
143
|
-
const container = renderer._dom.
|
|
146
|
+
const container = renderer._dom.document().body;
|
|
144
147
|
const originalResults = JSON.parse(JSON.stringify(sampleResults));
|
|
145
148
|
renderer.renderReport(sampleResults, container);
|
|
146
149
|
assert.deepStrictEqual(sampleResults, originalResults);
|
|
@@ -148,13 +151,13 @@ describe('ReportRenderer', () => {
|
|
|
148
151
|
|
|
149
152
|
it('renders no warning section when no lighthouseRunWarnings occur', () => {
|
|
150
153
|
const warningResults = Object.assign({}, sampleResults, {runWarnings: []});
|
|
151
|
-
const container = renderer._dom.
|
|
154
|
+
const container = renderer._dom.document().body;
|
|
152
155
|
const output = renderer.renderReport(warningResults, container);
|
|
153
156
|
assert.strictEqual(output.querySelector('.lh-warnings--toplevel'), null);
|
|
154
157
|
});
|
|
155
158
|
|
|
156
159
|
it('renders a warning section', () => {
|
|
157
|
-
const container = renderer._dom.
|
|
160
|
+
const container = renderer._dom.document().body;
|
|
158
161
|
const output = renderer.renderReport(sampleResults, container);
|
|
159
162
|
|
|
160
163
|
const warningEls = output.querySelectorAll('.lh-warnings--toplevel > ul > li');
|
|
@@ -165,7 +168,7 @@ describe('ReportRenderer', () => {
|
|
|
165
168
|
const warningResults = Object.assign({}, sampleResults, {
|
|
166
169
|
runWarnings: ['[I am a link](https://example.com/)'],
|
|
167
170
|
});
|
|
168
|
-
const container = renderer._dom.
|
|
171
|
+
const container = renderer._dom.document().body;
|
|
169
172
|
const output = renderer.renderReport(warningResults, container);
|
|
170
173
|
|
|
171
174
|
const warningEls = output.querySelectorAll('.lh-warnings--toplevel ul li a');
|
|
@@ -196,7 +199,7 @@ describe('ReportRenderer', () => {
|
|
|
196
199
|
// Make sure we have a channel in the LHR.
|
|
197
200
|
assert.ok(lhrChannel.length > 2);
|
|
198
201
|
|
|
199
|
-
const container = renderer._dom.
|
|
202
|
+
const container = renderer._dom.document().body;
|
|
200
203
|
const output = renderer.renderReport(sampleResults, container);
|
|
201
204
|
|
|
202
205
|
const DOCS_ORIGINS = ['https://developers.google.com', 'https://web.dev'];
|
|
@@ -225,7 +228,7 @@ describe('ReportRenderer', () => {
|
|
|
225
228
|
|
|
226
229
|
assert.ok(notApplicableCount > 20); // Make sure something's being tested.
|
|
227
230
|
|
|
228
|
-
const container = renderer._dom.
|
|
231
|
+
const container = renderer._dom.document().body;
|
|
229
232
|
const reportElement = renderer.renderReport(sampleResults, container);
|
|
230
233
|
const notApplicableElementCount = reportElement
|
|
231
234
|
.querySelectorAll('.lh-audit--notapplicable').length;
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
import {strict as assert} from 'assert';
|
|
11
11
|
|
|
12
12
|
import jsdom from 'jsdom';
|
|
13
|
+
import {jest} from '@jest/globals';
|
|
13
14
|
|
|
14
15
|
import reportAssets from '../../generator/report-assets.js';
|
|
15
16
|
import {Util} from '../../renderer/util.js';
|
|
@@ -33,13 +34,15 @@ describe('ReportUIFeatures', () => {
|
|
|
33
34
|
const categoryRenderer = new CategoryRenderer(dom, detailsRenderer);
|
|
34
35
|
const renderer = new ReportRenderer(dom, categoryRenderer);
|
|
35
36
|
const reportUIFeatures = new ReportUIFeatures(dom);
|
|
36
|
-
const container = dom.find('
|
|
37
|
+
const container = dom.find('body', dom.document());
|
|
37
38
|
renderer.renderReport(lhr, container);
|
|
38
39
|
reportUIFeatures.initFeatures(lhr);
|
|
39
40
|
return container;
|
|
40
41
|
}
|
|
41
42
|
|
|
42
43
|
beforeAll(() => {
|
|
44
|
+
global.console.warn = jest.fn();
|
|
45
|
+
|
|
43
46
|
// Stub out matchMedia for Node.
|
|
44
47
|
global.matchMedia = function() {
|
|
45
48
|
return {
|
|
@@ -57,14 +60,20 @@ describe('ReportUIFeatures', () => {
|
|
|
57
60
|
|
|
58
61
|
global.HTMLElement = document.window.HTMLElement;
|
|
59
62
|
global.HTMLInputElement = document.window.HTMLInputElement;
|
|
63
|
+
global.HTMLInputElement = document.window.HTMLInputElement;
|
|
60
64
|
|
|
61
65
|
global.window = document.window;
|
|
66
|
+
global.window.requestAnimationFrame = fn => fn();
|
|
62
67
|
global.window.getComputedStyle = function() {
|
|
63
68
|
return {
|
|
64
69
|
marginTop: '10px',
|
|
65
70
|
height: '10px',
|
|
66
71
|
};
|
|
67
72
|
};
|
|
73
|
+
global.window.ResizeObserver = class ResizeObserver {
|
|
74
|
+
observe() { }
|
|
75
|
+
unobserve() { }
|
|
76
|
+
};
|
|
68
77
|
|
|
69
78
|
dom = new DOM(document.window.document);
|
|
70
79
|
sampleResults = Util.prepareReportResult(sampleResultsOrig);
|
|
@@ -256,17 +265,15 @@ describe('ReportUIFeatures', () => {
|
|
|
256
265
|
.toThrowError('query #uses-rel-preconnect .lh-3p-filter-input not found');
|
|
257
266
|
});
|
|
258
267
|
|
|
259
|
-
it('filter is
|
|
260
|
-
const
|
|
261
|
-
dom.find('#render-blocking-resources .lh-3p-filter
|
|
262
|
-
expect(
|
|
263
|
-
expect(filterCheckbox.checked).toEqual(true);
|
|
268
|
+
it('filter is hidden when just third party resources', () => {
|
|
269
|
+
const filterControl =
|
|
270
|
+
dom.find('#render-blocking-resources .lh-3p-filter', container);
|
|
271
|
+
expect(filterControl.hidden).toEqual(true);
|
|
264
272
|
});
|
|
265
273
|
|
|
266
|
-
it('filter is
|
|
267
|
-
const
|
|
268
|
-
expect(
|
|
269
|
-
expect(filterCheckbox.checked).toEqual(false);
|
|
274
|
+
it('filter is hidden for just first party resources', () => {
|
|
275
|
+
const filterControl = dom.find('#uses-text-compression .lh-3p-filter', container);
|
|
276
|
+
expect(filterControl.hidden).toEqual(true);
|
|
270
277
|
});
|
|
271
278
|
});
|
|
272
279
|
});
|
|
@@ -515,6 +522,9 @@ describe('ReportUIFeatures', () => {
|
|
|
515
522
|
expect(render(lhr).querySelector('.lh-button.lh-report-icon--treemap')).toBeTruthy();
|
|
516
523
|
|
|
517
524
|
delete lhr.audits['script-treemap-data'];
|
|
525
|
+
const newAuditRefs = lhr.categories['performance'].auditRefs
|
|
526
|
+
.filter(a => a.id !== 'script-treemap-data');
|
|
527
|
+
lhr.categories['performance'].auditRefs = newAuditRefs;
|
|
518
528
|
expect(render(lhr).querySelector('.lh-button.lh-report-icon--treemap')).toBeNull();
|
|
519
529
|
});
|
|
520
530
|
});
|