Index: third_party/WebKit/Source/devtools/front_end/audits2/lighthouse/renderer/category-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/category-renderer.js |
similarity index 53% |
copy from third_party/WebKit/Source/devtools/front_end/audits2/lighthouse/renderer/report-renderer.js |
copy to third_party/WebKit/Source/devtools/front_end/audits2/lighthouse/renderer/category-renderer.js |
index 88c28fbeb4f18e243f165202aa60a53c62668ade..fa662daf03ad9418f85002ea16c55b6e36e09d19 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/category-renderer.js |
@@ -15,98 +15,22 @@ |
*/ |
'use strict'; |
-/** |
- * @fileoverview The entry point for rendering the Lighthouse report based on the JSON output. |
- * This file is injected into the report HTML along with the JSON report. |
- * |
- * 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 { |
+class CategoryRenderer { |
/** |
* @param {!DOM} dom |
* @param {!DetailsRenderer} detailsRenderer |
*/ |
constructor(dom, detailsRenderer) { |
+ /** @private {!DOM} */ |
this._dom = dom; |
+ /** @private {!DetailsRenderer} */ |
this._detailsRenderer = detailsRenderer; |
- |
+ /** @private {!Document|!Element} */ |
this._templateContext = this._dom.document(); |
} |
- /** |
- * @param {!ReportRenderer.ReportJSON} report |
- * @return {!Element} |
- */ |
- renderReport(report) { |
- try { |
- return this._renderReport(report); |
- } catch (e) { |
- return this._renderException(e); |
- } |
- } |
- |
- /** |
- * @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); |
- } |
- |
- /** |
- * Define a custom element for <templates> to be extracted from. For example: |
- * this.setTemplateContext(new DOMParser().parseFromString(htmlStr, 'text/html')) |
- * @param {!Document|!Element} context |
- */ |
- setTemplateContext(context) { |
- this._templateContext = context; |
- } |
- |
/** |
* @param {!ReportRenderer.AuditJSON} audit |
* @return {!Element} |
@@ -125,8 +49,13 @@ class ReportRenderer { |
title += ` (target: ${audit.result.optimalValue})`; |
} |
+ if (audit.result.debugString) { |
+ const debugStrEl = tmpl.appendChild(this._dom.createElement('div', 'lh-debug')); |
+ debugStrEl.textContent = audit.result.debugString; |
+ } |
+ |
// Append audit details to header section so the entire audit is within a <details>. |
- const header = tmpl.querySelector('.lh-score__header'); |
+ const header = /** @type {!HTMLDetailsElement} */ (this._dom.find('.lh-score__header', tmpl)); |
header.open = audit.score < 100; // expand failed audits |
if (audit.result.details) { |
header.appendChild(this._detailsRenderer.render(audit.result.details)); |
@@ -135,6 +64,28 @@ class ReportRenderer { |
return this._populateScore(tmpl, audit.score, scoringMode, title, description); |
} |
+ /** |
+ * @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 = this._dom.find('.lh-score__value', element); |
+ valueEl.textContent = Util.formatNumber(score); |
+ valueEl.classList.add(`lh-score__value--${Util.calculateRating(score)}`, |
+ `lh-score__value--${scoringMode}`); |
+ |
+ this._dom.find('.lh-score__title', element).textContent = title; |
+ this._dom.find('.lh-score__description', element) |
+ .appendChild(this._dom.createSpanFromMarkdown(description)); |
+ |
+ return /** @type {!Element} **/ (element); |
+ } |
+ |
/** |
* @param {!ReportRenderer.CategoryJSON} category |
* @return {!Element} |
@@ -146,33 +97,58 @@ class ReportRenderer { |
} |
/** |
- * @param {!Error} e |
+ * @param {!ReportRenderer.AuditJSON} audit |
* @return {!Element} |
*/ |
- _renderException(e) { |
- const element = this._dom.createElement('div', 'lh-exception'); |
- element.textContent = String(e.stack); |
+ _renderAudit(audit) { |
+ const element = this._dom.createElement('div', 'lh-audit'); |
+ element.appendChild(this._renderAuditScore(audit)); |
return element; |
} |
/** |
- * @param {!ReportRenderer.ReportJSON} report |
- * @return {!Element} |
+ * @param {!Document|!Element} context |
*/ |
- _renderReport(report) { |
- const element = this._dom.createElement('div', 'lh-report'); |
- for (const category of report.reportCategories) { |
- element.appendChild(this._renderCategory(category)); |
- } |
- return element; |
+ setTemplateContext(context) { |
+ this._templateContext = context; |
+ } |
+ |
+ /** |
+ * @param {!ReportRenderer.CategoryJSON} category |
+ * @return {!DocumentFragment} |
+ */ |
+ renderScoreGauge(category) { |
+ const tmpl = this._dom.cloneTemplate('#tmpl-lh-gauge', this._templateContext); |
+ this._dom.find('.lh-gauge__wrapper', tmpl).href = `#${category.id}`; |
+ this._dom.find('.lh-gauge__label', tmpl).textContent = category.name; |
+ |
+ const score = Math.round(category.score); |
+ const fillRotation = Math.floor((score / 100) * 180); |
+ |
+ const gauge = this._dom.find('.lh-gauge', tmpl); |
+ gauge.setAttribute('data-progress', score); // .dataset not supported in jsdom. |
+ gauge.classList.add(`lh-gauge--${Util.calculateRating(score)}`); |
+ |
+ this._dom.findAll('.lh-gauge__fill', gauge).forEach(el => { |
+ el.style.transform = `rotate(${fillRotation}deg)`; |
+ }); |
+ |
+ this._dom.find('.lh-gauge__mask--full', gauge).style.transform = |
+ `rotate(${fillRotation}deg)`; |
+ this._dom.find('.lh-gauge__fill--fix', gauge).style.transform = |
+ `rotate(${fillRotation * 2}deg)`; |
+ this._dom.find('.lh-gauge__percentage', gauge).textContent = score; |
+ |
+ return tmpl; |
} |
/** |
* @param {!ReportRenderer.CategoryJSON} category |
* @return {!Element} |
*/ |
- _renderCategory(category) { |
+ render(category) { |
const element = this._dom.createElement('div', 'lh-category'); |
+ element.id = category.id; |
element.appendChild(this._renderCategoryScore(category)); |
const passedAudits = category.audits.filter(audit => audit.score === 100); |
@@ -182,8 +158,10 @@ class ReportRenderer { |
element.appendChild(this._renderAudit(audit)); |
} |
- // don't create a passed section if there are no passed |
- if (!passedAudits.length) return element; |
+ // Don't create a passed section if there are no passed. |
+ if (!passedAudits.length) { |
+ return element; |
+ } |
const passedElem = this._dom.createElement('details', 'lh-passed-audits'); |
const passedSummary = this._dom.createElement('summary', 'lh-passed-audits-summary'); |
@@ -196,57 +174,10 @@ class ReportRenderer { |
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; |
- } |
} |
if (typeof module !== 'undefined' && module.exports) { |
- module.exports = ReportRenderer; |
+ module.exports = CategoryRenderer; |
+} else { |
+ self.CategoryRenderer = CategoryRenderer; |
} |
- |
-/** |
- * @typedef {{ |
- * id: string, weight: |
- * number, score: number, |
- * result: { |
- * description: string, |
- * displayValue: string, |
- * helpText: string, |
- * score: (number|boolean), |
- * scoringMode: string, |
- * details: (!DetailsRenderer.DetailsJSON|!DetailsRenderer.CardsDetailsJSON|undefined) |
- * } |
- * }} |
- */ |
-ReportRenderer.AuditJSON; // eslint-disable-line no-unused-expressions |
- |
-/** |
- * @typedef {{ |
- * name: string, |
- * weight: number, |
- * score: number, |
- * description: string, |
- * audits: !Array<!ReportRenderer.AuditJSON> |
- * }} |
- */ |
-ReportRenderer.CategoryJSON; // eslint-disable-line no-unused-expressions |
- |
-/** |
- * @typedef {{ |
- * lighthouseVersion: !string, |
- * generatedTime: !string, |
- * initialUrl: !string, |
- * url: !string, |
- * audits: ?Object, |
- * reportCategories: !Array<!ReportRenderer.CategoryJSON> |
- * }} |
- */ |
-ReportRenderer.ReportJSON; // eslint-disable-line no-unused-expressions |