lighthouse 8.6.0 → 9.0.0-dev.20211115
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/changelog.md +121 -0
- package/dist/report/bundle.esm.js +5994 -0
- package/dist/report/flow.js +30 -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 +108 -31
- package/flow-report/src/app.tsx +18 -18
- package/flow-report/src/common.tsx +28 -15
- package/flow-report/src/header.tsx +12 -13
- package/flow-report/src/help-dialog.tsx +3 -4
- package/flow-report/src/i18n/i18n.tsx +57 -16
- package/flow-report/src/i18n/ui-strings.js +50 -0
- package/flow-report/src/icons.tsx +36 -8
- package/flow-report/src/sidebar/flow.tsx +3 -3
- package/flow-report/src/sidebar/sidebar.tsx +35 -14
- package/flow-report/src/summary/category.tsx +97 -24
- package/flow-report/src/summary/summary.tsx +21 -14
- package/flow-report/src/topbar.tsx +9 -5
- package/flow-report/src/util.ts +33 -26
- package/flow-report/src/wrappers/category-score.tsx +0 -3
- package/flow-report/src/wrappers/markdown.tsx +27 -0
- package/flow-report/src/wrappers/report-renderer.tsx +10 -3
- package/flow-report/src/wrappers/report.tsx +27 -6
- package/flow-report/standalone-flow.tsx +4 -3
- package/flow-report/test/common-test.tsx +32 -17
- package/flow-report/test/flow-report-pptr-test.ts +46 -0
- 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 +120 -24
- package/flow-report/test/summary/summary-test.tsx +1 -1
- package/flow-report/test/topbar-test.tsx +1 -0
- package/flow-report/test/util-test.tsx +32 -11
- package/flow-report/test/wrappers/category-score-test.tsx +87 -0
- package/flow-report/test/wrappers/markdown-test.tsx +29 -0
- package/flow-report/tsconfig.json +1 -0
- package/jest.config.js +4 -2
- package/lighthouse-cli/.eslintrc.cjs +2 -0
- package/lighthouse-cli/cli-flags.js +1 -1
- package/lighthouse-cli/run.js +1 -1
- package/lighthouse-cli/test/smokehouse/lighthouse-runners/bundle.js +4 -2
- package/lighthouse-cli/test/smokehouse/lighthouse-runners/cli.js +1 -1
- package/lighthouse-cli/test/smokehouse/smokehouse.js +1 -1
- package/lighthouse-core/audits/accessibility/axe-audit.js +7 -1
- package/lighthouse-core/audits/audit.js +1 -1
- package/lighthouse-core/audits/byte-efficiency/byte-efficiency-audit.js +2 -0
- package/lighthouse-core/audits/byte-efficiency/modern-image-formats.js +4 -2
- package/lighthouse-core/audits/byte-efficiency/offscreen-images.js +11 -10
- package/lighthouse-core/audits/byte-efficiency/uses-optimized-images.js +5 -3
- package/lighthouse-core/audits/byte-efficiency/uses-responsive-images-snapshot.js +2 -1
- package/lighthouse-core/audits/byte-efficiency/uses-responsive-images.js +2 -1
- package/lighthouse-core/audits/deprecations.js +33 -11
- package/lighthouse-core/audits/dobetterweb/uses-http2.js +27 -11
- package/lighthouse-core/audits/errors-in-console.js +0 -2
- package/lighthouse-core/audits/image-aspect-ratio.js +5 -5
- package/lighthouse-core/audits/image-size-responsive.js +6 -6
- package/lighthouse-core/audits/installable-manifest.js +5 -3
- package/lighthouse-core/audits/preload-lcp-image.js +7 -5
- package/lighthouse-core/audits/script-treemap-data.js +1 -1
- package/lighthouse-core/audits/unsized-images.js +2 -3
- package/lighthouse-core/computed/image-records.js +4 -3
- 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 +21 -10
- package/lighthouse-core/config/constants.js +3 -4
- package/lighthouse-core/config/default-config.js +61 -71
- package/lighthouse-core/fraggle-rock/config/config.js +1 -1
- package/lighthouse-core/fraggle-rock/config/default-config.js +4 -5
- package/lighthouse-core/fraggle-rock/gather/base-gatherer.js +6 -1
- package/lighthouse-core/fraggle-rock/gather/session.js +3 -4
- package/lighthouse-core/gather/connections/cri.js +1 -1
- package/lighthouse-core/gather/driver/storage.js +0 -1
- package/lighthouse-core/gather/driver.js +3 -7
- package/lighthouse-core/gather/fetcher.js +4 -7
- package/lighthouse-core/gather/gather-runner.js +29 -6
- package/lighthouse-core/gather/gatherers/accessibility.js +27 -0
- package/lighthouse-core/gather/gatherers/dobetterweb/response-compression.js +1 -0
- package/lighthouse-core/gather/gatherers/image-elements.js +0 -3
- package/lighthouse-core/gather/gatherers/inspector-issues.js +5 -0
- package/lighthouse-core/lib/asset-saver.js +7 -4
- package/lighthouse-core/lib/emulation.js +45 -1
- package/lighthouse-core/lib/i18n/i18n.js +19 -8
- 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 +65 -46
- package/package.json +27 -17
- package/readme.md +3 -3
- package/report/.eslintrc.cjs +2 -0
- package/report/README.md +1 -1
- package/report/assets/standalone-template.html +2 -3
- package/report/assets/styles.css +360 -254
- package/report/assets/templates.html +21 -76
- package/report/clients/bundle.js +1 -0
- package/report/clients/standalone.js +6 -15
- package/report/generator/README.md +1 -1
- package/report/renderer/api.js +32 -0
- package/report/renderer/category-renderer.js +59 -19
- package/report/renderer/components.js +59 -110
- 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 +55 -34
- package/report/renderer/pwa-category-renderer.js +0 -10
- package/report/renderer/report-renderer.js +91 -44
- package/report/renderer/report-ui-features.js +36 -33
- package/report/renderer/swap-locale-feature.js +3 -3
- package/report/renderer/topbar-features.js +53 -48
- package/report/renderer/util.js +66 -46
- package/report/test/clients/bundle-test.js +70 -0
- package/report/test/renderer/category-renderer-test.js +48 -3
- package/report/test/renderer/i18n-test.js +0 -7
- package/report/test/renderer/performance-category-renderer-test.js +51 -14
- package/report/test/renderer/pwa-category-renderer-test.js +6 -6
- package/report/test/renderer/report-renderer-axe-test.js +10 -12
- package/report/test/renderer/report-renderer-test.js +23 -22
- package/report/test/renderer/report-ui-features-test.js +20 -10
- package/report/test/renderer/text-encoding-test.js +1 -1
- package/report/test/renderer/util-test.js +35 -31
- package/report/test-assets/faux-psi-template.html +3 -11
- package/report/test-assets/faux-psi.js +26 -22
- package/report/types/html-renderer.d.ts +2 -0
- package/report/types/report-renderer.d.ts +23 -0
- package/shared/localization/format.js +64 -30
- package/shared/localization/locales/ar-XB.json +72 -75
- package/shared/localization/locales/ar.json +72 -75
- package/shared/localization/locales/bg.json +72 -75
- package/shared/localization/locales/ca.json +72 -75
- package/shared/localization/locales/cs.json +89 -92
- package/shared/localization/locales/da.json +72 -75
- package/shared/localization/locales/de.json +89 -92
- package/shared/localization/locales/el.json +72 -75
- package/shared/localization/locales/en-GB.json +72 -75
- package/shared/localization/locales/en-US.json +75 -75
- package/shared/localization/locales/en-XA.json +72 -75
- package/shared/localization/locales/en-XL.json +75 -75
- package/shared/localization/locales/es-419.json +90 -93
- package/shared/localization/locales/es.json +90 -93
- package/shared/localization/locales/fi.json +72 -75
- package/shared/localization/locales/fil.json +72 -75
- package/shared/localization/locales/fr.json +89 -92
- package/shared/localization/locales/he.json +73 -76
- package/shared/localization/locales/hi.json +72 -75
- package/shared/localization/locales/hr.json +72 -75
- package/shared/localization/locales/hu.json +89 -92
- package/shared/localization/locales/id.json +89 -92
- package/shared/localization/locales/it.json +89 -92
- package/shared/localization/locales/ja.json +89 -92
- package/shared/localization/locales/ko.json +89 -92
- package/shared/localization/locales/lt.json +89 -92
- package/shared/localization/locales/lv.json +95 -98
- package/shared/localization/locales/nl.json +89 -92
- package/shared/localization/locales/no.json +72 -75
- package/shared/localization/locales/pl.json +72 -75
- package/shared/localization/locales/pt-PT.json +72 -75
- package/shared/localization/locales/pt.json +89 -92
- package/shared/localization/locales/ro.json +90 -93
- package/shared/localization/locales/ru.json +89 -92
- package/shared/localization/locales/sk.json +89 -92
- package/shared/localization/locales/sl.json +89 -92
- package/shared/localization/locales/sr-Latn.json +89 -92
- package/shared/localization/locales/sr.json +89 -92
- package/shared/localization/locales/sv.json +72 -75
- package/shared/localization/locales/ta.json +89 -92
- package/shared/localization/locales/te.json +89 -92
- package/shared/localization/locales/th.json +89 -92
- package/shared/localization/locales/tr.json +89 -92
- package/shared/localization/locales/uk.json +92 -95
- package/shared/localization/locales/vi.json +89 -92
- package/shared/localization/locales/zh-HK.json +89 -92
- package/shared/localization/locales/zh-TW.json +89 -92
- package/shared/localization/locales/zh.json +89 -92
- package/shared/localization/locales.js +6 -0
- package/shared/test/localization/format-test.js +45 -9
- package/shared/test/localization/swap-locale-test.js +0 -12
- package/third-party/chromium-synchronization/inspector-issueAdded-types-test.js +19 -11
- package/third-party/chromium-synchronization/installability-errors-test.js +2 -0
- package/third-party/download-content-shell/download-content-shell.js +2 -2
- package/third-party/download-content-shell/utils.js +0 -24
- package/tsconfig-all.json +2 -2
- package/tsconfig.json +0 -1
- package/types/artifacts.d.ts +7 -7
- package/types/enquirer.d.ts +3 -1
- package/types/lhr/audit-details.d.ts +6 -5
- package/types/lhr/flow.d.ts +3 -2
- package/types/lhr/lhr.d.ts +1 -1
- package/types/node.d.ts +5 -5
- package/types/rollup-plugin-postprocess.d.ts +10 -0
- package/lighthouse-core/audits/dobetterweb/appcache-manifest.js +0 -67
- package/lighthouse-core/audits/dobetterweb/external-anchors-use-rel-noopener.js +0 -96
- package/lighthouse-core/audits/redirects-http.js +0 -59
- package/lighthouse-core/gather/gatherers/dobetterweb/appcache.js +0 -34
- package/lighthouse-core/gather/gatherers/http-redirect.js +0 -46
- package/lighthouse-core/lib/i18n/locales/en-US.ctc.json +0 -6699
- package/lighthouse-core/lib/i18n/locales/en-XL.ctc.json +0 -6672
- package/report/clients/psi.js +0 -158
- package/report/test/clients/psi-test.js +0 -143
- package/shared/localization/locales/en-US.ctc.json +0 -6877
- package/shared/localization/locales/en-XL.ctc.json +0 -6852
|
@@ -21,12 +21,21 @@
|
|
|
21
21
|
* critical request chains network tree.
|
|
22
22
|
*/
|
|
23
23
|
|
|
24
|
+
import {Util} from './util.js';
|
|
25
|
+
|
|
24
26
|
/** @typedef {import('./dom.js').DOM} DOM */
|
|
25
27
|
/** @typedef {import('./details-renderer.js').DetailsRenderer} DetailsRenderer */
|
|
28
|
+
/**
|
|
29
|
+
* @typedef CRCSegment
|
|
30
|
+
* @property {LH.Audit.Details.SimpleCriticalRequestNode[string]} node
|
|
31
|
+
* @property {boolean} isLastChild
|
|
32
|
+
* @property {boolean} hasChildren
|
|
33
|
+
* @property {number} startTime
|
|
34
|
+
* @property {number} transferSize
|
|
35
|
+
* @property {boolean[]} treeMarkers
|
|
36
|
+
*/
|
|
26
37
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
export class CriticalRequestChainRenderer {
|
|
38
|
+
class CriticalRequestChainRenderer {
|
|
30
39
|
/**
|
|
31
40
|
* Create render context for critical-request-chain tree display.
|
|
32
41
|
* @param {LH.Audit.Details.SimpleCriticalRequestNode} tree
|
|
@@ -191,12 +200,6 @@ export class CriticalRequestChainRenderer {
|
|
|
191
200
|
// Alias b/c the name is really long.
|
|
192
201
|
const CRCRenderer = CriticalRequestChainRenderer;
|
|
193
202
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
hasChildren: boolean,
|
|
198
|
-
startTime: number,
|
|
199
|
-
transferSize: number,
|
|
200
|
-
treeMarkers: Array<boolean>
|
|
201
|
-
}} CRCSegment
|
|
202
|
-
*/
|
|
203
|
+
export {
|
|
204
|
+
CriticalRequestChainRenderer,
|
|
205
|
+
};
|
package/report/renderer/dom.js
CHANGED
|
@@ -27,14 +27,18 @@ import {createComponent} from './components.js';
|
|
|
27
27
|
export class DOM {
|
|
28
28
|
/**
|
|
29
29
|
* @param {Document} document
|
|
30
|
+
* @param {HTMLElement} rootEl
|
|
30
31
|
*/
|
|
31
|
-
constructor(document) {
|
|
32
|
+
constructor(document, rootEl) {
|
|
32
33
|
/** @type {Document} */
|
|
33
34
|
this._document = document;
|
|
34
35
|
/** @type {string} */
|
|
35
36
|
this._lighthouseChannel = 'unknown';
|
|
36
37
|
/** @type {Map<string, DocumentFragment>} */
|
|
37
38
|
this._componentCache = new Map();
|
|
39
|
+
/** @type {HTMLElement} */
|
|
40
|
+
// For legacy Report API users, this'll be undefined, but set in renderReport
|
|
41
|
+
this.rootEl = rootEl;
|
|
38
42
|
}
|
|
39
43
|
|
|
40
44
|
/**
|
|
@@ -76,6 +80,15 @@ export class DOM {
|
|
|
76
80
|
return this._document.createDocumentFragment();
|
|
77
81
|
}
|
|
78
82
|
|
|
83
|
+
/**
|
|
84
|
+
* @param {string} data
|
|
85
|
+
* @return {!Node}
|
|
86
|
+
*/
|
|
87
|
+
createTextNode(data) {
|
|
88
|
+
return this._document.createTextNode(data);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
|
|
79
92
|
/**
|
|
80
93
|
* @template {string} T
|
|
81
94
|
* @param {Element} parentElem
|
|
@@ -216,6 +229,8 @@ export class DOM {
|
|
|
216
229
|
}
|
|
217
230
|
|
|
218
231
|
/**
|
|
232
|
+
* ONLY use if `dom.rootEl` isn't sufficient for your needs. `dom.rootEl` is preferred
|
|
233
|
+
* for all scoping, because a document can have multiple reports within it.
|
|
219
234
|
* @return {Document}
|
|
220
235
|
*/
|
|
221
236
|
document() {
|
|
@@ -36,11 +36,11 @@ export class DropDownMenu {
|
|
|
36
36
|
* @param {function(MouseEvent): any} menuClickHandler
|
|
37
37
|
*/
|
|
38
38
|
setup(menuClickHandler) {
|
|
39
|
-
this._toggleEl = this._dom.find('button.lh-tools__button', this._dom.
|
|
39
|
+
this._toggleEl = this._dom.find('.lh-topbar button.lh-tools__button', this._dom.rootEl);
|
|
40
40
|
this._toggleEl.addEventListener('click', this.onToggleClick);
|
|
41
41
|
this._toggleEl.addEventListener('keydown', this.onToggleKeydown);
|
|
42
42
|
|
|
43
|
-
this._menuEl = this._dom.find('div.lh-tools__dropdown', this._dom.
|
|
43
|
+
this._menuEl = this._dom.find('.lh-topbar div.lh-tools__dropdown', this._dom.rootEl);
|
|
44
44
|
this._menuEl.addEventListener('keydown', this.onMenuKeydown);
|
|
45
45
|
this._menuEl.addEventListener('click', menuClickHandler);
|
|
46
46
|
}
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
/**
|
|
19
19
|
* @typedef InstallOverlayFeatureParams
|
|
20
20
|
* @property {DOM} dom
|
|
21
|
-
* @property {Element}
|
|
21
|
+
* @property {Element} rootEl
|
|
22
22
|
* @property {Element} overlayContainerEl
|
|
23
23
|
* @property {LH.Audit.Details.FullPageScreenshot} fullPageScreenshot
|
|
24
24
|
*/
|
|
@@ -51,7 +51,7 @@ function clamp(value, min, max) {
|
|
|
51
51
|
/**
|
|
52
52
|
* @param {Rect} rect
|
|
53
53
|
*/
|
|
54
|
-
function
|
|
54
|
+
function getElementRectCenterPoint(rect) {
|
|
55
55
|
return {
|
|
56
56
|
x: rect.left + rect.width / 2,
|
|
57
57
|
y: rect.top + rect.height / 2,
|
|
@@ -68,7 +68,7 @@ export class ElementScreenshotRenderer {
|
|
|
68
68
|
* @param {Size} screenshotSize
|
|
69
69
|
*/
|
|
70
70
|
static getScreenshotPositions(elementRectSC, elementPreviewSizeSC, screenshotSize) {
|
|
71
|
-
const elementRectCenter =
|
|
71
|
+
const elementRectCenter = getElementRectCenterPoint(elementRectSC);
|
|
72
72
|
|
|
73
73
|
// Try to center clipped region.
|
|
74
74
|
const screenshotLeftVisibleEdge = clamp(
|
|
@@ -135,7 +135,7 @@ export class ElementScreenshotRenderer {
|
|
|
135
135
|
* @param {LH.Audit.Details.FullPageScreenshot['screenshot']} screenshot
|
|
136
136
|
*/
|
|
137
137
|
static installFullPageScreenshot(el, screenshot) {
|
|
138
|
-
el.style.setProperty('--element-screenshot-url', `url(${screenshot.data})`);
|
|
138
|
+
el.style.setProperty('--element-screenshot-url', `url('${screenshot.data}')`);
|
|
139
139
|
}
|
|
140
140
|
|
|
141
141
|
/**
|
|
@@ -143,14 +143,14 @@ export class ElementScreenshotRenderer {
|
|
|
143
143
|
* @param {InstallOverlayFeatureParams} opts
|
|
144
144
|
*/
|
|
145
145
|
static installOverlayFeature(opts) {
|
|
146
|
-
const {dom,
|
|
146
|
+
const {dom, rootEl, overlayContainerEl, fullPageScreenshot} = opts;
|
|
147
147
|
const screenshotOverlayClass = 'lh-screenshot-overlay--enabled';
|
|
148
148
|
// Don't install the feature more than once.
|
|
149
|
-
if (
|
|
150
|
-
|
|
149
|
+
if (rootEl.classList.contains(screenshotOverlayClass)) return;
|
|
150
|
+
rootEl.classList.add(screenshotOverlayClass);
|
|
151
151
|
|
|
152
152
|
// Add a single listener to the provided element to handle all clicks within (event delegation).
|
|
153
|
-
|
|
153
|
+
rootEl.addEventListener('click', e => {
|
|
154
154
|
const target = /** @type {?HTMLElement} */ (e.target);
|
|
155
155
|
if (!target) return;
|
|
156
156
|
// Only activate the overlay for clicks on the screenshot *preview* of an element, not the full-size too.
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
* @param {boolean} [force]
|
|
15
15
|
*/
|
|
16
16
|
export function toggleDarkTheme(dom, force) {
|
|
17
|
-
const el = dom.
|
|
17
|
+
const el = dom.rootEl;
|
|
18
18
|
// This seems unnecessary, but in DevTools, passing "undefined" as the second
|
|
19
19
|
// parameter acts like passing "false".
|
|
20
20
|
// https://github.com/ChromeDevTools/devtools-frontend/blob/dd6a6d4153647c2a4203c327c595692c5e0a4256/front_end/dom_extension/DOMExtension.js#L809-L819
|
|
@@ -90,7 +90,7 @@ async function openTabWithUrlData(data, url_, windowName) {
|
|
|
90
90
|
* @param {LH.Result} lhr
|
|
91
91
|
* @protected
|
|
92
92
|
*/
|
|
93
|
-
|
|
93
|
+
async function openViewer(lhr) {
|
|
94
94
|
const windowName = 'viewer-' + computeWindowNameSuffix(lhr);
|
|
95
95
|
const url = getAppsOrigin() + '/viewer/';
|
|
96
96
|
await openTabWithUrlData({lhr}, url, windowName);
|
|
@@ -101,7 +101,7 @@ export async function openViewer(lhr) {
|
|
|
101
101
|
* @param {LH.Result} lhr
|
|
102
102
|
* @protected
|
|
103
103
|
*/
|
|
104
|
-
|
|
104
|
+
async function openViewerAndSendData(lhr) {
|
|
105
105
|
const windowName = 'viewer-' + computeWindowNameSuffix(lhr);
|
|
106
106
|
const url = getAppsOrigin() + '/viewer/';
|
|
107
107
|
openTabAndSendData({lhr}, url, windowName);
|
|
@@ -111,7 +111,7 @@ export async function openViewerAndSendData(lhr) {
|
|
|
111
111
|
* Opens a new tab to the treemap app and sends the JSON results using URL.fragment
|
|
112
112
|
* @param {LH.Result} json
|
|
113
113
|
*/
|
|
114
|
-
|
|
114
|
+
function openTreemap(json) {
|
|
115
115
|
const treemapData = json.audits['script-treemap-data'].details;
|
|
116
116
|
if (!treemapData) {
|
|
117
117
|
throw new Error('no script treemap data found');
|
|
@@ -135,3 +135,9 @@ export function openTreemap(json) {
|
|
|
135
135
|
|
|
136
136
|
openTabWithUrlData(treemapOptions, url, windowName);
|
|
137
137
|
}
|
|
138
|
+
|
|
139
|
+
export {
|
|
140
|
+
openViewer,
|
|
141
|
+
openViewerAndSendData,
|
|
142
|
+
openTreemap,
|
|
143
|
+
};
|
|
@@ -152,42 +152,62 @@ export class PerformanceCategoryRenderer extends CategoryRenderer {
|
|
|
152
152
|
return url.href;
|
|
153
153
|
}
|
|
154
154
|
|
|
155
|
+
/**
|
|
156
|
+
* For performance, audits with no group should be a diagnostic or opportunity.
|
|
157
|
+
* The audit details type will determine which of the two groups an audit is in.
|
|
158
|
+
*
|
|
159
|
+
* @param {LH.ReportResult.AuditRef} audit
|
|
160
|
+
* @return {'load-opportunity'|'diagnostic'|null}
|
|
161
|
+
*/
|
|
162
|
+
_classifyPerformanceAudit(audit) {
|
|
163
|
+
if (audit.group) return null;
|
|
164
|
+
if (audit.result.details && audit.result.details.type === 'opportunity') {
|
|
165
|
+
return 'load-opportunity';
|
|
166
|
+
}
|
|
167
|
+
return 'diagnostic';
|
|
168
|
+
}
|
|
169
|
+
|
|
155
170
|
/**
|
|
156
171
|
* @param {LH.ReportResult.Category} category
|
|
157
172
|
* @param {Object<string, LH.Result.ReportGroup>} groups
|
|
158
|
-
* @param {{gatherMode: LH.Result.GatherMode
|
|
173
|
+
* @param {{gatherMode: LH.Result.GatherMode}=} options
|
|
159
174
|
* @return {Element}
|
|
160
175
|
* @override
|
|
161
176
|
*/
|
|
162
177
|
render(category, groups, options) {
|
|
163
178
|
const strings = Util.i18n.strings;
|
|
164
179
|
const element = this.dom.createElement('div', 'lh-category');
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
gaugeEl.appendChild(this.renderCategoryScore(category, groups, options));
|
|
168
|
-
element.appendChild(gaugeEl);
|
|
169
|
-
} else {
|
|
170
|
-
element.id = category.id;
|
|
171
|
-
element.appendChild(this.renderCategoryHeader(category, groups, options));
|
|
172
|
-
}
|
|
180
|
+
element.id = category.id;
|
|
181
|
+
element.appendChild(this.renderCategoryHeader(category, groups, options));
|
|
173
182
|
|
|
174
183
|
// Metrics.
|
|
175
184
|
const metricAudits = category.auditRefs.filter(audit => audit.group === 'metrics');
|
|
176
185
|
if (metricAudits.length) {
|
|
177
|
-
const
|
|
186
|
+
const [metricsGroupEl, metricsFooterEl] = this.renderAuditGroup(groups.metrics);
|
|
178
187
|
|
|
179
188
|
// Metric descriptions toggle.
|
|
180
|
-
const
|
|
181
|
-
const
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
189
|
+
const checkboxEl = this.dom.createElement('input', 'lh-metrics-toggle__input');
|
|
190
|
+
const checkboxId = `lh-metrics-toggle${Util.getUniqueSuffix()}`;
|
|
191
|
+
checkboxEl.setAttribute('aria-label', 'Toggle the display of metric descriptions');
|
|
192
|
+
checkboxEl.type = 'checkbox';
|
|
193
|
+
checkboxEl.id = checkboxId;
|
|
194
|
+
metricsGroupEl.prepend(checkboxEl);
|
|
195
|
+
const metricHeaderEl = this.dom.find('.lh-audit-group__header', metricsGroupEl);
|
|
196
|
+
const labelEl = this.dom.createChildOf(metricHeaderEl, 'label', 'lh-metrics-toggle__label');
|
|
197
|
+
labelEl.htmlFor = checkboxId;
|
|
198
|
+
const showEl = this.dom.createChildOf(labelEl, 'span', 'lh-metrics-toggle__labeltext--show');
|
|
199
|
+
const hideEl = this.dom.createChildOf(labelEl, 'span', 'lh-metrics-toggle__labeltext--hide');
|
|
200
|
+
showEl.textContent = Util.i18n.strings.expandView;
|
|
201
|
+
hideEl.textContent = Util.i18n.strings.collapseView;
|
|
202
|
+
|
|
203
|
+
const metricsBoxesEl = this.dom.createElement('div', 'lh-metrics-container');
|
|
204
|
+
metricsGroupEl.insertBefore(metricsBoxesEl, metricsFooterEl);
|
|
186
205
|
metricAudits.forEach(item => {
|
|
187
206
|
metricsBoxesEl.appendChild(this._renderMetric(item));
|
|
188
207
|
});
|
|
189
208
|
|
|
190
|
-
const
|
|
209
|
+
const descriptionEl = this.dom.find('.lh-category-header__description', element);
|
|
210
|
+
const estValuesEl = this.dom.createChildOf(descriptionEl, 'div', 'lh-metrics__disclaimer');
|
|
191
211
|
const disclaimerEl = this.dom.convertMarkdownLinkSnippets(strings.varianceDisclaimer);
|
|
192
212
|
estValuesEl.appendChild(disclaimerEl);
|
|
193
213
|
|
|
@@ -198,8 +218,8 @@ export class PerformanceCategoryRenderer extends CategoryRenderer {
|
|
|
198
218
|
this.dom.safelySetHref(calculatorLink, this._getScoringCalculatorHref(category.auditRefs));
|
|
199
219
|
|
|
200
220
|
|
|
201
|
-
|
|
202
|
-
element.appendChild(
|
|
221
|
+
metricsGroupEl.classList.add('lh-audit-group--metrics');
|
|
222
|
+
element.appendChild(metricsGroupEl);
|
|
203
223
|
}
|
|
204
224
|
|
|
205
225
|
// Filmstrip
|
|
@@ -214,10 +234,10 @@ export class PerformanceCategoryRenderer extends CategoryRenderer {
|
|
|
214
234
|
|
|
215
235
|
// Opportunities
|
|
216
236
|
const opportunityAudits = category.auditRefs
|
|
217
|
-
.filter(audit => audit
|
|
237
|
+
.filter(audit => this._classifyPerformanceAudit(audit) === 'load-opportunity')
|
|
238
|
+
.filter(audit => !Util.showAsPassed(audit.result))
|
|
218
239
|
.sort((auditA, auditB) => this._getWastedMs(auditB) - this._getWastedMs(auditA));
|
|
219
240
|
|
|
220
|
-
|
|
221
241
|
const filterableMetrics = metricAudits.filter(a => !!a.relevantAudits);
|
|
222
242
|
// TODO: only add if there are opportunities & diagnostics rendered.
|
|
223
243
|
if (filterableMetrics.length) {
|
|
@@ -230,7 +250,7 @@ export class PerformanceCategoryRenderer extends CategoryRenderer {
|
|
|
230
250
|
const wastedMsValues = opportunityAudits.map(audit => this._getWastedMs(audit));
|
|
231
251
|
const maxWaste = Math.max(...wastedMsValues);
|
|
232
252
|
const scale = Math.max(Math.ceil(maxWaste / 1000) * 1000, minimumScale);
|
|
233
|
-
const groupEl = this.renderAuditGroup(groups['load-opportunities']);
|
|
253
|
+
const [groupEl, footerEl] = this.renderAuditGroup(groups['load-opportunities']);
|
|
234
254
|
const tmpl = this.dom.createComponent('opportunityHeader');
|
|
235
255
|
|
|
236
256
|
this.dom.find('.lh-load-opportunity__col--one', tmpl).textContent =
|
|
@@ -239,15 +259,17 @@ export class PerformanceCategoryRenderer extends CategoryRenderer {
|
|
|
239
259
|
strings.opportunitySavingsColumnLabel;
|
|
240
260
|
|
|
241
261
|
const headerEl = this.dom.find('.lh-load-opportunity__header', tmpl);
|
|
242
|
-
groupEl.
|
|
243
|
-
opportunityAudits.forEach(item =>
|
|
262
|
+
groupEl.insertBefore(headerEl, footerEl);
|
|
263
|
+
opportunityAudits.forEach(item =>
|
|
264
|
+
groupEl.insertBefore(this._renderOpportunity(item, scale), footerEl));
|
|
244
265
|
groupEl.classList.add('lh-audit-group--load-opportunities');
|
|
245
266
|
element.appendChild(groupEl);
|
|
246
267
|
}
|
|
247
268
|
|
|
248
269
|
// Diagnostics
|
|
249
270
|
const diagnosticAudits = category.auditRefs
|
|
250
|
-
.filter(audit => audit
|
|
271
|
+
.filter(audit => this._classifyPerformanceAudit(audit) === 'diagnostic')
|
|
272
|
+
.filter(audit => !Util.showAsPassed(audit.result))
|
|
251
273
|
.sort((a, b) => {
|
|
252
274
|
const scoreA = a.result.scoreDisplayMode === 'informative' ? 100 : Number(a.result.score);
|
|
253
275
|
const scoreB = b.result.scoreDisplayMode === 'informative' ? 100 : Number(b.result.score);
|
|
@@ -255,16 +277,15 @@ export class PerformanceCategoryRenderer extends CategoryRenderer {
|
|
|
255
277
|
});
|
|
256
278
|
|
|
257
279
|
if (diagnosticAudits.length) {
|
|
258
|
-
const groupEl = this.renderAuditGroup(groups['diagnostics']);
|
|
259
|
-
diagnosticAudits.forEach(item => groupEl.
|
|
280
|
+
const [groupEl, footerEl] = this.renderAuditGroup(groups['diagnostics']);
|
|
281
|
+
diagnosticAudits.forEach(item => groupEl.insertBefore(this.renderAudit(item), footerEl));
|
|
260
282
|
groupEl.classList.add('lh-audit-group--diagnostics');
|
|
261
283
|
element.appendChild(groupEl);
|
|
262
284
|
}
|
|
263
285
|
|
|
264
286
|
// Passed audits
|
|
265
287
|
const passedAudits = category.auditRefs
|
|
266
|
-
.filter(audit => (audit
|
|
267
|
-
Util.showAsPassed(audit.result));
|
|
288
|
+
.filter(audit => this._classifyPerformanceAudit(audit) && Util.showAsPassed(audit.result));
|
|
268
289
|
|
|
269
290
|
if (!passedAudits.length) return element;
|
|
270
291
|
|
|
@@ -284,16 +305,16 @@ export class PerformanceCategoryRenderer extends CategoryRenderer {
|
|
|
284
305
|
const table = this.detailsRenderer.render(audit.result.details);
|
|
285
306
|
if (table) {
|
|
286
307
|
table.id = id;
|
|
287
|
-
table.classList.add('lh-audit');
|
|
308
|
+
table.classList.add('lh-details', 'lh-details--budget', 'lh-audit');
|
|
288
309
|
budgetTableEls.push(table);
|
|
289
310
|
}
|
|
290
311
|
}
|
|
291
312
|
});
|
|
292
313
|
if (budgetTableEls.length > 0) {
|
|
293
|
-
const
|
|
294
|
-
budgetTableEls.forEach(table =>
|
|
295
|
-
|
|
296
|
-
element.appendChild(
|
|
314
|
+
const [groupEl, footerEl] = this.renderAuditGroup(groups.budgets);
|
|
315
|
+
budgetTableEls.forEach(table => groupEl.insertBefore(table, footerEl));
|
|
316
|
+
groupEl.classList.add('lh-audit-group--budgets');
|
|
317
|
+
element.appendChild(groupEl);
|
|
297
318
|
}
|
|
298
319
|
|
|
299
320
|
return element;
|
|
@@ -47,16 +47,6 @@ export class PwaCategoryRenderer extends CategoryRenderer {
|
|
|
47
47
|
return categoryElem;
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
-
/**
|
|
51
|
-
* Alias for backcompat.
|
|
52
|
-
* @param {LH.ReportResult.Category} category
|
|
53
|
-
* @param {Record<string, LH.Result.ReportGroup>} groupDefinitions
|
|
54
|
-
* @return {DocumentFragment}
|
|
55
|
-
*/
|
|
56
|
-
renderScoreGauge(category, groupDefinitions) {
|
|
57
|
-
return this.renderCategoryScore(category, groupDefinitions);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
50
|
/**
|
|
61
51
|
* @param {LH.ReportResult.Category} category
|
|
62
52
|
* @param {Record<string, LH.Result.ReportGroup>} groupDefinitions
|
|
@@ -36,22 +36,43 @@ export class ReportRenderer {
|
|
|
36
36
|
constructor(dom) {
|
|
37
37
|
/** @type {DOM} */
|
|
38
38
|
this._dom = dom;
|
|
39
|
+
/** @type {LH.Renderer.Options} */
|
|
40
|
+
this._opts = {};
|
|
39
41
|
}
|
|
40
42
|
|
|
41
43
|
/**
|
|
42
44
|
* @param {LH.Result} lhr
|
|
43
|
-
* @param {
|
|
45
|
+
* @param {HTMLElement?} rootEl Report root element containing the report
|
|
46
|
+
* @param {LH.Renderer.Options=} opts
|
|
44
47
|
* @return {!Element}
|
|
45
48
|
*/
|
|
46
|
-
renderReport(lhr,
|
|
49
|
+
renderReport(lhr, rootEl, opts) {
|
|
50
|
+
// Allow legacy report rendering API
|
|
51
|
+
if (!this._dom.rootEl && rootEl) {
|
|
52
|
+
console.warn('Please adopt the new report API in renderer/api.js.');
|
|
53
|
+
const closestRoot = rootEl.closest('.lh-root');
|
|
54
|
+
if (closestRoot) {
|
|
55
|
+
this._dom.rootEl = /** @type {HTMLElement} */ (closestRoot);
|
|
56
|
+
} else {
|
|
57
|
+
rootEl.classList.add('lh-root', 'lh-vars');
|
|
58
|
+
this._dom.rootEl = rootEl;
|
|
59
|
+
}
|
|
60
|
+
} else if (this._dom.rootEl && rootEl) {
|
|
61
|
+
// Handle legacy flow-report case
|
|
62
|
+
this._dom.rootEl = rootEl;
|
|
63
|
+
}
|
|
64
|
+
if (opts) {
|
|
65
|
+
this._opts = opts;
|
|
66
|
+
}
|
|
67
|
+
|
|
47
68
|
this._dom.setLighthouseChannel(lhr.configSettings.channel || 'unknown');
|
|
48
69
|
|
|
49
70
|
const report = Util.prepareReportResult(lhr);
|
|
50
71
|
|
|
51
|
-
|
|
52
|
-
|
|
72
|
+
this._dom.rootEl.textContent = ''; // Remove previous report.
|
|
73
|
+
this._dom.rootEl.appendChild(this._renderReport(report));
|
|
53
74
|
|
|
54
|
-
return
|
|
75
|
+
return this._dom.rootEl;
|
|
55
76
|
}
|
|
56
77
|
|
|
57
78
|
/**
|
|
@@ -85,44 +106,64 @@ export class ReportRenderer {
|
|
|
85
106
|
_renderReportFooter(report) {
|
|
86
107
|
const footer = this._dom.createComponent('footer');
|
|
87
108
|
|
|
88
|
-
|
|
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
|
-
}
|
|
109
|
+
this._renderMetaBlock(report, footer);
|
|
120
110
|
|
|
121
111
|
this._dom.find('.lh-footer__version_issue', footer).textContent = Util.i18n.strings.footerIssue;
|
|
122
112
|
this._dom.find('.lh-footer__version', footer).textContent = report.lighthouseVersion;
|
|
123
113
|
return footer;
|
|
124
114
|
}
|
|
125
115
|
|
|
116
|
+
/**
|
|
117
|
+
* @param {LH.ReportResult} report
|
|
118
|
+
* @param {DocumentFragment} footer
|
|
119
|
+
*/
|
|
120
|
+
_renderMetaBlock(report, footer) {
|
|
121
|
+
const envValues = Util.getEmulationDescriptions(report.configSettings || {});
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
const match = report.userAgent.match(/(\w*Chrome\/[\d.]+)/); // \w* to include 'HeadlessChrome'
|
|
125
|
+
const chromeVer = Array.isArray(match)
|
|
126
|
+
? match[1].replace('/', ' ').replace('Chrome', 'Chromium')
|
|
127
|
+
: 'Chromium';
|
|
128
|
+
const channel = report.configSettings.channel;
|
|
129
|
+
const benchmarkIndex = report.environment.benchmarkIndex.toFixed(0);
|
|
130
|
+
const axeVersion = report.environment.credits?.['axe-core'];
|
|
131
|
+
|
|
132
|
+
// [CSS icon class, textContent, tooltipText]
|
|
133
|
+
const metaItems = [
|
|
134
|
+
['date',
|
|
135
|
+
`Captured at ${Util.i18n.formatDateTime(report.fetchTime)}`],
|
|
136
|
+
['devices',
|
|
137
|
+
`${envValues.deviceEmulation} with Lighthouse ${report.lighthouseVersion}`,
|
|
138
|
+
`${Util.i18n.strings.runtimeSettingsBenchmark}: ${benchmarkIndex}` +
|
|
139
|
+
`\n${Util.i18n.strings.runtimeSettingsCPUThrottling}: ${envValues.cpuThrottling}` +
|
|
140
|
+
(axeVersion ? `\n${Util.i18n.strings.runtimeSettingsAxeVersion}: ${axeVersion}` : '')],
|
|
141
|
+
['samples-one',
|
|
142
|
+
Util.i18n.strings.runtimeSingleLoad,
|
|
143
|
+
Util.i18n.strings.runtimeSingleLoadTooltip],
|
|
144
|
+
['stopwatch',
|
|
145
|
+
Util.i18n.strings.runtimeAnalysisWindow],
|
|
146
|
+
['networkspeed',
|
|
147
|
+
`${envValues.summary}`,
|
|
148
|
+
`${Util.i18n.strings.runtimeSettingsNetworkThrottling}: ${envValues.networkThrottling}`],
|
|
149
|
+
['chrome',
|
|
150
|
+
`Using ${chromeVer}` + (channel ? ` with ${channel}` : ''),
|
|
151
|
+
`${Util.i18n.strings.runtimeSettingsUANetwork}: "${report.environment.networkUserAgent}"`],
|
|
152
|
+
];
|
|
153
|
+
|
|
154
|
+
const metaItemsEl = this._dom.find('.lh-meta__items', footer);
|
|
155
|
+
for (const [iconname, text, tooltip] of metaItems) {
|
|
156
|
+
const itemEl = this._dom.createChildOf(metaItemsEl, 'li', 'lh-meta__item');
|
|
157
|
+
itemEl.textContent = text;
|
|
158
|
+
if (tooltip) {
|
|
159
|
+
itemEl.classList.add('lh-tooltip-boundary');
|
|
160
|
+
const tooltipEl = this._dom.createChildOf(itemEl, 'div', 'lh-tooltip');
|
|
161
|
+
tooltipEl.textContent = tooltip;
|
|
162
|
+
}
|
|
163
|
+
itemEl.classList.add('lh-report-icon', `lh-report-icon--${iconname}`);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
126
167
|
/**
|
|
127
168
|
* Returns a div with a list of top-level warnings, or an empty div if no warnings.
|
|
128
169
|
* @param {LH.ReportResult} report
|
|
@@ -177,7 +218,7 @@ export class ReportRenderer {
|
|
|
177
218
|
gaugeWrapperEl.addEventListener('click', e => {
|
|
178
219
|
if (!gaugeWrapperEl.matches('[href^="#"]')) return;
|
|
179
220
|
const selector = gaugeWrapperEl.getAttribute('href');
|
|
180
|
-
const reportRoot =
|
|
221
|
+
const reportRoot = this._dom.rootEl;
|
|
181
222
|
if (!selector || !reportRoot) return;
|
|
182
223
|
const destEl = this._dom.find(selector, reportRoot);
|
|
183
224
|
e.preventDefault();
|
|
@@ -192,7 +233,7 @@ export class ReportRenderer {
|
|
|
192
233
|
// The renderer for default categories is just the default CategoryRenderer.
|
|
193
234
|
// If the functions are equal, then renderer is an instance of CategoryRenderer.
|
|
194
235
|
// For example, the PWA category uses PwaCategoryRenderer, which overrides
|
|
195
|
-
// CategoryRenderer.
|
|
236
|
+
// CategoryRenderer.renderCategoryScore, so it would fail this check and be placed
|
|
196
237
|
// in the customGauges bucket.
|
|
197
238
|
defaultGauges.push(categoryGauge);
|
|
198
239
|
} else {
|
|
@@ -247,8 +288,10 @@ export class ReportRenderer {
|
|
|
247
288
|
headerContainer.classList.add('lh-header--solo-category');
|
|
248
289
|
}
|
|
249
290
|
|
|
291
|
+
const scoreScale = this._dom.createElement('div');
|
|
292
|
+
scoreScale.classList.add('lh-scorescale-wrap');
|
|
293
|
+
scoreScale.append(this._dom.createComponent('scorescale'));
|
|
250
294
|
if (scoreHeader) {
|
|
251
|
-
const scoreScale = this._dom.createComponent('scorescale');
|
|
252
295
|
const scoresContainer = this._dom.find('.lh-scores-container', headerContainer);
|
|
253
296
|
scoreHeader.append(
|
|
254
297
|
...this._renderScoreGauges(report, categoryRenderer, specificCategoryRenderers));
|
|
@@ -266,7 +309,7 @@ export class ReportRenderer {
|
|
|
266
309
|
for (const category of Object.values(report.categories)) {
|
|
267
310
|
const renderer = specificCategoryRenderers[category.id] || categoryRenderer;
|
|
268
311
|
// .lh-category-wrapper is full-width and provides horizontal rules between categories.
|
|
269
|
-
// .lh-category within has the max-width: var(--report-width);
|
|
312
|
+
// .lh-category within has the max-width: var(--report-content-width);
|
|
270
313
|
const wrapper = renderer.dom.createChildOf(categories, 'div', 'lh-category-wrapper');
|
|
271
314
|
wrapper.appendChild(renderer.render(
|
|
272
315
|
category,
|
|
@@ -275,11 +318,15 @@ export class ReportRenderer {
|
|
|
275
318
|
));
|
|
276
319
|
}
|
|
277
320
|
|
|
321
|
+
categoryRenderer.injectFinalScreenshot(categories, report.audits, scoreScale);
|
|
322
|
+
|
|
278
323
|
const reportFragment = this._dom.createFragment();
|
|
279
324
|
reportFragment.append(this._dom.createComponent('styles'));
|
|
280
|
-
const topbarDocumentFragment = this._renderReportTopbar(report);
|
|
281
325
|
|
|
282
|
-
|
|
326
|
+
if (!this._opts.omitTopbar) {
|
|
327
|
+
reportFragment.appendChild(this._renderReportTopbar(report));
|
|
328
|
+
}
|
|
329
|
+
|
|
283
330
|
reportFragment.appendChild(reportContainer);
|
|
284
331
|
reportContainer.appendChild(headerContainer);
|
|
285
332
|
reportContainer.appendChild(reportSection);
|
|
@@ -287,7 +334,7 @@ export class ReportRenderer {
|
|
|
287
334
|
|
|
288
335
|
if (fullPageScreenshot) {
|
|
289
336
|
ElementScreenshotRenderer.installFullPageScreenshot(
|
|
290
|
-
|
|
337
|
+
this._dom.rootEl, fullPageScreenshot.screenshot);
|
|
291
338
|
}
|
|
292
339
|
|
|
293
340
|
return reportFragment;
|