lighthouse 9.5.0-dev.20230122 → 9.5.0-dev.20230124
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/.codecov.yml +0 -5
- package/core/audits/audit.js +1 -1
- package/core/audits/bf-cache.js +1 -1
- package/core/audits/byte-efficiency/legacy-javascript.d.ts +1 -0
- package/core/audits/byte-efficiency/legacy-javascript.js +7 -3
- package/core/audits/dobetterweb/uses-http2.d.ts +5 -2
- package/core/audits/dobetterweb/uses-http2.js +12 -7
- package/core/audits/screenshot-thumbnails.d.ts +2 -1
- package/core/audits/screenshot-thumbnails.js +14 -9
- package/core/audits/third-party-facades.d.ts +3 -2
- package/core/audits/third-party-facades.js +12 -9
- package/core/audits/third-party-summary.d.ts +7 -6
- package/core/audits/third-party-summary.js +16 -14
- package/core/audits/valid-source-maps.d.ts +5 -3
- package/core/audits/valid-source-maps.js +11 -9
- package/core/computed/entity-classification.d.ts +29 -0
- package/core/computed/entity-classification.js +102 -0
- package/core/computed/processed-navigation.d.ts +12 -6
- package/core/computed/processed-navigation.js +19 -6
- package/core/computed/resource-summary.d.ts +4 -2
- package/core/computed/resource-summary.js +8 -5
- package/core/lib/url-utils.js +1 -1
- package/core/runner.d.ts +10 -0
- package/core/runner.js +48 -0
- package/core/util.cjs +43 -10
- package/core/util.d.cts +114 -2
- package/dist/report/bundle.esm.js +858 -883
- package/dist/report/flow.js +33 -23
- package/dist/report/standalone.js +27 -17
- package/flow-report/assets/standalone-flow-template.html +1 -1
- package/flow-report/src/common.tsx +1 -1
- package/flow-report/src/i18n/i18n.d.ts +103 -100
- package/flow-report/src/i18n/i18n.tsx +23 -18
- package/flow-report/src/sidebar/sidebar.tsx +3 -3
- package/flow-report/src/summary/category.tsx +7 -7
- package/flow-report/src/summary/summary.tsx +2 -2
- package/flow-report/src/topbar.tsx +4 -28
- package/package.json +5 -5
- package/readme.md +1 -1
- package/report/assets/standalone-template.html +1 -1
- package/report/assets/styles.css +3 -3
- package/report/assets/templates.html +4 -28
- package/report/renderer/category-renderer.js +19 -17
- package/report/renderer/components.js +119 -202
- package/report/renderer/crc-details-renderer.js +7 -6
- package/report/renderer/details-renderer.js +7 -6
- package/report/renderer/dom.js +1 -1
- package/report/renderer/element-screenshot-renderer.js +3 -3
- package/report/renderer/{i18n.d.ts → i18n-formatter.d.ts} +3 -9
- package/report/renderer/{i18n.js → i18n-formatter.js} +2 -11
- package/report/renderer/performance-category-renderer.js +17 -15
- package/report/renderer/pwa-category-renderer.js +5 -4
- package/report/renderer/report-globals.d.ts +21 -0
- package/report/renderer/report-globals.js +49 -0
- package/report/renderer/report-renderer.js +22 -24
- package/report/renderer/report-ui-features.js +9 -8
- package/report/renderer/report-utils.d.ts +116 -0
- package/report/renderer/{util.js → report-utils.js} +100 -415
- package/report/renderer/snippet-renderer.js +3 -2
- package/report/test/renderer/category-renderer-test.js +10 -5
- package/report/test/renderer/crc-details-renderer-test.js +8 -4
- package/report/test/renderer/details-renderer-test.js +8 -4
- package/report/test/renderer/dom-test.js +8 -4
- package/report/test/renderer/element-screenshot-renderer-test.js +9 -5
- package/report/test/renderer/{i18n-test.js → i18n-formatter-test.js} +15 -21
- package/report/test/renderer/performance-category-renderer-test.js +15 -10
- package/report/test/renderer/pwa-category-renderer-test.js +11 -6
- package/report/test/renderer/report-renderer-test.js +2 -2
- package/report/test/renderer/report-ui-features-test.js +3 -3
- package/report/test/renderer/{util-test.js → report-utils-test.js} +37 -235
- package/report/test/renderer/snippet-renderer-test.js +8 -4
- package/report/test-assets/lhr-3.0.0.json +2332 -0
- package/report/test-assets/lhr-4.3.0.json +4754 -0
- package/report/test-assets/lhr-5.0.0.json +5994 -0
- package/report/test-assets/lhr-6.0.0.json +7222 -0
- package/report/test-assets/lhr-8.5.0.json +8679 -0
- package/shared/localization/format.js +5 -2
- package/shared/localization/locales/ar-XB.json +50 -50
- package/shared/localization/locales/ar.json +50 -50
- package/shared/localization/locales/bg.json +50 -50
- package/shared/localization/locales/ca.json +50 -50
- package/shared/localization/locales/cs.json +50 -50
- package/shared/localization/locales/da.json +50 -50
- package/shared/localization/locales/de.json +50 -50
- package/shared/localization/locales/el.json +50 -50
- package/shared/localization/locales/en-GB.json +50 -50
- package/shared/localization/locales/en-US.json +55 -55
- package/shared/localization/locales/en-XA.json +50 -50
- package/shared/localization/locales/en-XL.json +55 -55
- package/shared/localization/locales/es-419.json +50 -50
- package/shared/localization/locales/es.json +50 -50
- package/shared/localization/locales/fi.json +50 -50
- package/shared/localization/locales/fil.json +50 -50
- package/shared/localization/locales/fr.json +50 -50
- package/shared/localization/locales/he.json +50 -50
- package/shared/localization/locales/hi.json +50 -50
- package/shared/localization/locales/hr.json +50 -50
- package/shared/localization/locales/hu.json +50 -50
- package/shared/localization/locales/id.json +50 -50
- package/shared/localization/locales/it.json +50 -50
- package/shared/localization/locales/ja.json +50 -50
- package/shared/localization/locales/ko.json +50 -50
- package/shared/localization/locales/lt.json +50 -50
- package/shared/localization/locales/lv.json +50 -50
- package/shared/localization/locales/nl.json +50 -50
- package/shared/localization/locales/no.json +50 -50
- package/shared/localization/locales/pl.json +50 -50
- package/shared/localization/locales/pt-PT.json +50 -50
- package/shared/localization/locales/pt.json +50 -50
- package/shared/localization/locales/ro.json +50 -50
- package/shared/localization/locales/ru.json +50 -50
- package/shared/localization/locales/sk.json +50 -50
- package/shared/localization/locales/sl.json +50 -50
- package/shared/localization/locales/sr-Latn.json +50 -50
- package/shared/localization/locales/sr.json +50 -50
- package/shared/localization/locales/sv.json +50 -50
- package/shared/localization/locales/ta.json +50 -50
- package/shared/localization/locales/te.json +50 -50
- package/shared/localization/locales/th.json +50 -50
- package/shared/localization/locales/tr.json +50 -50
- package/shared/localization/locales/uk.json +50 -50
- package/shared/localization/locales/vi.json +50 -50
- package/shared/localization/locales/zh-HK.json +50 -50
- package/shared/localization/locales/zh-TW.json +50 -50
- package/shared/localization/locales/zh.json +50 -50
- package/shared/test/util-test.js +214 -0
- package/shared/util.d.ts +122 -0
- package/shared/util.js +332 -0
- package/types/artifacts.d.ts +14 -0
- package/types/lhr/lhr.d.ts +33 -0
- package/report/renderer/util.d.ts +0 -230
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
* critical request chains network tree.
|
|
21
21
|
*/
|
|
22
22
|
|
|
23
|
-
import {
|
|
23
|
+
import {Globals} from './report-globals.js';
|
|
24
24
|
|
|
25
25
|
/** @typedef {import('./dom.js').DOM} DOM */
|
|
26
26
|
/** @typedef {import('./details-renderer.js').DetailsRenderer} DetailsRenderer */
|
|
@@ -136,9 +136,10 @@ class CriticalRequestChainRenderer {
|
|
|
136
136
|
if (!segment.hasChildren) {
|
|
137
137
|
const {startTime, endTime, transferSize} = segment.node.request;
|
|
138
138
|
const span = dom.createElement('span', 'lh-crc-node__chain-duration');
|
|
139
|
-
span.textContent =
|
|
139
|
+
span.textContent =
|
|
140
|
+
' - ' + Globals.i18n.formatMilliseconds((endTime - startTime) * 1000) + ', ';
|
|
140
141
|
const span2 = dom.createElement('span', 'lh-crc-node__chain-duration');
|
|
141
|
-
span2.textContent =
|
|
142
|
+
span2.textContent = Globals.i18n.formatBytesToKiB(transferSize, 0.01);
|
|
142
143
|
|
|
143
144
|
treevalEl.append(span, span2);
|
|
144
145
|
}
|
|
@@ -177,11 +178,11 @@ class CriticalRequestChainRenderer {
|
|
|
177
178
|
const containerEl = dom.find('.lh-crc', tmpl);
|
|
178
179
|
|
|
179
180
|
// Fill in top summary.
|
|
180
|
-
dom.find('.lh-crc-initial-nav', tmpl).textContent =
|
|
181
|
+
dom.find('.lh-crc-initial-nav', tmpl).textContent = Globals.strings.crcInitialNavigation;
|
|
181
182
|
dom.find('.lh-crc__longest_duration_label', tmpl).textContent =
|
|
182
|
-
|
|
183
|
+
Globals.strings.crcLongestDurationLabel;
|
|
183
184
|
dom.find('.lh-crc__longest_duration', tmpl).textContent =
|
|
184
|
-
|
|
185
|
+
Globals.i18n.formatMilliseconds(details.longestChain.duration);
|
|
185
186
|
|
|
186
187
|
// Construct visual tree.
|
|
187
188
|
const root = CRCRenderer.initTree(details.chains);
|
|
@@ -25,9 +25,10 @@
|
|
|
25
25
|
/** @typedef {LH.FormattedIcu<LH.Audit.Details.ItemValue>} TableItemValue */
|
|
26
26
|
/** @typedef {LH.FormattedIcu<LH.Audit.Details.TableColumnHeading>} TableColumnHeading */
|
|
27
27
|
|
|
28
|
-
import {Util} from '
|
|
28
|
+
import {Util} from '../../shared/util.js';
|
|
29
29
|
import {CriticalRequestChainRenderer} from './crc-details-renderer.js';
|
|
30
30
|
import {ElementScreenshotRenderer} from './element-screenshot-renderer.js';
|
|
31
|
+
import {Globals} from './report-globals.js';
|
|
31
32
|
|
|
32
33
|
const URL_PREFIXES = ['http://', 'https://', 'data:'];
|
|
33
34
|
|
|
@@ -77,9 +78,9 @@ export class DetailsRenderer {
|
|
|
77
78
|
*/
|
|
78
79
|
_renderBytes(details) {
|
|
79
80
|
// TODO: handle displayUnit once we have something other than 'KiB'
|
|
80
|
-
const value =
|
|
81
|
+
const value = Globals.i18n.formatBytesToKiB(details.value, details.granularity || 0.1);
|
|
81
82
|
const textEl = this._renderText(value);
|
|
82
|
-
textEl.title =
|
|
83
|
+
textEl.title = Globals.i18n.formatBytes(details.value);
|
|
83
84
|
return textEl;
|
|
84
85
|
}
|
|
85
86
|
|
|
@@ -90,9 +91,9 @@ export class DetailsRenderer {
|
|
|
90
91
|
_renderMilliseconds(details) {
|
|
91
92
|
let value;
|
|
92
93
|
if (details.displayUnit === 'duration') {
|
|
93
|
-
value =
|
|
94
|
+
value = Globals.i18n.formatDuration(details.value);
|
|
94
95
|
} else {
|
|
95
|
-
value =
|
|
96
|
+
value = Globals.i18n.formatMilliseconds(details.value, details.granularity || 10);
|
|
96
97
|
}
|
|
97
98
|
|
|
98
99
|
return this._renderText(value);
|
|
@@ -171,7 +172,7 @@ export class DetailsRenderer {
|
|
|
171
172
|
* @return {Element}
|
|
172
173
|
*/
|
|
173
174
|
_renderNumeric(details) {
|
|
174
|
-
const value =
|
|
175
|
+
const value = Globals.i18n.formatNumber(details.value, details.granularity || 0.1);
|
|
175
176
|
const element = this._dom.createElement('div', 'lh-numeric');
|
|
176
177
|
element.textContent = value;
|
|
177
178
|
return element;
|
package/report/renderer/dom.js
CHANGED
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
/** @typedef {HTMLElementTagNameMap & {[id: string]: HTMLElement}} HTMLElementByTagName */
|
|
21
21
|
/** @template {string} T @typedef {import('typed-query-selector/parser').ParseSelector<T, Element>} ParseSelector */
|
|
22
22
|
|
|
23
|
-
import {Util} from '
|
|
23
|
+
import {Util} from '../../shared/util.js';
|
|
24
24
|
import {createComponent} from './components.js';
|
|
25
25
|
|
|
26
26
|
export class DOM {
|
|
@@ -10,6 +10,8 @@
|
|
|
10
10
|
* 2. Display coords (DC suffix): that match the CSS pixel coordinate space of the LH report's page.
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
+
import {Globals} from './report-globals.js';
|
|
14
|
+
|
|
13
15
|
/** @typedef {import('./dom.js').DOM} DOM */
|
|
14
16
|
/** @typedef {LH.Audit.Details.Rect} Rect */
|
|
15
17
|
/** @typedef {{width: number, height: number}} Size */
|
|
@@ -22,8 +24,6 @@
|
|
|
22
24
|
* @property {LH.Result.FullPageScreenshot} fullPageScreenshot
|
|
23
25
|
*/
|
|
24
26
|
|
|
25
|
-
import {Util} from './util.js';
|
|
26
|
-
|
|
27
27
|
/**
|
|
28
28
|
* @param {LH.Result.FullPageScreenshot['screenshot']} screenshot
|
|
29
29
|
* @param {LH.Audit.Details.Rect} rect
|
|
@@ -102,7 +102,7 @@ export class ElementScreenshotRenderer {
|
|
|
102
102
|
*/
|
|
103
103
|
static renderClipPathInScreenshot(dom, maskEl, positionClip, elementRect, elementPreviewSize) {
|
|
104
104
|
const clipPathEl = dom.find('clipPath', maskEl);
|
|
105
|
-
const clipId = `clip-${
|
|
105
|
+
const clipId = `clip-${Globals.getUniqueSuffix()}`;
|
|
106
106
|
clipPathEl.id = clipId;
|
|
107
107
|
maskEl.style.clipPath = `url(#${clipId})`;
|
|
108
108
|
|
|
@@ -1,16 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
* @template T
|
|
3
|
-
*/
|
|
4
|
-
export class I18n<T> {
|
|
1
|
+
export class I18nFormatter {
|
|
5
2
|
/**
|
|
6
3
|
* @param {LH.Locale} locale
|
|
7
|
-
* @param {T} strings
|
|
8
4
|
*/
|
|
9
|
-
constructor(locale: LH.Locale
|
|
5
|
+
constructor(locale: LH.Locale);
|
|
10
6
|
_locale: "en-US" | "en" | "en-AU" | "en-GB" | "en-IE" | "en-SG" | "en-ZA" | "en-IN" | "ar-XB" | "ar" | "bg" | "ca" | "cs" | "da" | "de" | "el" | "en-XL" | "es" | "es-419" | "es-AR" | "es-BO" | "es-BR" | "es-BZ" | "es-CL" | "es-CO" | "es-CR" | "es-CU" | "es-DO" | "es-EC" | "es-GT" | "es-HN" | "es-MX" | "es-NI" | "es-PA" | "es-PE" | "es-PR" | "es-PY" | "es-SV" | "es-US" | "es-UY" | "es-VE" | "fi" | "fil" | "fr" | "he" | "hi" | "hr" | "hu" | "gsw" | "id" | "in" | "it" | "iw" | "ja" | "ko" | "lt" | "lv" | "mo" | "nl" | "nb" | "no" | "pl" | "pt" | "pt-PT" | "ro" | "ru" | "sk" | "sl" | "sr" | "sr-Latn" | "sv" | "ta" | "te" | "th" | "tl" | "tr" | "uk" | "vi" | "zh" | "zh-HK" | "zh-TW";
|
|
11
|
-
_strings: T;
|
|
12
7
|
_cachedNumberFormatters: Map<any, any>;
|
|
13
|
-
get strings(): T;
|
|
14
8
|
/**
|
|
15
9
|
* @param {number} number
|
|
16
10
|
* @param {number|undefined} granularity
|
|
@@ -103,4 +97,4 @@ export class I18n<T> {
|
|
|
103
97
|
*/
|
|
104
98
|
formatDuration(timeInMilliseconds: number): string;
|
|
105
99
|
}
|
|
106
|
-
//# sourceMappingURL=i18n.d.ts.map
|
|
100
|
+
//# sourceMappingURL=i18n-formatter.d.ts.map
|
|
@@ -9,27 +9,18 @@ const NBSP2 = '\xa0';
|
|
|
9
9
|
const KiB = 1024;
|
|
10
10
|
const MiB = KiB * KiB;
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
* @template T
|
|
14
|
-
*/
|
|
15
|
-
export class I18n {
|
|
12
|
+
export class I18nFormatter {
|
|
16
13
|
/**
|
|
17
14
|
* @param {LH.Locale} locale
|
|
18
|
-
* @param {T} strings
|
|
19
15
|
*/
|
|
20
|
-
constructor(locale
|
|
16
|
+
constructor(locale) {
|
|
21
17
|
// When testing, use a locale with more exciting numeric formatting.
|
|
22
18
|
if (locale === 'en-XA') locale = 'de';
|
|
23
19
|
|
|
24
20
|
this._locale = locale;
|
|
25
|
-
this._strings = strings;
|
|
26
21
|
this._cachedNumberFormatters = new Map();
|
|
27
22
|
}
|
|
28
23
|
|
|
29
|
-
get strings() {
|
|
30
|
-
return this._strings;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
24
|
/**
|
|
34
25
|
* @param {number} number
|
|
35
26
|
* @param {number|undefined} granularity
|
|
@@ -17,8 +17,9 @@
|
|
|
17
17
|
|
|
18
18
|
/** @typedef {import('./dom.js').DOM} DOM */
|
|
19
19
|
|
|
20
|
-
import {Util} from './util.js';
|
|
21
20
|
import {CategoryRenderer} from './category-renderer.js';
|
|
21
|
+
import {ReportUtils} from './report-utils.js';
|
|
22
|
+
import {Globals} from './report-globals.js';
|
|
22
23
|
|
|
23
24
|
export class PerformanceCategoryRenderer extends CategoryRenderer {
|
|
24
25
|
/**
|
|
@@ -29,7 +30,7 @@ export class PerformanceCategoryRenderer extends CategoryRenderer {
|
|
|
29
30
|
const tmpl = this.dom.createComponent('metric');
|
|
30
31
|
const element = this.dom.find('.lh-metric', tmpl);
|
|
31
32
|
element.id = audit.result.id;
|
|
32
|
-
const rating =
|
|
33
|
+
const rating = ReportUtils.calculateRating(audit.result.score, audit.result.scoreDisplayMode);
|
|
33
34
|
element.classList.add(`lh-metric--${rating}`);
|
|
34
35
|
|
|
35
36
|
const titleEl = this.dom.find('.lh-metric__title', tmpl);
|
|
@@ -77,7 +78,7 @@ export class PerformanceCategoryRenderer extends CategoryRenderer {
|
|
|
77
78
|
this.dom.find('span.lh-audit__display-text, div.lh-audit__display-text', element);
|
|
78
79
|
const sparklineWidthPct = `${details.overallSavingsMs / scale * 100}%`;
|
|
79
80
|
this.dom.find('div.lh-sparkline__bar', element).style.width = sparklineWidthPct;
|
|
80
|
-
displayEl.textContent =
|
|
81
|
+
displayEl.textContent = Globals.i18n.formatSeconds(details.overallSavingsMs, 0.01);
|
|
81
82
|
|
|
82
83
|
// Set [title] tooltips
|
|
83
84
|
if (audit.result.displayValue) {
|
|
@@ -144,9 +145,9 @@ export class PerformanceCategoryRenderer extends CategoryRenderer {
|
|
|
144
145
|
});
|
|
145
146
|
const paramPairs = [...metricPairs];
|
|
146
147
|
|
|
147
|
-
if (
|
|
148
|
-
paramPairs.push(['device',
|
|
149
|
-
paramPairs.push(['version',
|
|
148
|
+
if (Globals.reportJson) {
|
|
149
|
+
paramPairs.push(['device', Globals.reportJson.configSettings.formFactor]);
|
|
150
|
+
paramPairs.push(['version', Globals.reportJson.lighthouseVersion]);
|
|
150
151
|
}
|
|
151
152
|
|
|
152
153
|
const params = new URLSearchParams(paramPairs);
|
|
@@ -178,7 +179,7 @@ export class PerformanceCategoryRenderer extends CategoryRenderer {
|
|
|
178
179
|
* @override
|
|
179
180
|
*/
|
|
180
181
|
render(category, groups, options) {
|
|
181
|
-
const strings =
|
|
182
|
+
const strings = Globals.strings;
|
|
182
183
|
const element = this.dom.createElement('div', 'lh-category');
|
|
183
184
|
element.id = category.id;
|
|
184
185
|
element.append(this.renderCategoryHeader(category, groups, options));
|
|
@@ -190,7 +191,7 @@ export class PerformanceCategoryRenderer extends CategoryRenderer {
|
|
|
190
191
|
|
|
191
192
|
// Metric descriptions toggle.
|
|
192
193
|
const checkboxEl = this.dom.createElement('input', 'lh-metrics-toggle__input');
|
|
193
|
-
const checkboxId = `lh-metrics-toggle${
|
|
194
|
+
const checkboxId = `lh-metrics-toggle${Globals.getUniqueSuffix()}`;
|
|
194
195
|
checkboxEl.setAttribute('aria-label', 'Toggle the display of metric descriptions');
|
|
195
196
|
checkboxEl.type = 'checkbox';
|
|
196
197
|
checkboxEl.id = checkboxId;
|
|
@@ -200,8 +201,8 @@ export class PerformanceCategoryRenderer extends CategoryRenderer {
|
|
|
200
201
|
labelEl.htmlFor = checkboxId;
|
|
201
202
|
const showEl = this.dom.createChildOf(labelEl, 'span', 'lh-metrics-toggle__labeltext--show');
|
|
202
203
|
const hideEl = this.dom.createChildOf(labelEl, 'span', 'lh-metrics-toggle__labeltext--hide');
|
|
203
|
-
showEl.textContent =
|
|
204
|
-
hideEl.textContent =
|
|
204
|
+
showEl.textContent = Globals.strings.expandView;
|
|
205
|
+
hideEl.textContent = Globals.strings.collapseView;
|
|
205
206
|
|
|
206
207
|
const metricsBoxesEl = this.dom.createElement('div', 'lh-metrics-container');
|
|
207
208
|
metricsGroupEl.insertBefore(metricsBoxesEl, metricsFooterEl);
|
|
@@ -240,7 +241,7 @@ export class PerformanceCategoryRenderer extends CategoryRenderer {
|
|
|
240
241
|
// Opportunities
|
|
241
242
|
const opportunityAudits = category.auditRefs
|
|
242
243
|
.filter(audit => this._classifyPerformanceAudit(audit) === 'load-opportunity')
|
|
243
|
-
.filter(audit => !
|
|
244
|
+
.filter(audit => !ReportUtils.showAsPassed(audit.result))
|
|
244
245
|
.sort((auditA, auditB) => this._getWastedMs(auditB) - this._getWastedMs(auditA));
|
|
245
246
|
|
|
246
247
|
const filterableMetrics = metricAudits.filter(a => !!a.relevantAudits);
|
|
@@ -274,7 +275,7 @@ export class PerformanceCategoryRenderer extends CategoryRenderer {
|
|
|
274
275
|
// Diagnostics
|
|
275
276
|
const diagnosticAudits = category.auditRefs
|
|
276
277
|
.filter(audit => this._classifyPerformanceAudit(audit) === 'diagnostic')
|
|
277
|
-
.filter(audit => !
|
|
278
|
+
.filter(audit => !ReportUtils.showAsPassed(audit.result))
|
|
278
279
|
.sort((a, b) => {
|
|
279
280
|
const scoreA = a.result.scoreDisplayMode === 'informative' ? 100 : Number(a.result.score);
|
|
280
281
|
const scoreB = b.result.scoreDisplayMode === 'informative' ? 100 : Number(b.result.score);
|
|
@@ -290,7 +291,8 @@ export class PerformanceCategoryRenderer extends CategoryRenderer {
|
|
|
290
291
|
|
|
291
292
|
// Passed audits
|
|
292
293
|
const passedAudits = category.auditRefs
|
|
293
|
-
.filter(audit =>
|
|
294
|
+
.filter(audit =>
|
|
295
|
+
this._classifyPerformanceAudit(audit) && ReportUtils.showAsPassed(audit.result));
|
|
294
296
|
|
|
295
297
|
if (!passedAudits.length) return element;
|
|
296
298
|
|
|
@@ -333,7 +335,7 @@ export class PerformanceCategoryRenderer extends CategoryRenderer {
|
|
|
333
335
|
renderMetricAuditFilter(filterableMetrics, categoryEl) {
|
|
334
336
|
const metricFilterEl = this.dom.createElement('div', 'lh-metricfilter');
|
|
335
337
|
const textEl = this.dom.createChildOf(metricFilterEl, 'span', 'lh-metricfilter__text');
|
|
336
|
-
textEl.textContent =
|
|
338
|
+
textEl.textContent = Globals.strings.showRelevantAudits;
|
|
337
339
|
|
|
338
340
|
const filterChoices = /** @type {LH.ReportResult.AuditRef[]} */ ([
|
|
339
341
|
({acronym: 'All'}),
|
|
@@ -342,7 +344,7 @@ export class PerformanceCategoryRenderer extends CategoryRenderer {
|
|
|
342
344
|
|
|
343
345
|
// Form labels need to reference unique IDs, but multiple reports rendered in the same DOM (eg PSI)
|
|
344
346
|
// would mean ID conflict. To address this, we 'scope' these radio inputs with a unique suffix.
|
|
345
|
-
const uniqSuffix =
|
|
347
|
+
const uniqSuffix = Globals.getUniqueSuffix();
|
|
346
348
|
for (const metric of filterChoices) {
|
|
347
349
|
const elemId = `metric-${metric.acronym}-${uniqSuffix}`;
|
|
348
350
|
const radioEl = this.dom.createChildOf(metricFilterEl, 'input', 'lh-metricfilter__radio');
|
|
@@ -15,8 +15,9 @@
|
|
|
15
15
|
* limitations under the License.
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
|
-
import {Util} from './util.js';
|
|
19
18
|
import {CategoryRenderer} from './category-renderer.js';
|
|
19
|
+
import {ReportUtils} from './report-utils.js';
|
|
20
|
+
import {Globals} from './report-globals.js';
|
|
20
21
|
|
|
21
22
|
export class PwaCategoryRenderer extends CategoryRenderer {
|
|
22
23
|
/**
|
|
@@ -101,7 +102,7 @@ export class PwaCategoryRenderer extends CategoryRenderer {
|
|
|
101
102
|
|
|
102
103
|
// Remove any that have a failing audit.
|
|
103
104
|
for (const auditRef of auditRefs) {
|
|
104
|
-
if (!
|
|
105
|
+
if (!ReportUtils.showAsPassed(auditRef.result) && auditRef.group) {
|
|
105
106
|
uniqueGroupIds.delete(auditRef.group);
|
|
106
107
|
}
|
|
107
108
|
}
|
|
@@ -122,7 +123,7 @@ export class PwaCategoryRenderer extends CategoryRenderer {
|
|
|
122
123
|
for (const groupId of groupIds) {
|
|
123
124
|
const groupAuditRefs = auditRefs.filter(ref => ref.group === groupId);
|
|
124
125
|
const auditCount = groupAuditRefs.length;
|
|
125
|
-
const passedCount = groupAuditRefs.filter(ref =>
|
|
126
|
+
const passedCount = groupAuditRefs.filter(ref => ReportUtils.showAsPassed(ref.result)).length;
|
|
126
127
|
|
|
127
128
|
const title = groupDefinitions[groupId].title;
|
|
128
129
|
tips.push(`${title}: ${passedCount}/${auditCount}`);
|
|
@@ -162,7 +163,7 @@ export class PwaCategoryRenderer extends CategoryRenderer {
|
|
|
162
163
|
const defsEl = svgRoot.querySelector('defs');
|
|
163
164
|
if (!defsEl) return;
|
|
164
165
|
|
|
165
|
-
const idSuffix =
|
|
166
|
+
const idSuffix = Globals.getUniqueSuffix();
|
|
166
167
|
const elementsToUpdate = defsEl.querySelectorAll('[id]');
|
|
167
168
|
for (const el of elementsToUpdate) {
|
|
168
169
|
const oldId = el.id;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export type I18nFormatter = import('./i18n-formatter').I18nFormatter;
|
|
2
|
+
export class Globals {
|
|
3
|
+
/** @type {I18nFormatter} */
|
|
4
|
+
static i18n: I18nFormatter;
|
|
5
|
+
/** @type {typeof UIStrings} */
|
|
6
|
+
static strings: typeof UIStrings;
|
|
7
|
+
/** @type {LH.ReportResult | null} */
|
|
8
|
+
static reportJson: LH.ReportResult | null;
|
|
9
|
+
/**
|
|
10
|
+
* @param {{providedStrings: Record<string, string>; i18n: I18nFormatter; reportJson: LH.ReportResult | null}} options
|
|
11
|
+
*/
|
|
12
|
+
static apply(options: {
|
|
13
|
+
providedStrings: Record<string, string>;
|
|
14
|
+
i18n: I18nFormatter;
|
|
15
|
+
reportJson: LH.ReportResult | null;
|
|
16
|
+
}): void;
|
|
17
|
+
static getUniqueSuffix(): number;
|
|
18
|
+
static resetUniqueSuffix(): void;
|
|
19
|
+
}
|
|
20
|
+
import { UIStrings } from "./report-utils.js";
|
|
21
|
+
//# sourceMappingURL=report-globals.d.ts.map
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license Copyright 2023 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
|
+
import {UIStrings} from './report-utils.js';
|
|
8
|
+
|
|
9
|
+
/** @typedef {import('./i18n-formatter').I18nFormatter} I18nFormatter */
|
|
10
|
+
|
|
11
|
+
let svgSuffix = 0;
|
|
12
|
+
|
|
13
|
+
class Globals {
|
|
14
|
+
/** @type {I18nFormatter} */
|
|
15
|
+
// @ts-expect-error: Set in report renderer.
|
|
16
|
+
static i18n = null;
|
|
17
|
+
|
|
18
|
+
/** @type {typeof UIStrings} */
|
|
19
|
+
// @ts-expect-error: Set in report renderer.
|
|
20
|
+
static strings = {};
|
|
21
|
+
|
|
22
|
+
/** @type {LH.ReportResult | null} */
|
|
23
|
+
static reportJson = null;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* @param {{providedStrings: Record<string, string>; i18n: I18nFormatter; reportJson: LH.ReportResult | null}} options
|
|
27
|
+
*/
|
|
28
|
+
static apply(options) {
|
|
29
|
+
Globals.strings = {
|
|
30
|
+
// Set missing renderer strings to default (english) values.
|
|
31
|
+
...UIStrings,
|
|
32
|
+
...options.providedStrings,
|
|
33
|
+
};
|
|
34
|
+
Globals.i18n = options.i18n;
|
|
35
|
+
Globals.reportJson = options.reportJson;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
static getUniqueSuffix() {
|
|
39
|
+
return svgSuffix++;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
static resetUniqueSuffix() {
|
|
43
|
+
svgSuffix = 0;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export {
|
|
48
|
+
Globals,
|
|
49
|
+
};
|
|
@@ -23,11 +23,11 @@
|
|
|
23
23
|
import {CategoryRenderer} from './category-renderer.js';
|
|
24
24
|
import {DetailsRenderer} from './details-renderer.js';
|
|
25
25
|
import {ElementScreenshotRenderer} from './element-screenshot-renderer.js';
|
|
26
|
-
import {
|
|
26
|
+
import {I18nFormatter} from './i18n-formatter.js';
|
|
27
27
|
import {PerformanceCategoryRenderer} from './performance-category-renderer.js';
|
|
28
28
|
import {PwaCategoryRenderer} from './pwa-category-renderer.js';
|
|
29
|
-
import {
|
|
30
|
-
|
|
29
|
+
import {ReportUtils} from './report-utils.js';
|
|
30
|
+
import {Globals} from './report-globals.js';
|
|
31
31
|
|
|
32
32
|
export class ReportRenderer {
|
|
33
33
|
/**
|
|
@@ -67,7 +67,7 @@ export class ReportRenderer {
|
|
|
67
67
|
|
|
68
68
|
this._dom.setLighthouseChannel(lhr.configSettings.channel || 'unknown');
|
|
69
69
|
|
|
70
|
-
const report =
|
|
70
|
+
const report = ReportUtils.prepareReportResult(lhr);
|
|
71
71
|
|
|
72
72
|
this._dom.rootEl.textContent = ''; // Remove previous report.
|
|
73
73
|
this._dom.rootEl.append(this._renderReport(report));
|
|
@@ -108,7 +108,7 @@ export class ReportRenderer {
|
|
|
108
108
|
|
|
109
109
|
this._renderMetaBlock(report, footer);
|
|
110
110
|
|
|
111
|
-
this._dom.find('.lh-footer__version_issue', footer).textContent =
|
|
111
|
+
this._dom.find('.lh-footer__version_issue', footer).textContent = Globals.strings.footerIssue;
|
|
112
112
|
this._dom.find('.lh-footer__version', footer).textContent = report.lighthouseVersion;
|
|
113
113
|
return footer;
|
|
114
114
|
}
|
|
@@ -118,7 +118,7 @@ export class ReportRenderer {
|
|
|
118
118
|
* @param {DocumentFragment} footer
|
|
119
119
|
*/
|
|
120
120
|
_renderMetaBlock(report, footer) {
|
|
121
|
-
const envValues =
|
|
121
|
+
const envValues = ReportUtils.getEmulationDescriptions(report.configSettings || {});
|
|
122
122
|
const match = report.userAgent.match(/(\w*Chrome\/[\d.]+)/); // \w* to include 'HeadlessChrome'
|
|
123
123
|
const chromeVer = Array.isArray(match)
|
|
124
124
|
? match[1].replace('/', ' ').replace('Chrome', 'Chromium')
|
|
@@ -128,35 +128,35 @@ export class ReportRenderer {
|
|
|
128
128
|
const axeVersion = report.environment.credits?.['axe-core'];
|
|
129
129
|
|
|
130
130
|
const devicesTooltipTextLines = [
|
|
131
|
-
`${
|
|
132
|
-
`${
|
|
131
|
+
`${Globals.strings.runtimeSettingsBenchmark}: ${benchmarkIndex}`,
|
|
132
|
+
`${Globals.strings.runtimeSettingsCPUThrottling}: ${envValues.cpuThrottling}`,
|
|
133
133
|
];
|
|
134
134
|
if (envValues.screenEmulation) {
|
|
135
135
|
devicesTooltipTextLines.push(
|
|
136
|
-
`${
|
|
136
|
+
`${Globals.strings.runtimeSettingsScreenEmulation}: ${envValues.screenEmulation}`);
|
|
137
137
|
}
|
|
138
138
|
if (axeVersion) {
|
|
139
|
-
devicesTooltipTextLines.push(`${
|
|
139
|
+
devicesTooltipTextLines.push(`${Globals.strings.runtimeSettingsAxeVersion}: ${axeVersion}`);
|
|
140
140
|
}
|
|
141
141
|
|
|
142
142
|
// [CSS icon class, textContent, tooltipText]
|
|
143
143
|
const metaItems = [
|
|
144
144
|
['date',
|
|
145
|
-
`Captured at ${
|
|
145
|
+
`Captured at ${Globals.i18n.formatDateTime(report.fetchTime)}`],
|
|
146
146
|
['devices',
|
|
147
147
|
`${envValues.deviceEmulation} with Lighthouse ${report.lighthouseVersion}`,
|
|
148
148
|
devicesTooltipTextLines.join('\n')],
|
|
149
149
|
['samples-one',
|
|
150
|
-
|
|
151
|
-
|
|
150
|
+
Globals.strings.runtimeSingleLoad,
|
|
151
|
+
Globals.strings.runtimeSingleLoadTooltip],
|
|
152
152
|
['stopwatch',
|
|
153
|
-
|
|
153
|
+
Globals.strings.runtimeAnalysisWindow],
|
|
154
154
|
['networkspeed',
|
|
155
155
|
`${envValues.summary}`,
|
|
156
|
-
`${
|
|
156
|
+
`${Globals.strings.runtimeSettingsNetworkThrottling}: ${envValues.networkThrottling}`],
|
|
157
157
|
['chrome',
|
|
158
158
|
`Using ${chromeVer}` + (channel ? ` with ${channel}` : ''),
|
|
159
|
-
`${
|
|
159
|
+
`${Globals.strings.runtimeSettingsUANetwork}: "${report.environment.networkUserAgent}"`],
|
|
160
160
|
];
|
|
161
161
|
|
|
162
162
|
const metaItemsEl = this._dom.find('.lh-meta__items', footer);
|
|
@@ -184,7 +184,7 @@ export class ReportRenderer {
|
|
|
184
184
|
|
|
185
185
|
const container = this._dom.createComponent('warningsToplevel');
|
|
186
186
|
const message = this._dom.find('.lh-warnings__msg', container);
|
|
187
|
-
message.textContent =
|
|
187
|
+
message.textContent = Globals.strings.toplevelWarningsMessage;
|
|
188
188
|
|
|
189
189
|
const warnings = [];
|
|
190
190
|
for (const warningString of report.runWarnings) {
|
|
@@ -238,7 +238,7 @@ export class ReportRenderer {
|
|
|
238
238
|
}
|
|
239
239
|
|
|
240
240
|
|
|
241
|
-
if (
|
|
241
|
+
if (ReportUtils.isPluginCategory(category.id)) {
|
|
242
242
|
pluginGauges.push(categoryGauge);
|
|
243
243
|
} else if (renderer.renderCategoryScore === categoryRenderer.renderCategoryScore) {
|
|
244
244
|
// The renderer for default categories is just the default CategoryRenderer.
|
|
@@ -260,13 +260,11 @@ export class ReportRenderer {
|
|
|
260
260
|
* @return {!DocumentFragment}
|
|
261
261
|
*/
|
|
262
262
|
_renderReport(report) {
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
263
|
+
Globals.apply({
|
|
264
|
+
providedStrings: report.i18n.rendererFormattedStrings,
|
|
265
|
+
i18n: new I18nFormatter(report.configSettings.locale),
|
|
266
|
+
reportJson: report,
|
|
267
267
|
});
|
|
268
|
-
Util.i18n = i18n;
|
|
269
|
-
Util.reportJson = report;
|
|
270
268
|
|
|
271
269
|
const detailsRenderer = new DetailsRenderer(this._dom, {
|
|
272
270
|
fullPageScreenshot: report.fullPageScreenshot ?? undefined,
|
|
@@ -26,8 +26,9 @@ import {ElementScreenshotRenderer} from './element-screenshot-renderer.js';
|
|
|
26
26
|
import {toggleDarkTheme} from './features-util.js';
|
|
27
27
|
import {openTreemap} from './open-tab.js';
|
|
28
28
|
import {TopbarFeatures} from './topbar-features.js';
|
|
29
|
-
import {Util} from '
|
|
29
|
+
import {Util} from '../../shared/util.js';
|
|
30
30
|
import {getLhrFilenamePrefix} from '../generator/file-namer.js';
|
|
31
|
+
import {Globals} from './report-globals.js';
|
|
31
32
|
|
|
32
33
|
/**
|
|
33
34
|
* @param {HTMLTableElement} tableEl
|
|
@@ -106,7 +107,7 @@ export class ReportUIFeatures {
|
|
|
106
107
|
this.json.audits['script-treemap-data'] && this.json.audits['script-treemap-data'].details;
|
|
107
108
|
if (showTreemapApp) {
|
|
108
109
|
this.addButton({
|
|
109
|
-
text:
|
|
110
|
+
text: Globals.strings.viewTreemapLabel,
|
|
110
111
|
icon: 'treemap',
|
|
111
112
|
onClick: () => openTreemap(this.json),
|
|
112
113
|
});
|
|
@@ -115,8 +116,8 @@ export class ReportUIFeatures {
|
|
|
115
116
|
if (this._opts.onViewTrace) {
|
|
116
117
|
this.addButton({
|
|
117
118
|
text: lhr.configSettings.throttlingMethod === 'simulate' ?
|
|
118
|
-
|
|
119
|
-
|
|
119
|
+
Globals.strings.viewOriginalTraceLabel :
|
|
120
|
+
Globals.strings.viewTraceLabel,
|
|
120
121
|
onClick: () => this._opts.onViewTrace?.(),
|
|
121
122
|
});
|
|
122
123
|
}
|
|
@@ -127,11 +128,11 @@ export class ReportUIFeatures {
|
|
|
127
128
|
|
|
128
129
|
// Fill in all i18n data.
|
|
129
130
|
for (const node of this._dom.findAll('[data-i18n]', this._dom.rootEl)) {
|
|
130
|
-
// These strings are guaranteed to (at least) have a default English string in
|
|
131
|
+
// These strings are guaranteed to (at least) have a default English string in UIStrings,
|
|
131
132
|
// so this cannot be undefined as long as `report-ui-features.data-i18n` test passes.
|
|
132
133
|
const i18nKey = node.getAttribute('data-i18n');
|
|
133
|
-
const i18nAttr = /** @type {keyof typeof
|
|
134
|
-
node.textContent =
|
|
134
|
+
const i18nAttr = /** @type {keyof typeof Globals.strings} */ (i18nKey);
|
|
135
|
+
node.textContent = Globals.strings[i18nAttr];
|
|
135
136
|
}
|
|
136
137
|
}
|
|
137
138
|
|
|
@@ -273,7 +274,7 @@ export class ReportUIFeatures {
|
|
|
273
274
|
this._dom.find('.lh-3p-filter-count', filterTemplate).textContent =
|
|
274
275
|
`${thirdPartyRows.length}`;
|
|
275
276
|
this._dom.find('.lh-3p-ui-string', filterTemplate).textContent =
|
|
276
|
-
|
|
277
|
+
Globals.strings.thirdPartyResourcesLabel;
|
|
277
278
|
|
|
278
279
|
const allThirdParty = thirdPartyRows.length === rowEls.length;
|
|
279
280
|
const allFirstParty = !thirdPartyRows.length;
|