lighthouse 8.6.0-dev.20211021 → 8.6.0
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 -30
- package/dist/report/standalone.js +17 -17
- package/flow-report/assets/styles.css +30 -51
- package/flow-report/src/common.tsx +1 -1
- package/flow-report/src/header.tsx +2 -2
- package/flow-report/src/help-dialog.tsx +3 -3
- package/flow-report/src/i18n/i18n.tsx +13 -50
- package/flow-report/src/i18n/ui-strings.js +0 -48
- package/flow-report/src/icons.tsx +0 -7
- package/flow-report/src/sidebar/sidebar.tsx +6 -8
- package/flow-report/src/summary/category.tsx +18 -17
- package/flow-report/src/summary/summary.tsx +10 -10
- package/flow-report/src/topbar.tsx +5 -9
- package/flow-report/src/util.ts +5 -0
- package/flow-report/src/wrappers/category-score.tsx +3 -0
- package/flow-report/test/common-test.tsx +1 -1
- package/flow-report/test/summary/category-test.tsx +5 -9
- package/flow-report/test/summary/summary-test.tsx +1 -1
- package/flow-report/test/topbar-test.tsx +0 -1
- package/flow-report/tsconfig.json +0 -1
- package/jest.config.js +0 -1
- package/lighthouse-cli/test/smokehouse/lighthouse-runners/cli.js +1 -1
- package/lighthouse-core/audits/accessibility/axe-audit.js +1 -7
- package/lighthouse-core/audits/redirects-http.js +59 -0
- package/lighthouse-core/config/config-helpers.js +0 -1
- package/lighthouse-core/config/default-config.js +11 -0
- package/lighthouse-core/fraggle-rock/config/config.js +1 -1
- package/lighthouse-core/gather/gatherers/accessibility.js +0 -27
- package/lighthouse-core/gather/gatherers/http-redirect.js +46 -0
- package/lighthouse-core/lib/asset-saver.js +3 -3
- package/lighthouse-core/lib/i18n/i18n.js +8 -19
- package/lighthouse-core/lib/i18n/locales/en-US.ctc.json +6699 -0
- package/lighthouse-core/lib/i18n/locales/en-XL.ctc.json +6672 -0
- package/lighthouse-core/util-commonjs.js +37 -25
- package/package.json +4 -8
- package/readme.md +3 -3
- package/report/assets/styles.css +28 -157
- package/report/assets/templates.html +47 -2
- package/report/clients/psi.js +158 -0
- package/report/renderer/category-renderer.js +1 -30
- package/report/renderer/components.js +34 -16
- package/report/renderer/performance-category-renderer.js +10 -5
- package/report/renderer/report-renderer.js +33 -56
- package/report/renderer/util.js +37 -25
- package/report/test/clients/psi-test.js +143 -0
- package/report/test/renderer/category-renderer-test.js +1 -5
- package/report/test/renderer/i18n-test.js +7 -0
- package/report/test/renderer/performance-category-renderer-test.js +1 -1
- package/report/test/renderer/pwa-category-renderer-test.js +2 -2
- package/report/test/renderer/report-renderer-axe-test.js +10 -5
- package/report/test/renderer/report-renderer-test.js +11 -9
- package/report/test-assets/faux-psi-template.html +4 -1
- package/report/test-assets/faux-psi.js +16 -39
- package/shared/localization/format.js +17 -50
- package/shared/localization/locales/ar-XB.json +24 -0
- package/shared/localization/locales/ar.json +24 -0
- package/shared/localization/locales/bg.json +24 -0
- package/shared/localization/locales/ca.json +24 -0
- package/shared/localization/locales/cs.json +24 -0
- package/shared/localization/locales/da.json +24 -0
- package/shared/localization/locales/de.json +24 -0
- package/shared/localization/locales/el.json +24 -0
- package/shared/localization/locales/en-GB.json +24 -0
- package/shared/localization/locales/en-US.ctc.json +6877 -0
- package/shared/localization/locales/en-US.json +23 -26
- package/shared/localization/locales/en-XA.json +24 -0
- package/shared/localization/locales/en-XL.ctc.json +6852 -0
- package/shared/localization/locales/en-XL.json +23 -26
- package/shared/localization/locales/es-419.json +24 -0
- package/shared/localization/locales/es.json +24 -0
- package/shared/localization/locales/fi.json +24 -0
- package/shared/localization/locales/fil.json +24 -0
- package/shared/localization/locales/fr.json +24 -0
- package/shared/localization/locales/he.json +24 -0
- package/shared/localization/locales/hi.json +24 -0
- package/shared/localization/locales/hr.json +24 -0
- package/shared/localization/locales/hu.json +24 -0
- package/shared/localization/locales/id.json +24 -0
- package/shared/localization/locales/it.json +24 -0
- package/shared/localization/locales/ja.json +24 -0
- package/shared/localization/locales/ko.json +24 -0
- package/shared/localization/locales/lt.json +24 -0
- package/shared/localization/locales/lv.json +24 -0
- package/shared/localization/locales/nl.json +24 -0
- package/shared/localization/locales/no.json +24 -0
- package/shared/localization/locales/pl.json +24 -0
- package/shared/localization/locales/pt-PT.json +24 -0
- package/shared/localization/locales/pt.json +24 -0
- package/shared/localization/locales/ro.json +24 -0
- package/shared/localization/locales/ru.json +24 -0
- package/shared/localization/locales/sk.json +24 -0
- package/shared/localization/locales/sl.json +24 -0
- package/shared/localization/locales/sr-Latn.json +24 -0
- package/shared/localization/locales/sr.json +24 -0
- package/shared/localization/locales/sv.json +24 -0
- package/shared/localization/locales/ta.json +24 -0
- package/shared/localization/locales/te.json +24 -0
- package/shared/localization/locales/th.json +24 -0
- package/shared/localization/locales/tr.json +24 -0
- package/shared/localization/locales/uk.json +24 -0
- package/shared/localization/locales/vi.json +24 -0
- package/shared/localization/locales/zh-HK.json +24 -0
- package/shared/localization/locales/zh-TW.json +24 -0
- package/shared/localization/locales/zh.json +24 -0
- package/shared/localization/locales.js +0 -6
- package/shared/test/localization/format-test.js +9 -45
- package/shared/test/localization/swap-locale-test.js +12 -0
- package/third-party/download-content-shell/download-content-shell.js +2 -2
- package/third-party/download-content-shell/utils.js +24 -0
- package/tsconfig.json +1 -0
- package/types/artifacts.d.ts +3 -1
- package/types/enquirer.d.ts +1 -3
- package/types/node.d.ts +5 -5
- package/flow-report/test/wrappers/category-score-test.tsx +0 -87
- package/report/test/clients/bundle-test.js +0 -62
|
@@ -85,64 +85,44 @@ export class ReportRenderer {
|
|
|
85
85
|
_renderReportFooter(report) {
|
|
86
86
|
const footer = this._dom.createComponent('footer');
|
|
87
87
|
|
|
88
|
-
this.
|
|
88
|
+
const env = this._dom.find('.lh-env__items', footer);
|
|
89
|
+
env.id = 'runtime-settings';
|
|
90
|
+
this._dom.find('.lh-env__title', footer).textContent = Util.i18n.strings.runtimeSettingsTitle;
|
|
91
|
+
|
|
92
|
+
const envValues = Util.getEnvironmentDisplayValues(report.configSettings || {});
|
|
93
|
+
const runtimeValues = [
|
|
94
|
+
{name: Util.i18n.strings.runtimeSettingsUrl, description: report.finalUrl},
|
|
95
|
+
{name: Util.i18n.strings.runtimeSettingsFetchTime,
|
|
96
|
+
description: Util.i18n.formatDateTime(report.fetchTime)},
|
|
97
|
+
...envValues,
|
|
98
|
+
{name: Util.i18n.strings.runtimeSettingsChannel, description: report.configSettings.channel},
|
|
99
|
+
{name: Util.i18n.strings.runtimeSettingsUA, description: report.userAgent},
|
|
100
|
+
{name: Util.i18n.strings.runtimeSettingsUANetwork, description: report.environment &&
|
|
101
|
+
report.environment.networkUserAgent},
|
|
102
|
+
{name: Util.i18n.strings.runtimeSettingsBenchmark, description: report.environment &&
|
|
103
|
+
report.environment.benchmarkIndex.toFixed(0)},
|
|
104
|
+
];
|
|
105
|
+
if (report.environment.credits && report.environment.credits['axe-core']) {
|
|
106
|
+
runtimeValues.push({
|
|
107
|
+
name: Util.i18n.strings.runtimeSettingsAxeVersion,
|
|
108
|
+
description: report.environment.credits['axe-core'],
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
for (const runtime of runtimeValues) {
|
|
113
|
+
if (!runtime.description) continue;
|
|
114
|
+
|
|
115
|
+
const item = this._dom.createComponent('envItem');
|
|
116
|
+
this._dom.find('.lh-env__name', item).textContent = runtime.name;
|
|
117
|
+
this._dom.find('.lh-env__description', item).textContent = runtime.description;
|
|
118
|
+
env.appendChild(item);
|
|
119
|
+
}
|
|
89
120
|
|
|
90
121
|
this._dom.find('.lh-footer__version_issue', footer).textContent = Util.i18n.strings.footerIssue;
|
|
91
122
|
this._dom.find('.lh-footer__version', footer).textContent = report.lighthouseVersion;
|
|
92
123
|
return footer;
|
|
93
124
|
}
|
|
94
125
|
|
|
95
|
-
/**
|
|
96
|
-
* @param {LH.ReportResult} report
|
|
97
|
-
* @param {DocumentFragment} footer
|
|
98
|
-
*/
|
|
99
|
-
_renderMetaBlock(report, footer) {
|
|
100
|
-
const envValues = Util.getEmulationDescriptions(report.configSettings || {});
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
const match = report.userAgent.match(/(\w*Chrome\/[\d.]+)/); // \w* to include 'HeadlessChrome'
|
|
104
|
-
const chromeVer = Array.isArray(match)
|
|
105
|
-
? match[1].replace('/', ' ').replace('Chrome', 'Chromium')
|
|
106
|
-
: 'Chromium';
|
|
107
|
-
const channel = report.configSettings.channel;
|
|
108
|
-
const benchmarkIndex = report.environment.benchmarkIndex.toFixed(0);
|
|
109
|
-
const axeVersion = report.environment.credits['axe-core'];
|
|
110
|
-
|
|
111
|
-
// [CSS icon class, textContent, tooltipText]
|
|
112
|
-
const metaItems = [
|
|
113
|
-
['date',
|
|
114
|
-
`Captured at ${Util.i18n.formatDateTime(report.fetchTime)}`],
|
|
115
|
-
['devices',
|
|
116
|
-
`${envValues.deviceEmulation} with Lighthouse ${report.lighthouseVersion}`,
|
|
117
|
-
`${Util.i18n.strings.runtimeSettingsBenchmark}: ${benchmarkIndex}` +
|
|
118
|
-
`\n${Util.i18n.strings.runtimeSettingsCPUThrottling}: ${envValues.cpuThrottling}` +
|
|
119
|
-
(axeVersion ? `\n${Util.i18n.strings.runtimeSettingsAxeVersion}: ${axeVersion}` : '')],
|
|
120
|
-
['samples-one',
|
|
121
|
-
Util.i18n.strings.runtimeSingleLoad,
|
|
122
|
-
Util.i18n.strings.runtimeSingleLoadTooltip],
|
|
123
|
-
['stopwatch',
|
|
124
|
-
Util.i18n.strings.runtimeAnalysisWindow],
|
|
125
|
-
['networkspeed',
|
|
126
|
-
`${envValues.summary}`,
|
|
127
|
-
`${Util.i18n.strings.runtimeSettingsNetworkThrottling}: ${envValues.networkThrottling}`],
|
|
128
|
-
['chrome',
|
|
129
|
-
`Using ${chromeVer}` + (channel ? ` with ${channel}` : ''),
|
|
130
|
-
`${Util.i18n.strings.runtimeSettingsUANetwork}: "${report.environment.networkUserAgent}"`],
|
|
131
|
-
];
|
|
132
|
-
|
|
133
|
-
const metaItemsEl = this._dom.find('.lh-meta__items', footer);
|
|
134
|
-
for (const [iconname, text, tooltip] of metaItems) {
|
|
135
|
-
const itemEl = this._dom.createChildOf(metaItemsEl, 'li', 'lh-meta__item');
|
|
136
|
-
itemEl.textContent = text;
|
|
137
|
-
if (tooltip) {
|
|
138
|
-
itemEl.classList.add('lh-tooltip-boundary');
|
|
139
|
-
const tooltipEl = this._dom.createChildOf(itemEl, 'div', 'lh-tooltip');
|
|
140
|
-
tooltipEl.textContent = tooltip;
|
|
141
|
-
}
|
|
142
|
-
itemEl.classList.add('lh-report-icon', `lh-report-icon--${iconname}`);
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
|
|
146
126
|
/**
|
|
147
127
|
* Returns a div with a list of top-level warnings, or an empty div if no warnings.
|
|
148
128
|
* @param {LH.ReportResult} report
|
|
@@ -267,9 +247,8 @@ export class ReportRenderer {
|
|
|
267
247
|
headerContainer.classList.add('lh-header--solo-category');
|
|
268
248
|
}
|
|
269
249
|
|
|
270
|
-
const scoreScale = this._dom.createElement('div');
|
|
271
|
-
scoreScale.append(this._dom.createComponent('scorescale'));
|
|
272
250
|
if (scoreHeader) {
|
|
251
|
+
const scoreScale = this._dom.createComponent('scorescale');
|
|
273
252
|
const scoresContainer = this._dom.find('.lh-scores-container', headerContainer);
|
|
274
253
|
scoreHeader.append(
|
|
275
254
|
...this._renderScoreGauges(report, categoryRenderer, specificCategoryRenderers));
|
|
@@ -296,8 +275,6 @@ export class ReportRenderer {
|
|
|
296
275
|
));
|
|
297
276
|
}
|
|
298
277
|
|
|
299
|
-
categoryRenderer.injectFinalScreenshot(categories, report.audits, scoreScale);
|
|
300
|
-
|
|
301
278
|
const reportFragment = this._dom.createFragment();
|
|
302
279
|
reportFragment.append(this._dom.createComponent('styles'));
|
|
303
280
|
const topbarDocumentFragment = this._renderReportTopbar(report);
|
package/report/renderer/util.js
CHANGED
|
@@ -167,8 +167,6 @@ export class Util {
|
|
|
167
167
|
|
|
168
168
|
/**
|
|
169
169
|
* Convert a score to a rating label.
|
|
170
|
-
* TODO: Return `'error'` for `score === null && !scoreDisplayMode`.
|
|
171
|
-
*
|
|
172
170
|
* @param {number|null} score
|
|
173
171
|
* @param {string=} scoreDisplayMode
|
|
174
172
|
* @return {string}
|
|
@@ -389,21 +387,43 @@ export class Util {
|
|
|
389
387
|
return hostname.split('.').slice(-splitTld.length).join('.');
|
|
390
388
|
}
|
|
391
389
|
|
|
390
|
+
/**
|
|
391
|
+
* @param {LH.Result['configSettings']} settings
|
|
392
|
+
* @return {!Array<{name: string, description: string}>}
|
|
393
|
+
*/
|
|
394
|
+
static getEnvironmentDisplayValues(settings) {
|
|
395
|
+
const emulationDesc = Util.getEmulationDescriptions(settings);
|
|
396
|
+
|
|
397
|
+
return [
|
|
398
|
+
{
|
|
399
|
+
name: Util.i18n.strings.runtimeSettingsDevice,
|
|
400
|
+
description: emulationDesc.deviceEmulation,
|
|
401
|
+
},
|
|
402
|
+
{
|
|
403
|
+
name: Util.i18n.strings.runtimeSettingsNetworkThrottling,
|
|
404
|
+
description: emulationDesc.networkThrottling,
|
|
405
|
+
},
|
|
406
|
+
{
|
|
407
|
+
name: Util.i18n.strings.runtimeSettingsCPUThrottling,
|
|
408
|
+
description: emulationDesc.cpuThrottling,
|
|
409
|
+
},
|
|
410
|
+
];
|
|
411
|
+
}
|
|
392
412
|
|
|
393
413
|
/**
|
|
394
414
|
* @param {LH.Result['configSettings']} settings
|
|
395
|
-
* @return {
|
|
415
|
+
* @return {{deviceEmulation: string, networkThrottling: string, cpuThrottling: string}}
|
|
396
416
|
*/
|
|
397
417
|
static getEmulationDescriptions(settings) {
|
|
398
418
|
let cpuThrottling;
|
|
399
419
|
let networkThrottling;
|
|
400
|
-
let summary;
|
|
401
420
|
|
|
402
421
|
const throttling = settings.throttling;
|
|
403
422
|
|
|
404
423
|
switch (settings.throttlingMethod) {
|
|
405
424
|
case 'provided':
|
|
406
|
-
|
|
425
|
+
cpuThrottling = Util.i18n.strings.throttlingProvided;
|
|
426
|
+
networkThrottling = Util.i18n.strings.throttlingProvided;
|
|
407
427
|
break;
|
|
408
428
|
case 'devtools': {
|
|
409
429
|
const {cpuSlowdownMultiplier, requestLatencyMs} = throttling;
|
|
@@ -411,13 +431,6 @@ export class Util {
|
|
|
411
431
|
networkThrottling = `${Util.i18n.formatNumber(requestLatencyMs)}${NBSP}ms HTTP RTT, ` +
|
|
412
432
|
`${Util.i18n.formatNumber(throttling.downloadThroughputKbps)}${NBSP}Kbps down, ` +
|
|
413
433
|
`${Util.i18n.formatNumber(throttling.uploadThroughputKbps)}${NBSP}Kbps up (DevTools)`;
|
|
414
|
-
|
|
415
|
-
const isSlow4G = () => {
|
|
416
|
-
return requestLatencyMs === 150 * 3.75 &&
|
|
417
|
-
throttling.downloadThroughputKbps === 1.6 * 1024 * 0.9 &&
|
|
418
|
-
throttling.uploadThroughputKbps === 750 * 0.9;
|
|
419
|
-
};
|
|
420
|
-
summary = `${isSlow4G() ? 'Slow 4G' : 'Custom'} throttling by DevTools`;
|
|
421
434
|
break;
|
|
422
435
|
}
|
|
423
436
|
case 'simulate': {
|
|
@@ -425,15 +438,11 @@ export class Util {
|
|
|
425
438
|
cpuThrottling = `${Util.i18n.formatNumber(cpuSlowdownMultiplier)}x slowdown (Simulated)`;
|
|
426
439
|
networkThrottling = `${Util.i18n.formatNumber(rttMs)}${NBSP}ms TCP RTT, ` +
|
|
427
440
|
`${Util.i18n.formatNumber(throughputKbps)}${NBSP}Kbps throughput (Simulated)`;
|
|
428
|
-
|
|
429
|
-
const isSlow4G = () => {
|
|
430
|
-
return rttMs === 150 && throughputKbps === 1.6 * 1024;
|
|
431
|
-
};
|
|
432
|
-
summary = isSlow4G() ? 'Simulated slow 4G' : 'Custom simulated throttling';
|
|
433
441
|
break;
|
|
434
442
|
}
|
|
435
443
|
default:
|
|
436
|
-
|
|
444
|
+
cpuThrottling = Util.i18n.strings.runtimeUnknown;
|
|
445
|
+
networkThrottling = Util.i18n.strings.runtimeUnknown;
|
|
437
446
|
}
|
|
438
447
|
|
|
439
448
|
// TODO(paulirish): revise Runtime Settings strings: https://github.com/GoogleChrome/lighthouse/pull/11796
|
|
@@ -446,7 +455,6 @@ export class Util {
|
|
|
446
455
|
deviceEmulation,
|
|
447
456
|
cpuThrottling,
|
|
448
457
|
networkThrottling,
|
|
449
|
-
summary,
|
|
450
458
|
};
|
|
451
459
|
}
|
|
452
460
|
|
|
@@ -629,12 +637,22 @@ Util.UIStrings = {
|
|
|
629
637
|
/** Option in a dropdown menu that toggles the themeing of the report between Light(default) and Dark themes. */
|
|
630
638
|
dropdownDarkTheme: 'Toggle Dark Theme',
|
|
631
639
|
|
|
640
|
+
/** Title of the Runtime settings table in a Lighthouse report. Runtime settings are the environment configurations that a specific report used at auditing time. */
|
|
641
|
+
runtimeSettingsTitle: 'Runtime Settings',
|
|
642
|
+
/** Label for a row in a table that shows the URL that was audited during a Lighthouse run. */
|
|
643
|
+
runtimeSettingsUrl: 'URL',
|
|
644
|
+
/** Label for a row in a table that shows the time at which a Lighthouse run was conducted; formatted as a timestamp, e.g. Jan 1, 1970 12:00 AM UTC. */
|
|
645
|
+
runtimeSettingsFetchTime: 'Fetch Time',
|
|
632
646
|
/** Label for a row in a table that describes the kind of device that was emulated for the Lighthouse run. Example values for row elements: 'No Emulation', 'Emulated Desktop', etc. */
|
|
633
647
|
runtimeSettingsDevice: 'Device',
|
|
634
648
|
/** Label for a row in a table that describes the network throttling conditions that were used during a Lighthouse run, if any. */
|
|
635
649
|
runtimeSettingsNetworkThrottling: 'Network throttling',
|
|
636
650
|
/** Label for a row in a table that describes the CPU throttling conditions that were used during a Lighthouse run, if any.*/
|
|
637
651
|
runtimeSettingsCPUThrottling: 'CPU throttling',
|
|
652
|
+
/** Label for a row in a table that shows in what tool Lighthouse is being run (e.g. The lighthouse CLI, Chrome DevTools, Lightrider, WebPageTest, etc). */
|
|
653
|
+
runtimeSettingsChannel: 'Channel',
|
|
654
|
+
/** Label for a row in a table that shows the User Agent that was detected on the Host machine that ran Lighthouse. */
|
|
655
|
+
runtimeSettingsUA: 'User agent (host)',
|
|
638
656
|
/** Label for a row in a table that shows the User Agent that was used to send out all network requests during the Lighthouse run. */
|
|
639
657
|
runtimeSettingsUANetwork: 'User agent (network)',
|
|
640
658
|
/** Label for a row in a table that shows the estimated CPU power of the machine running Lighthouse. Example row values: 532, 1492, 783. */
|
|
@@ -653,12 +671,6 @@ Util.UIStrings = {
|
|
|
653
671
|
runtimeDesktopEmulation: 'Emulated Desktop',
|
|
654
672
|
/** Descriptive explanation for a runtime setting that is set to an unknown value. */
|
|
655
673
|
runtimeUnknown: 'Unknown',
|
|
656
|
-
/** Descriptive label that this analysis run was from a single pageload of a browser (not a summary of hundreds of loads) */
|
|
657
|
-
runtimeSingleLoad: 'Single page load',
|
|
658
|
-
/** Descriptive label that this analysis only considers the initial load of the page, and no interaction beyond when the page had "fully loaded" */
|
|
659
|
-
runtimeAnalysisWindow: 'Initial page load',
|
|
660
|
-
/** Descriptive explanation that this analysis run was from a single pageload of a browser, whereas field data often summarizes hundreds+ of page loads */
|
|
661
|
-
runtimeSingleLoadTooltip: 'This data is taken from a single page load, as opposed to field data summarizing many sessions.', // eslint-disable-line max-len
|
|
662
674
|
|
|
663
675
|
/** Descriptive explanation for environment throttling that was provided by the runtime environment instead of provided by Lighthouse throttling. */
|
|
664
676
|
throttlingProvided: 'Provided by environment',
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license Copyright 2017 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
|
+
'use strict';
|
|
7
|
+
|
|
8
|
+
import {strict as assert} from 'assert';
|
|
9
|
+
import fs from 'fs';
|
|
10
|
+
|
|
11
|
+
import jsdom from 'jsdom';
|
|
12
|
+
|
|
13
|
+
import testUtils from '../../../lighthouse-core/test/test-utils.js';
|
|
14
|
+
import {prepareLabData} from '../../clients/psi.js';
|
|
15
|
+
import {Util} from '../../renderer/util.js';
|
|
16
|
+
import {I18n} from '../../renderer/i18n.js';
|
|
17
|
+
import {DOM} from '../../renderer/dom.js';
|
|
18
|
+
import {CategoryRenderer} from '../../renderer/category-renderer.js';
|
|
19
|
+
import {PerformanceCategoryRenderer} from '../../renderer/performance-category-renderer.js';
|
|
20
|
+
import {DetailsRenderer} from '../../renderer/details-renderer.js';
|
|
21
|
+
import {CriticalRequestChainRenderer} from '../../renderer/crc-details-renderer.js';
|
|
22
|
+
import {ElementScreenshotRenderer} from '../../renderer/element-screenshot-renderer.js';
|
|
23
|
+
import {ReportUIFeatures} from '../../renderer/report-ui-features.js';
|
|
24
|
+
import {LH_ROOT} from '../../../root.js';
|
|
25
|
+
|
|
26
|
+
const {itIfProtoExists, sampleResultsRoundtripStr} = testUtils.getProtoRoundTrip();
|
|
27
|
+
const sampleResultsStr =
|
|
28
|
+
fs.readFileSync(LH_ROOT + '/lighthouse-core/test/results/sample_v2.json', 'utf-8');
|
|
29
|
+
|
|
30
|
+
/* eslint-env jest */
|
|
31
|
+
|
|
32
|
+
describe('PSI', () => {
|
|
33
|
+
let document;
|
|
34
|
+
beforeAll(() => {
|
|
35
|
+
global.Util = Util;
|
|
36
|
+
global.I18n = I18n;
|
|
37
|
+
global.DOM = DOM;
|
|
38
|
+
global.CategoryRenderer = CategoryRenderer;
|
|
39
|
+
global.DetailsRenderer = DetailsRenderer;
|
|
40
|
+
global.PerformanceCategoryRenderer = PerformanceCategoryRenderer;
|
|
41
|
+
global.CriticalRequestChainRenderer = CriticalRequestChainRenderer;
|
|
42
|
+
global.ElementScreenshotRenderer = ElementScreenshotRenderer;
|
|
43
|
+
global.ReportUIFeatures = ReportUIFeatures;
|
|
44
|
+
|
|
45
|
+
const {window} = new jsdom.JSDOM();
|
|
46
|
+
document = window.document;
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
afterAll(() => {
|
|
50
|
+
global.I18n = undefined;
|
|
51
|
+
global.Util = undefined;
|
|
52
|
+
global.DOM = undefined;
|
|
53
|
+
global.CategoryRenderer = undefined;
|
|
54
|
+
global.DetailsRenderer = undefined;
|
|
55
|
+
global.PerformanceCategoryRenderer = undefined;
|
|
56
|
+
global.CriticalRequestChainRenderer = undefined;
|
|
57
|
+
global.ElementScreenshotRenderer = undefined;
|
|
58
|
+
global.ReportUIFeatures = undefined;
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
describe('psi prepareLabData helpers', () => {
|
|
62
|
+
describe('prepareLabData', () => {
|
|
63
|
+
itIfProtoExists('succeeds with LHResult object (roundtrip) input', () => {
|
|
64
|
+
const roundTripLHResult = /** @type {LH.Result} */ JSON.parse(sampleResultsRoundtripStr);
|
|
65
|
+
const result = prepareLabData(roundTripLHResult, document);
|
|
66
|
+
|
|
67
|
+
// Check that the report exists and has some content.
|
|
68
|
+
assert.ok(result.perfCategoryEl instanceof document.defaultView.Element);
|
|
69
|
+
assert.ok(result.perfCategoryEl.outerHTML.length > 50000, 'perfCategory HTML is populated');
|
|
70
|
+
// Assume using default locale.
|
|
71
|
+
const title = result.perfCategoryEl.querySelector('.lh-audit-group--metrics')
|
|
72
|
+
.querySelector('.lh-audit-group__title').textContent;
|
|
73
|
+
assert.equal(title, Util.UIStrings.labDataTitle);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it('succeeds with stringified LHResult input', () => {
|
|
77
|
+
const result = prepareLabData(sampleResultsStr, document);
|
|
78
|
+
assert.ok(result.scoreGaugeEl instanceof document.defaultView.Element);
|
|
79
|
+
assert.equal(result.scoreGaugeEl.querySelector('.lh-gauge__wrapper').href, '');
|
|
80
|
+
assert.ok(result.scoreGaugeEl.outerHTML.includes('<svg'), 'score gauge comes with SVG');
|
|
81
|
+
|
|
82
|
+
assert.ok(result.perfCategoryEl instanceof document.defaultView.Element);
|
|
83
|
+
assert.ok(result.perfCategoryEl.outerHTML.length > 50000, 'perfCategory HTML is populated');
|
|
84
|
+
|
|
85
|
+
assert.equal(typeof result.finalScreenshotDataUri, 'string');
|
|
86
|
+
assert.ok(result.finalScreenshotDataUri.startsWith('data:image/jpeg;base64,'));
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it('throws if there is no perf category', () => {
|
|
90
|
+
const lhrWithoutPerf = JSON.parse(sampleResultsStr);
|
|
91
|
+
delete lhrWithoutPerf.categories.performance;
|
|
92
|
+
const lhrWithoutPerfStr = JSON.stringify(lhrWithoutPerf);
|
|
93
|
+
|
|
94
|
+
assert.throws(() => {
|
|
95
|
+
prepareLabData(lhrWithoutPerfStr, document);
|
|
96
|
+
}, /no performance category/i);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it('throws if there is no category groups', () => {
|
|
100
|
+
const lhrWithoutGroups = JSON.parse(sampleResultsStr);
|
|
101
|
+
delete lhrWithoutGroups.categoryGroups;
|
|
102
|
+
const lhrWithoutGroupsStr = JSON.stringify(lhrWithoutGroups);
|
|
103
|
+
|
|
104
|
+
assert.throws(() => {
|
|
105
|
+
prepareLabData(lhrWithoutGroupsStr, document);
|
|
106
|
+
}, /no category groups/i);
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it('includes custom title and description', () => {
|
|
110
|
+
const {perfCategoryEl} = prepareLabData(sampleResultsStr, document);
|
|
111
|
+
const metricsGroupEl = perfCategoryEl.querySelector('.lh-audit-group--metrics');
|
|
112
|
+
|
|
113
|
+
// Assume using default locale.
|
|
114
|
+
// Replacing markdown because ".textContent" will be post-markdown.
|
|
115
|
+
const expectedDescription = Util.UIStrings.lsPerformanceCategoryDescription
|
|
116
|
+
.replace('[Lighthouse](https://developers.google.com/web/tools/lighthouse/)', 'Lighthouse');
|
|
117
|
+
|
|
118
|
+
// Assume using default locale.
|
|
119
|
+
const title = metricsGroupEl.querySelector('.lh-audit-group__title').textContent;
|
|
120
|
+
const description =
|
|
121
|
+
metricsGroupEl.querySelector('.lh-audit-group__description').textContent;
|
|
122
|
+
assert.equal(title, Util.UIStrings.labDataTitle);
|
|
123
|
+
assert.equal(description, expectedDescription);
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
describe('_getFinalScreenshot', () => {
|
|
129
|
+
it('gets a datauri as a string', () => {
|
|
130
|
+
const datauri = prepareLabData(sampleResultsStr, document).finalScreenshotDataUri;
|
|
131
|
+
assert.equal(typeof datauri, 'string');
|
|
132
|
+
assert.ok(datauri.startsWith('data:image/jpeg;base64,'));
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
it('returns null if there is no final-screenshot audit', () => {
|
|
136
|
+
const clonedResults = JSON.parse(sampleResultsStr);
|
|
137
|
+
delete clonedResults.audits['final-screenshot'];
|
|
138
|
+
const LHResultJsonString = JSON.stringify(clonedResults);
|
|
139
|
+
const datauri = prepareLabData(LHResultJsonString, document).finalScreenshotDataUri;
|
|
140
|
+
assert.equal(datauri, null);
|
|
141
|
+
});
|
|
142
|
+
});
|
|
143
|
+
});
|
|
@@ -409,10 +409,6 @@ describe('CategoryRenderer', () => {
|
|
|
409
409
|
const passingRefs = categoryClone.auditRefs.filter(ref => ref.result.score === 1);
|
|
410
410
|
passingRefs[0].result.warnings = ['Some warning'];
|
|
411
411
|
passingRefs[1].result.warnings = ['Some warning'];
|
|
412
|
-
// Make one audit n/a
|
|
413
|
-
const audit = categoryClone.auditRefs.find(ref => ref.id === 'themed-omnibox');
|
|
414
|
-
audit.result.scoreDisplayMode = 'notApplicable';
|
|
415
|
-
audit.result.score = null;
|
|
416
412
|
|
|
417
413
|
const elem = renderer.render(categoryClone, sampleResults.categoryGroups);
|
|
418
414
|
const passedAudits = elem.querySelectorAll('.lh-clump--passed .lh-audit');
|
|
@@ -422,7 +418,7 @@ describe('CategoryRenderer', () => {
|
|
|
422
418
|
const naAudits = elem.querySelectorAll('.lh-clump--notapplicable .lh-audit');
|
|
423
419
|
|
|
424
420
|
assert.equal(passedAudits.length, 0);
|
|
425
|
-
assert.equal(failedAudits.length,
|
|
421
|
+
assert.equal(failedAudits.length, 6);
|
|
426
422
|
assert.equal(warningAudits.length, 2);
|
|
427
423
|
assert.equal(manualAudits.length, 3);
|
|
428
424
|
assert.equal(naAudits.length, 1);
|
|
@@ -9,6 +9,7 @@ import {strict as assert} from 'assert';
|
|
|
9
9
|
|
|
10
10
|
import {Util} from '../../renderer/util.js';
|
|
11
11
|
import {I18n} from '../../renderer/i18n.js';
|
|
12
|
+
import {isNode12SmallIcu} from '../../../lighthouse-core/test/test-utils.js';
|
|
12
13
|
|
|
13
14
|
// Require i18n to make sure Intl is polyfilled in Node without full-icu for testing.
|
|
14
15
|
// When Util is run in a browser, Intl will be supplied natively (IE11+).
|
|
@@ -107,6 +108,9 @@ describe('util helpers', () => {
|
|
|
107
108
|
});
|
|
108
109
|
|
|
109
110
|
it('formats numbers based on locale', () => {
|
|
111
|
+
// COMPAT: Node 12 only has 'en' by default.
|
|
112
|
+
if (isNode12SmallIcu()) return;
|
|
113
|
+
|
|
110
114
|
// Requires full-icu or Intl polyfill.
|
|
111
115
|
const number = 12346.858558;
|
|
112
116
|
|
|
@@ -118,6 +122,9 @@ describe('util helpers', () => {
|
|
|
118
122
|
});
|
|
119
123
|
|
|
120
124
|
it('uses decimal comma with en-XA test locale', () => {
|
|
125
|
+
// COMPAT: Node 12 only has 'en' by default.
|
|
126
|
+
if (isNode12SmallIcu()) return;
|
|
127
|
+
|
|
121
128
|
// Requires full-icu or Intl polyfill.
|
|
122
129
|
const number = 12346.858558;
|
|
123
130
|
|
|
@@ -84,7 +84,7 @@ describe('PerfCategoryRenderer', () => {
|
|
|
84
84
|
it('renders the metrics variance disclaimer as markdown', () => {
|
|
85
85
|
const categoryDOM = renderer.render(category, sampleResults.categoryGroups);
|
|
86
86
|
const disclaimerEl =
|
|
87
|
-
categoryDOM.querySelector('.lh-
|
|
87
|
+
categoryDOM.querySelector('.lh-audit-group--metrics > .lh-metrics__disclaimer');
|
|
88
88
|
|
|
89
89
|
assert.ok(disclaimerEl.textContent.includes('Values are estimated'));
|
|
90
90
|
const disclamerLink = disclaimerEl.querySelector('a');
|
|
@@ -107,12 +107,12 @@ describe('PwaCategoryRenderer', () => {
|
|
|
107
107
|
const clone = JSON.parse(JSON.stringify(sampleResults));
|
|
108
108
|
const category = clone.categories.pwa;
|
|
109
109
|
|
|
110
|
-
// Set everything to passing, except
|
|
110
|
+
// Set everything to passing, except redirects-http set to n/a (as it is on localhost)
|
|
111
111
|
for (const auditRef of category.auditRefs) {
|
|
112
112
|
auditRef.result.score = 1;
|
|
113
113
|
auditRef.result.scoreDisplayMode = 'binary';
|
|
114
114
|
}
|
|
115
|
-
const audit = category.auditRefs.find(ref => ref.id === '
|
|
115
|
+
const audit = category.auditRefs.find(ref => ref.id === 'redirects-http');
|
|
116
116
|
audit.result.scoreDisplayMode = 'notApplicable';
|
|
117
117
|
audit.result.score = null;
|
|
118
118
|
|
|
@@ -46,7 +46,7 @@ describe('ReportRendererAxe', () => {
|
|
|
46
46
|
global.Element = undefined;
|
|
47
47
|
});
|
|
48
48
|
|
|
49
|
-
it('renders without axe violations',
|
|
49
|
+
it('renders without axe violations', () => {
|
|
50
50
|
const container = renderer._dom._document.createElement('main');
|
|
51
51
|
const output = renderer.renderReport(sampleResults, container);
|
|
52
52
|
|
|
@@ -70,11 +70,16 @@ describe('ReportRendererAxe', () => {
|
|
|
70
70
|
},
|
|
71
71
|
};
|
|
72
72
|
|
|
73
|
-
|
|
74
|
-
|
|
73
|
+
return new Promise(resolve => {
|
|
74
|
+
axe.run(output, config, (error, {violations}) => {
|
|
75
|
+
expect(error).toBeNull();
|
|
76
|
+
expect(violations).toEqual([]);
|
|
77
|
+
resolve();
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
// This test takes 40s on fast hardware, and 50-60s on GHA.
|
|
81
|
+
// https://github.com/dequelabs/axe-core/tree/b573b1c1/doc/examples/jest_react#timeout-issues
|
|
75
82
|
},
|
|
76
|
-
// This test takes 40s on fast hardware, and 50-60s on GHA.
|
|
77
|
-
// https://github.com/dequelabs/axe-core/tree/b573b1c1/doc/examples/jest_react#timeout-issues
|
|
78
83
|
/* timeout= */ 100 * 1000
|
|
79
84
|
);
|
|
80
85
|
});
|
|
@@ -179,15 +179,17 @@ describe('ReportRenderer', () => {
|
|
|
179
179
|
assert.ok(/Generated by Lighthouse \d/.test(footerContent), 'includes lh version');
|
|
180
180
|
assert.ok(footerContent.match(TIMESTAMP_REGEX), 'includes timestamp');
|
|
181
181
|
|
|
182
|
-
// Check
|
|
183
|
-
const
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
expect(
|
|
190
|
-
expect(
|
|
182
|
+
// Check runtime settings were populated.
|
|
183
|
+
const names = Array.from(footer.querySelectorAll('.lh-env__name'));
|
|
184
|
+
const descriptions = Array.from(footer.querySelectorAll('.lh-env__description'));
|
|
185
|
+
assert.ok(names.length >= 3);
|
|
186
|
+
assert.ok(descriptions.length >= 3);
|
|
187
|
+
|
|
188
|
+
const descriptionsTxt = descriptions.map(el => el.textContent).join('\n');
|
|
189
|
+
expect(descriptionsTxt).toContain('Moto G4');
|
|
190
|
+
expect(descriptionsTxt).toContain('RTT');
|
|
191
|
+
expect(descriptionsTxt).toMatch(/\dx/);
|
|
192
|
+
expect(descriptionsTxt).toContain(sampleResults.userAgent);
|
|
191
193
|
});
|
|
192
194
|
});
|
|
193
195
|
|
|
@@ -92,7 +92,10 @@ body {
|
|
|
92
92
|
<body >
|
|
93
93
|
<noscript>PSI requires JavaScript. Please enable.</noscript>
|
|
94
94
|
|
|
95
|
-
<div class="
|
|
95
|
+
<div class="element-screenshots-container"></div>
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
<div class="tabset lh-vars lh-root">
|
|
96
99
|
|
|
97
100
|
<input type="radio" name="tabset" id="tab1" aria-controls="mobile" checked>
|
|
98
101
|
<label for="tab1">Mobile</label>
|
|
@@ -5,13 +5,9 @@
|
|
|
5
5
|
*/
|
|
6
6
|
'use strict';
|
|
7
7
|
|
|
8
|
-
/** @fileoverview This file
|
|
8
|
+
/** @fileoverview This file is a glorified call of prepareLabData. */
|
|
9
9
|
|
|
10
|
-
/** @typedef {import('../clients/
|
|
11
|
-
|
|
12
|
-
/** @type {lighthouseRenderer} */
|
|
13
|
-
// @ts-expect-error
|
|
14
|
-
const lighthouseRenderer = window['report'];
|
|
10
|
+
/** @typedef {import('../clients/psi.js').PrepareLabDataResult} PrepareLabDataResult */
|
|
15
11
|
|
|
16
12
|
(async function __initPsiReports__() {
|
|
17
13
|
// @ts-expect-error
|
|
@@ -26,43 +22,24 @@ const lighthouseRenderer = window['report'];
|
|
|
26
22
|
for (const [tabId, lhr] of Object.entries(lhrs)) {
|
|
27
23
|
await distinguishLHR(lhr, tabId);
|
|
28
24
|
|
|
25
|
+
// @ts-expect-error
|
|
26
|
+
const pldd = /** @type {PrepareLabDataResult} */ (window.prepareLabData(lhr, document));
|
|
27
|
+
const {scoreGaugeEl, perfCategoryEl,
|
|
28
|
+
finalScreenshotDataUri, scoreScaleEl, installFeatures} = pldd;
|
|
29
|
+
|
|
29
30
|
const container = document.querySelector(`#${tabId} main`);
|
|
30
31
|
if (!container) throw new Error('Unexpected DOM. Bailing.');
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
* @param {LH.Result} lhrData
|
|
38
|
-
* @param {HTMLElement} reportContainer
|
|
39
|
-
*/
|
|
40
|
-
function renderLHReport(lhrData, reportContainer) {
|
|
41
|
-
/**
|
|
42
|
-
* @param {Document} doc
|
|
43
|
-
*/
|
|
44
|
-
function getRenderer(doc) {
|
|
45
|
-
const dom = new lighthouseRenderer.DOM(doc);
|
|
46
|
-
return new lighthouseRenderer.ReportRenderer(dom);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
const renderer = getRenderer(reportContainer.ownerDocument);
|
|
50
|
-
reportContainer.classList.add('lh-root', 'lh-vars');
|
|
51
|
-
|
|
52
|
-
try {
|
|
53
|
-
renderer.renderReport(lhrData, reportContainer);
|
|
54
|
-
// TODO: handle topbar removal better
|
|
55
|
-
// TODO: display warnings if appropriate.
|
|
56
|
-
for (const el of reportContainer.querySelectorAll('.lh-topbar, .lh-warnings')) {
|
|
57
|
-
el.setAttribute('hidden', 'true');
|
|
32
|
+
container.append(scoreGaugeEl);
|
|
33
|
+
container.append(scoreScaleEl);
|
|
34
|
+
if (finalScreenshotDataUri) {
|
|
35
|
+
const imgEl = document.createElement('img');
|
|
36
|
+
imgEl.src = finalScreenshotDataUri;
|
|
37
|
+
container.append(imgEl);
|
|
58
38
|
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
} catch (e) {
|
|
62
|
-
console.error(e);
|
|
63
|
-
reportContainer.textContent = 'Error: LHR failed to render.';
|
|
39
|
+
container.append(perfCategoryEl);
|
|
40
|
+
installFeatures(container);
|
|
64
41
|
}
|
|
65
|
-
}
|
|
42
|
+
})();
|
|
66
43
|
|
|
67
44
|
|
|
68
45
|
/**
|