| Index: third_party/WebKit/Source/devtools/front_end/audits2/lighthouse/renderer/report-renderer.js
|
| diff --git a/third_party/WebKit/Source/devtools/front_end/audits2/lighthouse/renderer/report-renderer.js b/third_party/WebKit/Source/devtools/front_end/audits2/lighthouse/renderer/report-renderer.js
|
| index 88c28fbeb4f18e243f165202aa60a53c62668ade..f5318cdaa40481a45d5d8f9b4120a3dafa9c3885 100644
|
| --- a/third_party/WebKit/Source/devtools/front_end/audits2/lighthouse/renderer/report-renderer.js
|
| +++ b/third_party/WebKit/Source/devtools/front_end/audits2/lighthouse/renderer/report-renderer.js
|
| @@ -22,78 +22,38 @@
|
| * Dummy text for ensuring report robustness: </script> pre$`post %%LIGHTHOUSE_JSON%%
|
| */
|
|
|
| -const RATINGS = {
|
| - PASS: {label: 'pass', minScore: 75},
|
| - AVERAGE: {label: 'average', minScore: 45},
|
| - FAIL: {label: 'fail'}
|
| -};
|
| -
|
| -/**
|
| - * Convert a score to a rating label.
|
| - * @param {number} score
|
| - * @return {string}
|
| - */
|
| -function calculateRating(score) {
|
| - let rating = RATINGS.FAIL.label;
|
| - if (score >= RATINGS.PASS.minScore) {
|
| - rating = RATINGS.PASS.label;
|
| - } else if (score >= RATINGS.AVERAGE.minScore) {
|
| - rating = RATINGS.AVERAGE.label;
|
| - }
|
| - return rating;
|
| -}
|
| -
|
| -/**
|
| - * Format number.
|
| - * @param {number} number
|
| - * @return {string}
|
| - */
|
| -function formatNumber(number) {
|
| - return number.toLocaleString(undefined, {maximumFractionDigits: 1});
|
| -}
|
| +/* globals self, Util */
|
|
|
| class ReportRenderer {
|
| /**
|
| * @param {!DOM} dom
|
| - * @param {!DetailsRenderer} detailsRenderer
|
| + * @param {!CategoryRenderer} categoryRenderer
|
| + * @param {?ReportUIFeatures=} uiFeatures
|
| */
|
| - constructor(dom, detailsRenderer) {
|
| + constructor(dom, categoryRenderer, uiFeatures = null) {
|
| + /** @private {!DOM} */
|
| this._dom = dom;
|
| - this._detailsRenderer = detailsRenderer;
|
| -
|
| + /** @private {!CategoryRenderer} */
|
| + this._categoryRenderer = categoryRenderer;
|
| + /** @private {!Document|!Element} */
|
| this._templateContext = this._dom.document();
|
| + /** @private {ReportUIFeatures} */
|
| + this._uiFeatures = uiFeatures;
|
| }
|
|
|
| /**
|
| * @param {!ReportRenderer.ReportJSON} report
|
| - * @return {!Element}
|
| + * @param {!Element} container Parent element to render the report into.
|
| */
|
| - renderReport(report) {
|
| - try {
|
| - return this._renderReport(report);
|
| - } catch (e) {
|
| - return this._renderException(e);
|
| + renderReport(report, container) {
|
| + container.textContent = ''; // Remove previous report.
|
| + const element = container.appendChild(this._renderReport(report));
|
| +
|
| + // Hook in JS features and page-level event listeners after the report
|
| + // is in the document.
|
| + if (this._uiFeatures) {
|
| + this._uiFeatures.initFeatures(report);
|
| }
|
| - }
|
| -
|
| - /**
|
| - * @param {!DocumentFragment|!Element} element DOM node to populate with values.
|
| - * @param {number} score
|
| - * @param {string} scoringMode
|
| - * @param {string} title
|
| - * @param {string} description
|
| - * @return {!Element}
|
| - */
|
| - _populateScore(element, score, scoringMode, title, description) {
|
| - // Fill in the blanks.
|
| - const valueEl = element.querySelector('.lh-score__value');
|
| - valueEl.textContent = formatNumber(score);
|
| - valueEl.classList.add(`lh-score__value--${calculateRating(score)}`,
|
| - `lh-score__value--${scoringMode}`);
|
| -
|
| - element.querySelector('.lh-score__title').textContent = title;
|
| - element.querySelector('.lh-score__description')
|
| - .appendChild(this._dom.createSpanFromMarkdown(description));
|
|
|
| return /** @type {!Element} **/ (element);
|
| }
|
| @@ -105,124 +65,119 @@ class ReportRenderer {
|
| */
|
| setTemplateContext(context) {
|
| this._templateContext = context;
|
| + this._categoryRenderer.setTemplateContext(context);
|
| }
|
|
|
| /**
|
| - * @param {!ReportRenderer.AuditJSON} audit
|
| - * @return {!Element}
|
| - */
|
| - _renderAuditScore(audit) {
|
| - const tmpl = this._dom.cloneTemplate('#tmpl-lh-audit-score', this._templateContext);
|
| -
|
| - const scoringMode = audit.result.scoringMode;
|
| - const description = audit.result.helpText;
|
| - let title = audit.result.description;
|
| -
|
| - if (audit.result.displayValue) {
|
| - title += `: ${audit.result.displayValue}`;
|
| - }
|
| - if (audit.result.optimalValue) {
|
| - title += ` (target: ${audit.result.optimalValue})`;
|
| - }
|
| -
|
| - // Append audit details to header section so the entire audit is within a <details>.
|
| - const header = tmpl.querySelector('.lh-score__header');
|
| - header.open = audit.score < 100; // expand failed audits
|
| - if (audit.result.details) {
|
| - header.appendChild(this._detailsRenderer.render(audit.result.details));
|
| - }
|
| -
|
| - return this._populateScore(tmpl, audit.score, scoringMode, title, description);
|
| - }
|
| -
|
| - /**
|
| - * @param {!ReportRenderer.CategoryJSON} category
|
| - * @return {!Element}
|
| + * @param {!ReportRenderer.ReportJSON} report
|
| + * @return {!DocumentFragment}
|
| */
|
| - _renderCategoryScore(category) {
|
| - const tmpl = this._dom.cloneTemplate('#tmpl-lh-category-score', this._templateContext);
|
| - const score = Math.round(category.score);
|
| - return this._populateScore(tmpl, score, 'numeric', category.name, category.description);
|
| + _renderReportHeader(report) {
|
| + const header = this._dom.cloneTemplate('#tmpl-lh-heading', this._templateContext);
|
| + this._dom.find('.lh-config__timestamp', header).textContent =
|
| + Util.formatDateTime(report.generatedTime);
|
| + const url = this._dom.find('.lh-metadata__url', header);
|
| + url.href = report.url;
|
| + url.textContent = report.url;
|
| +
|
| + const env = this._dom.find('.lh-env__items', header);
|
| + report.runtimeConfig.environment.forEach(runtime => {
|
| + const item = this._dom.cloneTemplate('#tmpl-lh-env__items', env);
|
| + this._dom.find('.lh-env__name', item).textContent = runtime.name;
|
| + this._dom.find('.lh-env__description', item).textContent = runtime.description;
|
| + this._dom.find('.lh-env__enabled', item).textContent =
|
| + runtime.enabled ? 'Enabled' : 'Disabled';
|
| + env.appendChild(item);
|
| + });
|
| +
|
| + return header;
|
| }
|
|
|
| /**
|
| - * @param {!Error} e
|
| - * @return {!Element}
|
| + * @param {!ReportRenderer.ReportJSON} report
|
| + * @return {!DocumentFragment}
|
| */
|
| - _renderException(e) {
|
| - const element = this._dom.createElement('div', 'lh-exception');
|
| - element.textContent = String(e.stack);
|
| - return element;
|
| + _renderReportFooter(report) {
|
| + const footer = this._dom.cloneTemplate('#tmpl-lh-footer', this._templateContext);
|
| + this._dom.find('.lh-footer__version', footer).textContent = report.lighthouseVersion;
|
| + this._dom.find('.lh-footer__timestamp', footer).textContent =
|
| + Util.formatDateTime(report.generatedTime);
|
| + return footer;
|
| }
|
|
|
| /**
|
| * @param {!ReportRenderer.ReportJSON} report
|
| - * @return {!Element}
|
| + * @return {!DocumentFragment}
|
| */
|
| - _renderReport(report) {
|
| - const element = this._dom.createElement('div', 'lh-report');
|
| + _renderReportNav(report) {
|
| + const leftNav = this._dom.cloneTemplate('#tmpl-lh-leftnav', this._templateContext);
|
| +
|
| + this._dom.find('.leftnav__header__version', leftNav).textContent =
|
| + `Version: ${report.lighthouseVersion}`;
|
| +
|
| + const nav = this._dom.find('.lh-leftnav', leftNav);
|
| for (const category of report.reportCategories) {
|
| - element.appendChild(this._renderCategory(category));
|
| + const itemsTmpl = this._dom.cloneTemplate('#tmpl-lh-leftnav__items', leftNav);
|
| +
|
| + const navItem = this._dom.find('.lh-leftnav__item', itemsTmpl);
|
| + navItem.href = `#${category.id}`;
|
| +
|
| + this._dom.find('.leftnav-item__category', navItem).textContent = category.name;
|
| + const score = this._dom.find('.leftnav-item__score', navItem);
|
| + score.classList.add(`lh-score__value--${Util.calculateRating(category.score)}`);
|
| + score.textContent = Math.round(Util.formatNumber(category.score));
|
| + nav.appendChild(navItem);
|
| }
|
| - return element;
|
| + return leftNav;
|
| }
|
|
|
| /**
|
| - * @param {!ReportRenderer.CategoryJSON} category
|
| + * @param {!ReportRenderer.ReportJSON} report
|
| * @return {!Element}
|
| */
|
| - _renderCategory(category) {
|
| - const element = this._dom.createElement('div', 'lh-category');
|
| - element.appendChild(this._renderCategoryScore(category));
|
| -
|
| - const passedAudits = category.audits.filter(audit => audit.score === 100);
|
| - const nonPassedAudits = category.audits.filter(audit => !passedAudits.includes(audit));
|
| + _renderReport(report) {
|
| + const container = this._dom.createElement('div', 'lh-container');
|
|
|
| - for (const audit of nonPassedAudits) {
|
| - element.appendChild(this._renderAudit(audit));
|
| - }
|
| + container.appendChild(this._renderReportHeader(report)); // sticky header goes at the top.
|
| + container.appendChild(this._renderReportNav(report));
|
|
|
| - // don't create a passed section if there are no passed
|
| - if (!passedAudits.length) return element;
|
| + const reportSection = container.appendChild(this._dom.createElement('div', 'lh-report'));
|
|
|
| - const passedElem = this._dom.createElement('details', 'lh-passed-audits');
|
| - const passedSummary = this._dom.createElement('summary', 'lh-passed-audits-summary');
|
| - passedSummary.textContent = `View ${passedAudits.length} passed items`;
|
| - passedElem.appendChild(passedSummary);
|
| + const scoreHeader = reportSection.appendChild(
|
| + this._dom.createElement('div', 'lh-scores-header'));
|
|
|
| - for (const audit of passedAudits) {
|
| - passedElem.appendChild(this._renderAudit(audit));
|
| + const categories = reportSection.appendChild(this._dom.createElement('div', 'lh-categories'));
|
| + for (const category of report.reportCategories) {
|
| + scoreHeader.appendChild(this._categoryRenderer.renderScoreGauge(category));
|
| + categories.appendChild(this._categoryRenderer.render(category));
|
| }
|
| - element.appendChild(passedElem);
|
| - return element;
|
| - }
|
|
|
| - /**
|
| - * @param {!ReportRenderer.AuditJSON} audit
|
| - * @return {!Element}
|
| - */
|
| - _renderAudit(audit) {
|
| - const element = this._dom.createElement('div', 'lh-audit');
|
| - element.appendChild(this._renderAuditScore(audit));
|
| - return element;
|
| + reportSection.appendChild(this._renderReportFooter(report));
|
| +
|
| + return container;
|
| }
|
| }
|
|
|
| if (typeof module !== 'undefined' && module.exports) {
|
| module.exports = ReportRenderer;
|
| +} else {
|
| + self.ReportRenderer = ReportRenderer;
|
| }
|
|
|
| /**
|
| * @typedef {{
|
| - * id: string, weight:
|
| - * number, score: number,
|
| + * id: string,
|
| + * weight: number,
|
| + * score: number,
|
| * result: {
|
| * description: string,
|
| + * debugString: string,
|
| * displayValue: string,
|
| * helpText: string,
|
| * score: (number|boolean),
|
| * scoringMode: string,
|
| - * details: (!DetailsRenderer.DetailsJSON|!DetailsRenderer.CardsDetailsJSON|undefined)
|
| + * optimalValue: number,
|
| + * details: (!DetailsRenderer.DetailsJSON|undefined)
|
| * }
|
| * }}
|
| */
|
| @@ -231,6 +186,7 @@ ReportRenderer.AuditJSON; // eslint-disable-line no-unused-expressions
|
| /**
|
| * @typedef {{
|
| * name: string,
|
| + * id: string,
|
| * weight: number,
|
| * score: number,
|
| * description: string,
|
| @@ -241,12 +197,15 @@ ReportRenderer.CategoryJSON; // eslint-disable-line no-unused-expressions
|
|
|
| /**
|
| * @typedef {{
|
| - * lighthouseVersion: !string,
|
| - * generatedTime: !string,
|
| - * initialUrl: !string,
|
| - * url: !string,
|
| - * audits: ?Object,
|
| - * reportCategories: !Array<!ReportRenderer.CategoryJSON>
|
| + * lighthouseVersion: string,
|
| + * generatedTime: string,
|
| + * initialUrl: string,
|
| + * url: string,
|
| + * reportCategories: !Array<!ReportRenderer.CategoryJSON>,
|
| + * runtimeConfig: {
|
| + * blockedUrlPatterns: !Array<string>,
|
| + * environment: !Array<{description: string, enabled: boolean, name: string}>
|
| + * }
|
| * }}
|
| */
|
| ReportRenderer.ReportJSON; // eslint-disable-line no-unused-expressions
|
|
|