diff --git a/lighthouse-core/report/html/renderer/performance-category-renderer.js b/lighthouse-core/report/html/renderer/performance-category-renderer.js index 772d277422d6..482ac442e76c 100644 --- a/lighthouse-core/report/html/renderer/performance-category-renderer.js +++ b/lighthouse-core/report/html/renderer/performance-category-renderer.js @@ -112,13 +112,20 @@ class PerformanceCategoryRenderer extends CategoryRenderer { /** * @param {LH.ReportResult.Category} category * @param {Object} groups + * @param {string=} environment 'PSI' and undefined are the only valid values * @return {Element} * @override */ - render(category, groups) { + render(category, groups, environment) { const element = this.dom.createElement('div', 'lh-category'); - this.createPermalinkSpan(element, category.id); - element.appendChild(this.renderCategoryHeader(category)); + if (environment === 'PSI') { + const gaugeEl = this.dom.createElement('div', 'lh-score__gauge'); + gaugeEl.appendChild(this.renderScoreGauge(category)); + element.appendChild(gaugeEl); + } else { + this.createPermalinkSpan(element, category.id); + element.appendChild(this.renderCategoryHeader(category)); + } // Metrics const metricAudits = category.auditRefs.filter(audit => audit.group === 'metrics'); diff --git a/lighthouse-core/report/html/renderer/psi.js b/lighthouse-core/report/html/renderer/psi.js new file mode 100644 index 000000000000..f0c0e8401af6 --- /dev/null +++ b/lighthouse-core/report/html/renderer/psi.js @@ -0,0 +1,74 @@ +/** + * @license + * Copyright 2018 Google Inc. All Rights Reserved. + * + * 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 + * + * 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. + */ +'use strict'; + +/* globals self DOM PerformanceCategoryRenderer Util DetailsRenderer */ + +class PSI { + /** + * Returns all the elements that PSI needs to render the report + * We expose this helper method to minimize the 'public' API surface of the renderer + * and allow us to refactor without two-sided patches. + * + * const {scoreGaugeEl, perfCategoryEl, finalScreenshotDataUri} = PSI.prepareLabData( + * LHResultJsonString, + * document + * ); + * + * @param {string} LHResultJsonString The stringified version of {LH.Result} + * @param {Document} document The host page's window.document + * @return {{scoreGaugeEl: Element, perfCategoryEl: Element, finalScreenshotDataUri: string|null}} + */ + static prepareLabData(LHResultJsonString, document) { + const lhResult = /** @type {LH.Result} */ JSON.parse(LHResultJsonString); + const dom = new DOM(document); + + const reportLHR = Util.prepareReportResult(lhResult); + const perfCategory = reportLHR.reportCategories.find(cat => cat.id === 'performance'); + if (!perfCategory) throw new Error(`No performance category. Can't make lab data section`); + if (!reportLHR.categoryGroups) throw new Error(`No category groups found.`); + + const perfRenderer = new PerformanceCategoryRenderer(dom, new DetailsRenderer(dom)); + // PSI environment string will ensure the categoryHeader and permalink elements are excluded + const perfCategoryEl = perfRenderer.render(perfCategory, reportLHR.categoryGroups, 'PSI'); + + const scoreGaugeEl = dom.find('.lh-score__gauge', perfCategoryEl); + const scoreGaugeWrapperEl = dom.find('.lh-gauge__wrapper', scoreGaugeEl); + scoreGaugeWrapperEl.classList.add('lh-gauge__wrapper--huge'); + // Remove navigation link on gauge + scoreGaugeWrapperEl.removeAttribute('href'); + + const finalScreenshotDataUri = PSI.getFinalScreenshot(perfCategory); + return {scoreGaugeEl, perfCategoryEl, finalScreenshotDataUri}; + } + + /** + * @param {LH.ReportResult.Category} perfCategory + * @return {null|string} + */ + static getFinalScreenshot(perfCategory) { + const auditRef = perfCategory.auditRefs.find(audit => audit.id === 'final-screenshot'); + if (!auditRef || !auditRef.result || auditRef.result.scoreDisplayMode === 'error') return null; + return auditRef.result.details.data; + } +} + +if (typeof module !== 'undefined' && module.exports) { + module.exports = PSI; +} else { + self.PSI = PSI; +} diff --git a/lighthouse-core/report/html/renderer/util.js b/lighthouse-core/report/html/renderer/util.js index 922b90517fbb..c076d10e79b2 100644 --- a/lighthouse-core/report/html/renderer/util.js +++ b/lighthouse-core/report/html/renderer/util.js @@ -377,18 +377,6 @@ class Util { ]; } - /** - * @param {LH.ReportResult} result - * @return {null|string} - */ - static getFinalScreenshot(result) { - const category = result.reportCategories.find(cat => cat.id === 'performance'); - if (!category) return null; - const auditRef = category.auditRefs.find(audit => audit.id === 'final-screenshot'); - if (!auditRef || !auditRef.result || auditRef.result.scoreDisplayMode === 'error') return null; - return auditRef.result.details.data; - } - /** * @param {LH.Config.Settings} settings * @return {{deviceEmulation: string, networkThrottling: string, cpuThrottling: string, summary: string}} diff --git a/lighthouse-core/test/report/html/renderer/psi-test.js b/lighthouse-core/test/report/html/renderer/psi-test.js new file mode 100644 index 000000000000..ef63895d7e12 --- /dev/null +++ b/lighthouse-core/test/report/html/renderer/psi-test.js @@ -0,0 +1,119 @@ +/** + * @license Copyright 2017 Google Inc. All Rights Reserved. + * 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 + * 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. + */ +'use strict'; + +const assert = require('assert'); +const fs = require('fs'); + +const jsdom = require('jsdom'); + +const URL = require('../../../../lib/url-shim'); +const PSI = require('../../../../report/html/renderer/psi.js'); +const Util = require('../../../../report/html/renderer/util.js'); +const DOM = require('../../../../report/html/renderer/dom.js'); +const CategoryRenderer = require('../../../../report/html/renderer/category-renderer'); +const DetailsRenderer = require('../../../../report/html/renderer/details-renderer'); +const CriticalRequestChainRenderer = + require('../../../../report/html/renderer/crc-details-renderer'); + +const sampleResultsStr = fs.readFileSync(__dirname + '/../../../results/sample_v2.json', 'utf-8'); +const sampleResults = JSON.parse(sampleResultsStr) +; +const TEMPLATE_FILE = fs.readFileSync( + __dirname + '/../../../../report/html/templates.html', + 'utf8' +); + +/* eslint-env jest */ + +describe('DOM', () => { + let document; + beforeAll(() => { + global.URL = URL; // COMPAT: Needed for Node < 10 + global.Util = Util; + global.DOM = DOM; + global.CategoryRenderer = CategoryRenderer; + global.DetailsRenderer = DetailsRenderer; + + // Delayed so that CategoryRenderer is in global scope + const PerformanceCategoryRenderer = + require('../../../../report/html/renderer/performance-category-renderer'); + global.PerformanceCategoryRenderer = PerformanceCategoryRenderer; + global.CriticalRequestChainRenderer = CriticalRequestChainRenderer; + + document = jsdom.jsdom(TEMPLATE_FILE); + }); + + afterAll(() => { + global.URL = undefined; + global.Util = undefined; + global.DOM = undefined; + global.CategoryRenderer = undefined; + global.DetailsRenderer = undefined; + global.PerformanceCategoryRenderer = undefined; + global.CriticalRequestChainRenderer = undefined; + }); + + describe('psi prepareLabData helpers', () => { + describe('prepareLabData', () => { + it('reports expected data', () => { + const result = PSI.prepareLabData(sampleResultsStr, document); + assert.ok(result.scoreGaugeEl instanceof document.defaultView.Element); + assert.equal(result.scoreGaugeEl.querySelector('.lh-gauge__wrapper').href, ''); + assert.ok(result.scoreGaugeEl.outerHTML.includes('